From fb132cb4b82caef304b8ed4fd4c0380a532731aa Mon Sep 17 00:00:00 2001 From: "Lucas.Xu" Date: Tue, 3 Dec 2024 21:06:51 +0800 Subject: [PATCH 001/576] chore: upgrade rust version to 1.80.1 --- .github/workflows/release.yml | 2 +- .github/workflows/rust_ci.yaml | 2 +- .github/workflows/rust_coverage.yml | 2 +- .github/workflows/tauri2_ci.yaml | 4 ++-- .github/workflows/tauri_ci.yaml | 6 +++--- .github/workflows/tauri_release.yml | 11 +++++------ 6 files changed, 13 insertions(+), 14 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1f2dde57e5..315f3ff310 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -7,7 +7,7 @@ on: env: FLUTTER_VERSION: "3.22.0" - RUST_TOOLCHAIN: "1.77.2" + RUST_TOOLCHAIN: "1.80.1" jobs: create-release: diff --git a/.github/workflows/rust_ci.yaml b/.github/workflows/rust_ci.yaml index 069e535976..6b2417f3db 100644 --- a/.github/workflows/rust_ci.yaml +++ b/.github/workflows/rust_ci.yaml @@ -19,7 +19,7 @@ on: env: CARGO_TERM_COLOR: always CLOUD_VERSION: 0.7.6-amd64 - RUST_TOOLCHAIN: "1.77.2" + RUST_TOOLCHAIN: "1.80.1" jobs: self-hosted-job: diff --git a/.github/workflows/rust_coverage.yml b/.github/workflows/rust_coverage.yml index 12e728698f..4e2dd3c8a4 100644 --- a/.github/workflows/rust_coverage.yml +++ b/.github/workflows/rust_coverage.yml @@ -11,7 +11,7 @@ on: env: CARGO_TERM_COLOR: always FLUTTER_VERSION: "3.22.0" - RUST_TOOLCHAIN: "1.77.2" + RUST_TOOLCHAIN: "1.80.1" jobs: tests: diff --git a/.github/workflows/tauri2_ci.yaml b/.github/workflows/tauri2_ci.yaml index 6bbb7928ee..1191b2a354 100644 --- a/.github/workflows/tauri2_ci.yaml +++ b/.github/workflows/tauri2_ci.yaml @@ -11,7 +11,7 @@ on: env: NODE_VERSION: "18.16.0" PNPM_VERSION: "8.5.0" - RUST_TOOLCHAIN: "1.77.2" + RUST_TOOLCHAIN: "1.80.1" CARGO_MAKE_VERSION: "0.36.6" CI: true @@ -121,4 +121,4 @@ jobs: with: tauriScript: pnpm tauri projectPath: frontend/appflowy_web_app - args: "--debug" \ No newline at end of file + args: "--debug" diff --git a/.github/workflows/tauri_ci.yaml b/.github/workflows/tauri_ci.yaml index 70ad621451..1cc817fb7c 100644 --- a/.github/workflows/tauri_ci.yaml +++ b/.github/workflows/tauri_ci.yaml @@ -7,7 +7,7 @@ on: env: NODE_VERSION: "18.16.0" PNPM_VERSION: "8.5.0" - RUST_TOOLCHAIN: "1.77.2" + RUST_TOOLCHAIN: "1.80.1" concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} @@ -19,7 +19,7 @@ jobs: strategy: fail-fast: false matrix: - platform: [ ubuntu-20.04 ] + platform: [ubuntu-20.04] runs-on: ${{ matrix.platform }} @@ -108,4 +108,4 @@ jobs: with: tauriScript: pnpm tauri projectPath: frontend/appflowy_tauri - args: "--debug" \ No newline at end of file + args: "--debug" diff --git a/.github/workflows/tauri_release.yml b/.github/workflows/tauri_release.yml index 7de80b017e..4595ec84ea 100644 --- a/.github/workflows/tauri_release.yml +++ b/.github/workflows/tauri_release.yml @@ -4,20 +4,19 @@ on: workflow_dispatch: inputs: branch: - description: 'The branch to release' + description: "The branch to release" required: true - default: 'main' + default: "main" version: - description: 'The version to release' + description: "The version to release" required: true - default: '0.0.0' + default: "0.0.0" env: NODE_VERSION: "18.16.0" PNPM_VERSION: "8.5.0" - RUST_TOOLCHAIN: "1.77.2" + RUST_TOOLCHAIN: "1.80.1" jobs: - publish-tauri: permissions: contents: write From 687121ff1422a39a3f0d4f740324562f74bf7570 Mon Sep 17 00:00:00 2001 From: Lucas Date: Tue, 3 Dec 2024 21:38:28 +0800 Subject: [PATCH 002/576] chore: upgrade rust version to 1.80.1 (#6916) --- .github/workflows/release.yml | 2 +- .github/workflows/rust_ci.yaml | 2 +- .github/workflows/rust_coverage.yml | 2 +- .github/workflows/tauri2_ci.yaml | 4 ++-- .github/workflows/tauri_ci.yaml | 6 +++--- .github/workflows/tauri_release.yml | 11 +++++------ 6 files changed, 13 insertions(+), 14 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1f2dde57e5..315f3ff310 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -7,7 +7,7 @@ on: env: FLUTTER_VERSION: "3.22.0" - RUST_TOOLCHAIN: "1.77.2" + RUST_TOOLCHAIN: "1.80.1" jobs: create-release: diff --git a/.github/workflows/rust_ci.yaml b/.github/workflows/rust_ci.yaml index 069e535976..6b2417f3db 100644 --- a/.github/workflows/rust_ci.yaml +++ b/.github/workflows/rust_ci.yaml @@ -19,7 +19,7 @@ on: env: CARGO_TERM_COLOR: always CLOUD_VERSION: 0.7.6-amd64 - RUST_TOOLCHAIN: "1.77.2" + RUST_TOOLCHAIN: "1.80.1" jobs: self-hosted-job: diff --git a/.github/workflows/rust_coverage.yml b/.github/workflows/rust_coverage.yml index 12e728698f..4e2dd3c8a4 100644 --- a/.github/workflows/rust_coverage.yml +++ b/.github/workflows/rust_coverage.yml @@ -11,7 +11,7 @@ on: env: CARGO_TERM_COLOR: always FLUTTER_VERSION: "3.22.0" - RUST_TOOLCHAIN: "1.77.2" + RUST_TOOLCHAIN: "1.80.1" jobs: tests: diff --git a/.github/workflows/tauri2_ci.yaml b/.github/workflows/tauri2_ci.yaml index 6bbb7928ee..1191b2a354 100644 --- a/.github/workflows/tauri2_ci.yaml +++ b/.github/workflows/tauri2_ci.yaml @@ -11,7 +11,7 @@ on: env: NODE_VERSION: "18.16.0" PNPM_VERSION: "8.5.0" - RUST_TOOLCHAIN: "1.77.2" + RUST_TOOLCHAIN: "1.80.1" CARGO_MAKE_VERSION: "0.36.6" CI: true @@ -121,4 +121,4 @@ jobs: with: tauriScript: pnpm tauri projectPath: frontend/appflowy_web_app - args: "--debug" \ No newline at end of file + args: "--debug" diff --git a/.github/workflows/tauri_ci.yaml b/.github/workflows/tauri_ci.yaml index 70ad621451..1cc817fb7c 100644 --- a/.github/workflows/tauri_ci.yaml +++ b/.github/workflows/tauri_ci.yaml @@ -7,7 +7,7 @@ on: env: NODE_VERSION: "18.16.0" PNPM_VERSION: "8.5.0" - RUST_TOOLCHAIN: "1.77.2" + RUST_TOOLCHAIN: "1.80.1" concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} @@ -19,7 +19,7 @@ jobs: strategy: fail-fast: false matrix: - platform: [ ubuntu-20.04 ] + platform: [ubuntu-20.04] runs-on: ${{ matrix.platform }} @@ -108,4 +108,4 @@ jobs: with: tauriScript: pnpm tauri projectPath: frontend/appflowy_tauri - args: "--debug" \ No newline at end of file + args: "--debug" diff --git a/.github/workflows/tauri_release.yml b/.github/workflows/tauri_release.yml index 7de80b017e..4595ec84ea 100644 --- a/.github/workflows/tauri_release.yml +++ b/.github/workflows/tauri_release.yml @@ -4,20 +4,19 @@ on: workflow_dispatch: inputs: branch: - description: 'The branch to release' + description: "The branch to release" required: true - default: 'main' + default: "main" version: - description: 'The version to release' + description: "The version to release" required: true - default: '0.0.0' + default: "0.0.0" env: NODE_VERSION: "18.16.0" PNPM_VERSION: "8.5.0" - RUST_TOOLCHAIN: "1.77.2" + RUST_TOOLCHAIN: "1.80.1" jobs: - publish-tauri: permissions: contents: write From 7c24b6feb0d2865e9dd03281100cfbc453d087ba Mon Sep 17 00:00:00 2001 From: Richard Shiue <71320345+richardshiue@users.noreply.github.com> Date: Tue, 3 Dec 2024 22:20:14 +0800 Subject: [PATCH 003/576] feat: revamp mention page interactions in AI chat (#6896) * chore: code cleanup * chore: improve mention page ui * chore: just use view pb * chore: remove chat input menu style * chore: code cleanup * chore: rewrite and unify chat input action handler and bloc * feat: improve appearance of mention page popup * fix: misaligned emoji text --- .../application/ai_prompt_input_bloc.dart | 88 ++- .../ai_chat/application/chat_entity.dart | 4 +- .../application/chat_input_action_bloc.dart | 217 ------ .../chat_input_action_control.dart | 172 ----- .../application/chat_input_control_cubit.dart | 239 +++++++ .../application/chat_message_service.dart | 10 +- .../lib/plugins/ai_chat/chat_page.dart | 3 +- .../chat_input/chat_input_file.dart | 2 +- .../chat_input/chat_input_span.dart | 62 +- .../chat_input/chat_mention_page_menu.dart | 419 +++++++++++ .../chat_input/desktop_ai_prompt_input.dart | 671 +++++++++++------- .../chat_input/mobile_ai_prompt_input.dart | 311 ++++---- .../presentation/chat_input_action_menu.dart | 320 --------- .../presentation/message/ai_text_message.dart | 9 +- .../loading_indicator.dart} | 17 +- 15 files changed, 1360 insertions(+), 1184 deletions(-) delete mode 100644 frontend/appflowy_flutter/lib/plugins/ai_chat/application/chat_input_action_bloc.dart delete mode 100644 frontend/appflowy_flutter/lib/plugins/ai_chat/application/chat_input_action_control.dart create mode 100644 frontend/appflowy_flutter/lib/plugins/ai_chat/application/chat_input_control_cubit.dart create mode 100644 frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_input/chat_mention_page_menu.dart delete mode 100644 frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_input_action_menu.dart rename frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/{chat_loading.dart => message/loading_indicator.dart} (79%) diff --git a/frontend/appflowy_flutter/lib/plugins/ai_chat/application/ai_prompt_input_bloc.dart b/frontend/appflowy_flutter/lib/plugins/ai_chat/application/ai_prompt_input_bloc.dart index 99a00228c7..c52a464199 100644 --- a/frontend/appflowy_flutter/lib/plugins/ai_chat/application/ai_prompt_input_bloc.dart +++ b/frontend/appflowy_flutter/lib/plugins/ai_chat/application/ai_prompt_input_bloc.dart @@ -5,6 +5,7 @@ import 'package:appflowy/workspace/application/settings/ai/local_llm_listener.da import 'package:appflowy_backend/dispatch/dispatch.dart'; import 'package:appflowy_backend/log.dart'; import 'package:appflowy_backend/protobuf/flowy-ai/entities.pb.dart'; +import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart'; import 'package:appflowy_result/appflowy_result.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; @@ -20,18 +21,6 @@ class AIPromptInputBloc extends Bloc { _init(); } - ChatInputFileMetadata consumeMetadata() { - final metadata = { - for (final file in state.uploadFiles) file.filePath: file, - }; - - if (metadata.isNotEmpty) { - add(const AIPromptInputEvent.clear()); - } - - return metadata; - } - final LocalLLMListener _listener; @override @@ -44,15 +33,6 @@ class AIPromptInputBloc extends Bloc { on( (event, emit) { event.when( - newFile: (String filePath, String fileName) { - final files = [...state.uploadFiles]; - - final newFile = ChatFile.fromFilePath(filePath); - if (newFile != null) { - files.add(newFile); - emit(state.copyWith(uploadFiles: files)); - } - }, updateChatState: (LocalAIChatPB chatState) { // Only user enable chat with file and the plugin is already running final supportChatWithFile = chatState.fileEnabled && @@ -80,19 +60,37 @@ class AIPromptInputBloc extends Bloc { ), ); }, - deleteFile: (file) { - final files = List.from(state.uploadFiles); + attachFile: (filePath, fileName) { + final newFile = ChatFile.fromFilePath(filePath); + if (newFile != null) { + emit( + state.copyWith( + attachedFiles: [...state.attachedFiles, newFile], + ), + ); + } + }, + removeFile: (file) { + final files = [...state.attachedFiles]; files.remove(file); emit( state.copyWith( - uploadFiles: files, + attachedFiles: files, ), ); }, - clear: () { + updateMentionedViews: (views) { emit( state.copyWith( - uploadFiles: [], + mentionedPages: views, + ), + ); + }, + clearMetadata: () { + emit( + state.copyWith( + attachedFiles: [], + mentionedPages: [], ), ); }, @@ -126,35 +124,55 @@ class AIPromptInputBloc extends Bloc { Log.error, ); } + + Map consumeMetadata() { + final metadata = { + for (final file in state.attachedFiles) file.filePath: file, + for (final page in state.mentionedPages) page.id: page, + }; + + if (metadata.isNotEmpty && !isClosed) { + add(const AIPromptInputEvent.clearMetadata()); + } + + return metadata; + } } @freezed class AIPromptInputEvent with _$AIPromptInputEvent { - const factory AIPromptInputEvent.newFile(String filePath, String fileName) = - _NewFile; - const factory AIPromptInputEvent.deleteFile(ChatFile file) = _DeleteFile; - const factory AIPromptInputEvent.clear() = _ClearFile; const factory AIPromptInputEvent.updateChatState( LocalAIChatPB chatState, ) = _UpdateChatState; const factory AIPromptInputEvent.updatePluginState( LocalAIPluginStatePB chatState, ) = _UpdatePluginState; + const factory AIPromptInputEvent.attachFile( + String filePath, + String fileName, + ) = _AttachFile; + const factory AIPromptInputEvent.removeFile(ChatFile file) = _RemoveFile; + const factory AIPromptInputEvent.updateMentionedViews(List views) = + _UpdateMentionedViews; + const factory AIPromptInputEvent.clearMetadata() = _ClearMetadata; } @freezed class AIPromptInputState with _$AIPromptInputState { const factory AIPromptInputState({ - required bool supportChatWithFile, - LocalAIChatPB? chatState, - required List uploadFiles, required AIType aiType, + required bool supportChatWithFile, + required LocalAIChatPB? chatState, + required List attachedFiles, + required List mentionedPages, }) = _AIPromptInputState; factory AIPromptInputState.initial() => const AIPromptInputState( - supportChatWithFile: false, - uploadFiles: [], aiType: AIType.appflowyAI, + supportChatWithFile: false, + chatState: null, + attachedFiles: [], + mentionedPages: [], ); } diff --git a/frontend/appflowy_flutter/lib/plugins/ai_chat/application/chat_entity.dart b/frontend/appflowy_flutter/lib/plugins/ai_chat/application/chat_entity.dart index 9f5f02f8fe..abf1b99f44 100644 --- a/frontend/appflowy_flutter/lib/plugins/ai_chat/application/chat_entity.dart +++ b/frontend/appflowy_flutter/lib/plugins/ai_chat/application/chat_entity.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'package:appflowy_backend/protobuf/flowy-ai/entities.pbenum.dart'; import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart'; +import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart'; import 'package:equatable/equatable.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:path/path.dart' as path; @@ -106,7 +107,8 @@ class ChatFile extends Equatable { List get props => [filePath]; } -typedef ChatInputFileMetadata = Map; +typedef ChatFileMap = Map; +typedef ChatMentionedPageMap = Map; @freezed class ChatLoadingState with _$ChatLoadingState { diff --git a/frontend/appflowy_flutter/lib/plugins/ai_chat/application/chat_input_action_bloc.dart b/frontend/appflowy_flutter/lib/plugins/ai_chat/application/chat_input_action_bloc.dart deleted file mode 100644 index 466f82ca0b..0000000000 --- a/frontend/appflowy_flutter/lib/plugins/ai_chat/application/chat_input_action_bloc.dart +++ /dev/null @@ -1,217 +0,0 @@ -import 'dart:async'; - -import 'package:appflowy/workspace/application/view/view_ext.dart'; -import 'package:appflowy/workspace/application/view/view_service.dart'; -import 'package:appflowy_backend/log.dart'; -import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart'; -import 'package:bloc/bloc.dart'; -import 'package:equatable/equatable.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter/widgets.dart'; -import 'package:freezed_annotation/freezed_annotation.dart'; - -import 'chat_input_action_control.dart'; - -part 'chat_input_action_bloc.freezed.dart'; - -class ChatInputActionBloc - extends Bloc { - ChatInputActionBloc({required this.chatId}) - : super(const ChatInputActionState()) { - on(_handleEvent); - } - - final String chatId; - - Future _handleEvent( - ChatInputActionEvent event, - Emitter emit, - ) async { - await event.when( - started: () async { - unawaited( - ViewBackendService.getAllViews().then( - (result) { - final views = result - .toNullable() - ?.items - .where( - (v) => - v.layout.isDocumentView && - !v.isSpace && - v.parentViewId.isNotEmpty, - ) - .toList() ?? - []; - if (!isClosed) { - add(ChatInputActionEvent.refreshViews(views)); - } - }, - ), - ); - }, - refreshViews: (List views) { - final List pages = _filterPages( - views, - state.selectedPages, - state.filter, - ); - emit( - state.copyWith( - views: views, - pages: pages, - indicator: const ChatActionMenuIndicator.ready(), - ), - ); - }, - filter: (String filter) { - Log.debug("Filter chat input pages: $filter"); - final List pages = _filterPages( - state.views, - state.selectedPages, - filter, - ); - - emit(state.copyWith(pages: pages, filter: filter)); - }, - handleKeyEvent: (PhysicalKeyboardKey physicalKey) { - emit( - state.copyWith( - keyboardKey: ChatInputKeyboardEvent(physicalKey: physicalKey), - ), - ); - }, - addPage: (ChatInputMention page) { - if (!state.selectedPages.any((p) => p.pageId == page.pageId)) { - final List pages = _filterPages( - state.views, - state.selectedPages, - state.filter, - ); - emit( - state.copyWith( - pages: pages, - selectedPages: [...state.selectedPages, page], - ), - ); - } - }, - removePage: (String text) { - final List selectedPages = - List.from(state.selectedPages); - selectedPages.retainWhere((t) => !text.contains(t.title)); - - final List allPages = _filterPages( - state.views, - state.selectedPages, - state.filter, - ); - - emit( - state.copyWith( - selectedPages: selectedPages, - pages: allPages, - ), - ); - }, - clear: () { - emit( - state.copyWith( - selectedPages: [], - filter: "", - ), - ); - }, - ); - } -} - -List _filterPages( - List views, - List selectedPages, - String filter, -) { - final pages = views - .map( - (v) => ViewActionPage(view: v), - ) - .toList(); - - pages.retainWhere((page) { - return !selectedPages.contains(page); - }); - - if (filter.isEmpty) { - return pages; - } - - return pages - .where( - (v) => v.title.toLowerCase().contains(filter.toLowerCase()), - ) - .toList(); -} - -class ViewActionPage extends ChatInputMention { - ViewActionPage({required this.view}); - - final ViewPB view; - - @override - String get pageId => view.id; - - @override - String get title => view.name; - - @override - List get props => [pageId]; - - @override - dynamic get page => view; - - @override - Widget get icon => view.defaultIcon(); -} - -@freezed -class ChatInputActionEvent with _$ChatInputActionEvent { - const factory ChatInputActionEvent.started() = _Started; - const factory ChatInputActionEvent.refreshViews(List views) = - _RefreshViews; - const factory ChatInputActionEvent.filter(String filter) = _Filter; - const factory ChatInputActionEvent.handleKeyEvent( - PhysicalKeyboardKey keyboardKey, - ) = _HandleKeyEvent; - const factory ChatInputActionEvent.addPage(ChatInputMention page) = _AddPage; - const factory ChatInputActionEvent.removePage(String text) = _RemovePage; - const factory ChatInputActionEvent.clear() = _Clear; -} - -@freezed -class ChatInputActionState with _$ChatInputActionState { - const factory ChatInputActionState({ - @Default([]) List views, - @Default([]) List pages, - @Default([]) List selectedPages, - @Default("") String filter, - ChatInputKeyboardEvent? keyboardKey, - @Default(ChatActionMenuIndicator.loading()) - ChatActionMenuIndicator indicator, - }) = _ChatInputActionState; -} - -class ChatInputKeyboardEvent extends Equatable { - ChatInputKeyboardEvent({required this.physicalKey}); - - final PhysicalKeyboardKey physicalKey; - final int timestamp = DateTime.now().millisecondsSinceEpoch; - - @override - List get props => [timestamp]; -} - -@freezed -class ChatActionMenuIndicator with _$ChatActionMenuIndicator { - const factory ChatActionMenuIndicator.ready() = _Ready; - const factory ChatActionMenuIndicator.loading() = _Loading; -} diff --git a/frontend/appflowy_flutter/lib/plugins/ai_chat/application/chat_input_action_control.dart b/frontend/appflowy_flutter/lib/plugins/ai_chat/application/chat_input_action_control.dart deleted file mode 100644 index b945b9c2d7..0000000000 --- a/frontend/appflowy_flutter/lib/plugins/ai_chat/application/chat_input_action_control.dart +++ /dev/null @@ -1,172 +0,0 @@ -import 'package:appflowy/plugins/ai_chat/application/chat_input_action_bloc.dart'; -import 'package:appflowy/plugins/ai_chat/presentation/chat_input_action_menu.dart'; -import 'package:appflowy_backend/log.dart'; -import 'package:equatable/equatable.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; - -abstract class ChatInputMention extends Equatable { - String get title; - String get pageId; - dynamic get page; - Widget get icon; -} - -/// Key: the key is the pageId -typedef ChatInputMentionMetadata = Map; - -class ChatInputActionControl extends ChatActionHandler { - ChatInputActionControl({ - required this.textController, - required this.textFieldFocusNode, - required this.chatId, - }) : _commandBloc = ChatInputActionBloc(chatId: chatId); - - final TextEditingController textController; - final ChatInputActionBloc _commandBloc; - final FocusNode textFieldFocusNode; - final String chatId; - - // Private attributes - String _atText = ""; - String _prevText = ""; - String _showMenuText = ""; - - // Getter - List get tags => - _commandBloc.state.selectedPages.map((e) => e.title).toList(); - - ChatInputMentionMetadata consumeMetaData() { - final metadata = _commandBloc.state.selectedPages.fold( - {}, - (map, page) => map..putIfAbsent(page.pageId, () => page), - ); - - if (metadata.isNotEmpty) { - _commandBloc.add(const ChatInputActionEvent.clear()); - } - - return metadata; - } - - void handleKeyEvent(KeyEvent event) { - // ignore: deprecated_member_use - if (event is KeyDownEvent || event is RawKeyDownEvent) { - _commandBloc.add(ChatInputActionEvent.handleKeyEvent(event.physicalKey)); - } - } - - bool canHandleKeyEvent(KeyEvent event) { - return _showMenuText.isNotEmpty && - { - PhysicalKeyboardKey.arrowDown, - PhysicalKeyboardKey.arrowUp, - PhysicalKeyboardKey.enter, - PhysicalKeyboardKey.escape, - }.contains(event.physicalKey); - } - - void dispose() { - _commandBloc.close(); - } - - @override - void onSelected(ChatInputMention page) { - _commandBloc.add(ChatInputActionEvent.addPage(page)); - textController.text = "$_showMenuText${page.title}"; - - onExit(); - } - - @override - void onExit() { - _atText = ""; - _showMenuText = ""; - _prevText = ""; - _commandBloc.add(const ChatInputActionEvent.filter("")); - } - - @override - void onEnter() { - _commandBloc.add(const ChatInputActionEvent.started()); - _showMenuText = textController.text; - } - - @override - double actionMenuOffsetX() { - final TextPosition textPosition = textController.selection.extent; - if (textFieldFocusNode.context == null) { - return 0; - } - - final RenderBox renderBox = - textFieldFocusNode.context?.findRenderObject() as RenderBox; - - final TextPainter textPainter = TextPainter( - text: TextSpan(text: textController.text), - textDirection: TextDirection.ltr, - ); - textPainter.layout( - minWidth: renderBox.size.width, - maxWidth: renderBox.size.width, - ); - - final Offset caretOffset = - textPainter.getOffsetForCaret(textPosition, Rect.zero); - final List boxes = textPainter.getBoxesForSelection( - TextSelection( - baseOffset: textPosition.offset, - extentOffset: textPosition.offset, - ), - ); - - if (boxes.isNotEmpty) { - return boxes.last.right; - } - return caretOffset.dx; - } - - bool onTextChanged(String text) { - final String inputText = text; - if (_prevText.length > inputText.length) { - final deleteStartIndex = textController.selection.baseOffset; - final deleteEndIndex = - _prevText.length - inputText.length + deleteStartIndex; - final deletedText = _prevText.substring(deleteStartIndex, deleteEndIndex); - _commandBloc.add(ChatInputActionEvent.removePage(deletedText)); - } - - // If the action menu is shown, filter the views - if (_showMenuText.isNotEmpty) { - if (text.length >= _showMenuText.length) { - final filterText = inputText.substring(_showMenuText.length); - _commandBloc.add(ChatInputActionEvent.filter(filterText)); - } - - // If the text change from "xxx @"" to "xxx", which means user delete the @, we should hide the action menu - if (_atText.isNotEmpty && !inputText.contains(_atText)) { - _commandBloc.add( - const ChatInputActionEvent.handleKeyEvent(PhysicalKeyboardKey.escape), - ); - } - } else { - final isTypingNewAt = - text.endsWith("@") && _prevText.length < text.length; - if (isTypingNewAt) { - _atText = text; - _prevText = text; - return true; - } - } - _prevText = text; - return false; - } - - @override - void onFilter(String filter) { - Log.info("filter: $filter"); - } - - @override - ChatInputActionBloc get commandBloc => _commandBloc; -} diff --git a/frontend/appflowy_flutter/lib/plugins/ai_chat/application/chat_input_control_cubit.dart b/frontend/appflowy_flutter/lib/plugins/ai_chat/application/chat_input_control_cubit.dart new file mode 100644 index 0000000000..c663699e2c --- /dev/null +++ b/frontend/appflowy_flutter/lib/plugins/ai_chat/application/chat_input_control_cubit.dart @@ -0,0 +1,239 @@ +import 'package:appflowy/generated/locale_keys.g.dart'; +import 'package:appflowy/workspace/application/view/prelude.dart'; +import 'package:appflowy/workspace/application/view/view_ext.dart'; +import 'package:appflowy_backend/log.dart'; +import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart'; +import 'package:appflowy_result/appflowy_result.dart'; +import 'package:bloc/bloc.dart'; +import 'package:collection/collection.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/services.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'chat_input_control_cubit.freezed.dart'; + +class ChatInputControlCubit extends Cubit { + ChatInputControlCubit() : super(const ChatInputControlState.loading()); + + final List allViews = []; + final List selectedViewIds = []; + + /// used when mentioning a page + /// + /// the text position after the @ character + int _filterStartPosition = -1; + + /// used when mentioning a page + /// + /// the text position after the @ character, at the end of the filter + int _filterEndPosition = -1; + + /// used when mentioning a page + /// + /// the entire string input in the prompt + String _inputText = ""; + + /// used when mentioning a page + /// + /// the current filtering text, after the @ characater + String _filter = ""; + + String get inputText => _inputText; + int get filterStartPosition => _filterStartPosition; + int get filterEndPosition => _filterEndPosition; + + void refreshViews() async { + final newViews = await ViewBackendService.getAllViews().fold( + (result) => result.items + .where((v) => v.layout.isDocumentView && v.parentViewId.isNotEmpty) + .toList(), + (err) { + Log.error(err); + return []; + }, + ); + allViews + ..clear() + ..addAll(newViews); + + // update visible views + newViews.retainWhere((v) => !selectedViewIds.contains(v.id)); + if (_filter.isNotEmpty) { + newViews.retainWhere( + (v) { + final nonEmptyName = v.name.isEmpty + ? LocaleKeys.document_title_placeholder.tr() + : v.name; + return nonEmptyName.toLowerCase().contains(_filter); + }, + ); + } + final focusedViewIndex = newViews.isEmpty ? -1 : 0; + emit( + ChatInputControlState.ready( + visibleViews: newViews, + focusedViewIndex: focusedViewIndex, + ), + ); + } + + void startSearching(TextEditingValue textEditingValue) { + _filterStartPosition = + _filterEndPosition = textEditingValue.selection.baseOffset; + _filter = ""; + _inputText = textEditingValue.text; + state.maybeMap( + ready: (readyState) { + emit( + readyState.copyWith( + visibleViews: allViews, + focusedViewIndex: allViews.isEmpty ? -1 : 0, + ), + ); + }, + orElse: () {}, + ); + } + + void reset() { + _filterStartPosition = _filterEndPosition = -1; + _filter = _inputText = ""; + state.maybeMap( + ready: (readyState) { + emit( + readyState.copyWith( + visibleViews: allViews, + focusedViewIndex: allViews.isEmpty ? -1 : 0, + ), + ); + }, + orElse: () {}, + ); + } + + void updateFilter( + String newInputText, + String newFilter, { + int? newEndPosition, + }) { + updateInputText(newInputText); + + // filter the views + _filter = newFilter.toLowerCase(); + if (newEndPosition != null) { + _filterEndPosition = newEndPosition; + } + + final newVisibleViews = + allViews.where((v) => !selectedViewIds.contains(v.id)).toList(); + + if (_filter.isNotEmpty) { + newVisibleViews.retainWhere( + (v) { + final nonEmptyName = v.name.isEmpty + ? LocaleKeys.document_title_placeholder.tr() + : v.name; + return nonEmptyName.toLowerCase().contains(_filter); + }, + ); + } + + state.maybeWhen( + ready: (_, oldFocusedIndex) { + final newFocusedViewIndex = oldFocusedIndex < newVisibleViews.length + ? oldFocusedIndex + : (newVisibleViews.isEmpty ? -1 : 0); + emit( + ChatInputControlState.ready( + visibleViews: newVisibleViews, + focusedViewIndex: newFocusedViewIndex, + ), + ); + }, + orElse: () {}, + ); + } + + void updateInputText(String newInputText) { + _inputText = newInputText; + + // input text is changed, see if there are any deletions + selectedViewIds.retainWhere(_inputText.contains); + _notifyUpdateSelectedViews(); + } + + void updateSelectionUp() { + state.maybeMap( + ready: (readyState) { + final newIndex = readyState.visibleViews.isEmpty + ? -1 + : (readyState.focusedViewIndex - 1) % + readyState.visibleViews.length; + emit( + readyState.copyWith(focusedViewIndex: newIndex), + ); + }, + orElse: () {}, + ); + } + + void updateSelectionDown() { + state.maybeMap( + ready: (readyState) { + final newIndex = readyState.visibleViews.isEmpty + ? -1 + : (readyState.focusedViewIndex + 1) % + readyState.visibleViews.length; + emit( + readyState.copyWith(focusedViewIndex: newIndex), + ); + }, + orElse: () {}, + ); + } + + void selectPage(ViewPB view) { + selectedViewIds.add(view.id); + _notifyUpdateSelectedViews(); + reset(); + } + + String formatIntputText(final String input) { + String result = input; + for (final viewId in selectedViewIds) { + if (!result.contains(viewId)) { + continue; + } + final view = allViews.firstWhereOrNull((view) => view.id == viewId); + if (view != null) { + final nonEmptyName = view.name.isEmpty + ? LocaleKeys.document_title_placeholder.tr() + : view.name; + result = result.replaceAll(RegExp(viewId), nonEmptyName); + } + } + return result; + } + + void _notifyUpdateSelectedViews() { + final stateCopy = state; + final selectedViews = + allViews.where((view) => selectedViewIds.contains(view.id)).toList(); + emit(ChatInputControlState.updateSelectedViews(selectedViews)); + emit(stateCopy); + } +} + +@freezed +class ChatInputControlState with _$ChatInputControlState { + const factory ChatInputControlState.loading() = _Loading; + + const factory ChatInputControlState.ready({ + required List visibleViews, + required int focusedViewIndex, + }) = _Ready; + + const factory ChatInputControlState.updateSelectedViews( + List selectedViews, + ) = _UpdateOneShot; +} diff --git a/frontend/appflowy_flutter/lib/plugins/ai_chat/application/chat_message_service.dart b/frontend/appflowy_flutter/lib/plugins/ai_chat/application/chat_message_service.dart index 0e08d1dafb..70b64ac927 100644 --- a/frontend/appflowy_flutter/lib/plugins/ai_chat/application/chat_message_service.dart +++ b/frontend/appflowy_flutter/lib/plugins/ai_chat/application/chat_message_service.dart @@ -1,12 +1,12 @@ import 'dart:convert'; import 'package:appflowy/plugins/ai_chat/application/chat_entity.dart'; -import 'package:appflowy/plugins/ai_chat/application/chat_input_action_bloc.dart'; import 'package:appflowy/workspace/application/view/view_ext.dart'; import 'package:appflowy_backend/dispatch/dispatch.dart'; import 'package:appflowy_backend/log.dart'; import 'package:appflowy_backend/protobuf/flowy-ai/protobuf.dart'; import 'package:appflowy_backend/protobuf/flowy-document/entities.pb.dart'; +import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart'; import 'package:appflowy_result/appflowy_result.dart'; import 'package:nanoid/nanoid.dart'; @@ -129,14 +129,14 @@ Future> metadataPBFromMetadata( for (final value in map.values) { switch (value) { - case ViewActionPage(view: final view) when view.layout.isDocumentView: - final payload = OpenDocumentPayloadPB(documentId: view.id); + case ViewPB _ when value.layout.isDocumentView: + final payload = OpenDocumentPayloadPB(documentId: value.id); await DocumentEventGetDocumentText(payload).send().fold( (pb) { metadata.add( ChatMessageMetaPB( - id: view.id, - name: view.name, + id: value.id, + name: value.name, data: pb.text, dataType: ChatMessageMetaTypePB.Txt, source: appflowySource, diff --git a/frontend/appflowy_flutter/lib/plugins/ai_chat/chat_page.dart b/frontend/appflowy_flutter/lib/plugins/ai_chat/chat_page.dart index 72cd3b65f9..6767bf80db 100644 --- a/frontend/appflowy_flutter/lib/plugins/ai_chat/chat_page.dart +++ b/frontend/appflowy_flutter/lib/plugins/ai_chat/chat_page.dart @@ -76,7 +76,7 @@ class AIChatPage extends StatelessWidget { for (final file in detail.files) { context .read() - .add(AIPromptInputEvent.newFile(file.path, file.name)); + .add(AIPromptInputEvent.attachFile(file.path, file.name)); } } }, @@ -345,7 +345,6 @@ class _ChatContentPage extends StatelessWidget { return UniversalPlatform.isDesktop ? DesktopAIPromptInput( chatId: view.id, - indicateFocus: true, onSubmitted: (text, metadata) { context.read().add( ChatEvent.sendMessage( diff --git a/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_input/chat_input_file.dart b/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_input/chat_input_file.dart index b0e260fd08..d2f6f6750e 100644 --- a/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_input/chat_input_file.dart +++ b/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_input/chat_input_file.dart @@ -23,7 +23,7 @@ class ChatInputFile extends StatelessWidget { @override Widget build(BuildContext context) { return BlocSelector>( - selector: (state) => state.uploadFiles, + selector: (state) => state.attachedFiles, builder: (context, files) { if (files.isEmpty) { return const SizedBox.shrink(); diff --git a/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_input/chat_input_span.dart b/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_input/chat_input_span.dart index 854211d95d..06d8248048 100644 --- a/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_input/chat_input_span.dart +++ b/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_input/chat_input_span.dart @@ -1,15 +1,18 @@ +import 'package:appflowy/generated/locale_keys.g.dart'; +import 'package:appflowy/plugins/ai_chat/application/chat_input_control_cubit.dart'; +import 'package:collection/collection.dart'; +import 'package:easy_localization/easy_localization.dart'; import 'package:extended_text_library/extended_text_library.dart'; -import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; -import '../../application/chat_input_action_control.dart'; - class ChatInputTextSpanBuilder extends SpecialTextSpanBuilder { ChatInputTextSpanBuilder({ - required this.inputActionControl, + required this.inputControlCubit, + this.specialTextStyle, }); - final ChatInputActionControl inputActionControl; + final ChatInputControlCubit inputControlCubit; + final TextStyle? specialTextStyle; @override SpecialText? createSpecialText( @@ -22,51 +25,54 @@ class ChatInputTextSpanBuilder extends SpecialTextSpanBuilder { return null; } - //index is end index of start flag, so text start index should be index-(flag.length-1) - if (isStart(flag, AtText.flag)) { - return AtText( - inputActionControl, - textStyle, - onTap, - start: index! - (AtText.flag.length - 1), - ); + if (!isStart(flag, AtText.flag)) { + return null; } - return null; + + // index is at the end of the start flag, so the start index should be index - (flag.length - 1) + return AtText( + inputControlCubit, + specialTextStyle ?? textStyle, + onTap, + // scrubbing over text is kinda funky + start: index! - (AtText.flag.length - 1), + ); } } class AtText extends SpecialText { AtText( - this.inputActionControl, + this.inputControlCubit, TextStyle? textStyle, SpecialTextGestureTapCallback? onTap, { this.start, }) : super(flag, '', textStyle, onTap: onTap); + static const String flag = '@'; + final int? start; - final ChatInputActionControl inputActionControl; + final ChatInputControlCubit inputControlCubit; @override - bool isEnd(String value) { - return inputActionControl.tags.contains(value); - } + bool isEnd(String value) => inputControlCubit.selectedViewIds.contains(value); @override InlineSpan finishText() { - final TextStyle? textStyle = - this.textStyle?.copyWith(color: Colors.blue, fontSize: 15.0); + final String actualText = toString(); - final String atText = toString(); + final viewName = inputControlCubit.allViews + .firstWhereOrNull((view) => view.id == actualText.substring(1)) + ?.name ?? + ""; + final nonEmptyName = viewName.isEmpty + ? LocaleKeys.document_title_placeholder.tr() + : viewName; return SpecialTextSpan( - text: atText, - actualText: atText, + text: "@$nonEmptyName", + actualText: actualText, start: start!, style: textStyle, - recognizer: (TapGestureRecognizer() - ..onTap = () { - onTap?.call(atText); - }), ); } } diff --git a/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_input/chat_mention_page_menu.dart b/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_input/chat_mention_page_menu.dart new file mode 100644 index 0000000000..fff5338e67 --- /dev/null +++ b/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_input/chat_mention_page_menu.dart @@ -0,0 +1,419 @@ +import 'package:appflowy/generated/flowy_svgs.g.dart'; +import 'package:appflowy/generated/locale_keys.g.dart'; +import 'package:appflowy/plugins/ai_chat/application/chat_input_control_cubit.dart'; +import 'package:appflowy/workspace/application/view/view_ext.dart'; +import 'package:appflowy/workspace/application/view_title/view_title_bar_bloc.dart'; +import 'package:appflowy/workspace/presentation/home/menu/sidebar/space/space_icon.dart'; +import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart'; +import 'package:easy_localization/easy_localization.dart' hide TextDirection; +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:scroll_to_index/scroll_to_index.dart'; + +const double _itemHeight = 44.0; +const double _noPageHeight = 20.0; +const double _fixedWidth = 360.0; +const double _maxHeight = 600.0; + +class ChatInputAnchor { + ChatInputAnchor(this.anchorKey, this.layerLink); + + final GlobalKey> anchorKey; + final LayerLink layerLink; +} + +class ChatMentionPageMenu extends StatefulWidget { + const ChatMentionPageMenu({ + super.key, + required this.anchor, + required this.textController, + required this.onPageSelected, + }); + + final ChatInputAnchor anchor; + final TextEditingController textController; + final void Function(ViewPB view) onPageSelected; + + @override + State createState() => _ChatMentionPageMenuState(); +} + +class _ChatMentionPageMenuState extends State { + @override + void initState() { + super.initState(); + Future.delayed(Duration.zero, () { + context.read().refreshViews(); + }); + } + + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + return Stack( + children: [ + CompositedTransformFollower( + link: widget.anchor.layerLink, + showWhenUnlinked: false, + offset: Offset(getPopupOffsetX(), 0.0), + followerAnchor: Alignment.bottomLeft, + child: Container( + constraints: const BoxConstraints( + minWidth: _fixedWidth, + maxWidth: _fixedWidth, + maxHeight: _maxHeight, + ), + decoration: BoxDecoration( + color: Theme.of(context).cardColor, + borderRadius: BorderRadius.circular(6.0), + boxShadow: const [ + BoxShadow( + color: Color(0x0A1F2329), + blurRadius: 24, + offset: Offset(0, 8), + spreadRadius: 8, + ), + BoxShadow( + color: Color(0x0A1F2329), + blurRadius: 12, + offset: Offset(0, 6), + ), + BoxShadow( + color: Color(0x0F1F2329), + blurRadius: 8, + offset: Offset(0, 4), + spreadRadius: -8, + ), + ], + ), + child: TextFieldTapRegion( + child: ChatMentionPageList( + onPageSelected: widget.onPageSelected, + ), + ), + ), + ), + ], + ); + }, + ); + } + + double getPopupOffsetX() { + if (widget.anchor.anchorKey.currentContext == null) { + return 0.0; + } + + final cubit = context.read(); + if (cubit.filterStartPosition == -1) { + return 0.0; + } + + final textPosition = TextPosition(offset: cubit.filterEndPosition); + final renderBox = + widget.anchor.anchorKey.currentContext?.findRenderObject() as RenderBox; + + final textPainter = TextPainter( + text: TextSpan(text: cubit.formatIntputText(widget.textController.text)), + textDirection: TextDirection.ltr, + ); + textPainter.layout( + minWidth: renderBox.size.width, + maxWidth: renderBox.size.width, + ); + + final caretOffset = textPainter.getOffsetForCaret(textPosition, Rect.zero); + final boxes = textPainter.getBoxesForSelection( + TextSelection( + baseOffset: textPosition.offset, + extentOffset: textPosition.offset, + ), + ); + + if (boxes.isNotEmpty) { + return boxes.last.right; + } + + return caretOffset.dx; + } +} + +class ChatMentionPageList extends StatefulWidget { + const ChatMentionPageList({ + super.key, + required this.onPageSelected, + }); + + final void Function(ViewPB view) onPageSelected; + + @override + State createState() => _ChatMentionPageListState(); +} + +class _ChatMentionPageListState extends State { + final autoScrollController = AutoScrollController( + suggestedRowHeight: _itemHeight, + ); + + @override + void dispose() { + autoScrollController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return BlocConsumer( + listenWhen: (previous, current) { + return previous.maybeWhen( + ready: (pVisibleViews, pFocusedViewIndex) => current.maybeWhen( + ready: (cIsibleViews, cFocusedViewIndex) => + pFocusedViewIndex != cFocusedViewIndex, + orElse: () => false, + ), + orElse: () => false, + ); + }, + listener: (context, state) { + state.maybeWhen( + ready: (views, focusedViewIndex) { + if (focusedViewIndex == -1 || !autoScrollController.hasClients) { + return; + } + if (autoScrollController.isAutoScrolling) { + autoScrollController.position + .jumpTo(autoScrollController.position.pixels); + } + autoScrollController.scrollToIndex( + focusedViewIndex, + duration: const Duration(milliseconds: 200), + preferPosition: AutoScrollPosition.begin, + ); + }, + orElse: () {}, + ); + }, + builder: (context, state) { + return state.maybeWhen( + loading: () { + return const Padding( + padding: EdgeInsets.all(8.0), + child: SizedBox( + height: _noPageHeight, + child: Center( + child: CircularProgressIndicator.adaptive(), + ), + ), + ); + }, + ready: (views, focusedViewIndex) { + if (views.isEmpty) { + return Padding( + padding: const EdgeInsets.all(8.0), + child: SizedBox( + height: _noPageHeight, + child: Center( + child: FlowyText( + LocaleKeys.chat_inputActionNoPages.tr(), + ), + ), + ), + ); + } + + return ListView.builder( + shrinkWrap: true, + cacheExtent: 1, + addAutomaticKeepAlives: false, + controller: autoScrollController, + padding: const EdgeInsets.all(8.0), + itemCount: views.length, + itemBuilder: (context, index) { + final view = views[index]; + return AutoScrollTag( + key: ValueKey("chat_mention_page_item_${view.id}"), + index: index, + controller: autoScrollController, + child: _ChatMentionPageItem( + view: view, + onTap: () => widget.onPageSelected(view), + isSelected: focusedViewIndex == index, + ), + ); + }, + ); + }, + orElse: () => const SizedBox.shrink(), + ); + }, + ); + } +} + +class _ChatMentionPageItem extends StatelessWidget { + const _ChatMentionPageItem({ + required this.view, + required this.isSelected, + required this.onTap, + }); + + final ViewPB view; + final bool isSelected; + final VoidCallback onTap; + + @override + Widget build(BuildContext context) { + return FlowyTooltip( + message: view.name, + child: MouseRegion( + cursor: SystemMouseCursors.click, + child: GestureDetector( + onTap: onTap, + behavior: HitTestBehavior.opaque, + child: Container( + height: _itemHeight, + decoration: BoxDecoration( + color: isSelected + ? AFThemeExtension.of(context).lightGreyHover + : Colors.transparent, + borderRadius: BorderRadius.circular(4.0), + ), + padding: const EdgeInsets.all(4.0), + child: Row( + children: [ + _buildIcon(context, view), + const HSpace(8.0), + Expanded(child: _ViewTitleAndAncestors(view: view)), + ], + ), + ), + ), + ), + ); + } + + Widget _buildIcon(BuildContext context, ViewPB view) { + final spaceIcon = view.buildSpaceIconSvg(context); + + if (view.icon.value.isNotEmpty) { + return SizedBox( + width: 16.0, + child: FlowyText.emoji( + view.icon.value, + fontSize: 14.0, + figmaLineHeight: 21.0, + ), + ); + } + + if (view.isSpace == true && spaceIcon != null) { + return SpaceIcon( + dimension: 16.0, + svgSize: 9.68, + space: view, + cornerRadius: 4, + ); + } + + return FlowySvg( + view.layout.icon, + size: const Size.square(16), + color: Theme.of(context).hintColor, + ); + } +} + +class _ViewTitleAndAncestors extends StatelessWidget { + const _ViewTitleAndAncestors({ + required this.view, + }); + + final ViewPB view; + + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (_) => + ViewTitleBarBloc(view: view)..add(const ViewTitleBarEvent.initial()), + child: BlocBuilder( + builder: (context, state) { + final nonEmptyName = view.name.isEmpty + ? LocaleKeys.document_title_placeholder.tr() + : view.name; + + final ancestorList = _getViewAncestorList(state.ancestors); + + if (state.ancestors.isEmpty || ancestorList.trim().isEmpty) { + return FlowyText( + nonEmptyName, + overflow: TextOverflow.ellipsis, + ); + } + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + FlowyText( + nonEmptyName, + figmaLineHeight: 20.0, + overflow: TextOverflow.ellipsis, + ), + FlowyText( + ancestorList, + fontSize: 12.0, + figmaLineHeight: 16.0, + color: Theme.of(context).hintColor, + overflow: TextOverflow.ellipsis, + ), + ], + ); + }, + ), + ); + } + + /// see workspace/presentation/widgets/view_title_bar.dart, upon which this + /// function was based. This version doesn't include the current view in the + /// result, and returns a string rather than a list of widgets + String _getViewAncestorList( + List views, + ) { + const lowerBound = 2; + final upperBound = views.length - 2; + bool hasAddedEllipsis = false; + String result = ""; + + if (views.length <= 1) { + return ""; + } + + // ignore the workspace name, use section name instead in the future + // skip the workspace view + for (var i = 1; i < views.length - 1; i++) { + final view = views[i]; + + if (i >= lowerBound && i < upperBound) { + if (!hasAddedEllipsis) { + hasAddedEllipsis = true; + result += "… / "; + } + continue; + } + + final nonEmptyName = view.name.isEmpty + ? LocaleKeys.document_title_placeholder.tr() + : view.name; + + result += nonEmptyName; + + if (i != views.length - 2) { + // if not the last one, add a divider + result += " / "; + } + } + return result; + } +} diff --git a/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_input/desktop_ai_prompt_input.dart b/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_input/desktop_ai_prompt_input.dart index 86b863f8c5..c31b7f37ca 100644 --- a/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_input/desktop_ai_prompt_input.dart +++ b/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_input/desktop_ai_prompt_input.dart @@ -1,10 +1,8 @@ import 'package:appflowy/generated/locale_keys.g.dart'; -import 'package:appflowy/plugins/ai_chat/application/chat_entity.dart'; import 'package:appflowy/plugins/ai_chat/application/ai_prompt_input_bloc.dart'; -import 'package:appflowy/plugins/ai_chat/application/chat_input_action_control.dart'; -import 'package:appflowy/plugins/ai_chat/presentation/chat_input/chat_input_file.dart'; -import 'package:appflowy/plugins/ai_chat/presentation/chat_input_action_menu.dart'; +import 'package:appflowy/plugins/ai_chat/application/chat_input_control_cubit.dart'; import 'package:appflowy/startup/startup.dart'; +import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:extended_text_field/extended_text_field.dart'; import 'package:flowy_infra/file_picker/file_picker_service.dart'; @@ -14,22 +12,22 @@ import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:universal_platform/universal_platform.dart'; -import 'ai_prompt_buttons.dart'; -import 'chat_input_span.dart'; +import 'chat_mention_page_menu.dart'; import '../layout_define.dart'; +import 'ai_prompt_buttons.dart'; +import 'chat_input_file.dart'; +import 'chat_input_span.dart'; class DesktopAIPromptInput extends StatefulWidget { const DesktopAIPromptInput({ super.key, required this.chatId, - required this.indicateFocus, required this.isStreaming, required this.onStopStreaming, required this.onSubmitted, }); final String chatId; - final bool indicateFocus; final bool isStreaming; final void Function() onStopStreaming; final void Function(String, Map) onSubmitted; @@ -39,12 +37,12 @@ class DesktopAIPromptInput extends StatefulWidget { } class _DesktopAIPromptInputState extends State { - final GlobalKey _textFieldKey = GlobalKey(); - final LayerLink _layerLink = LayerLink(); - - late final ChatInputActionControl _inputActionControl; - late final FocusNode _inputFocusNode; - late final TextEditingController _textController; + final textFieldKey = GlobalKey(); + final layerLink = LayerLink(); + final overlayController = OverlayPortalController(); + final inputControlCubit = ChatInputControlCubit(); + final focusNode = FocusNode(); + final textController = TextEditingController(); late SendButtonState sendButtonState; @@ -52,40 +50,22 @@ class _DesktopAIPromptInputState extends State { void initState() { super.initState(); - _textController = TextEditingController() - ..addListener(_handleTextControllerChange); + textController.addListener(handleTextControllerChanged); - _inputFocusNode = FocusNode( - onKeyEvent: (node, event) { - if (_inputActionControl.canHandleKeyEvent(event)) { - _inputActionControl.handleKeyEvent(event); - return KeyEventResult.handled; - } else { - return _handleEnterKeyWithoutShift( - event, - _textController, - widget.isStreaming, - _handleSendPressed, - ); + // refresh border color on focus change and hide menu when lost focus + focusNode.addListener( + () => setState(() { + if (!focusNode.hasFocus) { + cancelMentionPage(); } - }, - )..addListener(() { - // refresh border color on focus change - if (widget.indicateFocus) { - setState(() {}); - } - }); - WidgetsBinding.instance.addPostFrameCallback((_) { - _inputFocusNode.requestFocus(); - }); - - _inputActionControl = ChatInputActionControl( - chatId: widget.chatId, - textController: _textController, - textFieldFocusNode: _inputFocusNode, + }), ); updateSendButtonState(); + + WidgetsBinding.instance.addPostFrameCallback((_) { + focusNode.requestFocus(); + }); } @override @@ -96,205 +76,464 @@ class _DesktopAIPromptInputState extends State { @override void dispose() { - _inputFocusNode.dispose(); - _textController.dispose(); - _inputActionControl.dispose(); + focusNode.dispose(); + textController.dispose(); + inputControlCubit.close(); super.dispose(); } @override Widget build(BuildContext context) { - return DecoratedBox( - decoration: BoxDecoration( - border: Border.all( - color: _inputFocusNode.hasFocus && widget.indicateFocus - ? Theme.of(context).colorScheme.primary - : Theme.of(context).colorScheme.outline, - ), - borderRadius: DesktopAIPromptSizes.promptFrameRadius, - ), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - ConstrainedBox( - constraints: BoxConstraints( - maxHeight: DesktopAIPromptSizes.attachedFilesBarPadding.vertical + - DesktopAIPromptSizes.attachedFilesPreviewHeight, - ), - child: TextFieldTapRegion( - child: ChatInputFile( - chatId: widget.chatId, - onDeleted: (file) => context - .read() - .add(AIPromptInputEvent.deleteFile(file)), + return BlocProvider.value( + value: inputControlCubit, + child: BlocListener( + listener: (context, state) { + state.maybeWhen( + updateSelectedViews: (selectedViews) { + context + .read() + .add(AIPromptInputEvent.updateMentionedViews(selectedViews)); + }, + orElse: () {}, + ); + }, + child: OverlayPortal( + controller: overlayController, + overlayChildBuilder: (context) { + return ChatMentionPageMenu( + anchor: ChatInputAnchor(textFieldKey, layerLink), + textController: textController, + onPageSelected: handlePageSelected, + ); + }, + child: DecoratedBox( + decoration: BoxDecoration( + border: Border.all( + color: focusNode.hasFocus + ? Theme.of(context).colorScheme.primary + : Theme.of(context).colorScheme.outline, ), + borderRadius: DesktopAIPromptSizes.promptFrameRadius, ), - ), - Stack( - children: [ - ConstrainedBox( - constraints: const BoxConstraints( - minHeight: DesktopAIPromptSizes.textFieldMinHeight + - DesktopAIPromptSizes.actionBarHeight, - maxHeight: 300, - ), - child: _inputTextField(context), - ), - Positioned.fill( - top: null, - child: TextFieldTapRegion( - child: Container( - height: DesktopAIPromptSizes.actionBarHeight, - padding: const EdgeInsets.symmetric(horizontal: 8), - child: BlocBuilder( - builder: (context, state) { - return Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // _predefinedFormatButton(), - const Spacer(), - _mentionButton(), - const HSpace( - DesktopAIPromptSizes.actionBarButtonSpacing, - ), - if (UniversalPlatform.isDesktop && - state.supportChatWithFile) ...[ - _attachmentButton(), - const HSpace( - DesktopAIPromptSizes.actionBarButtonSpacing, - ), - ], - _sendButton(), - ], - ); - }, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + ConstrainedBox( + constraints: BoxConstraints( + maxHeight: + DesktopAIPromptSizes.attachedFilesBarPadding.vertical + + DesktopAIPromptSizes.attachedFilesPreviewHeight, + ), + child: TextFieldTapRegion( + child: ChatInputFile( + chatId: widget.chatId, + onDeleted: (file) => context + .read() + .add(AIPromptInputEvent.removeFile(file)), ), ), ), - ), - ], + Stack( + children: [ + ConstrainedBox( + constraints: const BoxConstraints( + minHeight: DesktopAIPromptSizes.textFieldMinHeight + + DesktopAIPromptSizes.actionBarHeight, + maxHeight: 300, + ), + child: inputTextField(), + ), + Positioned.fill( + top: null, + child: TextFieldTapRegion( + child: _PromptBottomActions( + textController: textController, + overlayController: overlayController, + focusNode: focusNode, + sendButtonState: sendButtonState, + onSendPressed: handleSendPressed, + onStopStreaming: widget.onStopStreaming, + ), + ), + ), + ], + ), + ], + ), ), - ], + ), ), ); } + void cancelMentionPage() { + if (overlayController.isShowing) { + inputControlCubit.reset(); + overlayController.hide(); + } + } + void updateSendButtonState() { if (widget.isStreaming) { sendButtonState = SendButtonState.streaming; - } else if (_textController.text.trim().isEmpty) { + } else if (textController.text.trim().isEmpty) { sendButtonState = SendButtonState.disabled; } else { sendButtonState = SendButtonState.enabled; } } - void _handleSendPressed() { - final trimmedText = _textController.text.trim(); - _textController.clear(); + void handleSendPressed() { + final trimmedText = inputControlCubit.formatIntputText( + textController.text.trim(), + ); + textController.clear(); if (trimmedText.isEmpty) { return; } - // consume metadata - final ChatInputMentionMetadata mentionPageMetadata = - _inputActionControl.consumeMetaData(); - final ChatInputFileMetadata fileMetadata = - context.read().consumeMetadata(); - // combine metadata - final Map metadata = {} - ..addAll(mentionPageMetadata) - ..addAll(fileMetadata); + // get the attached files and mentioned pages + final metadata = context.read().consumeMetadata(); widget.onSubmitted(trimmedText, metadata); } - void _handleTextControllerChange() { - if (_textController.value.isComposingRangeValid) { + void handleTextControllerChanged() { + if (!textController.value.composing.isCollapsed) { return; } + + // update whether send button is clickable setState(() => updateSendButtonState()); + + // handle text and selection changes ONLY when mentioning a page + if (!overlayController.isShowing || + inputControlCubit.filterStartPosition == -1) { + return; + } + + // handle cases where mention a page is cancelled + final textSelection = textController.value.selection; + final isSelectingMultipleCharacters = !textSelection.isCollapsed; + final isCaretBeforeStartOfRange = + textSelection.baseOffset < inputControlCubit.filterStartPosition; + final isCaretAfterEndOfRange = + textSelection.baseOffset > inputControlCubit.filterEndPosition; + final isTextSame = inputControlCubit.inputText == textController.text; + + if (isSelectingMultipleCharacters || + isTextSame && (isCaretBeforeStartOfRange || isCaretAfterEndOfRange)) { + cancelMentionPage(); + return; + } + + final previousLength = inputControlCubit.inputText.characters.length; + final currentLength = textController.text.characters.length; + + // delete "@" + if (previousLength != currentLength && isCaretBeforeStartOfRange) { + cancelMentionPage(); + return; + } + + // handle cases where mention the filter is updated + if (previousLength != currentLength) { + final diff = currentLength - previousLength; + final newEndPosition = inputControlCubit.filterEndPosition + diff; + final newFilter = textController.text.substring( + inputControlCubit.filterStartPosition, + newEndPosition, + ); + inputControlCubit.updateFilter( + textController.text, + newFilter, + newEndPosition: newEndPosition, + ); + } else if (!isTextSame) { + final newFilter = textController.text.substring( + inputControlCubit.filterStartPosition, + inputControlCubit.filterEndPosition, + ); + inputControlCubit.updateFilter(textController.text, newFilter); + } } - Widget _inputTextField(BuildContext context) { - return CompositedTransformTarget( - link: _layerLink, - child: BlocBuilder( - builder: (context, state) { - return ExtendedTextField( - key: _textFieldKey, - controller: _textController, - focusNode: _inputFocusNode, - decoration: InputDecoration( - border: InputBorder.none, - enabledBorder: InputBorder.none, - focusedBorder: InputBorder.none, - contentPadding: DesktopAIPromptSizes.textFieldContentPadding.add( - const EdgeInsets.only( - bottom: DesktopAIPromptSizes.actionBarHeight, - ), - ), + void handlePageSelected(ViewPB view) { + final newText = textController.text.replaceRange( + inputControlCubit.filterStartPosition, + inputControlCubit.filterEndPosition, + view.id, + ); + textController.value = TextEditingValue( + text: newText, + selection: TextSelection.collapsed( + offset: inputControlCubit.filterStartPosition + view.id.length, + affinity: TextAffinity.upstream, + ), + ); + + inputControlCubit.selectPage(view); + overlayController.hide(); + } + + Widget inputTextField() { + return Actions( + actions: buildActions(), + child: CompositedTransformTarget( + link: layerLink, + child: BlocBuilder( + builder: (context, state) { + return _PromptTextField( + key: textFieldKey, + cubit: inputControlCubit, + textController: textController, + textFieldFocusNode: focusNode, hintText: switch (state.aiType) { AIType.appflowyAI => LocaleKeys.chat_inputMessageHint.tr(), AIType.localAI => LocaleKeys.chat_inputLocalAIMessageHint.tr() }, - hintStyle: Theme.of(context) - .textTheme - .bodyMedium - ?.copyWith(color: Theme.of(context).hintColor), - isCollapsed: true, - isDense: true, + onStartMentioningPage: () { + WidgetsBinding.instance.addPostFrameCallback((_) { + inputControlCubit.startSearching(textController.value); + overlayController.show(); + }); + }, + ); + }, + ), + ), + ); + } + + Map> buildActions() { + return { + _FocusPreviousItemIntent: CallbackAction<_FocusPreviousItemIntent>( + onInvoke: (intent) { + inputControlCubit.updateSelectionUp(); + return; + }, + ), + _FocusNextItemIntent: CallbackAction<_FocusNextItemIntent>( + onInvoke: (intent) { + inputControlCubit.updateSelectionDown(); + return; + }, + ), + _CancelMentionPageIntent: CallbackAction<_CancelMentionPageIntent>( + onInvoke: (intent) { + cancelMentionPage(); + return; + }, + ), + _SubmitOrMentionPageIntent: CallbackAction<_SubmitOrMentionPageIntent>( + onInvoke: (intent) { + if (overlayController.isShowing) { + inputControlCubit.state.maybeWhen( + ready: (visibleViews, focusedViewIndex) { + if (focusedViewIndex != -1 && + focusedViewIndex < visibleViews.length) { + handlePageSelected(visibleViews[focusedViewIndex]); + } + }, + orElse: () {}, + ); + } else { + handleSendPressed(); + } + return; + }, + ), + }; + } +} + +class _PromptTextField extends StatefulWidget { + const _PromptTextField({ + super.key, + required this.cubit, + required this.textController, + required this.textFieldFocusNode, + required this.onStartMentioningPage, + this.hintText = "", + }); + + final ChatInputControlCubit cubit; + final TextEditingController textController; + final FocusNode textFieldFocusNode; + final void Function() onStartMentioningPage; + final String hintText; + + @override + State<_PromptTextField> createState() => _PromptTextFieldState(); +} + +class _PromptTextFieldState extends State<_PromptTextField> { + bool isComposing = false; + + @override + void initState() { + super.initState(); + widget.textFieldFocusNode.onKeyEvent = handleKeyEvent; + widget.textController.addListener(onTextChanged); + } + + @override + void dispose() { + widget.textFieldFocusNode.onKeyEvent = null; + widget.textController.removeListener(onTextChanged); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Shortcuts( + shortcuts: buildShortcuts(), + child: ExtendedTextField( + controller: widget.textController, + focusNode: widget.textFieldFocusNode, + decoration: InputDecoration( + border: InputBorder.none, + enabledBorder: InputBorder.none, + focusedBorder: InputBorder.none, + contentPadding: DesktopAIPromptSizes.textFieldContentPadding.add( + const EdgeInsets.only( + bottom: DesktopAIPromptSizes.actionBarHeight, ), - keyboardType: TextInputType.multiline, - textCapitalization: TextCapitalization.sentences, - minLines: 1, - maxLines: null, - style: Theme.of(context).textTheme.bodyMedium, - specialTextSpanBuilder: ChatInputTextSpanBuilder( - inputActionControl: _inputActionControl, - ), - onChanged: (text) { - _handleOnTextChange(context, text); - }, + ), + hintText: widget.hintText, + hintStyle: Theme.of(context) + .textTheme + .bodyMedium + ?.copyWith(color: Theme.of(context).hintColor), + isCollapsed: true, + isDense: true, + ), + keyboardType: TextInputType.multiline, + textCapitalization: TextCapitalization.sentences, + minLines: 1, + maxLines: null, + style: Theme.of(context).textTheme.bodyMedium, + specialTextSpanBuilder: ChatInputTextSpanBuilder( + inputControlCubit: widget.cubit, + specialTextStyle: Theme.of(context).textTheme.bodyMedium?.copyWith( + color: Theme.of(context).colorScheme.primary, + fontWeight: FontWeight.w600, + ), + ), + ), + ); + } + + KeyEventResult handleKeyEvent(FocusNode node, KeyEvent event) { + if (event.character == '@') { + widget.onStartMentioningPage(); + } + return KeyEventResult.ignored; + } + + void onTextChanged() { + setState( + () => isComposing = !widget.textController.value.composing.isCollapsed, + ); + } + + Map buildShortcuts() { + if (isComposing) { + return const {}; + } + + return const { + SingleActivator(LogicalKeyboardKey.arrowUp): _FocusPreviousItemIntent(), + SingleActivator(LogicalKeyboardKey.arrowDown): _FocusNextItemIntent(), + SingleActivator(LogicalKeyboardKey.escape): _CancelMentionPageIntent(), + SingleActivator(LogicalKeyboardKey.enter): _SubmitOrMentionPageIntent(), + }; + } +} + +class _SubmitOrMentionPageIntent extends Intent { + const _SubmitOrMentionPageIntent(); +} + +class _CancelMentionPageIntent extends Intent { + const _CancelMentionPageIntent(); +} + +class _FocusPreviousItemIntent extends Intent { + const _FocusPreviousItemIntent(); +} + +class _FocusNextItemIntent extends Intent { + const _FocusNextItemIntent(); +} + +class _PromptBottomActions extends StatelessWidget { + const _PromptBottomActions({ + required this.textController, + required this.overlayController, + required this.focusNode, + required this.sendButtonState, + required this.onSendPressed, + required this.onStopStreaming, + }); + + final TextEditingController textController; + final OverlayPortalController overlayController; + final FocusNode focusNode; + final SendButtonState sendButtonState; + final void Function() onSendPressed; + final void Function() onStopStreaming; + + @override + Widget build(BuildContext context) { + return Container( + height: DesktopAIPromptSizes.actionBarHeight, + padding: const EdgeInsets.symmetric(horizontal: 8), + child: BlocBuilder( + builder: (context, state) { + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // predefinedFormatButton(), + const Spacer(), + _mentionButton(context), + const HSpace( + DesktopAIPromptSizes.actionBarButtonSpacing, + ), + if (UniversalPlatform.isDesktop && state.supportChatWithFile) ...[ + _attachmentButton(context), + const HSpace( + DesktopAIPromptSizes.actionBarButtonSpacing, + ), + ], + _sendButton(), + ], ); }, ), ); } - Future _handleOnTextChange(BuildContext context, String text) async { - if (!_inputActionControl.onTextChanged(text)) { - return; - } - - ChatActionsMenu( - anchor: ChatInputAnchor( - anchorKey: _textFieldKey, - layerLink: _layerLink, - ), - handler: _inputActionControl, - context: context, - style: Theme.of(context).brightness == Brightness.dark - ? const ChatActionsMenuStyle.dark() - : const ChatActionsMenuStyle.light(), - ).show(); - } - - Widget _mentionButton() { + Widget _mentionButton(BuildContext context) { return PromptInputMentionButton( iconSize: DesktopAIPromptSizes.actionBarIconSize, buttonSize: DesktopAIPromptSizes.actionBarButtonSize, onTap: () { - _textController.text += '@'; - if (!_inputFocusNode.hasFocus) { - _inputFocusNode.requestFocus(); + if (!focusNode.hasFocus) { + focusNode.requestFocus(); } - _handleOnTextChange(context, _textController.text); + textController.text += '@'; + Future.delayed(Duration.zero, () { + context + .read() + .startSearching(textController.value); + overlayController.show(); + }); }, ); } - Widget _attachmentButton() { + Widget _attachmentButton(BuildContext context) { return PromptInputAttachmentButton( onTap: () async { final path = await getIt().pickFiles( @@ -308,12 +547,10 @@ class _DesktopAIPromptInputState extends State { } for (final file in path.files) { - if (file.path != null) { - if (mounted) { - context - .read() - .add(AIPromptInputEvent.newFile(file.path!, file.name)); - } + if (file.path != null && context.mounted) { + context + .read() + .add(AIPromptInputEvent.attachFile(file.path!, file.name)); } } }, @@ -325,56 +562,8 @@ class _DesktopAIPromptInputState extends State { buttonSize: DesktopAIPromptSizes.actionBarButtonSize, iconSize: DesktopAIPromptSizes.sendButtonSize, state: sendButtonState, - onSendPressed: _handleSendPressed, - onStopStreaming: widget.onStopStreaming, + onSendPressed: onSendPressed, + onStopStreaming: onStopStreaming, ); } } - -class ChatInputAnchor extends ChatAnchor { - ChatInputAnchor({ - required this.anchorKey, - required this.layerLink, - }); - - @override - final GlobalKey> anchorKey; - - @override - final LayerLink layerLink; -} - -/// Handles the key press event for the Enter key without Shift. -/// -/// This function checks if the Enter key is pressed without either of the Shift keys. -/// If the conditions are met, it performs the action of sending a message if the -/// text controller is not in a composing range and if the event is a key down event. -/// -/// - Returns: A `KeyEventResult` indicating whether the key event was handled or ignored. -KeyEventResult _handleEnterKeyWithoutShift( - KeyEvent event, - TextEditingController textController, - bool isStreaming, - void Function() handleSendPressed, -) { - if (event.physicalKey == PhysicalKeyboardKey.enter && - !HardwareKeyboard.instance.physicalKeysPressed.any( - (el) => { - PhysicalKeyboardKey.shiftLeft, - PhysicalKeyboardKey.shiftRight, - }.contains(el), - )) { - if (textController.value.isComposingRangeValid) { - return KeyEventResult.ignored; - } - - if (event is KeyDownEvent) { - if (!isStreaming) { - handleSendPressed(); - } - } - return KeyEventResult.handled; - } else { - return KeyEventResult.ignored; - } -} diff --git a/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_input/mobile_ai_prompt_input.dart b/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_input/mobile_ai_prompt_input.dart index 3adb427350..d42b247e84 100644 --- a/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_input/mobile_ai_prompt_input.dart +++ b/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_input/mobile_ai_prompt_input.dart @@ -1,10 +1,6 @@ import 'package:appflowy/generated/locale_keys.g.dart'; -import 'package:appflowy/plugins/ai_chat/application/chat_entity.dart'; import 'package:appflowy/plugins/ai_chat/application/ai_prompt_input_bloc.dart'; -import 'package:appflowy/plugins/ai_chat/application/chat_input_action_bloc.dart'; -import 'package:appflowy/plugins/ai_chat/application/chat_input_action_control.dart'; -import 'package:appflowy/plugins/ai_chat/presentation/chat_input/chat_input_file.dart'; -import 'package:appflowy/plugins/ai_chat/presentation/chat_input_action_menu.dart'; +import 'package:appflowy/plugins/ai_chat/application/chat_input_control_cubit.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/mention/mobile_page_selector_sheet.dart'; import 'package:appflowy/workspace/application/view/view_ext.dart'; import 'package:easy_localization/easy_localization.dart'; @@ -13,9 +9,10 @@ import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'ai_prompt_buttons.dart'; -import 'chat_input_span.dart'; import '../layout_define.dart'; +import 'ai_prompt_buttons.dart'; +import 'chat_input_file.dart'; +import 'chat_input_span.dart'; class MobileAIPromptInput extends StatefulWidget { const MobileAIPromptInput({ @@ -36,9 +33,9 @@ class MobileAIPromptInput extends StatefulWidget { } class _MobileAIPromptInputState extends State { - late final ChatInputActionControl _inputActionControl; - late final FocusNode _inputFocusNode; - late final TextEditingController _textController; + final inputControlCubit = ChatInputControlCubit(); + final focusNode = FocusNode(); + final textController = TextEditingController(); late SendButtonState sendButtonState; @@ -46,20 +43,13 @@ class _MobileAIPromptInputState extends State { void initState() { super.initState(); - _textController = TextEditingController() - ..addListener(_handleTextControllerChange); + textController.addListener(handleTextControllerChange); + focusNode.onKeyEvent = handleKeyEvent; - _inputFocusNode = FocusNode(); WidgetsBinding.instance.addPostFrameCallback((_) { - _inputFocusNode.requestFocus(); + focusNode.requestFocus(); }); - _inputActionControl = ChatInputActionControl( - chatId: widget.chatId, - textController: _textController, - textFieldFocusNode: _inputFocusNode, - ); - updateSendButtonState(); } @@ -71,9 +61,9 @@ class _MobileAIPromptInputState extends State { @override void dispose() { - _inputFocusNode.dispose(); - _textController.dispose(); - _inputActionControl.dispose(); + focusNode.dispose(); + textController.dispose(); + inputControlCubit.close(); super.dispose(); } @@ -81,55 +71,71 @@ class _MobileAIPromptInputState extends State { Widget build(BuildContext context) { return Hero( tag: "ai_chat_prompt", - child: DecoratedBox( - decoration: BoxDecoration( - border: Border( - top: BorderSide(color: Theme.of(context).colorScheme.outline), - ), - color: Theme.of(context).colorScheme.surface, - boxShadow: const [ - BoxShadow( - blurRadius: 4.0, - offset: Offset(0, -2), - color: Color.fromRGBO(0, 0, 0, 0.05), - ), - ], - borderRadius: MobileAIPromptSizes.promptFrameRadius, - ), - child: Column( - children: [ - ConstrainedBox( - constraints: BoxConstraints( - maxHeight: - MobileAIPromptSizes.attachedFilesBarPadding.vertical + - MobileAIPromptSizes.attachedFilesPreviewHeight, + child: BlocProvider.value( + value: inputControlCubit, + child: BlocListener( + listener: (context, state) { + state.maybeWhen( + updateSelectedViews: (selectedViews) { + context.read().add( + AIPromptInputEvent.updateMentionedViews(selectedViews), + ); + }, + orElse: () {}, + ); + }, + child: DecoratedBox( + decoration: BoxDecoration( + border: Border( + top: BorderSide(color: Theme.of(context).colorScheme.outline), ), - child: ChatInputFile( - chatId: widget.chatId, - onDeleted: (file) => context - .read() - .add(AIPromptInputEvent.deleteFile(file)), - ), - ), - Container( - constraints: const BoxConstraints( - minHeight: MobileAIPromptSizes.textFieldMinHeight, - maxHeight: 220, - ), - padding: const EdgeInsetsDirectional.fromSTEB(0, 8.0, 12.0, 8.0), - child: IntrinsicHeight( - child: Row( - children: [ - const HSpace(8.0), - Expanded(child: _inputTextField(context)), - _mentionButton(), - const HSpace(6.0), - _sendButton(), - ], + color: Theme.of(context).colorScheme.surface, + boxShadow: const [ + BoxShadow( + blurRadius: 4.0, + offset: Offset(0, -2), + color: Color.fromRGBO(0, 0, 0, 0.05), ), - ), + ], + borderRadius: MobileAIPromptSizes.promptFrameRadius, ), - ], + child: Column( + children: [ + ConstrainedBox( + constraints: BoxConstraints( + maxHeight: + MobileAIPromptSizes.attachedFilesBarPadding.vertical + + MobileAIPromptSizes.attachedFilesPreviewHeight, + ), + child: ChatInputFile( + chatId: widget.chatId, + onDeleted: (file) => context + .read() + .add(AIPromptInputEvent.removeFile(file)), + ), + ), + Container( + constraints: const BoxConstraints( + minHeight: MobileAIPromptSizes.textFieldMinHeight, + maxHeight: 220, + ), + padding: + const EdgeInsetsDirectional.fromSTEB(0, 8.0, 12.0, 8.0), + child: IntrinsicHeight( + child: Row( + children: [ + const HSpace(8.0), + Expanded(child: inputTextField(context)), + mentionButton(), + const HSpace(6.0), + sendButton(), + ], + ), + ), + ), + ], + ), + ), ), ), ); @@ -138,138 +144,149 @@ class _MobileAIPromptInputState extends State { void updateSendButtonState() { if (widget.isStreaming) { sendButtonState = SendButtonState.streaming; - } else if (_textController.text.trim().isEmpty) { + } else if (textController.text.trim().isEmpty) { sendButtonState = SendButtonState.disabled; } else { sendButtonState = SendButtonState.enabled; } } - void _handleSendPressed() { - final trimmedText = _textController.text.trim(); - _textController.clear(); + void handleSendPressed() { + final trimmedText = inputControlCubit.formatIntputText( + textController.text.trim(), + ); + textController.clear(); if (trimmedText.isEmpty) { return; } - // consume metadata - final ChatInputMentionMetadata mentionPageMetadata = - _inputActionControl.consumeMetaData(); - final ChatInputFileMetadata fileMetadata = - context.read().consumeMetadata(); - // combine metadata - final Map metadata = {} - ..addAll(mentionPageMetadata) - ..addAll(fileMetadata); + // get the attached files and mentioned pages + final metadata = context.read().consumeMetadata(); widget.onSubmitted(trimmedText, metadata); } - void _handleTextControllerChange() { - if (_textController.value.isComposingRangeValid) { + void handleTextControllerChange() { + if (textController.value.isComposingRangeValid) { return; } + inputControlCubit.updateInputText(textController.text); setState(() => updateSendButtonState()); } - Widget _inputTextField(BuildContext context) { + KeyEventResult handleKeyEvent(FocusNode node, KeyEvent event) { + if (event.character == '@') { + WidgetsBinding.instance.addPostFrameCallback((_) { + mentionPage(context); + }); + } + return KeyEventResult.ignored; + } + + Future mentionPage(BuildContext context) async { + // if the focus node is on focus, unfocus it for better animation + // otherwise, the page sheet animation will be blocked by the keyboard + inputControlCubit.refreshViews(); + inputControlCubit.startSearching(textController.value); + if (focusNode.hasFocus) { + focusNode.unfocus(); + await Future.delayed(const Duration(milliseconds: 100)); + } + + if (context.mounted) { + final selectedView = await showPageSelectorSheet( + context, + filter: (view) => + view.layout.isDocumentView && + view.parentViewId.isNotEmpty && + !inputControlCubit.selectedViewIds.contains(view.id), + ); + if (selectedView != null) { + final newText = textController.text.replaceRange( + inputControlCubit.filterStartPosition, + inputControlCubit.filterStartPosition, + selectedView.id, + ); + textController.value = TextEditingValue( + text: newText, + selection: TextSelection.collapsed( + offset: + textController.selection.baseOffset + selectedView.id.length, + affinity: TextAffinity.upstream, + ), + ); + + inputControlCubit.selectPage(selectedView); + } + focusNode.requestFocus(); + inputControlCubit.reset(); + } + } + + Widget inputTextField(BuildContext context) { return BlocBuilder( builder: (context, state) { return ExtendedTextField( - controller: _textController, - focusNode: _inputFocusNode, - decoration: _buildInputDecoration(state), + controller: textController, + focusNode: focusNode, + decoration: InputDecoration( + border: InputBorder.none, + enabledBorder: InputBorder.none, + focusedBorder: InputBorder.none, + contentPadding: MobileAIPromptSizes.textFieldContentPadding, + hintText: switch (state.aiType) { + AIType.appflowyAI => LocaleKeys.chat_inputMessageHint.tr(), + AIType.localAI => LocaleKeys.chat_inputLocalAIMessageHint.tr() + }, + isCollapsed: true, + isDense: true, + ), keyboardType: TextInputType.multiline, textCapitalization: TextCapitalization.sentences, minLines: 1, maxLines: null, style: Theme.of(context).textTheme.bodyMedium, specialTextSpanBuilder: ChatInputTextSpanBuilder( - inputActionControl: _inputActionControl, + inputControlCubit: inputControlCubit, + specialTextStyle: Theme.of(context).textTheme.bodyMedium?.copyWith( + color: Theme.of(context).colorScheme.primary, + fontWeight: FontWeight.w600, + ), ), - onChanged: (text) { - _handleOnTextChange(context, text); - }, ); }, ); } - InputDecoration _buildInputDecoration(AIPromptInputState state) { - return InputDecoration( - border: InputBorder.none, - enabledBorder: InputBorder.none, - focusedBorder: InputBorder.none, - contentPadding: MobileAIPromptSizes.textFieldContentPadding, - hintText: switch (state.aiType) { - AIType.appflowyAI => LocaleKeys.chat_inputMessageHint.tr(), - AIType.localAI => LocaleKeys.chat_inputLocalAIMessageHint.tr() - }, - isCollapsed: true, - isDense: true, - ); - } - - Future _handleOnTextChange(BuildContext context, String text) async { - if (!_inputActionControl.onTextChanged(text)) { - return; - } - - // if the focus node is on focus, unfocus it for better animation - // otherwise, the page sheet animation will be blocked by the keyboard - if (_inputFocusNode.hasFocus) { - _inputFocusNode.unfocus(); - Future.delayed(const Duration(milliseconds: 100), () async { - await _referPage(_inputActionControl); - }); - } else { - await _referPage(_inputActionControl); - } - } - - Widget _mentionButton() { + Widget mentionButton() { return Align( alignment: Alignment.bottomCenter, child: PromptInputMentionButton( iconSize: MobileAIPromptSizes.mentionIconSize, buttonSize: MobileAIPromptSizes.sendButtonSize, onTap: () { - _textController.text += '@'; - if (!_inputFocusNode.hasFocus) { - _inputFocusNode.requestFocus(); + textController.text += '@'; + if (!focusNode.hasFocus) { + focusNode.requestFocus(); } - _handleOnTextChange(context, _textController.text); + WidgetsBinding.instance.addPostFrameCallback((_) { + mentionPage(context); + }); }, ), ); } - Widget _sendButton() { + Widget sendButton() { return Align( alignment: Alignment.bottomCenter, child: PromptInputSendButton( buttonSize: MobileAIPromptSizes.sendButtonSize, iconSize: MobileAIPromptSizes.sendButtonSize, - onSendPressed: _handleSendPressed, + onSendPressed: handleSendPressed, onStopStreaming: widget.onStopStreaming, state: sendButtonState, ), ); } - - Future _referPage(ChatActionHandler handler) async { - handler.onEnter(); - final selectedView = await showPageSelectorSheet( - context, - filter: (view) => - view.layout.isDocumentView && - !view.isSpace && - view.parentViewId.isNotEmpty, - ); - if (selectedView != null) { - handler.onSelected(ViewActionPage(view: selectedView)); - } - handler.onExit(); - _inputFocusNode.requestFocus(); - } } diff --git a/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_input_action_menu.dart b/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_input_action_menu.dart deleted file mode 100644 index d23e966ecd..0000000000 --- a/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_input_action_menu.dart +++ /dev/null @@ -1,320 +0,0 @@ -import 'dart:math'; - -import 'package:appflowy/generated/locale_keys.g.dart'; -import 'package:appflowy/plugins/ai_chat/application/chat_input_action_bloc.dart'; -import 'package:appflowy/plugins/ai_chat/application/chat_input_action_control.dart'; -import 'package:easy_localization/easy_localization.dart'; -import 'package:flowy_infra_ui/flowy_infra_ui.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:scroll_to_index/scroll_to_index.dart'; - -abstract class ChatActionHandler { - void onEnter(); - void onSelected(ChatInputMention page); - void onExit(); - ChatInputActionBloc get commandBloc; - void onFilter(String filter); - double actionMenuOffsetX(); -} - -abstract class ChatAnchor { - GlobalKey get anchorKey; - LayerLink get layerLink; -} - -const int _itemHeight = 34; -const int _itemVerticalPadding = 4; -const int _noPageHeight = 20; - -class ChatActionsMenu { - ChatActionsMenu({ - required this.anchor, - required this.context, - required this.handler, - required this.style, - }); - - final BuildContext context; - final ChatAnchor anchor; - final ChatActionsMenuStyle style; - final ChatActionHandler handler; - - OverlayEntry? _overlayEntry; - - void dismiss() { - _overlayEntry?.remove(); - _overlayEntry = null; - handler.onExit(); - } - - void show() { - WidgetsBinding.instance.addPostFrameCallback((_) => _show()); - } - - void _show() { - if (_overlayEntry != null) { - dismiss(); - } - - if (anchor.anchorKey.currentContext == null) { - return; - } - - handler.onEnter(); - const double maxHeight = 300; - - _overlayEntry = OverlayEntry( - builder: (context) => BlocProvider.value( - value: handler.commandBloc, - child: BlocBuilder( - builder: (context, state) { - final height = min( - max( - state.pages.length * (_itemHeight + _itemVerticalPadding), - _noPageHeight, - ), - maxHeight, - ); - final isLoading = - state.indicator == const ChatActionMenuIndicator.loading(); - - return Stack( - children: [ - CompositedTransformFollower( - link: anchor.layerLink, - showWhenUnlinked: false, - offset: Offset(handler.actionMenuOffsetX(), -height - 4), - child: Material( - elevation: 4.0, - child: ConstrainedBox( - constraints: const BoxConstraints( - minWidth: 200, - maxWidth: 200, - maxHeight: maxHeight, - ), - child: DecoratedBox( - decoration: BoxDecoration( - color: Theme.of(context) - .colorScheme - .surfaceContainerHighest, - borderRadius: BorderRadius.circular(6.0), - ), - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 2, - vertical: 2, - ), - child: ActionList( - isLoading: isLoading, - handler: handler, - onDismiss: () => dismiss(), - pages: state.pages, - ), - ), - ), - ), - ), - ), - ], - ); - }, - ), - ), - ); - - Overlay.of(context).insert(_overlayEntry!); - } -} - -class _ActionItem extends StatelessWidget { - const _ActionItem({ - required this.item, - required this.onTap, - required this.isSelected, - }); - - final ChatInputMention item; - final VoidCallback? onTap; - final bool isSelected; - - @override - Widget build(BuildContext context) { - return Container( - height: _itemHeight.toDouble(), - padding: const EdgeInsets.symmetric(vertical: _itemVerticalPadding / 2.0), - decoration: BoxDecoration( - color: isSelected - ? Theme.of(context).colorScheme.primary.withOpacity(0.1) - : Colors.transparent, - borderRadius: BorderRadius.circular(4.0), - ), - child: FlowyTooltip( - message: item.title, - child: FlowyButton( - leftIcon: item.icon, - margin: const EdgeInsets.symmetric(horizontal: 6), - iconPadding: 10.0, - text: FlowyText( - item.title.isEmpty - ? LocaleKeys.document_title_placeholder.tr() - : item.title, - lineHeight: 1.0, - overflow: TextOverflow.ellipsis, - ), - onTap: onTap, - ), - ), - ); - } -} - -class ActionList extends StatefulWidget { - const ActionList({ - super.key, - required this.handler, - required this.onDismiss, - required this.pages, - required this.isLoading, - }); - - final ChatActionHandler handler; - final VoidCallback? onDismiss; - final List pages; - final bool isLoading; - - @override - State createState() => _ActionListState(); -} - -class _ActionListState extends State { - int _selectedIndex = 0; - final _scrollController = AutoScrollController(); - - @override - void dispose() { - _scrollController.dispose(); - super.dispose(); - } - - KeyEventResult _handleKeyPress(logicalKey) { - bool isHandle = false; - setState(() { - if (logicalKey == PhysicalKeyboardKey.arrowDown) { - _selectedIndex = (_selectedIndex + 1) % widget.pages.length; - _scrollToSelectedIndex(); - isHandle = true; - } else if (logicalKey == PhysicalKeyboardKey.arrowUp) { - _selectedIndex = - (_selectedIndex - 1 + widget.pages.length) % widget.pages.length; - _scrollToSelectedIndex(); - isHandle = true; - } else if (logicalKey == PhysicalKeyboardKey.enter) { - widget.handler.onSelected(widget.pages[_selectedIndex]); - widget.onDismiss?.call(); - isHandle = true; - } else if (logicalKey == PhysicalKeyboardKey.escape) { - widget.onDismiss?.call(); - isHandle = true; - } - }); - return isHandle ? KeyEventResult.handled : KeyEventResult.ignored; - } - - @override - Widget build(BuildContext context) { - return BlocListener( - listenWhen: (previous, current) => - previous.keyboardKey != current.keyboardKey, - listener: (context, state) { - if (state.keyboardKey != null) { - _handleKeyPress(state.keyboardKey!.physicalKey); - } - }, - child: ListView( - shrinkWrap: true, - controller: _scrollController, - padding: const EdgeInsets.all(4), - children: _buildPages(), - ), - ); - } - - List _buildPages() { - if (widget.isLoading) { - return [ - SizedBox( - height: _noPageHeight.toDouble(), - child: const Center(child: CircularProgressIndicator.adaptive()), - ), - ]; - } - - if (widget.pages.isEmpty) { - return [ - SizedBox( - height: _noPageHeight.toDouble(), - child: - Center(child: FlowyText(LocaleKeys.chat_inputActionNoPages.tr())), - ), - ]; - } - - return widget.pages.asMap().entries.map((entry) { - final index = entry.key; - final ChatInputMention item = entry.value; - return AutoScrollTag( - key: ValueKey(item.pageId), - index: index, - controller: _scrollController, - child: _ActionItem( - item: item, - onTap: () { - widget.handler.onSelected(item); - widget.onDismiss?.call(); - }, - isSelected: _selectedIndex == index, - ), - ); - }).toList(); - } - - void _scrollToSelectedIndex() { - _scrollController.scrollToIndex( - _selectedIndex, - duration: const Duration(milliseconds: 200), - preferPosition: AutoScrollPosition.begin, - ); - } -} - -class ChatActionsMenuStyle { - ChatActionsMenuStyle({ - required this.backgroundColor, - required this.groupTextColor, - required this.menuItemTextColor, - required this.menuItemSelectedColor, - required this.menuItemSelectedTextColor, - }); - - const ChatActionsMenuStyle.light() - : backgroundColor = Colors.white, - groupTextColor = const Color(0xFF555555), - menuItemTextColor = const Color(0xFF333333), - menuItemSelectedColor = const Color(0xFFE0F8FF), - menuItemSelectedTextColor = const Color.fromARGB(255, 56, 91, 247); - - const ChatActionsMenuStyle.dark() - : backgroundColor = const Color(0xFF282E3A), - groupTextColor = const Color(0xFFBBC3CD), - menuItemTextColor = const Color(0xFFBBC3CD), - menuItemSelectedColor = const Color(0xFF00BCF0), - menuItemSelectedTextColor = const Color(0xFF131720); - - final Color backgroundColor; - final Color groupTextColor; - final Color menuItemTextColor; - final Color menuItemSelectedColor; - final Color menuItemSelectedTextColor; -} diff --git a/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/message/ai_text_message.dart b/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/message/ai_text_message.dart index 69b7ec102a..1b58869688 100644 --- a/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/message/ai_text_message.dart +++ b/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/message/ai_text_message.dart @@ -9,11 +9,11 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_chat_core/flutter_chat_core.dart'; -import '../chat_loading.dart'; import '../layout_define.dart'; import 'ai_markdown_text.dart'; import 'ai_message_bubble.dart'; import 'ai_metadata.dart'; +import 'loading_indicator.dart'; import 'error_text_message.dart'; /// [ChatAIMessageWidget] includes both the text of the AI response as well as @@ -57,20 +57,23 @@ class ChatAIMessageWidget extends StatelessWidget { ), child: BlocBuilder( builder: (context, state) { + final loadingText = + state.progress?.step ?? LocaleKeys.chat_generatingResponse.tr(); + return Padding( padding: AIChatUILayout.messageMargin, child: state.messageState.when( loading: () => ChatAIMessageBubble( message: message, showActions: false, - child: const ChatAILoading(), + child: ChatAILoading(text: loadingText), ), ready: () { return state.text.isEmpty ? ChatAIMessageBubble( message: message, showActions: false, - child: const ChatAILoading(), + child: ChatAILoading(text: loadingText), ) : ChatAIMessageBubble( message: message, diff --git a/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_loading.dart b/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/message/loading_indicator.dart similarity index 79% rename from frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_loading.dart rename to frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/message/loading_indicator.dart index ee73e85b7d..be7bbf7ac9 100644 --- a/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_loading.dart +++ b/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/message/loading_indicator.dart @@ -1,18 +1,16 @@ -import 'package:appflowy/generated/locale_keys.g.dart'; -import 'package:appflowy/plugins/ai_chat/application/chat_ai_message_bloc.dart'; -import 'package:easy_localization/easy_localization.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flutter/material.dart'; import 'package:flutter_animate/flutter_animate.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; /// An animated generating indicator for an AI response class ChatAILoading extends StatelessWidget { const ChatAILoading({ super.key, + this.text = "", this.duration = const Duration(seconds: 1), }); + final String text; final Duration duration; @override @@ -27,14 +25,9 @@ class ChatAILoading extends StatelessWidget { children: [ Padding( padding: const EdgeInsetsDirectional.only(end: 4.0), - child: BlocBuilder( - builder: (context, state) { - return FlowyText( - state.progress?.step ?? - LocaleKeys.chat_generatingResponse.tr(), - color: Theme.of(context).hintColor, - ); - }, + child: FlowyText( + text, + color: Theme.of(context).hintColor, ), ), buildDot(const Color(0xFF9327FF)) From 0cf3ade332e64df953a45878dd02a35844364610 Mon Sep 17 00:00:00 2001 From: Richard Shiue <71320345+richardshiue@users.noreply.github.com> Date: Tue, 3 Dec 2024 22:20:30 +0800 Subject: [PATCH 004/576] fix(desktop): resize sidebar menu regression (#6897) --- .../lib/workspace/application/home/home_setting_bloc.dart | 2 +- .../lib/workspace/presentation/home/home_layout.dart | 7 ++++--- .../appflowy_flutter/packages/flowy_infra/lib/size.dart | 2 -- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/frontend/appflowy_flutter/lib/workspace/application/home/home_setting_bloc.dart b/frontend/appflowy_flutter/lib/workspace/application/home/home_setting_bloc.dart index 05418f3315..657f2592d7 100644 --- a/frontend/appflowy_flutter/lib/workspace/application/home/home_setting_bloc.dart +++ b/frontend/appflowy_flutter/lib/workspace/application/home/home_setting_bloc.dart @@ -86,7 +86,7 @@ class HomeSettingBloc extends Bloc { }, editPanelResized: (_EditPanelResized e) { final newPosition = - (e.offset + state.resizeStart).clamp(-50, 200).toDouble(); + (state.resizeStart + e.offset).clamp(0, 200).toDouble(); if (state.resizeOffset != newPosition) { emit(state.copyWith(resizeOffset: newPosition)); } diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/home/home_layout.dart b/frontend/appflowy_flutter/lib/workspace/presentation/home/home_layout.dart index fb108b702e..98139f1db7 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/home/home_layout.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/home/home_layout.dart @@ -14,10 +14,11 @@ class HomeLayout { HomeLayout(BuildContext context) { final homeSetting = context.read().state; showEditPanel = homeSetting.panelContext != null; - menuWidth = Sizes.sideBarWidth; - menuWidth += homeSetting.resizeOffset; - menuWidth = max(menuWidth, HomeSizes.minimumSidebarWidth); + menuWidth = max( + HomeSizes.minimumSidebarWidth + homeSetting.resizeOffset, + HomeSizes.minimumSidebarWidth, + ); final screenWidthPx = context.widthPx; context diff --git a/frontend/appflowy_flutter/packages/flowy_infra/lib/size.dart b/frontend/appflowy_flutter/packages/flowy_infra/lib/size.dart index b9c6d565f8..f58dad95b5 100644 --- a/frontend/appflowy_flutter/packages/flowy_infra/lib/size.dart +++ b/frontend/appflowy_flutter/packages/flowy_infra/lib/size.dart @@ -57,8 +57,6 @@ class Sizes { static double get hit => 40 * hitScale; static double get iconMed => 20; - - static double get sideBarWidth => 250 * hitScale; } class Corners { From 03c84ff8b5eb7e6485e0ab3a47d0f576fdf167b1 Mon Sep 17 00:00:00 2001 From: Richard Shiue <71320345+richardshiue@users.noreply.github.com> Date: Tue, 3 Dec 2024 22:55:34 +0800 Subject: [PATCH 005/576] feat: open ai response url source in browser (#6917) --- .../lib/plugins/ai_chat/chat_page.dart | 29 ++++++++++++++++--- .../presentation/message/ai_metadata.dart | 6 +--- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/frontend/appflowy_flutter/lib/plugins/ai_chat/chat_page.dart b/frontend/appflowy_flutter/lib/plugins/ai_chat/chat_page.dart index 6767bf80db..144c378d70 100644 --- a/frontend/appflowy_flutter/lib/plugins/ai_chat/chat_page.dart +++ b/frontend/appflowy_flutter/lib/plugins/ai_chat/chat_page.dart @@ -1,9 +1,12 @@ +import 'dart:io'; + import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/plugins/ai_chat/application/chat_bloc.dart'; import 'package:appflowy/plugins/ai_chat/application/chat_entity.dart'; import 'package:appflowy/plugins/ai_chat/application/ai_prompt_input_bloc.dart'; import 'package:appflowy/plugins/ai_chat/application/chat_message_stream.dart'; import 'package:appflowy/plugins/ai_chat/presentation/chat_related_question.dart'; +import 'package:appflowy_backend/log.dart'; import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart'; import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart'; import 'package:desktop_drop/desktop_drop.dart'; @@ -14,8 +17,10 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_chat_core/flutter_chat_core.dart'; import 'package:flutter_chat_ui/flutter_chat_ui.dart' hide ChatAnimatedListReversed; +import 'package:string_validator/string_validator.dart'; import 'package:styled_widget/styled_widget.dart'; import 'package:universal_platform/universal_platform.dart'; +import 'package:url_launcher/url_launcher.dart'; import 'application/chat_member_bloc.dart'; import 'application/chat_side_panel_bloc.dart'; @@ -273,10 +278,26 @@ class _ChatContentPage extends StatelessWidget { chatId: view.id, refSourceJsonString: refSourceJsonString, isLastMessage: isLastMessage, - onSelectedMetadata: (metadata) { - context - .read() - .add(ChatSidePanelEvent.selectedMetadata(metadata)); + onSelectedMetadata: (metadata) async { + if (isURL(metadata.name)) { + late Uri uri; + + try { + uri = Uri.parse(metadata.name); + // `Uri` identifies `localhost` as a scheme + if (!uri.hasScheme || uri.scheme == 'localhost') { + uri = Uri.parse("http://${metadata.name}"); + await InternetAddress.lookup(uri.host); + } + await launchUrl(uri); + } catch (err) { + Log.error("failed to open url $err"); + } + } else { + context + .read() + .add(ChatSidePanelEvent.selectedMetadata(metadata)); + } }, ); }, diff --git a/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/message/ai_metadata.dart b/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/message/ai_metadata.dart index 8b827cfbcb..d55be401f9 100644 --- a/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/message/ai_metadata.dart +++ b/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/message/ai_metadata.dart @@ -1,7 +1,6 @@ import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/plugins/ai_chat/application/chat_entity.dart'; -import 'package:appflowy/plugins/ai_chat/application/chat_message_service.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flowy_infra_ui/style_widget/button.dart'; import 'package:flowy_infra_ui/style_widget/text.dart'; @@ -43,6 +42,7 @@ class _AIMessageMetadataState extends State { child: FlowyButton( margin: const EdgeInsets.all(4.0), useIntrinsicWidth: true, + hoverColor: Colors.transparent, radius: BorderRadius.circular(8.0), text: FlowyText( LocaleKeys.chat_referenceSource.plural( @@ -87,11 +87,7 @@ class _AIMessageMetadataState extends State { size: const Size.square(16), color: Theme.of(context).hintColor, ), - disable: m.source != appflowySource, onTap: () { - if (m.source != appflowySource) { - return; - } widget.onSelectedMetadata?.call(m); }, ), From 5cf66172318eec172b565b2e6fc9af21c3fbb3b2 Mon Sep 17 00:00:00 2001 From: "Kilu.He" <108015703+qinluhe@users.noreply.github.com> Date: Wed, 4 Dec 2024 16:16:48 +0800 Subject: [PATCH 006/576] fix: simple table width (#6918) * fix: adjust min width * fix: adjust simple table font size * fix: do not need to run rust-ci and docker-ci when web codes have been changed --- .github/workflows/docker_ci.yml | 4 +-- .github/workflows/tauri2_ci.yaml | 1 + .../blocks/simple-table/SimpleTableCell.tsx | 6 ++--- .../blocks/simple-table/SimpleTableRow.tsx | 6 ++--- .../blocks/simple-table/simple-table.scss | 25 +++++++++++++++++-- 5 files changed, 32 insertions(+), 10 deletions(-) diff --git a/.github/workflows/docker_ci.yml b/.github/workflows/docker_ci.yml index e38ac4e671..51e8a2ac28 100644 --- a/.github/workflows/docker_ci.yml +++ b/.github/workflows/docker_ci.yml @@ -2,9 +2,9 @@ name: Docker-CI on: push: - branches: ["main", "release/*"] + branches: [ "main", "release/*" ] pull_request: - branches: ["main", "release/*"] + branches: [ "main", "release/*" ] workflow_dispatch: concurrency: diff --git a/.github/workflows/tauri2_ci.yaml b/.github/workflows/tauri2_ci.yaml index 1191b2a354..aeefaa5fe6 100644 --- a/.github/workflows/tauri2_ci.yaml +++ b/.github/workflows/tauri2_ci.yaml @@ -5,6 +5,7 @@ on: paths: - ".github/workflows/tauri2_ci.yaml" - "frontend/rust-lib/**" + paths-ignore: - "frontend/appflowy_web_app/**" - "frontend/resources/**" diff --git a/frontend/appflowy_web_app/src/components/editor/components/blocks/simple-table/SimpleTableCell.tsx b/frontend/appflowy_web_app/src/components/editor/components/blocks/simple-table/SimpleTableCell.tsx index baa0c15f82..29c3ea2379 100644 --- a/frontend/appflowy_web_app/src/components/editor/components/blocks/simple-table/SimpleTableCell.tsx +++ b/frontend/appflowy_web_app/src/components/editor/components/blocks/simple-table/SimpleTableCell.tsx @@ -1,4 +1,4 @@ -import { BlockType, TableAlignType } from '@/application/types'; +import { BlockType } from '@/application/types'; import { EditorElementProps, SimpleTableCellBlockNode, SimpleTableNode } from '@/components/editor/editor.type'; import { renderColor } from '@/utils/color'; import React, { forwardRef, useMemo } from 'react'; @@ -51,8 +51,8 @@ const SimpleTableCell = const { horizontalAlign, bgColor } = useMemo(() => { if (!table || !row) return { - bgColor: '', - horizontalAlign: TableAlignType.Left, + bgColor: undefined, + horizontalAlign: undefined, }; const [parentElement] = table; diff --git a/frontend/appflowy_web_app/src/components/editor/components/blocks/simple-table/SimpleTableRow.tsx b/frontend/appflowy_web_app/src/components/editor/components/blocks/simple-table/SimpleTableRow.tsx index b60c578cdc..25c0ee614f 100644 --- a/frontend/appflowy_web_app/src/components/editor/components/blocks/simple-table/SimpleTableRow.tsx +++ b/frontend/appflowy_web_app/src/components/editor/components/blocks/simple-table/SimpleTableRow.tsx @@ -1,4 +1,4 @@ -import { BlockType, TableAlignType } from '@/application/types'; +import { BlockType } from '@/application/types'; import { EditorElementProps, SimpleTableNode, SimpleTableRowNode } from '@/components/editor/editor.type'; import { renderColor } from '@/utils/color'; import React, { forwardRef, useMemo } from 'react'; @@ -38,8 +38,8 @@ const SimpleTableRow = const { align, bgColor } = useMemo(() => { if (!parent) return { - align: TableAlignType.Left, - bgColor: '', + align: undefined, + bgColor: undefined, }; const [parentElement] = parent; diff --git a/frontend/appflowy_web_app/src/components/editor/components/blocks/simple-table/simple-table.scss b/frontend/appflowy_web_app/src/components/editor/components/blocks/simple-table/simple-table.scss index 6457c94842..c992bf6d4a 100644 --- a/frontend/appflowy_web_app/src/components/editor/components/blocks/simple-table/simple-table.scss +++ b/frontend/appflowy_web_app/src/components/editor/components/blocks/simple-table/simple-table.scss @@ -1,4 +1,6 @@ .simple-table { + font-size: 14px; + &.enable-header-column { tr [data-block-type="simple_table_cell"]:first-child { @apply font-semibold; @@ -32,6 +34,15 @@ } } + tr[data-table-row-horizontal-align="left"] { + .block-element { + > div > .text-element { + text-align: left; + justify-content: flex-start; + } + } + } + tr[data-table-row-horizontal-align="center"] { .block-element { > div > .text-element { @@ -50,6 +61,15 @@ } } + td[data-table-cell-horizontal-align="left"] { + .block-element { + > div > .text-element { + text-align: left; + justify-content: flex-start; + } + } + } + td[data-table-cell-horizontal-align="center"] { .block-element { @@ -69,11 +89,12 @@ } } + td .cell-children { - @apply min-w-[120px] leading-[1.8em]; + @apply leading-[1.7em]; } td { - @apply p-2; + @apply px-2 py-0.5; } } \ No newline at end of file From 92945cafdf02236f6ecf14ebd1f9b6d180904477 Mon Sep 17 00:00:00 2001 From: Richard Shiue <71320345+richardshiue@users.noreply.github.com> Date: Wed, 4 Dec 2024 20:53:03 +0800 Subject: [PATCH 007/576] fix: initial ai chat load (#6920) --- .../plugins/ai_chat/application/chat_bloc.dart | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/frontend/appflowy_flutter/lib/plugins/ai_chat/application/chat_bloc.dart b/frontend/appflowy_flutter/lib/plugins/ai_chat/application/chat_bloc.dart index 5c0cdabe67..09d7ed2417 100644 --- a/frontend/appflowy_flutter/lib/plugins/ai_chat/application/chat_bloc.dart +++ b/frontend/appflowy_flutter/lib/plugins/ai_chat/application/chat_bloc.dart @@ -59,6 +59,16 @@ class ChatBloc extends Bloc { AnswerStream? answerStream; int numSendMessage = 0; + /// a counter used to determine whether the initial loading state should be + /// set to finish. It should hit two before we emit: one for the local fetch + /// and another for the server fetch. + /// + /// This is to work around a bug where if an ai chat that is not yet on the + /// user local storage is opened but has messages in the server, it will + /// remain stuck on the welcome screen until the user switches to another page + /// then come back. + int initialFetchCounter = 0; + @override Future close() async { await answerStream?.dispose(); @@ -76,7 +86,11 @@ class ChatBloc extends Bloc { await chatController.insert(message, index: 0); } - if (state.loadingState.isLoading) { + if (initialFetchCounter < 2) { + initialFetchCounter++; + } + + if (state.loadingState.isLoading && initialFetchCounter >= 2) { emit( state.copyWith(loadingState: const ChatLoadingState.finish()), ); From dddf5aa195e9abe00a0812765a3fc485a1891455 Mon Sep 17 00:00:00 2001 From: "Nathan.fooo" <86001920+appflowy@users.noreply.github.com> Date: Thu, 5 Dec 2024 14:12:25 +0800 Subject: [PATCH 008/576] chore: move type option to collab repo (#6921) * chore: bump collab * chore: bump collab * chore: fix test compile * chore: fix test * chore: remove numeric * chore: fix media type option --- frontend/appflowy_tauri/src-tauri/Cargo.lock | 16 +- frontend/appflowy_tauri/src-tauri/Cargo.toml | 16 +- .../appflowy_web_app/src-tauri/Cargo.lock | 16 +- .../appflowy_web_app/src-tauri/Cargo.toml | 16 +- frontend/rust-lib/Cargo.lock | 16 +- frontend/rust-lib/Cargo.toml | 16 +- .../src/database_event.rs | 2 +- .../type_option_entities/relation_entities.rs | 2 +- .../timestamp_entities.rs | 1 + .../flowy-database2/src/event_handler.rs | 5 +- .../src/services/calculations/service.rs | 4 +- .../src/services/cell/cell_operation.rs | 26 ++-- .../src/services/database/database_editor.rs | 15 +- .../checkbox_type_option.rs | 21 +-- .../checklist_entities.rs | 143 ------------------ .../checklist_type_option/checklist_filter.rs | 87 ++++++++++- .../checklist_type_option.rs | 27 +--- .../type_options/checklist_type_option/mod.rs | 7 +- .../date_type_option/date_filter.rs | 26 +++- .../date_type_option/date_tests.rs | 2 +- .../date_type_option/date_type_option.rs | 19 +-- .../date_type_option_entities.rs | 33 ---- .../type_options/date_type_option/mod.rs | 7 +- .../media_type_option/media_type_option.rs | 24 +-- .../src/services/field/type_options/mod.rs | 2 +- .../number_type_option/number_type_option.rs | 65 +------- .../relation_type_option/relation.rs | 26 +--- .../relation_type_option/relation_entities.rs | 80 +--------- .../multi_select_type_option.rs | 12 +- .../select_type_option.rs | 18 +-- .../single_select_type_option.rs | 14 +- .../type_option_transform.rs | 2 +- .../type_options/summary_type_option/mod.rs | 1 - .../summary_type_option/summary.rs | 20 +-- .../summary_type_option/summary_entities.rs | 46 ------ .../text_type_option/text_type_option.rs | 18 +-- .../type_options/time_type_option/mod.rs | 2 - .../type_options/time_type_option/time.rs | 20 +-- .../time_type_option/time_entities.rs | 47 ------ .../type_options/timestamp_type_option/mod.rs | 3 - .../timestamp_type_option.rs | 19 +-- .../timestamp_type_option_entities.rs | 69 --------- .../type_options/translate_type_option/mod.rs | 1 - .../translate_type_option/translate.rs | 22 +-- .../translate_entities.rs | 46 ------ .../field/type_options/type_option.rs | 22 +-- .../field/type_options/type_option_cell.rs | 13 +- .../url_type_option/url_type_option.rs | 15 +- .../url_type_option_entities.rs | 7 - .../src/services/filter/controller.rs | 8 +- .../group/controller_impls/date_controller.rs | 3 +- .../src/services/share/csv/export.rs | 4 +- .../src/services/sort/controller.rs | 12 +- .../tests/database/cell_test/test.rs | 8 +- .../tests/database/database_editor.rs | 2 +- .../tests/database/field_test/util.rs | 1 + .../filter_test/checklist_filter_test.rs | 2 +- .../database/mock_data/board_mock_data.rs | 1 + .../database/mock_data/grid_mock_data.rs | 4 +- 59 files changed, 298 insertions(+), 884 deletions(-) delete mode 100644 frontend/rust-lib/flowy-database2/src/services/field/type_options/checklist_type_option/checklist_entities.rs delete mode 100644 frontend/rust-lib/flowy-database2/src/services/field/type_options/date_type_option/date_type_option_entities.rs delete mode 100644 frontend/rust-lib/flowy-database2/src/services/field/type_options/summary_type_option/summary_entities.rs delete mode 100644 frontend/rust-lib/flowy-database2/src/services/field/type_options/time_type_option/time_entities.rs delete mode 100644 frontend/rust-lib/flowy-database2/src/services/field/type_options/timestamp_type_option/timestamp_type_option_entities.rs delete mode 100644 frontend/rust-lib/flowy-database2/src/services/field/type_options/translate_type_option/translate_entities.rs diff --git a/frontend/appflowy_tauri/src-tauri/Cargo.lock b/frontend/appflowy_tauri/src-tauri/Cargo.lock index 21d8645b83..02e7f665ae 100644 --- a/frontend/appflowy_tauri/src-tauri/Cargo.lock +++ b/frontend/appflowy_tauri/src-tauri/Cargo.lock @@ -1030,7 +1030,7 @@ dependencies = [ [[package]] name = "collab" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0efc824a6e1a56e4485646e6428c07fdccf6e918#0efc824a6e1a56e4485646e6428c07fdccf6e918" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8bd376c0c71ce366d763a70385c7b445a98241ed#8bd376c0c71ce366d763a70385c7b445a98241ed" dependencies = [ "anyhow", "arc-swap", @@ -1055,7 +1055,7 @@ dependencies = [ [[package]] name = "collab-database" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0efc824a6e1a56e4485646e6428c07fdccf6e918#0efc824a6e1a56e4485646e6428c07fdccf6e918" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8bd376c0c71ce366d763a70385c7b445a98241ed#8bd376c0c71ce366d763a70385c7b445a98241ed" dependencies = [ "anyhow", "async-trait", @@ -1094,7 +1094,7 @@ dependencies = [ [[package]] name = "collab-document" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0efc824a6e1a56e4485646e6428c07fdccf6e918#0efc824a6e1a56e4485646e6428c07fdccf6e918" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8bd376c0c71ce366d763a70385c7b445a98241ed#8bd376c0c71ce366d763a70385c7b445a98241ed" dependencies = [ "anyhow", "arc-swap", @@ -1115,7 +1115,7 @@ dependencies = [ [[package]] name = "collab-entity" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0efc824a6e1a56e4485646e6428c07fdccf6e918#0efc824a6e1a56e4485646e6428c07fdccf6e918" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8bd376c0c71ce366d763a70385c7b445a98241ed#8bd376c0c71ce366d763a70385c7b445a98241ed" dependencies = [ "anyhow", "bytes", @@ -1135,7 +1135,7 @@ dependencies = [ [[package]] name = "collab-folder" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0efc824a6e1a56e4485646e6428c07fdccf6e918#0efc824a6e1a56e4485646e6428c07fdccf6e918" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8bd376c0c71ce366d763a70385c7b445a98241ed#8bd376c0c71ce366d763a70385c7b445a98241ed" dependencies = [ "anyhow", "arc-swap", @@ -1157,7 +1157,7 @@ dependencies = [ [[package]] name = "collab-importer" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0efc824a6e1a56e4485646e6428c07fdccf6e918#0efc824a6e1a56e4485646e6428c07fdccf6e918" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8bd376c0c71ce366d763a70385c7b445a98241ed#8bd376c0c71ce366d763a70385c7b445a98241ed" dependencies = [ "anyhow", "async-recursion", @@ -1218,7 +1218,7 @@ dependencies = [ [[package]] name = "collab-plugins" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0efc824a6e1a56e4485646e6428c07fdccf6e918#0efc824a6e1a56e4485646e6428c07fdccf6e918" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8bd376c0c71ce366d763a70385c7b445a98241ed#8bd376c0c71ce366d763a70385c7b445a98241ed" dependencies = [ "anyhow", "async-stream", @@ -1298,7 +1298,7 @@ dependencies = [ [[package]] name = "collab-user" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0efc824a6e1a56e4485646e6428c07fdccf6e918#0efc824a6e1a56e4485646e6428c07fdccf6e918" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8bd376c0c71ce366d763a70385c7b445a98241ed#8bd376c0c71ce366d763a70385c7b445a98241ed" dependencies = [ "anyhow", "collab", diff --git a/frontend/appflowy_tauri/src-tauri/Cargo.toml b/frontend/appflowy_tauri/src-tauri/Cargo.toml index ec5fbf551c..2778286c5b 100644 --- a/frontend/appflowy_tauri/src-tauri/Cargo.toml +++ b/frontend/appflowy_tauri/src-tauri/Cargo.toml @@ -120,14 +120,14 @@ custom-protocol = ["tauri/custom-protocol"] # To switch to the local path, run: # scripts/tool/update_collab_source.sh # ⚠️⚠️⚠️️ -collab = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0efc824a6e1a56e4485646e6428c07fdccf6e918" } -collab-entity = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0efc824a6e1a56e4485646e6428c07fdccf6e918" } -collab-folder = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0efc824a6e1a56e4485646e6428c07fdccf6e918" } -collab-document = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0efc824a6e1a56e4485646e6428c07fdccf6e918" } -collab-database = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0efc824a6e1a56e4485646e6428c07fdccf6e918" } -collab-plugins = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0efc824a6e1a56e4485646e6428c07fdccf6e918" } -collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0efc824a6e1a56e4485646e6428c07fdccf6e918" } -collab-importer = { version = "0.1", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0efc824a6e1a56e4485646e6428c07fdccf6e918" } +collab = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8bd376c0c71ce366d763a70385c7b445a98241ed" } +collab-entity = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8bd376c0c71ce366d763a70385c7b445a98241ed" } +collab-folder = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8bd376c0c71ce366d763a70385c7b445a98241ed" } +collab-document = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8bd376c0c71ce366d763a70385c7b445a98241ed" } +collab-database = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8bd376c0c71ce366d763a70385c7b445a98241ed" } +collab-plugins = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8bd376c0c71ce366d763a70385c7b445a98241ed" } +collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8bd376c0c71ce366d763a70385c7b445a98241ed" } +collab-importer = { version = "0.1", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8bd376c0c71ce366d763a70385c7b445a98241ed" } # Working directory: frontend # To update the commit ID, run: diff --git a/frontend/appflowy_web_app/src-tauri/Cargo.lock b/frontend/appflowy_web_app/src-tauri/Cargo.lock index 4dde43d1f3..697877b14a 100644 --- a/frontend/appflowy_web_app/src-tauri/Cargo.lock +++ b/frontend/appflowy_web_app/src-tauri/Cargo.lock @@ -1028,7 +1028,7 @@ dependencies = [ [[package]] name = "collab" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0efc824a6e1a56e4485646e6428c07fdccf6e918#0efc824a6e1a56e4485646e6428c07fdccf6e918" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8bd376c0c71ce366d763a70385c7b445a98241ed#8bd376c0c71ce366d763a70385c7b445a98241ed" dependencies = [ "anyhow", "arc-swap", @@ -1053,7 +1053,7 @@ dependencies = [ [[package]] name = "collab-database" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0efc824a6e1a56e4485646e6428c07fdccf6e918#0efc824a6e1a56e4485646e6428c07fdccf6e918" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8bd376c0c71ce366d763a70385c7b445a98241ed#8bd376c0c71ce366d763a70385c7b445a98241ed" dependencies = [ "anyhow", "async-trait", @@ -1092,7 +1092,7 @@ dependencies = [ [[package]] name = "collab-document" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0efc824a6e1a56e4485646e6428c07fdccf6e918#0efc824a6e1a56e4485646e6428c07fdccf6e918" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8bd376c0c71ce366d763a70385c7b445a98241ed#8bd376c0c71ce366d763a70385c7b445a98241ed" dependencies = [ "anyhow", "arc-swap", @@ -1113,7 +1113,7 @@ dependencies = [ [[package]] name = "collab-entity" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0efc824a6e1a56e4485646e6428c07fdccf6e918#0efc824a6e1a56e4485646e6428c07fdccf6e918" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8bd376c0c71ce366d763a70385c7b445a98241ed#8bd376c0c71ce366d763a70385c7b445a98241ed" dependencies = [ "anyhow", "bytes", @@ -1133,7 +1133,7 @@ dependencies = [ [[package]] name = "collab-folder" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0efc824a6e1a56e4485646e6428c07fdccf6e918#0efc824a6e1a56e4485646e6428c07fdccf6e918" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8bd376c0c71ce366d763a70385c7b445a98241ed#8bd376c0c71ce366d763a70385c7b445a98241ed" dependencies = [ "anyhow", "arc-swap", @@ -1155,7 +1155,7 @@ dependencies = [ [[package]] name = "collab-importer" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0efc824a6e1a56e4485646e6428c07fdccf6e918#0efc824a6e1a56e4485646e6428c07fdccf6e918" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8bd376c0c71ce366d763a70385c7b445a98241ed#8bd376c0c71ce366d763a70385c7b445a98241ed" dependencies = [ "anyhow", "async-recursion", @@ -1216,7 +1216,7 @@ dependencies = [ [[package]] name = "collab-plugins" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0efc824a6e1a56e4485646e6428c07fdccf6e918#0efc824a6e1a56e4485646e6428c07fdccf6e918" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8bd376c0c71ce366d763a70385c7b445a98241ed#8bd376c0c71ce366d763a70385c7b445a98241ed" dependencies = [ "anyhow", "async-stream", @@ -1296,7 +1296,7 @@ dependencies = [ [[package]] name = "collab-user" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0efc824a6e1a56e4485646e6428c07fdccf6e918#0efc824a6e1a56e4485646e6428c07fdccf6e918" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8bd376c0c71ce366d763a70385c7b445a98241ed#8bd376c0c71ce366d763a70385c7b445a98241ed" dependencies = [ "anyhow", "collab", diff --git a/frontend/appflowy_web_app/src-tauri/Cargo.toml b/frontend/appflowy_web_app/src-tauri/Cargo.toml index 5a77c76a36..e570ab28e2 100644 --- a/frontend/appflowy_web_app/src-tauri/Cargo.toml +++ b/frontend/appflowy_web_app/src-tauri/Cargo.toml @@ -118,14 +118,14 @@ custom-protocol = ["tauri/custom-protocol"] # To switch to the local path, run: # scripts/tool/update_collab_source.sh # ⚠️⚠️⚠️️ -collab = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0efc824a6e1a56e4485646e6428c07fdccf6e918" } -collab-entity = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0efc824a6e1a56e4485646e6428c07fdccf6e918" } -collab-folder = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0efc824a6e1a56e4485646e6428c07fdccf6e918" } -collab-document = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0efc824a6e1a56e4485646e6428c07fdccf6e918" } -collab-database = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0efc824a6e1a56e4485646e6428c07fdccf6e918" } -collab-plugins = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0efc824a6e1a56e4485646e6428c07fdccf6e918" } -collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0efc824a6e1a56e4485646e6428c07fdccf6e918" } -collab-importer = { version = "0.1", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0efc824a6e1a56e4485646e6428c07fdccf6e918" } +collab = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8bd376c0c71ce366d763a70385c7b445a98241ed" } +collab-entity = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8bd376c0c71ce366d763a70385c7b445a98241ed" } +collab-folder = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8bd376c0c71ce366d763a70385c7b445a98241ed" } +collab-document = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8bd376c0c71ce366d763a70385c7b445a98241ed" } +collab-database = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8bd376c0c71ce366d763a70385c7b445a98241ed" } +collab-plugins = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8bd376c0c71ce366d763a70385c7b445a98241ed" } +collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8bd376c0c71ce366d763a70385c7b445a98241ed" } +collab-importer = { version = "0.1", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8bd376c0c71ce366d763a70385c7b445a98241ed" } # Working directory: frontend diff --git a/frontend/rust-lib/Cargo.lock b/frontend/rust-lib/Cargo.lock index 213d85d759..308ca45910 100644 --- a/frontend/rust-lib/Cargo.lock +++ b/frontend/rust-lib/Cargo.lock @@ -891,7 +891,7 @@ dependencies = [ [[package]] name = "collab" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0efc824a6e1a56e4485646e6428c07fdccf6e918#0efc824a6e1a56e4485646e6428c07fdccf6e918" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8bd376c0c71ce366d763a70385c7b445a98241ed#8bd376c0c71ce366d763a70385c7b445a98241ed" dependencies = [ "anyhow", "arc-swap", @@ -916,7 +916,7 @@ dependencies = [ [[package]] name = "collab-database" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0efc824a6e1a56e4485646e6428c07fdccf6e918#0efc824a6e1a56e4485646e6428c07fdccf6e918" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8bd376c0c71ce366d763a70385c7b445a98241ed#8bd376c0c71ce366d763a70385c7b445a98241ed" dependencies = [ "anyhow", "async-trait", @@ -955,7 +955,7 @@ dependencies = [ [[package]] name = "collab-document" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0efc824a6e1a56e4485646e6428c07fdccf6e918#0efc824a6e1a56e4485646e6428c07fdccf6e918" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8bd376c0c71ce366d763a70385c7b445a98241ed#8bd376c0c71ce366d763a70385c7b445a98241ed" dependencies = [ "anyhow", "arc-swap", @@ -976,7 +976,7 @@ dependencies = [ [[package]] name = "collab-entity" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0efc824a6e1a56e4485646e6428c07fdccf6e918#0efc824a6e1a56e4485646e6428c07fdccf6e918" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8bd376c0c71ce366d763a70385c7b445a98241ed#8bd376c0c71ce366d763a70385c7b445a98241ed" dependencies = [ "anyhow", "bytes", @@ -996,7 +996,7 @@ dependencies = [ [[package]] name = "collab-folder" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0efc824a6e1a56e4485646e6428c07fdccf6e918#0efc824a6e1a56e4485646e6428c07fdccf6e918" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8bd376c0c71ce366d763a70385c7b445a98241ed#8bd376c0c71ce366d763a70385c7b445a98241ed" dependencies = [ "anyhow", "arc-swap", @@ -1018,7 +1018,7 @@ dependencies = [ [[package]] name = "collab-importer" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0efc824a6e1a56e4485646e6428c07fdccf6e918#0efc824a6e1a56e4485646e6428c07fdccf6e918" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8bd376c0c71ce366d763a70385c7b445a98241ed#8bd376c0c71ce366d763a70385c7b445a98241ed" dependencies = [ "anyhow", "async-recursion", @@ -1079,7 +1079,7 @@ dependencies = [ [[package]] name = "collab-plugins" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0efc824a6e1a56e4485646e6428c07fdccf6e918#0efc824a6e1a56e4485646e6428c07fdccf6e918" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8bd376c0c71ce366d763a70385c7b445a98241ed#8bd376c0c71ce366d763a70385c7b445a98241ed" dependencies = [ "anyhow", "async-stream", @@ -1159,7 +1159,7 @@ dependencies = [ [[package]] name = "collab-user" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=0efc824a6e1a56e4485646e6428c07fdccf6e918#0efc824a6e1a56e4485646e6428c07fdccf6e918" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8bd376c0c71ce366d763a70385c7b445a98241ed#8bd376c0c71ce366d763a70385c7b445a98241ed" dependencies = [ "anyhow", "collab", diff --git a/frontend/rust-lib/Cargo.toml b/frontend/rust-lib/Cargo.toml index 52e5193aa0..ca8ea1a50d 100644 --- a/frontend/rust-lib/Cargo.toml +++ b/frontend/rust-lib/Cargo.toml @@ -142,14 +142,14 @@ rocksdb = { git = "https://github.com/rust-rocksdb/rust-rocksdb", rev = "1710120 # To switch to the local path, run: # scripts/tool/update_collab_source.sh # ⚠️⚠️⚠️️ -collab = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0efc824a6e1a56e4485646e6428c07fdccf6e918" } -collab-entity = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0efc824a6e1a56e4485646e6428c07fdccf6e918" } -collab-folder = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0efc824a6e1a56e4485646e6428c07fdccf6e918" } -collab-document = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0efc824a6e1a56e4485646e6428c07fdccf6e918" } -collab-database = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0efc824a6e1a56e4485646e6428c07fdccf6e918" } -collab-plugins = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0efc824a6e1a56e4485646e6428c07fdccf6e918" } -collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0efc824a6e1a56e4485646e6428c07fdccf6e918" } -collab-importer = { version = "0.1", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "0efc824a6e1a56e4485646e6428c07fdccf6e918" } +collab = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8bd376c0c71ce366d763a70385c7b445a98241ed" } +collab-entity = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8bd376c0c71ce366d763a70385c7b445a98241ed" } +collab-folder = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8bd376c0c71ce366d763a70385c7b445a98241ed" } +collab-document = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8bd376c0c71ce366d763a70385c7b445a98241ed" } +collab-database = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8bd376c0c71ce366d763a70385c7b445a98241ed" } +collab-plugins = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8bd376c0c71ce366d763a70385c7b445a98241ed" } +collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8bd376c0c71ce366d763a70385c7b445a98241ed" } +collab-importer = { version = "0.1", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8bd376c0c71ce366d763a70385c7b445a98241ed" } # Working directory: frontend # To update the commit ID, run: diff --git a/frontend/rust-lib/event-integration-test/src/database_event.rs b/frontend/rust-lib/event-integration-test/src/database_event.rs index 80ff21129a..c9504f2ac0 100644 --- a/frontend/rust-lib/event-integration-test/src/database_event.rs +++ b/frontend/rust-lib/event-integration-test/src/database_event.rs @@ -11,7 +11,7 @@ use collab_database::rows::{Row, RowId}; use flowy_database2::entities::*; use flowy_database2::event_map::DatabaseEvent; use flowy_database2::services::cell::CellBuilder; -use flowy_database2::services::field::ChecklistCellInsertChangeset; +use flowy_database2::services::field::checklist_filter::ChecklistCellInsertChangeset; use flowy_database2::services::share::csv::CSVFormat; use flowy_folder::entities::*; use flowy_folder::event_map::FolderEvent; diff --git a/frontend/rust-lib/flowy-database2/src/entities/type_option_entities/relation_entities.rs b/frontend/rust-lib/flowy-database2/src/entities/type_option_entities/relation_entities.rs index 346c9f8357..17d7434c08 100644 --- a/frontend/rust-lib/flowy-database2/src/entities/type_option_entities/relation_entities.rs +++ b/frontend/rust-lib/flowy-database2/src/entities/type_option_entities/relation_entities.rs @@ -1,8 +1,8 @@ use collab_database::fields::relation_type_option::RelationTypeOption; +use collab_database::template::relation_parse::RelationCellData; use flowy_derive::ProtoBuf; use crate::entities::CellIdPB; -use crate::services::field::RelationCellData; #[derive(Debug, Clone, Default, ProtoBuf)] pub struct RelationCellDataPB { diff --git a/frontend/rust-lib/flowy-database2/src/entities/type_option_entities/timestamp_entities.rs b/frontend/rust-lib/flowy-database2/src/entities/type_option_entities/timestamp_entities.rs index 16cc1b2bbf..1bf20be587 100644 --- a/frontend/rust-lib/flowy-database2/src/entities/type_option_entities/timestamp_entities.rs +++ b/frontend/rust-lib/flowy-database2/src/entities/type_option_entities/timestamp_entities.rs @@ -45,6 +45,7 @@ impl From for TimestampTypeOption { time_format: data.time_format.into(), include_time: data.include_time, field_type: data.field_type.into(), + timezone: None, } } } diff --git a/frontend/rust-lib/flowy-database2/src/event_handler.rs b/frontend/rust-lib/flowy-database2/src/event_handler.rs index c071fe2314..03ec69af5a 100644 --- a/frontend/rust-lib/flowy-database2/src/event_handler.rs +++ b/frontend/rust-lib/flowy-database2/src/event_handler.rs @@ -10,9 +10,10 @@ use lib_dispatch::prelude::{af_spawn, data_result_ok, AFPluginData, AFPluginStat use crate::entities::*; use crate::manager::DatabaseManager; +use crate::services::field::checklist_filter::ChecklistCellChangeset; +use crate::services::field::date_filter::DateCellChangeset; use crate::services::field::{ - type_option_data_from_pb, ChecklistCellChangeset, DateCellChangeset, RelationCellChangeset, - SelectOptionCellChangeset, TypeOptionCellExt, + type_option_data_from_pb, RelationCellChangeset, SelectOptionCellChangeset, TypeOptionCellExt, }; use crate::services::group::GroupChangeset; use crate::services::share::csv::CSVFormat; diff --git a/frontend/rust-lib/flowy-database2/src/services/calculations/service.rs b/frontend/rust-lib/flowy-database2/src/services/calculations/service.rs index bbde7e9e31..95ce0f49a3 100644 --- a/frontend/rust-lib/flowy-database2/src/services/calculations/service.rs +++ b/frontend/rust-lib/flowy-database2/src/services/calculations/service.rs @@ -95,7 +95,7 @@ impl CalculationsService { if let Some(handler) = TypeOptionCellExt::new(field, None).get_type_option_cell_data_handler() { let empty_count = cells .par_iter() - .filter(|cell| handler.handle_is_cell_empty(cell, field)) + .filter(|cell| handler.handle_is_empty(cell, field)) .count(); empty_count.to_string() } else { @@ -107,7 +107,7 @@ impl CalculationsService { if let Some(handler) = TypeOptionCellExt::new(field, None).get_type_option_cell_data_handler() { let non_empty_count = cells .par_iter() - .filter(|cell| !handler.handle_is_cell_empty(cell, field)) + .filter(|cell| !handler.handle_is_empty(cell, field)) .count(); non_empty_count.to_string() } else { diff --git a/frontend/rust-lib/flowy-database2/src/services/cell/cell_operation.rs b/frontend/rust-lib/flowy-database2/src/services/cell/cell_operation.rs index 8c3b757e02..d4a22c2180 100644 --- a/frontend/rust-lib/flowy-database2/src/services/cell/cell_operation.rs +++ b/frontend/rust-lib/flowy-database2/src/services/cell/cell_operation.rs @@ -5,12 +5,17 @@ use collab_database::fields::media_type_option::MediaCellData; use collab_database::fields::select_type_option::SelectOptionIds; use collab_database::fields::Field; use collab_database::rows::{get_field_type_from_cell, Cell, Cells}; - +use collab_database::template::relation_parse::RelationCellData; use flowy_error::{FlowyError, FlowyResult}; use lib_infra::box_any::BoxAny; +use tracing::trace; use crate::entities::{CheckboxCellDataPB, FieldType}; use crate::services::cell::{CellCache, CellProtobufBlob}; +use crate::services::field::checklist_filter::{ + ChecklistCellChangeset, ChecklistCellInsertChangeset, +}; +use crate::services::field::date_filter::DateCellChangeset; use crate::services::field::*; use crate::services::group::make_no_status_group; @@ -24,7 +29,9 @@ pub trait CellDataDecoder: TypeOption { /// /// * `cell`: the cell to be decoded /// - fn decode_cell(&self, cell: &Cell) -> FlowyResult<::CellData>; + fn decode_cell(&self, cell: &Cell) -> FlowyResult<::CellData> { + Ok(Self::CellData::from(cell)) + } /// Decodes the [Cell] that is of a particular field type into a `CellData` of this `TypeOption`'s field type. /// @@ -48,12 +55,6 @@ pub trait CellDataDecoder: TypeOption { /// separated by a comma. /// fn stringify_cell_data(&self, cell_data: ::CellData) -> String; - - /// Decode the cell into f64 - /// Different field type has different way to decode the cell data into f64 - /// If the field type doesn't support to decode the cell data into f64, it will return None - /// - fn numeric_cell(&self, cell: &Cell) -> Option; } pub trait CellDataChangeset: TypeOption { @@ -223,6 +224,7 @@ impl<'a> CellBuilder<'a> { for (field_id, cell_str) in cell_by_field_id { if let Some(field) = field_maps.get(&field_id) { let field_type = FieldType::from(field.field_type); + trace!("Field type: {:?}, cell_str: {}", field_type, cell_str); match field_type { FieldType::RichText | FieldType::Translate | FieldType::Summary => { cells.insert(field_id, insert_text_cell(cell_str, field)); @@ -262,10 +264,14 @@ impl<'a> CellBuilder<'a> { } }, FieldType::Relation => { - cells.insert(field_id, (&RelationCellData::from(cell_str)).into()); + if let Ok(cell_data) = RelationCellData::from_str(&cell_str) { + cells.insert(field_id, cell_data.into()); + } }, FieldType::Media => { - cells.insert(field_id, MediaCellData::from(cell_str).into()); + if let Ok(cell_data) = MediaCellData::from_str(&cell_str) { + cells.insert(field_id, cell_data.into()); + } }, } } diff --git a/frontend/rust-lib/flowy-database2/src/services/database/database_editor.rs b/frontend/rust-lib/flowy-database2/src/services/database/database_editor.rs index 6f2e5c6003..17e141fb4f 100644 --- a/frontend/rust-lib/flowy-database2/src/services/database/database_editor.rs +++ b/frontend/rust-lib/flowy-database2/src/services/database/database_editor.rs @@ -7,11 +7,11 @@ use crate::services::database::util::database_view_setting_pb_from_view; use crate::services::database_view::{ DatabaseViewChanged, DatabaseViewEditor, DatabaseViewOperation, DatabaseViews, EditorByViewId, }; +use crate::services::field::checklist_filter::ChecklistCellChangeset; use crate::services::field::type_option_transform::transform_type_option; use crate::services::field::{ default_type_option_data_from_type, select_type_option_from_field, type_option_data_from_pb, - ChecklistCellChangeset, SelectOptionCellChangeset, StringCellData, TimestampCellData, - TimestampCellDataWrapper, TypeOptionCellDataHandler, TypeOptionCellExt, + SelectOptionCellChangeset, StringCellData, TypeOptionCellDataHandler, TypeOptionCellExt, }; use crate::services::field_settings::{default_field_settings_by_layout_map, FieldSettings}; use crate::services::filter::{Filter, FilterChangeset}; @@ -30,6 +30,7 @@ use collab_database::fields::media_type_option::MediaCellData; use collab_database::fields::relation_type_option::RelationTypeOption; use collab_database::fields::{Field, TypeOptionData}; use collab_database::rows::{Cell, Cells, DatabaseRow, Row, RowCell, RowDetail, RowId, RowUpdate}; +use collab_database::template::timestamp_parse::TimestampCellData; use collab_database::views::{ DatabaseLayout, FilterMap, LayoutSetting, OrderObjectPosition, RowOrder, }; @@ -891,12 +892,12 @@ impl DatabaseEditor { match field_type { FieldType::LastEditedTime | FieldType::CreatedTime => { let row = database.get_row(row_id).await; - let wrapped_cell_data = if field_type.is_created_time() { - TimestampCellDataWrapper::from((field_type, TimestampCellData::new(row.created_at))) + let cell_data = if field_type.is_created_time() { + TimestampCellData::new(row.created_at) } else { - TimestampCellDataWrapper::from((field_type, TimestampCellData::new(row.modified_at))) + TimestampCellData::new(row.modified_at) }; - Some(Cell::from(wrapped_cell_data)) + Some(cell_data.to_cell(field.field_type)) }, _ => database.get_cell(field_id, row_id).await.cell, } @@ -938,7 +939,7 @@ impl DatabaseEditor { }; Some(RowCell { row_id: row.id, - cell: Some(Cell::from(data)), + cell: Some(data.to_cell(field.field_type)), }) }, Err(_) => None, diff --git a/frontend/rust-lib/flowy-database2/src/services/field/type_options/checkbox_type_option/checkbox_type_option.rs b/frontend/rust-lib/flowy-database2/src/services/field/type_options/checkbox_type_option/checkbox_type_option.rs index 4029f5f3c7..e41c089e4b 100644 --- a/frontend/rust-lib/flowy-database2/src/services/field/type_options/checkbox_type_option/checkbox_type_option.rs +++ b/frontend/rust-lib/flowy-database2/src/services/field/type_options/checkbox_type_option/checkbox_type_option.rs @@ -10,7 +10,7 @@ use flowy_error::FlowyResult; use crate::entities::{CheckboxCellDataPB, CheckboxFilterPB, FieldType}; use crate::services::cell::{CellDataChangeset, CellDataDecoder}; use crate::services::field::{ - TypeOption, TypeOptionCellDataCompare, TypeOptionCellDataFilter, TypeOptionCellDataSerde, + CellDataProtobufEncoder, TypeOption, TypeOptionCellDataCompare, TypeOptionCellDataFilter, TypeOptionTransform, }; use crate::services::sort::SortCondition; @@ -24,24 +24,16 @@ impl TypeOption for CheckboxTypeOption { impl TypeOptionTransform for CheckboxTypeOption {} -impl TypeOptionCellDataSerde for CheckboxTypeOption { +impl CellDataProtobufEncoder for CheckboxTypeOption { fn protobuf_encode( &self, cell_data: ::CellData, ) -> ::CellProtobufType { cell_data } - - fn parse_cell(&self, cell: &Cell) -> FlowyResult<::CellData> { - Ok(CheckboxCellDataPB::from(cell)) - } } impl CellDataDecoder for CheckboxTypeOption { - fn decode_cell(&self, cell: &Cell) -> FlowyResult<::CellData> { - self.parse_cell(cell) - } - fn decode_cell_with_transform( &self, cell: &Cell, @@ -58,15 +50,6 @@ impl CellDataDecoder for CheckboxTypeOption { fn stringify_cell_data(&self, cell_data: ::CellData) -> String { cell_data.to_string() } - - fn numeric_cell(&self, cell: &Cell) -> Option { - let cell_data = self.parse_cell(cell).ok()?; - if cell_data.is_checked { - Some(1.0) - } else { - Some(0.0) - } - } } pub type CheckboxCellChangeset = String; diff --git a/frontend/rust-lib/flowy-database2/src/services/field/type_options/checklist_type_option/checklist_entities.rs b/frontend/rust-lib/flowy-database2/src/services/field/type_options/checklist_type_option/checklist_entities.rs deleted file mode 100644 index 41c5ca4468..0000000000 --- a/frontend/rust-lib/flowy-database2/src/services/field/type_options/checklist_type_option/checklist_entities.rs +++ /dev/null @@ -1,143 +0,0 @@ -use crate::entities::{ChecklistCellDataChangesetPB, FieldType}; -use crate::services::field::{TypeOptionCellData, CELL_DATA}; -use collab::util::AnyMapExt; -use collab_database::fields::select_type_option::SelectOption; -use collab_database::rows::{new_cell_builder, Cell}; -use serde::{Deserialize, Serialize}; -use std::fmt::Debug; - -#[derive(Default, Clone, Debug, Serialize, Deserialize)] -pub struct ChecklistCellData { - pub options: Vec, - pub selected_option_ids: Vec, -} - -impl ToString for ChecklistCellData { - fn to_string(&self) -> String { - serde_json::to_string(self).unwrap_or_default() - } -} - -impl TypeOptionCellData for ChecklistCellData { - fn is_cell_empty(&self) -> bool { - self.options.is_empty() - } -} - -impl ChecklistCellData { - pub fn selected_options(&self) -> Vec { - self - .options - .iter() - .filter(|option| self.selected_option_ids.contains(&option.id)) - .cloned() - .collect() - } - - pub fn percentage_complete(&self) -> f64 { - let selected_options = self.selected_option_ids.len(); - let total_options = self.options.len(); - - if total_options == 0 { - return 0.0; - } - ((selected_options as f64) / (total_options as f64) * 100.0).round() / 100.0 - } - - pub fn from_options(new_tasks: Vec) -> Self { - let (options, selected_ids): (Vec<_>, Vec<_>) = new_tasks - .into_iter() - .map(|new_task| { - let option = SelectOption::new(&new_task.name); - let selected_id = new_task.is_complete.then(|| option.id.clone()); - (option, selected_id) - }) - .unzip(); - let selected_option_ids = selected_ids.into_iter().flatten().collect(); - - Self { - options, - selected_option_ids, - } - } -} - -impl From<&Cell> for ChecklistCellData { - fn from(cell: &Cell) -> Self { - cell - .get_as::(CELL_DATA) - .map(|data| serde_json::from_str::(&data).unwrap_or_default()) - .unwrap_or_default() - } -} - -impl From for Cell { - fn from(cell_data: ChecklistCellData) -> Self { - let data = serde_json::to_string(&cell_data).unwrap_or_default(); - let mut cell = new_cell_builder(FieldType::Checklist); - cell.insert(CELL_DATA.into(), data.into()); - cell - } -} - -#[derive(Debug, Clone, Default)] -pub struct ChecklistCellChangeset { - pub insert_tasks: Vec, - pub delete_tasks: Vec, - pub update_tasks: Vec, - pub completed_task_ids: Vec, - pub reorder: String, -} - -impl From for ChecklistCellChangeset { - fn from(value: ChecklistCellDataChangesetPB) -> Self { - ChecklistCellChangeset { - insert_tasks: value - .insert_task - .into_iter() - .map(|pb| ChecklistCellInsertChangeset { - name: pb.name, - is_complete: false, - index: pb.index, - }) - .collect(), - delete_tasks: value.delete_tasks, - update_tasks: value - .update_tasks - .into_iter() - .map(SelectOption::from) - .collect(), - completed_task_ids: value.completed_tasks, - reorder: value.reorder, - } - } -} - -#[derive(Debug, Clone, Default)] -pub struct ChecklistCellInsertChangeset { - pub name: String, - pub is_complete: bool, - pub index: Option, -} - -impl ChecklistCellInsertChangeset { - pub fn new(name: String, is_complete: bool) -> Self { - Self { - name, - is_complete, - index: None, - } - } -} - -#[cfg(test)] -mod tests { - #[test] - fn test() { - let a = 1; - let b = 2; - - let c = (a as f32) / (b as f32); - println!("{}", c); - } -} diff --git a/frontend/rust-lib/flowy-database2/src/services/field/type_options/checklist_type_option/checklist_filter.rs b/frontend/rust-lib/flowy-database2/src/services/field/type_options/checklist_type_option/checklist_filter.rs index 2dfbe23759..5fa9c11242 100644 --- a/frontend/rust-lib/flowy-database2/src/services/field/type_options/checklist_type_option/checklist_filter.rs +++ b/frontend/rust-lib/flowy-database2/src/services/field/type_options/checklist_type_option/checklist_filter.rs @@ -1,9 +1,13 @@ +use crate::entities::ChecklistCellDataChangesetPB; +use crate::entities::{ChecklistFilterConditionPB, ChecklistFilterPB}; +use crate::services::filter::PreFillCellsWithFilter; + use collab_database::fields::select_type_option::SelectOption; use collab_database::fields::Field; use collab_database::rows::Cell; +use collab_database::template::check_list_parse::ChecklistCellData; -use crate::entities::{ChecklistFilterConditionPB, ChecklistFilterPB}; -use crate::services::filter::PreFillCellsWithFilter; +use std::fmt::Debug; impl ChecklistFilterPB { pub fn is_visible( @@ -47,3 +51,82 @@ impl PreFillCellsWithFilter for ChecklistFilterPB { None } } + +pub fn checklist_from_options(new_tasks: Vec) -> ChecklistCellData { + let (options, selected_ids): (Vec<_>, Vec<_>) = new_tasks + .into_iter() + .map(|new_task| { + let option = SelectOption::new(&new_task.name); + let selected_id = new_task.is_complete.then(|| option.id.clone()); + (option, selected_id) + }) + .unzip(); + let selected_option_ids = selected_ids.into_iter().flatten().collect(); + + ChecklistCellData { + options, + selected_option_ids, + } +} + +#[derive(Debug, Clone, Default)] +pub struct ChecklistCellChangeset { + pub insert_tasks: Vec, + pub delete_tasks: Vec, + pub update_tasks: Vec, + pub completed_task_ids: Vec, + pub reorder: String, +} + +impl From for ChecklistCellChangeset { + fn from(value: ChecklistCellDataChangesetPB) -> Self { + ChecklistCellChangeset { + insert_tasks: value + .insert_task + .into_iter() + .map(|pb| ChecklistCellInsertChangeset { + name: pb.name, + is_complete: false, + index: pb.index, + }) + .collect(), + delete_tasks: value.delete_tasks, + update_tasks: value + .update_tasks + .into_iter() + .map(SelectOption::from) + .collect(), + completed_task_ids: value.completed_tasks, + reorder: value.reorder, + } + } +} + +#[derive(Debug, Clone, Default)] +pub struct ChecklistCellInsertChangeset { + pub name: String, + pub is_complete: bool, + pub index: Option, +} + +impl ChecklistCellInsertChangeset { + pub fn new(name: String, is_complete: bool) -> Self { + Self { + name, + is_complete, + index: None, + } + } +} + +#[cfg(test)] +mod tests { + #[test] + fn test() { + let a = 1; + let b = 2; + + let c = (a as f32) / (b as f32); + println!("{}", c); + } +} diff --git a/frontend/rust-lib/flowy-database2/src/services/field/type_options/checklist_type_option/checklist_type_option.rs b/frontend/rust-lib/flowy-database2/src/services/field/type_options/checklist_type_option/checklist_type_option.rs index 4d96173660..a9043f227c 100644 --- a/frontend/rust-lib/flowy-database2/src/services/field/type_options/checklist_type_option/checklist_type_option.rs +++ b/frontend/rust-lib/flowy-database2/src/services/field/type_options/checklist_type_option/checklist_type_option.rs @@ -1,14 +1,16 @@ use crate::entities::{ChecklistCellDataPB, ChecklistFilterPB, SelectOptionPB}; use crate::services::cell::{CellDataChangeset, CellDataDecoder}; -use crate::services::field::checklist_type_option::{ChecklistCellChangeset, ChecklistCellData}; +use crate::services::field::checklist_filter::{checklist_from_options, ChecklistCellChangeset}; use crate::services::field::{ - TypeOption, TypeOptionCellData, TypeOptionCellDataCompare, TypeOptionCellDataFilter, - TypeOptionCellDataSerde, TypeOptionTransform, + CellDataProtobufEncoder, TypeOption, TypeOptionCellDataCompare, TypeOptionCellDataFilter, + TypeOptionTransform, }; use crate::services::sort::SortCondition; use collab_database::fields::checklist_type_option::ChecklistTypeOption; use collab_database::fields::select_type_option::{SelectOption, SELECTION_IDS_SEPARATOR}; use collab_database::rows::Cell; +use collab_database::template::check_list_parse::ChecklistCellData; +use collab_database::template::util::TypeOptionCellData; use flowy_error::FlowyResult; use std::cmp::Ordering; @@ -19,7 +21,7 @@ impl TypeOption for ChecklistTypeOption { type CellFilter = ChecklistFilterPB; } -impl TypeOptionCellDataSerde for ChecklistTypeOption { +impl CellDataProtobufEncoder for ChecklistTypeOption { fn protobuf_encode( &self, cell_data: ::CellData, @@ -44,10 +46,6 @@ impl TypeOptionCellDataSerde for ChecklistTypeOption { percentage, } } - - fn parse_cell(&self, cell: &Cell) -> FlowyResult<::CellData> { - Ok(ChecklistCellData::from(cell)) - } } impl CellDataChangeset for ChecklistTypeOption { @@ -58,12 +56,12 @@ impl CellDataChangeset for ChecklistTypeOption { ) -> FlowyResult<(Cell, ::CellData)> { match cell { Some(cell) => { - let mut cell_data = self.parse_cell(&cell)?; + let mut cell_data = self.decode_cell(&cell)?; update_cell_data_with_changeset(&mut cell_data, changeset); Ok((Cell::from(cell_data.clone()), cell_data)) }, None => { - let cell_data = ChecklistCellData::from_options(changeset.insert_tasks); + let cell_data = checklist_from_options(changeset.insert_tasks); Ok((Cell::from(cell_data.clone()), cell_data)) }, } @@ -142,10 +140,6 @@ fn update_cell_data_with_changeset( } impl CellDataDecoder for ChecklistTypeOption { - fn decode_cell(&self, cell: &Cell) -> FlowyResult<::CellData> { - self.parse_cell(cell) - } - fn stringify_cell_data(&self, cell_data: ::CellData) -> String { cell_data .options @@ -154,11 +148,6 @@ impl CellDataDecoder for ChecklistTypeOption { .collect::>() .join(SELECTION_IDS_SEPARATOR) } - - fn numeric_cell(&self, _cell: &Cell) -> Option { - // return the percentage complete if needed - None - } } impl TypeOptionCellDataFilter for ChecklistTypeOption { diff --git a/frontend/rust-lib/flowy-database2/src/services/field/type_options/checklist_type_option/mod.rs b/frontend/rust-lib/flowy-database2/src/services/field/type_options/checklist_type_option/mod.rs index 7e981d51de..85ff615900 100644 --- a/frontend/rust-lib/flowy-database2/src/services/field/type_options/checklist_type_option/mod.rs +++ b/frontend/rust-lib/flowy-database2/src/services/field/type_options/checklist_type_option/mod.rs @@ -1,6 +1,3 @@ #![allow(clippy::module_inception)] -mod checklist_entities; -mod checklist_filter; -mod checklist_type_option; - -pub use checklist_entities::*; +pub mod checklist_filter; +pub mod checklist_type_option; diff --git a/frontend/rust-lib/flowy-database2/src/services/field/type_options/date_type_option/date_filter.rs b/frontend/rust-lib/flowy-database2/src/services/field/type_options/date_type_option/date_filter.rs index 382ec335e3..3f0808fac2 100644 --- a/frontend/rust-lib/flowy-database2/src/services/field/type_options/date_type_option/date_filter.rs +++ b/frontend/rust-lib/flowy-database2/src/services/field/type_options/date_type_option/date_filter.rs @@ -1,12 +1,17 @@ use crate::entities::{DateFilterConditionPB, DateFilterPB}; use crate::services::cell::insert_date_cell; -use crate::services::field::TimestampCellData; use crate::services::filter::PreFillCellsWithFilter; +use bytes::Bytes; use chrono::{Duration, Local, NaiveDate, TimeZone}; use collab_database::fields::date_type_option::DateCellData; use collab_database::fields::Field; use collab_database::rows::Cell; +use collab_database::template::timestamp_parse::TimestampCellData; +use flowy_error::{internal_error, FlowyResult}; + +use crate::entities::DateCellDataPB; +use crate::services::cell::CellProtobufBlobParser; impl DateFilterPB { /// Returns `None` if the DateFilterPB doesn't have the necessary data for @@ -179,6 +184,25 @@ impl PreFillCellsWithFilter for DateFilterPB { } } +#[derive(Clone, Debug, Default)] +pub struct DateCellChangeset { + pub timestamp: Option, + pub end_timestamp: Option, + pub include_time: Option, + pub is_range: Option, + pub clear_flag: Option, + pub reminder_id: Option, +} + +pub struct DateCellDataParser(); +impl CellProtobufBlobParser for DateCellDataParser { + type Object = DateCellDataPB; + + fn parser(bytes: &Bytes) -> FlowyResult { + DateCellDataPB::try_from(bytes.as_ref()).map_err(internal_error) + } +} + #[cfg(test)] mod tests { use crate::entities::{DateFilterConditionPB, DateFilterPB}; diff --git a/frontend/rust-lib/flowy-database2/src/services/field/type_options/date_type_option/date_tests.rs b/frontend/rust-lib/flowy-database2/src/services/field/type_options/date_type_option/date_tests.rs index 8210018f49..dbaf0be3ea 100644 --- a/frontend/rust-lib/flowy-database2/src/services/field/type_options/date_type_option/date_tests.rs +++ b/frontend/rust-lib/flowy-database2/src/services/field/type_options/date_type_option/date_tests.rs @@ -3,7 +3,7 @@ mod tests { use collab_database::rows::Cell; use crate::services::cell::{CellDataChangeset, CellDataDecoder}; - use crate::services::field::DateCellChangeset; + use crate::services::field::date_type_option::date_filter::DateCellChangeset; use collab_database::fields::date_type_option::{DateCellData, DateTypeOption}; #[test] diff --git a/frontend/rust-lib/flowy-database2/src/services/field/type_options/date_type_option/date_type_option.rs b/frontend/rust-lib/flowy-database2/src/services/field/type_options/date_type_option/date_type_option.rs index 58fe5beab9..d9739fa792 100644 --- a/frontend/rust-lib/flowy-database2/src/services/field/type_options/date_type_option/date_type_option.rs +++ b/frontend/rust-lib/flowy-database2/src/services/field/type_options/date_type_option/date_type_option.rs @@ -12,9 +12,10 @@ use tracing::info; use crate::entities::{DateCellDataPB, DateFilterPB, FieldType}; use crate::services::cell::{CellDataChangeset, CellDataDecoder}; +use crate::services::field::date_type_option::date_filter::DateCellChangeset; use crate::services::field::{ - default_order, DateCellChangeset, TypeOption, TypeOptionCellDataCompare, - TypeOptionCellDataFilter, TypeOptionCellDataSerde, TypeOptionTransform, CELL_DATA, + default_order, CellDataProtobufEncoder, TypeOption, TypeOptionCellDataCompare, + TypeOptionCellDataFilter, TypeOptionTransform, CELL_DATA, }; use crate::services::sort::SortCondition; @@ -25,7 +26,7 @@ impl TypeOption for DateTypeOption { type CellFilter = DateFilterPB; } -impl TypeOptionCellDataSerde for DateTypeOption { +impl CellDataProtobufEncoder for DateTypeOption { fn protobuf_encode( &self, cell_data: ::CellData, @@ -50,10 +51,6 @@ impl TypeOptionCellDataSerde for DateTypeOption { reminder_id, } } - - fn parse_cell(&self, cell: &Cell) -> FlowyResult<::CellData> { - Ok(DateCellData::from(cell)) - } } #[async_trait] @@ -104,10 +101,6 @@ impl TypeOptionTransform for DateTypeOption { } impl CellDataDecoder for DateTypeOption { - fn decode_cell(&self, cell: &Cell) -> FlowyResult<::CellData> { - self.parse_cell(cell) - } - fn stringify_cell_data(&self, cell_data: ::CellData) -> String { let include_time = cell_data.include_time; let timestamp = cell_data.timestamp; @@ -146,10 +139,6 @@ impl CellDataDecoder for DateTypeOption { let timestamp = cast_string_to_timestamp(&s)?; Some(DateCellData::from_timestamp(timestamp)) } - - fn numeric_cell(&self, _cell: &Cell) -> Option { - None - } } impl CellDataChangeset for DateTypeOption { diff --git a/frontend/rust-lib/flowy-database2/src/services/field/type_options/date_type_option/date_type_option_entities.rs b/frontend/rust-lib/flowy-database2/src/services/field/type_options/date_type_option/date_type_option_entities.rs deleted file mode 100644 index 86fcb3b9c1..0000000000 --- a/frontend/rust-lib/flowy-database2/src/services/field/type_options/date_type_option/date_type_option_entities.rs +++ /dev/null @@ -1,33 +0,0 @@ -#![allow(clippy::upper_case_acronyms)] - -use bytes::Bytes; -use collab_database::fields::date_type_option::DateCellData; -use flowy_error::{internal_error, FlowyResult}; - -use crate::entities::DateCellDataPB; -use crate::services::cell::CellProtobufBlobParser; -use crate::services::field::TypeOptionCellData; - -#[derive(Clone, Debug, Default)] -pub struct DateCellChangeset { - pub timestamp: Option, - pub end_timestamp: Option, - pub include_time: Option, - pub is_range: Option, - pub clear_flag: Option, - pub reminder_id: Option, -} - -impl TypeOptionCellData for DateCellData { - fn is_cell_empty(&self) -> bool { - self.timestamp.is_none() - } -} -pub struct DateCellDataParser(); -impl CellProtobufBlobParser for DateCellDataParser { - type Object = DateCellDataPB; - - fn parser(bytes: &Bytes) -> FlowyResult { - DateCellDataPB::try_from(bytes.as_ref()).map_err(internal_error) - } -} diff --git a/frontend/rust-lib/flowy-database2/src/services/field/type_options/date_type_option/mod.rs b/frontend/rust-lib/flowy-database2/src/services/field/type_options/date_type_option/mod.rs index 13191ec686..3d50a36a82 100644 --- a/frontend/rust-lib/flowy-database2/src/services/field/type_options/date_type_option/mod.rs +++ b/frontend/rust-lib/flowy-database2/src/services/field/type_options/date_type_option/mod.rs @@ -1,7 +1,4 @@ #![allow(clippy::module_inception)] -mod date_filter; +pub mod date_filter; mod date_tests; -mod date_type_option; -mod date_type_option_entities; - -pub use date_type_option_entities::*; +pub mod date_type_option; diff --git a/frontend/rust-lib/flowy-database2/src/services/field/type_options/media_type_option/media_type_option.rs b/frontend/rust-lib/flowy-database2/src/services/field/type_options/media_type_option/media_type_option.rs index ae892aefac..2d7a1eb010 100644 --- a/frontend/rust-lib/flowy-database2/src/services/field/type_options/media_type_option/media_type_option.rs +++ b/frontend/rust-lib/flowy-database2/src/services/field/type_options/media_type_option/media_type_option.rs @@ -8,19 +8,13 @@ use crate::{ services::{ cell::{CellDataChangeset, CellDataDecoder}, field::{ - default_order, StringCellData, TypeOption, TypeOptionCellData, TypeOptionCellDataCompare, - TypeOptionCellDataFilter, TypeOptionCellDataSerde, TypeOptionTransform, + default_order, CellDataProtobufEncoder, TypeOption, TypeOptionCellData, + TypeOptionCellDataCompare, TypeOptionCellDataFilter, TypeOptionTransform, }, sort::SortCondition, }, }; -impl TypeOptionCellData for MediaCellData { - fn is_cell_empty(&self) -> bool { - self.files.is_empty() - } -} - impl TypeOption for MediaTypeOption { type CellData = MediaCellData; type CellChangeset = MediaCellChangeset; @@ -30,24 +24,16 @@ impl TypeOption for MediaTypeOption { impl TypeOptionTransform for MediaTypeOption {} -impl TypeOptionCellDataSerde for MediaTypeOption { +impl CellDataProtobufEncoder for MediaTypeOption { fn protobuf_encode( &self, cell_data: ::CellData, ) -> ::CellProtobufType { cell_data.into() } - - fn parse_cell(&self, cell: &Cell) -> FlowyResult<::CellData> { - Ok(cell.into()) - } } impl CellDataDecoder for MediaTypeOption { - fn decode_cell(&self, cell: &Cell) -> FlowyResult<::CellData> { - self.parse_cell(cell) - } - fn decode_cell_with_transform( &self, _cell: &Cell, @@ -76,10 +62,6 @@ impl CellDataDecoder for MediaTypeOption { fn stringify_cell_data(&self, cell_data: ::CellData) -> String { cell_data.to_string() } - - fn numeric_cell(&self, cell: &Cell) -> Option { - StringCellData::from(cell).0.parse::().ok() - } } impl CellDataChangeset for MediaTypeOption { diff --git a/frontend/rust-lib/flowy-database2/src/services/field/type_options/mod.rs b/frontend/rust-lib/flowy-database2/src/services/field/type_options/mod.rs index b23bcdf1d9..0530eb746e 100644 --- a/frontend/rust-lib/flowy-database2/src/services/field/type_options/mod.rs +++ b/frontend/rust-lib/flowy-database2/src/services/field/type_options/mod.rs @@ -24,7 +24,7 @@ pub use relation_type_option::*; pub use selection_type_option::*; pub use text_type_option::*; pub use time_type_option::*; -pub use timestamp_type_option::*; + pub use type_option::*; pub use type_option_cell::*; pub use url_type_option::*; diff --git a/frontend/rust-lib/flowy-database2/src/services/field/type_options/number_type_option/number_type_option.rs b/frontend/rust-lib/flowy-database2/src/services/field/type_options/number_type_option/number_type_option.rs index 912f53d636..e4ebcd0e0b 100644 --- a/frontend/rust-lib/flowy-database2/src/services/field/type_options/number_type_option/number_type_option.rs +++ b/frontend/rust-lib/flowy-database2/src/services/field/type_options/number_type_option/number_type_option.rs @@ -1,18 +1,17 @@ use async_trait::async_trait; -use collab::util::AnyMapExt; use collab_database::database::Database; use collab_database::fields::number_type_option::{ NumberCellFormat, NumberFormat, NumberTypeOption, }; use collab_database::fields::{Field, TypeOptionData}; -use collab_database::rows::{new_cell_builder, Cell}; +use collab_database::rows::Cell; use fancy_regex::Regex; use flowy_error::FlowyResult; use lazy_static::lazy_static; +use collab_database::template::number_parse::NumberCellData; use std::cmp::Ordering; -use std::default::Default; use tracing::info; @@ -20,52 +19,11 @@ use crate::entities::{FieldType, NumberFilterPB}; use crate::services::cell::{CellDataChangeset, CellDataDecoder}; use crate::services::field::type_options::util::ProtobufStr; use crate::services::field::{ - TypeOption, TypeOptionCellData, TypeOptionCellDataCompare, TypeOptionCellDataFilter, - TypeOptionCellDataSerde, TypeOptionTransform, CELL_DATA, + CellDataProtobufEncoder, TypeOption, TypeOptionCellData, TypeOptionCellDataCompare, + TypeOptionCellDataFilter, TypeOptionTransform, }; use crate::services::sort::SortCondition; -#[derive(Clone, Debug, Default)] -pub struct NumberCellData(pub String); - -impl TypeOptionCellData for NumberCellData { - fn is_cell_empty(&self) -> bool { - self.0.is_empty() - } -} - -impl AsRef for NumberCellData { - fn as_ref(&self) -> &str { - &self.0 - } -} - -impl From<&Cell> for NumberCellData { - fn from(cell: &Cell) -> Self { - Self(cell.get_as(CELL_DATA).unwrap_or_default()) - } -} - -impl From for Cell { - fn from(data: NumberCellData) -> Self { - let mut cell = new_cell_builder(FieldType::Number); - cell.insert(CELL_DATA.into(), data.0.into()); - cell - } -} - -impl std::convert::From for NumberCellData { - fn from(s: String) -> Self { - Self(s) - } -} - -impl ToString for NumberCellData { - fn to_string(&self) -> String { - self.0.clone() - } -} - impl TypeOption for NumberTypeOption { type CellData = NumberCellData; type CellChangeset = NumberCellChangeset; @@ -73,17 +31,13 @@ impl TypeOption for NumberTypeOption { type CellFilter = NumberFilterPB; } -impl TypeOptionCellDataSerde for NumberTypeOption { +impl CellDataProtobufEncoder for NumberTypeOption { fn protobuf_encode( &self, cell_data: ::CellData, ) -> ::CellProtobufType { ProtobufStr::from(cell_data.0) } - - fn parse_cell(&self, cell: &Cell) -> FlowyResult<::CellData> { - Ok(NumberCellData::from(cell)) - } } #[async_trait] @@ -112,7 +66,7 @@ impl TypeOptionTransform for NumberTypeOption { ); for (row_id, cell_data) in rows { if let Ok(num_cell) = self - .parse_cell(&cell_data) + .decode_cell(&cell_data) .and_then(|num_cell_data| self.format_cell_data(num_cell_data).map_err(Into::into)) { database @@ -134,7 +88,7 @@ impl TypeOptionTransform for NumberTypeOption { impl CellDataDecoder for NumberTypeOption { fn decode_cell(&self, cell: &Cell) -> FlowyResult<::CellData> { - let num_cell_data = self.parse_cell(cell)?; + let num_cell_data = Self::CellData::from(cell); Ok(NumberCellData::from( self.format_cell_data(num_cell_data)?.to_string(), )) @@ -158,11 +112,6 @@ impl CellDataDecoder for NumberTypeOption { self.format_cell_data(num_cell).ok()?.to_string(), )) } - - fn numeric_cell(&self, cell: &Cell) -> Option { - let num_cell_data = self.parse_cell(cell).ok()?; - num_cell_data.0.parse::().ok() - } } pub type NumberCellChangeset = String; diff --git a/frontend/rust-lib/flowy-database2/src/services/field/type_options/relation_type_option/relation.rs b/frontend/rust-lib/flowy-database2/src/services/field/type_options/relation_type_option/relation.rs index 6f1cfb526a..09f8345717 100644 --- a/frontend/rust-lib/flowy-database2/src/services/field/type_options/relation_type_option/relation.rs +++ b/frontend/rust-lib/flowy-database2/src/services/field/type_options/relation_type_option/relation.rs @@ -3,17 +3,18 @@ use std::cmp::Ordering; use collab_database::fields::relation_type_option::RelationTypeOption; use collab_database::rows::Cell; +use collab_database::template::relation_parse::RelationCellData; use flowy_error::FlowyResult; use crate::entities::{RelationCellDataPB, RelationFilterPB}; use crate::services::cell::{CellDataChangeset, CellDataDecoder}; use crate::services::field::{ - default_order, TypeOption, TypeOptionCellDataCompare, TypeOptionCellDataFilter, - TypeOptionCellDataSerde, TypeOptionTransform, + default_order, CellDataProtobufEncoder, TypeOption, TypeOptionCellDataCompare, + TypeOptionCellDataFilter, TypeOptionTransform, }; use crate::services::sort::SortCondition; -use super::{RelationCellChangeset, RelationCellData}; +use super::RelationCellChangeset; impl TypeOption for RelationTypeOption { type CellData = RelationCellData; @@ -32,8 +33,7 @@ impl CellDataChangeset for RelationTypeOption { let cell_data = RelationCellData { row_ids: changeset.inserted_row_ids, }; - - return Ok(((&cell_data).into(), cell_data)); + return Ok(((cell_data.clone()).into(), cell_data)); } let cell_data: RelationCellData = cell.as_ref().unwrap().into(); @@ -51,22 +51,14 @@ impl CellDataChangeset for RelationTypeOption { let cell_data = RelationCellData { row_ids }; - Ok(((&cell_data).into(), cell_data)) + Ok(((cell_data.clone()).into(), cell_data)) } } impl CellDataDecoder for RelationTypeOption { - fn decode_cell(&self, cell: &Cell) -> FlowyResult { - Ok(cell.into()) - } - fn stringify_cell_data(&self, cell_data: RelationCellData) -> String { cell_data.to_string() } - - fn numeric_cell(&self, _cell: &Cell) -> Option { - None - } } impl TypeOptionCellDataCompare for RelationTypeOption { @@ -88,12 +80,8 @@ impl TypeOptionCellDataFilter for RelationTypeOption { impl TypeOptionTransform for RelationTypeOption {} -impl TypeOptionCellDataSerde for RelationTypeOption { +impl CellDataProtobufEncoder for RelationTypeOption { fn protobuf_encode(&self, cell_data: RelationCellData) -> RelationCellDataPB { cell_data.into() } - - fn parse_cell(&self, cell: &Cell) -> FlowyResult { - Ok(cell.into()) - } } diff --git a/frontend/rust-lib/flowy-database2/src/services/field/type_options/relation_type_option/relation_entities.rs b/frontend/rust-lib/flowy-database2/src/services/field/type_options/relation_type_option/relation_entities.rs index c8911a2ffe..6c61bca8fe 100644 --- a/frontend/rust-lib/flowy-database2/src/services/field/type_options/relation_type_option/relation_entities.rs +++ b/frontend/rust-lib/flowy-database2/src/services/field/type_options/relation_type_option/relation_entities.rs @@ -1,82 +1,4 @@ -use std::sync::Arc; - -use collab::preclude::Any; -use collab_database::rows::{new_cell_builder, Cell, RowId}; - -use crate::entities::FieldType; -use crate::services::field::{TypeOptionCellData, CELL_DATA}; - -#[derive(Debug, Clone, Default)] -pub struct RelationCellData { - pub row_ids: Vec, -} - -impl From<&Cell> for RelationCellData { - fn from(value: &Cell) -> Self { - let row_ids = match value.get(CELL_DATA) { - Some(Any::Array(array)) => array - .iter() - .flat_map(|item| { - if let Any::String(string) = item { - Some(RowId::from(string.clone().to_string())) - } else { - None - } - }) - .collect(), - _ => vec![], - }; - Self { row_ids } - } -} - -impl From<&RelationCellData> for Cell { - fn from(value: &RelationCellData) -> Self { - let data = Any::Array(Arc::from( - value - .row_ids - .clone() - .into_iter() - .map(|id| Any::String(Arc::from(id.to_string()))) - .collect::>(), - )); - let mut cell = new_cell_builder(FieldType::Relation); - cell.insert(CELL_DATA.into(), data); - cell - } -} - -impl From for RelationCellData { - fn from(s: String) -> Self { - if s.is_empty() { - return RelationCellData { row_ids: vec![] }; - } - - let ids = s - .split(", ") - .map(|id| id.to_string().into()) - .collect::>(); - - RelationCellData { row_ids: ids } - } -} - -impl TypeOptionCellData for RelationCellData { - fn is_cell_empty(&self) -> bool { - self.row_ids.is_empty() - } -} - -impl ToString for RelationCellData { - fn to_string(&self) -> String { - self - .row_ids - .iter() - .map(|id| id.to_string()) - .collect::>() - .join(", ") - } -} +use collab_database::rows::RowId; #[derive(Debug, Clone, Default)] pub struct RelationCellChangeset { diff --git a/frontend/rust-lib/flowy-database2/src/services/field/type_options/selection_type_option/multi_select_type_option.rs b/frontend/rust-lib/flowy-database2/src/services/field/type_options/selection_type_option/multi_select_type_option.rs index 896c3cbae2..3a24740ee7 100644 --- a/frontend/rust-lib/flowy-database2/src/services/field/type_options/selection_type_option/multi_select_type_option.rs +++ b/frontend/rust-lib/flowy-database2/src/services/field/type_options/selection_type_option/multi_select_type_option.rs @@ -1,8 +1,8 @@ use crate::entities::{FieldType, SelectOptionCellDataPB, SelectOptionFilterPB}; use crate::services::cell::CellDataChangeset; use crate::services::field::{ - default_order, SelectOptionCellChangeset, SelectTypeOptionSharedAction, TypeOption, - TypeOptionCellDataCompare, TypeOptionCellDataFilter, TypeOptionCellDataSerde, + default_order, CellDataProtobufEncoder, SelectOptionCellChangeset, SelectTypeOptionSharedAction, + TypeOption, TypeOptionCellDataCompare, TypeOptionCellDataFilter, }; use crate::services::sort::SortCondition; @@ -22,17 +22,13 @@ impl TypeOption for MultiSelectTypeOption { type CellFilter = SelectOptionFilterPB; } -impl TypeOptionCellDataSerde for MultiSelectTypeOption { +impl CellDataProtobufEncoder for MultiSelectTypeOption { fn protobuf_encode( &self, cell_data: ::CellData, ) -> ::CellProtobufType { self.get_selected_options(cell_data).into() } - - fn parse_cell(&self, cell: &Cell) -> FlowyResult<::CellData> { - Ok(SelectOptionIds::from(cell)) - } } impl SelectTypeOptionSharedAction for MultiSelectTypeOption { @@ -89,7 +85,7 @@ impl CellDataChangeset for MultiSelectTypeOption { }, }; Ok(( - select_option_ids.to_cell_data(FieldType::MultiSelect), + select_option_ids.to_cell(FieldType::MultiSelect), select_option_ids, )) } diff --git a/frontend/rust-lib/flowy-database2/src/services/field/type_options/selection_type_option/select_type_option.rs b/frontend/rust-lib/flowy-database2/src/services/field/type_options/selection_type_option/select_type_option.rs index 095830aa25..b1f9f0c935 100644 --- a/frontend/rust-lib/flowy-database2/src/services/field/type_options/selection_type_option/select_type_option.rs +++ b/frontend/rust-lib/flowy-database2/src/services/field/type_options/selection_type_option/select_type_option.rs @@ -2,7 +2,7 @@ use crate::entities::{CheckboxCellDataPB, FieldType, SelectOptionCellDataPB}; use crate::services::cell::{CellDataDecoder, CellProtobufBlobParser}; use crate::services::field::selection_type_option::type_option_transform::SelectOptionTypeOptionTransformHelper; use crate::services::field::{ - StringCellData, TypeOption, TypeOptionCellData, TypeOptionCellDataSerde, TypeOptionTransform, + CellDataProtobufEncoder, StringCellData, TypeOption, TypeOptionTransform, }; use async_trait::async_trait; use bytes::Bytes; @@ -16,12 +16,6 @@ use collab_database::rows::Cell; use flowy_error::{internal_error, ErrorCode, FlowyResult}; use std::str::FromStr; -impl TypeOptionCellData for SelectOptionIds { - fn is_cell_empty(&self) -> bool { - self.is_empty() - } -} - /// Defines the shared actions used by SingleSelect or Multi-Select. pub trait SelectTypeOptionSharedAction: Send + Sync { /// Returns `None` means there is no limited @@ -105,12 +99,8 @@ where impl CellDataDecoder for T where T: - SelectTypeOptionSharedAction + TypeOption + TypeOptionCellDataSerde, + SelectTypeOptionSharedAction + TypeOption + CellDataProtobufEncoder, { - fn decode_cell(&self, cell: &Cell) -> FlowyResult<::CellData> { - self.parse_cell(cell) - } - fn decode_cell_with_transform( &self, cell: &Cell, @@ -150,10 +140,6 @@ where .collect::>() .join(SELECTION_IDS_SEPARATOR) } - - fn numeric_cell(&self, _cell: &Cell) -> Option { - None - } } pub fn select_type_option_from_field( diff --git a/frontend/rust-lib/flowy-database2/src/services/field/type_options/selection_type_option/single_select_type_option.rs b/frontend/rust-lib/flowy-database2/src/services/field/type_options/selection_type_option/single_select_type_option.rs index a97d0bf3f5..b91cb5be3b 100644 --- a/frontend/rust-lib/flowy-database2/src/services/field/type_options/selection_type_option/single_select_type_option.rs +++ b/frontend/rust-lib/flowy-database2/src/services/field/type_options/selection_type_option/single_select_type_option.rs @@ -1,8 +1,8 @@ use crate::entities::{FieldType, SelectOptionCellDataPB, SelectOptionFilterPB}; use crate::services::cell::CellDataChangeset; use crate::services::field::{ - default_order, TypeOption, TypeOptionCellDataCompare, TypeOptionCellDataFilter, - TypeOptionCellDataSerde, + default_order, CellDataProtobufEncoder, TypeOption, TypeOptionCellDataCompare, + TypeOptionCellDataFilter, }; use crate::services::field::{SelectOptionCellChangeset, SelectTypeOptionSharedAction}; use crate::services::sort::SortCondition; @@ -25,17 +25,13 @@ impl TypeOption for SingleSelectTypeOption { type CellFilter = SelectOptionFilterPB; } -impl TypeOptionCellDataSerde for SingleSelectTypeOption { +impl CellDataProtobufEncoder for SingleSelectTypeOption { fn protobuf_encode( &self, cell_data: ::CellData, ) -> ::CellProtobufType { self.get_selected_options(cell_data).into() } - - fn parse_cell(&self, cell: &Cell) -> FlowyResult<::CellData> { - Ok(SelectOptionIds::from(cell)) - } } impl SelectTypeOptionSharedAction for SingleSelectTypeOption { @@ -84,7 +80,7 @@ impl CellDataChangeset for SingleSelectTypeOption { SelectOptionIds::from(insert_option_ids) }; Ok(( - select_option_ids.to_cell_data(FieldType::SingleSelect), + select_option_ids.to_cell(FieldType::SingleSelect), select_option_ids, )) } @@ -168,6 +164,6 @@ mod tests { // delete let changeset = SelectOptionCellChangeset::from_delete_options(option_ids); let select_option_ids = single_select.apply_changeset(changeset, None).unwrap().1; - assert!(select_option_ids.is_cell_empty()); + assert!(select_option_ids.is_empty()); } } diff --git a/frontend/rust-lib/flowy-database2/src/services/field/type_options/selection_type_option/type_option_transform.rs b/frontend/rust-lib/flowy-database2/src/services/field/type_options/selection_type_option/type_option_transform.rs index ffe5bc8b95..b085bab09d 100644 --- a/frontend/rust-lib/flowy-database2/src/services/field/type_options/selection_type_option/type_option_transform.rs +++ b/frontend/rust-lib/flowy-database2/src/services/field/type_options/selection_type_option/type_option_transform.rs @@ -74,7 +74,7 @@ impl SelectOptionTypeOptionTransformHelper { row.update_cells(|cell| { cell.insert( field_id, - SelectOptionIds::from(transformed_ids).to_cell_data(new_field_type), + SelectOptionIds::from(transformed_ids).to_cell(new_field_type), ); }); }) diff --git a/frontend/rust-lib/flowy-database2/src/services/field/type_options/summary_type_option/mod.rs b/frontend/rust-lib/flowy-database2/src/services/field/type_options/summary_type_option/mod.rs index e927cc4feb..1c631a9922 100644 --- a/frontend/rust-lib/flowy-database2/src/services/field/type_options/summary_type_option/mod.rs +++ b/frontend/rust-lib/flowy-database2/src/services/field/type_options/summary_type_option/mod.rs @@ -1,2 +1 @@ pub mod summary; -pub mod summary_entities; diff --git a/frontend/rust-lib/flowy-database2/src/services/field/type_options/summary_type_option/summary.rs b/frontend/rust-lib/flowy-database2/src/services/field/type_options/summary_type_option/summary.rs index cb2d749d8a..87aed94576 100644 --- a/frontend/rust-lib/flowy-database2/src/services/field/type_options/summary_type_option/summary.rs +++ b/frontend/rust-lib/flowy-database2/src/services/field/type_options/summary_type_option/summary.rs @@ -1,14 +1,14 @@ use crate::entities::TextFilterPB; use crate::services::cell::{CellDataChangeset, CellDataDecoder}; -use crate::services::field::summary_type_option::summary_entities::SummaryCellData; use crate::services::field::type_options::util::ProtobufStr; use crate::services::field::{ - TypeOption, TypeOptionCellData, TypeOptionCellDataCompare, TypeOptionCellDataFilter, - TypeOptionCellDataSerde, TypeOptionTransform, + CellDataProtobufEncoder, TypeOption, TypeOptionCellData, TypeOptionCellDataCompare, + TypeOptionCellDataFilter, TypeOptionTransform, }; use crate::services::sort::SortCondition; use collab_database::fields::summary_type_option::SummarizationTypeOption; use collab_database::rows::Cell; +use collab_database::template::summary_parse::SummaryCellData; use flowy_error::FlowyResult; use std::cmp::Ordering; @@ -60,29 +60,17 @@ impl TypeOptionCellDataCompare for SummarizationTypeOption { } impl CellDataDecoder for SummarizationTypeOption { - fn decode_cell(&self, cell: &Cell) -> FlowyResult { - Ok(SummaryCellData::from(cell)) - } - fn stringify_cell_data(&self, cell_data: SummaryCellData) -> String { cell_data.to_string() } - - fn numeric_cell(&self, _cell: &Cell) -> Option { - None - } } impl TypeOptionTransform for SummarizationTypeOption {} -impl TypeOptionCellDataSerde for SummarizationTypeOption { +impl CellDataProtobufEncoder for SummarizationTypeOption { fn protobuf_encode( &self, cell_data: ::CellData, ) -> ::CellProtobufType { ProtobufStr::from(cell_data.0) } - - fn parse_cell(&self, cell: &Cell) -> FlowyResult<::CellData> { - Ok(SummaryCellData::from(cell)) - } } diff --git a/frontend/rust-lib/flowy-database2/src/services/field/type_options/summary_type_option/summary_entities.rs b/frontend/rust-lib/flowy-database2/src/services/field/type_options/summary_type_option/summary_entities.rs deleted file mode 100644 index ef41e2d0f5..0000000000 --- a/frontend/rust-lib/flowy-database2/src/services/field/type_options/summary_type_option/summary_entities.rs +++ /dev/null @@ -1,46 +0,0 @@ -use crate::entities::FieldType; -use crate::services::field::{TypeOptionCellData, CELL_DATA}; -use collab::util::AnyMapExt; -use collab_database::rows::{new_cell_builder, Cell}; - -#[derive(Default, Debug, Clone)] -pub struct SummaryCellData(pub String); -impl std::ops::Deref for SummaryCellData { - type Target = String; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl TypeOptionCellData for SummaryCellData { - fn is_cell_empty(&self) -> bool { - self.0.is_empty() - } -} - -impl From<&Cell> for SummaryCellData { - fn from(cell: &Cell) -> Self { - Self(cell.get_as::(CELL_DATA).unwrap_or_default()) - } -} - -impl From for Cell { - fn from(data: SummaryCellData) -> Self { - let mut cell = new_cell_builder(FieldType::Summary); - cell.insert(CELL_DATA.into(), data.0.into()); - cell - } -} - -impl ToString for SummaryCellData { - fn to_string(&self) -> String { - self.0.clone() - } -} - -impl AsRef for SummaryCellData { - fn as_ref(&self) -> &str { - &self.0 - } -} diff --git a/frontend/rust-lib/flowy-database2/src/services/field/type_options/text_type_option/text_type_option.rs b/frontend/rust-lib/flowy-database2/src/services/field/type_options/text_type_option/text_type_option.rs index 254c60cb95..a234dfd2cd 100644 --- a/frontend/rust-lib/flowy-database2/src/services/field/type_options/text_type_option/text_type_option.rs +++ b/frontend/rust-lib/flowy-database2/src/services/field/type_options/text_type_option/text_type_option.rs @@ -11,8 +11,8 @@ use crate::entities::{FieldType, TextFilterPB}; use crate::services::cell::{stringify_cell, CellDataChangeset, CellDataDecoder}; use crate::services::field::type_options::util::ProtobufStr; use crate::services::field::{ - TypeOption, TypeOptionCellData, TypeOptionCellDataCompare, TypeOptionCellDataFilter, - TypeOptionCellDataSerde, TypeOptionTransform, CELL_DATA, + CellDataProtobufEncoder, TypeOption, TypeOptionCellData, TypeOptionCellDataCompare, + TypeOptionCellDataFilter, TypeOptionTransform, CELL_DATA, }; use crate::services::sort::SortCondition; @@ -25,24 +25,16 @@ impl TypeOption for RichTextTypeOption { impl TypeOptionTransform for RichTextTypeOption {} -impl TypeOptionCellDataSerde for RichTextTypeOption { +impl CellDataProtobufEncoder for RichTextTypeOption { fn protobuf_encode( &self, cell_data: ::CellData, ) -> ::CellProtobufType { ProtobufStr::from(cell_data.0) } - - fn parse_cell(&self, cell: &Cell) -> FlowyResult<::CellData> { - Ok(StringCellData::from(cell)) - } } impl CellDataDecoder for RichTextTypeOption { - fn decode_cell(&self, cell: &Cell) -> FlowyResult<::CellData> { - Ok(StringCellData::from(cell)) - } - fn decode_cell_with_transform( &self, cell: &Cell, @@ -71,10 +63,6 @@ impl CellDataDecoder for RichTextTypeOption { fn stringify_cell_data(&self, cell_data: ::CellData) -> String { cell_data.to_string() } - - fn numeric_cell(&self, cell: &Cell) -> Option { - StringCellData::from(cell).0.parse::().ok() - } } impl CellDataChangeset for RichTextTypeOption { diff --git a/frontend/rust-lib/flowy-database2/src/services/field/type_options/time_type_option/mod.rs b/frontend/rust-lib/flowy-database2/src/services/field/type_options/time_type_option/mod.rs index d64ecf45a3..7f700fc4c7 100644 --- a/frontend/rust-lib/flowy-database2/src/services/field/type_options/time_type_option/mod.rs +++ b/frontend/rust-lib/flowy-database2/src/services/field/type_options/time_type_option/mod.rs @@ -1,6 +1,4 @@ mod time; -mod time_entities; mod time_filter; pub use time::*; -pub use time_entities::*; diff --git a/frontend/rust-lib/flowy-database2/src/services/field/type_options/time_type_option/time.rs b/frontend/rust-lib/flowy-database2/src/services/field/type_options/time_type_option/time.rs index 0d877cef32..218ac8daf4 100644 --- a/frontend/rust-lib/flowy-database2/src/services/field/type_options/time_type_option/time.rs +++ b/frontend/rust-lib/flowy-database2/src/services/field/type_options/time_type_option/time.rs @@ -1,8 +1,8 @@ use crate::entities::{TimeCellDataPB, TimeFilterPB}; use crate::services::cell::{CellDataChangeset, CellDataDecoder}; use crate::services::field::{ - TimeCellData, TypeOption, TypeOptionCellDataCompare, TypeOptionCellDataFilter, - TypeOptionCellDataSerde, TypeOptionTransform, + CellDataProtobufEncoder, TypeOption, TypeOptionCellDataCompare, TypeOptionCellDataFilter, + TypeOptionTransform, }; use crate::services::sort::SortCondition; use collab_database::fields::date_type_option::TimeTypeOption; @@ -10,6 +10,7 @@ use collab_database::fields::date_type_option::TimeTypeOption; use collab_database::rows::Cell; use flowy_error::FlowyResult; +use collab_database::template::time_parse::TimeCellData; use std::cmp::Ordering; impl TypeOption for TimeTypeOption { @@ -19,7 +20,7 @@ impl TypeOption for TimeTypeOption { type CellFilter = TimeFilterPB; } -impl TypeOptionCellDataSerde for TimeTypeOption { +impl CellDataProtobufEncoder for TimeTypeOption { fn protobuf_encode( &self, cell_data: ::CellData, @@ -31,30 +32,17 @@ impl TypeOptionCellDataSerde for TimeTypeOption { time: i64::default(), } } - - fn parse_cell(&self, cell: &Cell) -> FlowyResult<::CellData> { - Ok(TimeCellData::from(cell)) - } } impl TypeOptionTransform for TimeTypeOption {} impl CellDataDecoder for TimeTypeOption { - fn decode_cell(&self, cell: &Cell) -> FlowyResult<::CellData> { - self.parse_cell(cell) - } - fn stringify_cell_data(&self, cell_data: ::CellData) -> String { if let Some(time) = cell_data.0 { return time.to_string(); } "".to_string() } - - fn numeric_cell(&self, cell: &Cell) -> Option { - let time_cell_data = self.parse_cell(cell).ok()?; - Some(time_cell_data.0.unwrap() as f64) - } } pub type TimeCellChangeset = String; diff --git a/frontend/rust-lib/flowy-database2/src/services/field/type_options/time_type_option/time_entities.rs b/frontend/rust-lib/flowy-database2/src/services/field/type_options/time_type_option/time_entities.rs deleted file mode 100644 index f07babeda0..0000000000 --- a/frontend/rust-lib/flowy-database2/src/services/field/type_options/time_type_option/time_entities.rs +++ /dev/null @@ -1,47 +0,0 @@ -use crate::entities::FieldType; -use crate::services::field::{TypeOptionCellData, CELL_DATA}; -use collab::util::AnyMapExt; -use collab_database::rows::{new_cell_builder, Cell}; - -#[derive(Clone, Debug, Default)] -pub struct TimeCellData(pub Option); - -impl TypeOptionCellData for TimeCellData { - fn is_cell_empty(&self) -> bool { - self.0.is_none() - } -} - -impl From<&Cell> for TimeCellData { - fn from(cell: &Cell) -> Self { - Self( - cell - .get_as::(CELL_DATA) - .and_then(|data| data.parse::().ok()), - ) - } -} - -impl std::convert::From for TimeCellData { - fn from(s: String) -> Self { - Self(s.trim().to_string().parse::().ok()) - } -} - -impl ToString for TimeCellData { - fn to_string(&self) -> String { - if let Some(time) = self.0 { - time.to_string() - } else { - "".to_string() - } - } -} - -impl From<&TimeCellData> for Cell { - fn from(data: &TimeCellData) -> Self { - let mut cell = new_cell_builder(FieldType::Time); - cell.insert(CELL_DATA.into(), data.to_string().into()); - cell - } -} diff --git a/frontend/rust-lib/flowy-database2/src/services/field/type_options/timestamp_type_option/mod.rs b/frontend/rust-lib/flowy-database2/src/services/field/type_options/timestamp_type_option/mod.rs index 953bf8839e..579d1b9ea6 100644 --- a/frontend/rust-lib/flowy-database2/src/services/field/type_options/timestamp_type_option/mod.rs +++ b/frontend/rust-lib/flowy-database2/src/services/field/type_options/timestamp_type_option/mod.rs @@ -1,5 +1,2 @@ #![allow(clippy::module_inception)] mod timestamp_type_option; -mod timestamp_type_option_entities; - -pub use timestamp_type_option_entities::*; diff --git a/frontend/rust-lib/flowy-database2/src/services/field/type_options/timestamp_type_option/timestamp_type_option.rs b/frontend/rust-lib/flowy-database2/src/services/field/type_options/timestamp_type_option/timestamp_type_option.rs index d8ce77ebe0..528f4df01b 100644 --- a/frontend/rust-lib/flowy-database2/src/services/field/type_options/timestamp_type_option/timestamp_type_option.rs +++ b/frontend/rust-lib/flowy-database2/src/services/field/type_options/timestamp_type_option/timestamp_type_option.rs @@ -1,12 +1,13 @@ use crate::entities::{DateFilterPB, TimestampCellDataPB}; use crate::services::cell::{CellDataChangeset, CellDataDecoder}; use crate::services::field::{ - default_order, TimestampCellData, TypeOption, TypeOptionCellDataCompare, - TypeOptionCellDataFilter, TypeOptionCellDataSerde, TypeOptionTransform, + default_order, CellDataProtobufEncoder, TypeOption, TypeOptionCellDataCompare, + TypeOptionCellDataFilter, TypeOptionTransform, }; use crate::services::sort::SortCondition; use collab_database::fields::timestamp_type_option::TimestampTypeOption; use collab_database::rows::Cell; +use collab_database::template::timestamp_parse::TimestampCellData; use flowy_error::{ErrorCode, FlowyError, FlowyResult}; use std::cmp::Ordering; @@ -17,7 +18,7 @@ impl TypeOption for TimestampTypeOption { type CellFilter = DateFilterPB; } -impl TypeOptionCellDataSerde for TimestampTypeOption { +impl CellDataProtobufEncoder for TimestampTypeOption { fn protobuf_encode( &self, cell_data: ::CellData, @@ -30,19 +31,11 @@ impl TypeOptionCellDataSerde for TimestampTypeOption { timestamp, } } - - fn parse_cell(&self, cell: &Cell) -> FlowyResult<::CellData> { - Ok(TimestampCellData::from(cell)) - } } impl TypeOptionTransform for TimestampTypeOption {} impl CellDataDecoder for TimestampTypeOption { - fn decode_cell(&self, cell: &Cell) -> FlowyResult<::CellData> { - self.parse_cell(cell) - } - fn stringify_cell_data(&self, cell_data: ::CellData) -> String { let timestamp = cell_data.timestamp; let (date_string, time_string) = self.formatted_date_time_from_timestamp(×tamp); @@ -52,10 +45,6 @@ impl CellDataDecoder for TimestampTypeOption { date_string } } - - fn numeric_cell(&self, _cell: &Cell) -> Option { - None - } } impl CellDataChangeset for TimestampTypeOption { diff --git a/frontend/rust-lib/flowy-database2/src/services/field/type_options/timestamp_type_option/timestamp_type_option_entities.rs b/frontend/rust-lib/flowy-database2/src/services/field/type_options/timestamp_type_option/timestamp_type_option_entities.rs deleted file mode 100644 index a1e416d688..0000000000 --- a/frontend/rust-lib/flowy-database2/src/services/field/type_options/timestamp_type_option/timestamp_type_option_entities.rs +++ /dev/null @@ -1,69 +0,0 @@ -use collab::util::AnyMapExt; -use collab_database::rows::{new_cell_builder, Cell}; -use serde::Serialize; - -use crate::{ - entities::FieldType, - services::field::{TypeOptionCellData, CELL_DATA}, -}; - -#[derive(Clone, Debug, Default, Serialize)] -pub struct TimestampCellData { - pub timestamp: Option, -} - -impl TimestampCellData { - pub fn new(timestamp: i64) -> Self { - Self { - timestamp: Some(timestamp), - } - } -} - -impl From<&Cell> for TimestampCellData { - fn from(cell: &Cell) -> Self { - let timestamp = cell - .get_as::(CELL_DATA) - .and_then(|data| data.parse::().ok()); - Self { timestamp } - } -} - -/// Wrapper for DateCellData that also contains the field type. -/// Handy struct to use when you need to convert a DateCellData to a Cell. -pub struct TimestampCellDataWrapper { - data: TimestampCellData, - field_type: FieldType, -} - -impl From<(FieldType, TimestampCellData)> for TimestampCellDataWrapper { - fn from((field_type, data): (FieldType, TimestampCellData)) -> Self { - Self { data, field_type } - } -} - -impl From for Cell { - fn from(wrapper: TimestampCellDataWrapper) -> Self { - let (field_type, data) = (wrapper.field_type, wrapper.data); - let timestamp_string = data.timestamp.unwrap_or_default().to_string(); - - let mut cell = new_cell_builder(field_type); - cell.insert(CELL_DATA.into(), timestamp_string.into()); - cell - } -} - -impl From for Cell { - fn from(data: TimestampCellData) -> Self { - let data: TimestampCellDataWrapper = (FieldType::LastEditedTime, data).into(); - Cell::from(data) - } -} - -impl TypeOptionCellData for TimestampCellData {} - -impl ToString for TimestampCellData { - fn to_string(&self) -> String { - serde_json::to_string(self).unwrap() - } -} diff --git a/frontend/rust-lib/flowy-database2/src/services/field/type_options/translate_type_option/mod.rs b/frontend/rust-lib/flowy-database2/src/services/field/type_options/translate_type_option/mod.rs index 08c163748f..d6edf7c9e9 100644 --- a/frontend/rust-lib/flowy-database2/src/services/field/type_options/translate_type_option/mod.rs +++ b/frontend/rust-lib/flowy-database2/src/services/field/type_options/translate_type_option/mod.rs @@ -1,2 +1 @@ pub mod translate; -pub mod translate_entities; diff --git a/frontend/rust-lib/flowy-database2/src/services/field/type_options/translate_type_option/translate.rs b/frontend/rust-lib/flowy-database2/src/services/field/type_options/translate_type_option/translate.rs index 899bb044ac..ecf9a4de26 100644 --- a/frontend/rust-lib/flowy-database2/src/services/field/type_options/translate_type_option/translate.rs +++ b/frontend/rust-lib/flowy-database2/src/services/field/type_options/translate_type_option/translate.rs @@ -1,14 +1,14 @@ use crate::entities::TextFilterPB; use crate::services::cell::{CellDataChangeset, CellDataDecoder}; -use crate::services::field::type_options::translate_type_option::translate_entities::TranslateCellData; use crate::services::field::type_options::util::ProtobufStr; use crate::services::field::{ - TypeOption, TypeOptionCellData, TypeOptionCellDataCompare, TypeOptionCellDataFilter, - TypeOptionCellDataSerde, TypeOptionTransform, + CellDataProtobufEncoder, TypeOption, TypeOptionCellDataCompare, TypeOptionCellDataFilter, + TypeOptionTransform, }; use crate::services::sort::SortCondition; use collab_database::fields::translate_type_option::TranslateTypeOption; use collab_database::rows::Cell; +use collab_database::template::translate_parse::TranslateCellData; use flowy_error::FlowyResult; use std::cmp::Ordering; @@ -47,7 +47,7 @@ impl TypeOptionCellDataCompare for TranslateTypeOption { other_cell_data: &::CellData, sort_condition: SortCondition, ) -> Ordering { - match (cell_data.is_cell_empty(), other_cell_data.is_cell_empty()) { + match (cell_data.is_empty(), other_cell_data.is_empty()) { (true, true) => Ordering::Equal, (true, false) => Ordering::Greater, (false, true) => Ordering::Less, @@ -60,29 +60,17 @@ impl TypeOptionCellDataCompare for TranslateTypeOption { } impl CellDataDecoder for TranslateTypeOption { - fn decode_cell(&self, cell: &Cell) -> FlowyResult { - Ok(TranslateCellData::from(cell)) - } - fn stringify_cell_data(&self, cell_data: TranslateCellData) -> String { cell_data.to_string() } - - fn numeric_cell(&self, _cell: &Cell) -> Option { - None - } } impl TypeOptionTransform for TranslateTypeOption {} -impl TypeOptionCellDataSerde for TranslateTypeOption { +impl CellDataProtobufEncoder for TranslateTypeOption { fn protobuf_encode( &self, cell_data: ::CellData, ) -> ::CellProtobufType { ProtobufStr::from(cell_data.0) } - - fn parse_cell(&self, cell: &Cell) -> FlowyResult<::CellData> { - Ok(TranslateCellData::from(cell)) - } } diff --git a/frontend/rust-lib/flowy-database2/src/services/field/type_options/translate_type_option/translate_entities.rs b/frontend/rust-lib/flowy-database2/src/services/field/type_options/translate_type_option/translate_entities.rs deleted file mode 100644 index eefbf873da..0000000000 --- a/frontend/rust-lib/flowy-database2/src/services/field/type_options/translate_type_option/translate_entities.rs +++ /dev/null @@ -1,46 +0,0 @@ -use crate::entities::FieldType; -use crate::services::field::{TypeOptionCellData, CELL_DATA}; -use collab::util::AnyMapExt; -use collab_database::rows::{new_cell_builder, Cell}; - -#[derive(Default, Debug, Clone)] -pub struct TranslateCellData(pub String); -impl std::ops::Deref for TranslateCellData { - type Target = String; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl TypeOptionCellData for TranslateCellData { - fn is_cell_empty(&self) -> bool { - self.0.is_empty() - } -} - -impl From<&Cell> for TranslateCellData { - fn from(cell: &Cell) -> Self { - Self(cell.get_as(CELL_DATA).unwrap_or_default()) - } -} - -impl From for Cell { - fn from(data: TranslateCellData) -> Self { - let mut cell = new_cell_builder(FieldType::Translate); - cell.insert(CELL_DATA.into(), data.0.into()); - cell - } -} - -impl ToString for TranslateCellData { - fn to_string(&self) -> String { - self.0.clone() - } -} - -impl AsRef for TranslateCellData { - fn as_ref(&self) -> &str { - &self.0 - } -} diff --git a/frontend/rust-lib/flowy-database2/src/services/field/type_options/type_option.rs b/frontend/rust-lib/flowy-database2/src/services/field/type_options/type_option.rs index 7f34fbb6b2..b129f52f3c 100644 --- a/frontend/rust-lib/flowy-database2/src/services/field/type_options/type_option.rs +++ b/frontend/rust-lib/flowy-database2/src/services/field/type_options/type_option.rs @@ -22,14 +22,14 @@ use collab_database::fields::text_type_option::RichTextTypeOption; use collab_database::fields::timestamp_type_option::TimestampTypeOption; use collab_database::fields::translate_type_option::TranslateTypeOption; use collab_database::fields::url_type_option::URLTypeOption; -use collab_database::fields::TypeOptionData; +use collab_database::fields::{TypeOptionCellReader, TypeOptionData}; use collab_database::rows::Cell; -use flowy_error::FlowyResult; +pub use collab_database::template::util::TypeOptionCellData; use protobuf::ProtobufError; use std::cmp::Ordering; use std::fmt::Debug; -pub trait TypeOption: From + Into { +pub trait TypeOption: From + Into + TypeOptionCellReader { /// `CellData` represents the decoded model for the current type option. Each of them must /// implement the From<&Cell> trait. If the `Cell` cannot be decoded into this type, the default /// value will be returned. @@ -71,7 +71,7 @@ pub trait TypeOption: From + Into { /// /// This trait ensures that a type which implements both `TypeOption` and `TypeOptionCellDataSerde` can /// be converted to and from a corresponding `Protobuf struct`, and can be parsed from an opaque [Cell] structure. -pub trait TypeOptionCellDataSerde: TypeOption { +pub trait CellDataProtobufEncoder: TypeOption { /// Encode the cell data into corresponding `Protobuf struct`. /// For example: /// FieldType::URL => URLCellDataPB @@ -80,20 +80,6 @@ pub trait TypeOptionCellDataSerde: TypeOption { &self, cell_data: ::CellData, ) -> ::CellProtobufType; - - /// Parse the opaque [Cell] to corresponding data struct. - /// The [Cell] is a map that stores list of key/value data. Each [TypeOption::CellData] - /// should implement the From<&Cell> trait to parse the [Cell] to corresponding data struct. - fn parse_cell(&self, cell: &Cell) -> FlowyResult<::CellData>; -} - -/// This trait that provides methods to extend the [TypeOption::CellData] functionalities. -pub trait TypeOptionCellData { - /// Checks if the cell content is considered empty based on certain criteria. e.g. empty text, - /// no date selected, no selected options - fn is_cell_empty(&self) -> bool { - false - } } #[async_trait] diff --git a/frontend/rust-lib/flowy-database2/src/services/field/type_options/type_option_cell.rs b/frontend/rust-lib/flowy-database2/src/services/field/type_options/type_option_cell.rs index 3a105148c6..27b2f05f9a 100644 --- a/frontend/rust-lib/flowy-database2/src/services/field/type_options/type_option_cell.rs +++ b/frontend/rust-lib/flowy-database2/src/services/field/type_options/type_option_cell.rs @@ -1,8 +1,8 @@ use crate::entities::FieldType; use crate::services::cell::{CellCache, CellDataChangeset, CellDataDecoder, CellProtobufBlob}; use crate::services::field::{ - TypeOption, TypeOptionCellData, TypeOptionCellDataCompare, TypeOptionCellDataFilter, - TypeOptionCellDataSerde, TypeOptionTransform, + CellDataProtobufEncoder, TypeOption, TypeOptionCellData, TypeOptionCellDataCompare, + TypeOptionCellDataFilter, TypeOptionTransform, }; use crate::services::sort::SortCondition; use collab::preclude::Any; @@ -95,7 +95,7 @@ pub trait TypeOptionCellDataHandler: Send + Sync + 'static { fn handle_numeric_cell(&self, cell: &Cell) -> Option; - fn handle_is_cell_empty(&self, cell: &Cell, field: &Field) -> bool; + fn handle_is_empty(&self, cell: &Cell, field: &Field) -> bool; } #[derive(Debug)] @@ -155,7 +155,7 @@ where T: TypeOption + CellDataDecoder + CellDataChangeset - + TypeOptionCellDataSerde + + CellDataProtobufEncoder + TypeOptionTransform + TypeOptionCellDataFilter + TypeOptionCellDataCompare @@ -251,7 +251,7 @@ where T: TypeOption + CellDataDecoder + CellDataChangeset - + TypeOptionCellDataSerde + + CellDataProtobufEncoder + TypeOptionTransform + TypeOptionCellDataFilter + TypeOptionCellDataCompare @@ -270,7 +270,6 @@ where field_rev: &Field, ) -> FlowyResult { let cell_data = self.get_cell_data(cell, field_rev).unwrap_or_default(); - CellProtobufBlob::from(self.protobuf_encode(cell_data)) } @@ -345,7 +344,7 @@ where self.numeric_cell(cell) } - fn handle_is_cell_empty(&self, cell: &Cell, field: &Field) -> bool { + fn handle_is_empty(&self, cell: &Cell, field: &Field) -> bool { let cell_data = self.get_cell_data(cell, field).unwrap_or_default(); cell_data.is_cell_empty() diff --git a/frontend/rust-lib/flowy-database2/src/services/field/type_options/url_type_option/url_type_option.rs b/frontend/rust-lib/flowy-database2/src/services/field/type_options/url_type_option/url_type_option.rs index 0a7da1298b..907837c0fc 100644 --- a/frontend/rust-lib/flowy-database2/src/services/field/type_options/url_type_option/url_type_option.rs +++ b/frontend/rust-lib/flowy-database2/src/services/field/type_options/url_type_option/url_type_option.rs @@ -1,7 +1,7 @@ use crate::entities::{FieldType, TextFilterPB, URLCellDataPB}; use crate::services::cell::{CellDataChangeset, CellDataDecoder}; use crate::services::field::{ - TypeOption, TypeOptionCellDataCompare, TypeOptionCellDataFilter, TypeOptionCellDataSerde, + CellDataProtobufEncoder, TypeOption, TypeOptionCellDataCompare, TypeOptionCellDataFilter, TypeOptionTransform, }; use crate::services::sort::SortCondition; @@ -63,23 +63,16 @@ impl TypeOptionTransform for URLTypeOption { } } -impl TypeOptionCellDataSerde for URLTypeOption { +impl CellDataProtobufEncoder for URLTypeOption { fn protobuf_encode( &self, cell_data: ::CellData, ) -> ::CellProtobufType { cell_data.into() } - - fn parse_cell(&self, cell: &Cell) -> FlowyResult<::CellData> { - Ok(URLCellData::from(cell)) - } } impl CellDataDecoder for URLTypeOption { - fn decode_cell(&self, cell: &Cell) -> FlowyResult<::CellData> { - self.parse_cell(cell) - } fn decode_cell_with_transform( &self, cell: &Cell, @@ -95,10 +88,6 @@ impl CellDataDecoder for URLTypeOption { fn stringify_cell_data(&self, cell_data: ::CellData) -> String { cell_data.data } - - fn numeric_cell(&self, _cell: &Cell) -> Option { - None - } } pub type URLCellChangeset = String; diff --git a/frontend/rust-lib/flowy-database2/src/services/field/type_options/url_type_option/url_type_option_entities.rs b/frontend/rust-lib/flowy-database2/src/services/field/type_options/url_type_option/url_type_option_entities.rs index 2a322c2a20..b9d1a07823 100644 --- a/frontend/rust-lib/flowy-database2/src/services/field/type_options/url_type_option/url_type_option_entities.rs +++ b/frontend/rust-lib/flowy-database2/src/services/field/type_options/url_type_option/url_type_option_entities.rs @@ -6,13 +6,6 @@ use flowy_error::{internal_error, FlowyResult}; use crate::entities::URLCellDataPB; use crate::services::cell::CellProtobufBlobParser; -use crate::services::field::TypeOptionCellData; - -impl TypeOptionCellData for URLCellData { - fn is_cell_empty(&self) -> bool { - self.data.is_empty() - } -} impl From for URLCellDataPB { fn from(data: URLCellData) -> Self { diff --git a/frontend/rust-lib/flowy-database2/src/services/filter/controller.rs b/frontend/rust-lib/flowy-database2/src/services/filter/controller.rs index edb9783154..13ebd615ae 100644 --- a/frontend/rust-lib/flowy-database2/src/services/filter/controller.rs +++ b/frontend/rust-lib/flowy-database2/src/services/filter/controller.rs @@ -7,6 +7,7 @@ use collab::lock::RwLock; use collab_database::database::gen_database_filter_id; use collab_database::fields::Field; use collab_database::rows::{Cell, Cells, Row, RowDetail, RowId}; +use collab_database::template::timestamp_parse::TimestampCellData; use dashmap::DashMap; use flowy_error::FlowyResult; use lib_infra::priority_task::{QualityOfService, Task, TaskContent, TaskDispatcher}; @@ -20,7 +21,7 @@ use crate::entities::filter_entities::*; use crate::entities::{FieldType, InsertedRowPB, RowMetaPB}; use crate::services::cell::CellCache; use crate::services::database_view::{DatabaseViewChanged, DatabaseViewChangedNotifier}; -use crate::services::field::{TimestampCellData, TimestampCellDataWrapper, TypeOptionCellExt}; +use crate::services::field::TypeOptionCellExt; use crate::services::filter::{Filter, FilterChangeset, FilterInner, FilterResultNotification}; #[async_trait] @@ -523,9 +524,8 @@ fn apply_filter( } else { row.modified_at }; - let cell = - TimestampCellDataWrapper::from((*field_type, TimestampCellData::new(timestamp))); - Some(cell.into()) + let cell = TimestampCellData::new(Some(timestamp)).to_cell(field.field_type); + Some(cell) }, _ => None, }; diff --git a/frontend/rust-lib/flowy-database2/src/services/group/controller_impls/date_controller.rs b/frontend/rust-lib/flowy-database2/src/services/group/controller_impls/date_controller.rs index dbf8bdda10..f3d03c6856 100644 --- a/frontend/rust-lib/flowy-database2/src/services/group/controller_impls/date_controller.rs +++ b/frontend/rust-lib/flowy-database2/src/services/group/controller_impls/date_controller.rs @@ -12,7 +12,8 @@ use crate::entities::{ FieldType, GroupPB, GroupRowsNotificationPB, InsertedGroupPB, InsertedRowPB, RowMetaPB, }; use crate::services::cell::insert_date_cell; -use crate::services::field::{DateCellDataParser, TypeOption}; +use crate::services::field::date_filter::DateCellDataParser; +use crate::services::field::TypeOption; use crate::services::group::action::GroupCustomize; use crate::services::group::configuration::GroupControllerContext; use crate::services::group::controller::BaseGroupController; diff --git a/frontend/rust-lib/flowy-database2/src/services/share/csv/export.rs b/frontend/rust-lib/flowy-database2/src/services/share/csv/export.rs index fd1b3ee70d..c556f7ad3a 100644 --- a/frontend/rust-lib/flowy-database2/src/services/share/csv/export.rs +++ b/frontend/rust-lib/flowy-database2/src/services/share/csv/export.rs @@ -1,6 +1,7 @@ use collab_database::database::Database; use collab_database::fields::Field; use collab_database::rows::Cell; +use collab_database::template::timestamp_parse::TimestampCellData; use futures::StreamExt; use indexmap::IndexMap; @@ -8,7 +9,6 @@ use flowy_error::{FlowyError, FlowyResult}; use crate::entities::FieldType; use crate::services::cell::stringify_cell; -use crate::services::field::{TimestampCellData, TimestampCellDataWrapper}; #[derive(Debug, Clone, Copy)] pub enum CSVFormat { @@ -72,7 +72,7 @@ impl CSVExport { } else { TimestampCellData::new(row.modified_at) }; - let cell = Cell::from(TimestampCellDataWrapper::from((field_type, cell_data))); + let cell = cell_data.to_cell(field.field_type); stringify(&cell, field, style) }, _ => match row.cells.get(field_id) { diff --git a/frontend/rust-lib/flowy-database2/src/services/sort/controller.rs b/frontend/rust-lib/flowy-database2/src/services/sort/controller.rs index f46534494b..b5bdbdc460 100644 --- a/frontend/rust-lib/flowy-database2/src/services/sort/controller.rs +++ b/frontend/rust-lib/flowy-database2/src/services/sort/controller.rs @@ -6,7 +6,7 @@ use std::sync::Arc; use collab_database::fields::Field; use collab_database::rows::{Cell, Row, RowId}; - +use collab_database::template::timestamp_parse::TimestampCellData; use rayon::prelude::ParallelSliceMut; use serde::{Deserialize, Serialize}; use tokio::sync::RwLock as TokioRwLock; @@ -18,9 +18,7 @@ use crate::entities::SortChangesetNotificationPB; use crate::entities::{FieldType, SortWithIndexPB}; use crate::services::cell::CellCache; use crate::services::database_view::{DatabaseViewChanged, DatabaseViewChangedNotifier}; -use crate::services::field::{ - default_order, TimestampCellData, TimestampCellDataWrapper, TypeOptionCellExt, -}; +use crate::services::field::{default_order, TypeOptionCellExt}; use crate::services::sort::{ ReorderAllRowsResult, ReorderSingleRowResult, Sort, SortChangeset, SortCondition, }; @@ -308,10 +306,10 @@ fn cmp_row( (left.modified_at, right.modified_at) }; let (left_cell, right_cell) = ( - TimestampCellDataWrapper::from((field_type, TimestampCellData::new(left_cell))), - TimestampCellDataWrapper::from((field_type, TimestampCellData::new(right_cell))), + TimestampCellData::new(left_cell).to_cell(field_rev.field_type), + TimestampCellData::new(right_cell).to_cell(field_rev.field_type), ); - Some((Some(left_cell.into()), Some(right_cell.into()))) + Some((Some(left_cell), Some(right_cell))) }, _ => None, }; diff --git a/frontend/rust-lib/flowy-database2/tests/database/cell_test/test.rs b/frontend/rust-lib/flowy-database2/tests/database/cell_test/test.rs index 6a467520f4..379d558b8e 100644 --- a/frontend/rust-lib/flowy-database2/tests/database/cell_test/test.rs +++ b/frontend/rust-lib/flowy-database2/tests/database/cell_test/test.rs @@ -3,10 +3,14 @@ use collab_database::fields::date_type_option::DateCellData; use collab_database::fields::media_type_option::{MediaFile, MediaFileType, MediaUploadType}; use collab_database::fields::select_type_option::{MultiSelectTypeOption, SingleSelectTypeOption}; use collab_database::fields::url_type_option::URLCellData; +use collab_database::template::time_parse::TimeCellData; use flowy_database2::entities::{FieldType, MediaCellChangeset}; +use flowy_database2::services::field::checklist_filter::{ + ChecklistCellChangeset, ChecklistCellInsertChangeset, +}; +use flowy_database2::services::field::date_filter::DateCellChangeset; use flowy_database2::services::field::{ - ChecklistCellChangeset, ChecklistCellInsertChangeset, DateCellChangeset, RelationCellChangeset, - SelectOptionCellChangeset, StringCellData, TimeCellData, + RelationCellChangeset, SelectOptionCellChangeset, StringCellData, }; use lib_infra::box_any::BoxAny; use std::time::Duration; diff --git a/frontend/rust-lib/flowy-database2/tests/database/database_editor.rs b/frontend/rust-lib/flowy-database2/tests/database/database_editor.rs index 1117d51873..852a4ea591 100644 --- a/frontend/rust-lib/flowy-database2/tests/database/database_editor.rs +++ b/frontend/rust-lib/flowy-database2/tests/database/database_editor.rs @@ -17,7 +17,7 @@ use event_integration_test::EventIntegrationTest; use flowy_database2::entities::{DatabasePB, FieldType, FilterPB, RowMetaPB}; use flowy_database2::services::database::DatabaseEditor; -use flowy_database2::services::field::checklist_type_option::ChecklistCellChangeset; +use flowy_database2::services::field::checklist_filter::ChecklistCellChangeset; use flowy_database2::services::field::SelectOptionCellChangeset; use flowy_database2::services::share::csv::{CSVFormat, ImportResult}; use flowy_error::FlowyResult; diff --git a/frontend/rust-lib/flowy-database2/tests/database/field_test/util.rs b/frontend/rust-lib/flowy-database2/tests/database/field_test/util.rs index 54c5d06bdc..0728cbab95 100644 --- a/frontend/rust-lib/flowy-database2/tests/database/field_test/util.rs +++ b/frontend/rust-lib/flowy-database2/tests/database/field_test/util.rs @@ -78,6 +78,7 @@ pub fn create_timestamp_field(grid_id: &str, field_type: FieldType) -> (CreateFi time_format: TimeFormat::TwentyFourHour, include_time: true, field_type: field_type.into(), + timezone: None, }; let field: Field = match field_type { diff --git a/frontend/rust-lib/flowy-database2/tests/database/filter_test/checklist_filter_test.rs b/frontend/rust-lib/flowy-database2/tests/database/filter_test/checklist_filter_test.rs index 7be2310ef5..88d48bceca 100644 --- a/frontend/rust-lib/flowy-database2/tests/database/filter_test/checklist_filter_test.rs +++ b/frontend/rust-lib/flowy-database2/tests/database/filter_test/checklist_filter_test.rs @@ -1,6 +1,6 @@ use crate::database::filter_test::script::{DatabaseFilterTest, FilterRowChanged}; +use collab_database::template::check_list_parse::ChecklistCellData; use flowy_database2::entities::{ChecklistFilterConditionPB, ChecklistFilterPB, FieldType}; -use flowy_database2::services::field::checklist_type_option::ChecklistCellData; use lib_infra::box_any::BoxAny; #[tokio::test] diff --git a/frontend/rust-lib/flowy-database2/tests/database/mock_data/board_mock_data.rs b/frontend/rust-lib/flowy-database2/tests/database/mock_data/board_mock_data.rs index f65a7f5808..c2c4851e86 100644 --- a/frontend/rust-lib/flowy-database2/tests/database/mock_data/board_mock_data.rs +++ b/frontend/rust-lib/flowy-database2/tests/database/mock_data/board_mock_data.rs @@ -60,6 +60,7 @@ pub fn make_test_board() -> DatabaseData { time_format: TimeFormat::TwentyFourHour, include_time: true, field_type: field_type.into(), + timezone: None, }; let name = match field_type { FieldType::LastEditedTime => "Last Modified", diff --git a/frontend/rust-lib/flowy-database2/tests/database/mock_data/grid_mock_data.rs b/frontend/rust-lib/flowy-database2/tests/database/mock_data/grid_mock_data.rs index 0cb77c3e51..5039a37b39 100644 --- a/frontend/rust-lib/flowy-database2/tests/database/mock_data/grid_mock_data.rs +++ b/frontend/rust-lib/flowy-database2/tests/database/mock_data/grid_mock_data.rs @@ -19,7 +19,8 @@ use strum::IntoEnumIterator; use crate::database::mock_data::{COMPLETED, FACEBOOK, GOOGLE, PAUSED, PLANNED, TWITTER}; use event_integration_test::database_event::TestRowBuilder; use flowy_database2::entities::FieldType; -use flowy_database2::services::field::{ChecklistCellInsertChangeset, FieldBuilder}; +use flowy_database2::services::field::checklist_filter::ChecklistCellInsertChangeset; +use flowy_database2::services::field::FieldBuilder; use flowy_database2::services::field_settings::default_field_settings_for_fields; pub fn make_test_grid() -> DatabaseData { @@ -67,6 +68,7 @@ pub fn make_test_grid() -> DatabaseData { time_format: TimeFormat::TwentyFourHour, include_time: true, field_type: field_type.into(), + timezone: None, }; let name = match field_type { FieldType::LastEditedTime => "Last Modified", From 67fe0d6bfd178849f43e519eb5eaeb2cd3d0278f Mon Sep 17 00:00:00 2001 From: Lucas Date: Fri, 6 Dec 2024 09:22:32 +0800 Subject: [PATCH 009/576] feat: support column and row reordering in table (#6912) * chore: update changelog * feat: add draggable in table reorder button * feat: support displaying text color, background color and font item in table cell * feat: separate gestures for popup menu and drag operations * feat: support feedback mode for table * feat: build dummy node to render table feedback * feat: disable column resize handle when dragging column * feat: higtlight the cell border when dragging * fix: unable to reorder in row * fix: do not rebuild the reorder button when reordering * feat: add reorder logic and tests * feat: reorder column * feat: reorder row * test: reorder row * fix: table attributes are broken after reordering * chore: remove unused listerner * chore: code refactor * fix: remove unused code * feat: support rendering table feedback * fix: unit test --- .../editor_plugins/base/text_robot.dart | 8 +- .../simple_table/_shared_widget.dart | 133 ------- .../simple_table_block_component.dart | 128 +------ .../simple_table_cell_block_component.dart | 195 ++-------- .../simple_table/simple_table_constants.dart | 80 +++-- .../simple_table_more_action.dart | 154 +++++--- .../simple_table_map_operation.dart | 336 +++++++++++++++++- .../simple_table_node_extension.dart | 15 + .../simple_table_operations.dart | 1 + .../simple_table_reorder_operation.dart | 120 +++++++ .../simple_table_border_builder.dart | 247 +++++++++++++ .../simple_table_column_resize_handle.dart | 110 ++++++ .../simple_table_reorder_button.dart | 258 ++++++++++++++ .../simple_table_widget.dart | 195 ++++++++++ .../simple_table_reorder_operation_test.dart | 335 +++++++++++++++++ .../simple_table_test_helper.dart | 66 +++- 16 files changed, 1887 insertions(+), 494 deletions(-) create mode 100644 frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_reorder_operation.dart create mode 100644 frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_border_builder.dart create mode 100644 frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_column_resize_handle.dart create mode 100644 frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_reorder_button.dart create mode 100644 frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_widget.dart create mode 100644 frontend/appflowy_flutter/test/unit_test/simple_table/simple_table_reorder_operation_test.dart diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/base/text_robot.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/base/text_robot.dart index bc4a28dbdd..68e1c8e5eb 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/base/text_robot.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/base/text_robot.dart @@ -109,18 +109,12 @@ class TextRobot { Position(path: next), ); await editorState.apply(transaction); - debugPrint( - 'AI insertNewParagraph: path: ${editorState.selection!.end.path}, index: ${editorState.selection!.endIndex}', - ); await Future.delayed(const Duration(milliseconds: 10)); } Future insertText(String text, Duration delay) async { final selection = editorState.selection; - debugPrint( - 'AI insertText: get selection, path: ${selection!.end.path}, index: ${selection.endIndex}', - ); - if (!selection.isCollapsed) { + if (selection == null || !selection.isCollapsed) { return; } final node = editorState.getNodeAtPath(selection.end.path); diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/_shared_widget.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/_shared_widget.dart index a3a306abf6..6f2e124fd8 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/_shared_widget.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/_shared_widget.dart @@ -1,5 +1,3 @@ -import 'dart:ui'; - import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart'; @@ -14,47 +12,6 @@ import 'package:provider/provider.dart'; /// when hovering on the last row / last column / last cell. bool _enableHoveringLogicV2 = true; -class SimpleTableReorderButton extends StatelessWidget { - const SimpleTableReorderButton({ - super.key, - required this.isShowingMenu, - required this.type, - }); - - final ValueNotifier isShowingMenu; - final SimpleTableMoreActionType type; - - @override - Widget build(BuildContext context) { - return ValueListenableBuilder( - valueListenable: isShowingMenu, - builder: (context, isShowingMenu, child) { - return MouseRegion( - cursor: SystemMouseCursors.click, - child: Container( - decoration: BoxDecoration( - color: isShowingMenu - ? context.simpleTableMoreActionHoverColor - : Theme.of(context).colorScheme.surface, - borderRadius: BorderRadius.circular(8.0), - border: Border.all( - color: context.simpleTableMoreActionBorderColor, - ), - ), - height: 16.0, - width: 16.0, - child: FlowySvg( - type.reorderIconSvg, - color: isShowingMenu ? Colors.white : null, - size: const Size.square(16.0), - ), - ), - ); - }, - ); - } -} - class SimpleTableAddRowHoverButton extends StatefulWidget { const SimpleTableAddRowHoverButton({ super.key, @@ -549,96 +506,6 @@ class SimpleTableBasicButton extends StatelessWidget { } } -class SimpleTableColumnResizeHandle extends StatefulWidget { - const SimpleTableColumnResizeHandle({ - super.key, - required this.node, - }); - - final Node node; - - @override - State createState() => - _SimpleTableColumnResizeHandleState(); -} - -class _SimpleTableColumnResizeHandleState - extends State { - bool isStartDragging = false; - - @override - Widget build(BuildContext context) { - return MouseRegion( - cursor: SystemMouseCursors.resizeColumn, - onEnter: (event) => context - .read() - .hoveringOnResizeHandle - .value = widget.node, - onExit: (event) { - Future.delayed(const Duration(milliseconds: 100), () { - // the onExit event will be triggered before dragging started. - // delay the hiding of the resize handle to avoid flickering. - if (!isStartDragging) { - context.read().hoveringOnResizeHandle.value = - null; - } - }); - }, - child: GestureDetector( - onHorizontalDragStart: (details) { - // disable the two-finger drag on trackpad - if (details.kind == PointerDeviceKind.trackpad) { - return; - } - isStartDragging = true; - }, - onHorizontalDragUpdate: (details) { - if (!isStartDragging) { - return; - } - context.read().updateColumnWidthInMemory( - tableCellNode: widget.node, - deltaX: details.delta.dx, - ); - }, - onHorizontalDragEnd: (details) { - if (!isStartDragging) { - return; - } - context.read().hoveringOnResizeHandle.value = - null; - isStartDragging = false; - context.read().updateColumnWidth( - tableCellNode: widget.node, - width: widget.node.columnWidth, - ); - }, - child: ValueListenableBuilder( - valueListenable: context.read().hoveringTableCell, - builder: (context, hoveringCell, child) { - return ValueListenableBuilder( - valueListenable: - context.read().hoveringOnResizeHandle, - builder: (context, hoveringOnResizeHandle, child) { - final isSameRowIndex = hoveringOnResizeHandle?.columnIndex == - widget.node.columnIndex; - return Opacity( - opacity: isSameRowIndex ? 1.0 : 0.0, - child: Container( - height: double.infinity, - width: SimpleTableConstants.resizeHandleWidth, - color: Theme.of(context).colorScheme.primary, - ), - ); - }, - ); - }, - ), - ), - ); - } -} - class SimpleTableBackgroundColorMenu extends StatefulWidget { const SimpleTableBackgroundColorMenu({ super.key, diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_block_component.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_block_component.dart index c5e2e5872e..f5eb5ea187 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_block_component.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_block_component.dart @@ -1,7 +1,7 @@ -import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_table/_shared_widget.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_table/simple_table_cell_block_component.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_table/simple_table_constants.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_table/simple_table_row_block_component.dart'; +import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_widget.dart'; import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; @@ -187,16 +187,21 @@ class _SimpleTableBlockWidgetState extends State @override Widget build(BuildContext context) { - Widget child = Transform.translate( - offset: Offset( - UniversalPlatform.isDesktop - ? -SimpleTableConstants.tableLeftPadding - : 0, - 0, - ), - child: _buildTable(), + Widget child = SimpleTableWidget( + node: node, + simpleTableContext: simpleTableContext, ); + if (UniversalPlatform.isDesktop) { + child = Transform.translate( + offset: const Offset( + -SimpleTableConstants.tableLeftPadding, + 0, + ), + child: child, + ); + } + child = Container( alignment: Alignment.topLeft, padding: padding, @@ -228,111 +233,6 @@ class _SimpleTableBlockWidgetState extends State return child; } - Widget _buildTable() { - if (UniversalPlatform.isDesktop) { - return _buildDesktopTable(); - } else { - return _buildMobileTable(); - } - } - - Widget _buildDesktopTable() { - // IntrinsicWidth and IntrinsicHeight are used to make the table size fit the content. - return MouseRegion( - onEnter: (event) => simpleTableContext.isHoveringOnTableArea.value = true, - onExit: (event) { - simpleTableContext.isHoveringOnTableArea.value = false; - }, - child: Provider.value( - value: simpleTableContext, - child: Stack( - children: [ - MouseRegion( - hitTestBehavior: HitTestBehavior.opaque, - onEnter: (event) => - simpleTableContext.isHoveringOnColumnsAndRows.value = true, - onExit: (event) { - simpleTableContext.isHoveringOnColumnsAndRows.value = false; - simpleTableContext.hoveringTableCell.value = null; - }, - child: Scrollbar( - controller: scrollController, - child: SingleChildScrollView( - controller: scrollController, - scrollDirection: Axis.horizontal, - child: Padding( - padding: SimpleTableConstants.tablePadding, - child: IntrinsicWidth( - child: IntrinsicHeight( - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: _buildRows(), - ), - ), - ), - ), - ), - ), - ), - if (UniversalPlatform.isDesktop) ...[ - SimpleTableAddColumnHoverButton( - editorState: editorState, - node: node, - ), - SimpleTableAddRowHoverButton( - editorState: editorState, - tableNode: node, - ), - SimpleTableAddColumnAndRowHoverButton( - editorState: editorState, - node: node, - ), - ], - ], - ), - ), - ); - } - - Widget _buildMobileTable() { - return Provider.value( - value: simpleTableContext, - child: SingleChildScrollView( - controller: scrollController, - scrollDirection: Axis.horizontal, - child: IntrinsicWidth( - child: IntrinsicHeight( - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: _buildRows(), - ), - ), - ), - ), - ); - } - - List _buildRows() { - final List rows = []; - - if (SimpleTableConstants.borderType == SimpleTableBorderRenderType.table) { - rows.add(const SimpleTableColumnDivider()); - } - - for (final child in node.children) { - rows.add(editorState.renderer.build(context, child)); - - if (SimpleTableConstants.borderType == - SimpleTableBorderRenderType.table) { - rows.add(const SimpleTableColumnDivider()); - } - } - - return rows; - } - void _onSelectionChanged() { final selection = editorState.selectionNotifier.value; final selectionType = editorState.selectionType; diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_cell_block_component.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_cell_block_component.dart index 1d99356696..70e618a186 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_cell_block_component.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_cell_block_component.dart @@ -1,5 +1,6 @@ -import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_table/_shared_widget.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_table/simple_table.dart'; +import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_border_builder.dart'; +import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_column_resize_handle.dart'; import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; @@ -79,6 +80,11 @@ class SimpleTableCellBlockWidgetState extends State late SimpleTableContext? simpleTableContext = context.read(); + late final borderBuilder = SimpleTableBorderBuilder( + context: context, + simpleTableContext: simpleTableContext!, + node: node, + ); ValueNotifier isEditingCellNotifier = ValueNotifier(false); @@ -120,17 +126,19 @@ class SimpleTableCellBlockWidgetState extends State clipBehavior: Clip.none, children: [ _buildCell(), - Positioned( - top: 0, - bottom: 0, - left: -SimpleTableConstants.tableLeftPadding, - child: _buildRowMoreActionButton(), - ), - Positioned( - left: 0, - right: 0, - child: _buildColumnMoreActionButton(), - ), + if (node.columnIndex == 0) + Positioned( + top: 0, + bottom: 0, + left: -SimpleTableConstants.tableLeftPadding, + child: _buildRowMoreActionButton(), + ), + if (node.rowIndex == 0) + Positioned( + left: 0, + right: 0, + child: _buildColumnMoreActionButton(), + ), Positioned( right: 0, top: node.rowIndex == 0 ? SimpleTableConstants.tableTopPadding : 0, @@ -156,27 +164,34 @@ class SimpleTableCellBlockWidgetState extends State padding: EdgeInsets.only( top: node.rowIndex == 0 ? SimpleTableConstants.tableTopPadding : 0, ), + // TODO(Lucas): find a better way to handle the multiple value listenable builder + // There's flutter pub can do that. child: ValueListenableBuilder( valueListenable: isEditingCellNotifier, builder: (context, isEditingCell, child) { return ValueListenableBuilder( valueListenable: simpleTableContext!.selectingColumn, - builder: (context, selectingColumn, child) { + builder: (context, selectingColumn, _) { return ValueListenableBuilder( valueListenable: simpleTableContext!.selectingRow, builder: (context, selectingRow, _) { - return DecoratedBox( - decoration: _buildDecoration(), - child: child!, + return ValueListenableBuilder( + valueListenable: simpleTableContext!.hoveringTableCell, + builder: (context, hoveringTableCell, _) { + return DecoratedBox( + decoration: _buildDecoration(), + child: child!, + ); + }, ); }, ); }, - child: Column( - children: node.children.map(_buildCellContent).toList(), - ), ); }, + child: Column( + children: node.children.map(_buildCellContent).toList(), + ), ), ); } @@ -199,13 +214,8 @@ class SimpleTableCellBlockWidgetState extends State } Widget _buildRowMoreActionButton() { - final columnIndex = node.columnIndex; final rowIndex = node.rowIndex; - if (columnIndex != 0) { - return const SizedBox.shrink(); - } - return SimpleTableMoreActionMenu( index: rowIndex, type: SimpleTableMoreActionType.row, @@ -214,11 +224,6 @@ class SimpleTableCellBlockWidgetState extends State Widget _buildColumnMoreActionButton() { final columnIndex = node.columnIndex; - final rowIndex = node.rowIndex; - - if (rowIndex != 0) { - return const SizedBox.shrink(); - } return SimpleTableMoreActionMenu( index: columnIndex, @@ -238,7 +243,9 @@ class SimpleTableCellBlockWidgetState extends State Decoration _buildDecoration() { final backgroundColor = _buildBackgroundColor(); - final border = _buildBorder(); + final border = borderBuilder.buildBorder( + isEditingCell: isEditingCellNotifier.value, + ); return BoxDecoration( border: border, @@ -269,29 +276,6 @@ class SimpleTableCellBlockWidgetState extends State return Theme.of(context).colorScheme.surface; } - Border? _buildBorder() { - if (SimpleTableConstants.borderType != SimpleTableBorderRenderType.cell) { - return null; - } - - final tableContext = context.watch(); - final isCellInSelectedColumn = - node.columnIndex == tableContext.selectingColumn.value; - final isCellInSelectedRow = - node.rowIndex == tableContext.selectingRow.value; - if (tableContext.isSelectingTable.value) { - return _buildSelectingTableBorder(); - } else if (isCellInSelectedColumn) { - return _buildColumnBorder(); - } else if (isCellInSelectedRow) { - return _buildRowBorder(); - } else if (isEditingCellNotifier.value) { - return _buildEditingBorder(); - } else { - return _buildCellBorder(); - } - } - bool _isInHeader() { final isHeaderColumnEnabled = node.isHeaderColumnEnabled; final isHeaderRowEnabled = node.isHeaderRowEnabled; @@ -303,111 +287,6 @@ class SimpleTableCellBlockWidgetState extends State isHeaderRowEnabled && isFirstColumn; } - /// the column border means the `VERTICAL` border of the cell - /// - /// ____ - /// | 1 | 2 | - /// | 3 | 4 | - /// |___| - /// - /// the border wrapping the cell 2 and cell 4 is the column border - Border _buildColumnBorder() { - return Border( - left: _buildHighlightBorderSide(), - right: _buildHighlightBorderSide(), - top: node.rowIndex == 0 - ? _buildHighlightBorderSide() - : _buildDefaultBorderSide(), - bottom: node.rowIndex + 1 == node.parentTableNode?.rowLength - ? _buildHighlightBorderSide() - : _buildDefaultBorderSide(), - ); - } - - /// the row border means the `HORIZONTAL` border of the cell - /// - /// ________ - /// | 1 | 2 | - /// |_______| - /// | 3 | 4 | - /// - /// the border wrapping the cell 1 and cell 2 is the row border - Border _buildRowBorder() { - return Border( - top: _buildHighlightBorderSide(), - bottom: _buildHighlightBorderSide(), - left: node.columnIndex == 0 - ? _buildHighlightBorderSide() - : _buildDefaultBorderSide(), - right: node.columnIndex + 1 == node.parentTableNode?.columnLength - ? _buildHighlightBorderSide() - : _buildDefaultBorderSide(), - ); - } - - Border _buildCellBorder() { - return Border( - top: node.rowIndex == 0 - ? _buildDefaultBorderSide() - : _buildLightBorderSide(), - bottom: node.rowIndex + 1 == node.parentTableNode?.rowLength - ? _buildDefaultBorderSide() - : _buildLightBorderSide(), - left: node.columnIndex == 0 - ? _buildDefaultBorderSide() - : _buildLightBorderSide(), - right: node.columnIndex + 1 == node.parentTableNode?.columnLength - ? _buildDefaultBorderSide() - : _buildLightBorderSide(), - ); - } - - Border _buildEditingBorder() { - return Border.all( - color: Theme.of(context).colorScheme.primary, - width: 2, - ); - } - - Border _buildSelectingTableBorder() { - final rowIndex = node.rowIndex; - final columnIndex = node.columnIndex; - - return Border( - top: - rowIndex == 0 ? _buildHighlightBorderSide() : _buildLightBorderSide(), - bottom: rowIndex + 1 == node.parentTableNode?.rowLength - ? _buildHighlightBorderSide() - : _buildLightBorderSide(), - left: columnIndex == 0 - ? _buildHighlightBorderSide() - : _buildLightBorderSide(), - right: columnIndex + 1 == node.parentTableNode?.columnLength - ? _buildHighlightBorderSide() - : _buildLightBorderSide(), - ); - } - - BorderSide _buildHighlightBorderSide() { - return BorderSide( - color: Theme.of(context).colorScheme.primary, - width: 2, - ); - } - - BorderSide _buildLightBorderSide() { - return BorderSide( - color: context.simpleTableBorderColor, - width: 0.5, - ); - } - - BorderSide _buildDefaultBorderSide() { - return BorderSide( - color: context.simpleTableBorderColor, - ); - } - void _onSelectingTableChanged() { if (mounted) { setState(() {}); diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_constants.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_constants.dart index cbf941f231..8cf73a8bcb 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_constants.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_constants.dart @@ -5,11 +5,11 @@ import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:flutter/material.dart'; import 'package:universal_platform/universal_platform.dart'; -const enableTableDebugLog = false; +const _enableTableDebugLog = false; class SimpleTableContext { SimpleTableContext() { - if (enableTableDebugLog) { + if (_enableTableDebugLog) { isHoveringOnColumnsAndRows.addListener( _onHoveringOnColumnsAndRowsChanged, ); @@ -21,39 +21,56 @@ class SimpleTableContext { selectingRow.addListener(_onSelectingRowChanged); isSelectingTable.addListener(_onSelectingTableChanged); isHoveringOnTableBlock.addListener(_onHoveringOnTableBlockChanged); + isReorderingColumn.addListener(_onDraggingColumnChanged); + isReorderingRow.addListener(_onDraggingRowChanged); } } - // the area only contains the columns and rows, - // the add row button, add column button, and add column and row button are not part of the table area + /// the area only contains the columns and rows, + /// the add row button, add column button, and add column and row button are not part of the table area final ValueNotifier isHoveringOnColumnsAndRows = ValueNotifier(false); - // the table area contains the columns and rows, - // the add row button, add column button, and add column and row button are not part of the table area, - // not including the selection area and padding + /// the table area contains the columns and rows, + /// the add row button, add column button, and add column and row button are not part of the table area, + /// not including the selection area and padding final ValueNotifier isHoveringOnTableArea = ValueNotifier(false); - // the table block area contains the table area and the add row button, add column button, and add column and row button - // also, the table block area contains the selection area and padding + /// the table block area contains the table area and the add row button, add column button, and add column and row button + /// also, the table block area contains the selection area and padding final ValueNotifier isHoveringOnTableBlock = ValueNotifier(false); - // the hovering table cell is the cell that the mouse is hovering on + /// the hovering table cell is the cell that the mouse is hovering on final ValueNotifier hoveringTableCell = ValueNotifier(null); - // the hovering on resize handle is the resize handle that the mouse is hovering on + /// the hovering on resize handle is the resize handle that the mouse is hovering on final ValueNotifier hoveringOnResizeHandle = ValueNotifier(null); - // the selecting column is the column that the user is selecting + /// the selecting column is the column that the user is selecting final ValueNotifier selectingColumn = ValueNotifier(null); - // the selecting row is the row that the user is selecting + /// the selecting row is the row that the user is selecting final ValueNotifier selectingRow = ValueNotifier(null); - // the is selecting table is the table that the user is selecting + /// the is selecting table is the table that the user is selecting final ValueNotifier isSelectingTable = ValueNotifier(false); + /// isReorderingColumn is a tuple of (isReordering, columnIndex) + final ValueNotifier<(bool, int)> isReorderingColumn = + ValueNotifier((false, -1)); + + /// isReorderingRow is a tuple of (isReordering, rowIndex) + final ValueNotifier<(bool, int)> isReorderingRow = ValueNotifier((false, -1)); + + /// reorderingOffset is the offset of the reordering + // + /// This value is only available when isReordering is true + final ValueNotifier reorderingOffset = ValueNotifier(Offset.zero); + + bool get isReordering => + isReorderingColumn.value.$1 || isReorderingRow.value.$1; + void _onHoveringOnColumnsAndRowsChanged() { - if (!enableTableDebugLog) { + if (!_enableTableDebugLog) { return; } @@ -61,7 +78,7 @@ class SimpleTableContext { } void _onHoveringTableNodeChanged() { - if (!enableTableDebugLog) { + if (!_enableTableDebugLog) { return; } @@ -74,7 +91,7 @@ class SimpleTableContext { } void _onSelectingColumnChanged() { - if (!enableTableDebugLog) { + if (!_enableTableDebugLog) { return; } @@ -82,7 +99,7 @@ class SimpleTableContext { } void _onSelectingRowChanged() { - if (!enableTableDebugLog) { + if (!_enableTableDebugLog) { return; } @@ -90,7 +107,7 @@ class SimpleTableContext { } void _onSelectingTableChanged() { - if (!enableTableDebugLog) { + if (!_enableTableDebugLog) { return; } @@ -98,7 +115,7 @@ class SimpleTableContext { } void _onHoveringOnTableBlockChanged() { - if (!enableTableDebugLog) { + if (!_enableTableDebugLog) { return; } @@ -106,13 +123,29 @@ class SimpleTableContext { } void _onHoveringOnTableAreaChanged() { - if (!enableTableDebugLog) { + if (!_enableTableDebugLog) { return; } Log.debug('isHoveringOnTableArea: ${isHoveringOnTableArea.value}'); } + void _onDraggingColumnChanged() { + if (!_enableTableDebugLog) { + return; + } + + Log.debug('isDraggingColumn: ${isReorderingColumn.value}'); + } + + void _onDraggingRowChanged() { + if (!_enableTableDebugLog) { + return; + } + + Log.debug('isDraggingRow: ${isReorderingRow.value}'); + } + void dispose() { isHoveringOnColumnsAndRows.dispose(); isHoveringOnTableBlock.dispose(); @@ -122,11 +155,14 @@ class SimpleTableContext { selectingColumn.dispose(); selectingRow.dispose(); isSelectingTable.dispose(); + isReorderingColumn.dispose(); + isReorderingRow.dispose(); + reorderingOffset.dispose(); } } class SimpleTableConstants { - // Table + /// Table static const defaultColumnWidth = 120.0; static const minimumColumnWidth = 36.0; diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_more_action.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_more_action.dart index 86a0915f01..380a62791e 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_more_action.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_more_action.dart @@ -4,6 +4,7 @@ import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_tab import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_table/simple_table_block_component.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_table/simple_table_constants.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_operations.dart'; +import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_reorder_button.dart'; import 'package:appflowy/workspace/presentation/widgets/toggle/toggle.dart'; import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:easy_localization/easy_localization.dart'; @@ -50,6 +51,14 @@ enum SimpleTableMoreActionType { return FlowySvgs.table_reorder_row_s; } } + + @override + String toString() { + return switch (this) { + SimpleTableMoreActionType.column => 'column', + SimpleTableMoreActionType.row => 'row', + }; + } } enum SimpleTableMoreAction { @@ -143,6 +152,7 @@ class _SimpleTableMoreActionMenuState extends State { @override Widget build(BuildContext context) { + final simpleTableContext = context.read(); return Align( alignment: widget.type == SimpleTableMoreActionType.row ? Alignment.centerLeft @@ -151,9 +161,24 @@ class _SimpleTableMoreActionMenuState extends State { valueListenable: isShowingMenu, builder: (context, isShowingMenu, child) { return ValueListenableBuilder( - valueListenable: - context.read().hoveringTableCell, + valueListenable: simpleTableContext.hoveringTableCell, builder: (context, hoveringTableNode, child) { + final reorderingIndex = switch (widget.type) { + SimpleTableMoreActionType.column => + simpleTableContext.isReorderingColumn.value.$2, + SimpleTableMoreActionType.row => + simpleTableContext.isReorderingRow.value.$2, + }; + final isReordering = simpleTableContext.isReordering; + if (isReordering) { + // when reordering, hide the menu for another column or row that is not the current dragging one. + if (reorderingIndex != widget.index) { + return const SizedBox.shrink(); + } else { + return child!; + } + } + final hoveringIndex = widget.type == SimpleTableMoreActionType.column ? hoveringTableNode?.columnIndex @@ -166,7 +191,6 @@ class _SimpleTableMoreActionMenuState extends State { return child!; }, child: SimpleTableMoreActionPopup( - key: ValueKey(widget.type.name + widget.index.toString()), index: widget.index, isShowingMenu: this.isShowingMenu, type: widget.type, @@ -198,6 +222,7 @@ class SimpleTableMoreActionPopup extends StatefulWidget { class _SimpleTableMoreActionPopupState extends State { late final editorState = context.read(); + SelectionGestureInterceptor? gestureInterceptor; RenderBox? get renderBox => context.findRenderObject() as RenderBox?; @@ -230,67 +255,85 @@ class _SimpleTableMoreActionPopupState @override Widget build(BuildContext context) { - final tableCellNode = - context.read().hoveringTableCell.value; + final simpleTableContext = context.read(); + final tableCellNode = simpleTableContext.hoveringTableCell.value; + final tableNode = tableCellNode?.parentTableNode; + + if (tableNode == null) { + return const SizedBox.shrink(); + } + return AppFlowyPopover( - onOpen: () { - widget.isShowingMenu.value = true; - switch (widget.type) { - case SimpleTableMoreActionType.column: - context.read().selectingColumn.value = - tableCellNode?.columnIndex; - case SimpleTableMoreActionType.row: - context.read().selectingRow.value = - tableCellNode?.rowIndex; - } - - // Workaround to clear the selection after the menu is opened. - Future.delayed(Durations.short3, () { - if (!editorState.isDisposed) { - editorState.selection = null; - } - }); - }, - onClose: () { - widget.isShowingMenu.value = false; - - // clear the selecting index - context.read().selectingColumn.value = null; - context.read().selectingRow.value = null; - }, + onOpen: () => _onOpen(tableCellNode: tableCellNode), + onClose: () => _onClose(), direction: widget.type == SimpleTableMoreActionType.row ? PopoverDirection.bottomWithCenterAligned : PopoverDirection.bottomWithLeftAligned, offset: widget.type == SimpleTableMoreActionType.row ? const Offset(24, 14) : const Offset(-14, 8), - popupBuilder: (_) { - if (tableCellNode == null) { - return const SizedBox.shrink(); - } - return MultiProvider( - providers: [ - Provider.value( - value: context.read(), - ), - Provider.value( - value: context.read(), - ), - ], - child: SimpleTableMoreActionList( - type: widget.type, - index: widget.index, - tableCellNode: tableCellNode, - ), - ); - }, - child: SimpleTableReorderButton( + clickHandler: PopoverClickHandler.gestureDetector, + popupBuilder: (_) => _buildPopup(tableCellNode: tableCellNode), + child: SimpleTableDraggableReorderButton( + editorState: editorState, + simpleTableContext: simpleTableContext, + node: tableNode, + index: widget.index, isShowingMenu: widget.isShowingMenu, type: widget.type, ), ); } + Widget _buildPopup({Node? tableCellNode}) { + if (tableCellNode == null) { + return const SizedBox.shrink(); + } + return MultiProvider( + providers: [ + Provider.value( + value: context.read(), + ), + Provider.value( + value: context.read(), + ), + ], + child: SimpleTableMoreActionList( + type: widget.type, + index: widget.index, + tableCellNode: tableCellNode, + ), + ); + } + + void _onOpen({Node? tableCellNode}) { + widget.isShowingMenu.value = true; + + switch (widget.type) { + case SimpleTableMoreActionType.column: + context.read().selectingColumn.value = + tableCellNode?.columnIndex; + case SimpleTableMoreActionType.row: + context.read().selectingRow.value = + tableCellNode?.rowIndex; + } + + // Workaround to clear the selection after the menu is opened. + Future.delayed(Durations.short3, () { + if (!editorState.isDisposed) { + editorState.selection = null; + } + }); + } + + void _onClose() { + widget.isShowingMenu.value = false; + + // clear the selecting index + context.read().selectingColumn.value = null; + context.read().selectingRow.value = null; + } + bool _isTapInBounds(Offset offset) { if (renderBox == null) { return false; @@ -506,22 +549,16 @@ class _SimpleTableMoreActionItemState extends State { _deleteRow(); break; } - break; case SimpleTableMoreAction.insertLeft: _insertColumnLeft(); - break; case SimpleTableMoreAction.insertRight: _insertColumnRight(); - break; case SimpleTableMoreAction.insertAbove: _insertRowAbove(); - break; case SimpleTableMoreAction.insertBelow: _insertRowBelow(); - break; case SimpleTableMoreAction.clearContents: _clearContent(); - break; case SimpleTableMoreAction.duplicate: switch (widget.type) { case SimpleTableMoreActionType.column: @@ -531,7 +568,6 @@ class _SimpleTableMoreActionItemState extends State { _duplicateRow(); break; } - break; default: break; } @@ -581,6 +617,8 @@ class _SimpleTableMoreActionItemState extends State { isEnableHeader.value, ); } + + PopoverContainer.of(context).close(); } void _clearContent() { diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_map_operation.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_map_operation.dart index dc05e3f2c1..5be39f5df4 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_map_operation.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_map_operation.dart @@ -10,6 +10,8 @@ enum TableMapOperationType { deleteColumn, duplicateRow, duplicateColumn, + reorderColumn, + reorderRow, } extension TableMapOperation on Node { @@ -17,6 +19,8 @@ extension TableMapOperation on Node { Node node, { required TableMapOperationType type, required int index, + // Only used for reorder column operation + int? toIndex, }) { assert(this.type == SimpleTableBlockKeys.type); @@ -39,10 +43,20 @@ extension TableMapOperation on Node { attributes = _mapRowDeletionAttributes(index); case TableMapOperationType.deleteColumn: attributes = _mapColumnDeletionAttributes(index); + case TableMapOperationType.reorderColumn: + if (toIndex != null) { + attributes = _mapColumnReorderingAttributes(index, toIndex); + } + case TableMapOperationType.reorderRow: + if (toIndex != null) { + attributes = _mapRowReorderingAttributes(index, toIndex); + } } // clear the attributes that are null - attributes?.removeWhere((key, value) => value == null); + attributes?.removeWhere( + (key, value) => value == null, + ); return attributes; } @@ -410,6 +424,7 @@ extension TableMapOperation on Node { comparator: (iKey, index) => iKey > index, filterIndex: index, ); + final rowAligns = _remapSource( this.rowAligns, index, @@ -432,6 +447,319 @@ extension TableMapOperation on Node { return attributes; } } + + /// Map the attributes of a column reordering operation. + /// + /// + /// Examples: + /// Case 1: + /// + /// When reordering a column, if the from index is greater than the to index, + /// the attributes of the table before the from index should be updated. + /// + /// Before: + /// ↓ reorder this column from index 1 to index 0 + /// | 0 | 1 | 2 | + /// | 3 | 4 | 5 | + /// + /// The original attributes of the table: + /// { + /// "rowColors": { + /// 0: "#FF0000", + /// 1: "#00FF00", + /// 2: "#0000FF", + /// } + /// } + /// + /// After reordering: + /// | 1 | 0 | 2 | + /// | 4 | 3 | 5 | + /// + /// The new attributes of the table: + /// { + /// "rowColors": { + /// 0: "#00FF00", ← The attributes of the original second column + /// 1: "#FF0000", ← The attributes of the original first column + /// 2: "#0000FF", + /// } + /// } + /// + /// Case 2: + /// + /// When reordering a column, if the from index is less than the to index, + /// the attributes of the table after the from index should be updated. + /// + /// Before: + /// ↓ reorder this column from index 1 to index 2 + /// | 0 | 1 | 2 | + /// | 3 | 4 | 5 | + /// + /// The original attributes of the table: + /// { + /// "columnColors": { + /// 0: "#FF0000", + /// 1: "#00FF00", + /// 2: "#0000FF", + /// } + /// } + /// + /// After reordering: + /// | 0 | 2 | 1 | + /// | 3 | 5 | 4 | + /// + /// The new attributes of the table: + /// { + /// "columnColors": { + /// 0: "#FF0000", + /// 1: "#0000FF", ← The attributes of the original third column + /// 2: "#00FF00", ← The attributes of the original second column + /// } + /// } + Attributes? _mapColumnReorderingAttributes(int fromIndex, int toIndex) { + final attributes = this.attributes; + try { + final duplicatedColumnColor = this.columnColors[fromIndex.toString()]; + final duplicatedColumnAlign = this.columnAligns[fromIndex.toString()]; + final duplicatedColumnWidth = this.columnWidths[fromIndex.toString()]; + + /// Case 1: fromIndex > toIndex + /// Before: + /// Row 0: | 0 | 1 | 2 | + /// Row 1: | 3 | 4 | 5 | + /// Row 2: | 6 | 7 | 8 | + /// + /// columnColors = { + /// "0": "#FF0000", + /// "1": "#00FF00", + /// "2": "#0000FF" ← Move this column (index 2) + /// } + /// + /// Move column 2 to index 0: + /// Row 0: | 2 | 0 | 1 | + /// Row 1: | 5 | 3 | 4 | + /// Row 2: | 8 | 6 | 7 | + /// + /// columnColors = { + /// "0": "#0000FF", ← Moved here + /// "1": "#FF0000", + /// "2": "#00FF00" + /// } + /// + /// Case 2: fromIndex < toIndex + /// Before: + /// Row 0: | 0 | 1 | 2 | + /// Row 1: | 3 | 4 | 5 | + /// Row 2: | 6 | 7 | 8 | + /// + /// columnColors = { + /// "0": "#FF0000" ← Move this column (index 0) + /// "1": "#00FF00", + /// "2": "#0000FF" + /// } + /// + /// Move column 0 to index 2: + /// Row 0: | 1 | 2 | 0 | + /// Row 1: | 4 | 5 | 3 | + /// Row 2: | 7 | 8 | 6 | + /// + /// columnColors = { + /// "0": "#00FF00", + /// "1": "#0000FF", + /// "2": "#FF0000" ← Moved here + /// } + final columnColors = _remapSource( + this.columnColors, + fromIndex, + increment: fromIndex > toIndex, + comparator: (iKey, index) { + if (fromIndex > toIndex) { + return iKey < fromIndex && iKey >= toIndex; + } else { + return iKey > fromIndex && iKey <= toIndex; + } + }, + filterIndex: fromIndex, + ); + + final columnAligns = _remapSource( + this.columnAligns, + fromIndex, + increment: fromIndex > toIndex, + comparator: (iKey, index) { + if (fromIndex > toIndex) { + return iKey < fromIndex && iKey >= toIndex; + } else { + return iKey > fromIndex && iKey <= toIndex; + } + }, + filterIndex: fromIndex, + ); + + final columnWidths = _remapSource( + this.columnWidths, + fromIndex, + increment: fromIndex > toIndex, + comparator: (iKey, index) { + if (fromIndex > toIndex) { + return iKey < fromIndex && iKey >= toIndex; + } else { + return iKey > fromIndex && iKey <= toIndex; + } + }, + filterIndex: fromIndex, + ); + + return attributes + .mergeValues( + SimpleTableBlockKeys.columnColors, + columnColors, + duplicatedEntry: duplicatedColumnColor != null + ? MapEntry( + toIndex.toString(), + duplicatedColumnColor, + ) + : null, + removeNullValue: true, + ) + .mergeValues( + SimpleTableBlockKeys.columnAligns, + columnAligns, + duplicatedEntry: duplicatedColumnAlign != null + ? MapEntry( + toIndex.toString(), + duplicatedColumnAlign, + ) + : null, + removeNullValue: true, + ) + .mergeValues( + SimpleTableBlockKeys.columnWidths, + columnWidths, + duplicatedEntry: duplicatedColumnWidth != null + ? MapEntry( + toIndex.toString(), + duplicatedColumnWidth, + ) + : null, + removeNullValue: true, + ); + } catch (e) { + Log.warn('Failed to map column deletion attributes: $e'); + return attributes; + } + } + + /// Map the attributes of a row reordering operation. + /// + /// See [_mapColumnReorderingAttributes] for more details. + Attributes? _mapRowReorderingAttributes(int fromIndex, int toIndex) { + final attributes = this.attributes; + try { + final duplicatedRowColor = this.rowColors[fromIndex.toString()]; + final duplicatedRowAlign = this.rowAligns[fromIndex.toString()]; + + /// Example: + /// Case 1: fromIndex > toIndex + /// Before: + /// Row 0: | 0 | 1 | 2 | + /// Row 1: | 3 | 4 | 5 | ← Move this row (index 1) + /// Row 2: | 6 | 7 | 8 | + /// + /// rowColors = { + /// "0": "#FF0000", + /// "1": "#00FF00", ← This will be moved + /// "2": "#0000FF" + /// } + /// + /// Move row 1 to index 0: + /// Row 0: | 3 | 4 | 5 | ← Moved here + /// Row 1: | 0 | 1 | 2 | + /// Row 2: | 6 | 7 | 8 | + /// + /// rowColors = { + /// "0": "#00FF00", ← Moved here + /// "1": "#FF0000", + /// "2": "#0000FF" + /// } + /// + /// Case 2: fromIndex < toIndex + /// Before: + /// Row 0: | 0 | 1 | 2 | + /// Row 1: | 3 | 4 | 5 | ← Move this row (index 1) + /// Row 2: | 6 | 7 | 8 | + /// + /// rowColors = { + /// "0": "#FF0000", + /// "1": "#00FF00", ← This will be moved + /// "2": "#0000FF" + /// } + /// + /// Move row 1 to index 2: + /// Row 0: | 0 | 1 | 2 | + /// Row 1: | 3 | 4 | 5 | + /// Row 2: | 6 | 7 | 8 | ← Moved here + /// + /// rowColors = { + /// "0": "#FF0000", + /// "1": "#0000FF", + /// "2": "#00FF00" ← Moved here + /// } + final rowColors = _remapSource( + this.rowColors, + fromIndex, + increment: fromIndex > toIndex, + comparator: (iKey, index) { + if (fromIndex > toIndex) { + return iKey < fromIndex && iKey >= toIndex; + } else { + return iKey > fromIndex && iKey <= toIndex; + } + }, + filterIndex: fromIndex, + ); + + final rowAligns = _remapSource( + this.rowAligns, + fromIndex, + increment: fromIndex > toIndex, + comparator: (iKey, index) { + if (fromIndex > toIndex) { + return iKey < fromIndex && iKey >= toIndex; + } else { + return iKey > fromIndex && iKey <= toIndex; + } + }, + filterIndex: fromIndex, + ); + + return attributes + .mergeValues( + SimpleTableBlockKeys.rowColors, + rowColors, + duplicatedEntry: duplicatedRowColor != null + ? MapEntry( + toIndex.toString(), + duplicatedRowColor, + ) + : null, + removeNullValue: true, + ) + .mergeValues( + SimpleTableBlockKeys.rowAligns, + rowAligns, + duplicatedEntry: duplicatedRowAlign != null + ? MapEntry( + toIndex.toString(), + duplicatedRowAlign, + ) + : null, + removeNullValue: true, + ); + } catch (e) { + Log.warn('Failed to map row reordering attributes: $e'); + return attributes; + } + } } /// Find the duplicated entry and remap the source. @@ -486,6 +814,7 @@ extension TableMapOperationAttributes on Attributes { String key, Map newSource, { MapEntry? duplicatedEntry, + bool removeNullValue = false, }) { final result = {...this}; @@ -493,6 +822,11 @@ extension TableMapOperationAttributes on Attributes { newSource[duplicatedEntry.key] = duplicatedEntry.value; } + if (removeNullValue) { + // remove the null value + newSource.removeWhere((key, value) => value == null); + } + result[key] = newSource; return result; diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_node_extension.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_node_extension.dart index 70f67bfe3b..c7e1f73ae2 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_node_extension.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_node_extension.dart @@ -514,4 +514,19 @@ extension TableNodeExtension on Node { return children[rowIndex].children[columnIndex]; } + + String? getTableCellContent({ + required int rowIndex, + required int columnIndex, + }) { + final cell = getTableCellNode(rowIndex: rowIndex, columnIndex: columnIndex); + if (cell == null) { + return null; + } + final content = cell.children + .map((e) => e.delta?.toPlainText()) + .where((e) => e != null) + .join('\n'); + return content; + } } diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_operations.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_operations.dart index ba5f6b47bd..c5bb8bac83 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_operations.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_operations.dart @@ -5,4 +5,5 @@ export 'simple_table_header_operation.dart'; export 'simple_table_insert_operation.dart'; export 'simple_table_map_operation.dart'; export 'simple_table_node_extension.dart'; +export 'simple_table_reorder_operation.dart'; export 'simple_table_style_operation.dart'; diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_reorder_operation.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_reorder_operation.dart new file mode 100644 index 0000000000..1bc2ad4ba7 --- /dev/null +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_reorder_operation.dart @@ -0,0 +1,120 @@ +import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_table/simple_table.dart'; +import 'package:appflowy_backend/log.dart'; +import 'package:appflowy_editor/appflowy_editor.dart'; + +extension SimpleTableReorderOperation on EditorState { + /// Reorder the column of the table. + /// + /// If the from index is equal to the to index, do nothing. + /// The node's type can be [SimpleTableCellBlockKeys.type] or [SimpleTableRowBlockKeys.type] or [SimpleTableBlockKeys.type]. + Future reorderColumn( + Node node, { + required int fromIndex, + required int toIndex, + }) async { + if (fromIndex == toIndex) { + return; + } + + final tableNode = node.parentTableNode; + + if (tableNode == null) { + assert(tableNode == null); + return; + } + + final columnLength = tableNode.columnLength; + final rowLength = tableNode.rowLength; + + if (fromIndex < 0 || + fromIndex >= columnLength || + toIndex < 0 || + toIndex >= columnLength) { + Log.warn( + 'reorder column: index out of range: fromIndex: $fromIndex, toIndex: $toIndex, column length: $columnLength', + ); + return; + } + + Log.info( + 'reorder column in table ${node.id} at fromIndex: $fromIndex, toIndex: $toIndex, column length: $columnLength, row length: $rowLength', + ); + + final attributes = tableNode.mapTableAttributes( + tableNode, + type: TableMapOperationType.reorderColumn, + index: fromIndex, + toIndex: toIndex, + ); + + final transaction = this.transaction; + for (var i = 0; i < rowLength; i++) { + final row = tableNode.children[i]; + final from = row.children[fromIndex]; + final to = row.children[toIndex]; + final path = fromIndex < toIndex ? to.path.next : to.path; + transaction.insertNode(path, from.copyWith()); + transaction.deleteNode(from); + } + if (attributes != null) { + transaction.updateNode(tableNode, attributes); + } + await apply(transaction); + } + + /// Reorder the row of the table. + /// + /// If the from index is equal to the to index, do nothing. + /// The node's type can be [SimpleTableCellBlockKeys.type] or [SimpleTableRowBlockKeys.type] or [SimpleTableBlockKeys.type]. + Future reorderRow( + Node node, { + required int fromIndex, + required int toIndex, + }) async { + if (fromIndex == toIndex) { + return; + } + + final tableNode = node.parentTableNode; + + if (tableNode == null) { + assert(tableNode == null); + return; + } + + final columnLength = tableNode.columnLength; + final rowLength = tableNode.rowLength; + + if (fromIndex < 0 || + fromIndex >= rowLength || + toIndex < 0 || + toIndex >= rowLength) { + Log.warn( + 'reorder row: index out of range: fromIndex: $fromIndex, toIndex: $toIndex, row length: $rowLength', + ); + return; + } + + Log.info( + 'reorder row in table ${node.id} at fromIndex: $fromIndex, toIndex: $toIndex, column length: $columnLength, row length: $rowLength', + ); + + final attributes = tableNode.mapTableAttributes( + tableNode, + type: TableMapOperationType.reorderRow, + index: fromIndex, + toIndex: toIndex, + ); + + final transaction = this.transaction; + final from = tableNode.children[fromIndex]; + final to = tableNode.children[toIndex]; + final path = fromIndex < toIndex ? to.path.next : to.path; + transaction.insertNode(path, from.copyWith()); + transaction.deleteNode(from); + if (attributes != null) { + transaction.updateNode(tableNode, attributes); + } + await apply(transaction); + } +} diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_border_builder.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_border_builder.dart new file mode 100644 index 0000000000..b3f7a4629c --- /dev/null +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_border_builder.dart @@ -0,0 +1,247 @@ +import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_table/simple_table.dart'; +import 'package:appflowy_editor/appflowy_editor.dart'; +import 'package:flutter/material.dart'; + +class SimpleTableBorderBuilder { + SimpleTableBorderBuilder({ + required this.context, + required this.simpleTableContext, + required this.node, + }); + + final BuildContext context; + final SimpleTableContext simpleTableContext; + final Node node; + + /// Build the border for the cell. + Border? buildBorder({bool isEditingCell = false}) { + if (SimpleTableConstants.borderType != SimpleTableBorderRenderType.cell) { + return null; + } + + // check if the cell is in the selected column + final isCellInSelectedColumn = + node.columnIndex == simpleTableContext.selectingColumn.value; + + // check if the cell is in the selected row + final isCellInSelectedRow = + node.rowIndex == simpleTableContext.selectingRow.value; + + // check if the cell is in the hovering column + final isCellInHoveringColumn = + simpleTableContext.hoveringTableCell.value?.columnIndex == + node.columnIndex; + + final isCellInHoveringRow = + simpleTableContext.hoveringTableCell.value?.rowIndex == node.rowIndex; + + // check if the cell is in the reordering column + final isReordering = simpleTableContext.isReordering; + + if (isReordering && (isCellInHoveringColumn || isCellInHoveringRow)) { + return buildReorderingBorder(); + } else if (simpleTableContext.isSelectingTable.value) { + return buildSelectingTableBorder(); + } else if (isCellInSelectedColumn) { + return buildColumnHighlightBorder(); + } else if (isCellInSelectedRow) { + return buildRowHighlightBorder(); + } else if (isEditingCell) { + return buildEditingBorder(); + } else { + return buildCellBorder(); + } + } + + /// the column border means the `VERTICAL` border of the cell + /// + /// ____ + /// | 1 | 2 | + /// | 3 | 4 | + /// |___| + /// + /// the border wrapping the cell 2 and cell 4 is the column border + Border buildColumnHighlightBorder() { + return Border( + left: _buildHighlightBorderSide(), + right: _buildHighlightBorderSide(), + top: node.rowIndex == 0 + ? _buildHighlightBorderSide() + : _buildDefaultBorderSide(), + bottom: node.rowIndex + 1 == node.parentTableNode?.rowLength + ? _buildHighlightBorderSide() + : _buildDefaultBorderSide(), + ); + } + + /// the row border means the `HORIZONTAL` border of the cell + /// + /// ________ + /// | 1 | 2 | + /// |_______| + /// | 3 | 4 | + /// + /// the border wrapping the cell 1 and cell 2 is the row border + Border buildRowHighlightBorder() { + return Border( + top: _buildHighlightBorderSide(), + bottom: _buildHighlightBorderSide(), + left: node.columnIndex == 0 + ? _buildHighlightBorderSide() + : _buildDefaultBorderSide(), + right: node.columnIndex + 1 == node.parentTableNode?.columnLength + ? _buildHighlightBorderSide() + : _buildDefaultBorderSide(), + ); + } + + /// Build the border for the reordering state. + /// + /// For example, when reordering a column, we should highlight the border of the + /// current column we're hovering. + Border buildReorderingBorder() { + final isReorderingColumn = simpleTableContext.isReorderingColumn.value.$1; + final isReorderingRow = simpleTableContext.isReorderingRow.value.$1; + + if (isReorderingColumn) { + return _buildColumnReorderingBorder(); + } else if (isReorderingRow) { + return _buildRowReorderingBorder(); + } + + return buildCellBorder(); + } + + /// Build the border for the cell without any state. + Border buildCellBorder() { + return Border( + top: node.rowIndex == 0 + ? _buildDefaultBorderSide() + : _buildLightBorderSide(), + bottom: node.rowIndex + 1 == node.parentTableNode?.rowLength + ? _buildDefaultBorderSide() + : _buildLightBorderSide(), + left: node.columnIndex == 0 + ? _buildDefaultBorderSide() + : _buildLightBorderSide(), + right: node.columnIndex + 1 == node.parentTableNode?.columnLength + ? _buildDefaultBorderSide() + : _buildLightBorderSide(), + ); + } + + /// Build the border for the editing state. + Border buildEditingBorder() { + return Border.all( + color: Theme.of(context).colorScheme.primary, + width: 2, + ); + } + + /// Build the border for the selecting table state. + Border buildSelectingTableBorder() { + final rowIndex = node.rowIndex; + final columnIndex = node.columnIndex; + + return Border( + top: + rowIndex == 0 ? _buildHighlightBorderSide() : _buildLightBorderSide(), + bottom: rowIndex + 1 == node.parentTableNode?.rowLength + ? _buildHighlightBorderSide() + : _buildLightBorderSide(), + left: columnIndex == 0 + ? _buildHighlightBorderSide() + : _buildLightBorderSide(), + right: columnIndex + 1 == node.parentTableNode?.columnLength + ? _buildHighlightBorderSide() + : _buildLightBorderSide(), + ); + } + + Border _buildColumnReorderingBorder() { + assert(simpleTableContext.isReordering); + + final isDraggingInCurrentColumn = + simpleTableContext.isReorderingColumn.value.$2 == node.columnIndex; + // if the dragging column is the current column, don't show the highlight border + if (isDraggingInCurrentColumn) { + return buildCellBorder(); + } + + final hoveringTableCell = simpleTableContext.hoveringTableCell.value; + // if the hovering column is not the current column, don't show the highlight border + final isHitCurrentCell = hoveringTableCell?.columnIndex == node.columnIndex; + if (!isHitCurrentCell) { + return buildCellBorder(); + } + + // if the dragging column index is less than the current column index, show the + // highlight border on the left side + final isLeftSide = + simpleTableContext.isReorderingColumn.value.$2 > node.columnIndex; + // if the dragging column index is greater than the current column index, show + // the highlight border on the right side + final isRightSide = + simpleTableContext.isReorderingColumn.value.$2 < node.columnIndex; + + return Border( + top: _buildDefaultBorderSide(), + bottom: _buildDefaultBorderSide(), + left: + isLeftSide ? _buildHighlightBorderSide() : _buildDefaultBorderSide(), + right: + isRightSide ? _buildHighlightBorderSide() : _buildDefaultBorderSide(), + ); + } + + Border _buildRowReorderingBorder() { + assert(simpleTableContext.isReordering); + + final isDraggingInCurrentRow = + simpleTableContext.isReorderingRow.value.$2 == node.rowIndex; + // if the dragging row is the current row, don't show the highlight border + if (isDraggingInCurrentRow) { + return buildCellBorder(); + } + + final hoveringTableCell = simpleTableContext.hoveringTableCell.value; + final isHitCurrentCell = hoveringTableCell?.rowIndex == node.rowIndex; + if (!isHitCurrentCell) { + return buildCellBorder(); + } + + final isTopSide = + simpleTableContext.isReorderingRow.value.$2 > node.rowIndex; + final isBottomSide = + simpleTableContext.isReorderingRow.value.$2 < node.rowIndex; + + return Border( + top: isTopSide ? _buildHighlightBorderSide() : _buildDefaultBorderSide(), + bottom: isBottomSide + ? _buildHighlightBorderSide() + : _buildDefaultBorderSide(), + left: _buildDefaultBorderSide(), + right: _buildDefaultBorderSide(), + ); + } + + BorderSide _buildHighlightBorderSide() { + return BorderSide( + color: Theme.of(context).colorScheme.primary, + width: 2, + ); + } + + BorderSide _buildLightBorderSide() { + return BorderSide( + color: context.simpleTableBorderColor, + width: 0.5, + ); + } + + BorderSide _buildDefaultBorderSide() { + return BorderSide( + color: context.simpleTableBorderColor, + ); + } +} diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_column_resize_handle.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_column_resize_handle.dart new file mode 100644 index 0000000000..5555c39de4 --- /dev/null +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_column_resize_handle.dart @@ -0,0 +1,110 @@ +import 'dart:ui'; + +import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart'; +import 'package:appflowy_editor/appflowy_editor.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +class SimpleTableColumnResizeHandle extends StatefulWidget { + const SimpleTableColumnResizeHandle({ + super.key, + required this.node, + }); + + final Node node; + + @override + State createState() => + _SimpleTableColumnResizeHandleState(); +} + +class _SimpleTableColumnResizeHandleState + extends State { + bool isStartDragging = false; + + @override + Widget build(BuildContext context) { + final simpleTableContext = context.read(); + + return MouseRegion( + cursor: SystemMouseCursors.resizeColumn, + onEnter: (_) => _onEnterHoverArea(), + onExit: (event) => _onExitHoverArea(), + child: GestureDetector( + onHorizontalDragStart: _onHorizontalDragStart, + onHorizontalDragUpdate: _onHorizontalDragUpdate, + onHorizontalDragEnd: _onHorizontalDragEnd, + child: ValueListenableBuilder( + valueListenable: simpleTableContext.hoveringOnResizeHandle, + builder: (context, hoveringOnResizeHandle, child) { + // when reordering a column, the resize handle should not be shown + final isSameRowIndex = hoveringOnResizeHandle?.columnIndex == + widget.node.columnIndex && + !simpleTableContext.isReordering; + return Opacity( + opacity: isSameRowIndex ? 1.0 : 0.0, + child: child, + ); + }, + child: Container( + height: double.infinity, + width: SimpleTableConstants.resizeHandleWidth, + color: Theme.of(context).colorScheme.primary, + ), + ), + ), + ); + } + + void _onEnterHoverArea() { + context.read().hoveringOnResizeHandle.value = + widget.node; + } + + void _onExitHoverArea() { + Future.delayed(const Duration(milliseconds: 100), () { + // the onExit event will be triggered before dragging started. + // delay the hiding of the resize handle to avoid flickering. + if (!isStartDragging) { + context.read().hoveringOnResizeHandle.value = null; + } + }); + } + + void _onHorizontalDragStart(DragStartDetails details) { + // disable the two-finger drag on trackpad + if (details.kind == PointerDeviceKind.trackpad) { + return; + } + + isStartDragging = true; + } + + void _onHorizontalDragUpdate(DragUpdateDetails details) { + if (!isStartDragging) { + return; + } + + // only update the column width in memory, + // the actual update will be applied in _onHorizontalDragEnd + context.read().updateColumnWidthInMemory( + tableCellNode: widget.node, + deltaX: details.delta.dx, + ); + } + + void _onHorizontalDragEnd(DragEndDetails details) { + if (!isStartDragging) { + return; + } + + isStartDragging = false; + context.read().hoveringOnResizeHandle.value = null; + + // apply the updated column width + context.read().updateColumnWidth( + tableCellNode: widget.node, + width: widget.node.columnWidth, + ); + } +} diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_reorder_button.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_reorder_button.dart new file mode 100644 index 0000000000..5a51103822 --- /dev/null +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_reorder_button.dart @@ -0,0 +1,258 @@ +import 'package:appflowy/generated/flowy_svgs.g.dart'; +import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart'; +import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_widget.dart'; +import 'package:appflowy_editor/appflowy_editor.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +class SimpleTableDraggableReorderButton extends StatelessWidget { + const SimpleTableDraggableReorderButton({ + super.key, + required this.node, + required this.index, + required this.isShowingMenu, + required this.type, + required this.editorState, + required this.simpleTableContext, + }); + + final Node node; + final int index; + final ValueNotifier isShowingMenu; + final SimpleTableMoreActionType type; + final EditorState editorState; + final SimpleTableContext simpleTableContext; + + @override + Widget build(BuildContext context) { + return Draggable( + data: index, + onDragStarted: () => _startDragging(), + onDragUpdate: (details) => _onDragUpdate(details), + onDragEnd: (_) => _stopDragging(), + feedback: SimpleTableFeedback( + editorState: editorState, + node: node, + type: type, + index: index, + ), + child: SimpleTableReorderButton( + isShowingMenu: isShowingMenu, + type: type, + ), + ); + } + + void _startDragging() { + switch (type) { + case SimpleTableMoreActionType.column: + simpleTableContext.isReorderingColumn.value = (true, index); + break; + case SimpleTableMoreActionType.row: + simpleTableContext.isReorderingRow.value = (true, index); + break; + } + } + + void _onDragUpdate(DragUpdateDetails details) { + simpleTableContext.reorderingOffset.value = details.globalPosition; + } + + void _stopDragging() { + switch (type) { + case SimpleTableMoreActionType.column: + _reorderColumn(); + case SimpleTableMoreActionType.row: + _reorderRow(); + } + + simpleTableContext.reorderingOffset.value = Offset.zero; + switch (type) { + case SimpleTableMoreActionType.column: + simpleTableContext.isReorderingColumn.value = (false, -1); + break; + case SimpleTableMoreActionType.row: + simpleTableContext.isReorderingRow.value = (false, -1); + break; + } + } + + void _reorderColumn() { + final fromIndex = simpleTableContext.isReorderingColumn.value.$2; + final toIndex = simpleTableContext.hoveringTableCell.value?.columnIndex; + if (toIndex == null) { + return; + } + + editorState.reorderColumn( + node, + fromIndex: fromIndex, + toIndex: toIndex, + ); + } + + void _reorderRow() { + final fromIndex = simpleTableContext.isReorderingRow.value.$2; + final toIndex = simpleTableContext.hoveringTableCell.value?.rowIndex; + if (toIndex == null) { + return; + } + + editorState.reorderRow( + node, + fromIndex: fromIndex, + toIndex: toIndex, + ); + } +} + +class SimpleTableReorderButton extends StatelessWidget { + const SimpleTableReorderButton({ + super.key, + required this.isShowingMenu, + required this.type, + }); + + final ValueNotifier isShowingMenu; + final SimpleTableMoreActionType type; + + @override + Widget build(BuildContext context) { + return ValueListenableBuilder( + valueListenable: isShowingMenu, + builder: (context, isShowingMenu, child) { + return MouseRegion( + cursor: SystemMouseCursors.click, + child: Container( + decoration: BoxDecoration( + color: isShowingMenu + ? context.simpleTableMoreActionHoverColor + : Theme.of(context).colorScheme.surface, + borderRadius: BorderRadius.circular(8.0), + border: Border.all( + color: context.simpleTableMoreActionBorderColor, + ), + ), + height: 16.0, + width: 16.0, + child: FlowySvg( + type.reorderIconSvg, + color: isShowingMenu ? Colors.white : null, + size: const Size.square(16.0), + ), + ), + ); + }, + ); + } +} + +class SimpleTableFeedback extends StatefulWidget { + const SimpleTableFeedback({ + super.key, + required this.editorState, + required this.node, + required this.type, + required this.index, + }); + + /// The node of the table. + /// Its type must be [SimpleTableBlockKeys.type]. + final Node node; + + /// The type of the more action. + /// + /// If the type is [SimpleTableMoreActionType.column], the feedback will use index as column index. + /// If the type is [SimpleTableMoreActionType.row], the feedback will use index as row index. + final SimpleTableMoreActionType type; + + /// The index of the column or row. + final int index; + + final EditorState editorState; + + @override + State createState() => _SimpleTableFeedbackState(); +} + +class _SimpleTableFeedbackState extends State { + final simpleTableContext = SimpleTableContext(); + late final Node dummyNode; + + @override + void initState() { + super.initState(); + + simpleTableContext.isSelectingTable.value = true; + dummyNode = _buildDummyNode(); + } + + @override + void dispose() { + simpleTableContext.dispose(); + dummyNode.dispose(); + + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Provider.value( + value: widget.editorState, + child: SimpleTableWidget( + node: dummyNode, + simpleTableContext: simpleTableContext, + enableAddColumnButton: false, + enableAddRowButton: false, + enableAddColumnAndRowButton: false, + enableHoverEffect: false, + isFeedback: true, + ), + ); + } + + /// Build the dummy node for the feedback. + /// + /// For example, + /// + /// If the type is [SimpleTableMoreActionType.row], we should build the dummy table node using the data from the first row of the table node. + /// If the type is [SimpleTableMoreActionType.column], we should build the dummy table node using the data from the first column of the table node. + Node _buildDummyNode() { + // deep copy the table node to avoid mutating the original node + final tableNode = widget.node.copyWith(); + + switch (widget.type) { + case SimpleTableMoreActionType.row: + if (widget.index >= tableNode.rowLength || widget.index < 0) { + return simpleTableBlockNode(children: []); + } + + final row = tableNode.children[widget.index]; + return tableNode.copyWith( + children: [row], + attributes: { + ...tableNode.attributes, + if (widget.index != 0) SimpleTableBlockKeys.enableHeaderRow: false, + }, + ); + case SimpleTableMoreActionType.column: + if (widget.index >= tableNode.columnLength || widget.index < 0) { + return simpleTableBlockNode(children: []); + } + + final rows = tableNode.children.map((row) { + final cell = row.children[widget.index]; + return simpleTableRowBlockNode(children: [cell]); + }).toList(); + + return tableNode.copyWith( + children: rows, + attributes: { + ...tableNode.attributes, + if (widget.index != 0) + SimpleTableBlockKeys.enableHeaderColumn: false, + }, + ); + } + } +} diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_widget.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_widget.dart new file mode 100644 index 0000000000..62acf509e1 --- /dev/null +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_widget.dart @@ -0,0 +1,195 @@ +import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_table/_shared_widget.dart'; +import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_table/simple_table.dart'; +import 'package:appflowy_editor/appflowy_editor.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:universal_platform/universal_platform.dart'; + +class SimpleTableWidget extends StatefulWidget { + const SimpleTableWidget({ + super.key, + required this.simpleTableContext, + required this.node, + this.enableAddColumnButton = true, + this.enableAddRowButton = true, + this.enableAddColumnAndRowButton = true, + this.enableHoverEffect = true, + this.isFeedback = false, + }); + + /// The node of the table. + /// + /// Its type must be [SimpleTableBlockKeys.type]. + final Node node; + + /// The context of the simple table. + final SimpleTableContext simpleTableContext; + + /// Whether to show the add column button. + /// + /// For the feedback widget builder, it should be false. + final bool enableAddColumnButton; + + /// Whether to show the add row button. + /// + /// For the feedback widget builder, it should be false. + final bool enableAddRowButton; + + /// Whether to show the add column and row button. + /// + /// For the feedback widget builder, it should be false. + final bool enableAddColumnAndRowButton; + + /// Whether to enable the hover effect. + /// + /// For the feedback widget builder, it should be false. + final bool enableHoverEffect; + + /// Whether the widget is a feedback widget. + final bool isFeedback; + + @override + State createState() => _SimpleTableWidgetState(); +} + +class _SimpleTableWidgetState extends State { + SimpleTableContext get simpleTableContext => widget.simpleTableContext; + + final scrollController = ScrollController(); + late final editorState = context.read(); + + @override + void dispose() { + scrollController.dispose(); + + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return UniversalPlatform.isDesktop + ? _buildDesktopTable() + : _buildMobileTable(); + } + + Widget _buildDesktopTable() { + if (widget.isFeedback) { + return Provider.value( + value: simpleTableContext, + child: IntrinsicWidth( + child: IntrinsicHeight( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: _buildRows(), + ), + ), + ), + ); + } + + // table content + Widget child = Scrollbar( + controller: scrollController, + child: SingleChildScrollView( + controller: scrollController, + scrollDirection: Axis.horizontal, + child: Padding( + padding: SimpleTableConstants.tablePadding, + // IntrinsicWidth and IntrinsicHeight are used to make the table size fit the content. + child: IntrinsicWidth( + child: IntrinsicHeight( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: _buildRows(), + ), + ), + ), + ), + ), + ); + + if (widget.enableHoverEffect) { + child = MouseRegion( + onEnter: (event) => + simpleTableContext.isHoveringOnTableArea.value = true, + onExit: (event) { + simpleTableContext.isHoveringOnTableArea.value = false; + }, + child: Provider.value( + value: simpleTableContext, + child: Stack( + children: [ + MouseRegion( + hitTestBehavior: HitTestBehavior.opaque, + onEnter: (event) => + simpleTableContext.isHoveringOnColumnsAndRows.value = true, + onExit: (event) { + simpleTableContext.isHoveringOnColumnsAndRows.value = false; + simpleTableContext.hoveringTableCell.value = null; + }, + child: child, + ), + if (widget.enableAddColumnButton) + SimpleTableAddColumnHoverButton( + editorState: editorState, + node: widget.node, + ), + if (widget.enableAddRowButton) + SimpleTableAddRowHoverButton( + editorState: editorState, + tableNode: widget.node, + ), + if (widget.enableAddColumnAndRowButton) + SimpleTableAddColumnAndRowHoverButton( + editorState: editorState, + node: widget.node, + ), + ], + ), + ), + ); + } + + return child; + } + + Widget _buildMobileTable() { + return Provider.value( + value: simpleTableContext, + child: SingleChildScrollView( + controller: scrollController, + scrollDirection: Axis.horizontal, + child: IntrinsicWidth( + child: IntrinsicHeight( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: _buildRows(), + ), + ), + ), + ), + ); + } + + List _buildRows() { + final List rows = []; + + if (SimpleTableConstants.borderType == SimpleTableBorderRenderType.table) { + rows.add(const SimpleTableColumnDivider()); + } + + for (final child in widget.node.children) { + rows.add(editorState.renderer.build(context, child)); + + if (SimpleTableConstants.borderType == + SimpleTableBorderRenderType.table) { + rows.add(const SimpleTableColumnDivider()); + } + } + + return rows; + } +} diff --git a/frontend/appflowy_flutter/test/unit_test/simple_table/simple_table_reorder_operation_test.dart b/frontend/appflowy_flutter/test/unit_test/simple_table/simple_table_reorder_operation_test.dart new file mode 100644 index 0000000000..703a63b40d --- /dev/null +++ b/frontend/appflowy_flutter/test/unit_test/simple_table/simple_table_reorder_operation_test.dart @@ -0,0 +1,335 @@ +import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_table/simple_table.dart'; +import 'package:appflowy_backend/log.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'simple_table_test_helper.dart'; + +void main() { + group('Simple table reorder operation:', () { + setUpAll(() { + Log.shared.disableLog = true; + }); + + tearDownAll(() { + Log.shared.disableLog = false; + }); + + group('reorder column', () { + test('reorder column from index 1 to index 2', () async { + final (editorState, tableNode) = createEditorStateAndTable( + rowCount: 4, + columnCount: 3, + contentBuilder: (rowIndex, columnIndex) => + 'cell $rowIndex-$columnIndex', + ); + await editorState.reorderColumn(tableNode, fromIndex: 1, toIndex: 2); + expect(tableNode.columnLength, 3); + expect(tableNode.rowLength, 4); + expect( + tableNode.getTableCellContent(rowIndex: 0, columnIndex: 0), + 'cell 0-0', + ); + expect( + tableNode.getTableCellContent(rowIndex: 0, columnIndex: 1), + 'cell 0-2', + ); + expect( + tableNode.getTableCellContent(rowIndex: 0, columnIndex: 2), + 'cell 0-1', + ); + }); + + test('reorder column from index 2 to index 0', () async { + final (editorState, tableNode) = createEditorStateAndTable( + rowCount: 4, + columnCount: 3, + contentBuilder: (rowIndex, columnIndex) => + 'cell $rowIndex-$columnIndex', + ); + await editorState.reorderColumn(tableNode, fromIndex: 2, toIndex: 0); + expect(tableNode.columnLength, 3); + expect(tableNode.rowLength, 4); + expect( + tableNode.getTableCellContent(rowIndex: 0, columnIndex: 0), + 'cell 0-2', + ); + expect( + tableNode.getTableCellContent(rowIndex: 0, columnIndex: 1), + 'cell 0-0', + ); + expect( + tableNode.getTableCellContent(rowIndex: 0, columnIndex: 2), + 'cell 0-1', + ); + }); + + test('reorder column with same index', () async { + final (editorState, tableNode) = createEditorStateAndTable( + rowCount: 4, + columnCount: 3, + contentBuilder: (rowIndex, columnIndex) => + 'cell $rowIndex-$columnIndex', + ); + await editorState.reorderColumn(tableNode, fromIndex: 1, toIndex: 1); + expect(tableNode.columnLength, 3); + expect(tableNode.rowLength, 4); + expect( + tableNode.getTableCellContent(rowIndex: 0, columnIndex: 0), + 'cell 0-0', + ); + expect( + tableNode.getTableCellContent(rowIndex: 0, columnIndex: 1), + 'cell 0-1', + ); + expect( + tableNode.getTableCellContent(rowIndex: 0, columnIndex: 2), + 'cell 0-2', + ); + }); + + test( + 'reorder column from index 0 to index 2 with align/color/width attributes (1)', + () async { + final (editorState, tableNode) = createEditorStateAndTable( + rowCount: 4, + columnCount: 3, + contentBuilder: (rowIndex, columnIndex) => + 'cell $rowIndex-$columnIndex', + ); + + // before reorder + // Column 0: align: right, color: 0xFF0000, width: 100 + // Column 1: align: center, color: 0x00FF00, width: 150 + // Column 2: align: left, color: 0x0000FF, width: 200 + await updateTableColumnAttributes( + editorState, + tableNode, + columnIndex: 0, + align: TableAlign.right, + color: '#FF0000', + width: 100, + ); + await updateTableColumnAttributes( + editorState, + tableNode, + columnIndex: 1, + align: TableAlign.center, + color: '#00FF00', + width: 150, + ); + await updateTableColumnAttributes( + editorState, + tableNode, + columnIndex: 2, + align: TableAlign.left, + color: '#0000FF', + width: 200, + ); + + // after reorder + // Column 0: align: center, color: 0x00FF00, width: 150 + // Column 1: align: left, color: 0x0000FF, width: 200 + // Column 2: align: right, color: 0xFF0000, width: 100 + await editorState.reorderColumn(tableNode, fromIndex: 0, toIndex: 2); + expect(tableNode.columnLength, 3); + expect(tableNode.rowLength, 4); + + expect(tableNode.columnAligns, { + "0": TableAlign.center.key, + "1": TableAlign.left.key, + "2": TableAlign.right.key, + }); + expect(tableNode.columnColors, { + "0": '#00FF00', + "1": '#0000FF', + "2": '#FF0000', + }); + expect(tableNode.columnWidths, { + "0": 150, + "1": 200, + "2": 100, + }); + }); + + test( + 'reorder column from index 0 to index 2 and reorder it back to index 0', + () async { + final (editorState, tableNode) = createEditorStateAndTable( + rowCount: 2, + columnCount: 3, + contentBuilder: (rowIndex, columnIndex) => + 'cell $rowIndex-$columnIndex', + ); + + // before reorder + // Column 0: null + // Column 1: align: center, color: 0x0000FF, width: 200 + // Column 2: align: right, color: 0x0000FF, width: 250 + await updateTableColumnAttributes( + editorState, + tableNode, + columnIndex: 1, + align: TableAlign.center, + color: '#FF0000', + width: 200, + ); + await updateTableColumnAttributes( + editorState, + tableNode, + columnIndex: 2, + align: TableAlign.right, + color: '#0000FF', + width: 250, + ); + + // move column from index 0 to index 2 + await editorState.reorderColumn(tableNode, fromIndex: 0, toIndex: 2); + // move column from index 2 to index 0 + await editorState.reorderColumn(tableNode, fromIndex: 2, toIndex: 0); + expect(tableNode.columnLength, 3); + expect(tableNode.rowLength, 2); + + expect(tableNode.columnAligns, { + "1": TableAlign.center.key, + "2": TableAlign.right.key, + }); + expect(tableNode.columnColors, { + "1": '#FF0000', + "2": '#0000FF', + }); + expect(tableNode.columnWidths, { + "1": 200, + "2": 250, + }); + }); + }); + + group('reorder row', () { + test('reorder row from index 1 to index 2', () async { + final (editorState, tableNode) = createEditorStateAndTable( + rowCount: 3, + columnCount: 2, + contentBuilder: (rowIndex, columnIndex) => + 'cell $rowIndex-$columnIndex', + ); + await editorState.reorderRow(tableNode, fromIndex: 1, toIndex: 2); + expect(tableNode.columnLength, 2); + expect(tableNode.rowLength, 3); + expect( + tableNode.getTableCellContent(rowIndex: 0, columnIndex: 0), + 'cell 0-0', + ); + expect( + tableNode.getTableCellContent(rowIndex: 1, columnIndex: 0), + 'cell 2-0', + ); + expect( + tableNode.getTableCellContent(rowIndex: 2, columnIndex: 0), + 'cell 1-0', + ); + }); + + test('reorder row from index 2 to index 0', () async { + final (editorState, tableNode) = createEditorStateAndTable( + rowCount: 3, + columnCount: 2, + contentBuilder: (rowIndex, columnIndex) => + 'cell $rowIndex-$columnIndex', + ); + await editorState.reorderRow(tableNode, fromIndex: 2, toIndex: 0); + expect(tableNode.columnLength, 2); + expect(tableNode.rowLength, 3); + expect( + tableNode.getTableCellContent(rowIndex: 0, columnIndex: 0), + 'cell 2-0', + ); + expect( + tableNode.getTableCellContent(rowIndex: 1, columnIndex: 0), + 'cell 0-0', + ); + expect( + tableNode.getTableCellContent(rowIndex: 2, columnIndex: 0), + 'cell 1-0', + ); + }); + + test('reorder row with same', () async { + final (editorState, tableNode) = createEditorStateAndTable( + rowCount: 3, + columnCount: 2, + contentBuilder: (rowIndex, columnIndex) => + 'cell $rowIndex-$columnIndex', + ); + await editorState.reorderRow(tableNode, fromIndex: 1, toIndex: 1); + expect(tableNode.columnLength, 2); + expect(tableNode.rowLength, 3); + expect( + tableNode.getTableCellContent(rowIndex: 0, columnIndex: 0), + 'cell 0-0', + ); + expect( + tableNode.getTableCellContent(rowIndex: 1, columnIndex: 0), + 'cell 1-0', + ); + expect( + tableNode.getTableCellContent(rowIndex: 2, columnIndex: 0), + 'cell 2-0', + ); + }); + + test('reorder row from index 0 to index 2 with align/color attributes', + () async { + final (editorState, tableNode) = createEditorStateAndTable( + rowCount: 3, + columnCount: 2, + contentBuilder: (rowIndex, columnIndex) => + 'cell $rowIndex-$columnIndex', + ); + + // before reorder + // Row 0: align: right, color: 0xFF0000 + // Row 1: align: center, color: 0x00FF00 + // Row 2: align: left, color: 0x0000FF + await updateTableRowAttributes( + editorState, + tableNode, + rowIndex: 0, + align: TableAlign.right, + color: '#FF0000', + ); + await updateTableRowAttributes( + editorState, + tableNode, + rowIndex: 1, + align: TableAlign.center, + color: '#00FF00', + ); + await updateTableRowAttributes( + editorState, + tableNode, + rowIndex: 2, + align: TableAlign.left, + color: '#0000FF', + ); + + // after reorder + // Row 0: align: center, color: 0x00FF00 + // Row 1: align: left, color: 0x0000FF + // Row 2: align: right, color: 0xFF0000 + await editorState.reorderRow(tableNode, fromIndex: 0, toIndex: 2); + expect(tableNode.columnLength, 2); + expect(tableNode.rowLength, 3); + expect(tableNode.rowAligns, { + "0": TableAlign.center.key, + "1": TableAlign.left.key, + "2": TableAlign.right.key, + }); + expect(tableNode.rowColors, { + "0": '#00FF00', + "1": '#0000FF', + "2": '#FF0000', + }); + }); + }); + }); +} diff --git a/frontend/appflowy_flutter/test/unit_test/simple_table/simple_table_test_helper.dart b/frontend/appflowy_flutter/test/unit_test/simple_table/simple_table_test_helper.dart index 8c97d53564..e190925bee 100644 --- a/frontend/appflowy_flutter/test/unit_test/simple_table/simple_table_test_helper.dart +++ b/frontend/appflowy_flutter/test/unit_test/simple_table/simple_table_test_helper.dart @@ -1,10 +1,11 @@ -import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_table/simple_table_block_component.dart'; +import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_table/simple_table.dart'; import 'package:appflowy_editor/appflowy_editor.dart'; (EditorState editorState, Node tableNode) createEditorStateAndTable({ required int rowCount, required int columnCount, String? defaultContent, + String Function(int rowIndex, int columnIndex)? contentBuilder, }) { final document = Document.blank() ..insert( @@ -14,9 +15,72 @@ import 'package:appflowy_editor/appflowy_editor.dart'; columnCount: columnCount, rowCount: rowCount, defaultContent: defaultContent, + contentBuilder: contentBuilder, ), ], ); final editorState = EditorState(document: document); return (editorState, document.nodeAtPath([0])!); } + +Future updateTableColumnAttributes( + EditorState editorState, + Node tableNode, { + required int columnIndex, + TableAlign? align, + String? color, + double? width, +}) async { + final cell = tableNode.getTableCellNode( + rowIndex: 0, + columnIndex: columnIndex, + )!; + + if (align != null) { + await editorState.updateColumnAlign( + tableCellNode: cell, + align: align, + ); + } + + if (color != null) { + await editorState.updateColumnBackgroundColor( + tableCellNode: cell, + color: color, + ); + } + + if (width != null) { + await editorState.updateColumnWidth( + tableCellNode: cell, + width: width, + ); + } +} + +Future updateTableRowAttributes( + EditorState editorState, + Node tableNode, { + required int rowIndex, + TableAlign? align, + String? color, +}) async { + final cell = tableNode.getTableCellNode( + rowIndex: rowIndex, + columnIndex: 0, + )!; + + if (align != null) { + await editorState.updateRowAlign( + tableCellNode: cell, + align: align, + ); + } + + if (color != null) { + await editorState.updateRowBackgroundColor( + tableCellNode: cell, + color: color, + ); + } +} From 9e82f3d7b82a290f432da9f8cd4c7828c9f28346 Mon Sep 17 00:00:00 2001 From: Lucas Date: Fri, 6 Dec 2024 14:36:48 +0800 Subject: [PATCH 010/576] fix: unable to open local file using afLaunchUrl function (#6927) * fix: unable to open local file using afLaunchUrl function * chore: use the latest api to open the local file * chore: use the latest api to open the local file * chore: use the latest api to open the local file * test: add local paht regex test --- .../lib/core/helpers/url_launcher.dart | 79 +++++++++++++++++-- .../base/view_page/more_bottom_sheet.dart | 2 +- .../desktop_grid/desktop_grid_media_cell.dart | 13 ++- .../desktop_row_detail_media_cell.dart | 3 +- .../file/file_block_component.dart | 19 +---- .../editor_plugins/file/file_util.dart | 5 +- .../lib/shared/patterns/common_patterns.dart | 3 + .../auth/af_cloud_auth_service.dart | 2 +- .../lib/util/share_log_files.dart | 4 +- .../settings/ai/plugin_state_bloc.dart | 3 +- .../pages/settings_manage_data_view.dart | 7 +- .../theme_upload_learn_more_button.dart | 7 +- .../interactive_image_toolbar.dart | 4 +- .../url_launcher/url_launcher_test.dart | 19 +++++ frontend/resources/translations/en.json | 9 ++- 15 files changed, 128 insertions(+), 51 deletions(-) create mode 100644 frontend/appflowy_flutter/test/unit_test/url_launcher/url_launcher_test.dart diff --git a/frontend/appflowy_flutter/lib/core/helpers/url_launcher.dart b/frontend/appflowy_flutter/lib/core/helpers/url_launcher.dart index 94d2074c6b..2b0bc7b345 100644 --- a/frontend/appflowy_flutter/lib/core/helpers/url_launcher.dart +++ b/frontend/appflowy_flutter/lib/core/helpers/url_launcher.dart @@ -1,16 +1,24 @@ -import 'package:flutter/services.dart'; -import 'package:flutter/widgets.dart'; +import 'dart:io'; import 'package:appflowy/generated/locale_keys.g.dart'; +import 'package:appflowy/shared/patterns/common_patterns.dart'; import 'package:appflowy/workspace/presentation/home/toast.dart'; +import 'package:appflowy/workspace/presentation/widgets/dialogs.dart'; import 'package:appflowy_backend/log.dart'; import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter/widgets.dart'; +import 'package:open_filex/open_filex.dart'; import 'package:string_validator/string_validator.dart'; import 'package:url_launcher/url_launcher.dart' as launcher; typedef OnFailureCallback = void Function(Uri uri); -Future afLaunchUrl( +/// Launch the uri +/// +/// If the uri is a local file path, it will be opened with the OpenFilex. +/// Otherwise, it will be launched with the url_launcher. +Future afLaunchUri( Uri uri, { BuildContext? context, OnFailureCallback? onFailure, @@ -18,6 +26,18 @@ Future afLaunchUrl( String? webOnlyWindowName, bool addingHttpSchemeWhenFailed = false, }) async { + final url = uri.toString(); + final decodedUrl = Uri.decodeComponent(url); + + // check if the uri is the local file path + if (localPathRegex.hasMatch(decodedUrl)) { + return _afLaunchLocalUri( + uri, + context: context, + onFailure: onFailure, + ); + } + // try to launch the uri directly bool result; try { @@ -32,7 +52,7 @@ Future afLaunchUrl( } // if the uri is not a valid url, try to launch it with http scheme - final url = uri.toString(); + if (addingHttpSchemeWhenFailed && !result && !isURL(url, {'require_protocol': true})) { @@ -54,9 +74,14 @@ Future afLaunchUrl( return result; } +/// Launch the url string +/// +/// See [afLaunchUri] for more details. Future afLaunchUrlString( String url, { bool addingHttpSchemeWhenFailed = false, + BuildContext? context, + OnFailureCallback? onFailure, }) async { final Uri uri; try { @@ -67,12 +92,56 @@ Future afLaunchUrlString( } // try to launch the uri directly - return afLaunchUrl( + return afLaunchUri( uri, addingHttpSchemeWhenFailed: addingHttpSchemeWhenFailed, + context: context, + onFailure: onFailure, ); } +/// Launch the local uri +/// +/// See [afLaunchUri] for more details. +Future _afLaunchLocalUri( + Uri uri, { + BuildContext? context, + OnFailureCallback? onFailure, +}) async { + final decodedUrl = Uri.decodeComponent(uri.toString()); + // open the file with the OpenfileX + var result = await OpenFilex.open(decodedUrl); + if (result.type != ResultType.done) { + // For the file cant be opened, fallback to open the folder + final parentFolder = Directory(decodedUrl).parent.path; + result = await OpenFilex.open(parentFolder); + } + // show the toast if the file is not found + final message = switch (result.type) { + ResultType.done => LocaleKeys.openFileMessage_success.tr(), + ResultType.fileNotFound => LocaleKeys.openFileMessage_fileNotFound.tr(), + ResultType.noAppToOpen => LocaleKeys.openFileMessage_noAppToOpenFile.tr(), + ResultType.permissionDenied => + LocaleKeys.openFileMessage_permissionDenied.tr(), + ResultType.error => LocaleKeys.failedToOpenUrl.tr(), + }; + if (context != null && context.mounted) { + showToastNotification( + context, + message: message, + type: result.type == ResultType.done + ? ToastificationType.success + : ToastificationType.error, + ); + } + final openFileSuccess = result.type == ResultType.done; + if (!openFileSuccess && onFailure != null) { + onFailure(uri); + Log.error('Failed to open file: $result.message'); + } + return openFileSuccess; +} + void _errorHandler( Uri uri, { BuildContext? context, diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/base/view_page/more_bottom_sheet.dart b/frontend/appflowy_flutter/lib/mobile/presentation/base/view_page/more_bottom_sheet.dart index eba8b09025..25541ed7d4 100644 --- a/frontend/appflowy_flutter/lib/mobile/presentation/base/view_page/more_bottom_sheet.dart +++ b/frontend/appflowy_flutter/lib/mobile/presentation/base/view_page/more_bottom_sheet.dart @@ -189,7 +189,7 @@ class MobileViewPageMoreBottomSheet extends StatelessWidget { final url = context.read().state.url; if (url.isNotEmpty) { unawaited( - afLaunchUrl( + afLaunchUri( Uri.parse(url), mode: LaunchMode.externalApplication, ), diff --git a/frontend/appflowy_flutter/lib/plugins/database/widgets/cell/desktop_grid/desktop_grid_media_cell.dart b/frontend/appflowy_flutter/lib/plugins/database/widgets/cell/desktop_grid/desktop_grid_media_cell.dart index bcf136bcf1..f66d106ed0 100644 --- a/frontend/appflowy_flutter/lib/plugins/database/widgets/cell/desktop_grid/desktop_grid_media_cell.dart +++ b/frontend/appflowy_flutter/lib/plugins/database/widgets/cell/desktop_grid/desktop_grid_media_cell.dart @@ -1,24 +1,23 @@ import 'package:appflowy/core/helpers/url_launcher.dart'; -import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet_media_upload.dart'; -import 'package:appflowy/plugins/document/presentation/editor_plugins/image/common.dart'; -import 'package:appflowy/workspace/presentation/widgets/image_viewer/image_provider.dart'; -import 'package:appflowy/workspace/presentation/widgets/image_viewer/interactive_image_viewer.dart'; -import 'package:flutter/material.dart'; - import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart'; +import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet_media_upload.dart'; import 'package:appflowy/plugins/database/application/cell/bloc/media_cell_bloc.dart'; import 'package:appflowy/plugins/database/widgets/cell/editable_cell_skeleton/media.dart'; import 'package:appflowy/plugins/database/widgets/cell_editor/media_cell_editor.dart'; import 'package:appflowy/plugins/database/widgets/cell_editor/mobile_media_cell_editor.dart'; import 'package:appflowy/plugins/database/widgets/media_file_type_ext.dart'; import 'package:appflowy/plugins/database/widgets/row/cells/cell_container.dart'; +import 'package:appflowy/plugins/document/presentation/editor_plugins/image/common.dart'; import 'package:appflowy/shared/af_image.dart'; +import 'package:appflowy/workspace/presentation/widgets/image_viewer/image_provider.dart'; +import 'package:appflowy/workspace/presentation/widgets/image_viewer/interactive_image_viewer.dart'; import 'package:appflowy_backend/protobuf/flowy-database2/media_entities.pb.dart'; import 'package:easy_localization/easy_localization.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:universal_platform/universal_platform.dart'; @@ -159,7 +158,7 @@ class GridMediaCellSkin extends IEditableMediaCellSkin { List files, ) { if (file.fileType != MediaFileTypePB.Image) { - afLaunchUrlString(file.url); + afLaunchUrlString(file.url, context: context); return; } diff --git a/frontend/appflowy_flutter/lib/plugins/database/widgets/cell/desktop_row_detail/desktop_row_detail_media_cell.dart b/frontend/appflowy_flutter/lib/plugins/database/widgets/cell/desktop_row_detail/desktop_row_detail_media_cell.dart index 772149fa59..68c5191682 100644 --- a/frontend/appflowy_flutter/lib/plugins/database/widgets/cell/desktop_row_detail/desktop_row_detail_media_cell.dart +++ b/frontend/appflowy_flutter/lib/plugins/database/widgets/cell/desktop_row_detail/desktop_row_detail_media_cell.dart @@ -27,7 +27,6 @@ import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flowy_infra_ui/style_widget/hover.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:open_filex/open_filex.dart'; import 'package:reorderables/reorderables.dart'; const _dropFileKey = 'files_media'; @@ -479,7 +478,7 @@ class _FilePreviewRenderState extends State<_FilePreviewRender> { ? null : () { if (file.uploadType == FileUploadTypePB.LocalFile) { - OpenFilex.open(file.url); + afLaunchUrlString(file.url); return; } diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/file/file_block_component.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/file/file_block_component.dart index e4dd1d4e4a..f1ffbe9d11 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/file/file_block_component.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/file/file_block_component.dart @@ -10,7 +10,6 @@ import 'package:appflowy/plugins/document/presentation/editor_plugins/copy_and_p import 'package:appflowy/plugins/document/presentation/editor_plugins/file/file_util.dart'; import 'package:appflowy/startup/startup.dart'; import 'package:appflowy/workspace/presentation/home/toast.dart'; -import 'package:appflowy/workspace/presentation/widgets/dialogs.dart'; import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:cross_file/cross_file.dart'; import 'package:desktop_drop/desktop_drop.dart'; @@ -19,7 +18,6 @@ import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flowy_infra_ui/style_widget/hover.dart'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; -import 'package:open_filex/open_filex.dart'; import 'package:provider/provider.dart'; import 'package:string_validator/string_validator.dart'; import 'package:universal_platform/universal_platform.dart'; @@ -323,22 +321,7 @@ class FileBlockComponentState extends State FileUrlType urlType, String url, ) async { - if ([FileUrlType.cloud, FileUrlType.network].contains(urlType)) { - await afLaunchUrlString(url); - } else { - final result = await OpenFilex.open(url); - if (result.type == ResultType.done) { - return; - } - - if (context.mounted) { - showToastNotification( - context, - message: LocaleKeys.document_plugins_file_failedToOpenMsg.tr(), - type: ToastificationType.error, - ); - } - } + await afLaunchUrlString(url, context: context); } void _openMenu() { diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/file/file_util.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/file/file_util.dart index f09aa1ca5e..3ab93b4c95 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/file/file_util.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/file/file_util.dart @@ -1,8 +1,6 @@ import 'dart:convert'; import 'dart:io'; -import 'package:flutter/material.dart'; - import 'package:appflowy/core/helpers/url_launcher.dart'; import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/plugins/document/application/document_service.dart'; @@ -22,6 +20,7 @@ import 'package:cross_file/cross_file.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flowy_infra/file_picker/file_picker_impl.dart'; import 'package:flowy_infra/uuid.dart'; +import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; import 'package:path/path.dart' as p; import 'package:universal_platform/universal_platform.dart'; @@ -102,7 +101,7 @@ Future downloadMediaFile( FileUploadTypePB.LocalFile, ].contains(file.uploadType)) { /// When the file is a network file or a local file, we can directly open the file. - await afLaunchUrl(Uri.parse(file.url)); + await afLaunchUrlString(file.url); } else { if (userProfile == null) { return showToastNotification( diff --git a/frontend/appflowy_flutter/lib/shared/patterns/common_patterns.dart b/frontend/appflowy_flutter/lib/shared/patterns/common_patterns.dart index 20444f7205..ebd310a747 100644 --- a/frontend/appflowy_flutter/lib/shared/patterns/common_patterns.dart +++ b/frontend/appflowy_flutter/lib/shared/patterns/common_patterns.dart @@ -42,3 +42,6 @@ final appflowySharePageLinkRegex = RegExp(appflowySharePageLinkPattern); const _numberedListPattern = r'^(\d+)\.'; final numberedListRegex = RegExp(_numberedListPattern); + +const _localPathPattern = r'^(file:\/\/|\/|\\|[a-zA-Z]:[/\\]|\.{1,2}[/\\])'; +final localPathRegex = RegExp(_localPathPattern, caseSensitive: false); diff --git a/frontend/appflowy_flutter/lib/user/application/auth/af_cloud_auth_service.dart b/frontend/appflowy_flutter/lib/user/application/auth/af_cloud_auth_service.dart index ae7fe37982..149bddc951 100644 --- a/frontend/appflowy_flutter/lib/user/application/auth/af_cloud_auth_service.dart +++ b/frontend/appflowy_flutter/lib/user/application/auth/af_cloud_auth_service.dart @@ -56,7 +56,7 @@ class AppFlowyCloudAuthService implements AuthService { (data) async { // Open the webview with oauth url final uri = Uri.parse(data.oauthUrl); - final isSuccess = await afLaunchUrl( + final isSuccess = await afLaunchUri( uri, mode: LaunchMode.externalApplication, webOnlyWindowName: '_self', diff --git a/frontend/appflowy_flutter/lib/util/share_log_files.dart b/frontend/appflowy_flutter/lib/util/share_log_files.dart index 21955e6c05..d7e7b6ce87 100644 --- a/frontend/appflowy_flutter/lib/util/share_log_files.dart +++ b/frontend/appflowy_flutter/lib/util/share_log_files.dart @@ -1,11 +1,11 @@ import 'dart:io'; +import 'package:appflowy/core/helpers/url_launcher.dart'; import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/workspace/presentation/widgets/dialogs.dart'; import 'package:archive/archive_io.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; -import 'package:open_filex/open_filex.dart'; import 'package:path/path.dart' as p; import 'package:path_provider/path_provider.dart'; import 'package:share_plus/share_plus.dart'; @@ -67,7 +67,7 @@ Future shareLogFiles(BuildContext? context) async { await zipFile.delete(); } else { // open the directory - await OpenFilex.open(zipFile.path); + await afLaunchUri(zipFile.uri); } } catch (e) { if (context != null && context.mounted) { diff --git a/frontend/appflowy_flutter/lib/workspace/application/settings/ai/plugin_state_bloc.dart b/frontend/appflowy_flutter/lib/workspace/application/settings/ai/plugin_state_bloc.dart index 41070e2fe4..4f24309bde 100644 --- a/frontend/appflowy_flutter/lib/workspace/application/settings/ai/plugin_state_bloc.dart +++ b/frontend/appflowy_flutter/lib/workspace/application/settings/ai/plugin_state_bloc.dart @@ -8,6 +8,7 @@ import 'package:appflowy_backend/protobuf/flowy-ai/entities.pb.dart'; import 'package:bloc/bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:url_launcher/url_launcher.dart' show launchUrl; + part 'plugin_state_bloc.freezed.dart'; class PluginStateBloc extends Bloc { @@ -91,7 +92,7 @@ class PluginStateBloc extends Bloc { final result = await AIEventGetModelStorageDirectory().send(); result.fold( (data) { - afLaunchUrl(Uri.file(data.filePath)); + afLaunchUri(Uri.file(data.filePath)); }, (err) => Log.error(err.toString()), ); diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/settings/pages/settings_manage_data_view.dart b/frontend/appflowy_flutter/lib/workspace/presentation/settings/pages/settings_manage_data_view.dart index 3499580bbe..7c096b4b2f 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/settings/pages/settings_manage_data_view.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/settings/pages/settings_manage_data_view.dart @@ -1,8 +1,5 @@ import 'dart:async'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; - import 'package:appflowy/core/helpers/url_launcher.dart'; import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart'; @@ -28,6 +25,8 @@ import 'package:flowy_infra/file_picker/file_picker_service.dart'; import 'package:flowy_infra/theme_extension.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flowy_infra_ui/style_widget/hover.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:fluttertoast/fluttertoast.dart'; @@ -450,7 +449,7 @@ class _DataPathActions extends StatelessWidget { label: LocaleKeys.settings_manageDataPage_dataStorage_actions_open.tr(), icon: const FlowySvg(FlowySvgs.folder_m, size: Size.square(20)), - onPressed: () => afLaunchUrl(Uri.file(currentPath)), + onPressed: () => afLaunchUri(Uri.file(currentPath)), ), ], ); diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/theme_upload/theme_upload_learn_more_button.dart b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/theme_upload/theme_upload_learn_more_button.dart index d57d2d2a00..9e25dece0e 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/theme_upload/theme_upload_learn_more_button.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/theme_upload/theme_upload_learn_more_button.dart @@ -1,14 +1,13 @@ -import 'package:flutter/material.dart'; - import 'package:appflowy/core/helpers/url_launcher.dart'; import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/workspace/presentation/settings/widgets/theme_upload/theme_upload_view.dart'; import 'package:appflowy/workspace/presentation/widgets/dialogs.dart'; import 'package:easy_localization/easy_localization.dart'; +import 'package:flowy_infra/theme_extension.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flowy_infra_ui/widget/buttons/secondary_button.dart'; import 'package:flowy_infra_ui/widget/error_page.dart'; -import 'package:flowy_infra/theme_extension.dart'; +import 'package:flutter/material.dart'; class ThemeUploadLearnMoreButton extends StatelessWidget { const ThemeUploadLearnMoreButton({super.key}); @@ -32,7 +31,7 @@ class ThemeUploadLearnMoreButton extends StatelessWidget { ), onPressed: () async { final uri = Uri.parse(learnMoreURL); - await afLaunchUrl( + await afLaunchUri( uri, context: context, onFailure: (_) async { diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/widgets/image_viewer/interactive_image_toolbar.dart b/frontend/appflowy_flutter/lib/workspace/presentation/widgets/image_viewer/interactive_image_toolbar.dart index 223910ceac..c1d9823bd1 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/widgets/image_viewer/interactive_image_toolbar.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/widgets/image_viewer/interactive_image_toolbar.dart @@ -220,12 +220,12 @@ class InteractiveImageToolbar extends StatelessWidget { Future _locateOrDownloadImage(BuildContext context) async { if (currentImage.isLocal) { /// If the image type is local, we simply open the image - await afLaunchUrl(Uri.file(currentImage.url)); + await afLaunchUri(Uri.file(currentImage.url)); } else if (currentImage.isNotInternal) { // In case of eg. Unsplash images (images without extension type in URL), // we don't know their mimetype. In the future we can write a parser // using the Mime package and read the image to get the proper extension. - await afLaunchUrl(Uri.parse(currentImage.url)); + await afLaunchUri(Uri.parse(currentImage.url)); } else { if (userProfile == null) { return showSnapBar( diff --git a/frontend/appflowy_flutter/test/unit_test/url_launcher/url_launcher_test.dart b/frontend/appflowy_flutter/test/unit_test/url_launcher/url_launcher_test.dart new file mode 100644 index 0000000000..feac569127 --- /dev/null +++ b/frontend/appflowy_flutter/test/unit_test/url_launcher/url_launcher_test.dart @@ -0,0 +1,19 @@ +import 'package:appflowy/shared/patterns/common_patterns.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('url launcher unit test', () { + test('launch local uri', () async { + const localUris = [ + 'file://path/to/file.txt', + '/path/to/file.txt', + 'C:\\path\\to\\file.txt', + '../path/to/file.txt', + ]; + for (final uri in localUris) { + final result = localPathRegex.hasMatch(uri); + expect(result, true); + } + }); + }); +} diff --git a/frontend/resources/translations/en.json b/frontend/resources/translations/en.json index dc93847dd6..8b1d97dba5 100644 --- a/frontend/resources/translations/en.json +++ b/frontend/resources/translations/en.json @@ -2896,5 +2896,12 @@ "favoriteDisabledHint": "Cannot favorite this view", "pinTab": "Pin", "unpinTab": "Unpin" + }, + "openFileMessage": { + "success": "File opened successfully", + "fileNotFound": "File not found", + "noAppToOpenFile": "No app to open this file", + "permissionDenied": "No permission to open this file", + "unknownError": "File open failed" } -} \ No newline at end of file +} From da0395be5e5bdf8d0d1f7a40cfcb00b52055ebb7 Mon Sep 17 00:00:00 2001 From: "Nathan.fooo" <86001920+appflowy@users.noreply.github.com> Date: Sat, 7 Dec 2024 00:02:56 +0800 Subject: [PATCH 011/576] chore: fetch rows by chunk size (#6934) * chore: chunk size * chore: chunk size --- frontend/appflowy_tauri/src-tauri/Cargo.lock | 77 +++++++++++------ frontend/appflowy_tauri/src-tauri/Cargo.toml | 18 ++-- .../appflowy_web_app/src-tauri/Cargo.lock | 77 +++++++++++------ .../appflowy_web_app/src-tauri/Cargo.toml | 18 ++-- frontend/rust-lib/Cargo.lock | 81 +++++++++++------- frontend/rust-lib/Cargo.toml | 20 ++--- .../rust-lib/flowy-database2/src/manager.rs | 32 ++++--- .../src/services/calculations/controller.rs | 59 +++++++------ .../src/services/database/database_editor.rs | 25 +++--- .../src/services/database_view/view_editor.rs | 84 ++++++++++++++----- .../src/services/share/csv/export.rs | 2 +- 11 files changed, 306 insertions(+), 187 deletions(-) diff --git a/frontend/appflowy_tauri/src-tauri/Cargo.lock b/frontend/appflowy_tauri/src-tauri/Cargo.lock index 02e7f665ae..e2415c8439 100644 --- a/frontend/appflowy_tauri/src-tauri/Cargo.lock +++ b/frontend/appflowy_tauri/src-tauri/Cargo.lock @@ -172,7 +172,7 @@ checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" [[package]] name = "app-error" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=0ec12c5f2fdd1bce0a0457eafb9963532b5208b3#0ec12c5f2fdd1bce0a0457eafb9963532b5208b3" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=66deaf3fa30564530160f7ac6de6dd2833d3d211#66deaf3fa30564530160f7ac6de6dd2833d3d211" dependencies = [ "anyhow", "bincode", @@ -192,7 +192,7 @@ dependencies = [ [[package]] name = "appflowy-ai-client" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=0ec12c5f2fdd1bce0a0457eafb9963532b5208b3#0ec12c5f2fdd1bce0a0457eafb9963532b5208b3" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=66deaf3fa30564530160f7ac6de6dd2833d3d211#66deaf3fa30564530160f7ac6de6dd2833d3d211" dependencies = [ "anyhow", "bytes", @@ -888,7 +888,7 @@ dependencies = [ [[package]] name = "client-api" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=0ec12c5f2fdd1bce0a0457eafb9963532b5208b3#0ec12c5f2fdd1bce0a0457eafb9963532b5208b3" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=66deaf3fa30564530160f7ac6de6dd2833d3d211#66deaf3fa30564530160f7ac6de6dd2833d3d211" dependencies = [ "again", "anyhow", @@ -918,7 +918,7 @@ dependencies = [ "parking_lot 0.12.1", "percent-encoding", "pin-project", - "prost", + "prost 0.13.3", "rayon", "reqwest", "scraper 0.17.1", @@ -943,7 +943,7 @@ dependencies = [ [[package]] name = "client-api-entity" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=0ec12c5f2fdd1bce0a0457eafb9963532b5208b3#0ec12c5f2fdd1bce0a0457eafb9963532b5208b3" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=66deaf3fa30564530160f7ac6de6dd2833d3d211#66deaf3fa30564530160f7ac6de6dd2833d3d211" dependencies = [ "collab-entity", "collab-rt-entity", @@ -956,7 +956,7 @@ dependencies = [ [[package]] name = "client-websocket" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=0ec12c5f2fdd1bce0a0457eafb9963532b5208b3#0ec12c5f2fdd1bce0a0457eafb9963532b5208b3" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=66deaf3fa30564530160f7ac6de6dd2833d3d211#66deaf3fa30564530160f7ac6de6dd2833d3d211" dependencies = [ "futures-channel", "futures-util", @@ -1030,7 +1030,7 @@ dependencies = [ [[package]] name = "collab" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8bd376c0c71ce366d763a70385c7b445a98241ed#8bd376c0c71ce366d763a70385c7b445a98241ed" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979" dependencies = [ "anyhow", "arc-swap", @@ -1055,7 +1055,7 @@ dependencies = [ [[package]] name = "collab-database" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8bd376c0c71ce366d763a70385c7b445a98241ed#8bd376c0c71ce366d763a70385c7b445a98241ed" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979" dependencies = [ "anyhow", "async-trait", @@ -1094,7 +1094,7 @@ dependencies = [ [[package]] name = "collab-document" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8bd376c0c71ce366d763a70385c7b445a98241ed#8bd376c0c71ce366d763a70385c7b445a98241ed" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979" dependencies = [ "anyhow", "arc-swap", @@ -1115,13 +1115,13 @@ dependencies = [ [[package]] name = "collab-entity" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8bd376c0c71ce366d763a70385c7b445a98241ed#8bd376c0c71ce366d763a70385c7b445a98241ed" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979" dependencies = [ "anyhow", "bytes", "collab", "getrandom 0.2.10", - "prost", + "prost 0.13.3", "prost-build", "protoc-bin-vendored", "serde", @@ -1135,7 +1135,7 @@ dependencies = [ [[package]] name = "collab-folder" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8bd376c0c71ce366d763a70385c7b445a98241ed#8bd376c0c71ce366d763a70385c7b445a98241ed" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979" dependencies = [ "anyhow", "arc-swap", @@ -1157,7 +1157,7 @@ dependencies = [ [[package]] name = "collab-importer" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8bd376c0c71ce366d763a70385c7b445a98241ed#8bd376c0c71ce366d763a70385c7b445a98241ed" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979" dependencies = [ "anyhow", "async-recursion", @@ -1218,7 +1218,7 @@ dependencies = [ [[package]] name = "collab-plugins" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8bd376c0c71ce366d763a70385c7b445a98241ed#8bd376c0c71ce366d763a70385c7b445a98241ed" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979" dependencies = [ "anyhow", "async-stream", @@ -1256,7 +1256,7 @@ dependencies = [ [[package]] name = "collab-rt-entity" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=0ec12c5f2fdd1bce0a0457eafb9963532b5208b3#0ec12c5f2fdd1bce0a0457eafb9963532b5208b3" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=66deaf3fa30564530160f7ac6de6dd2833d3d211#66deaf3fa30564530160f7ac6de6dd2833d3d211" dependencies = [ "anyhow", "bincode", @@ -1267,7 +1267,7 @@ dependencies = [ "collab-entity", "collab-rt-protocol", "database-entity", - "prost", + "prost 0.13.3", "prost-build", "protoc-bin-vendored", "serde", @@ -1281,7 +1281,7 @@ dependencies = [ [[package]] name = "collab-rt-protocol" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=0ec12c5f2fdd1bce0a0457eafb9963532b5208b3#0ec12c5f2fdd1bce0a0457eafb9963532b5208b3" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=66deaf3fa30564530160f7ac6de6dd2833d3d211#66deaf3fa30564530160f7ac6de6dd2833d3d211" dependencies = [ "anyhow", "async-trait", @@ -1298,7 +1298,7 @@ dependencies = [ [[package]] name = "collab-user" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8bd376c0c71ce366d763a70385c7b445a98241ed#8bd376c0c71ce366d763a70385c7b445a98241ed" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979" dependencies = [ "anyhow", "collab", @@ -1678,7 +1678,7 @@ checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" [[package]] name = "database-entity" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=0ec12c5f2fdd1bce0a0457eafb9963532b5208b3#0ec12c5f2fdd1bce0a0457eafb9963532b5208b3" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=66deaf3fa30564530160f7ac6de6dd2833d3d211#66deaf3fa30564530160f7ac6de6dd2833d3d211" dependencies = [ "anyhow", "app-error", @@ -1688,7 +1688,7 @@ dependencies = [ "chrono", "collab-entity", "infra", - "prost", + "prost 0.13.3", "serde", "serde_json", "serde_repr", @@ -3266,7 +3266,7 @@ dependencies = [ [[package]] name = "gotrue" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=0ec12c5f2fdd1bce0a0457eafb9963532b5208b3#0ec12c5f2fdd1bce0a0457eafb9963532b5208b3" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=66deaf3fa30564530160f7ac6de6dd2833d3d211#66deaf3fa30564530160f7ac6de6dd2833d3d211" dependencies = [ "anyhow", "futures-util", @@ -3283,7 +3283,7 @@ dependencies = [ [[package]] name = "gotrue-entity" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=0ec12c5f2fdd1bce0a0457eafb9963532b5208b3#0ec12c5f2fdd1bce0a0457eafb9963532b5208b3" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=66deaf3fa30564530160f7ac6de6dd2833d3d211#66deaf3fa30564530160f7ac6de6dd2833d3d211" dependencies = [ "anyhow", "app-error", @@ -3715,7 +3715,7 @@ dependencies = [ [[package]] name = "infra" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=0ec12c5f2fdd1bce0a0457eafb9963532b5208b3#0ec12c5f2fdd1bce0a0457eafb9963532b5208b3" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=66deaf3fa30564530160f7ac6de6dd2833d3d211#66deaf3fa30564530160f7ac6de6dd2833d3d211" dependencies = [ "anyhow", "bytes", @@ -5349,7 +5349,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "146c289cda302b98a28d40c8b3b90498d6e526dd24ac2ecea73e4e491685b94a" dependencies = [ "bytes", - "prost-derive", + "prost-derive 0.12.3", +] + +[[package]] +name = "prost" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b0487d90e047de87f984913713b85c601c05609aad5b0df4b4573fbf69aa13f" +dependencies = [ + "bytes", + "prost-derive 0.13.3", ] [[package]] @@ -5366,7 +5376,7 @@ dependencies = [ "once_cell", "petgraph", "prettyplease", - "prost", + "prost 0.12.3", "prost-types", "regex", "syn 2.0.47", @@ -5387,13 +5397,26 @@ dependencies = [ "syn 2.0.47", ] +[[package]] +name = "prost-derive" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9552f850d5f0964a4e4d0bf306459ac29323ddfbae05e35a7c0d35cb0803cc5" +dependencies = [ + "anyhow", + "itertools 0.12.1", + "proc-macro2", + "quote", + "syn 2.0.47", +] + [[package]] name = "prost-types" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "193898f59edcf43c26227dcd4c8427f00d99d61e95dcde58dabd49fa291d470e" dependencies = [ - "prost", + "prost 0.12.3", ] [[package]] @@ -6400,7 +6423,7 @@ dependencies = [ [[package]] name = "shared-entity" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=0ec12c5f2fdd1bce0a0457eafb9963532b5208b3#0ec12c5f2fdd1bce0a0457eafb9963532b5208b3" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=66deaf3fa30564530160f7ac6de6dd2833d3d211#66deaf3fa30564530160f7ac6de6dd2833d3d211" dependencies = [ "anyhow", "app-error", diff --git a/frontend/appflowy_tauri/src-tauri/Cargo.toml b/frontend/appflowy_tauri/src-tauri/Cargo.toml index 2778286c5b..97ba680f4d 100644 --- a/frontend/appflowy_tauri/src-tauri/Cargo.toml +++ b/frontend/appflowy_tauri/src-tauri/Cargo.toml @@ -59,7 +59,7 @@ collab-importer = { version = "0.1" } # Run the script: # scripts/tool/update_client_api_rev.sh new_rev_id # ⚠️⚠️⚠️️ -client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "0ec12c5f2fdd1bce0a0457eafb9963532b5208b3" } +client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "66deaf3fa30564530160f7ac6de6dd2833d3d211" } [dependencies] serde_json.workspace = true @@ -120,14 +120,14 @@ custom-protocol = ["tauri/custom-protocol"] # To switch to the local path, run: # scripts/tool/update_collab_source.sh # ⚠️⚠️⚠️️ -collab = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8bd376c0c71ce366d763a70385c7b445a98241ed" } -collab-entity = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8bd376c0c71ce366d763a70385c7b445a98241ed" } -collab-folder = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8bd376c0c71ce366d763a70385c7b445a98241ed" } -collab-document = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8bd376c0c71ce366d763a70385c7b445a98241ed" } -collab-database = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8bd376c0c71ce366d763a70385c7b445a98241ed" } -collab-plugins = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8bd376c0c71ce366d763a70385c7b445a98241ed" } -collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8bd376c0c71ce366d763a70385c7b445a98241ed" } -collab-importer = { version = "0.1", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8bd376c0c71ce366d763a70385c7b445a98241ed" } +collab = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" } +collab-entity = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" } +collab-folder = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" } +collab-document = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" } +collab-database = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" } +collab-plugins = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" } +collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" } +collab-importer = { version = "0.1", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" } # Working directory: frontend # To update the commit ID, run: diff --git a/frontend/appflowy_web_app/src-tauri/Cargo.lock b/frontend/appflowy_web_app/src-tauri/Cargo.lock index 697877b14a..4e99d32391 100644 --- a/frontend/appflowy_web_app/src-tauri/Cargo.lock +++ b/frontend/appflowy_web_app/src-tauri/Cargo.lock @@ -163,7 +163,7 @@ checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" [[package]] name = "app-error" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=0ec12c5f2fdd1bce0a0457eafb9963532b5208b3#0ec12c5f2fdd1bce0a0457eafb9963532b5208b3" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=66deaf3fa30564530160f7ac6de6dd2833d3d211#66deaf3fa30564530160f7ac6de6dd2833d3d211" dependencies = [ "anyhow", "bincode", @@ -183,7 +183,7 @@ dependencies = [ [[package]] name = "appflowy-ai-client" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=0ec12c5f2fdd1bce0a0457eafb9963532b5208b3#0ec12c5f2fdd1bce0a0457eafb9963532b5208b3" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=66deaf3fa30564530160f7ac6de6dd2833d3d211#66deaf3fa30564530160f7ac6de6dd2833d3d211" dependencies = [ "anyhow", "bytes", @@ -877,7 +877,7 @@ dependencies = [ [[package]] name = "client-api" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=0ec12c5f2fdd1bce0a0457eafb9963532b5208b3#0ec12c5f2fdd1bce0a0457eafb9963532b5208b3" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=66deaf3fa30564530160f7ac6de6dd2833d3d211#66deaf3fa30564530160f7ac6de6dd2833d3d211" dependencies = [ "again", "anyhow", @@ -907,7 +907,7 @@ dependencies = [ "parking_lot 0.12.1", "percent-encoding", "pin-project", - "prost", + "prost 0.13.3", "rayon", "reqwest", "scraper 0.17.1", @@ -932,7 +932,7 @@ dependencies = [ [[package]] name = "client-api-entity" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=0ec12c5f2fdd1bce0a0457eafb9963532b5208b3#0ec12c5f2fdd1bce0a0457eafb9963532b5208b3" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=66deaf3fa30564530160f7ac6de6dd2833d3d211#66deaf3fa30564530160f7ac6de6dd2833d3d211" dependencies = [ "collab-entity", "collab-rt-entity", @@ -945,7 +945,7 @@ dependencies = [ [[package]] name = "client-websocket" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=0ec12c5f2fdd1bce0a0457eafb9963532b5208b3#0ec12c5f2fdd1bce0a0457eafb9963532b5208b3" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=66deaf3fa30564530160f7ac6de6dd2833d3d211#66deaf3fa30564530160f7ac6de6dd2833d3d211" dependencies = [ "futures-channel", "futures-util", @@ -1028,7 +1028,7 @@ dependencies = [ [[package]] name = "collab" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8bd376c0c71ce366d763a70385c7b445a98241ed#8bd376c0c71ce366d763a70385c7b445a98241ed" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979" dependencies = [ "anyhow", "arc-swap", @@ -1053,7 +1053,7 @@ dependencies = [ [[package]] name = "collab-database" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8bd376c0c71ce366d763a70385c7b445a98241ed#8bd376c0c71ce366d763a70385c7b445a98241ed" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979" dependencies = [ "anyhow", "async-trait", @@ -1092,7 +1092,7 @@ dependencies = [ [[package]] name = "collab-document" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8bd376c0c71ce366d763a70385c7b445a98241ed#8bd376c0c71ce366d763a70385c7b445a98241ed" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979" dependencies = [ "anyhow", "arc-swap", @@ -1113,13 +1113,13 @@ dependencies = [ [[package]] name = "collab-entity" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8bd376c0c71ce366d763a70385c7b445a98241ed#8bd376c0c71ce366d763a70385c7b445a98241ed" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979" dependencies = [ "anyhow", "bytes", "collab", "getrandom 0.2.12", - "prost", + "prost 0.13.3", "prost-build", "protoc-bin-vendored", "serde", @@ -1133,7 +1133,7 @@ dependencies = [ [[package]] name = "collab-folder" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8bd376c0c71ce366d763a70385c7b445a98241ed#8bd376c0c71ce366d763a70385c7b445a98241ed" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979" dependencies = [ "anyhow", "arc-swap", @@ -1155,7 +1155,7 @@ dependencies = [ [[package]] name = "collab-importer" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8bd376c0c71ce366d763a70385c7b445a98241ed#8bd376c0c71ce366d763a70385c7b445a98241ed" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979" dependencies = [ "anyhow", "async-recursion", @@ -1216,7 +1216,7 @@ dependencies = [ [[package]] name = "collab-plugins" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8bd376c0c71ce366d763a70385c7b445a98241ed#8bd376c0c71ce366d763a70385c7b445a98241ed" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979" dependencies = [ "anyhow", "async-stream", @@ -1254,7 +1254,7 @@ dependencies = [ [[package]] name = "collab-rt-entity" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=0ec12c5f2fdd1bce0a0457eafb9963532b5208b3#0ec12c5f2fdd1bce0a0457eafb9963532b5208b3" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=66deaf3fa30564530160f7ac6de6dd2833d3d211#66deaf3fa30564530160f7ac6de6dd2833d3d211" dependencies = [ "anyhow", "bincode", @@ -1265,7 +1265,7 @@ dependencies = [ "collab-entity", "collab-rt-protocol", "database-entity", - "prost", + "prost 0.13.3", "prost-build", "protoc-bin-vendored", "serde", @@ -1279,7 +1279,7 @@ dependencies = [ [[package]] name = "collab-rt-protocol" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=0ec12c5f2fdd1bce0a0457eafb9963532b5208b3#0ec12c5f2fdd1bce0a0457eafb9963532b5208b3" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=66deaf3fa30564530160f7ac6de6dd2833d3d211#66deaf3fa30564530160f7ac6de6dd2833d3d211" dependencies = [ "anyhow", "async-trait", @@ -1296,7 +1296,7 @@ dependencies = [ [[package]] name = "collab-user" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8bd376c0c71ce366d763a70385c7b445a98241ed#8bd376c0c71ce366d763a70385c7b445a98241ed" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979" dependencies = [ "anyhow", "collab", @@ -1683,7 +1683,7 @@ checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" [[package]] name = "database-entity" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=0ec12c5f2fdd1bce0a0457eafb9963532b5208b3#0ec12c5f2fdd1bce0a0457eafb9963532b5208b3" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=66deaf3fa30564530160f7ac6de6dd2833d3d211#66deaf3fa30564530160f7ac6de6dd2833d3d211" dependencies = [ "anyhow", "app-error", @@ -1693,7 +1693,7 @@ dependencies = [ "chrono", "collab-entity", "infra", - "prost", + "prost 0.13.3", "serde", "serde_json", "serde_repr", @@ -3348,7 +3348,7 @@ dependencies = [ [[package]] name = "gotrue" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=0ec12c5f2fdd1bce0a0457eafb9963532b5208b3#0ec12c5f2fdd1bce0a0457eafb9963532b5208b3" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=66deaf3fa30564530160f7ac6de6dd2833d3d211#66deaf3fa30564530160f7ac6de6dd2833d3d211" dependencies = [ "anyhow", "futures-util", @@ -3365,7 +3365,7 @@ dependencies = [ [[package]] name = "gotrue-entity" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=0ec12c5f2fdd1bce0a0457eafb9963532b5208b3#0ec12c5f2fdd1bce0a0457eafb9963532b5208b3" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=66deaf3fa30564530160f7ac6de6dd2833d3d211#66deaf3fa30564530160f7ac6de6dd2833d3d211" dependencies = [ "anyhow", "app-error", @@ -3802,7 +3802,7 @@ dependencies = [ [[package]] name = "infra" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=0ec12c5f2fdd1bce0a0457eafb9963532b5208b3#0ec12c5f2fdd1bce0a0457eafb9963532b5208b3" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=66deaf3fa30564530160f7ac6de6dd2833d3d211#66deaf3fa30564530160f7ac6de6dd2833d3d211" dependencies = [ "anyhow", "bytes", @@ -5425,7 +5425,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "146c289cda302b98a28d40c8b3b90498d6e526dd24ac2ecea73e4e491685b94a" dependencies = [ "bytes", - "prost-derive", + "prost-derive 0.12.3", +] + +[[package]] +name = "prost" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b0487d90e047de87f984913713b85c601c05609aad5b0df4b4573fbf69aa13f" +dependencies = [ + "bytes", + "prost-derive 0.13.3", ] [[package]] @@ -5442,7 +5452,7 @@ dependencies = [ "once_cell", "petgraph", "prettyplease", - "prost", + "prost 0.12.3", "prost-types", "regex", "syn 2.0.55", @@ -5463,13 +5473,26 @@ dependencies = [ "syn 2.0.55", ] +[[package]] +name = "prost-derive" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9552f850d5f0964a4e4d0bf306459ac29323ddfbae05e35a7c0d35cb0803cc5" +dependencies = [ + "anyhow", + "itertools 0.12.1", + "proc-macro2", + "quote", + "syn 2.0.55", +] + [[package]] name = "prost-types" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "193898f59edcf43c26227dcd4c8427f00d99d61e95dcde58dabd49fa291d470e" dependencies = [ - "prost", + "prost 0.12.3", ] [[package]] @@ -6480,7 +6503,7 @@ dependencies = [ [[package]] name = "shared-entity" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=0ec12c5f2fdd1bce0a0457eafb9963532b5208b3#0ec12c5f2fdd1bce0a0457eafb9963532b5208b3" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=66deaf3fa30564530160f7ac6de6dd2833d3d211#66deaf3fa30564530160f7ac6de6dd2833d3d211" dependencies = [ "anyhow", "app-error", diff --git a/frontend/appflowy_web_app/src-tauri/Cargo.toml b/frontend/appflowy_web_app/src-tauri/Cargo.toml index e570ab28e2..8f74ea4cd8 100644 --- a/frontend/appflowy_web_app/src-tauri/Cargo.toml +++ b/frontend/appflowy_web_app/src-tauri/Cargo.toml @@ -58,7 +58,7 @@ collab-importer = { version = "0.1" } # Run the script: # scripts/tool/update_client_api_rev.sh new_rev_id # ⚠️⚠️⚠️️ -client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "0ec12c5f2fdd1bce0a0457eafb9963532b5208b3" } +client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "66deaf3fa30564530160f7ac6de6dd2833d3d211" } [dependencies] serde_json.workspace = true @@ -118,14 +118,14 @@ custom-protocol = ["tauri/custom-protocol"] # To switch to the local path, run: # scripts/tool/update_collab_source.sh # ⚠️⚠️⚠️️ -collab = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8bd376c0c71ce366d763a70385c7b445a98241ed" } -collab-entity = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8bd376c0c71ce366d763a70385c7b445a98241ed" } -collab-folder = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8bd376c0c71ce366d763a70385c7b445a98241ed" } -collab-document = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8bd376c0c71ce366d763a70385c7b445a98241ed" } -collab-database = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8bd376c0c71ce366d763a70385c7b445a98241ed" } -collab-plugins = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8bd376c0c71ce366d763a70385c7b445a98241ed" } -collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8bd376c0c71ce366d763a70385c7b445a98241ed" } -collab-importer = { version = "0.1", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8bd376c0c71ce366d763a70385c7b445a98241ed" } +collab = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" } +collab-entity = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" } +collab-folder = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" } +collab-document = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" } +collab-database = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" } +collab-plugins = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" } +collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" } +collab-importer = { version = "0.1", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" } # Working directory: frontend diff --git a/frontend/rust-lib/Cargo.lock b/frontend/rust-lib/Cargo.lock index 308ca45910..537343e0fe 100644 --- a/frontend/rust-lib/Cargo.lock +++ b/frontend/rust-lib/Cargo.lock @@ -163,7 +163,7 @@ checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" [[package]] name = "app-error" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=0ec12c5f2fdd1bce0a0457eafb9963532b5208b3#0ec12c5f2fdd1bce0a0457eafb9963532b5208b3" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=66deaf3fa30564530160f7ac6de6dd2833d3d211#66deaf3fa30564530160f7ac6de6dd2833d3d211" dependencies = [ "anyhow", "bincode", @@ -183,7 +183,7 @@ dependencies = [ [[package]] name = "appflowy-ai-client" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=0ec12c5f2fdd1bce0a0457eafb9963532b5208b3#0ec12c5f2fdd1bce0a0457eafb9963532b5208b3" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=66deaf3fa30564530160f7ac6de6dd2833d3d211#66deaf3fa30564530160f7ac6de6dd2833d3d211" dependencies = [ "anyhow", "bytes", @@ -780,7 +780,7 @@ dependencies = [ [[package]] name = "client-api" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=0ec12c5f2fdd1bce0a0457eafb9963532b5208b3#0ec12c5f2fdd1bce0a0457eafb9963532b5208b3" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=66deaf3fa30564530160f7ac6de6dd2833d3d211#66deaf3fa30564530160f7ac6de6dd2833d3d211" dependencies = [ "again", "anyhow", @@ -810,7 +810,7 @@ dependencies = [ "parking_lot 0.12.1", "percent-encoding", "pin-project", - "prost", + "prost 0.13.3", "rayon", "reqwest", "scraper 0.17.1", @@ -835,7 +835,7 @@ dependencies = [ [[package]] name = "client-api-entity" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=0ec12c5f2fdd1bce0a0457eafb9963532b5208b3#0ec12c5f2fdd1bce0a0457eafb9963532b5208b3" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=66deaf3fa30564530160f7ac6de6dd2833d3d211#66deaf3fa30564530160f7ac6de6dd2833d3d211" dependencies = [ "collab-entity", "collab-rt-entity", @@ -848,7 +848,7 @@ dependencies = [ [[package]] name = "client-websocket" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=0ec12c5f2fdd1bce0a0457eafb9963532b5208b3#0ec12c5f2fdd1bce0a0457eafb9963532b5208b3" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=66deaf3fa30564530160f7ac6de6dd2833d3d211#66deaf3fa30564530160f7ac6de6dd2833d3d211" dependencies = [ "futures-channel", "futures-util", @@ -891,7 +891,7 @@ dependencies = [ [[package]] name = "collab" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8bd376c0c71ce366d763a70385c7b445a98241ed#8bd376c0c71ce366d763a70385c7b445a98241ed" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979" dependencies = [ "anyhow", "arc-swap", @@ -916,7 +916,7 @@ dependencies = [ [[package]] name = "collab-database" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8bd376c0c71ce366d763a70385c7b445a98241ed#8bd376c0c71ce366d763a70385c7b445a98241ed" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979" dependencies = [ "anyhow", "async-trait", @@ -955,7 +955,7 @@ dependencies = [ [[package]] name = "collab-document" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8bd376c0c71ce366d763a70385c7b445a98241ed#8bd376c0c71ce366d763a70385c7b445a98241ed" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979" dependencies = [ "anyhow", "arc-swap", @@ -976,13 +976,13 @@ dependencies = [ [[package]] name = "collab-entity" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8bd376c0c71ce366d763a70385c7b445a98241ed#8bd376c0c71ce366d763a70385c7b445a98241ed" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979" dependencies = [ "anyhow", "bytes", "collab", "getrandom 0.2.10", - "prost", + "prost 0.13.3", "prost-build", "protoc-bin-vendored", "serde", @@ -996,7 +996,7 @@ dependencies = [ [[package]] name = "collab-folder" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8bd376c0c71ce366d763a70385c7b445a98241ed#8bd376c0c71ce366d763a70385c7b445a98241ed" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979" dependencies = [ "anyhow", "arc-swap", @@ -1018,7 +1018,7 @@ dependencies = [ [[package]] name = "collab-importer" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8bd376c0c71ce366d763a70385c7b445a98241ed#8bd376c0c71ce366d763a70385c7b445a98241ed" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979" dependencies = [ "anyhow", "async-recursion", @@ -1079,7 +1079,7 @@ dependencies = [ [[package]] name = "collab-plugins" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8bd376c0c71ce366d763a70385c7b445a98241ed#8bd376c0c71ce366d763a70385c7b445a98241ed" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979" dependencies = [ "anyhow", "async-stream", @@ -1117,7 +1117,7 @@ dependencies = [ [[package]] name = "collab-rt-entity" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=0ec12c5f2fdd1bce0a0457eafb9963532b5208b3#0ec12c5f2fdd1bce0a0457eafb9963532b5208b3" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=66deaf3fa30564530160f7ac6de6dd2833d3d211#66deaf3fa30564530160f7ac6de6dd2833d3d211" dependencies = [ "anyhow", "bincode", @@ -1128,7 +1128,7 @@ dependencies = [ "collab-entity", "collab-rt-protocol", "database-entity", - "prost", + "prost 0.13.3", "prost-build", "protoc-bin-vendored", "serde", @@ -1142,7 +1142,7 @@ dependencies = [ [[package]] name = "collab-rt-protocol" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=0ec12c5f2fdd1bce0a0457eafb9963532b5208b3#0ec12c5f2fdd1bce0a0457eafb9963532b5208b3" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=66deaf3fa30564530160f7ac6de6dd2833d3d211#66deaf3fa30564530160f7ac6de6dd2833d3d211" dependencies = [ "anyhow", "async-trait", @@ -1159,7 +1159,7 @@ dependencies = [ [[package]] name = "collab-user" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=8bd376c0c71ce366d763a70385c7b445a98241ed#8bd376c0c71ce366d763a70385c7b445a98241ed" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979" dependencies = [ "anyhow", "collab", @@ -1203,7 +1203,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd326812b3fd01da5bb1af7d340d0d555fd3d4b641e7f1dfcf5962a902952787" dependencies = [ "futures-core", - "prost", + "prost 0.12.3", "prost-types", "tonic", "tracing-core", @@ -1537,7 +1537,7 @@ checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" [[package]] name = "database-entity" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=0ec12c5f2fdd1bce0a0457eafb9963532b5208b3#0ec12c5f2fdd1bce0a0457eafb9963532b5208b3" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=66deaf3fa30564530160f7ac6de6dd2833d3d211#66deaf3fa30564530160f7ac6de6dd2833d3d211" dependencies = [ "anyhow", "app-error", @@ -1547,7 +1547,7 @@ dependencies = [ "chrono", "collab-entity", "infra", - "prost", + "prost 0.13.3", "serde", "serde_json", "serde_repr", @@ -2980,7 +2980,7 @@ dependencies = [ [[package]] name = "gotrue" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=0ec12c5f2fdd1bce0a0457eafb9963532b5208b3#0ec12c5f2fdd1bce0a0457eafb9963532b5208b3" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=66deaf3fa30564530160f7ac6de6dd2833d3d211#66deaf3fa30564530160f7ac6de6dd2833d3d211" dependencies = [ "anyhow", "futures-util", @@ -2997,7 +2997,7 @@ dependencies = [ [[package]] name = "gotrue-entity" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=0ec12c5f2fdd1bce0a0457eafb9963532b5208b3#0ec12c5f2fdd1bce0a0457eafb9963532b5208b3" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=66deaf3fa30564530160f7ac6de6dd2833d3d211#66deaf3fa30564530160f7ac6de6dd2833d3d211" dependencies = [ "anyhow", "app-error", @@ -3359,7 +3359,7 @@ dependencies = [ [[package]] name = "infra" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=0ec12c5f2fdd1bce0a0457eafb9963532b5208b3#0ec12c5f2fdd1bce0a0457eafb9963532b5208b3" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=66deaf3fa30564530160f7ac6de6dd2833d3d211#66deaf3fa30564530160f7ac6de6dd2833d3d211" dependencies = [ "anyhow", "bytes", @@ -4653,7 +4653,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "146c289cda302b98a28d40c8b3b90498d6e526dd24ac2ecea73e4e491685b94a" dependencies = [ "bytes", - "prost-derive", + "prost-derive 0.12.3", +] + +[[package]] +name = "prost" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b0487d90e047de87f984913713b85c601c05609aad5b0df4b4573fbf69aa13f" +dependencies = [ + "bytes", + "prost-derive 0.13.3", ] [[package]] @@ -4670,7 +4680,7 @@ dependencies = [ "once_cell", "petgraph", "prettyplease", - "prost", + "prost 0.12.3", "prost-types", "regex", "syn 2.0.47", @@ -4691,13 +4701,26 @@ dependencies = [ "syn 2.0.47", ] +[[package]] +name = "prost-derive" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9552f850d5f0964a4e4d0bf306459ac29323ddfbae05e35a7c0d35cb0803cc5" +dependencies = [ + "anyhow", + "itertools 0.12.1", + "proc-macro2", + "quote", + "syn 2.0.47", +] + [[package]] name = "prost-types" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "193898f59edcf43c26227dcd4c8427f00d99d61e95dcde58dabd49fa291d470e" dependencies = [ - "prost", + "prost 0.12.3", ] [[package]] @@ -5665,7 +5688,7 @@ dependencies = [ [[package]] name = "shared-entity" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=0ec12c5f2fdd1bce0a0457eafb9963532b5208b3#0ec12c5f2fdd1bce0a0457eafb9963532b5208b3" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=66deaf3fa30564530160f7ac6de6dd2833d3d211#66deaf3fa30564530160f7ac6de6dd2833d3d211" dependencies = [ "anyhow", "app-error", @@ -6522,7 +6545,7 @@ dependencies = [ "hyper-timeout", "percent-encoding", "pin-project", - "prost", + "prost 0.12.3", "tokio", "tokio-stream", "tower", diff --git a/frontend/rust-lib/Cargo.toml b/frontend/rust-lib/Cargo.toml index ca8ea1a50d..9a33800e7d 100644 --- a/frontend/rust-lib/Cargo.toml +++ b/frontend/rust-lib/Cargo.toml @@ -106,8 +106,8 @@ dashmap = "6.0.1" # Run the script.add_workspace_members: # scripts/tool/update_client_api_rev.sh new_rev_id # ⚠️⚠️⚠️️ -client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "0ec12c5f2fdd1bce0a0457eafb9963532b5208b3" } -client-api-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "0ec12c5f2fdd1bce0a0457eafb9963532b5208b3" } +client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "66deaf3fa30564530160f7ac6de6dd2833d3d211" } +client-api-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "66deaf3fa30564530160f7ac6de6dd2833d3d211" } [profile.dev] opt-level = 0 @@ -142,14 +142,14 @@ rocksdb = { git = "https://github.com/rust-rocksdb/rust-rocksdb", rev = "1710120 # To switch to the local path, run: # scripts/tool/update_collab_source.sh # ⚠️⚠️⚠️️ -collab = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8bd376c0c71ce366d763a70385c7b445a98241ed" } -collab-entity = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8bd376c0c71ce366d763a70385c7b445a98241ed" } -collab-folder = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8bd376c0c71ce366d763a70385c7b445a98241ed" } -collab-document = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8bd376c0c71ce366d763a70385c7b445a98241ed" } -collab-database = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8bd376c0c71ce366d763a70385c7b445a98241ed" } -collab-plugins = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8bd376c0c71ce366d763a70385c7b445a98241ed" } -collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8bd376c0c71ce366d763a70385c7b445a98241ed" } -collab-importer = { version = "0.1", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "8bd376c0c71ce366d763a70385c7b445a98241ed" } +collab = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" } +collab-entity = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" } +collab-folder = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" } +collab-document = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" } +collab-database = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" } +collab-plugins = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" } +collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" } +collab-importer = { version = "0.1", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" } # Working directory: frontend # To update the commit ID, run: diff --git a/frontend/rust-lib/flowy-database2/src/manager.rs b/frontend/rust-lib/flowy-database2/src/manager.rs index 8246da8a97..20be056e00 100644 --- a/frontend/rust-lib/flowy-database2/src/manager.rs +++ b/frontend/rust-lib/flowy-database2/src/manager.rs @@ -23,7 +23,7 @@ use std::collections::HashMap; use std::sync::{Arc, Weak}; use std::time::Duration; use tokio::sync::Mutex; -use tracing::{error, info, instrument, trace, warn}; +use tracing::{error, info, instrument, trace}; use collab_integrate::collab_builder::{AppFlowyCollabBuilder, CollabBuilderConfig}; use collab_integrate::{CollabKVAction, CollabKVDB}; @@ -776,13 +776,6 @@ impl DatabaseCollabService for WorkspaceDatabaseCollabServiceImpl { ) -> Result { let object = self.build_collab_object(object_id, collab_type.clone())?; let data_source = if self.persistence.is_collab_exist(object_id) { - if encoded_collab.is_some() { - warn!( - "build collab: {}:{} with both local and remote encode collab", - collab_type, object_id - ); - } - trace!( "build collab: {}:{} from local encode collab", collab_type, @@ -903,18 +896,21 @@ impl DatabaseCollabService for WorkspaceDatabaseCollabServiceImpl { encoded_collab_by_id.insert(k, v); } - // 2. Fetch remaining collabs from remote - let remote_collabs = self - .batch_get_encode_collab(object_ids, collab_type) - .await?; + if !object_ids.is_empty() { + // 2. Fetch remaining collabs from remote + let remote_collabs = self + .batch_get_encode_collab(object_ids, collab_type) + .await?; - trace!( - "[Database]: load {} database row from remote", - remote_collabs.len() - ); - for (k, v) in remote_collabs { - encoded_collab_by_id.insert(k, v); + trace!( + "[Database]: load {} database row from remote", + remote_collabs.len() + ); + for (k, v) in remote_collabs { + encoded_collab_by_id.insert(k, v); + } } + Ok(encoded_collab_by_id) } diff --git a/frontend/rust-lib/flowy-database2/src/services/calculations/controller.rs b/frontend/rust-lib/flowy-database2/src/services/calculations/controller.rs index f8a74839d5..f6df04b171 100644 --- a/frontend/rust-lib/flowy-database2/src/services/calculations/controller.rs +++ b/frontend/rust-lib/flowy-database2/src/services/calculations/controller.rs @@ -110,11 +110,6 @@ impl CalculationsController { .handle_field_type_changed(field_id, new_field_type) .await }, - CalculationEvent::InitialRows(rows) => { - for row in rows { - self.handle_row_changed(row.as_ref()).await; - } - }, } Ok(()) @@ -214,7 +209,10 @@ impl CalculationsController { .await; // Update the calculation - if let Some(update) = self.update_calculation(calculation, &field, cells).await { + if let Some(update) = self + .update_calculation(calculation.as_ref(), &field, cells) + .await + { self .delegate .update_calculation(&self.view_id, update.clone()) @@ -255,10 +253,16 @@ impl CalculationsController { if cells.is_empty() { let calculations = self.delegate.get_all_calculations(&self.view_id).await; for calculation in calculations.into_iter() { - let cells = self - .get_or_fetch_cells(&calculation.field_id, &mut cells_by_field) - .await; - updates.extend(self.handle_cells_changed(calculation, cells).await); + if let Some(field) = self.delegate.get_field(&calculation.field_id).await { + let cells = self + .get_or_fetch_cells(&calculation.field_id, &mut cells_by_field) + .await; + updates.extend( + self + .handle_cells_changed(&field, calculation.as_ref(), cells) + .await, + ); + } } } @@ -270,8 +274,13 @@ impl CalculationsController { let cells = self .get_or_fetch_cells(&calculation.field_id, &mut cells_by_field) .await; - let changes = self.handle_cells_changed(calculation, cells).await; - updates.extend(changes); + + if let Some(field) = self.delegate.get_field(field_id).await { + let changes = self + .handle_cells_changed(&field, calculation.as_ref(), cells) + .await; + updates.extend(changes); + } } } @@ -305,23 +314,22 @@ impl CalculationsController { } /// field_cells will be the cells that belong to the field with field_id - async fn handle_cells_changed( + pub async fn handle_cells_changed( &self, - calculation: Arc, + field: &Field, + calculation: &Calculation, field_cells: Vec>, ) -> Vec { let mut updates = vec![]; - if let Some(field) = self.delegate.get_field(&calculation.field_id).await { - let update = self - .update_calculation(calculation, &field, field_cells) + let update = self + .update_calculation(calculation, &field, field_cells) + .await; + if let Some(update) = update { + updates.push(CalculationPB::from(&update)); + self + .delegate + .update_calculation(&self.view_id, update) .await; - if let Some(update) = update { - updates.push(CalculationPB::from(&update)); - self - .delegate - .update_calculation(&self.view_id, update) - .await; - } } updates @@ -330,7 +338,7 @@ impl CalculationsController { #[instrument(level = "trace", skip_all)] async fn update_calculation( &self, - calculation: Arc, + calculation: &Calculation, field: &Field, cells: Vec>, ) -> Option { @@ -401,7 +409,6 @@ impl CalculationsController { #[derive(Serialize, Deserialize, Clone, Debug)] pub(crate) enum CalculationEvent { - InitialRows(Vec>), RowChanged(Row), CellUpdated(String), FieldTypeChanged(String, FieldType), diff --git a/frontend/rust-lib/flowy-database2/src/services/database/database_editor.rs b/frontend/rust-lib/flowy-database2/src/services/database/database_editor.rs index 17e141fb4f..aa3b136035 100644 --- a/frontend/rust-lib/flowy-database2/src/services/database/database_editor.rs +++ b/frontend/rust-lib/flowy-database2/src/services/database/database_editor.rs @@ -50,6 +50,7 @@ use tokio::select; use tokio::sync::oneshot::Sender; use tokio::sync::RwLock as TokioRwLock; use tokio::sync::{broadcast, oneshot}; +use tokio::task::yield_now; use tokio_util::sync::CancellationToken; use tracing::{debug, error, event, info, instrument, trace, warn}; @@ -927,7 +928,7 @@ impl DatabaseEditor { match field_type { FieldType::LastEditedTime | FieldType::CreatedTime => { database - .get_rows_for_view(view_id, None) + .get_rows_for_view(view_id, 10, None) .await .filter_map(|result| async { match result { @@ -1525,7 +1526,9 @@ impl DatabaseEditor { let blocking_read = notify_finish.is_some() || order_rows.len() < 50; let (tx, rx) = oneshot::channel(); - self.async_load_rows(view_editor, Some(tx), new_token, blocking_read, row_orders); + self + .async_load_rows(view_editor, Some(tx), new_token, blocking_read, row_orders) + .await; if blocking_read { // the rows returned here are applied with filters and sorts if let Ok(rows) = rx.await { @@ -1566,7 +1569,7 @@ impl DatabaseEditor { } } - fn async_load_rows( + async fn async_load_rows( &self, view_editor: Arc, notify_finish: Option>>>, @@ -1579,6 +1582,7 @@ impl DatabaseEditor { blocking_read ); let cloned_database = Arc::downgrade(&self.database); + let fields = self.get_fields(&view_editor.view_id, None).await; tokio::spawn(async move { let apply_filter_and_sort = |mut loaded_rows: Vec>, view_editor: Arc| async move { @@ -1626,7 +1630,7 @@ impl DatabaseEditor { }; let mut loaded_rows = vec![]; - const CHUNK_SIZE: usize = 20; + const CHUNK_SIZE: usize = 10; let row_orders = view_editor.row_orders.read().await; let row_orders_chunks = row_orders.chunks(CHUNK_SIZE).collect::>(); @@ -1646,7 +1650,7 @@ impl DatabaseEditor { let new_loaded_rows: Vec> = database .read() .await - .init_database_rows(row_ids, None) + .init_database_rows(row_ids, chunk_row_orders.len(), None) .filter_map(|result| async { let database_row = result.ok()?; let read_guard = database_row.read().await; @@ -1661,6 +1665,7 @@ impl DatabaseEditor { info!("[Database]: stop loading database rows"); return; } + yield_now().await; } drop(row_orders); @@ -1670,15 +1675,13 @@ impl DatabaseEditor { blocking_read ); let loaded_rows = apply_filter_and_sort(loaded_rows, view_editor.clone()).await; - // Update calculation values let calculate_rows = loaded_rows.clone(); - if let Some(notify_finish) = notify_finish { let _ = notify_finish.send(loaded_rows); } tokio::spawn(async move { - let _ = view_editor.v_calculate_rows(calculate_rows).await; + let _ = view_editor.v_calculate_rows(fields, calculate_rows).await; }); }); } @@ -1781,7 +1784,7 @@ impl DatabaseEditor { match row_ids { None => { let mut row_data = vec![]; - let rows_stream = database.get_all_rows(None).await; + let rows_stream = database.get_all_rows(10, None).await; pin_mut!(rows_stream); while let Some(result) = rows_stream.next().await { if let Ok(row) = result { @@ -1958,7 +1961,9 @@ impl DatabaseViewOperation for DatabaseViewOperationImpl { trace!("{} has total row orders: {}", view_id, row_orders.len()); let mut all_rows = vec![]; let read_guard = self.database.read().await; - let rows_stream = read_guard.get_rows_from_row_orders(&row_orders, None).await; + let rows_stream = read_guard + .get_rows_from_row_orders(&row_orders, 10, None) + .await; pin_mut!(rows_stream); while let Some(result) = rows_stream.next().await { diff --git a/frontend/rust-lib/flowy-database2/src/services/database_view/view_editor.rs b/frontend/rust-lib/flowy-database2/src/services/database_view/view_editor.rs index 8fbff8dee5..3ce6dba723 100644 --- a/frontend/rust-lib/flowy-database2/src/services/database_view/view_editor.rs +++ b/frontend/rust-lib/flowy-database2/src/services/database_view/view_editor.rs @@ -2,18 +2,16 @@ use std::borrow::Cow; use std::collections::HashMap; use std::sync::Arc; -use super::notify_did_update_calculation; +use super::{notify_did_update_calculation, DatabaseViewChanged}; use crate::entities::{ - CalendarEventPB, CreateRowPayloadPB, DatabaseLayoutMetaPB, DatabaseLayoutSettingPB, - DeleteSortPayloadPB, FieldSettingsChangesetPB, FieldType, GroupChangesPB, GroupPB, InsertedRowPB, - LayoutSettingChangeset, LayoutSettingParams, RemoveCalculationChangesetPB, ReorderSortPayloadPB, - RowMetaPB, RowsChangePB, SortChangesetNotificationPB, SortPB, UpdateCalculationChangesetPB, - UpdateSortPayloadPB, + CalculationChangesetNotificationPB, CalendarEventPB, CreateRowPayloadPB, DatabaseLayoutMetaPB, + DatabaseLayoutSettingPB, DeleteSortPayloadPB, FieldSettingsChangesetPB, FieldType, + GroupChangesPB, GroupPB, InsertedRowPB, LayoutSettingChangeset, LayoutSettingParams, + RemoveCalculationChangesetPB, ReorderSortPayloadPB, RowMetaPB, RowsChangePB, + SortChangesetNotificationPB, SortPB, UpdateCalculationChangesetPB, UpdateSortPayloadPB, }; use crate::notification::{send_notification, DatabaseNotification}; -use crate::services::calculations::{ - Calculation, CalculationChangeset, CalculationEvent, CalculationsController, -}; +use crate::services::calculations::{Calculation, CalculationChangeset, CalculationsController}; use crate::services::cell::{CellBuilder, CellCache}; use crate::services::database::{database_view_setting_pb_from_view, DatabaseRowEvent, UpdatedRow}; use crate::services::database_view::view_calculations::make_calculations_controller; @@ -36,14 +34,14 @@ use crate::services::sort::{Sort, SortChangeset, SortController}; use collab_database::database::{gen_database_calculation_id, gen_database_sort_id, gen_row_id}; use collab_database::entity::DatabaseView; use collab_database::fields::Field; -use collab_database::rows::{Cells, CreateRowParams, Row, RowCell, RowDetail, RowId}; +use collab_database::rows::{Cell, Cells, CreateRowParams, Row, RowCell, RowDetail, RowId}; use collab_database::views::{DatabaseLayout, RowOrder}; use dashmap::DashMap; use flowy_error::{FlowyError, FlowyResult}; -use lib_infra::priority_task::QualityOfService; + use lib_infra::util::timestamp; use tokio::sync::{broadcast, RwLock}; -use tracing::{instrument, trace, warn}; +use tracing::{error, instrument, trace, warn}; pub struct DatabaseViewEditor { database_id: String, @@ -695,14 +693,57 @@ impl DatabaseViewEditor { Ok(()) } - pub async fn v_calculate_rows(&self, rows: Vec>) -> FlowyResult<()> { - self - .calculations_controller - .gen_task( - CalculationEvent::InitialRows(rows), - QualityOfService::UserInteractive, - ) - .await; + pub async fn v_calculate_rows(&self, fields: Vec, rows: Vec>) -> FlowyResult<()> { + let mut updates = vec![]; + // Filter fields to only those with calculations + let fields_with_calculations: Vec<(&Field, Calculation)> = + futures::future::join_all(fields.iter().map(|field| async move { + self + .delegate + .get_calculation(&self.view_id, &field.id) + .await + .map(|cal| (field, cal)) + })) + .await + .into_iter() + .filter_map(|result| result) + .collect(); + + // Pre-compute cells by field ID only for fields that have calculations + let mut cells_by_field_id: HashMap>> = fields_with_calculations + .iter() + .map(|(field, _)| { + let cells = rows + .iter() + .filter_map(|row| row.cells.get(&field.id).cloned().map(Arc::new)) + .collect::>>(); + (field.id.clone(), cells) + }) + .collect(); + + // Perform calculations for the filtered fields + for (field, calculation) in fields_with_calculations { + if let Some(cells) = cells_by_field_id.remove(&field.id) { + let changes = self + .calculations_controller + .handle_cells_changed(field, &calculation, cells) + .await; + updates.extend(changes); + } + } + + // Send notification if updates were made + if !updates.is_empty() { + let notification = CalculationChangesetNotificationPB::from_update(&self.view_id, updates); + if let Err(_err) = self + .notifier + .send(DatabaseViewChanged::CalculationValueNotification( + notification, + )) + { + error!("Failed to send CalculationValueNotification"); + } + } Ok(()) } @@ -805,7 +846,8 @@ impl DatabaseViewEditor { let row_orders = self.delegate.get_all_row_orders(&self.view_id).await; let rows = self.delegate.get_all_rows(&self.view_id, row_orders).await; - self.v_calculate_rows(rows).await?; + let fields = self.delegate.get_fields(&self.view_id, None).await; + self.v_calculate_rows(fields, rows).await?; Ok(()) } diff --git a/frontend/rust-lib/flowy-database2/src/services/share/csv/export.rs b/frontend/rust-lib/flowy-database2/src/services/share/csv/export.rs index c556f7ad3a..dd704f43d5 100644 --- a/frontend/rust-lib/flowy-database2/src/services/share/csv/export.rs +++ b/frontend/rust-lib/flowy-database2/src/services/share/csv/export.rs @@ -49,7 +49,7 @@ impl CSVExport { field_by_field_id.insert(field.id.clone(), field); }); let rows = database - .get_rows_for_view(&inline_view_id, None) + .get_rows_for_view(&inline_view_id, 20, None) .await .filter_map(|result| async { result.ok() }) .collect::>() From bb50466aa989bba17f49778a43a10314a8bfc460 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laura=20B=C3=A9cogn=C3=A9e?= Date: Sat, 7 Dec 2024 05:49:42 +0100 Subject: [PATCH 012/576] chore(i18n): update fr-FR translations (#6936) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit correct typo redémarer => redémarrer --- frontend/resources/translations/fr-FR.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/resources/translations/fr-FR.json b/frontend/resources/translations/fr-FR.json index bd8084b69e..308a0d0c80 100644 --- a/frontend/resources/translations/fr-FR.json +++ b/frontend/resources/translations/fr-FR.json @@ -942,7 +942,7 @@ "cloudURLHint": "Saisissez l'URL de base de votre serveur", "cloudWSURL": "URL du websocket", "cloudWSURLHint": "Saisissez l'adresse websocket de votre serveur", - "restartApp": "Redémarer", + "restartApp": "Redémarrer", "restartAppTip": "Redémarrez l'application pour que les modifications prennent effet. Veuillez noter que cela pourrait déconnecter votre compte actuel.", "changeServerTip": "Après avoir changé de serveur, vous devez cliquer sur le bouton de redémarrer pour que les modifications prennent effet", "enableEncryptPrompt": "Activez le chiffrement pour sécuriser vos données avec cette clé. Rangez-la en toute sécurité ; une fois activé, il ne peut pas être désactivé. En cas de perte, vos données deviennent irrécupérables. Cliquez pour copier", From 722b436cadf8f1fbf62d653c1ca841e27a52ca01 Mon Sep 17 00:00:00 2001 From: "Nathan.fooo" <86001920+appflowy@users.noreply.github.com> Date: Sun, 8 Dec 2024 18:25:25 +0800 Subject: [PATCH 013/576] chore: Ai chat context (#6929) * chore: implement chat setting * chore: clippy * chore: rename * chore: set rag_ids when creating a chat * chore: clippy * chore: fix test * chore: fix test * chore: fix test * chore: clippy --- .github/workflows/rust_ci.yaml | 2 +- .../ai_chat/application/chat_entity.dart | 12 +- .../application/chat_message_service.dart | 4 +- .../settings/share/import_service.dart | 4 +- .../menu/sidebar/import/import_panel.dart | 10 +- .../test/bloc_test/grid_test/util.dart | 2 +- frontend/appflowy_tauri/src-tauri/Cargo.lock | 17 +- frontend/appflowy_tauri/src-tauri/Cargo.toml | 16 +- .../appflowy_web_app/src-tauri/Cargo.lock | 17 +- .../appflowy_web_app/src-tauri/Cargo.toml | 16 +- frontend/rust-lib/Cargo.lock | 17 +- frontend/rust-lib/Cargo.toml | 16 +- .../src/folder_event.rs | 24 +- .../event-integration-test/src/user_event.rs | 8 +- .../tests/folder/local_test/import_test.rs | 4 +- .../local_test/publish_database_test.rs | 10 +- .../local_test/publish_document_test.rs | 8 +- frontend/rust-lib/flowy-ai-pub/src/cloud.rs | 18 +- frontend/rust-lib/flowy-ai/build.rs | 17 - frontend/rust-lib/flowy-ai/src/ai_manager.rs | 51 ++- frontend/rust-lib/flowy-ai/src/chat.rs | 20 +- frontend/rust-lib/flowy-ai/src/entities.rs | 6 +- .../rust-lib/flowy-ai/src/event_handler.rs | 19 +- .../flowy-ai/src/local_ai/local_llm_chat.rs | 12 +- .../src/local_ai/local_llm_resource.rs | 1 - .../src/middleware/chat_service_mw.rs | 38 ++- .../rust-lib/flowy-ai/src/notification.rs | 2 +- .../flowy-core/src/deps_resolve/chat_deps.rs | 29 +- .../src/deps_resolve/folder_deps.rs | 131 ++++--- .../flowy-core/src/integrate/trait_impls.rs | 31 +- frontend/rust-lib/flowy-core/src/lib.rs | 44 +-- .../entities/filter_entities/date_filter.rs | 4 +- .../type_option_entities/checkbox_entities.rs | 12 + .../flowy-database2/src/event_handler.rs | 6 +- .../rust-lib/flowy-database2/src/manager.rs | 18 +- .../flowy-database2/src/notification.rs | 2 +- .../src/services/calculations/controller.rs | 8 +- .../src/services/cell/cell_operation.rs | 14 +- .../src/services/cell/type_cell_data.rs | 14 +- .../src/services/database/database_editor.rs | 18 +- .../src/services/database/database_observe.rs | 22 +- .../src/services/database_view/notifier.rs | 41 ++- .../src/services/database_view/view_editor.rs | 14 +- .../src/services/field/field_builder.rs | 2 +- .../checkbox_type_option/checkbox_tests.rs | 3 +- .../checkbox_type_option.rs | 4 +- .../checkbox_type_option_entities.rs | 14 +- .../media_type_option/media_type_option.rs | 12 +- .../relation_type_option/relation.rs | 3 +- .../multi_select_type_option.rs | 6 +- .../select_type_option.rs | 3 +- .../text_type_option/text_type_option.rs | 6 +- .../field/type_options/type_option.rs | 3 +- .../src/services/field/type_options/util.rs | 7 +- .../src/services/filter/controller.rs | 6 +- .../src/services/filter/entities.rs | 5 +- .../src/services/group/action.rs | 7 +- .../src/services/group/configuration.rs | 3 +- .../src/services/group/controller.rs | 7 +- .../controller_impls/default_controller.rs | 8 +- .../src/services/sort/controller.rs | 6 +- .../tests/database/filter_test/script.rs | 3 +- .../pre_fill_row_with_payload_test.rs | 3 +- frontend/rust-lib/flowy-document/build.rs | 17 - .../rust-lib/flowy-document/src/document.rs | 15 +- .../rust-lib/flowy-document/src/manager.rs | 3 +- .../flowy-document/src/notification.rs | 5 +- .../tests/parser/json/parser_test.rs | 2 +- frontend/rust-lib/flowy-error/build.rs | 9 - .../flowy-folder-pub/src/folder_builder.rs | 321 ------------------ frontend/rust-lib/flowy-folder-pub/src/lib.rs | 1 + .../rust-lib/flowy-folder-pub/src/query.rs | 6 + frontend/rust-lib/flowy-folder/Cargo.toml | 5 +- frontend/rust-lib/flowy-folder/build.rs | 17 - .../flowy-folder/src/entities/import.rs | 61 ++-- frontend/rust-lib/flowy-folder/src/lib.rs | 2 - frontend/rust-lib/flowy-folder/src/manager.rs | 173 ++++++---- .../rust-lib/flowy-folder/src/manager_init.rs | 5 - .../flowy-folder/src/manager_observer.rs | 66 +--- .../flowy-folder/src/manager_test_util.rs | 34 -- .../rust-lib/flowy-folder/src/notification.rs | 4 +- .../rust-lib/flowy-folder/src/share/import.rs | 23 +- .../rust-lib/flowy-folder/src/user_default.rs | 16 +- .../flowy-folder/src/view_operation.rs | 14 +- frontend/rust-lib/flowy-notification/build.rs | 9 - .../flowy-search/src/folder/indexer.rs | 4 +- .../flowy-search/src/services/manager.rs | 6 +- .../flowy-server/src/af_cloud/impls/chat.rs | 34 +- .../flowy-server/src/af_cloud/server.rs | 10 +- .../rust-lib/flowy-server/src/default_impl.rs | 22 +- .../rust-lib/flowy-server/src/response.rs | 119 +------ .../flowy-server/src/supabase/api/database.rs | 5 +- .../flowy-server/src/supabase/api/document.rs | 6 +- .../flowy-server/src/supabase/api/folder.rs | 4 +- .../flowy-server/src/supabase/api/user.rs | 8 +- .../flowy-sqlite/src/sqlite_impl/pragma.rs | 6 + .../rust-lib/flowy-user/src/event_handler.rs | 4 +- .../data_import/appflowy_data_import.rs | 12 +- .../rust-lib/flowy-user/src/services/db.rs | 4 +- .../flowy-user/src/user_manager/manager.rs | 7 +- .../user_manager/manager_user_workspace.rs | 3 +- .../rust-lib/lib-dispatch/src/dispatcher.rs | 9 - .../lib-dispatch/src/module/module.rs | 2 +- frontend/rust-lib/lib-log/src/lib.rs | 2 +- frontend/rust-lib/rust-toolchain.toml | 2 +- 105 files changed, 848 insertions(+), 1119 deletions(-) delete mode 100644 frontend/rust-lib/flowy-folder-pub/src/folder_builder.rs create mode 100644 frontend/rust-lib/flowy-folder-pub/src/query.rs delete mode 100644 frontend/rust-lib/flowy-folder/src/manager_test_util.rs diff --git a/.github/workflows/rust_ci.yaml b/.github/workflows/rust_ci.yaml index 6b2417f3db..5299c047cc 100644 --- a/.github/workflows/rust_ci.yaml +++ b/.github/workflows/rust_ci.yaml @@ -18,7 +18,7 @@ on: env: CARGO_TERM_COLOR: always - CLOUD_VERSION: 0.7.6-amd64 + CLOUD_VERSION: 0.8.3-amd64 RUST_TOOLCHAIN: "1.80.1" jobs: diff --git a/frontend/appflowy_flutter/lib/plugins/ai_chat/application/chat_entity.dart b/frontend/appflowy_flutter/lib/plugins/ai_chat/application/chat_entity.dart index abf1b99f44..e28c93a6a5 100644 --- a/frontend/appflowy_flutter/lib/plugins/ai_chat/application/chat_entity.dart +++ b/frontend/appflowy_flutter/lib/plugins/ai_chat/application/chat_entity.dart @@ -77,19 +77,19 @@ class ChatFile extends Equatable { final fileName = path.basename(filePath); final extension = path.extension(filePath).toLowerCase(); - ChatMessageMetaTypePB fileType; + ContextLoaderTypePB fileType; switch (extension) { case '.pdf': - fileType = ChatMessageMetaTypePB.PDF; + fileType = ContextLoaderTypePB.PDF; break; case '.txt': - fileType = ChatMessageMetaTypePB.Txt; + fileType = ContextLoaderTypePB.Txt; break; case '.md': - fileType = ChatMessageMetaTypePB.Markdown; + fileType = ContextLoaderTypePB.Markdown; break; default: - fileType = ChatMessageMetaTypePB.UnknownMetaType; + fileType = ContextLoaderTypePB.UnknownLoaderType; } return ChatFile( @@ -101,7 +101,7 @@ class ChatFile extends Equatable { final String filePath; final String fileName; - final ChatMessageMetaTypePB fileType; + final ContextLoaderTypePB fileType; @override List get props => [filePath]; diff --git a/frontend/appflowy_flutter/lib/plugins/ai_chat/application/chat_message_service.dart b/frontend/appflowy_flutter/lib/plugins/ai_chat/application/chat_message_service.dart index 70b64ac927..5bd8a35e5b 100644 --- a/frontend/appflowy_flutter/lib/plugins/ai_chat/application/chat_message_service.dart +++ b/frontend/appflowy_flutter/lib/plugins/ai_chat/application/chat_message_service.dart @@ -138,7 +138,7 @@ Future> metadataPBFromMetadata( id: value.id, name: value.name, data: pb.text, - dataType: ChatMessageMetaTypePB.Txt, + loaderType: ContextLoaderTypePB.Txt, source: appflowySource, ), ); @@ -156,7 +156,7 @@ Future> metadataPBFromMetadata( id: nanoid(8), name: fileName, data: filePath, - dataType: fileType, + loaderType: fileType, source: filePath, ), ); diff --git a/frontend/appflowy_flutter/lib/workspace/application/settings/share/import_service.dart b/frontend/appflowy_flutter/lib/workspace/application/settings/share/import_service.dart index 021203e9b9..205a61f7e3 100644 --- a/frontend/appflowy_flutter/lib/workspace/application/settings/share/import_service.dart +++ b/frontend/appflowy_flutter/lib/workspace/application/settings/share/import_service.dart @@ -18,11 +18,11 @@ class ImportPayload { class ImportBackendService { static Future> importPages( String parentViewId, - List values, + List values, ) async { final request = ImportPayloadPB( parentViewId: parentViewId, - values: values, + items: values, ); return FolderEventImportData(request).send(); diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/import/import_panel.dart b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/import/import_panel.dart index 6cfa675a90..6a23c7def9 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/import/import_panel.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/import/import_panel.dart @@ -151,7 +151,7 @@ class _ImportPanelState extends State { showLoading.value = true; - final importValues = []; + final importValues = []; for (final file in result.files) { final path = file.path; if (path == null) { @@ -163,7 +163,7 @@ class _ImportPanelState extends State { case ImportType.historyDatabase: final data = await File(path).readAsString(); importValues.add( - ImportValuePayloadPB.create() + ImportItemPayloadPB.create() ..name = name ..data = utf8.encode(data) ..viewLayout = ViewLayoutPB.Grid @@ -176,7 +176,7 @@ class _ImportPanelState extends State { final bytes = _documentDataFrom(importType, data); if (bytes != null) { importValues.add( - ImportValuePayloadPB.create() + ImportItemPayloadPB.create() ..name = name ..data = bytes ..viewLayout = ViewLayoutPB.Document @@ -187,7 +187,7 @@ class _ImportPanelState extends State { case ImportType.csv: final data = await File(path).readAsString(); importValues.add( - ImportValuePayloadPB.create() + ImportItemPayloadPB.create() ..name = name ..data = utf8.encode(data) ..viewLayout = ViewLayoutPB.Grid @@ -197,7 +197,7 @@ class _ImportPanelState extends State { case ImportType.afDatabase: final data = await File(path).readAsString(); importValues.add( - ImportValuePayloadPB.create() + ImportItemPayloadPB.create() ..name = name ..data = utf8.encode(data) ..viewLayout = ViewLayoutPB.Grid diff --git a/frontend/appflowy_flutter/test/bloc_test/grid_test/util.dart b/frontend/appflowy_flutter/test/bloc_test/grid_test/util.dart index becc14c883..e80ff1cc4b 100644 --- a/frontend/appflowy_flutter/test/bloc_test/grid_test/util.dart +++ b/frontend/appflowy_flutter/test/bloc_test/grid_test/util.dart @@ -121,7 +121,7 @@ class AppFlowyGridTest { final context = await ImportBackendService.importPages( workspace.id, [ - ImportValuePayloadPB() + ImportItemPayloadPB() ..name = fileName ..data = utf8.encode(data) ..viewLayout = ViewLayoutPB.Grid diff --git a/frontend/appflowy_tauri/src-tauri/Cargo.lock b/frontend/appflowy_tauri/src-tauri/Cargo.lock index e2415c8439..7ed6d8a970 100644 --- a/frontend/appflowy_tauri/src-tauri/Cargo.lock +++ b/frontend/appflowy_tauri/src-tauri/Cargo.lock @@ -1030,7 +1030,7 @@ dependencies = [ [[package]] name = "collab" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=79bc18e4651c17c83d9f00a5ce79423398e88ea1#79bc18e4651c17c83d9f00a5ce79423398e88ea1" dependencies = [ "anyhow", "arc-swap", @@ -1055,7 +1055,7 @@ dependencies = [ [[package]] name = "collab-database" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=79bc18e4651c17c83d9f00a5ce79423398e88ea1#79bc18e4651c17c83d9f00a5ce79423398e88ea1" dependencies = [ "anyhow", "async-trait", @@ -1094,7 +1094,7 @@ dependencies = [ [[package]] name = "collab-document" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=79bc18e4651c17c83d9f00a5ce79423398e88ea1#79bc18e4651c17c83d9f00a5ce79423398e88ea1" dependencies = [ "anyhow", "arc-swap", @@ -1115,7 +1115,7 @@ dependencies = [ [[package]] name = "collab-entity" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=79bc18e4651c17c83d9f00a5ce79423398e88ea1#79bc18e4651c17c83d9f00a5ce79423398e88ea1" dependencies = [ "anyhow", "bytes", @@ -1135,7 +1135,7 @@ dependencies = [ [[package]] name = "collab-folder" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=79bc18e4651c17c83d9f00a5ce79423398e88ea1#79bc18e4651c17c83d9f00a5ce79423398e88ea1" dependencies = [ "anyhow", "arc-swap", @@ -1157,7 +1157,7 @@ dependencies = [ [[package]] name = "collab-importer" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=79bc18e4651c17c83d9f00a5ce79423398e88ea1#79bc18e4651c17c83d9f00a5ce79423398e88ea1" dependencies = [ "anyhow", "async-recursion", @@ -1218,7 +1218,7 @@ dependencies = [ [[package]] name = "collab-plugins" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=79bc18e4651c17c83d9f00a5ce79423398e88ea1#79bc18e4651c17c83d9f00a5ce79423398e88ea1" dependencies = [ "anyhow", "async-stream", @@ -1298,7 +1298,7 @@ dependencies = [ [[package]] name = "collab-user" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=79bc18e4651c17c83d9f00a5ce79423398e88ea1#79bc18e4651c17c83d9f00a5ce79423398e88ea1" dependencies = [ "anyhow", "collab", @@ -2484,6 +2484,7 @@ dependencies = [ "collab-folder", "collab-integrate", "collab-plugins", + "dashmap 6.0.1", "flowy-codegen", "flowy-derive", "flowy-error", diff --git a/frontend/appflowy_tauri/src-tauri/Cargo.toml b/frontend/appflowy_tauri/src-tauri/Cargo.toml index 97ba680f4d..ed2228d3ab 100644 --- a/frontend/appflowy_tauri/src-tauri/Cargo.toml +++ b/frontend/appflowy_tauri/src-tauri/Cargo.toml @@ -120,14 +120,14 @@ custom-protocol = ["tauri/custom-protocol"] # To switch to the local path, run: # scripts/tool/update_collab_source.sh # ⚠️⚠️⚠️️ -collab = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" } -collab-entity = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" } -collab-folder = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" } -collab-document = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" } -collab-database = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" } -collab-plugins = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" } -collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" } -collab-importer = { version = "0.1", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" } +collab = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "79bc18e4651c17c83d9f00a5ce79423398e88ea1" } +collab-entity = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "79bc18e4651c17c83d9f00a5ce79423398e88ea1" } +collab-folder = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "79bc18e4651c17c83d9f00a5ce79423398e88ea1" } +collab-document = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "79bc18e4651c17c83d9f00a5ce79423398e88ea1" } +collab-database = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "79bc18e4651c17c83d9f00a5ce79423398e88ea1" } +collab-plugins = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "79bc18e4651c17c83d9f00a5ce79423398e88ea1" } +collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "79bc18e4651c17c83d9f00a5ce79423398e88ea1" } +collab-importer = { version = "0.1", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "79bc18e4651c17c83d9f00a5ce79423398e88ea1" } # Working directory: frontend # To update the commit ID, run: diff --git a/frontend/appflowy_web_app/src-tauri/Cargo.lock b/frontend/appflowy_web_app/src-tauri/Cargo.lock index 4e99d32391..60716c27aa 100644 --- a/frontend/appflowy_web_app/src-tauri/Cargo.lock +++ b/frontend/appflowy_web_app/src-tauri/Cargo.lock @@ -1028,7 +1028,7 @@ dependencies = [ [[package]] name = "collab" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=79bc18e4651c17c83d9f00a5ce79423398e88ea1#79bc18e4651c17c83d9f00a5ce79423398e88ea1" dependencies = [ "anyhow", "arc-swap", @@ -1053,7 +1053,7 @@ dependencies = [ [[package]] name = "collab-database" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=79bc18e4651c17c83d9f00a5ce79423398e88ea1#79bc18e4651c17c83d9f00a5ce79423398e88ea1" dependencies = [ "anyhow", "async-trait", @@ -1092,7 +1092,7 @@ dependencies = [ [[package]] name = "collab-document" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=79bc18e4651c17c83d9f00a5ce79423398e88ea1#79bc18e4651c17c83d9f00a5ce79423398e88ea1" dependencies = [ "anyhow", "arc-swap", @@ -1113,7 +1113,7 @@ dependencies = [ [[package]] name = "collab-entity" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=79bc18e4651c17c83d9f00a5ce79423398e88ea1#79bc18e4651c17c83d9f00a5ce79423398e88ea1" dependencies = [ "anyhow", "bytes", @@ -1133,7 +1133,7 @@ dependencies = [ [[package]] name = "collab-folder" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=79bc18e4651c17c83d9f00a5ce79423398e88ea1#79bc18e4651c17c83d9f00a5ce79423398e88ea1" dependencies = [ "anyhow", "arc-swap", @@ -1155,7 +1155,7 @@ dependencies = [ [[package]] name = "collab-importer" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=79bc18e4651c17c83d9f00a5ce79423398e88ea1#79bc18e4651c17c83d9f00a5ce79423398e88ea1" dependencies = [ "anyhow", "async-recursion", @@ -1216,7 +1216,7 @@ dependencies = [ [[package]] name = "collab-plugins" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=79bc18e4651c17c83d9f00a5ce79423398e88ea1#79bc18e4651c17c83d9f00a5ce79423398e88ea1" dependencies = [ "anyhow", "async-stream", @@ -1296,7 +1296,7 @@ dependencies = [ [[package]] name = "collab-user" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=79bc18e4651c17c83d9f00a5ce79423398e88ea1#79bc18e4651c17c83d9f00a5ce79423398e88ea1" dependencies = [ "anyhow", "collab", @@ -2529,6 +2529,7 @@ dependencies = [ "collab-folder", "collab-integrate", "collab-plugins", + "dashmap 6.0.1", "flowy-codegen", "flowy-derive", "flowy-error", diff --git a/frontend/appflowy_web_app/src-tauri/Cargo.toml b/frontend/appflowy_web_app/src-tauri/Cargo.toml index 8f74ea4cd8..713446d6e8 100644 --- a/frontend/appflowy_web_app/src-tauri/Cargo.toml +++ b/frontend/appflowy_web_app/src-tauri/Cargo.toml @@ -118,14 +118,14 @@ custom-protocol = ["tauri/custom-protocol"] # To switch to the local path, run: # scripts/tool/update_collab_source.sh # ⚠️⚠️⚠️️ -collab = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" } -collab-entity = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" } -collab-folder = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" } -collab-document = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" } -collab-database = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" } -collab-plugins = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" } -collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" } -collab-importer = { version = "0.1", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" } +collab = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "79bc18e4651c17c83d9f00a5ce79423398e88ea1" } +collab-entity = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "79bc18e4651c17c83d9f00a5ce79423398e88ea1" } +collab-folder = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "79bc18e4651c17c83d9f00a5ce79423398e88ea1" } +collab-document = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "79bc18e4651c17c83d9f00a5ce79423398e88ea1" } +collab-database = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "79bc18e4651c17c83d9f00a5ce79423398e88ea1" } +collab-plugins = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "79bc18e4651c17c83d9f00a5ce79423398e88ea1" } +collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "79bc18e4651c17c83d9f00a5ce79423398e88ea1" } +collab-importer = { version = "0.1", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "79bc18e4651c17c83d9f00a5ce79423398e88ea1" } # Working directory: frontend diff --git a/frontend/rust-lib/Cargo.lock b/frontend/rust-lib/Cargo.lock index 537343e0fe..f2df3b0c53 100644 --- a/frontend/rust-lib/Cargo.lock +++ b/frontend/rust-lib/Cargo.lock @@ -891,7 +891,7 @@ dependencies = [ [[package]] name = "collab" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=79bc18e4651c17c83d9f00a5ce79423398e88ea1#79bc18e4651c17c83d9f00a5ce79423398e88ea1" dependencies = [ "anyhow", "arc-swap", @@ -916,7 +916,7 @@ dependencies = [ [[package]] name = "collab-database" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=79bc18e4651c17c83d9f00a5ce79423398e88ea1#79bc18e4651c17c83d9f00a5ce79423398e88ea1" dependencies = [ "anyhow", "async-trait", @@ -955,7 +955,7 @@ dependencies = [ [[package]] name = "collab-document" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=79bc18e4651c17c83d9f00a5ce79423398e88ea1#79bc18e4651c17c83d9f00a5ce79423398e88ea1" dependencies = [ "anyhow", "arc-swap", @@ -976,7 +976,7 @@ dependencies = [ [[package]] name = "collab-entity" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=79bc18e4651c17c83d9f00a5ce79423398e88ea1#79bc18e4651c17c83d9f00a5ce79423398e88ea1" dependencies = [ "anyhow", "bytes", @@ -996,7 +996,7 @@ dependencies = [ [[package]] name = "collab-folder" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=79bc18e4651c17c83d9f00a5ce79423398e88ea1#79bc18e4651c17c83d9f00a5ce79423398e88ea1" dependencies = [ "anyhow", "arc-swap", @@ -1018,7 +1018,7 @@ dependencies = [ [[package]] name = "collab-importer" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=79bc18e4651c17c83d9f00a5ce79423398e88ea1#79bc18e4651c17c83d9f00a5ce79423398e88ea1" dependencies = [ "anyhow", "async-recursion", @@ -1079,7 +1079,7 @@ dependencies = [ [[package]] name = "collab-plugins" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=79bc18e4651c17c83d9f00a5ce79423398e88ea1#79bc18e4651c17c83d9f00a5ce79423398e88ea1" dependencies = [ "anyhow", "async-stream", @@ -1159,7 +1159,7 @@ dependencies = [ [[package]] name = "collab-user" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=35181c77b448d2ac9cbfbd729790bd98c5d1a979#35181c77b448d2ac9cbfbd729790bd98c5d1a979" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=79bc18e4651c17c83d9f00a5ce79423398e88ea1#79bc18e4651c17c83d9f00a5ce79423398e88ea1" dependencies = [ "anyhow", "collab", @@ -2349,6 +2349,7 @@ dependencies = [ "collab-folder", "collab-integrate", "collab-plugins", + "dashmap 6.0.1", "flowy-codegen", "flowy-derive", "flowy-error", diff --git a/frontend/rust-lib/Cargo.toml b/frontend/rust-lib/Cargo.toml index 9a33800e7d..81d29a9e51 100644 --- a/frontend/rust-lib/Cargo.toml +++ b/frontend/rust-lib/Cargo.toml @@ -142,14 +142,14 @@ rocksdb = { git = "https://github.com/rust-rocksdb/rust-rocksdb", rev = "1710120 # To switch to the local path, run: # scripts/tool/update_collab_source.sh # ⚠️⚠️⚠️️ -collab = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" } -collab-entity = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" } -collab-folder = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" } -collab-document = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" } -collab-database = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" } -collab-plugins = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" } -collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" } -collab-importer = { version = "0.1", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "35181c77b448d2ac9cbfbd729790bd98c5d1a979" } +collab = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "79bc18e4651c17c83d9f00a5ce79423398e88ea1" } +collab-entity = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "79bc18e4651c17c83d9f00a5ce79423398e88ea1" } +collab-folder = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "79bc18e4651c17c83d9f00a5ce79423398e88ea1" } +collab-document = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "79bc18e4651c17c83d9f00a5ce79423398e88ea1" } +collab-database = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "79bc18e4651c17c83d9f00a5ce79423398e88ea1" } +collab-plugins = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "79bc18e4651c17c83d9f00a5ce79423398e88ea1" } +collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "79bc18e4651c17c83d9f00a5ce79423398e88ea1" } +collab-importer = { version = "0.1", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "79bc18e4651c17c83d9f00a5ce79423398e88ea1" } # Working directory: frontend # To update the commit ID, run: diff --git a/frontend/rust-lib/event-integration-test/src/folder_event.rs b/frontend/rust-lib/event-integration-test/src/folder_event.rs index f7972c8712..c19ab85a8b 100644 --- a/frontend/rust-lib/event-integration-test/src/folder_event.rs +++ b/frontend/rust-lib/event-integration-test/src/folder_event.rs @@ -1,4 +1,4 @@ -use flowy_folder::view_operation::{EncodedCollabWrapper, ViewData}; +use flowy_folder::view_operation::{EncodedCollabType, ViewData}; use std::sync::Arc; use collab_folder::{FolderData, View}; @@ -165,15 +165,12 @@ impl EventIntegrationTest { } pub async fn get_folder_data(&self) -> FolderData { - let mutex_folder = self + self .appflowy_core .folder_manager - .get_mutex_folder() - .clone() - .unwrap(); - let folder = mutex_folder.read().await; - let workspace_id = self.appflowy_core.user_manager.workspace_id().unwrap(); - folder.get_folder_data(&workspace_id).clone().unwrap() + .get_folder_data() + .await + .unwrap() } pub async fn get_publish_payload( @@ -197,13 +194,10 @@ impl EventIntegrationTest { &self, view_id: &str, layout: ViewLayout, - ) -> EncodedCollabWrapper { - let manager = self.folder_manager.clone(); - let user = manager.get_user().clone(); - let handlers = manager.get_operation_handlers(); - let handler = handlers.get(&layout).unwrap(); - handler - .get_encoded_collab_v1_from_disk(user, view_id) + ) -> EncodedCollabType { + self + .folder_manager + .get_encode_collab_from_disk(view_id, &layout) .await .unwrap() } diff --git a/frontend/rust-lib/event-integration-test/src/user_event.rs b/frontend/rust-lib/event-integration-test/src/user_event.rs index a274121cba..b06242ba78 100644 --- a/frontend/rust-lib/event-integration-test/src/user_event.rs +++ b/frontend/rust-lib/event-integration-test/src/user_event.rs @@ -24,7 +24,7 @@ use flowy_user::entities::{ }; use flowy_user::errors::{FlowyError, FlowyResult}; use flowy_user::event_map::UserEvent; -use lib_dispatch::prelude::{af_spawn, AFPluginDispatcher, AFPluginRequest, ToBytes}; +use lib_dispatch::prelude::{AFPluginDispatcher, AFPluginRequest, ToBytes}; use crate::event_builder::EventBuilder; use crate::EventIntegrationTest; @@ -328,7 +328,7 @@ impl TestNotificationSender { let (tx, rx) = tokio::sync::mpsc::channel::(10); let mut receiver = self.sender.subscribe(); let ty = ty.into(); - af_spawn(async move { + tokio::spawn(async move { // DatabaseNotification::DidUpdateDatabaseSnapshotState while let Ok(value) = receiver.recv().await { if value.id == id && value.ty == ty { @@ -361,7 +361,7 @@ impl TestNotificationSender { let (tx, rx) = tokio::sync::mpsc::channel::<()>(10); let mut receiver = self.sender.subscribe(); let ty = ty.into(); - af_spawn(async move { + tokio::spawn(async move { // DatabaseNotification::DidUpdateDatabaseSnapshotState while let Ok(value) = receiver.recv().await { if value.id == id && value.ty == ty { @@ -380,7 +380,7 @@ impl TestNotificationSender { let id = id.to_string(); let (tx, rx) = tokio::sync::mpsc::channel::(1); let mut receiver = self.sender.subscribe(); - af_spawn(async move { + tokio::spawn(async move { while let Ok(value) = receiver.recv().await { if value.id == id { if let Some(payload) = value.payload { diff --git a/frontend/rust-lib/event-integration-test/tests/folder/local_test/import_test.rs b/frontend/rust-lib/event-integration-test/tests/folder/local_test/import_test.rs index 682cde232d..be31ecdaf3 100644 --- a/frontend/rust-lib/event-integration-test/tests/folder/local_test/import_test.rs +++ b/frontend/rust-lib/event-integration-test/tests/folder/local_test/import_test.rs @@ -1,7 +1,7 @@ use crate::util::unzip; use event_integration_test::EventIntegrationTest; use flowy_core::DEFAULT_NAME; -use flowy_folder::entities::{ImportPayloadPB, ImportTypePB, ImportValuePayloadPB, ViewLayoutPB}; +use flowy_folder::entities::{ImportItemPayloadPB, ImportPayloadPB, ImportTypePB, ViewLayoutPB}; #[tokio::test] async fn import_492_row_csv_file_test() { @@ -47,7 +47,7 @@ async fn import_10240_row_csv_file_test() { fn gen_import_data(file_name: String, csv_string: String, workspace_id: String) -> ImportPayloadPB { ImportPayloadPB { parent_view_id: workspace_id.clone(), - values: vec![ImportValuePayloadPB { + items: vec![ImportItemPayloadPB { name: file_name, data: Some(csv_string.as_bytes().to_vec()), file_path: None, diff --git a/frontend/rust-lib/event-integration-test/tests/folder/local_test/publish_database_test.rs b/frontend/rust-lib/event-integration-test/tests/folder/local_test/publish_database_test.rs index aeaa13c582..49935cbc86 100644 --- a/frontend/rust-lib/event-integration-test/tests/folder/local_test/publish_database_test.rs +++ b/frontend/rust-lib/event-integration-test/tests/folder/local_test/publish_database_test.rs @@ -3,9 +3,9 @@ use std::collections::HashMap; use collab_folder::ViewLayout; use event_integration_test::EventIntegrationTest; use flowy_folder::entities::{ - ImportPayloadPB, ImportTypePB, ImportValuePayloadPB, ViewLayoutPB, ViewPB, + ImportItemPayloadPB, ImportPayloadPB, ImportTypePB, ViewLayoutPB, ViewPB, }; -use flowy_folder::view_operation::EncodedCollabWrapper; +use flowy_folder::view_operation::EncodedCollabType; use crate::util::unzip; @@ -22,7 +22,7 @@ async fn publish_single_database_test() { .await; match grid_encoded_collab { - EncodedCollabWrapper::Database(encoded_collab) => { + EncodedCollabType::Database(encoded_collab) => { // the len of row collabs should be the same as the number of rows in the csv file let rows_len = encoded_collab.database_row_encoded_collabs.len(); assert_eq!(rows_len, 18); @@ -110,7 +110,7 @@ async fn test_publish_encode_collab_result( .await; match encoded_collab { - EncodedCollabWrapper::Database(encoded_collab) => { + EncodedCollabType::Database(encoded_collab) => { if let Some(rows_len) = expectations.get(&view.name.as_str()) { assert_eq!(encoded_collab.database_row_encoded_collabs.len(), *rows_len); } @@ -144,7 +144,7 @@ async fn import_csv(file_name: &str, test: &EventIntegrationTest) -> ViewPB { fn gen_import_data(file_name: String, csv_string: String, workspace_id: String) -> ImportPayloadPB { ImportPayloadPB { parent_view_id: workspace_id.clone(), - values: vec![ImportValuePayloadPB { + items: vec![ImportItemPayloadPB { name: file_name, data: Some(csv_string.as_bytes().to_vec()), file_path: None, diff --git a/frontend/rust-lib/event-integration-test/tests/folder/local_test/publish_document_test.rs b/frontend/rust-lib/event-integration-test/tests/folder/local_test/publish_document_test.rs index 268e5635fd..c5cad05eb7 100644 --- a/frontend/rust-lib/event-integration-test/tests/folder/local_test/publish_document_test.rs +++ b/frontend/rust-lib/event-integration-test/tests/folder/local_test/publish_document_test.rs @@ -2,7 +2,7 @@ use collab_folder::ViewLayout; use event_integration_test::EventIntegrationTest; use flowy_folder::entities::{ViewLayoutPB, ViewPB}; use flowy_folder::publish_util::generate_publish_name; -use flowy_folder::view_operation::EncodedCollabWrapper; +use flowy_folder::view_operation::EncodedCollabType; use flowy_folder_pub::entities::{ PublishDocumentPayload, PublishPayload, PublishViewInfo, PublishViewMeta, PublishViewMetaData, }; @@ -29,7 +29,7 @@ async fn mock_single_document_view_publish_payload( }; let data = match view_encoded_collab { - EncodedCollabWrapper::Document(doc) => doc.document_encoded_collab.doc_state.to_vec(), + EncodedCollabType::Document(doc) => doc.document_encoded_collab.doc_state.to_vec(), _ => panic!("Expected document collab"), }; @@ -89,12 +89,12 @@ async fn mock_nested_document_view_publish_payload( let child_publish_name = generate_publish_name(&child_view.id, &child_view.name); let data = match view_encoded_collab { - EncodedCollabWrapper::Document(doc) => doc.document_encoded_collab.doc_state.to_vec(), + EncodedCollabType::Document(doc) => doc.document_encoded_collab.doc_state.to_vec(), _ => panic!("Expected document collab"), }; let child_data = match child_view_encoded_collab { - EncodedCollabWrapper::Document(doc) => doc.document_encoded_collab.doc_state.to_vec(), + EncodedCollabType::Document(doc) => doc.document_encoded_collab.doc_state.to_vec(), _ => panic!("Expected document collab"), }; diff --git a/frontend/rust-lib/flowy-ai-pub/src/cloud.rs b/frontend/rust-lib/flowy-ai-pub/src/cloud.rs index c5293b400c..e5e6920c27 100644 --- a/frontend/rust-lib/flowy-ai-pub/src/cloud.rs +++ b/frontend/rust-lib/flowy-ai-pub/src/cloud.rs @@ -5,8 +5,8 @@ pub use client_api::entity::ai_dto::{ }; pub use client_api::entity::billing_dto::SubscriptionPlan; pub use client_api::entity::chat_dto::{ - ChatMessage, ChatMessageMetadata, ChatMessageType, ChatRAGData, ContextLoader, MessageCursor, - RepeatedChatMessage, + ChatMessage, ChatMessageMetadata, ChatMessageType, ChatRAGData, ChatSettings, ContextLoader, + MessageCursor, RepeatedChatMessage, UpdateChatParams, }; pub use client_api::entity::QuestionStreamValue; use client_api::error::AppResponseError; @@ -27,6 +27,7 @@ pub trait ChatCloudService: Send + Sync + 'static { uid: &i64, workspace_id: &str, chat_id: &str, + rag_ids: Vec, ) -> Result<(), FlowyError>; async fn create_question( @@ -97,4 +98,17 @@ pub trait ChatCloudService: Send + Sync + 'static { &self, workspace_id: &str, ) -> Result, FlowyError>; + + async fn get_chat_settings( + &self, + workspace_id: &str, + chat_id: &str, + ) -> Result; + + async fn update_chat_settings( + &self, + workspace_id: &str, + chat_id: &str, + params: UpdateChatParams, + ) -> Result<(), FlowyError>; } diff --git a/frontend/rust-lib/flowy-ai/build.rs b/frontend/rust-lib/flowy-ai/build.rs index fac4cc65ae..e9230d3d6d 100644 --- a/frontend/rust-lib/flowy-ai/build.rs +++ b/frontend/rust-lib/flowy-ai/build.rs @@ -20,21 +20,4 @@ fn main() { flowy_codegen::Project::TauriApp, ); } - - #[cfg(feature = "web_ts")] - { - flowy_codegen::ts_event::gen( - "folder", - flowy_codegen::Project::Web { - relative_path: "../../".to_string(), - }, - ); - flowy_codegen::protobuf_file::ts_gen( - env!("CARGO_PKG_NAME"), - "folder", - flowy_codegen::Project::Web { - relative_path: "../../".to_string(), - }, - ); - } } diff --git a/frontend/rust-lib/flowy-ai/src/ai_manager.rs b/frontend/rust-lib/flowy-ai/src/ai_manager.rs index 947ec7f857..6541052623 100644 --- a/frontend/rust-lib/flowy-ai/src/ai_manager.rs +++ b/frontend/rust-lib/flowy-ai/src/ai_manager.rs @@ -8,12 +8,15 @@ use crate::persistence::{insert_chat, read_chat_metadata, ChatTable}; use appflowy_plugin::manager::PluginManager; use dashmap::DashMap; -use flowy_ai_pub::cloud::{ChatCloudService, ChatMessageMetadata, ChatMessageType}; +use flowy_ai_pub::cloud::{ + ChatCloudService, ChatMessageMetadata, ChatMessageType, UpdateChatParams, +}; use flowy_error::{FlowyError, FlowyResult}; use flowy_sqlite::kv::KVStorePreferences; use flowy_sqlite::DBConnection; use flowy_storage_pub::storage::StorageService; +use lib_infra::async_trait::async_trait; use lib_infra::util::timestamp; use std::path::PathBuf; use std::sync::{Arc, Weak}; @@ -27,9 +30,19 @@ pub trait AIUserService: Send + Sync + 'static { fn application_root_dir(&self) -> Result; } +#[async_trait] +pub trait AIQueryService: Send + Sync + 'static { + async fn query_chat_rag_ids( + &self, + parent_view_id: &str, + chat_id: &str, + ) -> Result, FlowyError>; +} + pub struct AIManager { pub cloud_service_wm: Arc, pub user_service: Arc, + pub query_service: Arc, chats: Arc>>, pub local_ai_controller: Arc, } @@ -40,6 +53,7 @@ impl AIManager { user_service: impl AIUserService, store_preferences: Arc, storage_service: Weak, + query_service: impl AIQueryService, ) -> AIManager { let user_service = Arc::new(user_service); let plugin_manager = Arc::new(PluginManager::new()); @@ -49,6 +63,7 @@ impl AIManager { user_service.clone(), chat_cloud_service.clone(), )); + let query_service = Arc::new(query_service); // setup local chat service let cloud_service_wm = Arc::new(AICloudServiceMiddleware::new( @@ -63,6 +78,7 @@ impl AIManager { user_service, chats: Arc::new(DashMap::new()), local_ai_controller, + query_service, } } @@ -73,7 +89,6 @@ impl AIManager { } pub async fn open_chat(&self, chat_id: &str) -> Result<(), FlowyError> { - trace!("open chat: {}", chat_id); self.chats.entry(chat_id.to_string()).or_insert_with(|| { Arc::new(Chat::new( self.user_service.user_id().unwrap(), @@ -126,11 +141,23 @@ impl AIManager { }) } - pub async fn create_chat(&self, uid: &i64, chat_id: &str) -> Result, FlowyError> { + pub async fn create_chat( + &self, + uid: &i64, + parent_view_id: &str, + chat_id: &str, + ) -> Result, FlowyError> { let workspace_id = self.user_service.workspace_id()?; + let rag_ids = self + .query_service + .query_chat_rag_ids(parent_view_id, chat_id) + .await + .unwrap_or_default(); + info!("[Chat] create chat with rag_ids: {:?}", rag_ids); + self .cloud_service_wm - .create_chat(uid, &workspace_id, chat_id) + .create_chat(uid, &workspace_id, chat_id, rag_ids) .await?; save_chat(self.user_service.sqlite_connection(*uid)?, chat_id)?; @@ -257,6 +284,22 @@ impl AIManager { } pub fn local_ai_purchased(&self) {} + + pub async fn update_rag_ids(&self, chat_id: &str, rag_ids: Vec) -> FlowyResult<()> { + if !rag_ids.is_empty() { + let workspace_id = self.user_service.workspace_id()?; + let update_setting = UpdateChatParams { + name: None, + metadata: None, + rag_ids: Some(rag_ids), + }; + self + .cloud_service_wm + .update_chat_settings(&workspace_id, chat_id, update_setting) + .await?; + } + Ok(()) + } } fn save_chat(conn: DBConnection, chat_id: &str) -> FlowyResult<()> { diff --git a/frontend/rust-lib/flowy-ai/src/chat.rs b/frontend/rust-lib/flowy-ai/src/chat.rs index 9042642a9d..12d3b6c3d7 100644 --- a/frontend/rust-lib/flowy-ai/src/chat.rs +++ b/frontend/rust-lib/flowy-ai/src/chat.rs @@ -3,7 +3,7 @@ use crate::entities::{ ChatMessageErrorPB, ChatMessageListPB, ChatMessagePB, RepeatedRelatedQuestionPB, }; use crate::middleware::chat_service_mw::AICloudServiceMiddleware; -use crate::notification::{make_notification, ChatNotification}; +use crate::notification::{chat_notification_builder, ChatNotification}; use crate::persistence::{insert_chat_messages, select_chat_messages, ChatMessageTable}; use crate::stream_message::StreamMessage; use allo_isolate::Isolate; @@ -86,10 +86,6 @@ impl Chat { question_stream_port: i64, metadata: Vec, ) -> Result { - if message.len() > 2000 { - return Err(FlowyError::text_too_long().with_context("Exceeds maximum message 2000 length")); - } - trace!( "[Chat] stream chat message: chat_id={}, message={}, message_type={:?}, metadata={:?}", self.chat_id, @@ -181,7 +177,7 @@ impl Chat { chat_id: chat_id.clone(), error_message: err.to_string(), }; - make_notification(&chat_id, ChatNotification::StreamChatMessageError) + chat_notification_builder(&chat_id, ChatNotification::StreamChatMessageError) .payload(pb) .send(); return Err(err); @@ -201,14 +197,14 @@ impl Chat { chat_id: chat_id.clone(), error_message: err.to_string(), }; - make_notification(&chat_id, ChatNotification::StreamChatMessageError) + chat_notification_builder(&chat_id, ChatNotification::StreamChatMessageError) .payload(pb) .send(); return Err(err); }, } - make_notification(&chat_id, ChatNotification::FinishStreaming).send(); + chat_notification_builder(&chat_id, ChatNotification::FinishStreaming).send(); if answer_stream_buffer.lock().await.is_empty() { return Ok(()); } @@ -260,7 +256,7 @@ impl Chat { has_more: true, total: 0, }; - make_notification(&self.chat_id, ChatNotification::DidLoadPrevChatMessage) + chat_notification_builder(&self.chat_id, ChatNotification::DidLoadPrevChatMessage) .payload(pb.clone()) .send(); return Ok(pb); @@ -381,11 +377,11 @@ impl Chat { } else { *prev_message_state.write().await = PrevMessageState::NoMore; } - make_notification(&chat_id, ChatNotification::DidLoadPrevChatMessage) + chat_notification_builder(&chat_id, ChatNotification::DidLoadPrevChatMessage) .payload(pb) .send(); } else { - make_notification(&chat_id, ChatNotification::DidLoadLatestChatMessage) + chat_notification_builder(&chat_id, ChatNotification::DidLoadLatestChatMessage) .payload(pb) .send(); } @@ -571,7 +567,7 @@ pub(crate) fn save_and_notify_message( vec![message.clone()], )?; let pb = ChatMessagePB::from(message); - make_notification(chat_id, ChatNotification::DidReceiveChatMessage) + chat_notification_builder(chat_id, ChatNotification::DidReceiveChatMessage) .payload(pb) .send(); diff --git a/frontend/rust-lib/flowy-ai/src/entities.rs b/frontend/rust-lib/flowy-ai/src/entities.rs index 3dbfe55722..3f318797b9 100644 --- a/frontend/rust-lib/flowy-ai/src/entities.rs +++ b/frontend/rust-lib/flowy-ai/src/entities.rs @@ -83,16 +83,16 @@ pub struct ChatMessageMetaPB { pub data: String, #[pb(index = 4)] - pub data_type: ChatMessageMetaTypePB, + pub loader_type: ContextLoaderTypePB, #[pb(index = 5)] pub source: String, } #[derive(Debug, Default, Clone, ProtoBuf_Enum, PartialEq, Eq, Copy)] -pub enum ChatMessageMetaTypePB { +pub enum ContextLoaderTypePB { #[default] - UnknownMetaType = 0, + UnknownLoaderType = 0, Txt = 1, Markdown = 2, PDF = 3, diff --git a/frontend/rust-lib/flowy-ai/src/event_handler.rs b/frontend/rust-lib/flowy-ai/src/event_handler.rs index c3af7fbdca..ce2f1b878f 100644 --- a/frontend/rust-lib/flowy-ai/src/event_handler.rs +++ b/frontend/rust-lib/flowy-ai/src/event_handler.rs @@ -5,7 +5,9 @@ use crate::ai_manager::AIManager; use crate::completion::AICompletion; use crate::entities::*; use crate::local_ai::local_llm_chat::LLMModelInfo; -use crate::notification::{make_notification, ChatNotification, APPFLOWY_AI_NOTIFICATION_KEY}; +use crate::notification::{ + chat_notification_builder, ChatNotification, APPFLOWY_AI_NOTIFICATION_KEY, +}; use allo_isolate::Isolate; use flowy_ai_pub::cloud::{ChatMessageMetadata, ChatMessageType, ChatRAGData, ContextLoader}; use flowy_error::{ErrorCode, FlowyError, FlowyResult}; @@ -34,15 +36,16 @@ pub(crate) async fn stream_chat_message_handler( ChatMessageTypePB::System => ChatMessageType::System, ChatMessageTypePB::User => ChatMessageType::User, }; + let metadata = data .metadata .into_iter() .map(|metadata| { - let (content_type, content_len) = match metadata.data_type { - ChatMessageMetaTypePB::Txt => (ContextLoader::Text, metadata.data.len()), - ChatMessageMetaTypePB::Markdown => (ContextLoader::Markdown, metadata.data.len()), - ChatMessageMetaTypePB::PDF => (ContextLoader::PDF, 0), - ChatMessageMetaTypePB::UnknownMetaType => (ContextLoader::Unknown, 0), + let (content_type, content_len) = match metadata.loader_type { + ContextLoaderTypePB::Txt => (ContextLoader::Text, metadata.data.len()), + ContextLoaderTypePB::Markdown => (ContextLoader::Markdown, metadata.data.len()), + ContextLoaderTypePB::PDF => (ContextLoader::PDF, 0), + ContextLoaderTypePB::UnknownLoaderType => (ContextLoader::Unknown, 0), }; ChatMessageMetadata { @@ -298,7 +301,7 @@ pub(crate) async fn toggle_local_ai_chat_handler( file_enabled, plugin_state, }; - make_notification( + chat_notification_builder( APPFLOWY_AI_NOTIFICATION_KEY, ChatNotification::UpdateLocalChatAI, ) @@ -323,7 +326,7 @@ pub(crate) async fn toggle_local_ai_chat_file_handler( file_enabled, plugin_state, }; - make_notification( + chat_notification_builder( APPFLOWY_AI_NOTIFICATION_KEY, ChatNotification::UpdateLocalChatAI, ) diff --git a/frontend/rust-lib/flowy-ai/src/local_ai/local_llm_chat.rs b/frontend/rust-lib/flowy-ai/src/local_ai/local_llm_chat.rs index 6e4ac10bc2..ece0bdddda 100644 --- a/frontend/rust-lib/flowy-ai/src/local_ai/local_llm_chat.rs +++ b/frontend/rust-lib/flowy-ai/src/local_ai/local_llm_chat.rs @@ -1,7 +1,9 @@ use crate::ai_manager::AIUserService; use crate::entities::{LocalAIPluginStatePB, LocalModelResourcePB, RunningStatePB}; use crate::local_ai::local_llm_resource::{LLMResourceService, LocalAIResourceController}; -use crate::notification::{make_notification, ChatNotification, APPFLOWY_AI_NOTIFICATION_KEY}; +use crate::notification::{ + chat_notification_builder, ChatNotification, APPFLOWY_AI_NOTIFICATION_KEY, +}; use anyhow::Error; use appflowy_local_ai::chat_plugin::{AIPluginConfig, AppFlowyLocalAI}; use appflowy_plugin::manager::PluginManager; @@ -90,7 +92,7 @@ impl LocalAIController { info!("[AI Plugin] state: {:?}", state); let offline_ai_ready = cloned_llm_res.is_offline_app_ready(); let new_state = RunningStatePB::from(state); - make_notification( + chat_notification_builder( APPFLOWY_AI_NOTIFICATION_KEY, ChatNotification::UpdateChatPluginState, ) @@ -590,12 +592,6 @@ impl LLMResourceService for LLMResourceServiceImpl { .store_preferences .get_object::(LOCAL_AI_SETTING_KEY) } - - fn is_rag_enabled(&self) -> bool { - self - .store_preferences - .get_bool_or_default(APPFLOWY_LOCAL_AI_CHAT_RAG_ENABLED) - } } fn local_ai_enabled_key(workspace_id: &str) -> String { diff --git a/frontend/rust-lib/flowy-ai/src/local_ai/local_llm_resource.rs b/frontend/rust-lib/flowy-ai/src/local_ai/local_llm_resource.rs index 2340ea053f..90dc328a6d 100644 --- a/frontend/rust-lib/flowy-ai/src/local_ai/local_llm_resource.rs +++ b/frontend/rust-lib/flowy-ai/src/local_ai/local_llm_resource.rs @@ -28,7 +28,6 @@ pub trait LLMResourceService: Send + Sync + 'static { async fn fetch_local_ai_config(&self) -> Result; fn store_setting(&self, setting: LLMSetting) -> Result<(), anyhow::Error>; fn retrieve_setting(&self) -> Option; - fn is_rag_enabled(&self) -> bool; } const LLM_MODEL_DIR: &str = "models"; diff --git a/frontend/rust-lib/flowy-ai/src/middleware/chat_service_mw.rs b/frontend/rust-lib/flowy-ai/src/middleware/chat_service_mw.rs index 54f5972862..35ee1e191c 100644 --- a/frontend/rust-lib/flowy-ai/src/middleware/chat_service_mw.rs +++ b/frontend/rust-lib/flowy-ai/src/middleware/chat_service_mw.rs @@ -1,15 +1,17 @@ use crate::ai_manager::AIUserService; use crate::entities::{ChatStatePB, ModelTypePB}; use crate::local_ai::local_llm_chat::LocalAIController; -use crate::notification::{make_notification, ChatNotification, APPFLOWY_AI_NOTIFICATION_KEY}; +use crate::notification::{ + chat_notification_builder, ChatNotification, APPFLOWY_AI_NOTIFICATION_KEY, +}; use crate::persistence::{select_single_message, ChatMessageTable}; use appflowy_plugin::error::PluginError; use std::collections::HashMap; use flowy_ai_pub::cloud::{ - ChatCloudService, ChatMessage, ChatMessageMetadata, ChatMessageType, CompletionType, - LocalAIConfig, MessageCursor, RelatedQuestion, RepeatedChatMessage, RepeatedRelatedQuestion, - StreamAnswer, StreamComplete, SubscriptionPlan, + ChatCloudService, ChatMessage, ChatMessageMetadata, ChatMessageType, ChatSettings, + CompletionType, LocalAIConfig, MessageCursor, RelatedQuestion, RepeatedChatMessage, + RepeatedRelatedQuestion, StreamAnswer, StreamComplete, SubscriptionPlan, UpdateChatParams, }; use flowy_error::{FlowyError, FlowyResult}; use futures::{stream, Sink, StreamExt, TryStreamExt}; @@ -92,7 +94,7 @@ impl AICloudServiceMiddleware { err, PluginError::PluginNotConnected | PluginError::PeerDisconnect ) { - make_notification( + chat_notification_builder( APPFLOWY_AI_NOTIFICATION_KEY, ChatNotification::UpdateChatPluginState, ) @@ -112,10 +114,11 @@ impl ChatCloudService for AICloudServiceMiddleware { uid: &i64, workspace_id: &str, chat_id: &str, + rag_ids: Vec, ) -> Result<(), FlowyError> { self .cloud_service - .create_chat(uid, workspace_id, chat_id) + .create_chat(uid, workspace_id, chat_id, rag_ids) .await } @@ -314,4 +317,27 @@ impl ChatCloudService for AICloudServiceMiddleware { ) -> Result, FlowyError> { self.cloud_service.get_workspace_plan(workspace_id).await } + + async fn get_chat_settings( + &self, + workspace_id: &str, + chat_id: &str, + ) -> Result { + self + .cloud_service + .get_chat_settings(workspace_id, chat_id) + .await + } + + async fn update_chat_settings( + &self, + workspace_id: &str, + chat_id: &str, + params: UpdateChatParams, + ) -> Result<(), FlowyError> { + self + .cloud_service + .update_chat_settings(workspace_id, chat_id, params) + .await + } } diff --git a/frontend/rust-lib/flowy-ai/src/notification.rs b/frontend/rust-lib/flowy-ai/src/notification.rs index 7c0280ce31..24cd464cd1 100644 --- a/frontend/rust-lib/flowy-ai/src/notification.rs +++ b/frontend/rust-lib/flowy-ai/src/notification.rs @@ -37,6 +37,6 @@ impl std::convert::From for ChatNotification { } #[tracing::instrument(level = "trace")] -pub(crate) fn make_notification(id: &str, ty: ChatNotification) -> NotificationBuilder { +pub(crate) fn chat_notification_builder(id: &str, ty: ChatNotification) -> NotificationBuilder { NotificationBuilder::new(id, ty, CHAT_OBSERVABLE_SOURCE) } diff --git a/frontend/rust-lib/flowy-core/src/deps_resolve/chat_deps.rs b/frontend/rust-lib/flowy-core/src/deps_resolve/chat_deps.rs index c12cc0f181..82dd62b7d8 100644 --- a/frontend/rust-lib/flowy-core/src/deps_resolve/chat_deps.rs +++ b/frontend/rust-lib/flowy-core/src/deps_resolve/chat_deps.rs @@ -1,10 +1,12 @@ -use flowy_ai::ai_manager::{AIManager, AIUserService}; +use flowy_ai::ai_manager::{AIManager, AIQueryService, AIUserService}; use flowy_ai_pub::cloud::ChatCloudService; use flowy_error::FlowyError; +use flowy_folder_pub::query::FolderQueryService; use flowy_sqlite::kv::KVStorePreferences; use flowy_sqlite::DBConnection; use flowy_storage_pub::storage::StorageService; use flowy_user::services::authenticate_user::AuthenticateUser; +use lib_infra::async_trait::async_trait; use std::path::PathBuf; use std::sync::{Arc, Weak}; @@ -16,6 +18,7 @@ impl ChatDepsResolver { cloud_service: Arc, store_preferences: Arc, storage_service: Weak, + folder_query: impl FolderQueryService, ) -> Arc { let user_service = ChatUserServiceImpl(authenticate_user); Arc::new(AIManager::new( @@ -23,10 +26,34 @@ impl ChatDepsResolver { user_service, store_preferences, storage_service, + ChatQueryServiceImpl { + folder_query: Box::new(folder_query), + }, )) } } +struct ChatQueryServiceImpl { + folder_query: Box, +} + +#[async_trait] +impl AIQueryService for ChatQueryServiceImpl { + async fn query_chat_rag_ids( + &self, + parent_view_id: &str, + chat_id: &str, + ) -> Result, FlowyError> { + let mut ids = self.folder_query.get_sibling_ids(parent_view_id).await; + + if !ids.is_empty() { + ids.retain(|id| id != chat_id); + } + + Ok(ids) + } +} + struct ChatUserServiceImpl(Weak); impl ChatUserServiceImpl { fn upgrade_user(&self) -> Result, FlowyError> { diff --git a/frontend/rust-lib/flowy-core/src/deps_resolve/folder_deps.rs b/frontend/rust-lib/flowy-core/src/deps_resolve/folder_deps.rs index 1f38d30473..2a5747efce 100644 --- a/frontend/rust-lib/flowy-core/src/deps_resolve/folder_deps.rs +++ b/frontend/rust-lib/flowy-core/src/deps_resolve/folder_deps.rs @@ -17,8 +17,8 @@ use flowy_folder::entities::{CreateViewParams, ViewLayoutPB}; use flowy_folder::manager::{FolderManager, FolderUser}; use flowy_folder::share::ImportType; use flowy_folder::view_operation::{ - DatabaseEncodedCollab, DocumentEncodedCollab, EncodedCollabWrapper, FolderOperationHandler, - FolderOperationHandlers, ImportedData, View, ViewData, + DatabaseEncodedCollab, DocumentEncodedCollab, EncodedCollabType, FolderOperationHandler, + ImportedData, View, ViewData, }; use flowy_folder::ViewLayout; use flowy_search::folder::indexer::FolderIndexManagerImpl; @@ -34,6 +34,7 @@ use tokio::sync::RwLock; use crate::integrate::server::ServerProvider; use collab_plugins::local_storage::kv::KVTransactionDB; +use flowy_folder_pub::query::FolderQueryService; use lib_infra::async_trait::async_trait; pub struct FolderDepsResolver(); @@ -45,7 +46,6 @@ impl FolderDepsResolver { server_provider: Arc, folder_indexer: Arc, store_preferences: Arc, - operation_handlers: FolderOperationHandlers, ) -> Arc { let user: Arc = Arc::new(FolderUserImpl { authenticate_user: authenticate_user.clone(), @@ -55,7 +55,6 @@ impl FolderDepsResolver { FolderManager::new( user.clone(), collab_builder, - operation_handlers, server_provider.clone(), folder_indexer, store_preferences, @@ -65,23 +64,21 @@ impl FolderDepsResolver { } } -pub fn folder_operation_handlers( +pub fn register_handlers( + folder_manager: &Arc, document_manager: Arc, database_manager: Arc, chat_manager: Arc, -) -> FolderOperationHandlers { - let mut map: HashMap> = HashMap::new(); - +) { let document_folder_operation = Arc::new(DocumentFolderOperation(document_manager)); - map.insert(ViewLayout::Document, document_folder_operation); + folder_manager.register_operation_handler(ViewLayout::Document, document_folder_operation); let database_folder_operation = Arc::new(DatabaseFolderOperation(database_manager)); let chat_folder_operation = Arc::new(ChatFolderOperation(chat_manager)); - map.insert(ViewLayout::Board, database_folder_operation.clone()); - map.insert(ViewLayout::Grid, database_folder_operation.clone()); - map.insert(ViewLayout::Calendar, database_folder_operation); - map.insert(ViewLayout::Chat, chat_folder_operation); - Arc::new(map) + folder_manager.register_operation_handler(ViewLayout::Board, database_folder_operation.clone()); + folder_manager.register_operation_handler(ViewLayout::Grid, database_folder_operation.clone()); + folder_manager.register_operation_handler(ViewLayout::Calendar, database_folder_operation); + folder_manager.register_operation_handler(ViewLayout::Chat, chat_folder_operation); } struct FolderUserImpl { @@ -190,11 +187,33 @@ impl FolderOperationHandler for DocumentFolderOperation { Ok(Some(encoded_collab)) } + /// Create a view with built-in data. + async fn create_default_view( + &self, + user_id: i64, + _parent_view_id: &str, + view_id: &str, + _name: &str, + layout: ViewLayout, + ) -> Result<(), FlowyError> { + debug_assert_eq!(layout, ViewLayout::Document); + match self.0.create_document(user_id, view_id, None).await { + Ok(_) => Ok(()), + Err(err) => { + if err.is_already_exists() { + Ok(()) + } else { + Err(err) + } + }, + } + } + async fn get_encoded_collab_v1_from_disk( &self, - user: Arc, + user: &Arc, view_id: &str, - ) -> Result { + ) -> Result { // get the collab_object_id for the document. // the collab_object_id for the document is the view_id. let workspace_id = user.workspace_id()?; @@ -226,32 +245,11 @@ impl FolderOperationHandler for DocumentFolderOperation { FlowyError::internal().with_context(format!("encode document collab failed: {}", e)) })?; - Ok(EncodedCollabWrapper::Document(DocumentEncodedCollab { + Ok(EncodedCollabType::Document(DocumentEncodedCollab { document_encoded_collab: encoded_collab, })) } - /// Create a view with built-in data. - async fn create_view_with_default_data( - &self, - user_id: i64, - view_id: &str, - _name: &str, - layout: ViewLayout, - ) -> Result<(), FlowyError> { - debug_assert_eq!(layout, ViewLayout::Document); - match self.0.create_document(user_id, view_id, None).await { - Ok(_) => Ok(()), - Err(err) => { - if err.is_already_exists() { - Ok(()) - } else { - Err(err) - } - }, - } - } - async fn import_from_bytes( &self, uid: i64, @@ -272,13 +270,13 @@ impl FolderOperationHandler for DocumentFolderOperation { )]) } - // will implement soon async fn import_from_file_path( &self, _view_id: &str, _name: &str, _path: String, ) -> Result<(), FlowyError> { + // TODO(lucas): import file from local markdown file Ok(()) } @@ -311,9 +309,9 @@ impl FolderOperationHandler for DatabaseFolderOperation { async fn get_encoded_collab_v1_from_disk( &self, - user: Arc, + user: &Arc, view_id: &str, - ) -> Result { + ) -> Result { let workspace_id = user.workspace_id()?; // get the collab_object_id for the database. // @@ -405,7 +403,7 @@ impl FolderOperationHandler for DatabaseFolderOperation { }) .collect::, FlowyError>>()?; - Ok(EncodedCollabWrapper::Database(DatabaseEncodedCollab { + Ok(EncodedCollabType::Database(DatabaseEncodedCollab { database_encoded_collab, database_row_encoded_collabs, database_row_document_encoded_collabs, @@ -478,9 +476,10 @@ impl FolderOperationHandler for DatabaseFolderOperation { /// If the ext contains the {"database_id": "xx"}, then it will link to /// the existing database. The data of the database will be shared within /// these references views. - async fn create_view_with_default_data( + async fn create_default_view( &self, _user_id: i64, + _parent_view_id: &str, view_id: &str, name: &str, layout: ViewLayout, @@ -551,7 +550,10 @@ impl FolderOperationHandler for DatabaseFolderOperation { _name: &str, path: String, ) -> Result<(), FlowyError> { - self.0.import_csv_from_file(path, CSVFormat::META).await?; + self + .0 + .import_csv_from_file(path, CSVFormat::Original) + .await?; Ok(()) } @@ -625,14 +627,18 @@ impl FolderOperationHandler for ChatFolderOperation { Err(FlowyError::not_support()) } - async fn create_view_with_default_data( + async fn create_default_view( &self, user_id: i64, + parent_view_id: &str, view_id: &str, _name: &str, _layout: ViewLayout, ) -> Result<(), FlowyError> { - self.0.create_chat(&user_id, view_id).await?; + self + .0 + .create_chat(&user_id, parent_view_id, view_id) + .await?; Ok(()) } @@ -656,3 +662,36 @@ impl FolderOperationHandler for ChatFolderOperation { Err(FlowyError::not_support()) } } + +#[derive(Clone, Debug)] +pub struct FolderQueryServiceImpl { + folder_manager: Weak, +} + +impl FolderQueryServiceImpl { + pub fn new(folder_manager: Weak) -> Self { + Self { folder_manager } + } +} + +#[async_trait] +impl FolderQueryService for FolderQueryServiceImpl { + async fn get_sibling_ids(&self, parent_view_id: &str) -> Vec { + match self.folder_manager.upgrade() { + None => vec![], + Some(folder_manager) => { + if let Ok(parent_view) = folder_manager.get_view(parent_view_id).await { + if parent_view.space_info().is_none() { + if let Ok(views) = folder_manager.get_views_belong_to(parent_view_id).await { + return views + .into_iter() + .map(|child| child.id.clone()) + .collect::>(); + } + } + } + vec![] + }, + } + } +} diff --git a/frontend/rust-lib/flowy-core/src/integrate/trait_impls.rs b/frontend/rust-lib/flowy-core/src/integrate/trait_impls.rs index a4cfb7b0bc..a40aca5ba2 100644 --- a/frontend/rust-lib/flowy-core/src/integrate/trait_impls.rs +++ b/frontend/rust-lib/flowy-core/src/integrate/trait_impls.rs @@ -21,8 +21,9 @@ use collab_integrate::collab_builder::{ CollabCloudPluginProvider, CollabPluginProviderContext, CollabPluginProviderType, }; use flowy_ai_pub::cloud::{ - ChatCloudService, ChatMessage, ChatMessageMetadata, ChatMessageType, LocalAIConfig, + ChatCloudService, ChatMessage, ChatMessageMetadata, ChatMessageType, ChatSettings, LocalAIConfig, MessageCursor, RepeatedChatMessage, StreamAnswer, StreamComplete, SubscriptionPlan, + UpdateChatParams, }; use flowy_database_pub::cloud::{ DatabaseAIService, DatabaseCloudService, DatabaseSnapshot, EncodeCollabByOid, SummaryRowContent, @@ -643,11 +644,12 @@ impl ChatCloudService for ServerProvider { uid: &i64, workspace_id: &str, chat_id: &str, + rag_ids: Vec, ) -> Result<(), FlowyError> { let server = self.get_server(); server? .chat_service() - .create_chat(uid, workspace_id, chat_id) + .create_chat(uid, workspace_id, chat_id, rag_ids) .await } @@ -786,6 +788,31 @@ impl ChatCloudService for ServerProvider { .get_workspace_plan(workspace_id) .await } + + async fn get_chat_settings( + &self, + workspace_id: &str, + chat_id: &str, + ) -> Result { + self + .get_server()? + .chat_service() + .get_chat_settings(workspace_id, chat_id) + .await + } + + async fn update_chat_settings( + &self, + workspace_id: &str, + chat_id: &str, + params: UpdateChatParams, + ) -> Result<(), FlowyError> { + self + .get_server()? + .chat_service() + .update_chat_settings(workspace_id, chat_id, params) + .await + } } #[async_trait] diff --git a/frontend/rust-lib/flowy-core/src/lib.rs b/frontend/rust-lib/flowy-core/src/lib.rs index 182b2b7918..95411d5cca 100644 --- a/frontend/rust-lib/flowy-core/src/lib.rs +++ b/frontend/rust-lib/flowy-core/src/lib.rs @@ -161,11 +161,27 @@ impl AppFlowyCore { collab_builder .set_snapshot_persistence(Arc::new(SnapshotDBImpl(Arc::downgrade(&authenticate_user)))); + let folder_indexer = Arc::new(FolderIndexManagerImpl::new(Some(Arc::downgrade( + &authenticate_user, + )))); + + let folder_manager = FolderDepsResolver::resolve( + Arc::downgrade(&authenticate_user), + collab_builder.clone(), + server_provider.clone(), + folder_indexer.clone(), + store_preference.clone(), + ) + .await; + + let folder_query_service = FolderQueryServiceImpl::new(Arc::downgrade(&folder_manager)); + let ai_manager = ChatDepsResolver::resolve( Arc::downgrade(&authenticate_user), server_provider.clone(), store_preference.clone(), Arc::downgrade(&storage_manager.storage_service), + folder_query_service.clone(), ); let database_manager = DatabaseDepsResolver::resolve( @@ -186,26 +202,6 @@ impl AppFlowyCore { Arc::downgrade(&storage_manager.storage_service), ); - let folder_indexer = Arc::new(FolderIndexManagerImpl::new(Some(Arc::downgrade( - &authenticate_user, - )))); - - let folder_operation_handlers = folder_operation_handlers( - document_manager.clone(), - database_manager.clone(), - ai_manager.clone(), - ); - - let folder_manager = FolderDepsResolver::resolve( - Arc::downgrade(&authenticate_user), - collab_builder.clone(), - server_provider.clone(), - folder_indexer.clone(), - store_preference.clone(), - folder_operation_handlers, - ) - .await; - let user_manager = UserDepsResolver::resolve( authenticate_user.clone(), collab_builder.clone(), @@ -223,6 +219,14 @@ impl AppFlowyCore { ) .await; + // Register the folder operation handlers + register_handlers( + &folder_manager, + document_manager.clone(), + database_manager.clone(), + ai_manager.clone(), + ); + ( user_manager, folder_manager, diff --git a/frontend/rust-lib/flowy-database2/src/entities/filter_entities/date_filter.rs b/frontend/rust-lib/flowy-database2/src/entities/filter_entities/date_filter.rs index 9172c96c32..4b4683eb35 100644 --- a/frontend/rust-lib/flowy-database2/src/entities/filter_entities/date_filter.rs +++ b/frontend/rust-lib/flowy-database2/src/entities/filter_entities/date_filter.rs @@ -29,8 +29,8 @@ pub struct DateFilterContent { pub timestamp: Option, } -impl ToString for DateFilterContent { - fn to_string(&self) -> String { +impl DateFilterContent { + pub fn to_json_string(&self) -> String { serde_json::to_string(self).unwrap() } } diff --git a/frontend/rust-lib/flowy-database2/src/entities/type_option_entities/checkbox_entities.rs b/frontend/rust-lib/flowy-database2/src/entities/type_option_entities/checkbox_entities.rs index 3df44f4ec0..1b284a9927 100644 --- a/frontend/rust-lib/flowy-database2/src/entities/type_option_entities/checkbox_entities.rs +++ b/frontend/rust-lib/flowy-database2/src/entities/type_option_entities/checkbox_entities.rs @@ -1,4 +1,6 @@ +use crate::services::field::{CHECK, UNCHECK}; use collab_database::fields::checkbox_type_option::CheckboxTypeOption; +use collab_database::template::util::ToCellString; use flowy_derive::ProtoBuf; #[derive(Default, Debug, Clone, ProtoBuf)] @@ -13,6 +15,16 @@ impl CheckboxCellDataPB { } } +impl ToCellString for CheckboxCellDataPB { + fn to_cell_string(&self) -> String { + if self.is_checked { + CHECK.to_string() + } else { + UNCHECK.to_string() + } + } +} + #[derive(Debug, Clone, Default, ProtoBuf)] pub struct CheckboxTypeOptionPB { /// unused diff --git a/frontend/rust-lib/flowy-database2/src/event_handler.rs b/frontend/rust-lib/flowy-database2/src/event_handler.rs index 03ec69af5a..0252d72be5 100644 --- a/frontend/rust-lib/flowy-database2/src/event_handler.rs +++ b/frontend/rust-lib/flowy-database2/src/event_handler.rs @@ -6,7 +6,7 @@ use tokio::sync::oneshot; use tracing::{info, instrument}; use flowy_error::{FlowyError, FlowyResult}; -use lib_dispatch::prelude::{af_spawn, data_result_ok, AFPluginData, AFPluginState, DataResult}; +use lib_dispatch::prelude::{data_result_ok, AFPluginData, AFPluginState, DataResult}; use crate::entities::*; use crate::manager::DatabaseManager; @@ -1260,7 +1260,7 @@ pub(crate) async fn summarize_row_handler( let data = data.into_inner(); let row_id = RowId::from(data.row_id); let (tx, rx) = oneshot::channel(); - af_spawn(async move { + tokio::spawn(async move { let result = manager .summarize_row(data.view_id, row_id, data.field_id) .await; @@ -1279,7 +1279,7 @@ pub(crate) async fn translate_row_handler( let data = data.try_into_inner()?; let row_id = RowId::from(data.row_id); let (tx, rx) = oneshot::channel(); - af_spawn(async move { + tokio::spawn(async move { let result = manager .translate_row(data.view_id, row_id, data.field_id) .await; diff --git a/frontend/rust-lib/flowy-database2/src/manager.rs b/frontend/rust-lib/flowy-database2/src/manager.rs index 20be056e00..aa05fd4005 100644 --- a/frontend/rust-lib/flowy-database2/src/manager.rs +++ b/frontend/rust-lib/flowy-database2/src/manager.rs @@ -6,7 +6,7 @@ use collab::core::origin::CollabOrigin; use collab::lock::RwLock; use collab::preclude::Collab; use collab_database::database::{Database, DatabaseData}; -use collab_database::entity::{CreateDatabaseParams, CreateViewParams}; +use collab_database::entity::{CreateDatabaseParams, CreateViewParams, EncodedDatabase}; use collab_database::error::DatabaseError; use collab_database::fields::translate_type_option::TranslateTypeOption; use collab_database::rows::RowId; @@ -31,7 +31,7 @@ use flowy_database_pub::cloud::{ DatabaseAIService, DatabaseCloudService, SummaryRowContent, TranslateItem, TranslateRowContent, }; use flowy_error::{internal_error, FlowyError, FlowyResult}; -use lib_dispatch::prelude::af_spawn; + use lib_infra::box_any::BoxAny; use lib_infra::priority_task::TaskDispatcher; @@ -189,6 +189,17 @@ impl DatabaseManager { }) } + pub async fn encode_database(&self, view_id: &str) -> FlowyResult { + let editor = self.get_database_editor_with_view_id(view_id).await?; + let collabs = editor + .database + .read() + .await + .encode_database_collabs() + .await?; + Ok(collabs) + } + pub async fn get_database_row_ids_with_view_id(&self, view_id: &str) -> FlowyResult> { let database = self.get_database_editor_with_view_id(view_id).await?; Ok(database.get_row_ids().await) @@ -316,7 +327,7 @@ impl DatabaseManager { let weak_workspace_database = Arc::downgrade(&self.workspace_database()?); let weak_removing_editors = Arc::downgrade(&self.removing_editor); - af_spawn(async move { + tokio::spawn(async move { tokio::time::sleep(std::time::Duration::from_secs(120)).await; if let Some(removing_editors) = weak_removing_editors.upgrade() { if removing_editors.lock().await.remove(&database_id).is_some() { @@ -891,6 +902,7 @@ impl DatabaseCollabService for WorkspaceDatabaseCollabServiceImpl { "[Database]: load {} database row from local disk", local_disk_encoded_collab.len() ); + object_ids.retain(|object_id| !local_disk_encoded_collab.contains_key(object_id)); for (k, v) in local_disk_encoded_collab { encoded_collab_by_id.insert(k, v); diff --git a/frontend/rust-lib/flowy-database2/src/notification.rs b/frontend/rust-lib/flowy-database2/src/notification.rs index 71f3c43d96..93e438452f 100644 --- a/frontend/rust-lib/flowy-database2/src/notification.rs +++ b/frontend/rust-lib/flowy-database2/src/notification.rs @@ -90,7 +90,7 @@ impl std::convert::From for DatabaseNotification { } #[tracing::instrument(level = "trace")] -pub fn send_notification(id: &str, ty: DatabaseNotification) -> NotificationBuilder { +pub fn database_notification_builder(id: &str, ty: DatabaseNotification) -> NotificationBuilder { #[cfg(feature = "verbose_log")] tracing::trace!("[Database Notification]: id:{}, ty:{:?}", id, ty); diff --git a/frontend/rust-lib/flowy-database2/src/services/calculations/controller.rs b/frontend/rust-lib/flowy-database2/src/services/calculations/controller.rs index f6df04b171..0ff938cd6e 100644 --- a/frontend/rust-lib/flowy-database2/src/services/calculations/controller.rs +++ b/frontend/rust-lib/flowy-database2/src/services/calculations/controller.rs @@ -86,7 +86,7 @@ impl CalculationsController { let task = Task::new( &self.handler_id, task_id, - TaskContent::Text(task_type.to_string()), + TaskContent::Text(task_type.to_json_string()), qos, ); self.task_scheduler.write().await.add_task(task); @@ -322,7 +322,7 @@ impl CalculationsController { ) -> Vec { let mut updates = vec![]; let update = self - .update_calculation(calculation, &field, field_cells) + .update_calculation(calculation, field, field_cells) .await; if let Some(update) = update { updates.push(CalculationPB::from(&update)); @@ -415,8 +415,8 @@ pub(crate) enum CalculationEvent { FieldDeleted(String), } -impl ToString for CalculationEvent { - fn to_string(&self) -> String { +impl CalculationEvent { + fn to_json_string(&self) -> String { serde_json::to_string(self).unwrap() } } diff --git a/frontend/rust-lib/flowy-database2/src/services/cell/cell_operation.rs b/frontend/rust-lib/flowy-database2/src/services/cell/cell_operation.rs index d4a22c2180..5009c674d0 100644 --- a/frontend/rust-lib/flowy-database2/src/services/cell/cell_operation.rs +++ b/frontend/rust-lib/flowy-database2/src/services/cell/cell_operation.rs @@ -285,7 +285,7 @@ impl<'a> CellBuilder<'a> { } pub fn insert_text_cell(&mut self, field_id: &str, data: String) { - match self.field_maps.get(&field_id.to_owned()) { + match self.field_maps.get(field_id) { None => tracing::warn!("Can't find the text field with id: {}", field_id), Some(field) => { self @@ -296,7 +296,7 @@ impl<'a> CellBuilder<'a> { } pub fn insert_url_cell(&mut self, field_id: &str, data: String) { - match self.field_maps.get(&field_id.to_owned()) { + match self.field_maps.get(field_id) { None => tracing::warn!("Can't find the url field with id: {}", field_id), Some(field) => { self @@ -307,7 +307,7 @@ impl<'a> CellBuilder<'a> { } pub fn insert_number_cell(&mut self, field_id: &str, num: i64) { - match self.field_maps.get(&field_id.to_owned()) { + match self.field_maps.get(field_id) { None => tracing::warn!("Can't find the number field with id: {}", field_id), Some(field) => { self @@ -318,7 +318,7 @@ impl<'a> CellBuilder<'a> { } pub fn insert_checkbox_cell(&mut self, field_id: &str, is_checked: bool) { - match self.field_maps.get(&field_id.to_owned()) { + match self.field_maps.get(field_id) { None => tracing::warn!("Can't find the checkbox field with id: {}", field_id), Some(field) => { self @@ -329,7 +329,7 @@ impl<'a> CellBuilder<'a> { } pub fn insert_date_cell(&mut self, field_id: &str, timestamp: i64, include_time: Option) { - match self.field_maps.get(&field_id.to_owned()) { + match self.field_maps.get(field_id) { None => tracing::warn!("Can't find the date field with id: {}", field_id), Some(field) => { self.cells.insert( @@ -341,7 +341,7 @@ impl<'a> CellBuilder<'a> { } pub fn insert_select_option_cell(&mut self, field_id: &str, option_ids: Vec) { - match self.field_maps.get(&field_id.to_owned()) { + match self.field_maps.get(field_id) { None => tracing::warn!("Can't find the select option field with id: {}", field_id), Some(field) => { self.cells.insert( @@ -356,7 +356,7 @@ impl<'a> CellBuilder<'a> { field_id: &str, new_tasks: Vec, ) { - match self.field_maps.get(&field_id.to_owned()) { + match self.field_maps.get(field_id) { None => tracing::warn!("Can't find the field with id: {}", field_id), Some(field) => { self diff --git a/frontend/rust-lib/flowy-database2/src/services/cell/type_cell_data.rs b/frontend/rust-lib/flowy-database2/src/services/cell/type_cell_data.rs index ccc877059a..09bd13793a 100644 --- a/frontend/rust-lib/flowy-database2/src/services/cell/type_cell_data.rs +++ b/frontend/rust-lib/flowy-database2/src/services/cell/type_cell_data.rs @@ -1,4 +1,5 @@ use bytes::Bytes; +use std::fmt::Display; use flowy_error::{internal_error, FlowyResult}; @@ -64,15 +65,10 @@ impl CellProtobufBlob { // } } -impl ToString for CellProtobufBlob { - fn to_string(&self) -> String { - match String::from_utf8(self.0.to_vec()) { - Ok(s) => s, - Err(e) => { - tracing::error!("DecodedCellData to string failed: {:?}", e); - "".to_string() - }, - } +impl Display for CellProtobufBlob { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let s = String::from_utf8(self.0.to_vec()).unwrap_or_default(); + write!(f, "{}", s) } } diff --git a/frontend/rust-lib/flowy-database2/src/services/database/database_editor.rs b/frontend/rust-lib/flowy-database2/src/services/database/database_editor.rs index aa3b136035..42ef2d8cc0 100644 --- a/frontend/rust-lib/flowy-database2/src/services/database/database_editor.rs +++ b/frontend/rust-lib/flowy-database2/src/services/database/database_editor.rs @@ -1,5 +1,5 @@ use crate::entities::*; -use crate::notification::{send_notification, DatabaseNotification}; +use crate::notification::{database_notification_builder, DatabaseNotification}; use crate::services::calculations::Calculation; use crate::services::cell::{apply_cell_changeset, get_cell_protobuf, CellCache}; use crate::services::database::database_observe::*; @@ -226,7 +226,7 @@ impl DatabaseEditor { let field_type = FieldType::from(field.field_type); setting_content = group_config_pb_to_json_str(data, &field_type)?; let mut group_setting = default_group_setting(&field); - group_setting.content = setting_content.clone(); + group_setting.content.clone_from(&setting_content); database.update_database_view(view_id, |view| { view.set_groups(vec![group_setting.into()]); }); @@ -252,7 +252,7 @@ impl DatabaseEditor { let changes = view_editor.v_delete_group(¶ms.group_id).await?; if !changes.is_empty() { for view in self.database_views.editors().await { - send_notification(&view.view_id, DatabaseNotification::DidUpdateRow) + database_notification_builder(&view.view_id, DatabaseNotification::DidUpdateRow) .payload(changes.clone()) .send(); } @@ -762,7 +762,7 @@ impl DatabaseEditor { updated_fields: vec![], }; - send_notification(¶ms.view_id, DatabaseNotification::DidUpdateFields) + database_notification_builder(¶ms.view_id, DatabaseNotification::DidUpdateFields) .payload(notified_changeset) .send(); } @@ -879,7 +879,7 @@ impl DatabaseEditor { } // Notifies the client that the row meta has been updated. - send_notification(row_id.as_str(), DatabaseNotification::DidUpdateRowMeta) + database_notification_builder(row_id.as_str(), DatabaseNotification::DidUpdateRowMeta) .payload(RowMetaPB::from(row_detail)) .send(); } @@ -1403,7 +1403,7 @@ impl DatabaseEditor { ) -> FlowyResult<()> { let views = self.database.read().await.get_all_database_views_meta(); for view in views { - send_notification(&view.id, DatabaseNotification::DidUpdateFields) + database_notification_builder(&view.id, DatabaseNotification::DidUpdateFields) .payload(changeset.clone()) .send(); } @@ -2219,7 +2219,7 @@ impl DatabaseViewOperation for DatabaseViewOperationImpl { new_field_settings.clone(), ); - send_notification( + database_notification_builder( ¶ms.view_id, DatabaseNotification::DidUpdateFieldSettings, ) @@ -2290,12 +2290,12 @@ fn notify_did_update_database_field(database: &Database, field_id: &str) -> Flow DatabaseFieldChangesetPB::update(&database_id, vec![updated_field.clone()]); for view in views { - send_notification(&view.id, DatabaseNotification::DidUpdateFields) + database_notification_builder(&view.id, DatabaseNotification::DidUpdateFields) .payload(notified_changeset.clone()) .send(); } - send_notification(field_id, DatabaseNotification::DidUpdateField) + database_notification_builder(field_id, DatabaseNotification::DidUpdateField) .payload(updated_field) .send(); } diff --git a/frontend/rust-lib/flowy-database2/src/services/database/database_observe.rs b/frontend/rust-lib/flowy-database2/src/services/database/database_observe.rs index 993c494f1c..081d23f1b3 100644 --- a/frontend/rust-lib/flowy-database2/src/services/database/database_observe.rs +++ b/frontend/rust-lib/flowy-database2/src/services/database/database_observe.rs @@ -1,5 +1,7 @@ use crate::entities::{DatabaseSyncStatePB, DidFetchRowPB, RowsChangePB}; -use crate::notification::{send_notification, DatabaseNotification, DATABASE_OBSERVABLE_SOURCE}; +use crate::notification::{ + database_notification_builder, DatabaseNotification, DATABASE_OBSERVABLE_SOURCE, +}; use crate::services::database::{DatabaseEditor, UpdatedRow}; use crate::services::database_view::DatabaseViewEditor; use collab::lock::RwLock; @@ -11,7 +13,7 @@ use collab_database::views::{DatabaseViewChange, RowOrder}; use dashmap::DashMap; use flowy_notification::{DebounceNotificationSender, NotificationBuilder}; use futures::StreamExt; -use lib_dispatch::prelude::af_spawn; + use std::sync::Arc; use tracing::{error, trace, warn}; @@ -19,13 +21,13 @@ pub(crate) async fn observe_sync_state(database_id: &str, database: &Arc { let reorder_row = ReorderSingleRowPB { @@ -71,19 +74,21 @@ impl DatabaseViewChangedReceiverRunner { old_index: notification.old_index as i32, new_index: notification.new_index as i32, }; - send_notification( + database_notification_builder( ¬ification.view_id, DatabaseNotification::DidReorderSingleRow, ) .payload(reorder_row) .send() }, - DatabaseViewChanged::CalculationValueNotification(notification) => send_notification( - ¬ification.view_id, - DatabaseNotification::DidUpdateCalculation, - ) - .payload(notification) - .send(), + DatabaseViewChanged::CalculationValueNotification(notification) => { + database_notification_builder( + ¬ification.view_id, + DatabaseNotification::DidUpdateCalculation, + ) + .payload(notification) + .send() + }, } }) .await; @@ -91,19 +96,19 @@ impl DatabaseViewChangedReceiverRunner { } pub async fn notify_did_update_group_rows(payload: GroupRowsNotificationPB) { - send_notification(&payload.group_id, DatabaseNotification::DidUpdateGroupRow) + database_notification_builder(&payload.group_id, DatabaseNotification::DidUpdateGroupRow) .payload(payload) .send(); } pub async fn notify_did_update_filter(notification: FilterChangesetNotificationPB) { - send_notification(¬ification.view_id, DatabaseNotification::DidUpdateFilter) + database_notification_builder(¬ification.view_id, DatabaseNotification::DidUpdateFilter) .payload(notification) .send(); } pub async fn notify_did_update_calculation(notification: CalculationChangesetNotificationPB) { - send_notification( + database_notification_builder( ¬ification.view_id, DatabaseNotification::DidUpdateCalculation, ) @@ -113,20 +118,20 @@ pub async fn notify_did_update_calculation(notification: CalculationChangesetNot pub async fn notify_did_update_sort(notification: SortChangesetNotificationPB) { if !notification.is_empty() { - send_notification(¬ification.view_id, DatabaseNotification::DidUpdateSort) + database_notification_builder(¬ification.view_id, DatabaseNotification::DidUpdateSort) .payload(notification) .send(); } } pub(crate) async fn notify_did_update_num_of_groups(view_id: &str, changeset: GroupChangesPB) { - send_notification(view_id, DatabaseNotification::DidUpdateNumOfGroups) + database_notification_builder(view_id, DatabaseNotification::DidUpdateNumOfGroups) .payload(changeset) .send(); } pub(crate) async fn notify_did_update_setting(view_id: &str, setting: DatabaseViewSettingPB) { - send_notification(view_id, DatabaseNotification::DidUpdateSettings) + database_notification_builder(view_id, DatabaseNotification::DidUpdateSettings) .payload(setting) .send(); } diff --git a/frontend/rust-lib/flowy-database2/src/services/database_view/view_editor.rs b/frontend/rust-lib/flowy-database2/src/services/database_view/view_editor.rs index 3ce6dba723..f946a6c881 100644 --- a/frontend/rust-lib/flowy-database2/src/services/database_view/view_editor.rs +++ b/frontend/rust-lib/flowy-database2/src/services/database_view/view_editor.rs @@ -10,7 +10,7 @@ use crate::entities::{ RemoveCalculationChangesetPB, ReorderSortPayloadPB, RowMetaPB, RowsChangePB, SortChangesetNotificationPB, SortPB, UpdateCalculationChangesetPB, UpdateSortPayloadPB, }; -use crate::notification::{send_notification, DatabaseNotification}; +use crate::notification::{database_notification_builder, DatabaseNotification}; use crate::services::calculations::{Calculation, CalculationChangeset, CalculationsController}; use crate::services::cell::{CellBuilder, CellCache}; use crate::services::database::{database_view_setting_pb_from_view, DatabaseRowEvent, UpdatedRow}; @@ -233,7 +233,7 @@ impl DatabaseViewEditor { if rows.pop().is_some() { let update_row = UpdatedRow::new(row_id.as_str()).with_row_meta(row_detail.clone()); let changeset = RowsChangePB::from_update(update_row.into()); - send_notification(&self.view_id, DatabaseNotification::DidUpdateRow) + database_notification_builder(&self.view_id, DatabaseNotification::DidUpdateRow) .payload(changeset) .send(); } @@ -706,7 +706,7 @@ impl DatabaseViewEditor { })) .await .into_iter() - .filter_map(|result| result) + .flatten() .collect(); // Pre-compute cells by field ID only for fields that have calculations @@ -940,7 +940,7 @@ impl DatabaseViewEditor { }; if let Some(payload) = layout_setting_pb { - send_notification(&self.view_id, DatabaseNotification::DidUpdateLayoutSettings) + database_notification_builder(&self.view_id, DatabaseNotification::DidUpdateLayoutSettings) .payload(payload) .send(); } @@ -1060,7 +1060,7 @@ impl DatabaseViewEditor { debug_assert!(!changeset.is_empty()); if !changeset.is_empty() { - send_notification(&changeset.view_id, DatabaseNotification::DidGroupByField) + database_notification_builder(&changeset.view_id, DatabaseNotification::DidGroupByField) .payload(changeset) .send(); } @@ -1196,7 +1196,7 @@ impl DatabaseViewEditor { view_id: self.view_id.clone(), layout: new_layout_type.into(), }; - send_notification(&self.view_id, DatabaseNotification::DidUpdateDatabaseLayout) + database_notification_builder(&self.view_id, DatabaseNotification::DidUpdateDatabaseLayout) .payload(payload) .send(); @@ -1214,7 +1214,7 @@ impl DatabaseViewEditor { } => RowsChangePB::from_move(vec![deleted_row_id.into_inner()], vec![inserted_row.into()]), }; - send_notification(&self.view_id, DatabaseNotification::DidUpdateRow) + database_notification_builder(&self.view_id, DatabaseNotification::DidUpdateRow) .payload(changeset) .send(); } diff --git a/frontend/rust-lib/flowy-database2/src/services/field/field_builder.rs b/frontend/rust-lib/flowy-database2/src/services/field/field_builder.rs index 18c72313c0..323ff2c815 100644 --- a/frontend/rust-lib/flowy-database2/src/services/field/field_builder.rs +++ b/frontend/rust-lib/flowy-database2/src/services/field/field_builder.rs @@ -22,7 +22,7 @@ impl FieldBuilder { } pub fn name(mut self, name: &str) -> Self { - self.field.name = name.to_owned(); + name.clone_into(&mut self.field.name); self } diff --git a/frontend/rust-lib/flowy-database2/src/services/field/type_options/checkbox_type_option/checkbox_tests.rs b/frontend/rust-lib/flowy-database2/src/services/field/type_options/checkbox_type_option/checkbox_tests.rs index 2746658e3a..c0a0eadd86 100644 --- a/frontend/rust-lib/flowy-database2/src/services/field/type_options/checkbox_type_option/checkbox_tests.rs +++ b/frontend/rust-lib/flowy-database2/src/services/field/type_options/checkbox_type_option/checkbox_tests.rs @@ -10,6 +10,7 @@ mod tests { use crate::services::field::type_options::checkbox_type_option::*; use crate::services::field::FieldBuilder; use collab_database::fields::checkbox_type_option::CheckboxTypeOption; + use collab_database::template::util::ToCellString; #[test] fn checkout_box_description_test() { @@ -46,7 +47,7 @@ mod tests { type_option .decode_cell(&CheckboxCellDataPB::from_str(input_str).unwrap().into()) .unwrap() - .to_string(), + .to_cell_string(), expected_str.to_owned() ); } diff --git a/frontend/rust-lib/flowy-database2/src/services/field/type_options/checkbox_type_option/checkbox_type_option.rs b/frontend/rust-lib/flowy-database2/src/services/field/type_options/checkbox_type_option/checkbox_type_option.rs index e41c089e4b..6afe4c4b57 100644 --- a/frontend/rust-lib/flowy-database2/src/services/field/type_options/checkbox_type_option/checkbox_type_option.rs +++ b/frontend/rust-lib/flowy-database2/src/services/field/type_options/checkbox_type_option/checkbox_type_option.rs @@ -4,7 +4,7 @@ use std::str::FromStr; use collab_database::fields::checkbox_type_option::CheckboxTypeOption; use collab_database::fields::Field; use collab_database::rows::Cell; - +use collab_database::template::util::ToCellString; use flowy_error::FlowyResult; use crate::entities::{CheckboxCellDataPB, CheckboxFilterPB, FieldType}; @@ -48,7 +48,7 @@ impl CellDataDecoder for CheckboxTypeOption { } fn stringify_cell_data(&self, cell_data: ::CellData) -> String { - cell_data.to_string() + cell_data.to_cell_string() } } diff --git a/frontend/rust-lib/flowy-database2/src/services/field/type_options/checkbox_type_option/checkbox_type_option_entities.rs b/frontend/rust-lib/flowy-database2/src/services/field/type_options/checkbox_type_option/checkbox_type_option_entities.rs index 8b93382ac3..55ca8dd77f 100644 --- a/frontend/rust-lib/flowy-database2/src/services/field/type_options/checkbox_type_option/checkbox_type_option_entities.rs +++ b/frontend/rust-lib/flowy-database2/src/services/field/type_options/checkbox_type_option/checkbox_type_option_entities.rs @@ -3,7 +3,7 @@ use std::str::FromStr; use bytes::Bytes; use collab::util::AnyMapExt; use collab_database::rows::{new_cell_builder, Cell}; - +use collab_database::template::util::ToCellString; use flowy_error::{FlowyError, FlowyResult}; use crate::entities::{CheckboxCellDataPB, FieldType}; @@ -29,7 +29,7 @@ impl From<&Cell> for CheckboxCellDataPB { impl From for Cell { fn from(data: CheckboxCellDataPB) -> Self { let mut cell = new_cell_builder(FieldType::Checkbox); - cell.insert(CELL_DATA.into(), data.to_string().into()); + cell.insert(CELL_DATA.into(), data.to_cell_string().into()); cell } } @@ -49,16 +49,6 @@ impl FromStr for CheckboxCellDataPB { } } -impl ToString for CheckboxCellDataPB { - fn to_string(&self) -> String { - if self.is_checked { - CHECK.to_string() - } else { - UNCHECK.to_string() - } - } -} - pub struct CheckboxCellDataParser(); impl CellProtobufBlobParser for CheckboxCellDataParser { type Object = CheckboxCellDataPB; diff --git a/frontend/rust-lib/flowy-database2/src/services/field/type_options/media_type_option/media_type_option.rs b/frontend/rust-lib/flowy-database2/src/services/field/type_options/media_type_option/media_type_option.rs index 2d7a1eb010..6fca683358 100644 --- a/frontend/rust-lib/flowy-database2/src/services/field/type_options/media_type_option/media_type_option.rs +++ b/frontend/rust-lib/flowy-database2/src/services/field/type_options/media_type_option/media_type_option.rs @@ -1,8 +1,3 @@ -use collab_database::fields::media_type_option::{MediaCellData, MediaTypeOption}; -use collab_database::{fields::Field, rows::Cell}; -use flowy_error::FlowyResult; -use std::cmp::Ordering; - use crate::{ entities::{FieldType, MediaCellChangeset, MediaCellDataPB, MediaFilterPB}, services::{ @@ -14,6 +9,11 @@ use crate::{ sort::SortCondition, }, }; +use collab_database::fields::media_type_option::{MediaCellData, MediaTypeOption}; +use collab_database::template::util::ToCellString; +use collab_database::{fields::Field, rows::Cell}; +use flowy_error::FlowyResult; +use std::cmp::Ordering; impl TypeOption for MediaTypeOption { type CellData = MediaCellData; @@ -60,7 +60,7 @@ impl CellDataDecoder for MediaTypeOption { } fn stringify_cell_data(&self, cell_data: ::CellData) -> String { - cell_data.to_string() + cell_data.to_cell_string() } } diff --git a/frontend/rust-lib/flowy-database2/src/services/field/type_options/relation_type_option/relation.rs b/frontend/rust-lib/flowy-database2/src/services/field/type_options/relation_type_option/relation.rs index 09f8345717..1ed1d6b1be 100644 --- a/frontend/rust-lib/flowy-database2/src/services/field/type_options/relation_type_option/relation.rs +++ b/frontend/rust-lib/flowy-database2/src/services/field/type_options/relation_type_option/relation.rs @@ -4,6 +4,7 @@ use collab_database::fields::relation_type_option::RelationTypeOption; use collab_database::rows::Cell; use collab_database::template::relation_parse::RelationCellData; +use collab_database::template::util::ToCellString; use flowy_error::FlowyResult; use crate::entities::{RelationCellDataPB, RelationFilterPB}; @@ -57,7 +58,7 @@ impl CellDataChangeset for RelationTypeOption { impl CellDataDecoder for RelationTypeOption { fn stringify_cell_data(&self, cell_data: RelationCellData) -> String { - cell_data.to_string() + cell_data.to_cell_string() } } diff --git a/frontend/rust-lib/flowy-database2/src/services/field/type_options/selection_type_option/multi_select_type_option.rs b/frontend/rust-lib/flowy-database2/src/services/field/type_options/selection_type_option/multi_select_type_option.rs index 3a24740ee7..5f1cbeb5d8 100644 --- a/frontend/rust-lib/flowy-database2/src/services/field/type_options/selection_type_option/multi_select_type_option.rs +++ b/frontend/rust-lib/flowy-database2/src/services/field/type_options/selection_type_option/multi_select_type_option.rs @@ -13,6 +13,7 @@ use collab_database::fields::TypeOptionData; use collab_database::rows::Cell; use flowy_error::FlowyResult; +use collab_database::template::util::ToCellString; use std::cmp::Ordering; impl TypeOption for MultiSelectTypeOption { @@ -80,7 +81,7 @@ impl CellDataChangeset for MultiSelectTypeOption { select_ids.retain(|id| id != &delete_option_id); } - tracing::trace!("Multi-select cell data: {}", select_ids.to_string()); + tracing::trace!("Multi-select cell data: {}", select_ids.to_cell_string()); select_ids }, }; @@ -153,6 +154,7 @@ mod tests { use collab_database::fields::select_type_option::{ MultiSelectTypeOption, SelectOption, SelectOptionIds, SelectTypeOption, }; + use collab_database::template::util::ToCellString; #[test] fn multi_select_insert_multi_option_test() { @@ -202,7 +204,7 @@ mod tests { let changeset = SelectOptionCellChangeset::from_insert_option_id(&google.id); let select_option_ids = multi_select.apply_changeset(changeset, None).unwrap().1; - assert_eq!(select_option_ids.to_string(), google.id); + assert_eq!(select_option_ids.to_cell_string(), google.id); } #[test] diff --git a/frontend/rust-lib/flowy-database2/src/services/field/type_options/selection_type_option/select_type_option.rs b/frontend/rust-lib/flowy-database2/src/services/field/type_options/selection_type_option/select_type_option.rs index b1f9f0c935..dde93d4f50 100644 --- a/frontend/rust-lib/flowy-database2/src/services/field/type_options/selection_type_option/select_type_option.rs +++ b/frontend/rust-lib/flowy-database2/src/services/field/type_options/selection_type_option/select_type_option.rs @@ -13,6 +13,7 @@ use collab_database::fields::select_type_option::{ }; use collab_database::fields::{Field, TypeOptionData}; use collab_database::rows::Cell; +use collab_database::template::util::ToCellString; use flowy_error::{internal_error, ErrorCode, FlowyResult}; use std::str::FromStr; @@ -118,7 +119,7 @@ where Some(SelectOptionIds::from(transformed_ids)) }, FieldType::Checkbox => { - let cell_content = CheckboxCellDataPB::from(cell).to_string(); + let cell_content = CheckboxCellDataPB::from(cell).to_cell_string(); let mut transformed_ids = Vec::new(); let options = self.options(); if let Some(option) = options.iter().find(|option| option.name == cell_content) { diff --git a/frontend/rust-lib/flowy-database2/src/services/field/type_options/text_type_option/text_type_option.rs b/frontend/rust-lib/flowy-database2/src/services/field/type_options/text_type_option/text_type_option.rs index a234dfd2cd..6872d97d95 100644 --- a/frontend/rust-lib/flowy-database2/src/services/field/type_options/text_type_option/text_type_option.rs +++ b/frontend/rust-lib/flowy-database2/src/services/field/type_options/text_type_option/text_type_option.rs @@ -4,7 +4,7 @@ use std::cmp::Ordering; use collab_database::fields::text_type_option::RichTextTypeOption; use collab_database::fields::Field; use collab_database::rows::{new_cell_builder, Cell}; - +use collab_database::template::util::ToCellString; use flowy_error::{FlowyError, FlowyResult}; use crate::entities::{FieldType, TextFilterPB}; @@ -159,8 +159,8 @@ impl std::convert::From for StringCellData { } } -impl ToString for StringCellData { - fn to_string(&self) -> String { +impl ToCellString for StringCellData { + fn to_cell_string(&self) -> String { self.0.clone() } } diff --git a/frontend/rust-lib/flowy-database2/src/services/field/type_options/type_option.rs b/frontend/rust-lib/flowy-database2/src/services/field/type_options/type_option.rs index b129f52f3c..86a187f3b6 100644 --- a/frontend/rust-lib/flowy-database2/src/services/field/type_options/type_option.rs +++ b/frontend/rust-lib/flowy-database2/src/services/field/type_options/type_option.rs @@ -24,6 +24,7 @@ use collab_database::fields::translate_type_option::TranslateTypeOption; use collab_database::fields::url_type_option::URLTypeOption; use collab_database::fields::{TypeOptionCellReader, TypeOptionData}; use collab_database::rows::Cell; +use collab_database::template::util::ToCellString; pub use collab_database::template::util::TypeOptionCellData; use protobuf::ProtobufError; use std::cmp::Ordering; @@ -42,7 +43,7 @@ pub trait TypeOption: From + Into + TypeOptionCe /// type CellData: for<'a> From<&'a Cell> + TypeOptionCellData - + ToString + + ToCellString + Default + Send + Sync diff --git a/frontend/rust-lib/flowy-database2/src/services/field/type_options/util.rs b/frontend/rust-lib/flowy-database2/src/services/field/type_options/util.rs index 6bf03b127a..ec06f84e61 100644 --- a/frontend/rust-lib/flowy-database2/src/services/field/type_options/util.rs +++ b/frontend/rust-lib/flowy-database2/src/services/field/type_options/util.rs @@ -1,5 +1,6 @@ use bytes::Bytes; use protobuf::ProtobufError; +use std::fmt::Display; #[derive(Default, Debug, Clone)] pub struct ProtobufStr(pub String); @@ -23,9 +24,9 @@ impl std::convert::From for ProtobufStr { } } -impl ToString for ProtobufStr { - fn to_string(&self) -> String { - self.0.clone() +impl Display for ProtobufStr { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0.clone()) } } diff --git a/frontend/rust-lib/flowy-database2/src/services/filter/controller.rs b/frontend/rust-lib/flowy-database2/src/services/filter/controller.rs index 13ebd615ae..0578298365 100644 --- a/frontend/rust-lib/flowy-database2/src/services/filter/controller.rs +++ b/frontend/rust-lib/flowy-database2/src/services/filter/controller.rs @@ -131,7 +131,7 @@ impl FilterController { let task = Task::new( &self.handler_id, task_id, - TaskContent::Text(task_type.to_string()), + TaskContent::Text(task_type.to_json_string()), qos, ); self.task_scheduler.write().await.add_task(task); @@ -547,8 +547,8 @@ enum FilterEvent { RowDidChanged(RowId), } -impl ToString for FilterEvent { - fn to_string(&self) -> String { +impl FilterEvent { + fn to_json_string(&self) -> String { serde_json::to_string(self).unwrap() } } diff --git a/frontend/rust-lib/flowy-database2/src/services/filter/entities.rs b/frontend/rust-lib/flowy-database2/src/services/filter/entities.rs index 070dd5b889..81aacd3f65 100644 --- a/frontend/rust-lib/flowy-database2/src/services/filter/entities.rs +++ b/frontend/rust-lib/flowy-database2/src/services/filter/entities.rs @@ -8,6 +8,7 @@ use collab::util::AnyMapExt; use collab_database::database::gen_database_filter_id; use collab_database::fields::select_type_option::SelectOptionIds; use collab_database::rows::RowId; +use collab_database::template::util::ToCellString; use collab_database::views::{FilterMap, FilterMapBuilder}; use flowy_error::{FlowyError, FlowyResult}; use lib_infra::box_any::BoxAny; @@ -365,12 +366,12 @@ impl<'a> From<&'a Filter> for FilterMap { end: filter.end, timestamp: filter.timestamp, } - .to_string(); + .to_json_string(); (filter.condition as u8, content) }, FieldType::SingleSelect | FieldType::MultiSelect => { let filter = condition_and_content.cloned::()?; - let content = SelectOptionIds::from(filter.option_ids).to_string(); + let content = SelectOptionIds::from(filter.option_ids).to_cell_string(); (filter.condition as u8, content) }, FieldType::Checkbox => { diff --git a/frontend/rust-lib/flowy-database2/src/services/group/action.rs b/frontend/rust-lib/flowy-database2/src/services/group/action.rs index cc77e30fd7..7b876e0ddb 100644 --- a/frontend/rust-lib/flowy-database2/src/services/group/action.rs +++ b/frontend/rust-lib/flowy-database2/src/services/group/action.rs @@ -4,7 +4,7 @@ use collab_database::rows::{Cell, Cells, Row, RowId}; use flowy_error::FlowyResult; -use crate::entities::{GroupChangesPB, GroupPB, GroupRowsNotificationPB, InsertedGroupPB}; +use crate::entities::{GroupPB, GroupRowsNotificationPB, InsertedGroupPB}; use crate::services::field::TypeOption; use crate::services::group::{GroupChangeset, GroupData, MoveGroupRowContext}; @@ -166,11 +166,6 @@ pub trait GroupController: Send + Sync { /// * `context`: information about the row being moved and its destination fn move_group_row(&mut self, context: MoveGroupRowContext) -> FlowyResult; - /// Updates the groups after a field change. (currently never does anything) - /// - /// * `field`: new changeset - fn did_update_group_field(&mut self, field: &Field) -> FlowyResult>; - /// Delete a group from the group configuration. /// /// Return a list of deleted row ids and/or a new `TypeOptionData` if diff --git a/frontend/rust-lib/flowy-database2/src/services/group/configuration.rs b/frontend/rust-lib/flowy-database2/src/services/group/configuration.rs index ba949de745..97636920a7 100644 --- a/frontend/rust-lib/flowy-database2/src/services/group/configuration.rs +++ b/frontend/rust-lib/flowy-database2/src/services/group/configuration.rs @@ -10,7 +10,6 @@ use serde::Serialize; use tracing::event; use flowy_error::{FlowyError, FlowyResult}; -use lib_dispatch::prelude::af_spawn; use crate::entities::{GroupChangesPB, GroupPB, InsertedGroupPB}; use crate::services::field::RowSingleCellData; @@ -364,7 +363,7 @@ where let configuration = (*self.setting).clone(); let delegate = self.delegate.clone(); let view_id = self.view_id.clone(); - af_spawn(async move { + tokio::spawn(async move { match delegate.save_configuration(&view_id, configuration).await { Ok(_) => {}, Err(e) => { diff --git a/frontend/rust-lib/flowy-database2/src/services/group/controller.rs b/frontend/rust-lib/flowy-database2/src/services/group/controller.rs index 09e93ef725..eed12e492a 100644 --- a/frontend/rust-lib/flowy-database2/src/services/group/controller.rs +++ b/frontend/rust-lib/flowy-database2/src/services/group/controller.rs @@ -10,8 +10,7 @@ use serde::Serialize; use tracing::trace; use crate::entities::{ - FieldType, GroupChangesPB, GroupPB, GroupRowsNotificationPB, InsertedGroupPB, InsertedRowPB, - RowMetaPB, + FieldType, GroupPB, GroupRowsNotificationPB, InsertedGroupPB, InsertedRowPB, RowMetaPB, }; use crate::services::cell::{get_cell_protobuf, CellProtobufBlobParser}; use crate::services::field::{default_type_option_data_from_type, TypeOption, TypeOptionCellData}; @@ -382,10 +381,6 @@ where Ok(result) } - fn did_update_group_field(&mut self, _field: &Field) -> FlowyResult> { - Ok(None) - } - async fn delete_group( &mut self, group_id: &str, diff --git a/frontend/rust-lib/flowy-database2/src/services/group/controller_impls/default_controller.rs b/frontend/rust-lib/flowy-database2/src/services/group/controller_impls/default_controller.rs index bf142b2057..0ce7dd036c 100644 --- a/frontend/rust-lib/flowy-database2/src/services/group/controller_impls/default_controller.rs +++ b/frontend/rust-lib/flowy-database2/src/services/group/controller_impls/default_controller.rs @@ -6,9 +6,7 @@ use collab_database::rows::{Cells, Row, RowId}; use flowy_error::FlowyResult; use tracing::trace; -use crate::entities::{ - GroupChangesPB, GroupPB, GroupRowsNotificationPB, InsertedGroupPB, InsertedRowPB, -}; +use crate::entities::{GroupPB, GroupRowsNotificationPB, InsertedGroupPB, InsertedRowPB}; use crate::services::group::action::{ DidMoveGroupRowResult, DidUpdateGroupRowResult, GroupController, }; @@ -140,10 +138,6 @@ impl GroupController for DefaultGroupController { }) } - fn did_update_group_field(&mut self, _field: &Field) -> FlowyResult> { - Ok(None) - } - async fn delete_group( &mut self, _group_id: &str, diff --git a/frontend/rust-lib/flowy-database2/src/services/sort/controller.rs b/frontend/rust-lib/flowy-database2/src/services/sort/controller.rs index b5bdbdc460..8dc3393a0f 100644 --- a/frontend/rust-lib/flowy-database2/src/services/sort/controller.rs +++ b/frontend/rust-lib/flowy-database2/src/services/sort/controller.rs @@ -177,7 +177,7 @@ impl SortController { let task = Task::new( &self.handler_id, task_id, - TaskContent::Text(task_type.to_string()), + TaskContent::Text(task_type.to_json_string()), qos, ); self.task_scheduler.write().await.add_task(task); @@ -351,8 +351,8 @@ enum SortEvent { DeleteAllSorts, } -impl ToString for SortEvent { - fn to_string(&self) -> String { +impl SortEvent { + fn to_json_string(&self) -> String { serde_json::to_string(self).unwrap() } } diff --git a/frontend/rust-lib/flowy-database2/tests/database/filter_test/script.rs b/frontend/rust-lib/flowy-database2/tests/database/filter_test/script.rs index cd09a45ede..6592c04305 100644 --- a/frontend/rust-lib/flowy-database2/tests/database/filter_test/script.rs +++ b/frontend/rust-lib/flowy-database2/tests/database/filter_test/script.rs @@ -11,7 +11,6 @@ use flowy_database2::entities::{ DatabaseViewSettingPB, FieldType, FilterPB, FilterType, TextFilterConditionPB, TextFilterPB, }; use flowy_database2::services::database_view::DatabaseViewChanged; -use lib_dispatch::prelude::af_spawn; use crate::database::database_editor::DatabaseEditorTest; @@ -265,7 +264,7 @@ impl DatabaseFilterTest { } let change = change.unwrap(); let mut receiver = self.recv.take().unwrap(); - af_spawn(async move { + tokio::spawn(async move { match tokio::time::timeout(Duration::from_secs(2), receiver.recv()).await { Ok(changed) => { if let DatabaseViewChanged::FilterNotification(notification) = changed.unwrap() { diff --git a/frontend/rust-lib/flowy-database2/tests/database/pre_fill_cell_test/pre_fill_row_with_payload_test.rs b/frontend/rust-lib/flowy-database2/tests/database/pre_fill_cell_test/pre_fill_row_with_payload_test.rs index 8c5bea419d..8007051037 100644 --- a/frontend/rust-lib/flowy-database2/tests/database/pre_fill_cell_test/pre_fill_row_with_payload_test.rs +++ b/frontend/rust-lib/flowy-database2/tests/database/pre_fill_cell_test/pre_fill_row_with_payload_test.rs @@ -1,6 +1,7 @@ use crate::database::pre_fill_cell_test::script::DatabasePreFillRowCellTest; use collab_database::fields::date_type_option::DateCellData; use collab_database::fields::select_type_option::SELECTION_IDS_SEPARATOR; +use collab_database::template::util::ToCellString; use flowy_database2::entities::{CreateRowPayloadPB, FieldType}; use std::collections::HashMap; @@ -182,7 +183,7 @@ async fn row_data_payload_with_invalid_date_time_test() { timestamp: Some(1710510086), ..Default::default() } - .to_string(); + .to_cell_string(); test .create_row_with_payload(CreateRowPayloadPB { diff --git a/frontend/rust-lib/flowy-document/build.rs b/frontend/rust-lib/flowy-document/build.rs index 9fdde3edf6..e015eb2580 100644 --- a/frontend/rust-lib/flowy-document/build.rs +++ b/frontend/rust-lib/flowy-document/build.rs @@ -20,21 +20,4 @@ fn main() { flowy_codegen::Project::TauriApp, ); } - - #[cfg(feature = "web_ts")] - { - flowy_codegen::ts_event::gen( - "document", - flowy_codegen::Project::Web { - relative_path: "../../".to_string(), - }, - ); - flowy_codegen::protobuf_file::ts_gen( - env!("CARGO_PKG_NAME"), - "document", - flowy_codegen::Project::Web { - relative_path: "../../".to_string(), - }, - ); - } } diff --git a/frontend/rust-lib/flowy-document/src/document.rs b/frontend/rust-lib/flowy-document/src/document.rs index 7e1c1d143b..da0007fbd9 100644 --- a/frontend/rust-lib/flowy-document/src/document.rs +++ b/frontend/rust-lib/flowy-document/src/document.rs @@ -1,11 +1,10 @@ use crate::entities::{ DocEventPB, DocumentAwarenessStatesPB, DocumentSnapshotStatePB, DocumentSyncStatePB, }; -use crate::notification::{send_notification, DocumentNotification}; +use crate::notification::{document_notification_builder, DocumentNotification}; use collab::preclude::Collab; use collab_document::document::Document; use futures::StreamExt; -use lib_dispatch::prelude::af_spawn; pub fn subscribe_document_changed(doc_id: &str, document: &mut Document) { let doc_id_clone_for_block_changed = doc_id.to_owned(); @@ -14,7 +13,7 @@ pub fn subscribe_document_changed(doc_id: &str, document: &mut Document) { tracing::trace!("subscribe_document_changed: {:?}", events); // send notification to the client. - send_notification( + document_notification_builder( &doc_id_clone_for_block_changed, DocumentNotification::DidReceiveUpdate, ) @@ -26,7 +25,7 @@ pub fn subscribe_document_changed(doc_id: &str, document: &mut Document) { document.subscribe_awareness_state("key", move |events| { #[cfg(feature = "verbose_log")] tracing::trace!("subscribe_awareness_state: {:?}", events); - send_notification( + document_notification_builder( &doc_id_clone_for_awareness_state, DocumentNotification::DidUpdateDocumentAwarenessState, ) @@ -38,11 +37,11 @@ pub fn subscribe_document_changed(doc_id: &str, document: &mut Document) { pub fn subscribe_document_snapshot_state(collab: &Collab) { let document_id = collab.object_id().to_string(); let mut snapshot_state = collab.subscribe_snapshot_state(); - af_spawn(async move { + tokio::spawn(async move { while let Some(snapshot_state) = snapshot_state.next().await { if let Some(new_snapshot_id) = snapshot_state.snapshot_id() { tracing::debug!("Did create document remote snapshot: {}", new_snapshot_id); - send_notification( + document_notification_builder( &document_id, DocumentNotification::DidUpdateDocumentSnapshotState, ) @@ -56,9 +55,9 @@ pub fn subscribe_document_snapshot_state(collab: &Collab) { pub fn subscribe_document_sync_state(collab: &Collab) { let document_id = collab.object_id().to_string(); let mut sync_state_stream = collab.subscribe_sync_state(); - af_spawn(async move { + tokio::spawn(async move { while let Some(sync_state) = sync_state_stream.next().await { - send_notification( + document_notification_builder( &document_id, DocumentNotification::DidUpdateDocumentSyncState, ) diff --git a/frontend/rust-lib/flowy-document/src/manager.rs b/frontend/rust-lib/flowy-document/src/manager.rs index dc07870877..517cb47fea 100644 --- a/frontend/rust-lib/flowy-document/src/manager.rs +++ b/frontend/rust-lib/flowy-document/src/manager.rs @@ -29,7 +29,6 @@ use collab_integrate::collab_builder::{ use flowy_document_pub::cloud::DocumentCloudService; use flowy_error::{internal_error, ErrorCode, FlowyError, FlowyResult}; use flowy_storage_pub::storage::{CreatedUpload, StorageService}; -use lib_dispatch::prelude::af_spawn; use crate::entities::UpdateDocumentAwarenessStatePB; use crate::entities::{ @@ -328,7 +327,7 @@ impl DocumentManager { self.removing_documents.insert(doc_id, document); let weak_removing_documents = Arc::downgrade(&self.removing_documents); - af_spawn(async move { + tokio::spawn(async move { tokio::time::sleep(std::time::Duration::from_secs(120)).await; if let Some(removing_documents) = weak_removing_documents.upgrade() { if removing_documents.remove(&clone_doc_id).is_some() { diff --git a/frontend/rust-lib/flowy-document/src/notification.rs b/frontend/rust-lib/flowy-document/src/notification.rs index 9909971667..5d843014e7 100644 --- a/frontend/rust-lib/flowy-document/src/notification.rs +++ b/frontend/rust-lib/flowy-document/src/notification.rs @@ -32,6 +32,9 @@ impl std::convert::From for DocumentNotification { } #[tracing::instrument(level = "trace")] -pub(crate) fn send_notification(id: &str, ty: DocumentNotification) -> NotificationBuilder { +pub(crate) fn document_notification_builder( + id: &str, + ty: DocumentNotification, +) -> NotificationBuilder { NotificationBuilder::new(id, ty, DOCUMENT_OBSERVABLE_SOURCE) } diff --git a/frontend/rust-lib/flowy-document/tests/parser/json/parser_test.rs b/frontend/rust-lib/flowy-document/tests/parser/json/parser_test.rs index 096b4fd5ce..f60c19e945 100644 --- a/frontend/rust-lib/flowy-document/tests/parser/json/parser_test.rs +++ b/frontend/rust-lib/flowy-document/tests/parser/json/parser_test.rs @@ -112,7 +112,7 @@ async fn parse_readme_test() { let data = json_str_to_hashmap(&block.data).ok(); assert!(data.is_some()); if let Some(data) = data { - assert!(data.get("delta").is_none()); + assert!(!data.contains_key("delta")); } if let Some(external_id) = &block.external_id { diff --git a/frontend/rust-lib/flowy-error/build.rs b/frontend/rust-lib/flowy-error/build.rs index c3081d7488..81f0556ae3 100644 --- a/frontend/rust-lib/flowy-error/build.rs +++ b/frontend/rust-lib/flowy-error/build.rs @@ -15,13 +15,4 @@ fn main() { flowy_codegen::Project::TauriApp, ); } - - #[cfg(feature = "web_ts")] - flowy_codegen::protobuf_file::ts_gen( - env!("CARGO_PKG_NAME"), - "error", - flowy_codegen::Project::Web { - relative_path: "../../".to_string(), - }, - ); } diff --git a/frontend/rust-lib/flowy-folder-pub/src/folder_builder.rs b/frontend/rust-lib/flowy-folder-pub/src/folder_builder.rs deleted file mode 100644 index 20604b0018..0000000000 --- a/frontend/rust-lib/flowy-folder-pub/src/folder_builder.rs +++ /dev/null @@ -1,321 +0,0 @@ -use std::future::Future; - -use crate::cloud::gen_view_id; -use collab_folder::{RepeatedViewIdentifier, View, ViewIcon, ViewIdentifier, ViewLayout}; -use lib_infra::util::timestamp; - -/// A builder for creating views, each able to have children views of -/// their own. -pub struct NestedViewBuilder { - pub uid: i64, - pub parent_view_id: String, - pub views: Vec, -} - -impl NestedViewBuilder { - pub fn new(parent_view_id: String, uid: i64) -> Self { - Self { - uid, - parent_view_id, - views: vec![], - } - } - - pub async fn with_view_builder(&mut self, view_builder: F) - where - F: Fn(ViewBuilder) -> O, - O: Future, - { - let builder = ViewBuilder::new(self.uid, self.parent_view_id.clone()); - self.views.push(view_builder(builder).await); - } - - pub fn build(&mut self) -> Vec { - std::mem::take(&mut self.views) - } -} - -/// A builder for creating a view. -/// The default layout of the view is [ViewLayout::Document] -pub struct ViewBuilder { - uid: i64, - parent_view_id: String, - view_id: String, - name: String, - desc: String, - layout: ViewLayout, - child_views: Vec, - is_favorite: bool, - icon: Option, -} - -impl ViewBuilder { - pub fn new(uid: i64, parent_view_id: String) -> Self { - Self { - uid, - parent_view_id, - view_id: gen_view_id().to_string(), - name: Default::default(), - desc: Default::default(), - layout: ViewLayout::Document, - child_views: vec![], - is_favorite: false, - - icon: None, - } - } - - pub fn view_id(&self) -> &str { - &self.view_id - } - - pub fn with_view_id(mut self, view_id: T) -> Self { - self.view_id = view_id.to_string(); - self - } - - pub fn with_layout(mut self, layout: ViewLayout) -> Self { - self.layout = layout; - self - } - - pub fn with_name(mut self, name: T) -> Self { - self.name = name.to_string(); - self - } - - pub fn with_desc(mut self, desc: &str) -> Self { - self.desc = desc.to_string(); - self - } - - pub fn with_icon(mut self, icon: &str) -> Self { - self.icon = Some(ViewIcon { - ty: collab_folder::IconType::Emoji, - value: icon.to_string(), - }); - self - } - - pub fn with_view(mut self, view: ParentChildViews) -> Self { - self.child_views.push(view); - self - } - - pub fn with_child_views(mut self, mut views: Vec) -> Self { - self.child_views.append(&mut views); - self - } - - /// Create a child view for the current view. - /// The view created by this builder will be the next level view of the current view. - pub async fn with_child_view_builder(mut self, child_view_builder: F) -> Self - where - F: Fn(ViewBuilder) -> O, - O: Future, - { - let builder = ViewBuilder::new(self.uid, self.view_id.clone()); - self.child_views.push(child_view_builder(builder).await); - self - } - - pub fn build(self) -> ParentChildViews { - let view = View { - id: self.view_id, - parent_view_id: self.parent_view_id, - name: self.name, - desc: self.desc, - created_at: timestamp(), - is_favorite: self.is_favorite, - layout: self.layout, - icon: self.icon, - created_by: Some(self.uid), - last_edited_time: 0, - children: RepeatedViewIdentifier::new( - self - .child_views - .iter() - .map(|v| ViewIdentifier { - id: v.parent_view.id.clone(), - }) - .collect(), - ), - last_edited_by: Some(self.uid), - extra: None, - }; - ParentChildViews { - parent_view: view, - child_views: self.child_views, - } - } -} - -#[derive(Clone)] -pub struct ParentChildViews { - pub parent_view: View, - pub child_views: Vec, -} - -impl ParentChildViews { - pub fn new(view: View) -> Self { - Self { - parent_view: view, - child_views: vec![], - } - } - - pub fn flatten(self) -> Vec { - FlattedViews::flatten_views(vec![self]) - } -} - -pub struct FlattedViews; - -impl FlattedViews { - pub fn flatten_views(views: Vec) -> Vec { - let mut result = vec![]; - for view in views { - result.push(view.parent_view); - result.append(&mut Self::flatten_views(view.child_views)); - } - result - } -} - -#[cfg(test)] -mod tests { - use crate::folder_builder::{FlattedViews, NestedViewBuilder}; - - #[tokio::test] - async fn create_first_level_views_test() { - let workspace_id = "w1".to_string(); - let mut builder = NestedViewBuilder::new(workspace_id, 1); - builder - .with_view_builder(|view_builder| async { view_builder.with_name("1").build() }) - .await; - builder - .with_view_builder(|view_builder| async { view_builder.with_name("2").build() }) - .await; - builder - .with_view_builder(|view_builder| async { view_builder.with_name("3").build() }) - .await; - let workspace_views = builder.build(); - assert_eq!(workspace_views.len(), 3); - - let views = FlattedViews::flatten_views(workspace_views); - assert_eq!(views.len(), 3); - } - - #[tokio::test] - async fn create_view_with_child_views_test() { - let workspace_id = "w1".to_string(); - let mut builder = NestedViewBuilder::new(workspace_id, 1); - builder - .with_view_builder(|view_builder| async { - view_builder - .with_name("1") - .with_child_view_builder(|child_view_builder| async { - child_view_builder.with_name("1_1").build() - }) - .await - .with_child_view_builder(|child_view_builder| async { - child_view_builder.with_name("1_2").build() - }) - .await - .build() - }) - .await; - builder - .with_view_builder(|view_builder| async { - view_builder - .with_name("2") - .with_child_view_builder(|child_view_builder| async { - child_view_builder.with_name("2_1").build() - }) - .await - .build() - }) - .await; - let workspace_views = builder.build(); - assert_eq!(workspace_views.len(), 2); - - assert_eq!(workspace_views[0].parent_view.name, "1"); - assert_eq!(workspace_views[0].child_views.len(), 2); - assert_eq!(workspace_views[0].child_views[0].parent_view.name, "1_1"); - assert_eq!(workspace_views[0].child_views[1].parent_view.name, "1_2"); - assert_eq!(workspace_views[1].child_views.len(), 1); - assert_eq!(workspace_views[1].child_views[0].parent_view.name, "2_1"); - - let views = FlattedViews::flatten_views(workspace_views); - assert_eq!(views.len(), 5); - } - - #[tokio::test] - async fn create_three_level_view_test() { - let workspace_id = "w1".to_string(); - let mut builder = NestedViewBuilder::new(workspace_id, 1); - builder - .with_view_builder(|view_builder| async { - view_builder - .with_name("1") - .with_child_view_builder(|child_view_builder| async { - child_view_builder - .with_name("1_1") - .with_child_view_builder(|b| async { b.with_name("1_1_1").build() }) - .await - .with_child_view_builder(|b| async { b.with_name("1_1_2").build() }) - .await - .build() - }) - .await - .with_child_view_builder(|child_view_builder| async { - child_view_builder - .with_name("1_2") - .with_child_view_builder(|b| async { b.with_name("1_2_1").build() }) - .await - .with_child_view_builder(|b| async { b.with_name("1_2_2").build() }) - .await - .build() - }) - .await - .build() - }) - .await; - let workspace_views = builder.build(); - assert_eq!(workspace_views.len(), 1); - - assert_eq!(workspace_views[0].parent_view.name, "1"); - assert_eq!(workspace_views[0].child_views.len(), 2); - assert_eq!(workspace_views[0].child_views[0].parent_view.name, "1_1"); - assert_eq!(workspace_views[0].child_views[1].parent_view.name, "1_2"); - - assert_eq!( - workspace_views[0].child_views[0].child_views[0] - .parent_view - .name, - "1_1_1" - ); - assert_eq!( - workspace_views[0].child_views[0].child_views[1] - .parent_view - .name, - "1_1_2" - ); - - assert_eq!( - workspace_views[0].child_views[1].child_views[0] - .parent_view - .name, - "1_2_1" - ); - assert_eq!( - workspace_views[0].child_views[1].child_views[1] - .parent_view - .name, - "1_2_2" - ); - - let views = FlattedViews::flatten_views(workspace_views); - assert_eq!(views.len(), 7); - } -} diff --git a/frontend/rust-lib/flowy-folder-pub/src/lib.rs b/frontend/rust-lib/flowy-folder-pub/src/lib.rs index ee0ade69c4..38d61c8e9c 100644 --- a/frontend/rust-lib/flowy-folder-pub/src/lib.rs +++ b/frontend/rust-lib/flowy-folder-pub/src/lib.rs @@ -1,2 +1,3 @@ pub mod cloud; pub mod entities; +pub mod query; diff --git a/frontend/rust-lib/flowy-folder-pub/src/query.rs b/frontend/rust-lib/flowy-folder-pub/src/query.rs new file mode 100644 index 0000000000..5ef69c8384 --- /dev/null +++ b/frontend/rust-lib/flowy-folder-pub/src/query.rs @@ -0,0 +1,6 @@ +use lib_infra::async_trait::async_trait; + +#[async_trait] +pub trait FolderQueryService: Send + Sync + 'static { + async fn get_sibling_ids(&self, parent_view_id: &str) -> Vec; +} diff --git a/frontend/rust-lib/flowy-folder/Cargo.toml b/frontend/rust-lib/flowy-folder/Cargo.toml index b6878ee9a1..30d9850e55 100644 --- a/frontend/rust-lib/flowy-folder/Cargo.toml +++ b/frontend/rust-lib/flowy-folder/Cargo.toml @@ -21,8 +21,8 @@ arc-swap.workspace = true unicode-segmentation = "1.10" tracing.workspace = true flowy-error = { path = "../flowy-error", features = [ - "impl_from_dispatch_error", - "impl_from_collab_folder", + "impl_from_dispatch_error", + "impl_from_collab_folder", ] } lib-dispatch = { workspace = true } bytes.workspace = true @@ -42,6 +42,7 @@ async-trait.workspace = true client-api = { workspace = true } regex = "1.9.5" futures = "0.3.30" +dashmap.workspace = true [build-dependencies] diff --git a/frontend/rust-lib/flowy-folder/build.rs b/frontend/rust-lib/flowy-folder/build.rs index fac4cc65ae..e9230d3d6d 100644 --- a/frontend/rust-lib/flowy-folder/build.rs +++ b/frontend/rust-lib/flowy-folder/build.rs @@ -20,21 +20,4 @@ fn main() { flowy_codegen::Project::TauriApp, ); } - - #[cfg(feature = "web_ts")] - { - flowy_codegen::ts_event::gen( - "folder", - flowy_codegen::Project::Web { - relative_path: "../../".to_string(), - }, - ); - flowy_codegen::protobuf_file::ts_gen( - env!("CARGO_PKG_NAME"), - "folder", - flowy_codegen::Project::Web { - relative_path: "../../".to_string(), - }, - ); - } } diff --git a/frontend/rust-lib/flowy-folder/src/entities/import.rs b/frontend/rust-lib/flowy-folder/src/entities/import.rs index 6a14b922aa..4189dfaa6d 100644 --- a/frontend/rust-lib/flowy-folder/src/entities/import.rs +++ b/frontend/rust-lib/flowy-folder/src/entities/import.rs @@ -1,6 +1,6 @@ use crate::entities::parser::empty_str::NotEmptyStr; use crate::entities::ViewLayoutPB; -use crate::share::{ImportParams, ImportType, ImportValue}; +use crate::share::{ImportData, ImportItem, ImportParams, ImportType}; use flowy_derive::{ProtoBuf, ProtoBuf_Enum}; use flowy_error::FlowyError; use lib_infra::validator_fn::required_not_empty_str; @@ -34,7 +34,7 @@ impl Default for ImportTypePB { } #[derive(Clone, Debug, ProtoBuf, Default)] -pub struct ImportValuePayloadPB { +pub struct ImportItemPayloadPB { // the name of the import page #[pb(index = 1)] pub name: String, @@ -65,7 +65,7 @@ pub struct ImportPayloadPB { pub parent_view_id: String, #[pb(index = 2)] - pub values: Vec, + pub items: Vec, } impl TryInto for ImportPayloadPB { @@ -76,38 +76,39 @@ impl TryInto for ImportPayloadPB { .map_err(|_| FlowyError::invalid_view_id())? .0; - let mut values = Vec::new(); + let items = self + .items + .into_iter() + .map(|item| { + let name = if item.name.is_empty() { + "Untitled".to_string() + } else { + item.name + }; - for value in self.values { - let name = if value.name.is_empty() { - "Untitled".to_string() - } else { - value.name - }; + let data = match (item.file_path, item.data) { + (Some(file_path), None) => ImportData::FilePath { file_path }, + (None, Some(bytes)) => ImportData::Bytes { bytes }, + (None, None) => { + return Err(FlowyError::invalid_data().with_context("The import data is empty")); + }, + (Some(_), Some(_)) => { + return Err(FlowyError::invalid_data().with_context("The import data is ambiguous")); + }, + }; - let file_path = match value.file_path { - None => None, - Some(file_path) => Some( - NotEmptyStr::parse(file_path) - .map_err(|_| FlowyError::invalid_data().with_context("The import file path is empty"))? - .0, - ), - }; - - let params = ImportValue { - name, - data: value.data, - file_path, - view_layout: value.view_layout.into(), - import_type: value.import_type.into(), - }; - - values.push(params); - } + Ok(ImportItem { + name, + data, + view_layout: item.view_layout.into(), + import_type: item.import_type.into(), + }) + }) + .collect::, _>>()?; Ok(ImportParams { parent_view_id, - values, + items, }) } } diff --git a/frontend/rust-lib/flowy-folder/src/lib.rs b/frontend/rust-lib/flowy-folder/src/lib.rs index 6c3305c681..d08b94dbfa 100644 --- a/frontend/rust-lib/flowy-folder/src/lib.rs +++ b/frontend/rust-lib/flowy-folder/src/lib.rs @@ -11,8 +11,6 @@ pub mod view_operation; mod manager_init; mod manager_observer; -#[cfg(debug_assertions)] -pub mod manager_test_util; pub mod publish_util; pub mod share; diff --git a/frontend/rust-lib/flowy-folder/src/manager.rs b/frontend/rust-lib/flowy-folder/src/manager.rs index 5cec8da50c..dcf8a7fdd8 100644 --- a/frontend/rust-lib/flowy-folder/src/manager.rs +++ b/frontend/rust-lib/flowy-folder/src/manager.rs @@ -10,13 +10,13 @@ use crate::manager_observer::{ ChildViewChangeReason, }; use crate::notification::{ - send_current_workspace_notification, send_notification, FolderNotification, + folder_notification_builder, send_current_workspace_notification, FolderNotification, }; use crate::publish_util::{generate_publish_name, view_pb_to_publish_view}; -use crate::share::{ImportParams, ImportValue}; +use crate::share::{ImportData, ImportItem, ImportParams}; use crate::util::{folder_not_init_error, workspace_data_not_sync_error}; use crate::view_operation::{ - create_view, EncodedCollabWrapper, FolderOperationHandler, FolderOperationHandlers, ViewData, + create_view, EncodedCollabType, FolderOperationHandler, FolderOperationHandlers, ViewData, }; use arc_swap::ArcSwapOption; use client_api::entity::workspace_dto::PublishInfoView; @@ -57,11 +57,6 @@ pub trait FolderUser: Send + Sync { } pub struct FolderManager { - //FIXME: there's no sense in having a mutex_folder behind an RwLock. It's being obtained multiple - // times in the same function. FolderManager itself should be hidden behind RwLock if necessary. - // Unfortunately, this would require a changing the SyncPlugin architecture which requires access - // to Arc>>. Eventually SyncPlugin should be refactored. - /// MutexFolder is the folder that is used to store the data. pub(crate) mutex_folder: ArcSwapOption>, pub(crate) collab_builder: Arc, pub(crate) user: Arc, @@ -75,7 +70,6 @@ impl FolderManager { pub fn new( user: Arc, collab_builder: Arc, - operation_handlers: FolderOperationHandlers, cloud_service: Arc, folder_indexer: Arc, store_preferences: Arc, @@ -84,7 +78,7 @@ impl FolderManager { user, mutex_folder: Default::default(), collab_builder, - operation_handlers, + operation_handlers: Default::default(), cloud_service, folder_indexer, store_preferences, @@ -93,10 +87,17 @@ impl FolderManager { Ok(manager) } + pub fn register_operation_handler( + &self, + layout: ViewLayout, + handler: Arc, + ) { + self.operation_handlers.insert(layout, handler); + } + #[instrument(level = "debug", skip(self), err)] pub async fn get_current_workspace(&self) -> FlowyResult { let workspace_id = self.user.workspace_id()?; - match self.mutex_folder.load_full() { None => { let uid = self.user.user_id()?; @@ -118,6 +119,31 @@ impl FolderManager { } } + pub async fn get_folder_data(&self) -> FlowyResult { + let workspace_id = self.user.workspace_id()?; + let data = self + .mutex_folder + .load_full() + .ok_or_else(|| internal_error("The folder is not initialized"))? + .read() + .await + .get_folder_data(&workspace_id) + .ok_or_else(|| internal_error("Workspace id not match the id in current folder"))?; + Ok(data) + } + + pub async fn get_encode_collab_from_disk( + &self, + view_id: &str, + layout: &ViewLayout, + ) -> FlowyResult { + let handler = self.get_handler(layout)?; + let encoded_collab = handler + .get_encoded_collab_v1_from_disk(&self.user, view_id) + .await?; + Ok(encoded_collab) + } + /// Return a list of views of the current workspace. /// Only the first level of child views are included. pub async fn get_current_workspace_public_views(&self) -> FlowyResult> { @@ -372,7 +398,7 @@ impl FolderManager { .ok_or_else(|| FlowyError::internal().with_context("Cannot find the workspace ID"))?; views.iter_mut().for_each(|view| { - view.view.parent_view_id = workspace_id.clone(); + view.view.parent_view_id.clone_from(&workspace_id); view.view.extra = Some( serde_json::to_string( &ViewExtraBuilder::new() @@ -460,7 +486,7 @@ impl FolderManager { latest_view.space_info(), ); views.iter_mut().for_each(|child_view| { - child_view.view.parent_view_id = latest_view.id.clone(); + child_view.view.parent_view_id.clone_from(&latest_view.id); }); }, } @@ -517,7 +543,13 @@ impl FolderManager { ); if params.meta.is_empty() && params.initial_data.is_empty() { handler - .create_view_with_default_data(user_id, ¶ms.view_id, ¶ms.name, view_layout.clone()) + .create_default_view( + user_id, + ¶ms.parent_view_id, + ¶ms.view_id, + ¶ms.name, + view_layout.clone(), + ) .await?; } else { encoded_collab = handler @@ -555,7 +587,13 @@ impl FolderManager { let handler = self.get_handler(&view_layout)?; let user_id = self.user.user_id()?; handler - .create_view_with_default_data(user_id, ¶ms.view_id, ¶ms.name, view_layout.clone()) + .create_default_view( + user_id, + ¶ms.parent_view_id, + ¶ms.view_id, + ¶ms.name, + view_layout.clone(), + ) .await?; let view = create_view(self.user.user_id()?, params, view_layout); @@ -707,7 +745,7 @@ impl FolderManager { break; } ancestors.push(view_pb_without_child_views(view.as_ref().clone())); - parent_view_id = view.parent_view_id.clone(); + parent_view_id.clone_from(&view.parent_view_id); } ancestors.reverse(); } @@ -740,7 +778,7 @@ impl FolderManager { drop(folder); // notify the parent view that the view is moved to trash - send_notification(view_id, FolderNotification::DidMoveViewToTrash) + folder_notification_builder(view_id, FolderNotification::DidMoveViewToTrash) .payload(DeletedViewPB { view_id: view_id.to_string(), index: None, @@ -774,7 +812,7 @@ impl FolderManager { .map(|v| v.id.clone()) .collect(), ); - send_notification("favorite", FolderNotification::DidUnfavoriteView) + folder_notification_builder("favorite", FolderNotification::DidUnfavoriteView) .payload(RepeatedViewPB { items: favorite_descendant_views, }) @@ -881,6 +919,20 @@ impl FolderManager { } } + pub async fn get_view(&self, view_id: &str) -> FlowyResult> { + match self.mutex_folder.load_full() { + Some(folder) => { + let folder = folder.read().await; + Ok( + folder + .get_view(view_id) + .ok_or_else(FlowyError::record_not_found)?, + ) + }, + None => Err(FlowyError::internal().with_context("The folder is not initialized")), + } + } + /// Update the view with the given params. #[tracing::instrument(level = "trace", skip(self), err)] pub async fn update_view_with_params(&self, params: UpdateViewParams) -> FlowyResult<()> { @@ -1066,7 +1118,7 @@ impl FolderManager { .await?; if is_source_view { - new_view_id = duplicated_view.id.clone(); + new_view_id.clone_from(&duplicated_view.id); } if sync_after_create { @@ -1248,7 +1300,9 @@ impl FolderManager { .into_iter() .map(|mut p| { if let PublishPayload::Database(p) = &mut p { - p.data.visible_database_view_ids = selected_view_ids.clone(); + p.data + .visible_database_view_ids + .clone_from(&selected_view_ids); } p }) @@ -1448,9 +1502,9 @@ impl FolderManager { publish_name: Option, layout: ViewLayout, ) -> FlowyResult { - let handler: Arc = self.get_handler(&layout)?; - let encoded_collab_wrapper: EncodedCollabWrapper = handler - .get_encoded_collab_v1_from_disk(self.user.clone(), view_id) + let handler = self.get_handler(&layout)?; + let encoded_collab_wrapper: EncodedCollabType = handler + .get_encoded_collab_v1_from_disk(&self.user, view_id) .await?; let view = self.get_view_pb(view_id).await?; @@ -1481,7 +1535,7 @@ impl FolderManager { }; let payload = match encoded_collab_wrapper { - EncodedCollabWrapper::Database(v) => { + EncodedCollabType::Database(v) => { let database_collab = v.database_encoded_collab.doc_state.to_vec(); let database_relations = v.database_relations; let database_row_collabs = v @@ -1504,11 +1558,11 @@ impl FolderManager { }; PublishPayload::Database(PublishDatabasePayload { meta, data }) }, - EncodedCollabWrapper::Document(v) => { + EncodedCollabType::Document(v) => { let data = v.document_encoded_collab.doc_state.to_vec(); PublishPayload::Document(PublishDocumentPayload { meta, data }) }, - EncodedCollabWrapper::Unknown => PublishPayload::Unknown, + EncodedCollabType::Unknown => PublishPayload::Unknown, }; Ok(payload) @@ -1522,13 +1576,13 @@ impl FolderManager { } else { FolderNotification::DidUnfavoriteView }; - send_notification("favorite", notification_type) + folder_notification_builder("favorite", notification_type) .payload(RepeatedViewPB { items: vec![view.clone()], }) .send(); - send_notification(&view.id, FolderNotification::DidUpdateView) + folder_notification_builder(&view.id, FolderNotification::DidUpdateView) .payload(view) .send() } @@ -1536,7 +1590,7 @@ impl FolderManager { async fn send_update_recent_views_notification(&self) { let recent_views = self.get_my_recent_sections().await; - send_notification("recent_views", FolderNotification::DidUpdateRecentViews) + folder_notification_builder("recent_views", FolderNotification::DidUpdateRecentViews) .payload(RepeatedViewIdPB { items: recent_views.into_iter().map(|item| item.id).collect(), }) @@ -1566,7 +1620,7 @@ impl FolderManager { if let Some(lock) = self.mutex_folder.load_full() { let mut folder = lock.write().await; folder.remove_all_my_trash_sections(); - send_notification("trash", FolderNotification::DidUpdateTrash) + folder_notification_builder("trash", FolderNotification::DidUpdateTrash) .payload(RepeatedTrashPB { items: vec![] }) .send(); } @@ -1592,7 +1646,7 @@ impl FolderManager { for trash in deleted_trash { let _ = self.delete_trash(&trash.id).await; } - send_notification("trash", FolderNotification::DidUpdateTrash) + folder_notification_builder("trash", FolderNotification::DidUpdateTrash) .payload(RepeatedTrashPB { items: vec![] }) .send(); } @@ -1622,42 +1676,36 @@ impl FolderManager { } /// Imports a single file to the folder and returns the encoded collab for immediate cloud sync. + #[allow(clippy::type_complexity)] + #[instrument(level = "debug", skip_all, err)] pub(crate) async fn import_single_file( &self, parent_view_id: String, - import_data: ImportValue, + import_data: ImportItem, ) -> FlowyResult<(View, Vec<(String, CollabType, EncodedCollab)>)> { - // Ensure either data or file_path is provided - if import_data.data.is_none() && import_data.file_path.is_none() { - return Err(FlowyError::new( - ErrorCode::InvalidParams, - "Either data or file_path is required", - )); - } - let handler = self.get_handler(&import_data.view_layout)?; let view_id = gen_view_id().to_string(); let uid = self.user.user_id()?; let mut encoded_collab = vec![]; - // Import data from bytes if available - if let Some(data) = import_data.data { - encoded_collab = handler - .import_from_bytes( - uid, - &view_id, - &import_data.name, - import_data.import_type, - data, - ) - .await?; - } - - // Import data from file path if available - if let Some(file_path) = import_data.file_path { - handler - .import_from_file_path(&view_id, &import_data.name, file_path) - .await?; + info!("import single file from:{}", import_data.data); + match import_data.data { + ImportData::FilePath { file_path } => { + handler + .import_from_file_path(&view_id, &import_data.name, file_path) + .await?; + }, + ImportData::Bytes { bytes } => { + encoded_collab = handler + .import_from_bytes( + uid, + &view_id, + &import_data.name, + import_data.import_type, + bytes, + ) + .await?; + }, } let params = CreateViewParams { @@ -1695,7 +1743,7 @@ impl FolderManager { let workspace_id = self.user.workspace_id()?; let mut objects = vec![]; let mut views = vec![]; - for data in import_data.values { + for data in import_data.items { // Import a single file and get the view and encoded collab data let (view, encoded_collabs) = self .import_single_file(import_data.parent_view_id.clone(), data) @@ -1751,7 +1799,7 @@ impl FolderManager { } if let Ok(view_pb) = self.get_view_pb(view_id).await { - send_notification(&view_pb.id, FolderNotification::DidUpdateView) + folder_notification_builder(&view_pb.id, FolderNotification::DidUpdateView) .payload(view_pb) .send(); @@ -1765,10 +1813,7 @@ impl FolderManager { } /// Returns a handler that implements the [FolderOperationHandler] trait - fn get_handler( - &self, - view_layout: &ViewLayout, - ) -> FlowyResult> { + fn get_handler(&self, view_layout: &ViewLayout) -> FlowyResult> { match self.operation_handlers.get(view_layout) { None => Err(FlowyError::internal().with_context(format!( "Get data processor failed. Unknown layout type: {:?}", diff --git a/frontend/rust-lib/flowy-folder/src/manager_init.rs b/frontend/rust-lib/flowy-folder/src/manager_init.rs index e333e445b6..4393bfbb29 100644 --- a/frontend/rust-lib/flowy-folder/src/manager_init.rs +++ b/frontend/rust-lib/flowy-folder/src/manager_init.rs @@ -128,11 +128,6 @@ impl FolderManager { folder_state_rx, Arc::downgrade(&self.user), ); - subscribe_folder_snapshot_state_changed( - workspace_id.clone(), - weak_mutex_folder.clone(), - Arc::downgrade(&self.user), - ); subscribe_folder_trash_changed( workspace_id.clone(), section_change_rx, diff --git a/frontend/rust-lib/flowy-folder/src/manager_observer.rs b/frontend/rust-lib/flowy-folder/src/manager_observer.rs index 8c8af3f0bb..12069e7317 100644 --- a/frontend/rust-lib/flowy-folder/src/manager_observer.rs +++ b/frontend/rust-lib/flowy-folder/src/manager_observer.rs @@ -1,16 +1,16 @@ use crate::entities::{ - view_pb_with_child_views, view_pb_without_child_views, ChildViewUpdatePB, FolderSnapshotStatePB, - FolderSyncStatePB, RepeatedTrashPB, RepeatedViewPB, SectionViewsPB, ViewPB, ViewSectionPB, + view_pb_with_child_views, view_pb_without_child_views, ChildViewUpdatePB, FolderSyncStatePB, + RepeatedTrashPB, RepeatedViewPB, SectionViewsPB, ViewPB, ViewSectionPB, }; use crate::manager::{get_workspace_private_view_pbs, get_workspace_public_view_pbs, FolderUser}; -use crate::notification::{send_notification, FolderNotification}; +use crate::notification::{folder_notification_builder, FolderNotification}; use collab::core::collab_state::SyncState; use collab::lock::RwLock; use collab_folder::{ Folder, SectionChange, SectionChangeReceiver, TrashSectionChange, View, ViewChange, ViewChangeReceiver, }; -use lib_dispatch::prelude::af_spawn; + use std::collections::HashSet; use std::sync::Weak; use tokio_stream::wrappers::WatchStream; @@ -24,7 +24,7 @@ pub(crate) fn subscribe_folder_view_changed( weak_mutex_folder: Weak>, user: Weak, ) { - af_spawn(async move { + tokio::spawn(async move { while let Ok(value) = rx.recv().await { if let Some(user) = user.upgrade() { if let Ok(actual_workspace_id) = user.workspace_id() { @@ -37,7 +37,7 @@ pub(crate) fn subscribe_folder_view_changed( } if let Some(lock) = weak_mutex_folder.upgrade() { - tracing::trace!("Did receive view change: {:?}", value); + trace!("Did receive view change: {:?}", value); match value { ViewChange::DidCreateView { view } => { notify_child_views_changed( @@ -70,44 +70,12 @@ pub(crate) fn subscribe_folder_view_changed( }); } -pub(crate) fn subscribe_folder_snapshot_state_changed( - workspace_id: String, - weak_mutex_folder: Weak>, - user: Weak, -) { - af_spawn(async move { - if let Some(folder) = weak_mutex_folder.upgrade() { - let mut state_stream = folder.read().await.subscribe_snapshot_state(); - - while let Some(snapshot_state) = state_stream.next().await { - if let Some(user) = user.upgrade() { - if let Ok(actual_workspace_id) = user.workspace_id() { - if actual_workspace_id != workspace_id { - // break the loop when the workspace id is not matched. - break; - } - } - } - if let Some(new_snapshot_id) = snapshot_state.snapshot_id() { - tracing::debug!("Did create folder remote snapshot: {}", new_snapshot_id); - send_notification( - &workspace_id, - FolderNotification::DidUpdateFolderSnapshotState, - ) - .payload(FolderSnapshotStatePB { new_snapshot_id }) - .send(); - } - } - } - }); -} - pub(crate) fn subscribe_folder_sync_state_changed( workspace_id: String, mut folder_sync_state_rx: WatchStream, user: Weak, ) { - af_spawn(async move { + tokio::spawn(async move { while let Some(state) = folder_sync_state_rx.next().await { if let Some(user) = user.upgrade() { if let Ok(actual_workspace_id) = user.workspace_id() { @@ -118,7 +86,7 @@ pub(crate) fn subscribe_folder_sync_state_changed( } } - send_notification(&workspace_id, FolderNotification::DidUpdateFolderSyncUpdate) + folder_notification_builder(&workspace_id, FolderNotification::DidUpdateFolderSyncUpdate) .payload(FolderSyncStatePB::from(state)) .send(); } @@ -132,7 +100,7 @@ pub(crate) fn subscribe_folder_trash_changed( weak_mutex_folder: Weak>, user: Weak, ) { - af_spawn(async move { + tokio::spawn(async move { while let Ok(value) = rx.recv().await { if let Some(user) = user.upgrade() { if let Ok(actual_workspace_id) = user.workspace_id() { @@ -160,7 +128,7 @@ pub(crate) fn subscribe_folder_trash_changed( } let repeated_trash: RepeatedTrashPB = folder.get_my_trash_info().into(); - send_notification("trash", FolderNotification::DidUpdateTrash) + folder_notification_builder("trash", FolderNotification::DidUpdateTrash) .payload(repeated_trash) .send(); @@ -204,7 +172,7 @@ pub(crate) fn notify_parent_view_did_change>( // Post the notification let parent_view_pb = view_pb_with_child_views(parent_view, child_views); - send_notification(parent_view_id, FolderNotification::DidUpdateView) + folder_notification_builder(parent_view_id, FolderNotification::DidUpdateView) .payload(parent_view_pb) .send(); } @@ -216,14 +184,14 @@ pub(crate) fn notify_parent_view_did_change>( pub(crate) fn notify_did_update_section_views(workspace_id: &str, folder: &Folder) { let public_views = get_workspace_public_view_pbs(workspace_id, folder); let private_views = get_workspace_private_view_pbs(workspace_id, folder); - tracing::trace!( + trace!( "Did update section views: public len = {}, private len = {}", public_views.len(), private_views.len() ); // Notify the public views - send_notification(workspace_id, FolderNotification::DidUpdateSectionViews) + folder_notification_builder(workspace_id, FolderNotification::DidUpdateSectionViews) .payload(SectionViewsPB { section: ViewSectionPB::Public, views: public_views, @@ -231,7 +199,7 @@ pub(crate) fn notify_did_update_section_views(workspace_id: &str, folder: &Folde .send(); // Notify the private views - send_notification(workspace_id, FolderNotification::DidUpdateSectionViews) + folder_notification_builder(workspace_id, FolderNotification::DidUpdateSectionViews) .payload(SectionViewsPB { section: ViewSectionPB::Private, views: private_views, @@ -241,7 +209,7 @@ pub(crate) fn notify_did_update_section_views(workspace_id: &str, folder: &Folde pub(crate) fn notify_did_update_workspace(workspace_id: &str, folder: &Folder) { let repeated_view: RepeatedViewPB = get_workspace_public_view_pbs(workspace_id, folder).into(); - send_notification(workspace_id, FolderNotification::DidUpdateWorkspaceViews) + folder_notification_builder(workspace_id, FolderNotification::DidUpdateWorkspaceViews) .payload(repeated_view) .send(); } @@ -249,7 +217,7 @@ pub(crate) fn notify_did_update_workspace(workspace_id: &str, folder: &Folder) { fn notify_view_did_change(view: View) -> Option<()> { let view_id = view.id.clone(); let view_pb = view_pb_without_child_views(view); - send_notification(&view_id, FolderNotification::DidUpdateView) + folder_notification_builder(&view_id, FolderNotification::DidUpdateView) .payload(view_pb) .send(); None @@ -282,7 +250,7 @@ pub(crate) fn notify_child_views_changed(view_pb: ViewPB, reason: ChildViewChang }, } - send_notification(&parent_view_id, FolderNotification::DidUpdateChildViews) + folder_notification_builder(&parent_view_id, FolderNotification::DidUpdateChildViews) .payload(payload) .send(); } diff --git a/frontend/rust-lib/flowy-folder/src/manager_test_util.rs b/frontend/rust-lib/flowy-folder/src/manager_test_util.rs deleted file mode 100644 index e7c29f6d58..0000000000 --- a/frontend/rust-lib/flowy-folder/src/manager_test_util.rs +++ /dev/null @@ -1,34 +0,0 @@ -use crate::manager::{FolderManager, FolderUser}; -use crate::view_operation::FolderOperationHandlers; -use collab::lock::RwLock; -use collab_folder::Folder; -use collab_integrate::collab_builder::AppFlowyCollabBuilder; -use flowy_folder_pub::cloud::FolderCloudService; -use flowy_search_pub::entities::FolderIndexManager; -use std::sync::Arc; - -impl FolderManager { - pub fn get_mutex_folder(&self) -> Option>> { - self.mutex_folder.load_full() - } - - pub fn get_cloud_service(&self) -> Arc { - self.cloud_service.clone() - } - - pub fn get_user(&self) -> Arc { - self.user.clone() - } - - pub fn get_indexer(&self) -> Arc { - self.folder_indexer.clone() - } - - pub fn get_collab_builder(&self) -> Arc { - self.collab_builder.clone() - } - - pub fn get_operation_handlers(&self) -> FolderOperationHandlers { - self.operation_handlers.clone() - } -} diff --git a/frontend/rust-lib/flowy-folder/src/notification.rs b/frontend/rust-lib/flowy-folder/src/notification.rs index bf72f2097e..8d9bfd5dea 100644 --- a/frontend/rust-lib/flowy-folder/src/notification.rs +++ b/frontend/rust-lib/flowy-folder/src/notification.rs @@ -69,7 +69,7 @@ impl std::convert::From for FolderNotification { } #[tracing::instrument(level = "trace")] -pub(crate) fn send_notification(id: &str, ty: FolderNotification) -> NotificationBuilder { +pub(crate) fn folder_notification_builder(id: &str, ty: FolderNotification) -> NotificationBuilder { NotificationBuilder::new(id, ty, FOLDER_OBSERVABLE_SOURCE) } @@ -77,7 +77,7 @@ pub(crate) fn send_notification(id: &str, ty: FolderNotification) -> Notificatio /// user. Only one workspace can be opened at a time. const CURRENT_WORKSPACE: &str = "current-workspace"; pub(crate) fn send_current_workspace_notification(ty: FolderNotification, payload: T) { - send_notification(CURRENT_WORKSPACE, ty) + folder_notification_builder(CURRENT_WORKSPACE, ty) .payload(payload) .send(); } diff --git a/frontend/rust-lib/flowy-folder/src/share/import.rs b/frontend/rust-lib/flowy-folder/src/share/import.rs index 2140c0f4ee..2abac3540d 100644 --- a/frontend/rust-lib/flowy-folder/src/share/import.rs +++ b/frontend/rust-lib/flowy-folder/src/share/import.rs @@ -1,4 +1,5 @@ use collab_folder::ViewLayout; +use std::fmt::{Display, Formatter}; #[derive(Clone, Debug)] pub enum ImportType { @@ -10,16 +11,30 @@ pub enum ImportType { } #[derive(Clone, Debug)] -pub struct ImportValue { +pub struct ImportItem { pub name: String, - pub data: Option>, - pub file_path: Option, + pub data: ImportData, pub view_layout: ViewLayout, pub import_type: ImportType, } +#[derive(Clone, Debug)] +pub enum ImportData { + FilePath { file_path: String }, + Bytes { bytes: Vec }, +} + +impl Display for ImportData { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + ImportData::FilePath { file_path } => write!(f, "file: {}", file_path), + ImportData::Bytes { .. } => write!(f, "binary"), + } + } +} + #[derive(Clone, Debug)] pub struct ImportParams { pub parent_view_id: String, - pub values: Vec, + pub items: Vec, } diff --git a/frontend/rust-lib/flowy-folder/src/user_default.rs b/frontend/rust-lib/flowy-folder/src/user_default.rs index 6b8174de76..82fe1730fc 100644 --- a/frontend/rust-lib/flowy-folder/src/user_default.rs +++ b/frontend/rust-lib/flowy-folder/src/user_default.rs @@ -7,7 +7,7 @@ use tokio::sync::RwLock; use lib_infra::util::timestamp; use crate::entities::{view_pb_with_child_views, ViewPB}; -use crate::view_operation::FolderOperationHandlers; +use crate::view_operation::{FolderOperationHandler, FolderOperationHandlers}; pub struct DefaultFolderBuilder(); impl DefaultFolderBuilder { @@ -20,7 +20,19 @@ impl DefaultFolderBuilder { workspace_id.clone(), uid, ))); - for handler in handlers.values() { + + // Collect all handlers from the DashMap into a vector. + // + // - `DashMap::iter()` returns references to the stored values, which are not `Send` + // and can cause issues in an `async` context where thread-safety is required. + // - By cloning the values into a `Vec`, we ensure they are owned and implement + // `Send + Sync`, making them safe to use in asynchronous operations. + // - This avoids lifetime conflicts and allows the handlers to be used in the + // asynchronous loop without tying their lifetimes to the DashMap. + // + let handler_clones: Vec> = + handlers.iter().map(|entry| entry.value().clone()).collect(); + for handler in handler_clones { let _ = handler .create_workspace_view(uid, workspace_view_builder.clone()) .await; diff --git a/frontend/rust-lib/flowy-folder/src/view_operation.rs b/frontend/rust-lib/flowy-folder/src/view_operation.rs index 6f44278d46..589d344256 100644 --- a/frontend/rust-lib/flowy-folder/src/view_operation.rs +++ b/frontend/rust-lib/flowy-folder/src/view_operation.rs @@ -5,6 +5,7 @@ use collab_entity::CollabType; use collab_folder::hierarchy_builder::NestedViewBuilder; pub use collab_folder::View; use collab_folder::ViewLayout; +use dashmap::DashMap; use std::collections::HashMap; use std::sync::Arc; use tokio::sync::RwLock; @@ -18,7 +19,7 @@ use crate::manager::FolderUser; use crate::share::ImportType; #[derive(Debug, Clone)] -pub enum EncodedCollabWrapper { +pub enum EncodedCollabType { Document(DocumentEncodedCollab), Database(DatabaseEncodedCollab), Unknown, @@ -43,7 +44,7 @@ pub type ImportedData = (String, CollabType, EncodedCollab); /// view layout. Each [ViewLayout] will have a handler. So when creating a new /// view, the [ViewLayout] will be used to get the handler. #[async_trait] -pub trait FolderOperationHandler { +pub trait FolderOperationHandler: Send + Sync { fn name(&self) -> &str; /// Create the view for the workspace of new user. /// Only called once when the user is created. @@ -70,9 +71,9 @@ pub trait FolderOperationHandler { /// get the encoded collab data from the disk. async fn get_encoded_collab_v1_from_disk( &self, - _user: Arc, + _user: &Arc, _view_id: &str, - ) -> Result { + ) -> Result { Err(FlowyError::not_support()) } @@ -103,9 +104,10 @@ pub trait FolderOperationHandler { /// Create a view with the pre-defined data. /// For example, the initial data of the grid/calendar/kanban board when /// you create a new view. - async fn create_view_with_default_data( + async fn create_default_view( &self, user_id: i64, + parent_view_id: &str, view_id: &str, name: &str, layout: ViewLayout, @@ -138,7 +140,7 @@ pub trait FolderOperationHandler { } pub type FolderOperationHandlers = - Arc>>; + Arc>>; impl From for ViewLayout { fn from(pb: ViewLayoutPB) -> Self { diff --git a/frontend/rust-lib/flowy-notification/build.rs b/frontend/rust-lib/flowy-notification/build.rs index 0be74ea9bc..81f0556ae3 100644 --- a/frontend/rust-lib/flowy-notification/build.rs +++ b/frontend/rust-lib/flowy-notification/build.rs @@ -15,13 +15,4 @@ fn main() { flowy_codegen::Project::TauriApp, ); } - - #[cfg(feature = "web_ts")] - flowy_codegen::protobuf_file::ts_gen( - env!("CARGO_PKG_NAME"), - "notification", - flowy_codegen::Project::Web { - relative_path: "../../".to_string(), - }, - ); } diff --git a/frontend/rust-lib/flowy-search/src/folder/indexer.rs b/frontend/rust-lib/flowy-search/src/folder/indexer.rs index 6624067788..8c1d5633ac 100644 --- a/frontend/rust-lib/flowy-search/src/folder/indexer.rs +++ b/frontend/rust-lib/flowy-search/src/folder/indexer.rs @@ -19,7 +19,7 @@ use collab_folder::{folder_diff::FolderViewChange, View, ViewIcon, ViewIndexCont use flowy_error::{FlowyError, FlowyResult}; use flowy_search_pub::entities::{FolderIndexManager, IndexManager, IndexableData}; use flowy_user::services::authenticate_user::AuthenticateUser; -use lib_dispatch::prelude::af_spawn; + use strsim::levenshtein; use tantivy::{ collector::TopDocs, directory::MmapDirectory, doc, query::QueryParser, schema::Field, Document, @@ -296,7 +296,7 @@ impl IndexManager for FolderIndexManagerImpl { fn set_index_content_receiver(&self, mut rx: IndexContentReceiver, workspace_id: String) { let indexer = self.clone(); let wid = workspace_id.clone(); - af_spawn(async move { + tokio::spawn(async move { while let Ok(msg) = rx.recv().await { match msg { IndexContent::Create(value) => match serde_json::from_value::(value) { diff --git a/frontend/rust-lib/flowy-search/src/services/manager.rs b/frontend/rust-lib/flowy-search/src/services/manager.rs index 35e7e2c1cb..84659b3037 100644 --- a/frontend/rust-lib/flowy-search/src/services/manager.rs +++ b/frontend/rust-lib/flowy-search/src/services/manager.rs @@ -4,7 +4,7 @@ use std::sync::Arc; use super::notifier::{SearchNotifier, SearchResultChanged, SearchResultReceiverRunner}; use crate::entities::{SearchFilterPB, SearchResultNotificationPB, SearchResultPB}; use flowy_error::FlowyResult; -use lib_dispatch::prelude::af_spawn; + use lib_infra::async_trait::async_trait; use tokio::sync::broadcast; @@ -48,7 +48,7 @@ impl SearchManager { // Initialize Search Notifier let (notifier, _) = broadcast::channel(100); - af_spawn(SearchResultReceiverRunner(Some(notifier.subscribe())).run()); + tokio::spawn(SearchResultReceiverRunner(Some(notifier.subscribe())).run()); Self { handlers, notifier } } @@ -71,7 +71,7 @@ impl SearchManager { let ch = channel.clone(); let notifier = self.notifier.clone(); - af_spawn(async move { + tokio::spawn(async move { let res = handler.perform_search(q.clone(), f).await; let items = res.unwrap_or_default(); diff --git a/frontend/rust-lib/flowy-server/src/af_cloud/impls/chat.rs b/frontend/rust-lib/flowy-server/src/af_cloud/impls/chat.rs index 65ee80b6dc..f8efb9b137 100644 --- a/frontend/rust-lib/flowy-server/src/af_cloud/impls/chat.rs +++ b/frontend/rust-lib/flowy-server/src/af_cloud/impls/chat.rs @@ -5,8 +5,8 @@ use client_api::entity::chat_dto::{ RepeatedChatMessage, }; use flowy_ai_pub::cloud::{ - ChatCloudService, ChatMessage, ChatMessageMetadata, ChatMessageType, LocalAIConfig, StreamAnswer, - StreamComplete, SubscriptionPlan, + ChatCloudService, ChatMessage, ChatMessageMetadata, ChatMessageType, ChatSettings, LocalAIConfig, + StreamAnswer, StreamComplete, SubscriptionPlan, UpdateChatParams, }; use flowy_error::FlowyError; use futures_util::{StreamExt, TryStreamExt}; @@ -30,13 +30,14 @@ where _uid: &i64, workspace_id: &str, chat_id: &str, + rag_ids: Vec, ) -> Result<(), FlowyError> { let chat_id = chat_id.to_string(); let try_get_client = self.inner.try_get_client(); let params = CreateChatParams { chat_id, name: "".to_string(), - rag_ids: vec![], + rag_ids, }; try_get_client? .create_chat(workspace_id, params) @@ -216,4 +217,31 @@ where .await?; Ok(plans) } + + async fn get_chat_settings( + &self, + workspace_id: &str, + chat_id: &str, + ) -> Result { + let settings = self + .inner + .try_get_client()? + .get_chat_settings(workspace_id, chat_id) + .await?; + Ok(settings) + } + + async fn update_chat_settings( + &self, + workspace_id: &str, + chat_id: &str, + params: UpdateChatParams, + ) -> Result<(), FlowyError> { + self + .inner + .try_get_client()? + .update_chat_settings(workspace_id, chat_id, params) + .await?; + Ok(()) + } } diff --git a/frontend/rust-lib/flowy-server/src/af_cloud/server.rs b/frontend/rust-lib/flowy-server/src/af_cloud/server.rs index d63ff66bc2..ee908ac9cc 100644 --- a/frontend/rust-lib/flowy-server/src/af_cloud/server.rs +++ b/frontend/rust-lib/flowy-server/src/af_cloud/server.rs @@ -25,7 +25,7 @@ use flowy_server_pub::af_cloud_config::AFCloudConfiguration; use flowy_storage_pub::cloud::StorageCloudService; use flowy_user_pub::cloud::{UserCloudService, UserUpdate}; use flowy_user_pub::entities::UserTokenState; -use lib_dispatch::prelude::af_spawn; + use rand::Rng; use semver::Version; use tokio::select; @@ -132,7 +132,7 @@ impl AppFlowyServer for AppFlowyCloudServer { let mut token_state_rx = self.client.subscribe_token_state(); let (watch_tx, watch_rx) = watch::channel(UserTokenState::Init); let weak_client = Arc::downgrade(&self.client); - af_spawn(async move { + tokio::spawn(async move { while let Ok(token_state) = token_state_rx.recv().await { if let Some(client) = weak_client.upgrade() { match token_state { @@ -172,7 +172,7 @@ impl AppFlowyServer for AppFlowyCloudServer { }; let mut user_change = self.ws_client.subscribe_user_changed(); let (tx, rx) = tokio::sync::mpsc::channel(1); - af_spawn(async move { + tokio::spawn(async move { while let Ok(user_message) = user_change.recv().await { if let UserMessage::ProfileChange(change) = user_message { let user_update = UserUpdate { @@ -302,7 +302,7 @@ fn spawn_ws_conn( let cancellation_token = Arc::new(ArcSwap::new(Arc::new(CancellationToken::new()))); let cloned_cancellation_token = cancellation_token.clone(); - af_spawn(async move { + tokio::spawn(async move { if let Some(ws_client) = weak_ws_client.upgrade() { let mut state_recv = ws_client.subscribe_connect_state(); while let Ok(state) = state_recv.recv().await { @@ -331,7 +331,7 @@ fn spawn_ws_conn( }); let weak_ws_client = Arc::downgrade(ws_client); - af_spawn(async move { + tokio::spawn(async move { while let Ok(token_state) = token_state_rx.recv().await { info!("🟢token state: {:?}", token_state); match token_state { diff --git a/frontend/rust-lib/flowy-server/src/default_impl.rs b/frontend/rust-lib/flowy-server/src/default_impl.rs index cf55395c36..bfde5217b0 100644 --- a/frontend/rust-lib/flowy-server/src/default_impl.rs +++ b/frontend/rust-lib/flowy-server/src/default_impl.rs @@ -1,7 +1,7 @@ use client_api::entity::ai_dto::{CompletionType, LocalAIConfig, RepeatedRelatedQuestion}; use flowy_ai_pub::cloud::{ - ChatCloudService, ChatMessage, ChatMessageMetadata, ChatMessageType, MessageCursor, - RepeatedChatMessage, StreamAnswer, StreamComplete, SubscriptionPlan, + ChatCloudService, ChatMessage, ChatMessageMetadata, ChatMessageType, ChatSettings, MessageCursor, + RepeatedChatMessage, StreamAnswer, StreamComplete, SubscriptionPlan, UpdateChatParams, }; use flowy_error::FlowyError; use lib_infra::async_trait::async_trait; @@ -18,6 +18,7 @@ impl ChatCloudService for DefaultChatCloudServiceImpl { _uid: &i64, _workspace_id: &str, _chat_id: &str, + _rag_ids: Vec, ) -> Result<(), FlowyError> { Err(FlowyError::not_support().with_context("Chat is not supported in local server.")) } @@ -116,4 +117,21 @@ impl ChatCloudService for DefaultChatCloudServiceImpl { .with_context("Get local ai config is not supported in local server."), ) } + + async fn get_chat_settings( + &self, + _workspace_id: &str, + _chat_id: &str, + ) -> Result { + Err(FlowyError::not_support().with_context("Chat is not supported in local server.")) + } + + async fn update_chat_settings( + &self, + _workspace_id: &str, + _chat_id: &str, + _params: UpdateChatParams, + ) -> Result<(), FlowyError> { + Err(FlowyError::not_support().with_context("Chat is not supported in local server.")) + } } diff --git a/frontend/rust-lib/flowy-server/src/response.rs b/frontend/rust-lib/flowy-server/src/response.rs index 3e58fae69b..dff2faf961 100644 --- a/frontend/rust-lib/flowy-server/src/response.rs +++ b/frontend/rust-lib/flowy-server/src/response.rs @@ -1,13 +1,9 @@ use std::fmt; -use anyhow::Error; use bytes::Bytes; -use reqwest::{Response, StatusCode}; use serde::{Deserialize, Serialize}; -use serde_json::Value; -use flowy_error::{ErrorCode, FlowyError}; -use lib_infra::future::{to_fut, Fut}; +use flowy_error::ErrorCode; #[derive(Debug, Serialize, Deserialize)] pub struct HttpResponse { @@ -34,116 +30,3 @@ impl fmt::Display for HttpError { write!(f, "{:?}: {}", self.code, self.msg) } } - -/// Trait `ExtendedResponse` provides an extension method to handle and transform the response data. -/// -/// This trait introduces a single method: -/// -/// - `get_value`: It extracts the value from the response, and returns it as an instance of a type `T`. -/// This method will return an error if the status code of the response signifies a failure (not success). -/// Otherwise, it attempts to parse the response body into an instance of type `T`, which must implement -/// `serde::de::DeserializeOwned`, `Send`, `Sync`, and have a static lifetime ('static). -pub trait ExtendedResponse { - /// Returns the value of the response as a Future of `Result`. - /// - /// If the status code of the response is not a success, returns an `Error`. - /// Otherwise, attempts to parse the response into an instance of type `T`. - /// - /// # Type Parameters - /// - /// * `T`: The type of the value to be returned. Must implement `serde::de::DeserializeOwned`, - /// `Send`, `Sync`, and have a static lifetime ('static). - fn get_value(self) -> Fut> - where - T: serde::de::DeserializeOwned + Send + Sync + 'static; - - fn get_bytes(self) -> Fut>; - - fn get_json(self) -> Fut>; - - fn success(self) -> Fut>; - - fn success_with_body(self) -> Fut>; -} - -impl ExtendedResponse for Response { - fn get_value(self) -> Fut> - where - T: serde::de::DeserializeOwned + Send + Sync + 'static, - { - to_fut(async move { - let status_code = self.status(); - if !status_code.is_success() { - return Err(parse_response_as_error(self).await.into()); - } - let bytes = self.bytes().await?; - let value = serde_json::from_slice(&bytes).map_err(|e| { - FlowyError::new( - ErrorCode::Serde, - format!( - "failed to parse json: {}, body: {}", - e, - String::from_utf8_lossy(&bytes) - ), - ) - })?; - Ok(value) - }) - } - - fn get_bytes(self) -> Fut> { - to_fut(async move { - let status_code = self.status(); - if !status_code.is_success() { - return Err(parse_response_as_error(self).await.into()); - } - let bytes = self.bytes().await?; - Ok(bytes) - }) - } - - fn get_json(self) -> Fut> { - to_fut(async move { - if !self.status().is_success() { - return Err(parse_response_as_error(self).await.into()); - } - let bytes = self.bytes().await?; - let value = serde_json::from_slice::(&bytes)?; - Ok(value) - }) - } - - fn success(self) -> Fut> { - to_fut(async move { - if !self.status().is_success() { - return Err(parse_response_as_error(self).await.into()); - } - Ok(()) - }) - } - - fn success_with_body(self) -> Fut> { - to_fut(async move { - if !self.status().is_success() { - return Err(parse_response_as_error(self).await.into()); - } - Ok(self.text().await?) - }) - } -} - -async fn parse_response_as_error(response: Response) -> FlowyError { - let status_code = response.status(); - let msg = response.text().await.unwrap_or_default(); - if status_code == StatusCode::CONFLICT { - return FlowyError::new(ErrorCode::Conflict, msg); - } - - FlowyError::new( - ErrorCode::HttpError, - format!( - "expected status code 2XX, but got {}, body: {}", - status_code, msg - ), - ) -} diff --git a/frontend/rust-lib/flowy-server/src/supabase/api/database.rs b/frontend/rust-lib/flowy-server/src/supabase/api/database.rs index 372aa35f3d..af1732b500 100644 --- a/frontend/rust-lib/flowy-server/src/supabase/api/database.rs +++ b/frontend/rust-lib/flowy-server/src/supabase/api/database.rs @@ -4,7 +4,6 @@ use tokio::sync::oneshot::channel; use flowy_database_pub::cloud::{CollabDocStateByOid, DatabaseCloudService, DatabaseSnapshot}; -use lib_dispatch::prelude::af_spawn; use lib_infra::async_trait::async_trait; use lib_infra::future::FutureResult; @@ -37,7 +36,7 @@ where let try_get_postgrest = self.server.try_get_weak_postgrest(); let object_id = object_id.to_string(); let (tx, rx) = channel(); - af_spawn(async move { + tokio::spawn(async move { tx.send( async move { let postgrest = try_get_postgrest?; @@ -60,7 +59,7 @@ where ) -> FutureResult { let try_get_postgrest = self.server.try_get_weak_postgrest(); let (tx, rx) = channel(); - af_spawn(async move { + tokio::spawn(async move { tx.send( async move { let postgrest = try_get_postgrest?; diff --git a/frontend/rust-lib/flowy-server/src/supabase/api/document.rs b/frontend/rust-lib/flowy-server/src/supabase/api/document.rs index 66edb7ed7c..b3f45a7670 100644 --- a/frontend/rust-lib/flowy-server/src/supabase/api/document.rs +++ b/frontend/rust-lib/flowy-server/src/supabase/api/document.rs @@ -8,7 +8,7 @@ use tokio::sync::oneshot::channel; use flowy_document_pub::cloud::{DocumentCloudService, DocumentSnapshot}; use flowy_error::FlowyError; -use lib_dispatch::prelude::af_spawn; + use lib_infra::future::FutureResult; use crate::supabase::api::request::{get_snapshots_from_server, FetchObjectUpdateAction}; @@ -37,7 +37,7 @@ where let try_get_postgrest = self.server.try_get_weak_postgrest(); let document_id = document_id.to_string(); let (tx, rx) = channel(); - af_spawn(async move { + tokio::spawn(async move { tx.send( async move { let postgrest = try_get_postgrest?; @@ -87,7 +87,7 @@ where let try_get_postgrest = self.server.try_get_weak_postgrest(); let document_id = document_id.to_string(); let (tx, rx) = channel(); - af_spawn(async move { + tokio::spawn(async move { tx.send( async move { let postgrest = try_get_postgrest?; diff --git a/frontend/rust-lib/flowy-server/src/supabase/api/folder.rs b/frontend/rust-lib/flowy-server/src/supabase/api/folder.rs index 43c12ae5f1..253d11e0d8 100644 --- a/frontend/rust-lib/flowy-server/src/supabase/api/folder.rs +++ b/frontend/rust-lib/flowy-server/src/supabase/api/folder.rs @@ -14,7 +14,7 @@ use flowy_folder_pub::cloud::{ Workspace, WorkspaceRecord, }; use flowy_folder_pub::entities::PublishPayload; -use lib_dispatch::prelude::af_spawn; + use lib_infra::future::FutureResult; use lib_infra::util::timestamp; @@ -144,7 +144,7 @@ where let try_get_postgrest = self.server.try_get_weak_postgrest(); let object_id = object_id.to_string(); let (tx, rx) = channel(); - af_spawn(async move { + tokio::spawn(async move { tx.send( async move { let postgrest = try_get_postgrest?; diff --git a/frontend/rust-lib/flowy-server/src/supabase/api/user.rs b/frontend/rust-lib/flowy-server/src/supabase/api/user.rs index b8c55cf535..65d02c704a 100644 --- a/frontend/rust-lib/flowy-server/src/supabase/api/user.rs +++ b/frontend/rust-lib/flowy-server/src/supabase/api/user.rs @@ -21,7 +21,7 @@ use flowy_folder_pub::cloud::{Folder, FolderData, Workspace}; use flowy_user_pub::cloud::*; use flowy_user_pub::entities::*; use flowy_user_pub::DEFAULT_USER_NAME; -use lib_dispatch::prelude::af_spawn; + use lib_infra::box_any::BoxAny; use lib_infra::future::FutureResult; @@ -271,7 +271,7 @@ where let try_get_postgrest = self.server.try_get_weak_postgrest(); let (tx, rx) = channel(); let object_id = object_id.to_string(); - af_spawn(async move { + tokio::spawn(async move { tx.send( async move { let postgrest = try_get_postgrest?; @@ -313,7 +313,7 @@ where let try_get_postgrest = self.server.try_get_weak_postgrest(); let (tx, rx) = channel(); let init_update = default_workspace_doc_state(&collab_object); - af_spawn(async move { + tokio::spawn(async move { tx.send( async move { let postgrest = try_get_postgrest? @@ -351,7 +351,7 @@ where let try_get_postgrest = self.server.try_get_weak_postgrest(); let cloned_collab_object = collab_object.clone(); let (tx, rx) = channel(); - af_spawn(async move { + tokio::spawn(async move { tx.send( async move { CreateCollabAction::new(cloned_collab_object, try_get_postgrest?, data) diff --git a/frontend/rust-lib/flowy-sqlite/src/sqlite_impl/pragma.rs b/frontend/rust-lib/flowy-sqlite/src/sqlite_impl/pragma.rs index 05054a6406..10dfda7a9d 100644 --- a/frontend/rust-lib/flowy-sqlite/src/sqlite_impl/pragma.rs +++ b/frontend/rust-lib/flowy-sqlite/src/sqlite_impl/pragma.rs @@ -51,6 +51,7 @@ pub trait PragmaExtension: ConnectionExtension { self.query::(&query) } + #[allow(dead_code)] fn pragma_get<'query, ST, T>(&mut self, key: &str, schema: Option<&str>) -> Result where SqlLiteral: LoadQuery<'query, SqliteConnection, T>, @@ -64,10 +65,12 @@ pub trait PragmaExtension: ConnectionExtension { self.query::(&query) } + #[allow(dead_code)] fn pragma_set_busy_timeout(&mut self, timeout_ms: i32) -> Result { self.pragma_ret::("busy_timeout", timeout_ms, None) } + #[allow(dead_code)] fn pragma_get_busy_timeout(&mut self) -> Result { self.pragma_get::("busy_timeout", None) } @@ -80,12 +83,14 @@ pub trait PragmaExtension: ConnectionExtension { self.pragma_ret::("journal_mode", mode, schema) } + #[allow(dead_code)] fn pragma_get_journal_mode(&mut self, schema: Option<&str>) -> Result { self .pragma_get::("journal_mode", schema)? .parse() } + #[allow(dead_code)] fn pragma_set_synchronous( &mut self, synchronous: SQLiteSynchronous, @@ -94,6 +99,7 @@ pub trait PragmaExtension: ConnectionExtension { self.pragma("synchronous", synchronous as u8, schema) } + #[allow(dead_code)] fn pragma_get_synchronous(&mut self, schema: Option<&str>) -> Result { self .pragma_get::("synchronous", schema)? diff --git a/frontend/rust-lib/flowy-user/src/event_handler.rs b/frontend/rust-lib/flowy-user/src/event_handler.rs index 96b0bbb73a..c22af4f1c1 100644 --- a/frontend/rust-lib/flowy-user/src/event_handler.rs +++ b/frontend/rust-lib/flowy-user/src/event_handler.rs @@ -107,7 +107,7 @@ pub async fn get_user_profile_handler( let cloned_user_profile = user_profile.clone(); // Refresh the user profile in the background - af_spawn(async move { + tokio::spawn(async move { if let Some(manager) = weak_manager.upgrade() { let _ = manager.refresh_user_profile(&cloned_user_profile).await; } @@ -274,7 +274,7 @@ pub async fn import_appflowy_data_folder_handler( ) -> Result<(), FlowyError> { let data = data.try_into_inner()?; let (tx, rx) = tokio::sync::oneshot::channel(); - af_spawn(async move { + tokio::spawn(async move { let result = async { let manager = upgrade_manager(manager)?; let imported_folder = prepare_import( diff --git a/frontend/rust-lib/flowy-user/src/services/data_import/appflowy_data_import.rs b/frontend/rust-lib/flowy-user/src/services/data_import/appflowy_data_import.rs index 8c334dc78b..129b605281 100644 --- a/frontend/rust-lib/flowy-user/src/services/data_import/appflowy_data_import.rs +++ b/frontend/rust-lib/flowy-user/src/services/data_import/appflowy_data_import.rs @@ -347,7 +347,10 @@ pub(crate) fn generate_import_data( invalid_orphan_views .iter_mut() .for_each(|parent_child_views| { - parent_child_views.view.parent_view_id = other_view_id.clone(); + parent_child_views + .view + .parent_view_id + .clone_from(&other_view_id); }); let mut other_view = create_new_container_view( current_session, @@ -364,7 +367,10 @@ pub(crate) fn generate_import_data( views.push(other_view); } else { let first_view = views.first_mut().unwrap(); - other_view.view.parent_view_id = first_view.view.id.clone(); + other_view + .view + .parent_view_id + .clone_from(&first_view.view.id); first_view.children.push(other_view); } } @@ -1250,7 +1256,7 @@ pub async fn upload_collab_objects_data( // Spawn a new task to upload the collab objects data in the background. If the // upload fails, we will retry the upload later. - // af_spawn(async move { + // tokio::spawn(async move { if !objects.is_empty() { batch_create( uid, diff --git a/frontend/rust-lib/flowy-user/src/services/db.rs b/frontend/rust-lib/flowy-user/src/services/db.rs index 97d8f865e6..e324c2820f 100644 --- a/frontend/rust-lib/flowy-user/src/services/db.rs +++ b/frontend/rust-lib/flowy-user/src/services/db.rs @@ -15,7 +15,7 @@ use flowy_sqlite::{ DBConnection, Database, ExpressionMethods, }; use flowy_user_pub::entities::{UserProfile, UserWorkspace}; -use lib_dispatch::prelude::af_spawn; + use lib_infra::file_util::{unzip_and_replace, zip_folder}; use tracing::{error, event, info, instrument}; @@ -60,7 +60,7 @@ impl UserDB { if is_ok { // If database is valid, update the shared map and initiate backup. // Asynchronous backup operation. - af_spawn(async move { + tokio::spawn(async move { if let Err(err) = tokio::task::spawn_blocking(move || zip_backup.backup()).await { error!("Backup of collab db failed: {:?}", err); } diff --git a/frontend/rust-lib/flowy-user/src/user_manager/manager.rs b/frontend/rust-lib/flowy-user/src/user_manager/manager.rs index a76c32f598..c995ca0874 100644 --- a/frontend/rust-lib/flowy-user/src/user_manager/manager.rs +++ b/frontend/rust-lib/flowy-user/src/user_manager/manager.rs @@ -23,7 +23,6 @@ use tokio::sync::Mutex; use tokio_stream::StreamExt; use tracing::{debug, error, event, info, instrument, warn}; -use lib_dispatch::prelude::af_spawn; use lib_infra::box_any::BoxAny; use crate::entities::{AuthStateChangedPB, AuthStatePB, UserProfilePB, UserSettingPB}; @@ -91,7 +90,7 @@ impl UserManager { let weak_user_manager = Arc::downgrade(&user_manager); if let Ok(user_service) = user_manager.cloud_services.get_user_service() { if let Some(mut rx) = user_service.subscribe_user_update() { - af_spawn(async move { + tokio::spawn(async move { while let Some(update) = rx.recv().await { if let Some(user_manager) = weak_user_manager.upgrade() { if let Err(err) = user_manager.handler_user_update(update).await { @@ -184,7 +183,7 @@ impl UserManager { event!(tracing::Level::DEBUG, "Listen token state change"); let user_uid = user.uid; let local_token = user.token.clone(); - af_spawn(async move { + tokio::spawn(async move { while let Some(token_state) = token_state_rx.next().await { debug!("Token state changed: {:?}", token_state); match token_state { @@ -678,7 +677,7 @@ impl UserManager { params: UpdateUserProfileParams, ) -> Result<(), FlowyError> { let server = self.cloud_services.get_user_service()?; - af_spawn(async move { + tokio::spawn(async move { let credentials = UserCredentials::new(Some(token), Some(uid), None); server.update_user(credentials, params).await }) diff --git a/frontend/rust-lib/flowy-user/src/user_manager/manager_user_workspace.rs b/frontend/rust-lib/flowy-user/src/user_manager/manager_user_workspace.rs index a51fea59c1..98a8cf5657 100644 --- a/frontend/rust-lib/flowy-user/src/user_manager/manager_user_workspace.rs +++ b/frontend/rust-lib/flowy-user/src/user_manager/manager_user_workspace.rs @@ -17,7 +17,6 @@ use flowy_user_pub::entities::{ Role, UpdateUserProfileParams, UserWorkspace, WorkspaceInvitation, WorkspaceInvitationStatus, WorkspaceMember, }; -use lib_dispatch::prelude::af_spawn; use crate::entities::{ RepeatedUserWorkspacePB, ResetWorkspacePB, SubscribeWorkspacePB, SuccessWorkspaceSubscriptionPB, @@ -395,7 +394,7 @@ impl UserManager { if let Ok(service) = self.cloud_services.get_user_service() { if let Ok(pool) = self.db_pool(uid) { - af_spawn(async move { + tokio::spawn(async move { if let Ok(new_user_workspaces) = service.get_all_workspace(uid).await { if let Ok(conn) = pool.get() { let _ = save_all_user_workspaces(uid, conn, &new_user_workspaces); diff --git a/frontend/rust-lib/lib-dispatch/src/dispatcher.rs b/frontend/rust-lib/lib-dispatch/src/dispatcher.rs index 4746014d14..0e8e84fa6b 100644 --- a/frontend/rust-lib/lib-dispatch/src/dispatcher.rs +++ b/frontend/rust-lib/lib-dispatch/src/dispatcher.rs @@ -60,15 +60,6 @@ pub type BoxFutureCallback = pub type BoxFutureCallback = Box AFBoxFuture<'static, ()> + Send + Sync + 'static>; -#[track_caller] -pub fn af_spawn(future: T) -> tokio::task::JoinHandle -where - T: Future + Send + 'static, - T::Output: Send + 'static, -{ - tokio::spawn(future) -} - pub struct AFPluginDispatcher { plugins: AFPluginMap, #[allow(dead_code)] diff --git a/frontend/rust-lib/lib-dispatch/src/module/module.rs b/frontend/rust-lib/lib-dispatch/src/module/module.rs index 54f8babad9..80fe281f97 100644 --- a/frontend/rust-lib/lib-dispatch/src/module/module.rs +++ b/frontend/rust-lib/lib-dispatch/src/module/module.rs @@ -89,7 +89,7 @@ impl AFPlugin { } pub fn name(mut self, s: &str) -> Self { - self.name = s.to_owned(); + s.clone_into(&mut self.name); self } diff --git a/frontend/rust-lib/lib-log/src/lib.rs b/frontend/rust-lib/lib-log/src/lib.rs index a956368883..ee5bcd8f89 100644 --- a/frontend/rust-lib/lib-log/src/lib.rs +++ b/frontend/rust-lib/lib-log/src/lib.rs @@ -57,7 +57,7 @@ impl Builder { } pub fn env_filter(mut self, env_filter: &str) -> Self { - self.env_filter = env_filter.to_owned(); + env_filter.clone_into(&mut self.env_filter); self } diff --git a/frontend/rust-lib/rust-toolchain.toml b/frontend/rust-lib/rust-toolchain.toml index 6f14058b2e..51985806fc 100644 --- a/frontend/rust-lib/rust-toolchain.toml +++ b/frontend/rust-lib/rust-toolchain.toml @@ -1,2 +1,2 @@ [toolchain] -channel = "1.77.2" +channel = "1.78.0" From b5d5312c70463320d869710a752a5c7eba2a2fec Mon Sep 17 00:00:00 2001 From: "Nathan.fooo" <86001920+appflowy@users.noreply.github.com> Date: Sun, 8 Dec 2024 20:59:40 +0800 Subject: [PATCH 014/576] chore: remove self-hosted runner (#6944) --- .github/workflows/rust_ci.yaml | 70 -------------------------------- .github/workflows/tauri2_ci.yaml | 27 ------------ 2 files changed, 97 deletions(-) diff --git a/.github/workflows/rust_ci.yaml b/.github/workflows/rust_ci.yaml index 5299c047cc..a67fd9d6ef 100644 --- a/.github/workflows/rust_ci.yaml +++ b/.github/workflows/rust_ci.yaml @@ -22,77 +22,7 @@ env: RUST_TOOLCHAIN: "1.80.1" jobs: - self-hosted-job: - if: github.event.pull_request.head.repo.full_name == github.repository - runs-on: self-hosted - steps: - - name: Checkout source code - uses: actions/checkout@v4 - - - name: Checkout Appflowy Cloud - uses: actions/checkout@v4 - with: - repository: AppFlowy-IO/AppFlowy-Cloud - path: AppFlowy-Cloud - - - name: Prepare Appflowy Cloud env - working-directory: AppFlowy-Cloud - run: | - cp deploy.env .env - sed -i '' 's|RUST_LOG=.*|RUST_LOG=trace|' .env - sed -i '' 's|API_EXTERNAL_URL=.*|API_EXTERNAL_URL=http://localhost|' .env - sed -i '' 's|APPFLOWY_AI_OPENAI_API_KEY=.*|APPFLOWY_AI_OPENAI_API_KEY=${{ secrets.CI_OPENAI_API_KEY }}|' .env - - - name: Ensure AppFlowy-Cloud is Running with Correct Version - working-directory: AppFlowy-Cloud - env: - APPFLOWY_CLOUD_VERSION: ${{ env.CLOUD_VERSION }} - APPFLOWY_HISTORY_VERSION: ${{ env.CLOUD_VERSION }} - APPFLOWY_WORKER_VERSION: ${{ env.CLOUD_VERSION }} - run: | - container_id=$(docker ps --filter name=appflowy-cloud-appflowy_cloud-1 -q) - if [ -z "$container_id" ]; then - echo "AppFlowy-Cloud container is not running. Pulling and starting the container..." - docker compose pull - docker compose up -d - echo "Waiting for the container to be ready..." - sleep 10 - else - running_image=$(docker inspect --format='{{index .Config.Image}}' "$container_id") - if [ "$running_image" != "appflowy-cloud:$APPFLOWY_CLOUD_VERSION" ]; then - echo "AppFlowy-Cloud is running with an incorrect version. Pulling the correct version..." - docker compose pull - docker compose up -d - echo "Waiting for the container to be ready..." - sleep 10 - docker ps -a - docker compose logs - else - echo "AppFlowy-Cloud is running with the correct version." - fi - fi - - - name: Run rust-lib tests - working-directory: frontend/rust-lib - env: - RUST_LOG: info - RUST_BACKTRACE: 1 - af_cloud_test_base_url: http://localhost - af_cloud_test_ws_url: ws://localhost/ws/v1 - af_cloud_test_gotrue_url: http://localhost/gotrue - run: | - DISABLE_CI_TEST_LOG="true" cargo test --no-default-features --features="dart" - - - name: rustfmt rust-lib - run: cargo fmt --all -- --check - working-directory: frontend/rust-lib/ - - - name: clippy rust-lib - run: cargo clippy --all-targets -- -D warnings - working-directory: frontend/rust-lib - ubuntu-job: - if: github.event.pull_request.head.repo.full_name != github.repository runs-on: ubuntu-latest steps: - name: Set timezone for action diff --git a/.github/workflows/tauri2_ci.yaml b/.github/workflows/tauri2_ci.yaml index aeefaa5fe6..b4b9183d01 100644 --- a/.github/workflows/tauri2_ci.yaml +++ b/.github/workflows/tauri2_ci.yaml @@ -21,34 +21,7 @@ concurrency: cancel-in-progress: true jobs: - # tauri-build-self-hosted: - # if: github.event.pull_request.head.repo.full_name == github.repository - # runs-on: self-hosted - # - # steps: - # - uses: actions/checkout@v4 - # - name: install frontend dependencies - # working-directory: frontend/appflowy_web_app - # run: | - # mkdir dist - # pnpm install - # cd src-tauri && cargo build - # - # - name: test and lint - # working-directory: frontend/appflowy_web_app - # run: | - # pnpm run lint:tauri - # - # - uses: tauri-apps/tauri-action@v0 - # env: - # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - # with: - # tauriScript: pnpm tauri - # projectPath: frontend/appflowy_web_app - # args: "--debug" - tauri-build-ubuntu: - #if: github.event.pull_request.head.repo.full_name != github.repository runs-on: ubuntu-20.04 steps: From 3b568872670cc5c0d7327f6af337ec770f4ee67e Mon Sep 17 00:00:00 2001 From: Richard Shiue <71320345+richardshiue@users.noreply.github.com> Date: Mon, 9 Dec 2024 08:48:38 +0800 Subject: [PATCH 015/576] feat(flutter_desktop): AI chat refer to UI (#6930) * chore: code cleanup * feat(desktop): implement ai chat side panel * chore: set min width for right side panel --- .../application/chat_side_panel_bloc.dart | 75 --- .../lib/plugins/ai_chat/chat.dart | 2 +- .../lib/plugins/ai_chat/chat_page.dart | 211 ++++----- .../ai_chat/presentation/chat_side_panel.dart | 50 -- .../workspace/application/tabs/tabs_bloc.dart | 25 + .../presentation/home/home_stack.dart | 430 ++++++++++++++++-- .../presentation/home/tabs/tabs_manager.dart | 89 ++-- 7 files changed, 550 insertions(+), 332 deletions(-) delete mode 100644 frontend/appflowy_flutter/lib/plugins/ai_chat/application/chat_side_panel_bloc.dart delete mode 100644 frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_side_panel.dart diff --git a/frontend/appflowy_flutter/lib/plugins/ai_chat/application/chat_side_panel_bloc.dart b/frontend/appflowy_flutter/lib/plugins/ai_chat/application/chat_side_panel_bloc.dart deleted file mode 100644 index 18786c4165..0000000000 --- a/frontend/appflowy_flutter/lib/plugins/ai_chat/application/chat_side_panel_bloc.dart +++ /dev/null @@ -1,75 +0,0 @@ -import 'package:appflowy/plugins/ai_chat/application/chat_entity.dart'; -import 'package:appflowy/workspace/application/view/view_service.dart'; -import 'package:appflowy_backend/log.dart'; -import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart'; -import 'package:appflowy_result/appflowy_result.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:freezed_annotation/freezed_annotation.dart'; - -part 'chat_side_panel_bloc.freezed.dart'; - -class ChatSidePanelBloc extends Bloc { - ChatSidePanelBloc({ - required this.chatId, - }) : super(const ChatSidePanelState()) { - on( - (event, emit) async { - await event.when( - selectedMetadata: (ChatMessageRefSource metadata) async { - emit( - state.copyWith( - metadata: metadata, - indicator: const ChatSidePanelIndicator.loading(), - ), - ); - await ViewBackendService.getView(metadata.id).fold( - (view) { - if (!isClosed) { - add(ChatSidePanelEvent.open(view)); - } - }, - (err) => Log.error("Failed to get view: $err"), - ); - }, - close: () { - emit(state.copyWith(metadata: null, isShowPanel: false)); - }, - open: (ViewPB view) { - emit( - state.copyWith( - indicator: ChatSidePanelIndicator.ready(view), - isShowPanel: true, - ), - ); - }, - ); - }, - ); - } - - final String chatId; -} - -@freezed -class ChatSidePanelEvent with _$ChatSidePanelEvent { - const factory ChatSidePanelEvent.selectedMetadata( - ChatMessageRefSource metadata, - ) = _SelectedMetadata; - const factory ChatSidePanelEvent.close() = _Close; - const factory ChatSidePanelEvent.open(ViewPB view) = _Open; -} - -@freezed -class ChatSidePanelState with _$ChatSidePanelState { - const factory ChatSidePanelState({ - ChatMessageRefSource? metadata, - @Default(ChatSidePanelIndicator.loading()) ChatSidePanelIndicator indicator, - @Default(false) bool isShowPanel, - }) = _ChatSidePanelState; -} - -@freezed -class ChatSidePanelIndicator with _$ChatSidePanelIndicator { - const factory ChatSidePanelIndicator.ready(ViewPB view) = _Ready; - const factory ChatSidePanelIndicator.loading() = _Loading; -} diff --git a/frontend/appflowy_flutter/lib/plugins/ai_chat/chat.dart b/frontend/appflowy_flutter/lib/plugins/ai_chat/chat.dart index 568f31b714..72e6b8256a 100644 --- a/frontend/appflowy_flutter/lib/plugins/ai_chat/chat.dart +++ b/frontend/appflowy_flutter/lib/plugins/ai_chat/chat.dart @@ -110,7 +110,7 @@ class AIChatPagePluginWidgetBuilder extends PluginWidgetBuilder return const SizedBox(); } - return BlocProvider.value( + return BlocProvider.value( value: bloc, child: AIChatPage( userProfile: context.userProfile!, diff --git a/frontend/appflowy_flutter/lib/plugins/ai_chat/chat_page.dart b/frontend/appflowy_flutter/lib/plugins/ai_chat/chat_page.dart index 144c378d70..9c6ccc688d 100644 --- a/frontend/appflowy_flutter/lib/plugins/ai_chat/chat_page.dart +++ b/frontend/appflowy_flutter/lib/plugins/ai_chat/chat_page.dart @@ -6,9 +6,14 @@ import 'package:appflowy/plugins/ai_chat/application/chat_entity.dart'; import 'package:appflowy/plugins/ai_chat/application/ai_prompt_input_bloc.dart'; import 'package:appflowy/plugins/ai_chat/application/chat_message_stream.dart'; import 'package:appflowy/plugins/ai_chat/presentation/chat_related_question.dart'; +import 'package:appflowy/startup/startup.dart'; +import 'package:appflowy/workspace/application/tabs/tabs_bloc.dart'; +import 'package:appflowy/workspace/application/view/view_ext.dart'; +import 'package:appflowy/workspace/application/view/view_service.dart'; import 'package:appflowy_backend/log.dart'; import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart'; import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart'; +import 'package:appflowy_result/appflowy_result.dart'; import 'package:desktop_drop/desktop_drop.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; @@ -18,16 +23,13 @@ import 'package:flutter_chat_core/flutter_chat_core.dart'; import 'package:flutter_chat_ui/flutter_chat_ui.dart' hide ChatAnimatedListReversed; import 'package:string_validator/string_validator.dart'; -import 'package:styled_widget/styled_widget.dart'; import 'package:universal_platform/universal_platform.dart'; import 'package:url_launcher/url_launcher.dart'; import 'application/chat_member_bloc.dart'; -import 'application/chat_side_panel_bloc.dart'; import 'presentation/animated_chat_list.dart'; import 'presentation/chat_input/desktop_ai_prompt_input.dart'; import 'presentation/chat_input/mobile_ai_prompt_input.dart'; -import 'presentation/chat_side_panel.dart'; import 'presentation/chat_welcome_page.dart'; import 'presentation/layout_define.dart'; import 'presentation/message/ai_text_message.dart'; @@ -70,7 +72,6 @@ class AIChatPage extends StatelessWidget { /// [AIPromptInputBloc] is used to handle the user prompt BlocProvider(create: (_) => AIPromptInputBloc()), - BlocProvider(create: (_) => ChatSidePanelBloc(chatId: view.id)), BlocProvider(create: (_) => ChatMemberBloc()), ], child: Builder( @@ -107,110 +108,50 @@ class _ChatContentPage extends StatelessWidget { @override Widget build(BuildContext context) { - if (UniversalPlatform.isDesktop) { - return BlocSelector( - selector: (state) => state.isShowPanel, - builder: (context, isShowPanel) { - return LayoutBuilder( - builder: (BuildContext context, BoxConstraints constraints) { - final sidePanelRatio = isShowPanel ? 0.33 : 0.0; - final chatWidth = constraints.maxWidth * (1 - sidePanelRatio); - final sidePanelWidth = - constraints.maxWidth * sidePanelRatio - 1.0; - - return Row( - children: [ - Center( - child: buildChatWidget(context) - .constrained( - maxWidth: 784, - ) - .padding(horizontal: 60) - .animate( - const Duration(milliseconds: 200), - Curves.easeOut, + return Center( + child: Container( + constraints: const BoxConstraints(maxWidth: 784), + margin: UniversalPlatform.isDesktop + ? const EdgeInsets.symmetric(horizontal: 60.0) + : null, + child: ScrollConfiguration( + behavior: ScrollConfiguration.of(context).copyWith(scrollbars: false), + child: BlocBuilder( + builder: (context, state) { + return state.loadingState.when( + loading: () { + return const Center( + child: CircularProgressIndicator.adaptive(), + ); + }, + finish: (_) { + final chatController = + context.read().chatController; + return Column( + children: [ + Expanded( + child: Chat( + chatController: chatController, + user: User(id: userProfile.id.toString()), + darkTheme: ChatTheme.fromThemeData(Theme.of(context)), + theme: ChatTheme.fromThemeData(Theme.of(context)), + builders: Builders( + inputBuilder: (_) => const SizedBox.shrink(), + textMessageBuilder: _buildTextMessage, + chatMessageBuilder: _buildChatMessage, + scrollToBottomBuilder: _buildScrollToBottom, + chatAnimatedListBuilder: _buildChatAnimatedList, + ), ), - ).constrained(width: chatWidth), - if (isShowPanel) ...[ - const VerticalDivider( - width: 1.0, - thickness: 1.0, - ), - buildChatSidePanel() - .constrained(width: sidePanelWidth) - .animate( - const Duration(milliseconds: 200), - Curves.easeOut, - ), - ], - ], - ); - }, - ); - }, - ); - } else { - return Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Flexible( - child: ConstrainedBox( - constraints: const BoxConstraints(maxWidth: 784), - child: buildChatWidget(context), - ), - ), - ], - ); - } - } - - Widget buildChatSidePanel() { - return BlocBuilder( - builder: (context, state) { - if (state.metadata == null) { - return const SizedBox.shrink(); - } - return const ChatSidePanel(); - }, - ); - } - - Widget buildChatWidget(BuildContext context) { - return ScrollConfiguration( - behavior: ScrollConfiguration.of(context).copyWith(scrollbars: false), - child: BlocBuilder( - builder: (context, state) { - return state.loadingState.when( - loading: () { - return const Center( - child: CircularProgressIndicator.adaptive(), - ); - }, - finish: (_) { - final chatController = context.read().chatController; - return Column( - children: [ - Expanded( - child: Chat( - chatController: chatController, - user: User(id: userProfile.id.toString()), - darkTheme: ChatTheme.fromThemeData(Theme.of(context)), - theme: ChatTheme.fromThemeData(Theme.of(context)), - builders: Builders( - inputBuilder: (_) => const SizedBox.shrink(), - textMessageBuilder: _buildTextMessage, - chatMessageBuilder: _buildChatMessage, - scrollToBottomBuilder: _buildScrollToBottom, - chatAnimatedListBuilder: _buildChatAnimatedList, ), - ), - ), - _buildInput(context), - ], + _buildInput(context), + ], + ); + }, ); }, - ); - }, + ), + ), ), ); } @@ -278,27 +219,7 @@ class _ChatContentPage extends StatelessWidget { chatId: view.id, refSourceJsonString: refSourceJsonString, isLastMessage: isLastMessage, - onSelectedMetadata: (metadata) async { - if (isURL(metadata.name)) { - late Uri uri; - - try { - uri = Uri.parse(metadata.name); - // `Uri` identifies `localhost` as a scheme - if (!uri.hasScheme || uri.scheme == 'localhost') { - uri = Uri.parse("http://${metadata.name}"); - await InternetAddress.lookup(uri.host); - } - await launchUrl(uri); - } catch (err) { - Log.error("failed to open url $err"); - } - } else { - context - .read() - .add(ChatSidePanelEvent.selectedMetadata(metadata)); - } - }, + onSelectedMetadata: _onSelectMetadata, ); }, ); @@ -375,9 +296,9 @@ class _ChatContentPage extends StatelessWidget { ); }, isStreaming: !canSendMessage, - onStopStreaming: () { - context.read().add(const ChatEvent.stopStream()); - }, + onStopStreaming: () => context + .read() + .add(const ChatEvent.stopStream()), ) : MobileAIPromptInput( chatId: view.id, @@ -390,12 +311,40 @@ class _ChatContentPage extends StatelessWidget { ); }, isStreaming: !canSendMessage, - onStopStreaming: () { - context.read().add(const ChatEvent.stopStream()); - }, + onStopStreaming: () => context + .read() + .add(const ChatEvent.stopStream()), ); }, ), ); } + + void _onSelectMetadata(ChatMessageRefSource metadata) async { + if (isURL(metadata.name)) { + late Uri uri; + try { + uri = Uri.parse(metadata.name); + // `Uri` identifies `localhost` as a scheme + if (!uri.hasScheme || uri.scheme == 'localhost') { + uri = Uri.parse("http://${metadata.name}"); + await InternetAddress.lookup(uri.host); + } + await launchUrl(uri); + } catch (err) { + Log.error("failed to open url $err"); + } + } else { + await ViewBackendService.getView(metadata.id).fold( + (sidebarView) { + getIt().add( + TabsEvent.openSecondaryPlugin( + plugin: sidebarView.plugin(), + ), + ); + }, + (err) => Log.error("Failed to get view: $err"), + ); + } + } } diff --git a/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_side_panel.dart b/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_side_panel.dart deleted file mode 100644 index 7904b44437..0000000000 --- a/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_side_panel.dart +++ /dev/null @@ -1,50 +0,0 @@ -import 'package:appflowy/generated/flowy_svgs.g.dart'; -import 'package:appflowy/plugins/ai_chat/application/chat_side_panel_bloc.dart'; -import 'package:appflowy/startup/plugin/plugin.dart'; -import 'package:appflowy/workspace/application/view/view_ext.dart'; -import 'package:flowy_infra_ui/flowy_infra_ui.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; - -class ChatSidePanel extends StatelessWidget { - const ChatSidePanel({super.key}); - - @override - Widget build(BuildContext context) { - return BlocBuilder( - builder: (context, state) { - return state.indicator.when( - loading: () { - return const CircularProgressIndicator.adaptive(); - }, - ready: (view) { - final plugin = view.plugin(); - plugin.init(); - - final pluginContext = PluginContext(); - final child = plugin.widgetBuilder - .buildWidget(context: pluginContext, shrinkWrap: false); - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.all(8.0), - child: FlowyIconButton( - icon: const FlowySvg(FlowySvgs.show_menu_s), - onPressed: () { - context - .read() - .add(const ChatSidePanelEvent.close()); - }, - ), - ), - const VSpace(6), - Expanded(child: child), - ], - ); - }, - ); - }, - ); - } -} diff --git a/frontend/appflowy_flutter/lib/workspace/application/tabs/tabs_bloc.dart b/frontend/appflowy_flutter/lib/workspace/application/tabs/tabs_bloc.dart index d9f5571f91..a437c9747c 100644 --- a/frontend/appflowy_flutter/lib/workspace/application/tabs/tabs_bloc.dart +++ b/frontend/appflowy_flutter/lib/workspace/application/tabs/tabs_bloc.dart @@ -57,10 +57,12 @@ class TabsBloc extends Bloc { _setLatestOpenView(); }, openTab: (Plugin plugin, ViewPB view) { + state.currentPageManager.hideSecondaryPlugin(); emit(state.openView(plugin)); _setLatestOpenView(view); }, openPlugin: (Plugin plugin, ViewPB? view, bool setLatest) { + state.currentPageManager.hideSecondaryPlugin(); emit(state.openPlugin(plugin: plugin, setLatest: setLatest)); if (setLatest) { _setLatestOpenView(view); @@ -151,6 +153,23 @@ class TabsBloc extends Bloc { ); } }, + openSecondaryPlugin: (plugin, view) { + state.currentPageManager.setSecondaryPlugin(plugin); + }, + closeSecondaryPlugin: () { + final pageManager = state.currentPageManager; + pageManager.hideSecondaryPlugin(); + }, + expandSecondaryPlugin: () { + final pageManager = state.currentPageManager; + pageManager.setPlugin( + pageManager.secondaryNotifier.plugin, + true, + false, + ); + pageManager.hideSecondaryPlugin(); + _setLatestOpenView(); + }, switchWorkspace: (workspaceId) { final pluginId = state.currentPageManager.plugin.id; @@ -237,6 +256,12 @@ class TabsEvent with _$TabsEvent { ViewPB? view, @Default(true) bool setLatest, }) = _OpenPlugin; + const factory TabsEvent.openSecondaryPlugin({ + required Plugin plugin, + ViewPB? view, + }) = _OpenSecondaryPlugin; + const factory TabsEvent.closeSecondaryPlugin() = _CloseSecondaryPlugin; + const factory TabsEvent.expandSecondaryPlugin() = _ExpandSecondaryPlugin; const factory TabsEvent.switchWorkspace(String workspaceId) = _SwitchWorkspace; } diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/home/home_stack.dart b/frontend/appflowy_flutter/lib/workspace/presentation/home/home_stack.dart index 0e63792faa..4261865e5e 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/home/home_stack.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/home/home_stack.dart @@ -1,4 +1,6 @@ +import 'dart:async'; import 'dart:io'; +import 'dart:math'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; @@ -25,6 +27,7 @@ import 'package:flowy_infra_ui/style_widget/hover.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:provider/provider.dart'; import 'package:time/time.dart'; +import 'package:universal_platform/universal_platform.dart'; import 'home_layout.dart'; @@ -58,13 +61,9 @@ class _HomeStackState extends State { return BlocProvider.value( value: getIt(), child: BlocBuilder( - buildWhen: (prev, curr) => - prev.currentIndex != curr.currentIndex && - prev.currentPageManager.plugin.id != - curr.currentPageManager.plugin.id, builder: (context, state) => Column( children: [ - if (Platform.isWindows) + if (UniversalPlatform.isWindows) Column( mainAxisSize: MainAxisSize.min, children: [ @@ -92,17 +91,32 @@ class _HomeStackState extends State { index: selectedIndex, children: state.pageManagers .map( - (pm) => Column( - children: [ - pm.stackTopBar(layout: widget.layout), - Expanded( - child: PageStack( - pageManager: pm, - delegate: widget.delegate, - userProfile: widget.userProfile, - ), - ), - ], + (pm) => LayoutBuilder( + builder: (context, constraints) { + return Row( + children: [ + Expanded( + child: Column( + children: [ + pm.stackTopBar(layout: widget.layout), + Expanded( + child: PageStack( + pageManager: pm, + delegate: widget.delegate, + userProfile: widget.userProfile, + ), + ), + ], + ), + ), + SecondaryView( + pageManager: pm, + adaptedPercentageWidth: + constraints.maxWidth * 3 / 7, + ), + ], + ); + }, ), ) .toList(), @@ -195,6 +209,241 @@ class _PageStackState extends State bool get wantKeepAlive => true; } +class SecondaryView extends StatefulWidget { + const SecondaryView({ + super.key, + required this.pageManager, + required this.adaptedPercentageWidth, + }); + + final PageManager pageManager; + final double adaptedPercentageWidth; + + @override + State createState() => _SecondaryViewState(); +} + +class _SecondaryViewState extends State + with SingleTickerProviderStateMixin, AutomaticKeepAliveClientMixin { + late final ValueNotifier widthNotifier; + + late final AnimationController animationController; + late Animation widthAnimation; + late final Animation offsetAnimation; + + CurvedAnimation get curveAnimation => CurvedAnimation( + parent: animationController, + curve: Curves.easeOut, + ); + + @override + void initState() { + super.initState(); + widget.pageManager.showSecondaryPluginNotifier + .addListener(onShowSecondaryChanged); + final width = widget.pageManager.showSecondaryPluginNotifier.value + ? max(450.0, widget.adaptedPercentageWidth) + : 0.0; + widthNotifier = ValueNotifier(width) + ..addListener(updateWidthAnimation); + + animationController = AnimationController( + duration: const Duration(milliseconds: 300), + vsync: this, + ); + + widthAnimation = Tween( + begin: 0.0, + end: width, + ).animate(curveAnimation); + offsetAnimation = Tween( + begin: const Offset(1.0, 0.0), + end: Offset.zero, + ).animate(curveAnimation); + } + + @override + void dispose() { + widget.pageManager.showSecondaryPluginNotifier + .removeListener(onShowSecondaryChanged); + widthNotifier.dispose(); + super.dispose(); + } + + void onShowSecondaryChanged() async { + if (widget.pageManager.showSecondaryPluginNotifier.value) { + widthNotifier.value = max(450.0, widget.adaptedPercentageWidth); + updateWidthAnimation(); + await animationController.forward(); + } else { + updateWidthAnimation(); + await animationController.reverse(); + setState(() => widthNotifier.value = 0.0); + } + } + + void updateWidthAnimation() { + widthAnimation = Tween( + begin: 0.0, + end: widthNotifier.value, + ).animate(curveAnimation); + } + + @override + Widget build(BuildContext context) { + super.build(context); + return Container( + color: Theme.of(context).colorScheme.surface, + child: FocusTraversalGroup( + child: ValueListenableBuilder( + valueListenable: widthNotifier, + builder: (context, value, child) { + return AnimatedBuilder( + animation: Listenable.merge([ + widthAnimation, + offsetAnimation, + ]), + builder: (context, child) { + return Container( + width: widthAnimation.value, + alignment: Alignment( + offsetAnimation.value.dx, + offsetAnimation.value.dy, + ), + child: OverflowBox( + alignment: AlignmentDirectional.centerStart, + maxWidth: value, + child: SecondaryViewResizer( + pageManager: widget.pageManager, + notifier: widthNotifier, + child: Column( + children: [ + widget.pageManager.stackSecondaryTopBar(value), + Expanded( + child: + widget.pageManager.stackSecondaryWidget(value), + ), + ], + ), + ), + ), + ); + }, + ); + }, + ), + ), + ); + } + + @override + bool get wantKeepAlive => true; +} + +class SecondaryViewResizer extends StatefulWidget { + const SecondaryViewResizer({ + super.key, + required this.pageManager, + required this.notifier, + required this.child, + }); + + final PageManager pageManager; + final ValueNotifier notifier; + final Widget child; + + @override + State createState() => _SecondaryViewResizerState(); +} + +class _SecondaryViewResizerState extends State { + final overlayController = OverlayPortalController(); + final layerLink = LayerLink(); + + bool isHover = false; + bool isDragging = false; + Timer? showHoverTimer; + + @override + void initState() { + super.initState(); + overlayController.show(); + } + + @override + Widget build(BuildContext context) { + return OverlayPortal( + controller: overlayController, + overlayChildBuilder: (context) { + return CompositedTransformFollower( + showWhenUnlinked: false, + link: layerLink, + targetAnchor: Alignment.center, + followerAnchor: Alignment.center, + child: Center( + child: MouseRegion( + cursor: SystemMouseCursors.resizeLeftRight, + onEnter: (_) { + showHoverTimer = Timer(const Duration(milliseconds: 500), () { + setState(() => isHover = true); + }); + }, + onExit: (_) { + showHoverTimer?.cancel(); + setState(() => isHover = false); + }, + child: GestureDetector( + behavior: HitTestBehavior.opaque, + onHorizontalDragStart: (_) => setState(() => isDragging = true), + onHorizontalDragUpdate: (details) { + final newWidth = MediaQuery.sizeOf(context).width - + details.globalPosition.dx; + if (newWidth >= 450.0) { + widget.notifier.value = newWidth; + } + }, + onHorizontalDragEnd: (_) => setState(() => isDragging = false), + child: TweenAnimationBuilder( + tween: ColorTween( + end: isHover || isDragging + ? Theme.of(context).colorScheme.primary + : Colors.transparent, + ), + duration: const Duration(milliseconds: 100), + builder: (context, color, child) { + return SizedBox( + width: 11, + child: Center( + child: Container( + color: color, + width: 2, + ), + ), + ); + }, + ), + ), + ), + ), + ); + }, + child: Row( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + CompositedTransformTarget( + link: layerLink, + child: Container( + width: 1, + color: Theme.of(context).dividerColor, + ), + ), + Flexible(child: widget.child), + ], + ), + ); + } +} + class FadingIndexedStack extends StatefulWidget { const FadingIndexedStack({ super.key, @@ -267,8 +516,9 @@ class PageNotifier extends ChangeNotifier { /// This is the only place where the plugin is set. /// No need compare the old plugin with the new plugin. Just set it. void setPlugin(Plugin newPlugin, bool setLatest) { - _plugin.dispose(); - newPlugin.init(); + if (newPlugin.id != plugin.id) { + _plugin.dispose(); + } // Set the plugin view as the latest view. if (setLatest) { @@ -287,24 +537,37 @@ class PageManager { PageManager(); final PageNotifier _notifier = PageNotifier(); + final PageNotifier _secondaryNotifier = PageNotifier(); PageNotifier get notifier => _notifier; + PageNotifier get secondaryNotifier => _secondaryNotifier; bool isPinned = false; - Widget title() { - return _notifier.plugin.widgetBuilder.leftBarItem; - } + final showSecondaryPluginNotifier = ValueNotifier(false); Plugin get plugin => _notifier.plugin; - void setPlugin(Plugin newPlugin, bool setLatest) { + void setPlugin(Plugin newPlugin, bool setLatest, [bool init = true]) { + if (init) { + newPlugin.init(); + } _notifier.setPlugin(newPlugin, setLatest); } + void setSecondaryPlugin(Plugin newPlugin) { + newPlugin.init(); + _secondaryNotifier.setPlugin(newPlugin, false); + showSecondaryPluginNotifier.value = true; + } + + void hideSecondaryPlugin() { + showSecondaryPluginNotifier.value = false; + } + Widget stackTopBar({required HomeLayout layout}) { - return MultiProvider( - providers: [ChangeNotifierProvider.value(value: _notifier)], + return ChangeNotifierProvider.value( + value: _notifier, child: Selector( selector: (context, notifier) => notifier.titleWidget, builder: (_, __, child) => MoveWindowDetector( @@ -318,10 +581,10 @@ class PageManager { required UserProfilePB userProfile, required Function(ViewPB, int?) onDeleted, }) { - return MultiProvider( - providers: [ChangeNotifierProvider.value(value: _notifier)], - child: Consumer( - builder: (_, PageNotifier notifier, __) { + return ChangeNotifierProvider.value( + value: _notifier, + child: Consumer( + builder: (_, notifier, __) { return FadingIndexedStack( index: getIt().indexOf(notifier.plugin.pluginType), children: getIt().supportPluginTypes.map( @@ -351,8 +614,59 @@ class PageManager { ); } + Widget stackSecondaryWidget(double width) { + return ValueListenableBuilder( + valueListenable: showSecondaryPluginNotifier, + builder: (context, value, child) { + if (width == 0.0) { + return const SizedBox.shrink(); + } + + return child!; + }, + child: ChangeNotifierProvider.value( + value: _secondaryNotifier, + child: Selector( + selector: (context, notifier) => notifier.plugin.widgetBuilder, + builder: (_, widgetBuilder, __) { + return widgetBuilder.buildWidget( + context: PluginContext(), + shrinkWrap: false, + ); + }, + ), + ), + ); + } + + Widget stackSecondaryTopBar(double width) { + return ValueListenableBuilder( + valueListenable: showSecondaryPluginNotifier, + builder: (context, value, child) { + if (width == 0.0) { + return const SizedBox.shrink(); + } + + return child!; + }, + child: ChangeNotifierProvider.value( + value: _secondaryNotifier, + child: Selector( + selector: (context, notifier) => notifier.plugin.widgetBuilder, + builder: (_, widgetBuilder, __) { + return const MoveWindowDetector( + child: HomeSecondaryTopBar(), + ); + }, + ), + ), + ); + } + void dispose() { _notifier.dispose(); + _secondaryNotifier.dispose(); + showSecondaryPluginNotifier.dispose(); } } @@ -403,3 +717,63 @@ class _HomeTopBarState extends State @override bool get wantKeepAlive => true; } + +class HomeSecondaryTopBar extends StatelessWidget { + const HomeSecondaryTopBar({super.key}); + + @override + Widget build(BuildContext context) { + return Container( + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surface, + ), + height: HomeSizes.topBarHeight + HomeInsets.topBarTitleVerticalPadding, + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: HomeInsets.topBarTitleHorizontalPadding, + vertical: HomeInsets.topBarTitleVerticalPadding, + ), + child: Row( + children: [ + FlowyIconButton( + width: 24, + radius: const BorderRadius.all(Radius.circular(8.0)), + icon: const FlowySvg( + FlowySvgs.show_menu_s, + size: Size.square(16), + ), + onPressed: () { + getIt().add(const TabsEvent.closeSecondaryPlugin()); + }, + ), + const HSpace(8.0), + FlowyIconButton( + width: 24, + radius: const BorderRadius.all(Radius.circular(8.0)), + icon: const FlowySvg( + FlowySvgs.full_view_s, + size: Size.square(16), + ), + onPressed: () { + getIt().add(const TabsEvent.expandSecondaryPlugin()); + }, + ), + Expanded( + child: Align( + alignment: AlignmentDirectional.centerEnd, + child: ChangeNotifierProvider.value( + value: Provider.of(context, listen: false), + child: Consumer( + builder: (_, PageNotifier notifier, __) => + notifier.plugin.widgetBuilder.rightBarItem ?? + const SizedBox.shrink(), + ), + ), + ), + ), + ], + ), + ), + ); + } +} diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/home/tabs/tabs_manager.dart b/frontend/appflowy_flutter/lib/workspace/presentation/home/tabs/tabs_manager.dart index 08d9a478c1..38ede2421e 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/home/tabs/tabs_manager.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/home/tabs/tabs_manager.dart @@ -13,55 +13,50 @@ class TabsManager extends StatelessWidget { @override Widget build(BuildContext context) { - return BlocProvider.value( - value: context.read(), - child: BlocListener( - listenWhen: (prev, curr) => - prev.currentIndex != curr.currentIndex || prev.pages != curr.pages, - listener: (context, state) => onIndexChanged(state.currentIndex), - child: BlocBuilder( - builder: (context, state) { - if (state.pages == 1) { - return const SizedBox.shrink(); - } + return BlocConsumer( + listenWhen: (prev, curr) => + prev.currentIndex != curr.currentIndex || prev.pages != curr.pages, + listener: (context, state) => onIndexChanged(state.currentIndex), + builder: (context, state) { + if (state.pages == 1) { + return const SizedBox.shrink(); + } - final isAllPinned = state.isAllPinned; + final isAllPinned = state.isAllPinned; - return Container( - alignment: Alignment.bottomLeft, - height: HomeSizes.tabBarHeight, - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.surfaceContainerHighest, - ), - child: MoveWindowDetector( - child: Row( - children: state.pageManagers.map((pm) { - return Flexible( - child: ConstrainedBox( - constraints: const BoxConstraints( - maxWidth: HomeSizes.tabBarWidth, - ), - child: FlowyTab( - key: ValueKey('tab-${pm.plugin.id}'), - pageManager: pm, - isCurrent: state.currentPageManager == pm, - isAllPinned: isAllPinned, - onTap: () { - if (state.currentPageManager != pm) { - final index = state.pageManagers.indexOf(pm); - onIndexChanged(index); - } - }, - ), - ), - ); - }).toList(), - ), - ), - ); - }, - ), - ), + return Container( + alignment: Alignment.bottomLeft, + height: HomeSizes.tabBarHeight, + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surfaceContainerHighest, + ), + child: MoveWindowDetector( + child: Row( + children: state.pageManagers.map((pm) { + return Flexible( + child: ConstrainedBox( + constraints: const BoxConstraints( + maxWidth: HomeSizes.tabBarWidth, + ), + child: FlowyTab( + key: ValueKey('tab-${pm.plugin.id}'), + pageManager: pm, + isCurrent: state.currentPageManager == pm, + isAllPinned: isAllPinned, + onTap: () { + if (state.currentPageManager != pm) { + final index = state.pageManagers.indexOf(pm); + onIndexChanged(index); + } + }, + ), + ), + ); + }).toList(), + ), + ), + ); + }, ); } } From e0885e2567a5f5b1350c322ede3039ccc6b6f8ec Mon Sep 17 00:00:00 2001 From: Lucas Date: Mon, 9 Dec 2024 09:17:07 +0800 Subject: [PATCH 016/576] fix: image tests (#6928) --- .../document_with_image_block_test.dart | 39 +++++++++++++------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/frontend/appflowy_flutter/integration_test/desktop/document/document_with_image_block_test.dart b/frontend/appflowy_flutter/integration_test/desktop/document/document_with_image_block_test.dart index c6fb6f7293..382a9d9134 100644 --- a/frontend/appflowy_flutter/integration_test/desktop/document/document_with_image_block_test.dart +++ b/frontend/appflowy_flutter/integration_test/desktop/document/document_with_image_block_test.dart @@ -24,13 +24,6 @@ import 'package:run_with_network_images/run_with_network_images.dart'; import '../../shared/mock/mock_file_picker.dart'; import '../../shared/util.dart'; -const _testImageUrls = [ - 'https://images.unsplash.com/photo-1469474968028-56623f02e42e?ixlib=rb-4.0.3&q=85&fm=jpg&crop=entropy&cs=srgb&dl=david-marcu-78A265wPiO4-unsplash.jpg&w=640', - 'https://www.easygifanimator.net/images/samples/eglite.gif', - 'https://people.math.sc.edu/Burkardt/data/bmp/snail.bmp', - 'https://file-examples.com/storage/fe9566cb7d67345489a5a97/2017/10/file_example_JPG_100kB.jpg', -]; - void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); TestWidgetsFlutterBinding.ensureInitialized(); @@ -138,11 +131,33 @@ void main() { file.deleteSync(); }); - for (final url in _testImageUrls) { - testWidgets('insert an image from network: $url', (tester) async { - await testEmbedImage(tester, url); - }); - } + testWidgets('insert a gif image from network', (tester) async { + await testEmbedImage( + tester, + 'https://www.easygifanimator.net/images/samples/sparkles.gif', + ); + }); + + testWidgets('insert a jpg image from network', (tester) async { + await testEmbedImage( + tester, + 'https://file-examples.com/storage/fe9566cb7d67345489a5a97/2017/10/file_example_JPG_100kB.jpg', + ); + }); + + testWidgets('insert a bmp image from network', (tester) async { + await testEmbedImage( + tester, + 'https://people.math.sc.edu/Burkardt/data/bmp/snail.bmp', + ); + }); + + testWidgets('insert a jpg image from network', (tester) async { + await testEmbedImage( + tester, + 'https://images.unsplash.com/photo-1469474968028-56623f02e42e?ixlib=rb-4.0.3&q=85&fm=jpg&crop=entropy&cs=srgb&dl=david-marcu-78A265wPiO4-unsplash.jpg&w=640', + ); + }); testWidgets('insert an image from unsplash', (tester) async { await runWithNetworkImages(() async { From f5e46967ec284f6f9ac7bc3ac90ac2e6c1680e49 Mon Sep 17 00:00:00 2001 From: Richard Shiue <71320345+richardshiue@users.noreply.github.com> Date: Mon, 9 Dec 2024 14:40:34 +0800 Subject: [PATCH 017/576] fix(flutter): implement mention date transaction handler (#6933) * fix: implement mention date transaction handler * test: add integration tests * chore: code cleanup * chore: early return if null delta --- .../document_with_date_reminder_test.dart | 343 +++++++++++++++++- .../child_page_transaction_handler.dart | 3 +- .../mention/date_transaction_handler.dart | 263 ++++++++++++++ .../editor_plugins/mention/mention_block.dart | 1 - .../sub_page_transaction_handler.dart | 1 + .../editor_transaction_handler.dart | 1 + .../editor_transaction_service.dart | 21 +- .../mention_transaction_handler.dart | 8 +- 8 files changed, 627 insertions(+), 14 deletions(-) create mode 100644 frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mention/date_transaction_handler.dart diff --git a/frontend/appflowy_flutter/integration_test/desktop/document/document_with_date_reminder_test.dart b/frontend/appflowy_flutter/integration_test/desktop/document/document_with_date_reminder_test.dart index c7fea448f7..ccfdbae76e 100644 --- a/frontend/appflowy_flutter/integration_test/desktop/document/document_with_date_reminder_test.dart +++ b/frontend/appflowy_flutter/integration_test/desktop/document/document_with_date_reminder_test.dart @@ -1,14 +1,21 @@ +import 'dart:io'; + import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/mention/mention_date_block.dart'; +import 'package:appflowy/startup/startup.dart'; +import 'package:appflowy/user/application/reminder/reminder_bloc.dart'; import 'package:appflowy/workspace/application/settings/date_time/date_format_ext.dart'; import 'package:appflowy/workspace/presentation/widgets/date_picker/desktop_date_picker.dart'; import 'package:appflowy/workspace/presentation/widgets/toggle/toggle.dart'; import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart'; +import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; +import 'package:table_calendar/table_calendar.dart'; import '../../shared/util.dart'; @@ -18,7 +25,7 @@ void main() { TestWidgetsFlutterBinding.ensureInitialized(); }); - group('date or reminder block in document', () { + group('date or reminder block in document:', () { testWidgets("insert date with time block", (tester) async { await tester.initializeAppFlowy(); await tester.tapAnonymousSignInButton(); @@ -121,5 +128,339 @@ void main() { expect(find.text('@$formattedDate'), findsOneWidget); expect(find.byFlowySvg(FlowySvgs.reminder_clock_s), findsOneWidget); }); + + testWidgets("copy, cut and paste a date mention", (tester) async { + await tester.initializeAppFlowy(); + await tester.tapAnonymousSignInButton(); + + // create a new document + await tester.createNewPageWithNameUnderParent( + name: 'copy, cut and paste a date mention', + ); + + // tap the first line of the document + await tester.editor.tapLineOfEditorAt(0); + await tester.editor.showSlashMenu(); + await tester.editor.tapSlashMenuItemWithName( + LocaleKeys.document_slashMenu_name_dateOrReminder.tr(), + ); + + final dateTimeSettings = DateTimeSettingsPB( + dateFormat: UserDateFormatPB.Friendly, + timeFormat: UserTimeFormatPB.TwentyFourHour, + ); + final DateTime currentDateTime = DateTime.now(); + final String formattedDate = + dateTimeSettings.dateFormat.formatDate(currentDateTime, false); + + // get current date in editor + expect(find.byType(MentionDateBlock), findsOneWidget); + expect(find.text('@$formattedDate'), findsOneWidget); + + // update selection and copy + await tester.editor.updateSelection( + Selection( + start: Position(path: [0]), + end: Position(path: [0], offset: 1), + ), + ); + await tester.simulateKeyEvent( + LogicalKeyboardKey.keyC, + isControlPressed: Platform.isLinux || Platform.isWindows, + isMetaPressed: Platform.isMacOS, + ); + await tester.pumpAndSettle(); + + // update selection and paste + await tester.editor.updateSelection( + Selection.collapsed(Position(path: [0], offset: 1)), + ); + await tester.simulateKeyEvent( + LogicalKeyboardKey.keyV, + isControlPressed: Platform.isLinux || Platform.isWindows, + isMetaPressed: Platform.isMacOS, + ); + await tester.pumpAndSettle(); + + expect(find.byType(MentionDateBlock), findsNWidgets(2)); + expect(find.text('@$formattedDate'), findsNWidgets(2)); + + // update selection and cut + await tester.editor.updateSelection( + Selection( + start: Position(path: [0], offset: 1), + end: Position(path: [0], offset: 2), + ), + ); + await tester.simulateKeyEvent( + LogicalKeyboardKey.keyX, + isControlPressed: Platform.isLinux || Platform.isWindows, + isMetaPressed: Platform.isMacOS, + ); + await tester.pumpAndSettle(); + + expect(find.byType(MentionDateBlock), findsOneWidget); + expect(find.text('@$formattedDate'), findsOneWidget); + + // update selection and paste + await tester.editor.updateSelection( + Selection.collapsed(Position(path: [0], offset: 1)), + ); + await tester.simulateKeyEvent( + LogicalKeyboardKey.keyV, + isControlPressed: Platform.isLinux || Platform.isWindows, + isMetaPressed: Platform.isMacOS, + ); + await tester.pumpAndSettle(); + + expect(find.byType(MentionDateBlock), findsNWidgets(2)); + expect(find.text('@$formattedDate'), findsNWidgets(2)); + }); + + testWidgets("copy, cut and paste a reminder mention", (tester) async { + await tester.initializeAppFlowy(); + await tester.tapAnonymousSignInButton(); + + // create a new document + await tester.createNewPageWithNameUnderParent( + name: 'copy, cut and paste a reminder mention', + ); + + // tap the first line of the document + await tester.editor.tapLineOfEditorAt(0); + await tester.editor.showSlashMenu(); + await tester.editor.tapSlashMenuItemWithName( + LocaleKeys.document_slashMenu_name_dateOrReminder.tr(), + ); + + // trigger popup + await tester.tapButton(find.byType(MentionDateBlock)); + await tester.pumpAndSettle(); + + // set date to be fifteenth of the next month + await tester.tap( + find.descendant( + of: find.byType(DesktopAppFlowyDatePicker), + matching: find.byFlowySvg(FlowySvgs.arrow_right_s), + ), + ); + await tester.pumpAndSettle(); + await tester.tap( + find.descendant( + of: find.byType(TableCalendar), + matching: find.text(15.toString()), + ), + ); + await tester.pumpAndSettle(); + await tester.simulateKeyEvent(LogicalKeyboardKey.escape); + await tester.pumpAndSettle(); + + // add a reminder + await tester.tap(find.byType(MentionDateBlock)); + await tester.pumpAndSettle(); + await tester.tap(find.text(LocaleKeys.datePicker_reminderLabel.tr())); + await tester.pumpAndSettle(); + await tester.tap( + find.textContaining( + LocaleKeys.datePicker_reminderOptions_oneDayBefore.tr(), + ), + ); + await tester.pumpAndSettle(); + await tester.simulateKeyEvent(LogicalKeyboardKey.escape); + await tester.pumpAndSettle(); + + // verify + final dateTimeSettings = DateTimeSettingsPB( + dateFormat: UserDateFormatPB.Friendly, + timeFormat: UserTimeFormatPB.TwentyFourHour, + ); + final now = DateTime.now(); + final fifteenthOfNextMonth = DateTime(now.year, now.month + 1, 15); + final formattedDate = + dateTimeSettings.dateFormat.formatDate(fifteenthOfNextMonth, false); + + expect(find.byType(MentionDateBlock), findsOneWidget); + expect(find.text('@$formattedDate'), findsOneWidget); + expect(find.byFlowySvg(FlowySvgs.reminder_clock_s), findsOneWidget); + expect(getIt().state.reminders.map((e) => e.id).length, 1); + + // update selection and copy + await tester.editor.updateSelection( + Selection( + start: Position(path: [0]), + end: Position(path: [0], offset: 1), + ), + ); + await tester.simulateKeyEvent( + LogicalKeyboardKey.keyC, + isControlPressed: Platform.isLinux || Platform.isWindows, + isMetaPressed: Platform.isMacOS, + ); + await tester.pumpAndSettle(); + + // update selection and paste + await tester.editor.updateSelection( + Selection.collapsed(Position(path: [0], offset: 1)), + ); + await tester.simulateKeyEvent( + LogicalKeyboardKey.keyV, + isControlPressed: Platform.isLinux || Platform.isWindows, + isMetaPressed: Platform.isMacOS, + ); + await tester.pumpAndSettle(); + + expect(find.byType(MentionDateBlock), findsNWidgets(2)); + expect(find.text('@$formattedDate'), findsNWidgets(2)); + expect(find.byFlowySvg(FlowySvgs.reminder_clock_s), findsNWidgets(2)); + expect( + getIt().state.reminders.map((e) => e.id).toSet().length, + 2, + ); + + // update selection and cut + await tester.editor.updateSelection( + Selection( + start: Position(path: [0], offset: 1), + end: Position(path: [0], offset: 2), + ), + ); + await tester.simulateKeyEvent( + LogicalKeyboardKey.keyX, + isControlPressed: Platform.isLinux || Platform.isWindows, + isMetaPressed: Platform.isMacOS, + ); + await tester.pumpAndSettle(); + + expect(find.byType(MentionDateBlock), findsOneWidget); + expect(find.text('@$formattedDate'), findsOneWidget); + expect(find.byFlowySvg(FlowySvgs.reminder_clock_s), findsOneWidget); + expect(getIt().state.reminders.map((e) => e.id).length, 1); + + // update selection and paste + await tester.editor.updateSelection( + Selection.collapsed(Position(path: [0], offset: 1)), + ); + await tester.simulateKeyEvent( + LogicalKeyboardKey.keyV, + isControlPressed: Platform.isLinux || Platform.isWindows, + isMetaPressed: Platform.isMacOS, + ); + await tester.pumpAndSettle(); + + expect(find.byType(MentionDateBlock), findsNWidgets(2)); + expect(find.text('@$formattedDate'), findsNWidgets(2)); + expect(find.byType(MentionDateBlock), findsNWidgets(2)); + expect(find.text('@$formattedDate'), findsNWidgets(2)); + expect(find.byFlowySvg(FlowySvgs.reminder_clock_s), findsNWidgets(2)); + expect( + getIt().state.reminders.map((e) => e.id).toSet().length, + 2, + ); + }); + + testWidgets("delete, undo and redo a reminder mention", (tester) async { + await tester.initializeAppFlowy(); + await tester.tapAnonymousSignInButton(); + + // create a new document + await tester.createNewPageWithNameUnderParent( + name: 'delete, undo and redo a reminder mention', + ); + + // tap the first line of the document + await tester.editor.tapLineOfEditorAt(0); + await tester.editor.showSlashMenu(); + await tester.editor.tapSlashMenuItemWithName( + LocaleKeys.document_slashMenu_name_dateOrReminder.tr(), + ); + + // trigger popup + await tester.tapButton(find.byType(MentionDateBlock)); + await tester.pumpAndSettle(); + + // set date to be fifteenth of the next month + await tester.tap( + find.descendant( + of: find.byType(DesktopAppFlowyDatePicker), + matching: find.byFlowySvg(FlowySvgs.arrow_right_s), + ), + ); + await tester.pumpAndSettle(); + await tester.tap( + find.descendant( + of: find.byType(TableCalendar), + matching: find.text(15.toString()), + ), + ); + await tester.pumpAndSettle(); + await tester.simulateKeyEvent(LogicalKeyboardKey.escape); + await tester.pumpAndSettle(); + + // add a reminder + await tester.tap(find.byType(MentionDateBlock)); + await tester.pumpAndSettle(); + await tester.tap(find.text(LocaleKeys.datePicker_reminderLabel.tr())); + await tester.pumpAndSettle(); + await tester.tap( + find.textContaining( + LocaleKeys.datePicker_reminderOptions_oneDayBefore.tr(), + ), + ); + await tester.pumpAndSettle(); + await tester.simulateKeyEvent(LogicalKeyboardKey.escape); + await tester.pumpAndSettle(); + + // verify + final dateTimeSettings = DateTimeSettingsPB( + dateFormat: UserDateFormatPB.Friendly, + timeFormat: UserTimeFormatPB.TwentyFourHour, + ); + final now = DateTime.now(); + final fifteenthOfNextMonth = DateTime(now.year, now.month + 1, 15); + final formattedDate = + dateTimeSettings.dateFormat.formatDate(fifteenthOfNextMonth, false); + + expect(find.byType(MentionDateBlock), findsOneWidget); + expect(find.text('@$formattedDate'), findsOneWidget); + expect(find.byFlowySvg(FlowySvgs.reminder_clock_s), findsOneWidget); + expect(getIt().state.reminders.map((e) => e.id).length, 1); + + // update selection and backspace to delete the mention + await tester.editor.updateSelection( + Selection.collapsed(Position(path: [0], offset: 1)), + ); + await tester.simulateKeyEvent(LogicalKeyboardKey.backspace); + await tester.pumpAndSettle(); + + expect(find.byType(MentionDateBlock), findsNothing); + expect(find.text('@$formattedDate'), findsNothing); + expect(find.byFlowySvg(FlowySvgs.reminder_clock_s), findsNothing); + expect(getIt().state.reminders.isEmpty, isTrue); + + // undo + await tester.simulateKeyEvent( + LogicalKeyboardKey.keyZ, + isControlPressed: Platform.isWindows || Platform.isLinux, + isMetaPressed: Platform.isMacOS, + ); + + expect(find.byType(MentionDateBlock), findsOneWidget); + expect(find.text('@$formattedDate'), findsOneWidget); + expect(find.byFlowySvg(FlowySvgs.reminder_clock_s), findsOneWidget); + expect(getIt().state.reminders.map((e) => e.id).length, 1); + + // redo + await tester.simulateKeyEvent( + LogicalKeyboardKey.keyZ, + isControlPressed: Platform.isWindows || Platform.isLinux, + isMetaPressed: Platform.isMacOS, + isShiftPressed: true, + ); + + expect(find.byType(MentionDateBlock), findsNothing); + expect(find.text('@$formattedDate'), findsNothing); + expect(find.byFlowySvg(FlowySvgs.reminder_clock_s), findsNothing); + expect(getIt().state.reminders.isEmpty, isTrue); + }); }); } diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mention/child_page_transaction_handler.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mention/child_page_transaction_handler.dart index 491bf10fa5..ef32ad1098 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mention/child_page_transaction_handler.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mention/child_page_transaction_handler.dart @@ -16,11 +16,12 @@ import '../transaction_handler/mention_transaction_handler.dart'; const _pasteIdentifier = 'child_page_transaction'; class ChildPageTransactionHandler extends MentionTransactionHandler { - ChildPageTransactionHandler() : super(subType: MentionType.childPage.name); + ChildPageTransactionHandler(); @override Future onTransaction( BuildContext context, + String viewId, EditorState editorState, List added, List removed, { diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mention/date_transaction_handler.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mention/date_transaction_handler.dart new file mode 100644 index 0000000000..972ed229dd --- /dev/null +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mention/date_transaction_handler.dart @@ -0,0 +1,263 @@ +import 'package:appflowy/shared/clipboard_state.dart'; +import 'package:appflowy/startup/startup.dart'; +import 'package:appflowy/user/application/reminder/reminder_bloc.dart'; +import 'package:appflowy/user/application/reminder/reminder_extension.dart'; +import 'package:appflowy/workspace/presentation/widgets/date_picker/widgets/reminder_selector.dart'; +import 'package:appflowy_backend/log.dart'; +import 'package:appflowy_editor/appflowy_editor.dart'; +import 'package:collection/collection.dart'; +import 'package:fixnum/fixnum.dart'; +import 'package:flutter/material.dart'; +import 'package:nanoid/nanoid.dart'; +import 'package:provider/provider.dart'; + +import '../plugins.dart'; +import '../transaction_handler/mention_transaction_handler.dart'; + +const _pasteIdentifier = 'date_transaction'; + +class DateTransactionHandler extends MentionTransactionHandler { + DateTransactionHandler(); + + @override + Future onTransaction( + BuildContext context, + String viewId, + EditorState editorState, + List added, + List removed, { + bool isCut = false, + bool isUndoRedo = false, + bool isPaste = false, + bool isDraggingNode = false, + bool isTurnInto = false, + String? parentViewId, + }) async { + if (isDraggingNode || isTurnInto) { + return; + } + + // Remove the mentions that were both added and removed in the same transaction. + // These were just moved around. + final moved = []; + for (final mention in added) { + if (removed.any((r) => r.$2 == mention.$2)) { + moved.add(mention); + } + } + + for (final mention in removed) { + if (!context.mounted || moved.any((m) => m.$2 == mention.$2)) { + return; + } + + if (mention.$2[MentionBlockKeys.type] != MentionType.date.name) { + continue; + } + + _handleDeletion(context, mention); + } + + if (isPaste || isUndoRedo) { + if (context.mounted) { + context.read().startHandlingPaste(_pasteIdentifier); + } + + for (final mention in added) { + if (!context.mounted || moved.any((m) => m.$2 == mention.$2)) { + return; + } + + if (mention.$2[MentionBlockKeys.type] != MentionType.date.name) { + continue; + } + + _handleAddition( + context, + viewId, + editorState, + mention, + isPaste, + isCut, + ); + } + + if (context.mounted) { + context.read().endHandlingPaste(_pasteIdentifier); + } + } + } + + void _handleDeletion( + BuildContext context, + MentionBlockData data, + ) { + final reminderId = data.$2[MentionBlockKeys.reminderId]; + + if (reminderId case String _ when reminderId.isNotEmpty) { + getIt().add(ReminderEvent.remove(reminderId: reminderId)); + } + } + + void _handleAddition( + BuildContext context, + String viewId, + EditorState editorState, + MentionBlockData data, + bool isPaste, + bool isCut, + ) { + final dateData = _MentionDateBlockData.fromData(data.$2); + if (dateData.dateString.isEmpty) { + Log.error("mention date block doesn't have a valid date string"); + return; + } + + if (isPaste && !isCut) { + _handlePasteFromCopy( + context, + viewId, + editorState, + data.$1, + data.$3, + dateData, + ); + } else { + _handlePasteFromCut(viewId, data.$1, dateData); + } + } + + void _handlePasteFromCut( + String viewId, + Node node, + _MentionDateBlockData data, + ) { + final dateTime = DateTime.tryParse(data.dateString); + + if (data.reminderId == null || dateTime == null) { + return; + } + + getIt().add( + ReminderEvent.addById( + reminderId: data.reminderId!, + objectId: viewId, + scheduledAt: Int64( + data.reminderOption + .getNotificationDateTime(dateTime) + .millisecondsSinceEpoch ~/ + 1000, + ), + meta: { + ReminderMetaKeys.includeTime: data.includeTime.toString(), + ReminderMetaKeys.blockId: node.id, + }, + ), + ); + } + + void _handlePasteFromCopy( + BuildContext context, + String viewId, + EditorState editorState, + Node node, + int index, + _MentionDateBlockData data, + ) async { + final dateTime = DateTime.tryParse(data.dateString); + + if (node.delta == null) { + return; + } + + if (data.reminderId == null || dateTime == null) { + return; + } + + final reminderId = nanoid(); + getIt().add( + ReminderEvent.addById( + reminderId: reminderId, + objectId: viewId, + scheduledAt: Int64( + data.reminderOption + .getNotificationDateTime(dateTime) + .millisecondsSinceEpoch ~/ + 1000, + ), + meta: { + ReminderMetaKeys.includeTime: data.includeTime.toString(), + ReminderMetaKeys.blockId: node.id, + }, + ), + ); + + final newMentionAttributes = { + MentionBlockKeys.mention: { + MentionBlockKeys.type: MentionType.date.name, + MentionBlockKeys.date: dateTime.toIso8601String(), + MentionBlockKeys.reminderId: reminderId, + MentionBlockKeys.includeTime: data.includeTime, + MentionBlockKeys.reminderOption: data.reminderOption.name, + }, + }; + + // The index is the index of the delta, to get the index of the mention character + // in all the text, we need to calculate it based on the deltas before the current delta. + int mentionIndex = 0; + for (final (i, delta) in node.delta!.indexed) { + if (i >= index) { + break; + } + + mentionIndex += delta.length; + } + + // Required to prevent editing the same spot at the same time + await Future.delayed(const Duration(milliseconds: 100)); + + final transaction = editorState.transaction + ..formatText( + node, + mentionIndex, + MentionBlockKeys.mentionChar.length, + newMentionAttributes, + ); + + await editorState.apply( + transaction, + options: const ApplyOptions(recordUndo: false), + ); + } +} + +/// A helper class to parse and store the mention date block data +class _MentionDateBlockData { + _MentionDateBlockData.fromData(Map data) { + dateString = switch (data[MentionBlockKeys.date]) { + final String string when DateTime.tryParse(string) != null => string, + _ => "", + }; + includeTime = switch (data[MentionBlockKeys.includeTime]) { + final bool flag => flag, + _ => false, + }; + reminderOption = switch (data[MentionBlockKeys.reminderOption]) { + final String name => + ReminderOption.values.firstWhereOrNull((o) => o.name == name) ?? + ReminderOption.none, + _ => ReminderOption.none, + }; + reminderId = switch (data[MentionBlockKeys.reminderId]) { + final String id + when id.isNotEmpty && reminderOption != ReminderOption.none => + id, + _ => null, + }; + } + + late final String dateString; + late final bool includeTime; + late final String? reminderId; + late final ReminderOption reminderOption; +} diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mention/mention_block.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mention/mention_block.dart index cf2f0b8e11..a5bc340f71 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mention/mention_block.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mention/mention_block.dart @@ -8,7 +8,6 @@ import 'package:provider/provider.dart'; enum MentionType { page, - reminder, date, childPage; diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/sub_page/sub_page_transaction_handler.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/sub_page/sub_page_transaction_handler.dart index b7d784d534..c5c7398bdb 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/sub_page/sub_page_transaction_handler.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/sub_page/sub_page_transaction_handler.dart @@ -21,6 +21,7 @@ class SubPageTransactionHandler extends BlockTransactionHandler { @override Future onTransaction( BuildContext context, + String viewId, EditorState editorState, List added, List removed, { diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/transaction_handler/editor_transaction_handler.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/transaction_handler/editor_transaction_handler.dart index 334f649341..243532e8ce 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/transaction_handler/editor_transaction_handler.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/transaction_handler/editor_transaction_handler.dart @@ -25,6 +25,7 @@ abstract class EditorTransactionHandler { Future onTransaction( BuildContext context, + String viewId, EditorState editorState, List added, List removed, { diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/transaction_handler/editor_transaction_service.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/transaction_handler/editor_transaction_service.dart index 29cc7435ff..b56066ae8b 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/transaction_handler/editor_transaction_service.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/transaction_handler/editor_transaction_service.dart @@ -2,20 +2,23 @@ import 'dart:async'; import 'package:appflowy/plugins/document/presentation/editor_notification.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/mention/child_page_transaction_handler.dart'; +import 'package:appflowy/plugins/document/presentation/editor_plugins/mention/date_transaction_handler.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/sub_page/sub_page_transaction_handler.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/transaction_handler/editor_transaction_handler.dart'; -import 'package:appflowy/plugins/document/presentation/editor_plugins/transaction_handler/mention_transaction_handler.dart'; import 'package:appflowy/shared/clipboard_state.dart'; import 'package:appflowy/shared/feature_flags.dart'; import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'mention_transaction_handler.dart'; + final _transactionHandlers = [ if (FeatureFlag.inlineSubPageMention.isOn) ...[ SubPageTransactionHandler(), ChildPageTransactionHandler(), ], + DateTransactionHandler(), ]; /// Handles delegating transactions to appropriate handlers. @@ -148,10 +151,16 @@ class _EditorTransactionServiceState extends State { handler.type: handler.livesInDelta ? [] : [], }; + // based on the type of the transaction handler + final uniqueTransactionHandlers = {}; + for (final handler in _transactionHandlers) { + uniqueTransactionHandlers.putIfAbsent(handler.type, () => handler); + } + for (final op in transaction.operations) { if (op is InsertOperation) { for (final n in op.nodes) { - for (final handler in _transactionHandlers) { + for (final handler in uniqueTransactionHandlers.values) { if (handler.livesInDelta) { added[handler.type]! .addAll(extractMentionsForType(n, handler.type)); @@ -163,7 +172,7 @@ class _EditorTransactionServiceState extends State { } } else if (op is DeleteOperation) { for (final n in op.nodes) { - for (final handler in _transactionHandlers) { + for (final handler in uniqueTransactionHandlers.values) { if (handler.livesInDelta) { removed[handler.type]!.addAll( extractMentionsForType(n, handler.type, false), @@ -191,8 +200,9 @@ class _EditorTransactionServiceState extends State { final (add, del) = diffDeltas(deltaBefore, deltaAfter); + bool fetchedMentions = false; for (final handler in _transactionHandlers) { - if (!handler.livesInDelta) { + if (!handler.livesInDelta || fetchedMentions) { continue; } @@ -212,6 +222,8 @@ class _EditorTransactionServiceState extends State { removed[handler.type]!.addAll(mentionBlockDatas); } + + fetchedMentions = true; } } } @@ -226,6 +238,7 @@ class _EditorTransactionServiceState extends State { handler.onTransaction( context, + widget.viewId, widget.editorState, additions, removals, diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/transaction_handler/mention_transaction_handler.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/transaction_handler/mention_transaction_handler.dart index 632561589e..d08ce05510 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/transaction_handler/mention_transaction_handler.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/transaction_handler/mention_transaction_handler.dart @@ -12,12 +12,6 @@ typedef MentionBlockData = (Node, Map, int); abstract class MentionTransactionHandler extends EditorTransactionHandler { - const MentionTransactionHandler({ - required this.subType, - }) + const MentionTransactionHandler() : super(type: MentionBlockKeys.mention, livesInDelta: true); - - final String subType; - - MentionType get mentionType => MentionType.fromString(subType); } From d21c0c0dfccf666571661bb7b81e54dc791368c7 Mon Sep 17 00:00:00 2001 From: Ahad Patel <69256193+Ahad-patel@users.noreply.github.com> Date: Mon, 9 Dec 2024 12:43:20 +0530 Subject: [PATCH 018/576] feat: add same delete design in database (#6620) * add same delete design in database * fix: remove padding when widget is null or function is null --- .../database/widgets/field/field_editor.dart | 30 ++++++++++++------- .../lib/style_widget/button.dart | 17 +++++++---- 2 files changed, 30 insertions(+), 17 deletions(-) diff --git a/frontend/appflowy_flutter/lib/plugins/database/widgets/field/field_editor.dart b/frontend/appflowy_flutter/lib/plugins/database/widgets/field/field_editor.dart index d91f207797..4a9fedcd94 100644 --- a/frontend/appflowy_flutter/lib/plugins/database/widgets/field/field_editor.dart +++ b/frontend/appflowy_flutter/lib/plugins/database/widgets/field/field_editor.dart @@ -199,22 +199,30 @@ class FieldActionCell extends StatelessWidget { (action == FieldAction.duplicate || action == FieldAction.delete)) { enable = false; } - - return FlowyButton( + return FlowyIconTextButton( resetHoverOnRebuild: false, disable: !enable, - text: FlowyText( - action.title(fieldInfo), - lineHeight: 1.0, - color: enable ? null : Theme.of(context).disabledColor, - ), onHover: (_) => popoverMutex?.close(), onTap: () => action.run(context, viewId, fieldInfo), - leftIcon: action.leading( - fieldInfo, - enable ? null : Theme.of(context).disabledColor, + // show the error color when delete is hovered + textBuilder: (onHover) => FlowyText( + action.title(fieldInfo), + lineHeight: 1.0, + color: enable + ? action == FieldAction.delete && onHover + ? Theme.of(context).colorScheme.error + : null + : Theme.of(context).disabledColor, ), - rightIcon: action.trailing(context, fieldInfo), + leftIconBuilder: (onHover) => action.leading( + fieldInfo, + enable + ? action == FieldAction.delete && onHover + ? Theme.of(context).colorScheme.error + : null + : Theme.of(context).disabledColor, + ), + rightIconBuilder: (_) => action.trailing(context, fieldInfo), ); } } diff --git a/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/style_widget/button.dart b/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/style_widget/button.dart index e1b9308e32..0e5b656a35 100644 --- a/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/style_widget/button.dart +++ b/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/style_widget/button.dart @@ -13,8 +13,8 @@ class FlowyIconTextButton extends StatelessWidget { final VoidCallback? onSecondaryTap; final void Function(bool)? onHover; final EdgeInsets? margin; - final Widget Function(bool onHover)? leftIconBuilder; - final Widget Function(bool onHover)? rightIconBuilder; + final Widget? Function(bool onHover)? leftIconBuilder; + final Widget? Function(bool onHover)? rightIconBuilder; final Color? hoverColor; final bool isSelected; final BorderRadius? radius; @@ -29,6 +29,7 @@ class FlowyIconTextButton extends StatelessWidget { final double iconPadding; final bool expand; final Color? borderColor; + final bool resetHoverOnRebuild; const FlowyIconTextButton({ super.key, @@ -53,6 +54,7 @@ class FlowyIconTextButton extends StatelessWidget { this.iconPadding = 6, this.expand = false, this.borderColor, + this.resetHoverOnRebuild = true, }); @override @@ -64,6 +66,7 @@ class FlowyIconTextButton extends StatelessWidget { onTap: disable ? null : onTap, onSecondaryTap: disable ? null : onSecondaryTap, child: FlowyHover( + resetHoverOnRebuild: resetHoverOnRebuild, cursor: disable ? SystemMouseCursors.forbidden : SystemMouseCursors.click, style: HoverStyle( @@ -81,11 +84,12 @@ class FlowyIconTextButton extends StatelessWidget { Widget _render(BuildContext context, bool onHover) { final List children = []; - if (leftIconBuilder != null) { + final Widget? leftIcon = leftIconBuilder?.call(onHover); + if (leftIcon != null) { children.add( SizedBox.fromSize( size: leftIconSize, - child: leftIconBuilder!(onHover), + child: leftIcon, ), ); children.add(HSpace(iconPadding)); @@ -97,10 +101,11 @@ class FlowyIconTextButton extends StatelessWidget { children.add(textBuilder(onHover)); } - if (rightIconBuilder != null) { + final Widget? rightIcon = rightIconBuilder?.call(onHover); + if (rightIcon != null) { children.add(HSpace(iconPadding)); // No need to define the size of rightIcon. Just use its intrinsic width - children.add(rightIconBuilder!(onHover)); + children.add(rightIcon); } Widget child = Row( From 7b93bbe5ffacf8d9d3b905fde2e6d0a0df9a608a Mon Sep 17 00:00:00 2001 From: Richard Shiue <71320345+richardshiue@users.noreply.github.com> Date: Mon, 9 Dec 2024 15:13:37 +0800 Subject: [PATCH 019/576] =?UTF-8?q?fix(flutter=5Fdesktop):=20clicking=20on?= =?UTF-8?q?=20empty=20space=20when=20editing=20a=20cell=20sho=E2=80=A6=20(?= =?UTF-8?q?#6949)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(flutter_desktop): clicking on empty space when editing a cell shouldn't close event card * test: fix integration tests --- .../desktop/database/database_calendar_test.dart | 2 ++ .../cell/desktop_row_detail/desktop_row_detail_date_cell.dart | 1 + .../cell/desktop_row_detail/desktop_row_detail_media_cell.dart | 2 ++ .../desktop_row_detail/desktop_row_detail_relation_cell.dart | 1 + .../desktop_row_detail_select_option_cell.dart | 1 + 5 files changed, 7 insertions(+) diff --git a/frontend/appflowy_flutter/integration_test/desktop/database/database_calendar_test.dart b/frontend/appflowy_flutter/integration_test/desktop/database/database_calendar_test.dart index eb1d67ffcd..d6df648bb3 100644 --- a/frontend/appflowy_flutter/integration_test/desktop/database/database_calendar_test.dart +++ b/frontend/appflowy_flutter/integration_test/desktop/database/database_calendar_test.dart @@ -301,6 +301,7 @@ void main() { await tester.createOption(name: "qwer"); await tester.selectOption(name: "asdf"); await tester.dismissCellEditor(); + await tester.dismissCellEditor(); await tester.tapDatabaseFilterButton(); await tester.tapCreateFilterByFieldType(FieldType.MultiSelect, "Tags"); @@ -332,6 +333,7 @@ void main() { await tester.tapButton(finderForFieldType(FieldType.MultiSelect)); await tester.selectOption(name: "asdf"); await tester.dismissCellEditor(); + await tester.dismissCellEditor(); tester.assertNumberOfEventsInCalendar(0); diff --git a/frontend/appflowy_flutter/lib/plugins/database/widgets/cell/desktop_row_detail/desktop_row_detail_date_cell.dart b/frontend/appflowy_flutter/lib/plugins/database/widgets/cell/desktop_row_detail/desktop_row_detail_date_cell.dart index 9d0c0f0324..f10f9a9279 100644 --- a/frontend/appflowy_flutter/lib/plugins/database/widgets/cell/desktop_row_detail/desktop_row_detail_date_cell.dart +++ b/frontend/appflowy_flutter/lib/plugins/database/widgets/cell/desktop_row_detail/desktop_row_detail_date_cell.dart @@ -31,6 +31,7 @@ class DesktopRowDetailDateCellSkin extends IEditableDateCellSkin { direction: PopoverDirection.bottomWithLeftAligned, constraints: BoxConstraints.loose(const Size(260, 620)), margin: EdgeInsets.zero, + asBarrier: true, child: Container( alignment: AlignmentDirectional.centerStart, padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6), diff --git a/frontend/appflowy_flutter/lib/plugins/database/widgets/cell/desktop_row_detail/desktop_row_detail_media_cell.dart b/frontend/appflowy_flutter/lib/plugins/database/widgets/cell/desktop_row_detail/desktop_row_detail_media_cell.dart index 68c5191682..34d8a18ad8 100644 --- a/frontend/appflowy_flutter/lib/plugins/database/widgets/cell/desktop_row_detail/desktop_row_detail_media_cell.dart +++ b/frontend/appflowy_flutter/lib/plugins/database/widgets/cell/desktop_row_detail/desktop_row_detail_media_cell.dart @@ -252,6 +252,7 @@ class _AddFileButtonState extends State<_AddFileButton> { direction: widget.direction, constraints: const BoxConstraints(maxWidth: _menuWidth), margin: EdgeInsets.zero, + asBarrier: true, onClose: () => context.read().remove(_dropFileKey), popupBuilder: (_) { @@ -458,6 +459,7 @@ class _FilePreviewRenderState extends State<_FilePreviewRender> { offset: const Offset(0, 5), triggerActions: PopoverTriggerFlags.none, onClose: () => setState(() => isSelected = false), + asBarrier: true, popupBuilder: (popoverContext) => MultiBlocProvider( providers: [ BlocProvider.value(value: context.read()), diff --git a/frontend/appflowy_flutter/lib/plugins/database/widgets/cell/desktop_row_detail/desktop_row_detail_relation_cell.dart b/frontend/appflowy_flutter/lib/plugins/database/widgets/cell/desktop_row_detail/desktop_row_detail_relation_cell.dart index 996d04267c..b3096d49e4 100644 --- a/frontend/appflowy_flutter/lib/plugins/database/widgets/cell/desktop_row_detail/desktop_row_detail_relation_cell.dart +++ b/frontend/appflowy_flutter/lib/plugins/database/widgets/cell/desktop_row_detail/desktop_row_detail_relation_cell.dart @@ -24,6 +24,7 @@ class DesktopRowDetailRelationCellSkin extends IEditableRelationCellSkin { direction: PopoverDirection.bottomWithLeftAligned, constraints: const BoxConstraints(maxWidth: 400, maxHeight: 400), margin: EdgeInsets.zero, + asBarrier: true, onClose: () => cellContainerNotifier.isFocus = false, popupBuilder: (context) { return BlocProvider.value( diff --git a/frontend/appflowy_flutter/lib/plugins/database/widgets/cell/desktop_row_detail/desktop_row_detail_select_option_cell.dart b/frontend/appflowy_flutter/lib/plugins/database/widgets/cell/desktop_row_detail/desktop_row_detail_select_option_cell.dart index 6eab6438dc..3d41a824ce 100644 --- a/frontend/appflowy_flutter/lib/plugins/database/widgets/cell/desktop_row_detail/desktop_row_detail_select_option_cell.dart +++ b/frontend/appflowy_flutter/lib/plugins/database/widgets/cell/desktop_row_detail/desktop_row_detail_select_option_cell.dart @@ -25,6 +25,7 @@ class DesktopRowDetailSelectOptionCellSkin controller: popoverController, constraints: const BoxConstraints.tightFor(width: 300), margin: EdgeInsets.zero, + asBarrier: true, triggerActions: PopoverTriggerFlags.none, direction: PopoverDirection.bottomWithLeftAligned, onClose: () => cellContainerNotifier.isFocus = false, From 3fdd19f7a26a664629a6fddd3ebcbe6fc9720f22 Mon Sep 17 00:00:00 2001 From: nathan Date: Mon, 9 Dec 2024 15:17:11 +0800 Subject: [PATCH 020/576] chore: do not pull object when object ids is empty --- .../rust-lib/flowy-database2/src/manager.rs | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/frontend/rust-lib/flowy-database2/src/manager.rs b/frontend/rust-lib/flowy-database2/src/manager.rs index 8246da8a97..cf14295e4c 100644 --- a/frontend/rust-lib/flowy-database2/src/manager.rs +++ b/frontend/rust-lib/flowy-database2/src/manager.rs @@ -903,18 +903,21 @@ impl DatabaseCollabService for WorkspaceDatabaseCollabServiceImpl { encoded_collab_by_id.insert(k, v); } - // 2. Fetch remaining collabs from remote - let remote_collabs = self - .batch_get_encode_collab(object_ids, collab_type) - .await?; + if !object_ids.is_empty() { + // 2. Fetch remaining collabs from remote + let remote_collabs = self + .batch_get_encode_collab(object_ids, collab_type) + .await?; - trace!( - "[Database]: load {} database row from remote", - remote_collabs.len() - ); - for (k, v) in remote_collabs { - encoded_collab_by_id.insert(k, v); + trace!( + "[Database]: load {} database row from remote", + remote_collabs.len() + ); + for (k, v) in remote_collabs { + encoded_collab_by_id.insert(k, v); + } } + Ok(encoded_collab_by_id) } From 45b4eb4b3a683b7854865e264c12233abe14574e Mon Sep 17 00:00:00 2001 From: Lucas Date: Mon, 9 Dec 2024 16:19:37 +0800 Subject: [PATCH 021/576] fix: hotfix issues for v0.7.7 (#6948) * fix: include link preview block and file block in exported markdown * test: include link preview block and file block in exported markdown * chore: remove unused logs * chore: update editor version * fix: "+" menu should be close after pressing space * test: cancel inline page reference menu by space * chore: update editor version * chore: remove unused logs --- .../document_inline_sub_page_test.dart | 21 ++++++++++- .../base/page_reference_commands.dart | 30 +++++++++++++++ .../parsers/document_markdown_parsers.dart | 3 ++ .../parsers/file_block_node_parser.dart | 19 ++++++++++ .../parsers/link_preview_node_parser.dart | 18 +++++++++ .../parsers/markdown_parsers.dart | 6 +-- ...ser.dart => simple_table_node_parser.dart} | 0 .../presentation/editor_plugins/plugins.dart | 1 + .../inline_actions/inline_actions_menu.dart | 3 ++ .../widgets/inline_actions_handler.dart | 13 +++++-- .../lib/plugins/shared/share/share_bloc.dart | 19 +++++++++- .../lib/shared/markdown_to_document.dart | 2 + .../sidebar/billing/sidebar_plan_bloc.dart | 12 +++--- .../application/sidebar/space/space_bloc.dart | 9 +++-- frontend/appflowy_flutter/pubspec.lock | 4 +- frontend/appflowy_flutter/pubspec.yaml | 2 +- .../markdown/markdown_parser_test.dart | 37 +++++++++++++++++++ 17 files changed, 174 insertions(+), 25 deletions(-) create mode 100644 frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/file_block_node_parser.dart create mode 100644 frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/link_preview_node_parser.dart rename frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/{simple_table_parser.dart => simple_table_node_parser.dart} (100%) create mode 100644 frontend/appflowy_flutter/test/unit_test/markdown/markdown_parser_test.dart diff --git a/frontend/appflowy_flutter/integration_test/desktop/document/document_inline_sub_page_test.dart b/frontend/appflowy_flutter/integration_test/desktop/document/document_inline_sub_page_test.dart index ef9ef73b3c..30e115774a 100644 --- a/frontend/appflowy_flutter/integration_test/desktop/document/document_inline_sub_page_test.dart +++ b/frontend/appflowy_flutter/integration_test/desktop/document/document_inline_sub_page_test.dart @@ -2,12 +2,12 @@ import 'dart:io'; import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/mention/mention_page_block.dart'; +import 'package:appflowy/plugins/inline_actions/inline_actions_menu.dart'; import 'package:appflowy/workspace/presentation/home/menu/view/view_action_type.dart'; +import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart'; import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/services.dart'; - -import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; @@ -330,6 +330,23 @@ void main() { expect(find.text("$_createdPageName (copy)"), findsNWidgets(2)); expect(find.text("$_createdPageName (copy) (copy)"), findsOneWidget); }); + + testWidgets('Cancel inline page reference menu by space', (tester) async { + await tester.initializeAppFlowy(); + await tester.tapAnonymousSignInButton(); + await tester.createOpenRenameDocumentUnderParent(name: _firstDocName); + + await tester.editor.tapLineOfEditorAt(0); + await tester.editor.showPlusMenu(); + + // Cancel by space + await tester.simulateKeyEvent( + LogicalKeyboardKey.space, + ); + await tester.pumpAndSettle(); + + expect(find.byType(InlineActionsMenu), findsNothing); + }); }); } diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/base/page_reference_commands.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/base/page_reference_commands.dart index e42bb3a2bf..079a062837 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/base/page_reference_commands.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/base/page_reference_commands.dart @@ -117,6 +117,22 @@ Future inlinePageReferenceCommandHandler( initialResults: initialResults, style: style, startCharAmount: previousChar != null ? 2 : 1, + cancelBySpaceHandler: () { + if (character == _plusChar) { + final currentSelection = editorState.selection; + if (currentSelection == null) { + return false; + } + // check if the space is after the character + if (currentSelection.isCollapsed && + currentSelection.start.offset == + selection.start.offset + character.length) { + _cancelInlinePageReferenceMenu(editorState); + return true; + } + } + return false; + }, ); selectionMenuService?.show(); @@ -124,3 +140,17 @@ Future inlinePageReferenceCommandHandler( return true; } + +void _cancelInlinePageReferenceMenu(EditorState editorState) { + selectionMenuService?.dismiss(); + selectionMenuService = null; + + // re-focus the selection + final selection = editorState.selection; + if (selection != null) { + editorState.updateSelectionWithReason( + selection, + reason: SelectionUpdateReason.uiEvent, + ); + } +} diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/document_markdown_parsers.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/document_markdown_parsers.dart index 0b694f396e..3f2895d57e 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/document_markdown_parsers.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/document_markdown_parsers.dart @@ -1,4 +1,7 @@ export 'callout_node_parser.dart'; export 'custom_image_node_parser.dart'; +export 'file_block_node_parser.dart'; +export 'link_preview_node_parser.dart'; export 'math_equation_node_parser.dart'; +export 'simple_table_node_parser.dart'; export 'toggle_list_node_parser.dart'; diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/file_block_node_parser.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/file_block_node_parser.dart new file mode 100644 index 0000000000..e57ededcec --- /dev/null +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/file_block_node_parser.dart @@ -0,0 +1,19 @@ +import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart'; +import 'package:appflowy_editor/appflowy_editor.dart'; + +class FileBlockNodeParser extends NodeParser { + const FileBlockNodeParser(); + + @override + String get id => FileBlockKeys.type; + + @override + String transform(Node node, DocumentMarkdownEncoder? encoder) { + final name = node.attributes[FileBlockKeys.name]; + final url = node.attributes[FileBlockKeys.url]; + if (name == null || url == null) { + return ''; + } + return '[$name]($url)\n'; + } +} diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/link_preview_node_parser.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/link_preview_node_parser.dart new file mode 100644 index 0000000000..c7ce69d221 --- /dev/null +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/link_preview_node_parser.dart @@ -0,0 +1,18 @@ +import 'package:appflowy_editor/appflowy_editor.dart'; +import 'package:appflowy_editor_plugins/appflowy_editor_plugins.dart'; + +class LinkPreviewNodeParser extends NodeParser { + const LinkPreviewNodeParser(); + + @override + String get id => LinkPreviewBlockKeys.type; + + @override + String transform(Node node, DocumentMarkdownEncoder? encoder) { + final href = node.attributes[LinkPreviewBlockKeys.url]; + if (href == null) { + return ''; + } + return '[$href]($href)\n'; + } +} diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/markdown_parsers.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/markdown_parsers.dart index 568baeaac0..4ad7734643 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/markdown_parsers.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/markdown_parsers.dart @@ -1,6 +1,2 @@ -export 'callout_node_parser.dart'; -export 'custom_image_node_parser.dart'; export 'markdown_code_parser.dart'; -export 'math_equation_node_parser.dart'; -export 'toggle_list_node_parser.dart'; -export 'simple_table_parser.dart'; +export 'markdown_simple_table_parser.dart'; diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/simple_table_parser.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/simple_table_node_parser.dart similarity index 100% rename from frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/simple_table_parser.dart rename to frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/simple_table_node_parser.dart diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/plugins.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/plugins.dart index da28f7f720..4d1fd95558 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/plugins.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/plugins.dart @@ -57,6 +57,7 @@ export 'openai/widgets/ai_writer_block_component.dart'; export 'openai/widgets/ask_ai_block_component.dart'; export 'openai/widgets/ask_ai_toolbar_item.dart'; export 'outline/outline_block_component.dart'; +export 'parsers/document_markdown_parsers.dart'; export 'parsers/markdown_parsers.dart'; export 'parsers/markdown_simple_table_parser.dart'; export 'quote/quote_block_shortcuts.dart'; diff --git a/frontend/appflowy_flutter/lib/plugins/inline_actions/inline_actions_menu.dart b/frontend/appflowy_flutter/lib/plugins/inline_actions/inline_actions_menu.dart index e31d7c2ef0..dadc4ebf6f 100644 --- a/frontend/appflowy_flutter/lib/plugins/inline_actions/inline_actions_menu.dart +++ b/frontend/appflowy_flutter/lib/plugins/inline_actions/inline_actions_menu.dart @@ -19,12 +19,14 @@ class InlineActionsMenu extends InlineActionsMenuService { required this.initialResults, required this.style, this.startCharAmount = 1, + this.cancelBySpaceHandler, }); final BuildContext context; final EditorState editorState; final InlineActionsService service; final List initialResults; + final bool Function()? cancelBySpaceHandler; @override final InlineActionsMenuStyle style; @@ -137,6 +139,7 @@ class InlineActionsMenu extends InlineActionsMenuService { onSelectionUpdate: _onSelectionUpdate, style: style, startCharAmount: startCharAmount, + cancelBySpaceHandler: cancelBySpaceHandler, ), ), ), diff --git a/frontend/appflowy_flutter/lib/plugins/inline_actions/widgets/inline_actions_handler.dart b/frontend/appflowy_flutter/lib/plugins/inline_actions/widgets/inline_actions_handler.dart index edf34a3dc1..be9a6c2f5f 100644 --- a/frontend/appflowy_flutter/lib/plugins/inline_actions/widgets/inline_actions_handler.dart +++ b/frontend/appflowy_flutter/lib/plugins/inline_actions/widgets/inline_actions_handler.dart @@ -62,6 +62,7 @@ class InlineActionsHandler extends StatefulWidget { required this.onSelectionUpdate, required this.style, this.startCharAmount = 1, + this.cancelBySpaceHandler, }); final InlineActionsService service; @@ -72,6 +73,7 @@ class InlineActionsHandler extends StatefulWidget { final VoidCallback onSelectionUpdate; final InlineActionsMenuStyle style; final int startCharAmount; + final bool Function()? cancelBySpaceHandler; @override State createState() => _InlineActionsHandlerState(); @@ -288,12 +290,17 @@ class _InlineActionsHandlerState extends State { /// that the selection change occurred from the handler. widget.onSelectionUpdate(); + if (event.logicalKey == LogicalKeyboardKey.space) { + final cancelBySpaceHandler = widget.cancelBySpaceHandler; + if (cancelBySpaceHandler != null && cancelBySpaceHandler()) { + return KeyEventResult.handled; + } + } + // Interpolation to avoid having a getter for private variable _insertCharacter(event.character!); return KeyEventResult.handled; - } - - if (moveKeys.contains(event.logicalKey)) { + } else if (moveKeys.contains(event.logicalKey)) { _moveSelection(event.logicalKey); return KeyEventResult.handled; } diff --git a/frontend/appflowy_flutter/lib/plugins/shared/share/share_bloc.dart b/frontend/appflowy_flutter/lib/plugins/shared/share/share_bloc.dart index c533f67ed8..c42bbda5a0 100644 --- a/frontend/appflowy_flutter/lib/plugins/shared/share/share_bloc.dart +++ b/frontend/appflowy_flutter/lib/plugins/shared/share/share_bloc.dart @@ -197,8 +197,23 @@ class ShareBloc extends Bloc { (p) => false, ); - Log.info( - 'get publish info: $publishInfo for view: ${view.name}(${view.id})', + // skip the "Record not found" error, it's because the view is not published yet + publishInfo.fold( + (s) { + Log.info( + 'get publish info success: $publishInfo for view: ${view.name}(${view.id})', + ); + }, + (f) { + if (![ + ErrorCode.RecordNotFound, + ErrorCode.LocalVersionNotSupport, + ].contains(f.code)) { + Log.info( + 'get publish info failed: $f for view: ${view.name}(${view.id})', + ); + } + }, ); String workspaceId = state.workspaceId; diff --git a/frontend/appflowy_flutter/lib/shared/markdown_to_document.dart b/frontend/appflowy_flutter/lib/shared/markdown_to_document.dart index 328c66eca9..b3e2034602 100644 --- a/frontend/appflowy_flutter/lib/shared/markdown_to_document.dart +++ b/frontend/appflowy_flutter/lib/shared/markdown_to_document.dart @@ -20,6 +20,8 @@ String customDocumentToMarkdown(Document document) { const ToggleListNodeParser(), const CustomImageNodeParser(), const SimpleTableNodeParser(), + const LinkPreviewNodeParser(), + const FileBlockNodeParser(), ], ); } diff --git a/frontend/appflowy_flutter/lib/workspace/application/sidebar/billing/sidebar_plan_bloc.dart b/frontend/appflowy_flutter/lib/workspace/application/sidebar/billing/sidebar_plan_bloc.dart index 7af2a547dd..1737494530 100644 --- a/frontend/appflowy_flutter/lib/workspace/application/sidebar/billing/sidebar_plan_bloc.dart +++ b/frontend/appflowy_flutter/lib/workspace/application/sidebar/billing/sidebar_plan_bloc.dart @@ -12,6 +12,7 @@ import 'package:appflowy_backend/protobuf/flowy-user/user_profile.pb.dart'; import 'package:appflowy_backend/protobuf/flowy-user/workspace.pb.dart'; import 'package:bloc/bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; + part 'sidebar_plan_bloc.freezed.dart'; class SidebarPlanBloc extends Bloc { @@ -113,7 +114,8 @@ class SidebarPlanBloc extends Bloc { } else if (error.code == ErrorCode.SingleUploadLimitExceeded) { emit( state.copyWith( - tierIndicator: const SidebarToastTierIndicator.singleFileLimitHit(), + tierIndicator: + const SidebarToastTierIndicator.singleFileLimitHit(), ), ); } else { @@ -184,13 +186,10 @@ class SidebarPlanBloc extends Bloc { if (state.workspaceId != null) { final payload = UserWorkspaceIdPB(workspaceId: state.workspaceId!); UserEventGetWorkspaceUsage(payload).send().then((result) { - result.fold( + result.onSuccess( (usage) { add(SidebarPlanEvent.updateWorkspaceUsage(usage)); }, - (error) { - Log.error("Failed to get workspace usage, error: $error"); - }, ); }); } @@ -231,7 +230,8 @@ class SidebarPlanState with _$SidebarPlanState { @freezed class SidebarToastTierIndicator with _$SidebarToastTierIndicator { const factory SidebarToastTierIndicator.storageLimitHit() = _StorageLimitHit; - const factory SidebarToastTierIndicator.singleFileLimitHit() = _SingleFileLimitHit; + const factory SidebarToastTierIndicator.singleFileLimitHit() = + _SingleFileLimitHit; const factory SidebarToastTierIndicator.aiMaxiLimitHit() = _aiMaxLimitHit; const factory SidebarToastTierIndicator.loading() = _Loading; } diff --git a/frontend/appflowy_flutter/lib/workspace/application/sidebar/space/space_bloc.dart b/frontend/appflowy_flutter/lib/workspace/application/sidebar/space/space_bloc.dart index 46d4943ddf..e1a825d631 100644 --- a/frontend/appflowy_flutter/lib/workspace/application/sidebar/space/space_bloc.dart +++ b/frontend/appflowy_flutter/lib/workspace/application/sidebar/space/space_bloc.dart @@ -329,14 +329,15 @@ class SpaceBloc extends Bloc { final (spaces, _, _) = await _getSpaces(); final currentSpace = await _getLastOpenedSpace(spaces); + Log.info( + 'receive space update, current space: ${currentSpace?.name}(${currentSpace?.id})', + ); + for (var i = 0; i < spaces.length; i++) { Log.info( - 'did receive space update[$i]: ${spaces[i].name}(${spaces[i].id})', + 'receive space update[$i]: ${spaces[i].name}(${spaces[i].id})', ); } - Log.info( - 'did receive space update, current space: ${currentSpace?.name}(${currentSpace?.id})', - ); emit( state.copyWith( diff --git a/frontend/appflowy_flutter/pubspec.lock b/frontend/appflowy_flutter/pubspec.lock index 71bc08bf67..c8dc56a973 100644 --- a/frontend/appflowy_flutter/pubspec.lock +++ b/frontend/appflowy_flutter/pubspec.lock @@ -61,8 +61,8 @@ packages: dependency: "direct main" description: path: "." - ref: ac3b090 - resolved-ref: ac3b0906ea2e7f2e7e3c2c7852e9ec3529e07512 + ref: "157ded3" + resolved-ref: "157ded3cd321b9a54d011c0cc27e270ded35d3aa" url: "https://github.com/AppFlowy-IO/appflowy-editor.git" source: git version: "4.0.0" diff --git a/frontend/appflowy_flutter/pubspec.yaml b/frontend/appflowy_flutter/pubspec.yaml index 29a3d57cf7..f02b420acb 100644 --- a/frontend/appflowy_flutter/pubspec.yaml +++ b/frontend/appflowy_flutter/pubspec.yaml @@ -173,7 +173,7 @@ dependency_overrides: appflowy_editor: git: url: https://github.com/AppFlowy-IO/appflowy-editor.git - ref: "ac3b090" + ref: "157ded3" appflowy_editor_plugins: git: diff --git a/frontend/appflowy_flutter/test/unit_test/markdown/markdown_parser_test.dart b/frontend/appflowy_flutter/test/unit_test/markdown/markdown_parser_test.dart new file mode 100644 index 0000000000..70775612e2 --- /dev/null +++ b/frontend/appflowy_flutter/test/unit_test/markdown/markdown_parser_test.dart @@ -0,0 +1,37 @@ +import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart'; +import 'package:appflowy/shared/markdown_to_document.dart'; +import 'package:appflowy_editor/appflowy_editor.dart'; +import 'package:appflowy_editor_plugins/appflowy_editor_plugins.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('export markdown to document', () { + test('file block', () async { + final document = Document.blank() + ..insert( + [0], + [ + fileNode( + name: 'file.txt', + url: 'https://file.com', + ), + ], + ); + final markdown = customDocumentToMarkdown(document); + expect(markdown, '[file.txt](https://file.com)\n'); + }); + + test('link preview', () { + final document = Document.blank() + ..insert( + [0], + [linkPreviewNode(url: 'https://www.link_preview.com')], + ); + final markdown = customDocumentToMarkdown(document); + expect( + markdown, + '[https://www.link_preview.com](https://www.link_preview.com)\n', + ); + }); + }); +} From 8726df703ec4e733be462b1c9d98253c143ac10f Mon Sep 17 00:00:00 2001 From: Lucas Date: Mon, 9 Dec 2024 16:19:37 +0800 Subject: [PATCH 022/576] fix: hotfix issues for v0.7.7 (#6948) * fix: include link preview block and file block in exported markdown * test: include link preview block and file block in exported markdown * chore: remove unused logs * chore: update editor version * fix: "+" menu should be close after pressing space * test: cancel inline page reference menu by space * chore: update editor version * chore: remove unused logs --- .../document_inline_sub_page_test.dart | 21 ++++++++++- .../base/page_reference_commands.dart | 30 +++++++++++++++ .../parsers/document_markdown_parsers.dart | 3 ++ .../parsers/file_block_node_parser.dart | 19 ++++++++++ .../parsers/link_preview_node_parser.dart | 18 +++++++++ .../parsers/markdown_parsers.dart | 6 +-- ...ser.dart => simple_table_node_parser.dart} | 0 .../presentation/editor_plugins/plugins.dart | 1 + .../inline_actions/inline_actions_menu.dart | 3 ++ .../widgets/inline_actions_handler.dart | 13 +++++-- .../lib/plugins/shared/share/share_bloc.dart | 19 +++++++++- .../lib/shared/markdown_to_document.dart | 2 + .../sidebar/billing/sidebar_plan_bloc.dart | 12 +++--- .../application/sidebar/space/space_bloc.dart | 9 +++-- frontend/appflowy_flutter/pubspec.lock | 4 +- frontend/appflowy_flutter/pubspec.yaml | 2 +- .../markdown/markdown_parser_test.dart | 37 +++++++++++++++++++ 17 files changed, 174 insertions(+), 25 deletions(-) create mode 100644 frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/file_block_node_parser.dart create mode 100644 frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/link_preview_node_parser.dart rename frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/{simple_table_parser.dart => simple_table_node_parser.dart} (100%) create mode 100644 frontend/appflowy_flutter/test/unit_test/markdown/markdown_parser_test.dart diff --git a/frontend/appflowy_flutter/integration_test/desktop/document/document_inline_sub_page_test.dart b/frontend/appflowy_flutter/integration_test/desktop/document/document_inline_sub_page_test.dart index ef9ef73b3c..30e115774a 100644 --- a/frontend/appflowy_flutter/integration_test/desktop/document/document_inline_sub_page_test.dart +++ b/frontend/appflowy_flutter/integration_test/desktop/document/document_inline_sub_page_test.dart @@ -2,12 +2,12 @@ import 'dart:io'; import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/mention/mention_page_block.dart'; +import 'package:appflowy/plugins/inline_actions/inline_actions_menu.dart'; import 'package:appflowy/workspace/presentation/home/menu/view/view_action_type.dart'; +import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart'; import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/services.dart'; - -import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; @@ -330,6 +330,23 @@ void main() { expect(find.text("$_createdPageName (copy)"), findsNWidgets(2)); expect(find.text("$_createdPageName (copy) (copy)"), findsOneWidget); }); + + testWidgets('Cancel inline page reference menu by space', (tester) async { + await tester.initializeAppFlowy(); + await tester.tapAnonymousSignInButton(); + await tester.createOpenRenameDocumentUnderParent(name: _firstDocName); + + await tester.editor.tapLineOfEditorAt(0); + await tester.editor.showPlusMenu(); + + // Cancel by space + await tester.simulateKeyEvent( + LogicalKeyboardKey.space, + ); + await tester.pumpAndSettle(); + + expect(find.byType(InlineActionsMenu), findsNothing); + }); }); } diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/base/page_reference_commands.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/base/page_reference_commands.dart index e42bb3a2bf..079a062837 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/base/page_reference_commands.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/base/page_reference_commands.dart @@ -117,6 +117,22 @@ Future inlinePageReferenceCommandHandler( initialResults: initialResults, style: style, startCharAmount: previousChar != null ? 2 : 1, + cancelBySpaceHandler: () { + if (character == _plusChar) { + final currentSelection = editorState.selection; + if (currentSelection == null) { + return false; + } + // check if the space is after the character + if (currentSelection.isCollapsed && + currentSelection.start.offset == + selection.start.offset + character.length) { + _cancelInlinePageReferenceMenu(editorState); + return true; + } + } + return false; + }, ); selectionMenuService?.show(); @@ -124,3 +140,17 @@ Future inlinePageReferenceCommandHandler( return true; } + +void _cancelInlinePageReferenceMenu(EditorState editorState) { + selectionMenuService?.dismiss(); + selectionMenuService = null; + + // re-focus the selection + final selection = editorState.selection; + if (selection != null) { + editorState.updateSelectionWithReason( + selection, + reason: SelectionUpdateReason.uiEvent, + ); + } +} diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/document_markdown_parsers.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/document_markdown_parsers.dart index 0b694f396e..3f2895d57e 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/document_markdown_parsers.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/document_markdown_parsers.dart @@ -1,4 +1,7 @@ export 'callout_node_parser.dart'; export 'custom_image_node_parser.dart'; +export 'file_block_node_parser.dart'; +export 'link_preview_node_parser.dart'; export 'math_equation_node_parser.dart'; +export 'simple_table_node_parser.dart'; export 'toggle_list_node_parser.dart'; diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/file_block_node_parser.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/file_block_node_parser.dart new file mode 100644 index 0000000000..e57ededcec --- /dev/null +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/file_block_node_parser.dart @@ -0,0 +1,19 @@ +import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart'; +import 'package:appflowy_editor/appflowy_editor.dart'; + +class FileBlockNodeParser extends NodeParser { + const FileBlockNodeParser(); + + @override + String get id => FileBlockKeys.type; + + @override + String transform(Node node, DocumentMarkdownEncoder? encoder) { + final name = node.attributes[FileBlockKeys.name]; + final url = node.attributes[FileBlockKeys.url]; + if (name == null || url == null) { + return ''; + } + return '[$name]($url)\n'; + } +} diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/link_preview_node_parser.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/link_preview_node_parser.dart new file mode 100644 index 0000000000..c7ce69d221 --- /dev/null +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/link_preview_node_parser.dart @@ -0,0 +1,18 @@ +import 'package:appflowy_editor/appflowy_editor.dart'; +import 'package:appflowy_editor_plugins/appflowy_editor_plugins.dart'; + +class LinkPreviewNodeParser extends NodeParser { + const LinkPreviewNodeParser(); + + @override + String get id => LinkPreviewBlockKeys.type; + + @override + String transform(Node node, DocumentMarkdownEncoder? encoder) { + final href = node.attributes[LinkPreviewBlockKeys.url]; + if (href == null) { + return ''; + } + return '[$href]($href)\n'; + } +} diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/markdown_parsers.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/markdown_parsers.dart index 568baeaac0..4ad7734643 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/markdown_parsers.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/markdown_parsers.dart @@ -1,6 +1,2 @@ -export 'callout_node_parser.dart'; -export 'custom_image_node_parser.dart'; export 'markdown_code_parser.dart'; -export 'math_equation_node_parser.dart'; -export 'toggle_list_node_parser.dart'; -export 'simple_table_parser.dart'; +export 'markdown_simple_table_parser.dart'; diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/simple_table_parser.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/simple_table_node_parser.dart similarity index 100% rename from frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/simple_table_parser.dart rename to frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/simple_table_node_parser.dart diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/plugins.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/plugins.dart index da28f7f720..4d1fd95558 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/plugins.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/plugins.dart @@ -57,6 +57,7 @@ export 'openai/widgets/ai_writer_block_component.dart'; export 'openai/widgets/ask_ai_block_component.dart'; export 'openai/widgets/ask_ai_toolbar_item.dart'; export 'outline/outline_block_component.dart'; +export 'parsers/document_markdown_parsers.dart'; export 'parsers/markdown_parsers.dart'; export 'parsers/markdown_simple_table_parser.dart'; export 'quote/quote_block_shortcuts.dart'; diff --git a/frontend/appflowy_flutter/lib/plugins/inline_actions/inline_actions_menu.dart b/frontend/appflowy_flutter/lib/plugins/inline_actions/inline_actions_menu.dart index e31d7c2ef0..dadc4ebf6f 100644 --- a/frontend/appflowy_flutter/lib/plugins/inline_actions/inline_actions_menu.dart +++ b/frontend/appflowy_flutter/lib/plugins/inline_actions/inline_actions_menu.dart @@ -19,12 +19,14 @@ class InlineActionsMenu extends InlineActionsMenuService { required this.initialResults, required this.style, this.startCharAmount = 1, + this.cancelBySpaceHandler, }); final BuildContext context; final EditorState editorState; final InlineActionsService service; final List initialResults; + final bool Function()? cancelBySpaceHandler; @override final InlineActionsMenuStyle style; @@ -137,6 +139,7 @@ class InlineActionsMenu extends InlineActionsMenuService { onSelectionUpdate: _onSelectionUpdate, style: style, startCharAmount: startCharAmount, + cancelBySpaceHandler: cancelBySpaceHandler, ), ), ), diff --git a/frontend/appflowy_flutter/lib/plugins/inline_actions/widgets/inline_actions_handler.dart b/frontend/appflowy_flutter/lib/plugins/inline_actions/widgets/inline_actions_handler.dart index edf34a3dc1..be9a6c2f5f 100644 --- a/frontend/appflowy_flutter/lib/plugins/inline_actions/widgets/inline_actions_handler.dart +++ b/frontend/appflowy_flutter/lib/plugins/inline_actions/widgets/inline_actions_handler.dart @@ -62,6 +62,7 @@ class InlineActionsHandler extends StatefulWidget { required this.onSelectionUpdate, required this.style, this.startCharAmount = 1, + this.cancelBySpaceHandler, }); final InlineActionsService service; @@ -72,6 +73,7 @@ class InlineActionsHandler extends StatefulWidget { final VoidCallback onSelectionUpdate; final InlineActionsMenuStyle style; final int startCharAmount; + final bool Function()? cancelBySpaceHandler; @override State createState() => _InlineActionsHandlerState(); @@ -288,12 +290,17 @@ class _InlineActionsHandlerState extends State { /// that the selection change occurred from the handler. widget.onSelectionUpdate(); + if (event.logicalKey == LogicalKeyboardKey.space) { + final cancelBySpaceHandler = widget.cancelBySpaceHandler; + if (cancelBySpaceHandler != null && cancelBySpaceHandler()) { + return KeyEventResult.handled; + } + } + // Interpolation to avoid having a getter for private variable _insertCharacter(event.character!); return KeyEventResult.handled; - } - - if (moveKeys.contains(event.logicalKey)) { + } else if (moveKeys.contains(event.logicalKey)) { _moveSelection(event.logicalKey); return KeyEventResult.handled; } diff --git a/frontend/appflowy_flutter/lib/plugins/shared/share/share_bloc.dart b/frontend/appflowy_flutter/lib/plugins/shared/share/share_bloc.dart index c533f67ed8..c42bbda5a0 100644 --- a/frontend/appflowy_flutter/lib/plugins/shared/share/share_bloc.dart +++ b/frontend/appflowy_flutter/lib/plugins/shared/share/share_bloc.dart @@ -197,8 +197,23 @@ class ShareBloc extends Bloc { (p) => false, ); - Log.info( - 'get publish info: $publishInfo for view: ${view.name}(${view.id})', + // skip the "Record not found" error, it's because the view is not published yet + publishInfo.fold( + (s) { + Log.info( + 'get publish info success: $publishInfo for view: ${view.name}(${view.id})', + ); + }, + (f) { + if (![ + ErrorCode.RecordNotFound, + ErrorCode.LocalVersionNotSupport, + ].contains(f.code)) { + Log.info( + 'get publish info failed: $f for view: ${view.name}(${view.id})', + ); + } + }, ); String workspaceId = state.workspaceId; diff --git a/frontend/appflowy_flutter/lib/shared/markdown_to_document.dart b/frontend/appflowy_flutter/lib/shared/markdown_to_document.dart index 328c66eca9..b3e2034602 100644 --- a/frontend/appflowy_flutter/lib/shared/markdown_to_document.dart +++ b/frontend/appflowy_flutter/lib/shared/markdown_to_document.dart @@ -20,6 +20,8 @@ String customDocumentToMarkdown(Document document) { const ToggleListNodeParser(), const CustomImageNodeParser(), const SimpleTableNodeParser(), + const LinkPreviewNodeParser(), + const FileBlockNodeParser(), ], ); } diff --git a/frontend/appflowy_flutter/lib/workspace/application/sidebar/billing/sidebar_plan_bloc.dart b/frontend/appflowy_flutter/lib/workspace/application/sidebar/billing/sidebar_plan_bloc.dart index 7af2a547dd..1737494530 100644 --- a/frontend/appflowy_flutter/lib/workspace/application/sidebar/billing/sidebar_plan_bloc.dart +++ b/frontend/appflowy_flutter/lib/workspace/application/sidebar/billing/sidebar_plan_bloc.dart @@ -12,6 +12,7 @@ import 'package:appflowy_backend/protobuf/flowy-user/user_profile.pb.dart'; import 'package:appflowy_backend/protobuf/flowy-user/workspace.pb.dart'; import 'package:bloc/bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; + part 'sidebar_plan_bloc.freezed.dart'; class SidebarPlanBloc extends Bloc { @@ -113,7 +114,8 @@ class SidebarPlanBloc extends Bloc { } else if (error.code == ErrorCode.SingleUploadLimitExceeded) { emit( state.copyWith( - tierIndicator: const SidebarToastTierIndicator.singleFileLimitHit(), + tierIndicator: + const SidebarToastTierIndicator.singleFileLimitHit(), ), ); } else { @@ -184,13 +186,10 @@ class SidebarPlanBloc extends Bloc { if (state.workspaceId != null) { final payload = UserWorkspaceIdPB(workspaceId: state.workspaceId!); UserEventGetWorkspaceUsage(payload).send().then((result) { - result.fold( + result.onSuccess( (usage) { add(SidebarPlanEvent.updateWorkspaceUsage(usage)); }, - (error) { - Log.error("Failed to get workspace usage, error: $error"); - }, ); }); } @@ -231,7 +230,8 @@ class SidebarPlanState with _$SidebarPlanState { @freezed class SidebarToastTierIndicator with _$SidebarToastTierIndicator { const factory SidebarToastTierIndicator.storageLimitHit() = _StorageLimitHit; - const factory SidebarToastTierIndicator.singleFileLimitHit() = _SingleFileLimitHit; + const factory SidebarToastTierIndicator.singleFileLimitHit() = + _SingleFileLimitHit; const factory SidebarToastTierIndicator.aiMaxiLimitHit() = _aiMaxLimitHit; const factory SidebarToastTierIndicator.loading() = _Loading; } diff --git a/frontend/appflowy_flutter/lib/workspace/application/sidebar/space/space_bloc.dart b/frontend/appflowy_flutter/lib/workspace/application/sidebar/space/space_bloc.dart index 46d4943ddf..e1a825d631 100644 --- a/frontend/appflowy_flutter/lib/workspace/application/sidebar/space/space_bloc.dart +++ b/frontend/appflowy_flutter/lib/workspace/application/sidebar/space/space_bloc.dart @@ -329,14 +329,15 @@ class SpaceBloc extends Bloc { final (spaces, _, _) = await _getSpaces(); final currentSpace = await _getLastOpenedSpace(spaces); + Log.info( + 'receive space update, current space: ${currentSpace?.name}(${currentSpace?.id})', + ); + for (var i = 0; i < spaces.length; i++) { Log.info( - 'did receive space update[$i]: ${spaces[i].name}(${spaces[i].id})', + 'receive space update[$i]: ${spaces[i].name}(${spaces[i].id})', ); } - Log.info( - 'did receive space update, current space: ${currentSpace?.name}(${currentSpace?.id})', - ); emit( state.copyWith( diff --git a/frontend/appflowy_flutter/pubspec.lock b/frontend/appflowy_flutter/pubspec.lock index 71bc08bf67..c8dc56a973 100644 --- a/frontend/appflowy_flutter/pubspec.lock +++ b/frontend/appflowy_flutter/pubspec.lock @@ -61,8 +61,8 @@ packages: dependency: "direct main" description: path: "." - ref: ac3b090 - resolved-ref: ac3b0906ea2e7f2e7e3c2c7852e9ec3529e07512 + ref: "157ded3" + resolved-ref: "157ded3cd321b9a54d011c0cc27e270ded35d3aa" url: "https://github.com/AppFlowy-IO/appflowy-editor.git" source: git version: "4.0.0" diff --git a/frontend/appflowy_flutter/pubspec.yaml b/frontend/appflowy_flutter/pubspec.yaml index 29a3d57cf7..f02b420acb 100644 --- a/frontend/appflowy_flutter/pubspec.yaml +++ b/frontend/appflowy_flutter/pubspec.yaml @@ -173,7 +173,7 @@ dependency_overrides: appflowy_editor: git: url: https://github.com/AppFlowy-IO/appflowy-editor.git - ref: "ac3b090" + ref: "157ded3" appflowy_editor_plugins: git: diff --git a/frontend/appflowy_flutter/test/unit_test/markdown/markdown_parser_test.dart b/frontend/appflowy_flutter/test/unit_test/markdown/markdown_parser_test.dart new file mode 100644 index 0000000000..70775612e2 --- /dev/null +++ b/frontend/appflowy_flutter/test/unit_test/markdown/markdown_parser_test.dart @@ -0,0 +1,37 @@ +import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart'; +import 'package:appflowy/shared/markdown_to_document.dart'; +import 'package:appflowy_editor/appflowy_editor.dart'; +import 'package:appflowy_editor_plugins/appflowy_editor_plugins.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('export markdown to document', () { + test('file block', () async { + final document = Document.blank() + ..insert( + [0], + [ + fileNode( + name: 'file.txt', + url: 'https://file.com', + ), + ], + ); + final markdown = customDocumentToMarkdown(document); + expect(markdown, '[file.txt](https://file.com)\n'); + }); + + test('link preview', () { + final document = Document.blank() + ..insert( + [0], + [linkPreviewNode(url: 'https://www.link_preview.com')], + ); + final markdown = customDocumentToMarkdown(document); + expect( + markdown, + '[https://www.link_preview.com](https://www.link_preview.com)\n', + ); + }); + }); +} From d3c7d4b1d2bee47d96ef70693cbf38741813f6df Mon Sep 17 00:00:00 2001 From: "Lucas.Xu" Date: Mon, 9 Dec 2024 16:21:12 +0800 Subject: [PATCH 023/576] Revert "fix: hotfix issues for v0.7.7 (#6948)" This reverts commit 8726df703ec4e733be462b1c9d98253c143ac10f. --- .../document_inline_sub_page_test.dart | 21 +---------- .../base/page_reference_commands.dart | 30 --------------- .../parsers/document_markdown_parsers.dart | 3 -- .../parsers/file_block_node_parser.dart | 19 ---------- .../parsers/link_preview_node_parser.dart | 18 --------- .../parsers/markdown_parsers.dart | 6 ++- ...e_parser.dart => simple_table_parser.dart} | 0 .../presentation/editor_plugins/plugins.dart | 1 - .../inline_actions/inline_actions_menu.dart | 3 -- .../widgets/inline_actions_handler.dart | 13 ++----- .../lib/plugins/shared/share/share_bloc.dart | 19 +--------- .../lib/shared/markdown_to_document.dart | 2 - .../sidebar/billing/sidebar_plan_bloc.dart | 12 +++--- .../application/sidebar/space/space_bloc.dart | 9 ++--- frontend/appflowy_flutter/pubspec.lock | 4 +- frontend/appflowy_flutter/pubspec.yaml | 2 +- .../markdown/markdown_parser_test.dart | 37 ------------------- 17 files changed, 25 insertions(+), 174 deletions(-) delete mode 100644 frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/file_block_node_parser.dart delete mode 100644 frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/link_preview_node_parser.dart rename frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/{simple_table_node_parser.dart => simple_table_parser.dart} (100%) delete mode 100644 frontend/appflowy_flutter/test/unit_test/markdown/markdown_parser_test.dart diff --git a/frontend/appflowy_flutter/integration_test/desktop/document/document_inline_sub_page_test.dart b/frontend/appflowy_flutter/integration_test/desktop/document/document_inline_sub_page_test.dart index 30e115774a..ef9ef73b3c 100644 --- a/frontend/appflowy_flutter/integration_test/desktop/document/document_inline_sub_page_test.dart +++ b/frontend/appflowy_flutter/integration_test/desktop/document/document_inline_sub_page_test.dart @@ -2,12 +2,12 @@ import 'dart:io'; import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/mention/mention_page_block.dart'; -import 'package:appflowy/plugins/inline_actions/inline_actions_menu.dart'; import 'package:appflowy/workspace/presentation/home/menu/view/view_action_type.dart'; -import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart'; import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/services.dart'; + +import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; @@ -330,23 +330,6 @@ void main() { expect(find.text("$_createdPageName (copy)"), findsNWidgets(2)); expect(find.text("$_createdPageName (copy) (copy)"), findsOneWidget); }); - - testWidgets('Cancel inline page reference menu by space', (tester) async { - await tester.initializeAppFlowy(); - await tester.tapAnonymousSignInButton(); - await tester.createOpenRenameDocumentUnderParent(name: _firstDocName); - - await tester.editor.tapLineOfEditorAt(0); - await tester.editor.showPlusMenu(); - - // Cancel by space - await tester.simulateKeyEvent( - LogicalKeyboardKey.space, - ); - await tester.pumpAndSettle(); - - expect(find.byType(InlineActionsMenu), findsNothing); - }); }); } diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/base/page_reference_commands.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/base/page_reference_commands.dart index 079a062837..e42bb3a2bf 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/base/page_reference_commands.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/base/page_reference_commands.dart @@ -117,22 +117,6 @@ Future inlinePageReferenceCommandHandler( initialResults: initialResults, style: style, startCharAmount: previousChar != null ? 2 : 1, - cancelBySpaceHandler: () { - if (character == _plusChar) { - final currentSelection = editorState.selection; - if (currentSelection == null) { - return false; - } - // check if the space is after the character - if (currentSelection.isCollapsed && - currentSelection.start.offset == - selection.start.offset + character.length) { - _cancelInlinePageReferenceMenu(editorState); - return true; - } - } - return false; - }, ); selectionMenuService?.show(); @@ -140,17 +124,3 @@ Future inlinePageReferenceCommandHandler( return true; } - -void _cancelInlinePageReferenceMenu(EditorState editorState) { - selectionMenuService?.dismiss(); - selectionMenuService = null; - - // re-focus the selection - final selection = editorState.selection; - if (selection != null) { - editorState.updateSelectionWithReason( - selection, - reason: SelectionUpdateReason.uiEvent, - ); - } -} diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/document_markdown_parsers.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/document_markdown_parsers.dart index 3f2895d57e..0b694f396e 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/document_markdown_parsers.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/document_markdown_parsers.dart @@ -1,7 +1,4 @@ export 'callout_node_parser.dart'; export 'custom_image_node_parser.dart'; -export 'file_block_node_parser.dart'; -export 'link_preview_node_parser.dart'; export 'math_equation_node_parser.dart'; -export 'simple_table_node_parser.dart'; export 'toggle_list_node_parser.dart'; diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/file_block_node_parser.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/file_block_node_parser.dart deleted file mode 100644 index e57ededcec..0000000000 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/file_block_node_parser.dart +++ /dev/null @@ -1,19 +0,0 @@ -import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart'; -import 'package:appflowy_editor/appflowy_editor.dart'; - -class FileBlockNodeParser extends NodeParser { - const FileBlockNodeParser(); - - @override - String get id => FileBlockKeys.type; - - @override - String transform(Node node, DocumentMarkdownEncoder? encoder) { - final name = node.attributes[FileBlockKeys.name]; - final url = node.attributes[FileBlockKeys.url]; - if (name == null || url == null) { - return ''; - } - return '[$name]($url)\n'; - } -} diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/link_preview_node_parser.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/link_preview_node_parser.dart deleted file mode 100644 index c7ce69d221..0000000000 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/link_preview_node_parser.dart +++ /dev/null @@ -1,18 +0,0 @@ -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:appflowy_editor_plugins/appflowy_editor_plugins.dart'; - -class LinkPreviewNodeParser extends NodeParser { - const LinkPreviewNodeParser(); - - @override - String get id => LinkPreviewBlockKeys.type; - - @override - String transform(Node node, DocumentMarkdownEncoder? encoder) { - final href = node.attributes[LinkPreviewBlockKeys.url]; - if (href == null) { - return ''; - } - return '[$href]($href)\n'; - } -} diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/markdown_parsers.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/markdown_parsers.dart index 4ad7734643..568baeaac0 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/markdown_parsers.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/markdown_parsers.dart @@ -1,2 +1,6 @@ +export 'callout_node_parser.dart'; +export 'custom_image_node_parser.dart'; export 'markdown_code_parser.dart'; -export 'markdown_simple_table_parser.dart'; +export 'math_equation_node_parser.dart'; +export 'toggle_list_node_parser.dart'; +export 'simple_table_parser.dart'; diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/simple_table_node_parser.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/simple_table_parser.dart similarity index 100% rename from frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/simple_table_node_parser.dart rename to frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/simple_table_parser.dart diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/plugins.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/plugins.dart index 4d1fd95558..da28f7f720 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/plugins.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/plugins.dart @@ -57,7 +57,6 @@ export 'openai/widgets/ai_writer_block_component.dart'; export 'openai/widgets/ask_ai_block_component.dart'; export 'openai/widgets/ask_ai_toolbar_item.dart'; export 'outline/outline_block_component.dart'; -export 'parsers/document_markdown_parsers.dart'; export 'parsers/markdown_parsers.dart'; export 'parsers/markdown_simple_table_parser.dart'; export 'quote/quote_block_shortcuts.dart'; diff --git a/frontend/appflowy_flutter/lib/plugins/inline_actions/inline_actions_menu.dart b/frontend/appflowy_flutter/lib/plugins/inline_actions/inline_actions_menu.dart index dadc4ebf6f..e31d7c2ef0 100644 --- a/frontend/appflowy_flutter/lib/plugins/inline_actions/inline_actions_menu.dart +++ b/frontend/appflowy_flutter/lib/plugins/inline_actions/inline_actions_menu.dart @@ -19,14 +19,12 @@ class InlineActionsMenu extends InlineActionsMenuService { required this.initialResults, required this.style, this.startCharAmount = 1, - this.cancelBySpaceHandler, }); final BuildContext context; final EditorState editorState; final InlineActionsService service; final List initialResults; - final bool Function()? cancelBySpaceHandler; @override final InlineActionsMenuStyle style; @@ -139,7 +137,6 @@ class InlineActionsMenu extends InlineActionsMenuService { onSelectionUpdate: _onSelectionUpdate, style: style, startCharAmount: startCharAmount, - cancelBySpaceHandler: cancelBySpaceHandler, ), ), ), diff --git a/frontend/appflowy_flutter/lib/plugins/inline_actions/widgets/inline_actions_handler.dart b/frontend/appflowy_flutter/lib/plugins/inline_actions/widgets/inline_actions_handler.dart index be9a6c2f5f..edf34a3dc1 100644 --- a/frontend/appflowy_flutter/lib/plugins/inline_actions/widgets/inline_actions_handler.dart +++ b/frontend/appflowy_flutter/lib/plugins/inline_actions/widgets/inline_actions_handler.dart @@ -62,7 +62,6 @@ class InlineActionsHandler extends StatefulWidget { required this.onSelectionUpdate, required this.style, this.startCharAmount = 1, - this.cancelBySpaceHandler, }); final InlineActionsService service; @@ -73,7 +72,6 @@ class InlineActionsHandler extends StatefulWidget { final VoidCallback onSelectionUpdate; final InlineActionsMenuStyle style; final int startCharAmount; - final bool Function()? cancelBySpaceHandler; @override State createState() => _InlineActionsHandlerState(); @@ -290,17 +288,12 @@ class _InlineActionsHandlerState extends State { /// that the selection change occurred from the handler. widget.onSelectionUpdate(); - if (event.logicalKey == LogicalKeyboardKey.space) { - final cancelBySpaceHandler = widget.cancelBySpaceHandler; - if (cancelBySpaceHandler != null && cancelBySpaceHandler()) { - return KeyEventResult.handled; - } - } - // Interpolation to avoid having a getter for private variable _insertCharacter(event.character!); return KeyEventResult.handled; - } else if (moveKeys.contains(event.logicalKey)) { + } + + if (moveKeys.contains(event.logicalKey)) { _moveSelection(event.logicalKey); return KeyEventResult.handled; } diff --git a/frontend/appflowy_flutter/lib/plugins/shared/share/share_bloc.dart b/frontend/appflowy_flutter/lib/plugins/shared/share/share_bloc.dart index c42bbda5a0..c533f67ed8 100644 --- a/frontend/appflowy_flutter/lib/plugins/shared/share/share_bloc.dart +++ b/frontend/appflowy_flutter/lib/plugins/shared/share/share_bloc.dart @@ -197,23 +197,8 @@ class ShareBloc extends Bloc { (p) => false, ); - // skip the "Record not found" error, it's because the view is not published yet - publishInfo.fold( - (s) { - Log.info( - 'get publish info success: $publishInfo for view: ${view.name}(${view.id})', - ); - }, - (f) { - if (![ - ErrorCode.RecordNotFound, - ErrorCode.LocalVersionNotSupport, - ].contains(f.code)) { - Log.info( - 'get publish info failed: $f for view: ${view.name}(${view.id})', - ); - } - }, + Log.info( + 'get publish info: $publishInfo for view: ${view.name}(${view.id})', ); String workspaceId = state.workspaceId; diff --git a/frontend/appflowy_flutter/lib/shared/markdown_to_document.dart b/frontend/appflowy_flutter/lib/shared/markdown_to_document.dart index b3e2034602..328c66eca9 100644 --- a/frontend/appflowy_flutter/lib/shared/markdown_to_document.dart +++ b/frontend/appflowy_flutter/lib/shared/markdown_to_document.dart @@ -20,8 +20,6 @@ String customDocumentToMarkdown(Document document) { const ToggleListNodeParser(), const CustomImageNodeParser(), const SimpleTableNodeParser(), - const LinkPreviewNodeParser(), - const FileBlockNodeParser(), ], ); } diff --git a/frontend/appflowy_flutter/lib/workspace/application/sidebar/billing/sidebar_plan_bloc.dart b/frontend/appflowy_flutter/lib/workspace/application/sidebar/billing/sidebar_plan_bloc.dart index 1737494530..7af2a547dd 100644 --- a/frontend/appflowy_flutter/lib/workspace/application/sidebar/billing/sidebar_plan_bloc.dart +++ b/frontend/appflowy_flutter/lib/workspace/application/sidebar/billing/sidebar_plan_bloc.dart @@ -12,7 +12,6 @@ import 'package:appflowy_backend/protobuf/flowy-user/user_profile.pb.dart'; import 'package:appflowy_backend/protobuf/flowy-user/workspace.pb.dart'; import 'package:bloc/bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; - part 'sidebar_plan_bloc.freezed.dart'; class SidebarPlanBloc extends Bloc { @@ -114,8 +113,7 @@ class SidebarPlanBloc extends Bloc { } else if (error.code == ErrorCode.SingleUploadLimitExceeded) { emit( state.copyWith( - tierIndicator: - const SidebarToastTierIndicator.singleFileLimitHit(), + tierIndicator: const SidebarToastTierIndicator.singleFileLimitHit(), ), ); } else { @@ -186,10 +184,13 @@ class SidebarPlanBloc extends Bloc { if (state.workspaceId != null) { final payload = UserWorkspaceIdPB(workspaceId: state.workspaceId!); UserEventGetWorkspaceUsage(payload).send().then((result) { - result.onSuccess( + result.fold( (usage) { add(SidebarPlanEvent.updateWorkspaceUsage(usage)); }, + (error) { + Log.error("Failed to get workspace usage, error: $error"); + }, ); }); } @@ -230,8 +231,7 @@ class SidebarPlanState with _$SidebarPlanState { @freezed class SidebarToastTierIndicator with _$SidebarToastTierIndicator { const factory SidebarToastTierIndicator.storageLimitHit() = _StorageLimitHit; - const factory SidebarToastTierIndicator.singleFileLimitHit() = - _SingleFileLimitHit; + const factory SidebarToastTierIndicator.singleFileLimitHit() = _SingleFileLimitHit; const factory SidebarToastTierIndicator.aiMaxiLimitHit() = _aiMaxLimitHit; const factory SidebarToastTierIndicator.loading() = _Loading; } diff --git a/frontend/appflowy_flutter/lib/workspace/application/sidebar/space/space_bloc.dart b/frontend/appflowy_flutter/lib/workspace/application/sidebar/space/space_bloc.dart index e1a825d631..46d4943ddf 100644 --- a/frontend/appflowy_flutter/lib/workspace/application/sidebar/space/space_bloc.dart +++ b/frontend/appflowy_flutter/lib/workspace/application/sidebar/space/space_bloc.dart @@ -329,15 +329,14 @@ class SpaceBloc extends Bloc { final (spaces, _, _) = await _getSpaces(); final currentSpace = await _getLastOpenedSpace(spaces); - Log.info( - 'receive space update, current space: ${currentSpace?.name}(${currentSpace?.id})', - ); - for (var i = 0; i < spaces.length; i++) { Log.info( - 'receive space update[$i]: ${spaces[i].name}(${spaces[i].id})', + 'did receive space update[$i]: ${spaces[i].name}(${spaces[i].id})', ); } + Log.info( + 'did receive space update, current space: ${currentSpace?.name}(${currentSpace?.id})', + ); emit( state.copyWith( diff --git a/frontend/appflowy_flutter/pubspec.lock b/frontend/appflowy_flutter/pubspec.lock index c8dc56a973..71bc08bf67 100644 --- a/frontend/appflowy_flutter/pubspec.lock +++ b/frontend/appflowy_flutter/pubspec.lock @@ -61,8 +61,8 @@ packages: dependency: "direct main" description: path: "." - ref: "157ded3" - resolved-ref: "157ded3cd321b9a54d011c0cc27e270ded35d3aa" + ref: ac3b090 + resolved-ref: ac3b0906ea2e7f2e7e3c2c7852e9ec3529e07512 url: "https://github.com/AppFlowy-IO/appflowy-editor.git" source: git version: "4.0.0" diff --git a/frontend/appflowy_flutter/pubspec.yaml b/frontend/appflowy_flutter/pubspec.yaml index f02b420acb..29a3d57cf7 100644 --- a/frontend/appflowy_flutter/pubspec.yaml +++ b/frontend/appflowy_flutter/pubspec.yaml @@ -173,7 +173,7 @@ dependency_overrides: appflowy_editor: git: url: https://github.com/AppFlowy-IO/appflowy-editor.git - ref: "157ded3" + ref: "ac3b090" appflowy_editor_plugins: git: diff --git a/frontend/appflowy_flutter/test/unit_test/markdown/markdown_parser_test.dart b/frontend/appflowy_flutter/test/unit_test/markdown/markdown_parser_test.dart deleted file mode 100644 index 70775612e2..0000000000 --- a/frontend/appflowy_flutter/test/unit_test/markdown/markdown_parser_test.dart +++ /dev/null @@ -1,37 +0,0 @@ -import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart'; -import 'package:appflowy/shared/markdown_to_document.dart'; -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:appflowy_editor_plugins/appflowy_editor_plugins.dart'; -import 'package:flutter_test/flutter_test.dart'; - -void main() { - group('export markdown to document', () { - test('file block', () async { - final document = Document.blank() - ..insert( - [0], - [ - fileNode( - name: 'file.txt', - url: 'https://file.com', - ), - ], - ); - final markdown = customDocumentToMarkdown(document); - expect(markdown, '[file.txt](https://file.com)\n'); - }); - - test('link preview', () { - final document = Document.blank() - ..insert( - [0], - [linkPreviewNode(url: 'https://www.link_preview.com')], - ); - final markdown = customDocumentToMarkdown(document); - expect( - markdown, - '[https://www.link_preview.com](https://www.link_preview.com)\n', - ); - }); - }); -} From 2c88653a690c00f232e4d94589fe377d98818b69 Mon Sep 17 00:00:00 2001 From: Richard Shiue <71320345+richardshiue@users.noreply.github.com> Date: Mon, 9 Dec 2024 17:20:39 +0800 Subject: [PATCH 024/576] feat(flutter_mobile): improve appearance of mention page selector (#6946) * feat(flutter_mobile): improve appearance of mention page selector * chore: move to use CustomScrollview --- .../chat_mention_page_bottom_sheet.dart | 207 ++++++++++++++++++ .../chat_input/chat_mention_page_menu.dart | 23 +- .../chat_input/mobile_ai_prompt_input.dart | 2 +- 3 files changed, 226 insertions(+), 6 deletions(-) create mode 100644 frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_input/chat_mention_page_bottom_sheet.dart diff --git a/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_input/chat_mention_page_bottom_sheet.dart b/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_input/chat_mention_page_bottom_sheet.dart new file mode 100644 index 0000000000..d12ca5abd6 --- /dev/null +++ b/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_input/chat_mention_page_bottom_sheet.dart @@ -0,0 +1,207 @@ +import 'package:appflowy/generated/locale_keys.g.dart'; +import 'package:appflowy/mobile/presentation/base/flowy_search_text_field.dart'; +import 'package:appflowy/mobile/presentation/bottom_sheet/show_mobile_bottom_sheet.dart'; +import 'package:appflowy/plugins/base/drag_handler.dart'; +import 'package:appflowy/workspace/application/view/view_ext.dart'; +import 'package:appflowy/workspace/application/view/view_service.dart'; +import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flowy_infra_ui/flowy_infra_ui.dart'; +import 'package:flutter/material.dart'; + +import 'chat_mention_page_menu.dart'; + +Future showPageSelectorSheet( + BuildContext context, { + bool Function(ViewPB view)? filter, +}) async { + filter ??= (v) => !v.isSpace && v.parentViewId.isNotEmpty; + + return showMobileBottomSheet( + context, + backgroundColor: Theme.of(context).colorScheme.surface, + maxChildSize: 0.98, + enableDraggableScrollable: true, + scrollableWidgetBuilder: (context, scrollController) { + return Expanded( + child: _MobilePageSelectorBody( + filter: filter, + scrollController: scrollController, + ), + ); + }, + builder: (context) => const SizedBox.shrink(), + ); +} + +class _MobilePageSelectorBody extends StatefulWidget { + const _MobilePageSelectorBody({ + this.filter, + this.scrollController, + }); + + final bool Function(ViewPB view)? filter; + final ScrollController? scrollController; + + @override + State<_MobilePageSelectorBody> createState() => + _MobilePageSelectorBodyState(); +} + +class _MobilePageSelectorBodyState extends State<_MobilePageSelectorBody> { + final textController = TextEditingController(); + late final Future> _viewsFuture = _fetchViews(); + + @override + void dispose() { + textController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return CustomScrollView( + controller: widget.scrollController, + shrinkWrap: true, + slivers: [ + SliverPersistentHeader( + pinned: true, + delegate: _Header( + child: ColoredBox( + color: Theme.of(context).cardColor, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const DragHandle(), + SizedBox( + height: 44.0, + child: Center( + child: FlowyText.medium( + LocaleKeys.document_mobilePageSelector_title.tr(), + fontSize: 16.0, + ), + ), + ), + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 16.0, + vertical: 8.0, + ), + child: SizedBox( + height: 44.0, + child: FlowySearchTextField( + controller: textController, + onChanged: (_) => setState(() {}), + ), + ), + ), + const Divider(height: 0.5, thickness: 0.5), + ], + ), + ), + ), + ), + FutureBuilder( + future: _viewsFuture, + builder: (_, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const SliverToBoxAdapter( + child: CircularProgressIndicator.adaptive(), + ); + } + + if (snapshot.hasError || snapshot.data == null) { + return SliverToBoxAdapter( + child: FlowyText( + LocaleKeys.document_mobilePageSelector_failedToLoad.tr(), + ), + ); + } + + final views = snapshot.data! + .where((v) => widget.filter?.call(v) ?? true) + .toList(); + + final filtered = views.where( + (v) => + textController.text.isEmpty || + v.name + .toLowerCase() + .contains(textController.text.toLowerCase()), + ); + + if (filtered.isEmpty) { + return SliverToBoxAdapter( + child: FlowyText( + LocaleKeys.document_mobilePageSelector_noPagesFound.tr(), + ), + ); + } + + return SliverPadding( + padding: + const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0), + sliver: SliverList( + delegate: SliverChildBuilderDelegate( + (context, index) { + final view = filtered.elementAt(index); + return InkWell( + onTap: () => Navigator.of(context).pop(view), + borderRadius: BorderRadius.circular(12), + splashColor: Colors.transparent, + child: Container( + height: 44, + padding: const EdgeInsets.all(4.0), + child: Row( + children: [ + MentionViewIcon(view: view), + const HSpace(8), + Expanded( + child: MentionViewTitleAndAncestors(view: view), + ), + ], + ), + ), + ); + }, + childCount: filtered.length, + ), + ), + ); + }, + ), + ], + ); + } + + Future> _fetchViews() async => + (await ViewBackendService.getAllViews()).toNullable()?.items ?? []; +} + +class _Header extends SliverPersistentHeaderDelegate { + const _Header({ + required this.child, + }); + + final Widget child; + + @override + Widget build( + BuildContext context, + double shrinkOffset, + bool overlapsContent, + ) { + return child; + } + + @override + double get maxExtent => 120.5; + + @override + double get minExtent => 120.5; + + @override + bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) { + return false; + } +} diff --git a/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_input/chat_mention_page_menu.dart b/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_input/chat_mention_page_menu.dart index fff5338e67..14db6e1a1c 100644 --- a/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_input/chat_mention_page_menu.dart +++ b/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_input/chat_mention_page_menu.dart @@ -284,9 +284,9 @@ class _ChatMentionPageItem extends StatelessWidget { padding: const EdgeInsets.all(4.0), child: Row( children: [ - _buildIcon(context, view), + MentionViewIcon(view: view), const HSpace(8.0), - Expanded(child: _ViewTitleAndAncestors(view: view)), + Expanded(child: MentionViewTitleAndAncestors(view: view)), ], ), ), @@ -294,8 +294,18 @@ class _ChatMentionPageItem extends StatelessWidget { ), ); } +} - Widget _buildIcon(BuildContext context, ViewPB view) { +class MentionViewIcon extends StatelessWidget { + const MentionViewIcon({ + super.key, + required this.view, + }); + + final ViewPB view; + + @override + Widget build(BuildContext context) { final spaceIcon = view.buildSpaceIconSvg(context); if (view.icon.value.isNotEmpty) { @@ -326,8 +336,9 @@ class _ChatMentionPageItem extends StatelessWidget { } } -class _ViewTitleAndAncestors extends StatelessWidget { - const _ViewTitleAndAncestors({ +class MentionViewTitleAndAncestors extends StatelessWidget { + const MentionViewTitleAndAncestors({ + super.key, required this.view, }); @@ -349,6 +360,7 @@ class _ViewTitleAndAncestors extends StatelessWidget { if (state.ancestors.isEmpty || ancestorList.trim().isEmpty) { return FlowyText( nonEmptyName, + fontSize: 14.0, overflow: TextOverflow.ellipsis, ); } @@ -358,6 +370,7 @@ class _ViewTitleAndAncestors extends StatelessWidget { children: [ FlowyText( nonEmptyName, + fontSize: 14.0, figmaLineHeight: 20.0, overflow: TextOverflow.ellipsis, ), diff --git a/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_input/mobile_ai_prompt_input.dart b/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_input/mobile_ai_prompt_input.dart index d42b247e84..f7a657bc18 100644 --- a/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_input/mobile_ai_prompt_input.dart +++ b/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_input/mobile_ai_prompt_input.dart @@ -1,7 +1,6 @@ import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/plugins/ai_chat/application/ai_prompt_input_bloc.dart'; import 'package:appflowy/plugins/ai_chat/application/chat_input_control_cubit.dart'; -import 'package:appflowy/plugins/document/presentation/editor_plugins/mention/mobile_page_selector_sheet.dart'; import 'package:appflowy/workspace/application/view/view_ext.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:extended_text_field/extended_text_field.dart'; @@ -13,6 +12,7 @@ import '../layout_define.dart'; import 'ai_prompt_buttons.dart'; import 'chat_input_file.dart'; import 'chat_input_span.dart'; +import 'chat_mention_page_bottom_sheet.dart'; class MobileAIPromptInput extends StatefulWidget { const MobileAIPromptInput({ From c3bfae866bc3d6265577b6c5a36790d73bd5378c Mon Sep 17 00:00:00 2001 From: Lucas Date: Mon, 9 Dec 2024 17:50:45 +0800 Subject: [PATCH 025/576] chore: bump version 0.7.7 (#6951) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: add same delete design in database (#6620) * add same delete design in database * fix: remove padding when widget is null or function is null * fix(desktop): resize sidebar menu regression (#6897) * fix: initial ai chat load (#6920) * fix: unable to open local file using afLaunchUrl function (#6927) * fix: unable to open local file using afLaunchUrl function * chore: use the latest api to open the local file * chore: use the latest api to open the local file * chore: use the latest api to open the local file * test: add local paht regex test * fix(flutter): implement mention date transaction handler (#6933) * fix: implement mention date transaction handler * test: add integration tests * chore: code cleanup * chore: early return if null delta * fix(flutter_desktop): clicking on empty space when editing a cell sho… (#6949) * fix(flutter_desktop): clicking on empty space when editing a cell shouldn't close event card * test: fix integration tests * chore: bump version 0.7.7 * fix: hotfix issues for v0.7.7 (#6948) * fix: include link preview block and file block in exported markdown * test: include link preview block and file block in exported markdown * chore: remove unused logs * chore: update editor version * fix: "+" menu should be close after pressing space * test: cancel inline page reference menu by space * chore: update editor version * chore: remove unused logs --------- Co-authored-by: Ahad Patel <69256193+Ahad-patel@users.noreply.github.com> Co-authored-by: Richard Shiue <71320345+richardshiue@users.noreply.github.com> --- CHANGELOG.md | 4 + frontend/Makefile.toml | 2 +- .../database/database_calendar_test.dart | 2 + .../document_inline_sub_page_test.dart | 21 +- .../document_with_date_reminder_test.dart | 343 +++++++++++++++++- .../lib/core/helpers/url_launcher.dart | 79 +++- .../base/view_page/more_bottom_sheet.dart | 2 +- .../ai_chat/application/chat_bloc.dart | 16 +- .../desktop_grid/desktop_grid_media_cell.dart | 13 +- .../desktop_row_detail_date_cell.dart | 1 + .../desktop_row_detail_media_cell.dart | 5 +- .../desktop_row_detail_relation_cell.dart | 1 + ...desktop_row_detail_select_option_cell.dart | 1 + .../database/widgets/field/field_editor.dart | 30 +- .../base/page_reference_commands.dart | 30 ++ .../file/file_block_component.dart | 19 +- .../editor_plugins/file/file_util.dart | 5 +- .../child_page_transaction_handler.dart | 3 +- .../mention/date_transaction_handler.dart | 263 ++++++++++++++ .../editor_plugins/mention/mention_block.dart | 1 - .../parsers/document_markdown_parsers.dart | 3 + .../parsers/file_block_node_parser.dart | 19 + .../parsers/link_preview_node_parser.dart | 18 + .../parsers/markdown_parsers.dart | 6 +- ...ser.dart => simple_table_node_parser.dart} | 0 .../presentation/editor_plugins/plugins.dart | 1 + .../sub_page_transaction_handler.dart | 1 + .../editor_transaction_handler.dart | 1 + .../editor_transaction_service.dart | 21 +- .../mention_transaction_handler.dart | 8 +- .../inline_actions/inline_actions_menu.dart | 3 + .../widgets/inline_actions_handler.dart | 13 +- .../lib/plugins/shared/share/share_bloc.dart | 19 +- .../lib/shared/markdown_to_document.dart | 2 + .../lib/shared/patterns/common_patterns.dart | 3 + .../auth/af_cloud_auth_service.dart | 2 +- .../lib/util/share_log_files.dart | 4 +- .../application/home/home_setting_bloc.dart | 2 +- .../settings/ai/plugin_state_bloc.dart | 3 +- .../sidebar/billing/sidebar_plan_bloc.dart | 12 +- .../application/sidebar/space/space_bloc.dart | 9 +- .../presentation/home/home_layout.dart | 7 +- .../pages/settings_manage_data_view.dart | 7 +- .../theme_upload_learn_more_button.dart | 7 +- .../interactive_image_toolbar.dart | 4 +- .../packages/flowy_infra/lib/size.dart | 2 - .../lib/style_widget/button.dart | 17 +- frontend/appflowy_flutter/pubspec.lock | 4 +- frontend/appflowy_flutter/pubspec.yaml | 4 +- .../markdown/markdown_parser_test.dart | 37 ++ .../url_launcher/url_launcher_test.dart | 19 + frontend/resources/translations/en.json | 9 +- 52 files changed, 992 insertions(+), 116 deletions(-) create mode 100644 frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mention/date_transaction_handler.dart create mode 100644 frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/file_block_node_parser.dart create mode 100644 frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/link_preview_node_parser.dart rename frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/{simple_table_parser.dart => simple_table_node_parser.dart} (100%) create mode 100644 frontend/appflowy_flutter/test/unit_test/markdown/markdown_parser_test.dart create mode 100644 frontend/appflowy_flutter/test/unit_test/url_launcher/url_launcher_test.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index 51df2a5742..de5c792d8d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,8 @@ # Release Notes +## Version 0.7.6 - 03/12/2024 +### Bug Fixes + + ## Version 0.7.6 - 03/12/2024 ### New Features - Revamped the simple table UI diff --git a/frontend/Makefile.toml b/frontend/Makefile.toml index f86cb1befe..deb314db41 100644 --- a/frontend/Makefile.toml +++ b/frontend/Makefile.toml @@ -26,7 +26,7 @@ CARGO_MAKE_EXTEND_WORKSPACE_MAKEFILE = true CARGO_MAKE_CRATE_FS_NAME = "dart_ffi" CARGO_MAKE_CRATE_NAME = "dart-ffi" LIB_NAME = "dart_ffi" -APPFLOWY_VERSION = "0.7.6" +APPFLOWY_VERSION = "0.7.7" FLUTTER_DESKTOP_FEATURES = "dart" PRODUCT_NAME = "AppFlowy" MACOSX_DEPLOYMENT_TARGET = "11.0" diff --git a/frontend/appflowy_flutter/integration_test/desktop/database/database_calendar_test.dart b/frontend/appflowy_flutter/integration_test/desktop/database/database_calendar_test.dart index eb1d67ffcd..d6df648bb3 100644 --- a/frontend/appflowy_flutter/integration_test/desktop/database/database_calendar_test.dart +++ b/frontend/appflowy_flutter/integration_test/desktop/database/database_calendar_test.dart @@ -301,6 +301,7 @@ void main() { await tester.createOption(name: "qwer"); await tester.selectOption(name: "asdf"); await tester.dismissCellEditor(); + await tester.dismissCellEditor(); await tester.tapDatabaseFilterButton(); await tester.tapCreateFilterByFieldType(FieldType.MultiSelect, "Tags"); @@ -332,6 +333,7 @@ void main() { await tester.tapButton(finderForFieldType(FieldType.MultiSelect)); await tester.selectOption(name: "asdf"); await tester.dismissCellEditor(); + await tester.dismissCellEditor(); tester.assertNumberOfEventsInCalendar(0); diff --git a/frontend/appflowy_flutter/integration_test/desktop/document/document_inline_sub_page_test.dart b/frontend/appflowy_flutter/integration_test/desktop/document/document_inline_sub_page_test.dart index ef9ef73b3c..30e115774a 100644 --- a/frontend/appflowy_flutter/integration_test/desktop/document/document_inline_sub_page_test.dart +++ b/frontend/appflowy_flutter/integration_test/desktop/document/document_inline_sub_page_test.dart @@ -2,12 +2,12 @@ import 'dart:io'; import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/mention/mention_page_block.dart'; +import 'package:appflowy/plugins/inline_actions/inline_actions_menu.dart'; import 'package:appflowy/workspace/presentation/home/menu/view/view_action_type.dart'; +import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart'; import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/services.dart'; - -import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; @@ -330,6 +330,23 @@ void main() { expect(find.text("$_createdPageName (copy)"), findsNWidgets(2)); expect(find.text("$_createdPageName (copy) (copy)"), findsOneWidget); }); + + testWidgets('Cancel inline page reference menu by space', (tester) async { + await tester.initializeAppFlowy(); + await tester.tapAnonymousSignInButton(); + await tester.createOpenRenameDocumentUnderParent(name: _firstDocName); + + await tester.editor.tapLineOfEditorAt(0); + await tester.editor.showPlusMenu(); + + // Cancel by space + await tester.simulateKeyEvent( + LogicalKeyboardKey.space, + ); + await tester.pumpAndSettle(); + + expect(find.byType(InlineActionsMenu), findsNothing); + }); }); } diff --git a/frontend/appflowy_flutter/integration_test/desktop/document/document_with_date_reminder_test.dart b/frontend/appflowy_flutter/integration_test/desktop/document/document_with_date_reminder_test.dart index c7fea448f7..ccfdbae76e 100644 --- a/frontend/appflowy_flutter/integration_test/desktop/document/document_with_date_reminder_test.dart +++ b/frontend/appflowy_flutter/integration_test/desktop/document/document_with_date_reminder_test.dart @@ -1,14 +1,21 @@ +import 'dart:io'; + import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/mention/mention_date_block.dart'; +import 'package:appflowy/startup/startup.dart'; +import 'package:appflowy/user/application/reminder/reminder_bloc.dart'; import 'package:appflowy/workspace/application/settings/date_time/date_format_ext.dart'; import 'package:appflowy/workspace/presentation/widgets/date_picker/desktop_date_picker.dart'; import 'package:appflowy/workspace/presentation/widgets/toggle/toggle.dart'; import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart'; +import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; +import 'package:table_calendar/table_calendar.dart'; import '../../shared/util.dart'; @@ -18,7 +25,7 @@ void main() { TestWidgetsFlutterBinding.ensureInitialized(); }); - group('date or reminder block in document', () { + group('date or reminder block in document:', () { testWidgets("insert date with time block", (tester) async { await tester.initializeAppFlowy(); await tester.tapAnonymousSignInButton(); @@ -121,5 +128,339 @@ void main() { expect(find.text('@$formattedDate'), findsOneWidget); expect(find.byFlowySvg(FlowySvgs.reminder_clock_s), findsOneWidget); }); + + testWidgets("copy, cut and paste a date mention", (tester) async { + await tester.initializeAppFlowy(); + await tester.tapAnonymousSignInButton(); + + // create a new document + await tester.createNewPageWithNameUnderParent( + name: 'copy, cut and paste a date mention', + ); + + // tap the first line of the document + await tester.editor.tapLineOfEditorAt(0); + await tester.editor.showSlashMenu(); + await tester.editor.tapSlashMenuItemWithName( + LocaleKeys.document_slashMenu_name_dateOrReminder.tr(), + ); + + final dateTimeSettings = DateTimeSettingsPB( + dateFormat: UserDateFormatPB.Friendly, + timeFormat: UserTimeFormatPB.TwentyFourHour, + ); + final DateTime currentDateTime = DateTime.now(); + final String formattedDate = + dateTimeSettings.dateFormat.formatDate(currentDateTime, false); + + // get current date in editor + expect(find.byType(MentionDateBlock), findsOneWidget); + expect(find.text('@$formattedDate'), findsOneWidget); + + // update selection and copy + await tester.editor.updateSelection( + Selection( + start: Position(path: [0]), + end: Position(path: [0], offset: 1), + ), + ); + await tester.simulateKeyEvent( + LogicalKeyboardKey.keyC, + isControlPressed: Platform.isLinux || Platform.isWindows, + isMetaPressed: Platform.isMacOS, + ); + await tester.pumpAndSettle(); + + // update selection and paste + await tester.editor.updateSelection( + Selection.collapsed(Position(path: [0], offset: 1)), + ); + await tester.simulateKeyEvent( + LogicalKeyboardKey.keyV, + isControlPressed: Platform.isLinux || Platform.isWindows, + isMetaPressed: Platform.isMacOS, + ); + await tester.pumpAndSettle(); + + expect(find.byType(MentionDateBlock), findsNWidgets(2)); + expect(find.text('@$formattedDate'), findsNWidgets(2)); + + // update selection and cut + await tester.editor.updateSelection( + Selection( + start: Position(path: [0], offset: 1), + end: Position(path: [0], offset: 2), + ), + ); + await tester.simulateKeyEvent( + LogicalKeyboardKey.keyX, + isControlPressed: Platform.isLinux || Platform.isWindows, + isMetaPressed: Platform.isMacOS, + ); + await tester.pumpAndSettle(); + + expect(find.byType(MentionDateBlock), findsOneWidget); + expect(find.text('@$formattedDate'), findsOneWidget); + + // update selection and paste + await tester.editor.updateSelection( + Selection.collapsed(Position(path: [0], offset: 1)), + ); + await tester.simulateKeyEvent( + LogicalKeyboardKey.keyV, + isControlPressed: Platform.isLinux || Platform.isWindows, + isMetaPressed: Platform.isMacOS, + ); + await tester.pumpAndSettle(); + + expect(find.byType(MentionDateBlock), findsNWidgets(2)); + expect(find.text('@$formattedDate'), findsNWidgets(2)); + }); + + testWidgets("copy, cut and paste a reminder mention", (tester) async { + await tester.initializeAppFlowy(); + await tester.tapAnonymousSignInButton(); + + // create a new document + await tester.createNewPageWithNameUnderParent( + name: 'copy, cut and paste a reminder mention', + ); + + // tap the first line of the document + await tester.editor.tapLineOfEditorAt(0); + await tester.editor.showSlashMenu(); + await tester.editor.tapSlashMenuItemWithName( + LocaleKeys.document_slashMenu_name_dateOrReminder.tr(), + ); + + // trigger popup + await tester.tapButton(find.byType(MentionDateBlock)); + await tester.pumpAndSettle(); + + // set date to be fifteenth of the next month + await tester.tap( + find.descendant( + of: find.byType(DesktopAppFlowyDatePicker), + matching: find.byFlowySvg(FlowySvgs.arrow_right_s), + ), + ); + await tester.pumpAndSettle(); + await tester.tap( + find.descendant( + of: find.byType(TableCalendar), + matching: find.text(15.toString()), + ), + ); + await tester.pumpAndSettle(); + await tester.simulateKeyEvent(LogicalKeyboardKey.escape); + await tester.pumpAndSettle(); + + // add a reminder + await tester.tap(find.byType(MentionDateBlock)); + await tester.pumpAndSettle(); + await tester.tap(find.text(LocaleKeys.datePicker_reminderLabel.tr())); + await tester.pumpAndSettle(); + await tester.tap( + find.textContaining( + LocaleKeys.datePicker_reminderOptions_oneDayBefore.tr(), + ), + ); + await tester.pumpAndSettle(); + await tester.simulateKeyEvent(LogicalKeyboardKey.escape); + await tester.pumpAndSettle(); + + // verify + final dateTimeSettings = DateTimeSettingsPB( + dateFormat: UserDateFormatPB.Friendly, + timeFormat: UserTimeFormatPB.TwentyFourHour, + ); + final now = DateTime.now(); + final fifteenthOfNextMonth = DateTime(now.year, now.month + 1, 15); + final formattedDate = + dateTimeSettings.dateFormat.formatDate(fifteenthOfNextMonth, false); + + expect(find.byType(MentionDateBlock), findsOneWidget); + expect(find.text('@$formattedDate'), findsOneWidget); + expect(find.byFlowySvg(FlowySvgs.reminder_clock_s), findsOneWidget); + expect(getIt().state.reminders.map((e) => e.id).length, 1); + + // update selection and copy + await tester.editor.updateSelection( + Selection( + start: Position(path: [0]), + end: Position(path: [0], offset: 1), + ), + ); + await tester.simulateKeyEvent( + LogicalKeyboardKey.keyC, + isControlPressed: Platform.isLinux || Platform.isWindows, + isMetaPressed: Platform.isMacOS, + ); + await tester.pumpAndSettle(); + + // update selection and paste + await tester.editor.updateSelection( + Selection.collapsed(Position(path: [0], offset: 1)), + ); + await tester.simulateKeyEvent( + LogicalKeyboardKey.keyV, + isControlPressed: Platform.isLinux || Platform.isWindows, + isMetaPressed: Platform.isMacOS, + ); + await tester.pumpAndSettle(); + + expect(find.byType(MentionDateBlock), findsNWidgets(2)); + expect(find.text('@$formattedDate'), findsNWidgets(2)); + expect(find.byFlowySvg(FlowySvgs.reminder_clock_s), findsNWidgets(2)); + expect( + getIt().state.reminders.map((e) => e.id).toSet().length, + 2, + ); + + // update selection and cut + await tester.editor.updateSelection( + Selection( + start: Position(path: [0], offset: 1), + end: Position(path: [0], offset: 2), + ), + ); + await tester.simulateKeyEvent( + LogicalKeyboardKey.keyX, + isControlPressed: Platform.isLinux || Platform.isWindows, + isMetaPressed: Platform.isMacOS, + ); + await tester.pumpAndSettle(); + + expect(find.byType(MentionDateBlock), findsOneWidget); + expect(find.text('@$formattedDate'), findsOneWidget); + expect(find.byFlowySvg(FlowySvgs.reminder_clock_s), findsOneWidget); + expect(getIt().state.reminders.map((e) => e.id).length, 1); + + // update selection and paste + await tester.editor.updateSelection( + Selection.collapsed(Position(path: [0], offset: 1)), + ); + await tester.simulateKeyEvent( + LogicalKeyboardKey.keyV, + isControlPressed: Platform.isLinux || Platform.isWindows, + isMetaPressed: Platform.isMacOS, + ); + await tester.pumpAndSettle(); + + expect(find.byType(MentionDateBlock), findsNWidgets(2)); + expect(find.text('@$formattedDate'), findsNWidgets(2)); + expect(find.byType(MentionDateBlock), findsNWidgets(2)); + expect(find.text('@$formattedDate'), findsNWidgets(2)); + expect(find.byFlowySvg(FlowySvgs.reminder_clock_s), findsNWidgets(2)); + expect( + getIt().state.reminders.map((e) => e.id).toSet().length, + 2, + ); + }); + + testWidgets("delete, undo and redo a reminder mention", (tester) async { + await tester.initializeAppFlowy(); + await tester.tapAnonymousSignInButton(); + + // create a new document + await tester.createNewPageWithNameUnderParent( + name: 'delete, undo and redo a reminder mention', + ); + + // tap the first line of the document + await tester.editor.tapLineOfEditorAt(0); + await tester.editor.showSlashMenu(); + await tester.editor.tapSlashMenuItemWithName( + LocaleKeys.document_slashMenu_name_dateOrReminder.tr(), + ); + + // trigger popup + await tester.tapButton(find.byType(MentionDateBlock)); + await tester.pumpAndSettle(); + + // set date to be fifteenth of the next month + await tester.tap( + find.descendant( + of: find.byType(DesktopAppFlowyDatePicker), + matching: find.byFlowySvg(FlowySvgs.arrow_right_s), + ), + ); + await tester.pumpAndSettle(); + await tester.tap( + find.descendant( + of: find.byType(TableCalendar), + matching: find.text(15.toString()), + ), + ); + await tester.pumpAndSettle(); + await tester.simulateKeyEvent(LogicalKeyboardKey.escape); + await tester.pumpAndSettle(); + + // add a reminder + await tester.tap(find.byType(MentionDateBlock)); + await tester.pumpAndSettle(); + await tester.tap(find.text(LocaleKeys.datePicker_reminderLabel.tr())); + await tester.pumpAndSettle(); + await tester.tap( + find.textContaining( + LocaleKeys.datePicker_reminderOptions_oneDayBefore.tr(), + ), + ); + await tester.pumpAndSettle(); + await tester.simulateKeyEvent(LogicalKeyboardKey.escape); + await tester.pumpAndSettle(); + + // verify + final dateTimeSettings = DateTimeSettingsPB( + dateFormat: UserDateFormatPB.Friendly, + timeFormat: UserTimeFormatPB.TwentyFourHour, + ); + final now = DateTime.now(); + final fifteenthOfNextMonth = DateTime(now.year, now.month + 1, 15); + final formattedDate = + dateTimeSettings.dateFormat.formatDate(fifteenthOfNextMonth, false); + + expect(find.byType(MentionDateBlock), findsOneWidget); + expect(find.text('@$formattedDate'), findsOneWidget); + expect(find.byFlowySvg(FlowySvgs.reminder_clock_s), findsOneWidget); + expect(getIt().state.reminders.map((e) => e.id).length, 1); + + // update selection and backspace to delete the mention + await tester.editor.updateSelection( + Selection.collapsed(Position(path: [0], offset: 1)), + ); + await tester.simulateKeyEvent(LogicalKeyboardKey.backspace); + await tester.pumpAndSettle(); + + expect(find.byType(MentionDateBlock), findsNothing); + expect(find.text('@$formattedDate'), findsNothing); + expect(find.byFlowySvg(FlowySvgs.reminder_clock_s), findsNothing); + expect(getIt().state.reminders.isEmpty, isTrue); + + // undo + await tester.simulateKeyEvent( + LogicalKeyboardKey.keyZ, + isControlPressed: Platform.isWindows || Platform.isLinux, + isMetaPressed: Platform.isMacOS, + ); + + expect(find.byType(MentionDateBlock), findsOneWidget); + expect(find.text('@$formattedDate'), findsOneWidget); + expect(find.byFlowySvg(FlowySvgs.reminder_clock_s), findsOneWidget); + expect(getIt().state.reminders.map((e) => e.id).length, 1); + + // redo + await tester.simulateKeyEvent( + LogicalKeyboardKey.keyZ, + isControlPressed: Platform.isWindows || Platform.isLinux, + isMetaPressed: Platform.isMacOS, + isShiftPressed: true, + ); + + expect(find.byType(MentionDateBlock), findsNothing); + expect(find.text('@$formattedDate'), findsNothing); + expect(find.byFlowySvg(FlowySvgs.reminder_clock_s), findsNothing); + expect(getIt().state.reminders.isEmpty, isTrue); + }); }); } diff --git a/frontend/appflowy_flutter/lib/core/helpers/url_launcher.dart b/frontend/appflowy_flutter/lib/core/helpers/url_launcher.dart index 94d2074c6b..2b0bc7b345 100644 --- a/frontend/appflowy_flutter/lib/core/helpers/url_launcher.dart +++ b/frontend/appflowy_flutter/lib/core/helpers/url_launcher.dart @@ -1,16 +1,24 @@ -import 'package:flutter/services.dart'; -import 'package:flutter/widgets.dart'; +import 'dart:io'; import 'package:appflowy/generated/locale_keys.g.dart'; +import 'package:appflowy/shared/patterns/common_patterns.dart'; import 'package:appflowy/workspace/presentation/home/toast.dart'; +import 'package:appflowy/workspace/presentation/widgets/dialogs.dart'; import 'package:appflowy_backend/log.dart'; import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter/widgets.dart'; +import 'package:open_filex/open_filex.dart'; import 'package:string_validator/string_validator.dart'; import 'package:url_launcher/url_launcher.dart' as launcher; typedef OnFailureCallback = void Function(Uri uri); -Future afLaunchUrl( +/// Launch the uri +/// +/// If the uri is a local file path, it will be opened with the OpenFilex. +/// Otherwise, it will be launched with the url_launcher. +Future afLaunchUri( Uri uri, { BuildContext? context, OnFailureCallback? onFailure, @@ -18,6 +26,18 @@ Future afLaunchUrl( String? webOnlyWindowName, bool addingHttpSchemeWhenFailed = false, }) async { + final url = uri.toString(); + final decodedUrl = Uri.decodeComponent(url); + + // check if the uri is the local file path + if (localPathRegex.hasMatch(decodedUrl)) { + return _afLaunchLocalUri( + uri, + context: context, + onFailure: onFailure, + ); + } + // try to launch the uri directly bool result; try { @@ -32,7 +52,7 @@ Future afLaunchUrl( } // if the uri is not a valid url, try to launch it with http scheme - final url = uri.toString(); + if (addingHttpSchemeWhenFailed && !result && !isURL(url, {'require_protocol': true})) { @@ -54,9 +74,14 @@ Future afLaunchUrl( return result; } +/// Launch the url string +/// +/// See [afLaunchUri] for more details. Future afLaunchUrlString( String url, { bool addingHttpSchemeWhenFailed = false, + BuildContext? context, + OnFailureCallback? onFailure, }) async { final Uri uri; try { @@ -67,12 +92,56 @@ Future afLaunchUrlString( } // try to launch the uri directly - return afLaunchUrl( + return afLaunchUri( uri, addingHttpSchemeWhenFailed: addingHttpSchemeWhenFailed, + context: context, + onFailure: onFailure, ); } +/// Launch the local uri +/// +/// See [afLaunchUri] for more details. +Future _afLaunchLocalUri( + Uri uri, { + BuildContext? context, + OnFailureCallback? onFailure, +}) async { + final decodedUrl = Uri.decodeComponent(uri.toString()); + // open the file with the OpenfileX + var result = await OpenFilex.open(decodedUrl); + if (result.type != ResultType.done) { + // For the file cant be opened, fallback to open the folder + final parentFolder = Directory(decodedUrl).parent.path; + result = await OpenFilex.open(parentFolder); + } + // show the toast if the file is not found + final message = switch (result.type) { + ResultType.done => LocaleKeys.openFileMessage_success.tr(), + ResultType.fileNotFound => LocaleKeys.openFileMessage_fileNotFound.tr(), + ResultType.noAppToOpen => LocaleKeys.openFileMessage_noAppToOpenFile.tr(), + ResultType.permissionDenied => + LocaleKeys.openFileMessage_permissionDenied.tr(), + ResultType.error => LocaleKeys.failedToOpenUrl.tr(), + }; + if (context != null && context.mounted) { + showToastNotification( + context, + message: message, + type: result.type == ResultType.done + ? ToastificationType.success + : ToastificationType.error, + ); + } + final openFileSuccess = result.type == ResultType.done; + if (!openFileSuccess && onFailure != null) { + onFailure(uri); + Log.error('Failed to open file: $result.message'); + } + return openFileSuccess; +} + void _errorHandler( Uri uri, { BuildContext? context, diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/base/view_page/more_bottom_sheet.dart b/frontend/appflowy_flutter/lib/mobile/presentation/base/view_page/more_bottom_sheet.dart index eba8b09025..25541ed7d4 100644 --- a/frontend/appflowy_flutter/lib/mobile/presentation/base/view_page/more_bottom_sheet.dart +++ b/frontend/appflowy_flutter/lib/mobile/presentation/base/view_page/more_bottom_sheet.dart @@ -189,7 +189,7 @@ class MobileViewPageMoreBottomSheet extends StatelessWidget { final url = context.read().state.url; if (url.isNotEmpty) { unawaited( - afLaunchUrl( + afLaunchUri( Uri.parse(url), mode: LaunchMode.externalApplication, ), diff --git a/frontend/appflowy_flutter/lib/plugins/ai_chat/application/chat_bloc.dart b/frontend/appflowy_flutter/lib/plugins/ai_chat/application/chat_bloc.dart index 5c0cdabe67..09d7ed2417 100644 --- a/frontend/appflowy_flutter/lib/plugins/ai_chat/application/chat_bloc.dart +++ b/frontend/appflowy_flutter/lib/plugins/ai_chat/application/chat_bloc.dart @@ -59,6 +59,16 @@ class ChatBloc extends Bloc { AnswerStream? answerStream; int numSendMessage = 0; + /// a counter used to determine whether the initial loading state should be + /// set to finish. It should hit two before we emit: one for the local fetch + /// and another for the server fetch. + /// + /// This is to work around a bug where if an ai chat that is not yet on the + /// user local storage is opened but has messages in the server, it will + /// remain stuck on the welcome screen until the user switches to another page + /// then come back. + int initialFetchCounter = 0; + @override Future close() async { await answerStream?.dispose(); @@ -76,7 +86,11 @@ class ChatBloc extends Bloc { await chatController.insert(message, index: 0); } - if (state.loadingState.isLoading) { + if (initialFetchCounter < 2) { + initialFetchCounter++; + } + + if (state.loadingState.isLoading && initialFetchCounter >= 2) { emit( state.copyWith(loadingState: const ChatLoadingState.finish()), ); diff --git a/frontend/appflowy_flutter/lib/plugins/database/widgets/cell/desktop_grid/desktop_grid_media_cell.dart b/frontend/appflowy_flutter/lib/plugins/database/widgets/cell/desktop_grid/desktop_grid_media_cell.dart index bcf136bcf1..f66d106ed0 100644 --- a/frontend/appflowy_flutter/lib/plugins/database/widgets/cell/desktop_grid/desktop_grid_media_cell.dart +++ b/frontend/appflowy_flutter/lib/plugins/database/widgets/cell/desktop_grid/desktop_grid_media_cell.dart @@ -1,24 +1,23 @@ import 'package:appflowy/core/helpers/url_launcher.dart'; -import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet_media_upload.dart'; -import 'package:appflowy/plugins/document/presentation/editor_plugins/image/common.dart'; -import 'package:appflowy/workspace/presentation/widgets/image_viewer/image_provider.dart'; -import 'package:appflowy/workspace/presentation/widgets/image_viewer/interactive_image_viewer.dart'; -import 'package:flutter/material.dart'; - import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart'; +import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet_media_upload.dart'; import 'package:appflowy/plugins/database/application/cell/bloc/media_cell_bloc.dart'; import 'package:appflowy/plugins/database/widgets/cell/editable_cell_skeleton/media.dart'; import 'package:appflowy/plugins/database/widgets/cell_editor/media_cell_editor.dart'; import 'package:appflowy/plugins/database/widgets/cell_editor/mobile_media_cell_editor.dart'; import 'package:appflowy/plugins/database/widgets/media_file_type_ext.dart'; import 'package:appflowy/plugins/database/widgets/row/cells/cell_container.dart'; +import 'package:appflowy/plugins/document/presentation/editor_plugins/image/common.dart'; import 'package:appflowy/shared/af_image.dart'; +import 'package:appflowy/workspace/presentation/widgets/image_viewer/image_provider.dart'; +import 'package:appflowy/workspace/presentation/widgets/image_viewer/interactive_image_viewer.dart'; import 'package:appflowy_backend/protobuf/flowy-database2/media_entities.pb.dart'; import 'package:easy_localization/easy_localization.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:universal_platform/universal_platform.dart'; @@ -159,7 +158,7 @@ class GridMediaCellSkin extends IEditableMediaCellSkin { List files, ) { if (file.fileType != MediaFileTypePB.Image) { - afLaunchUrlString(file.url); + afLaunchUrlString(file.url, context: context); return; } diff --git a/frontend/appflowy_flutter/lib/plugins/database/widgets/cell/desktop_row_detail/desktop_row_detail_date_cell.dart b/frontend/appflowy_flutter/lib/plugins/database/widgets/cell/desktop_row_detail/desktop_row_detail_date_cell.dart index 9d0c0f0324..f10f9a9279 100644 --- a/frontend/appflowy_flutter/lib/plugins/database/widgets/cell/desktop_row_detail/desktop_row_detail_date_cell.dart +++ b/frontend/appflowy_flutter/lib/plugins/database/widgets/cell/desktop_row_detail/desktop_row_detail_date_cell.dart @@ -31,6 +31,7 @@ class DesktopRowDetailDateCellSkin extends IEditableDateCellSkin { direction: PopoverDirection.bottomWithLeftAligned, constraints: BoxConstraints.loose(const Size(260, 620)), margin: EdgeInsets.zero, + asBarrier: true, child: Container( alignment: AlignmentDirectional.centerStart, padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6), diff --git a/frontend/appflowy_flutter/lib/plugins/database/widgets/cell/desktop_row_detail/desktop_row_detail_media_cell.dart b/frontend/appflowy_flutter/lib/plugins/database/widgets/cell/desktop_row_detail/desktop_row_detail_media_cell.dart index 772149fa59..34d8a18ad8 100644 --- a/frontend/appflowy_flutter/lib/plugins/database/widgets/cell/desktop_row_detail/desktop_row_detail_media_cell.dart +++ b/frontend/appflowy_flutter/lib/plugins/database/widgets/cell/desktop_row_detail/desktop_row_detail_media_cell.dart @@ -27,7 +27,6 @@ import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flowy_infra_ui/style_widget/hover.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:open_filex/open_filex.dart'; import 'package:reorderables/reorderables.dart'; const _dropFileKey = 'files_media'; @@ -253,6 +252,7 @@ class _AddFileButtonState extends State<_AddFileButton> { direction: widget.direction, constraints: const BoxConstraints(maxWidth: _menuWidth), margin: EdgeInsets.zero, + asBarrier: true, onClose: () => context.read().remove(_dropFileKey), popupBuilder: (_) { @@ -459,6 +459,7 @@ class _FilePreviewRenderState extends State<_FilePreviewRender> { offset: const Offset(0, 5), triggerActions: PopoverTriggerFlags.none, onClose: () => setState(() => isSelected = false), + asBarrier: true, popupBuilder: (popoverContext) => MultiBlocProvider( providers: [ BlocProvider.value(value: context.read()), @@ -479,7 +480,7 @@ class _FilePreviewRenderState extends State<_FilePreviewRender> { ? null : () { if (file.uploadType == FileUploadTypePB.LocalFile) { - OpenFilex.open(file.url); + afLaunchUrlString(file.url); return; } diff --git a/frontend/appflowy_flutter/lib/plugins/database/widgets/cell/desktop_row_detail/desktop_row_detail_relation_cell.dart b/frontend/appflowy_flutter/lib/plugins/database/widgets/cell/desktop_row_detail/desktop_row_detail_relation_cell.dart index 996d04267c..b3096d49e4 100644 --- a/frontend/appflowy_flutter/lib/plugins/database/widgets/cell/desktop_row_detail/desktop_row_detail_relation_cell.dart +++ b/frontend/appflowy_flutter/lib/plugins/database/widgets/cell/desktop_row_detail/desktop_row_detail_relation_cell.dart @@ -24,6 +24,7 @@ class DesktopRowDetailRelationCellSkin extends IEditableRelationCellSkin { direction: PopoverDirection.bottomWithLeftAligned, constraints: const BoxConstraints(maxWidth: 400, maxHeight: 400), margin: EdgeInsets.zero, + asBarrier: true, onClose: () => cellContainerNotifier.isFocus = false, popupBuilder: (context) { return BlocProvider.value( diff --git a/frontend/appflowy_flutter/lib/plugins/database/widgets/cell/desktop_row_detail/desktop_row_detail_select_option_cell.dart b/frontend/appflowy_flutter/lib/plugins/database/widgets/cell/desktop_row_detail/desktop_row_detail_select_option_cell.dart index 6eab6438dc..3d41a824ce 100644 --- a/frontend/appflowy_flutter/lib/plugins/database/widgets/cell/desktop_row_detail/desktop_row_detail_select_option_cell.dart +++ b/frontend/appflowy_flutter/lib/plugins/database/widgets/cell/desktop_row_detail/desktop_row_detail_select_option_cell.dart @@ -25,6 +25,7 @@ class DesktopRowDetailSelectOptionCellSkin controller: popoverController, constraints: const BoxConstraints.tightFor(width: 300), margin: EdgeInsets.zero, + asBarrier: true, triggerActions: PopoverTriggerFlags.none, direction: PopoverDirection.bottomWithLeftAligned, onClose: () => cellContainerNotifier.isFocus = false, diff --git a/frontend/appflowy_flutter/lib/plugins/database/widgets/field/field_editor.dart b/frontend/appflowy_flutter/lib/plugins/database/widgets/field/field_editor.dart index d91f207797..4a9fedcd94 100644 --- a/frontend/appflowy_flutter/lib/plugins/database/widgets/field/field_editor.dart +++ b/frontend/appflowy_flutter/lib/plugins/database/widgets/field/field_editor.dart @@ -199,22 +199,30 @@ class FieldActionCell extends StatelessWidget { (action == FieldAction.duplicate || action == FieldAction.delete)) { enable = false; } - - return FlowyButton( + return FlowyIconTextButton( resetHoverOnRebuild: false, disable: !enable, - text: FlowyText( - action.title(fieldInfo), - lineHeight: 1.0, - color: enable ? null : Theme.of(context).disabledColor, - ), onHover: (_) => popoverMutex?.close(), onTap: () => action.run(context, viewId, fieldInfo), - leftIcon: action.leading( - fieldInfo, - enable ? null : Theme.of(context).disabledColor, + // show the error color when delete is hovered + textBuilder: (onHover) => FlowyText( + action.title(fieldInfo), + lineHeight: 1.0, + color: enable + ? action == FieldAction.delete && onHover + ? Theme.of(context).colorScheme.error + : null + : Theme.of(context).disabledColor, ), - rightIcon: action.trailing(context, fieldInfo), + leftIconBuilder: (onHover) => action.leading( + fieldInfo, + enable + ? action == FieldAction.delete && onHover + ? Theme.of(context).colorScheme.error + : null + : Theme.of(context).disabledColor, + ), + rightIconBuilder: (_) => action.trailing(context, fieldInfo), ); } } diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/base/page_reference_commands.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/base/page_reference_commands.dart index e42bb3a2bf..079a062837 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/base/page_reference_commands.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/base/page_reference_commands.dart @@ -117,6 +117,22 @@ Future inlinePageReferenceCommandHandler( initialResults: initialResults, style: style, startCharAmount: previousChar != null ? 2 : 1, + cancelBySpaceHandler: () { + if (character == _plusChar) { + final currentSelection = editorState.selection; + if (currentSelection == null) { + return false; + } + // check if the space is after the character + if (currentSelection.isCollapsed && + currentSelection.start.offset == + selection.start.offset + character.length) { + _cancelInlinePageReferenceMenu(editorState); + return true; + } + } + return false; + }, ); selectionMenuService?.show(); @@ -124,3 +140,17 @@ Future inlinePageReferenceCommandHandler( return true; } + +void _cancelInlinePageReferenceMenu(EditorState editorState) { + selectionMenuService?.dismiss(); + selectionMenuService = null; + + // re-focus the selection + final selection = editorState.selection; + if (selection != null) { + editorState.updateSelectionWithReason( + selection, + reason: SelectionUpdateReason.uiEvent, + ); + } +} diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/file/file_block_component.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/file/file_block_component.dart index e4dd1d4e4a..f1ffbe9d11 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/file/file_block_component.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/file/file_block_component.dart @@ -10,7 +10,6 @@ import 'package:appflowy/plugins/document/presentation/editor_plugins/copy_and_p import 'package:appflowy/plugins/document/presentation/editor_plugins/file/file_util.dart'; import 'package:appflowy/startup/startup.dart'; import 'package:appflowy/workspace/presentation/home/toast.dart'; -import 'package:appflowy/workspace/presentation/widgets/dialogs.dart'; import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:cross_file/cross_file.dart'; import 'package:desktop_drop/desktop_drop.dart'; @@ -19,7 +18,6 @@ import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flowy_infra_ui/style_widget/hover.dart'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; -import 'package:open_filex/open_filex.dart'; import 'package:provider/provider.dart'; import 'package:string_validator/string_validator.dart'; import 'package:universal_platform/universal_platform.dart'; @@ -323,22 +321,7 @@ class FileBlockComponentState extends State FileUrlType urlType, String url, ) async { - if ([FileUrlType.cloud, FileUrlType.network].contains(urlType)) { - await afLaunchUrlString(url); - } else { - final result = await OpenFilex.open(url); - if (result.type == ResultType.done) { - return; - } - - if (context.mounted) { - showToastNotification( - context, - message: LocaleKeys.document_plugins_file_failedToOpenMsg.tr(), - type: ToastificationType.error, - ); - } - } + await afLaunchUrlString(url, context: context); } void _openMenu() { diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/file/file_util.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/file/file_util.dart index f09aa1ca5e..3ab93b4c95 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/file/file_util.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/file/file_util.dart @@ -1,8 +1,6 @@ import 'dart:convert'; import 'dart:io'; -import 'package:flutter/material.dart'; - import 'package:appflowy/core/helpers/url_launcher.dart'; import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/plugins/document/application/document_service.dart'; @@ -22,6 +20,7 @@ import 'package:cross_file/cross_file.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flowy_infra/file_picker/file_picker_impl.dart'; import 'package:flowy_infra/uuid.dart'; +import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; import 'package:path/path.dart' as p; import 'package:universal_platform/universal_platform.dart'; @@ -102,7 +101,7 @@ Future downloadMediaFile( FileUploadTypePB.LocalFile, ].contains(file.uploadType)) { /// When the file is a network file or a local file, we can directly open the file. - await afLaunchUrl(Uri.parse(file.url)); + await afLaunchUrlString(file.url); } else { if (userProfile == null) { return showToastNotification( diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mention/child_page_transaction_handler.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mention/child_page_transaction_handler.dart index 491bf10fa5..ef32ad1098 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mention/child_page_transaction_handler.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mention/child_page_transaction_handler.dart @@ -16,11 +16,12 @@ import '../transaction_handler/mention_transaction_handler.dart'; const _pasteIdentifier = 'child_page_transaction'; class ChildPageTransactionHandler extends MentionTransactionHandler { - ChildPageTransactionHandler() : super(subType: MentionType.childPage.name); + ChildPageTransactionHandler(); @override Future onTransaction( BuildContext context, + String viewId, EditorState editorState, List added, List removed, { diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mention/date_transaction_handler.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mention/date_transaction_handler.dart new file mode 100644 index 0000000000..972ed229dd --- /dev/null +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mention/date_transaction_handler.dart @@ -0,0 +1,263 @@ +import 'package:appflowy/shared/clipboard_state.dart'; +import 'package:appflowy/startup/startup.dart'; +import 'package:appflowy/user/application/reminder/reminder_bloc.dart'; +import 'package:appflowy/user/application/reminder/reminder_extension.dart'; +import 'package:appflowy/workspace/presentation/widgets/date_picker/widgets/reminder_selector.dart'; +import 'package:appflowy_backend/log.dart'; +import 'package:appflowy_editor/appflowy_editor.dart'; +import 'package:collection/collection.dart'; +import 'package:fixnum/fixnum.dart'; +import 'package:flutter/material.dart'; +import 'package:nanoid/nanoid.dart'; +import 'package:provider/provider.dart'; + +import '../plugins.dart'; +import '../transaction_handler/mention_transaction_handler.dart'; + +const _pasteIdentifier = 'date_transaction'; + +class DateTransactionHandler extends MentionTransactionHandler { + DateTransactionHandler(); + + @override + Future onTransaction( + BuildContext context, + String viewId, + EditorState editorState, + List added, + List removed, { + bool isCut = false, + bool isUndoRedo = false, + bool isPaste = false, + bool isDraggingNode = false, + bool isTurnInto = false, + String? parentViewId, + }) async { + if (isDraggingNode || isTurnInto) { + return; + } + + // Remove the mentions that were both added and removed in the same transaction. + // These were just moved around. + final moved = []; + for (final mention in added) { + if (removed.any((r) => r.$2 == mention.$2)) { + moved.add(mention); + } + } + + for (final mention in removed) { + if (!context.mounted || moved.any((m) => m.$2 == mention.$2)) { + return; + } + + if (mention.$2[MentionBlockKeys.type] != MentionType.date.name) { + continue; + } + + _handleDeletion(context, mention); + } + + if (isPaste || isUndoRedo) { + if (context.mounted) { + context.read().startHandlingPaste(_pasteIdentifier); + } + + for (final mention in added) { + if (!context.mounted || moved.any((m) => m.$2 == mention.$2)) { + return; + } + + if (mention.$2[MentionBlockKeys.type] != MentionType.date.name) { + continue; + } + + _handleAddition( + context, + viewId, + editorState, + mention, + isPaste, + isCut, + ); + } + + if (context.mounted) { + context.read().endHandlingPaste(_pasteIdentifier); + } + } + } + + void _handleDeletion( + BuildContext context, + MentionBlockData data, + ) { + final reminderId = data.$2[MentionBlockKeys.reminderId]; + + if (reminderId case String _ when reminderId.isNotEmpty) { + getIt().add(ReminderEvent.remove(reminderId: reminderId)); + } + } + + void _handleAddition( + BuildContext context, + String viewId, + EditorState editorState, + MentionBlockData data, + bool isPaste, + bool isCut, + ) { + final dateData = _MentionDateBlockData.fromData(data.$2); + if (dateData.dateString.isEmpty) { + Log.error("mention date block doesn't have a valid date string"); + return; + } + + if (isPaste && !isCut) { + _handlePasteFromCopy( + context, + viewId, + editorState, + data.$1, + data.$3, + dateData, + ); + } else { + _handlePasteFromCut(viewId, data.$1, dateData); + } + } + + void _handlePasteFromCut( + String viewId, + Node node, + _MentionDateBlockData data, + ) { + final dateTime = DateTime.tryParse(data.dateString); + + if (data.reminderId == null || dateTime == null) { + return; + } + + getIt().add( + ReminderEvent.addById( + reminderId: data.reminderId!, + objectId: viewId, + scheduledAt: Int64( + data.reminderOption + .getNotificationDateTime(dateTime) + .millisecondsSinceEpoch ~/ + 1000, + ), + meta: { + ReminderMetaKeys.includeTime: data.includeTime.toString(), + ReminderMetaKeys.blockId: node.id, + }, + ), + ); + } + + void _handlePasteFromCopy( + BuildContext context, + String viewId, + EditorState editorState, + Node node, + int index, + _MentionDateBlockData data, + ) async { + final dateTime = DateTime.tryParse(data.dateString); + + if (node.delta == null) { + return; + } + + if (data.reminderId == null || dateTime == null) { + return; + } + + final reminderId = nanoid(); + getIt().add( + ReminderEvent.addById( + reminderId: reminderId, + objectId: viewId, + scheduledAt: Int64( + data.reminderOption + .getNotificationDateTime(dateTime) + .millisecondsSinceEpoch ~/ + 1000, + ), + meta: { + ReminderMetaKeys.includeTime: data.includeTime.toString(), + ReminderMetaKeys.blockId: node.id, + }, + ), + ); + + final newMentionAttributes = { + MentionBlockKeys.mention: { + MentionBlockKeys.type: MentionType.date.name, + MentionBlockKeys.date: dateTime.toIso8601String(), + MentionBlockKeys.reminderId: reminderId, + MentionBlockKeys.includeTime: data.includeTime, + MentionBlockKeys.reminderOption: data.reminderOption.name, + }, + }; + + // The index is the index of the delta, to get the index of the mention character + // in all the text, we need to calculate it based on the deltas before the current delta. + int mentionIndex = 0; + for (final (i, delta) in node.delta!.indexed) { + if (i >= index) { + break; + } + + mentionIndex += delta.length; + } + + // Required to prevent editing the same spot at the same time + await Future.delayed(const Duration(milliseconds: 100)); + + final transaction = editorState.transaction + ..formatText( + node, + mentionIndex, + MentionBlockKeys.mentionChar.length, + newMentionAttributes, + ); + + await editorState.apply( + transaction, + options: const ApplyOptions(recordUndo: false), + ); + } +} + +/// A helper class to parse and store the mention date block data +class _MentionDateBlockData { + _MentionDateBlockData.fromData(Map data) { + dateString = switch (data[MentionBlockKeys.date]) { + final String string when DateTime.tryParse(string) != null => string, + _ => "", + }; + includeTime = switch (data[MentionBlockKeys.includeTime]) { + final bool flag => flag, + _ => false, + }; + reminderOption = switch (data[MentionBlockKeys.reminderOption]) { + final String name => + ReminderOption.values.firstWhereOrNull((o) => o.name == name) ?? + ReminderOption.none, + _ => ReminderOption.none, + }; + reminderId = switch (data[MentionBlockKeys.reminderId]) { + final String id + when id.isNotEmpty && reminderOption != ReminderOption.none => + id, + _ => null, + }; + } + + late final String dateString; + late final bool includeTime; + late final String? reminderId; + late final ReminderOption reminderOption; +} diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mention/mention_block.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mention/mention_block.dart index cf2f0b8e11..a5bc340f71 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mention/mention_block.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mention/mention_block.dart @@ -8,7 +8,6 @@ import 'package:provider/provider.dart'; enum MentionType { page, - reminder, date, childPage; diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/document_markdown_parsers.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/document_markdown_parsers.dart index 0b694f396e..3f2895d57e 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/document_markdown_parsers.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/document_markdown_parsers.dart @@ -1,4 +1,7 @@ export 'callout_node_parser.dart'; export 'custom_image_node_parser.dart'; +export 'file_block_node_parser.dart'; +export 'link_preview_node_parser.dart'; export 'math_equation_node_parser.dart'; +export 'simple_table_node_parser.dart'; export 'toggle_list_node_parser.dart'; diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/file_block_node_parser.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/file_block_node_parser.dart new file mode 100644 index 0000000000..e57ededcec --- /dev/null +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/file_block_node_parser.dart @@ -0,0 +1,19 @@ +import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart'; +import 'package:appflowy_editor/appflowy_editor.dart'; + +class FileBlockNodeParser extends NodeParser { + const FileBlockNodeParser(); + + @override + String get id => FileBlockKeys.type; + + @override + String transform(Node node, DocumentMarkdownEncoder? encoder) { + final name = node.attributes[FileBlockKeys.name]; + final url = node.attributes[FileBlockKeys.url]; + if (name == null || url == null) { + return ''; + } + return '[$name]($url)\n'; + } +} diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/link_preview_node_parser.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/link_preview_node_parser.dart new file mode 100644 index 0000000000..c7ce69d221 --- /dev/null +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/link_preview_node_parser.dart @@ -0,0 +1,18 @@ +import 'package:appflowy_editor/appflowy_editor.dart'; +import 'package:appflowy_editor_plugins/appflowy_editor_plugins.dart'; + +class LinkPreviewNodeParser extends NodeParser { + const LinkPreviewNodeParser(); + + @override + String get id => LinkPreviewBlockKeys.type; + + @override + String transform(Node node, DocumentMarkdownEncoder? encoder) { + final href = node.attributes[LinkPreviewBlockKeys.url]; + if (href == null) { + return ''; + } + return '[$href]($href)\n'; + } +} diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/markdown_parsers.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/markdown_parsers.dart index 568baeaac0..4ad7734643 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/markdown_parsers.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/markdown_parsers.dart @@ -1,6 +1,2 @@ -export 'callout_node_parser.dart'; -export 'custom_image_node_parser.dart'; export 'markdown_code_parser.dart'; -export 'math_equation_node_parser.dart'; -export 'toggle_list_node_parser.dart'; -export 'simple_table_parser.dart'; +export 'markdown_simple_table_parser.dart'; diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/simple_table_parser.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/simple_table_node_parser.dart similarity index 100% rename from frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/simple_table_parser.dart rename to frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/parsers/simple_table_node_parser.dart diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/plugins.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/plugins.dart index da28f7f720..4d1fd95558 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/plugins.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/plugins.dart @@ -57,6 +57,7 @@ export 'openai/widgets/ai_writer_block_component.dart'; export 'openai/widgets/ask_ai_block_component.dart'; export 'openai/widgets/ask_ai_toolbar_item.dart'; export 'outline/outline_block_component.dart'; +export 'parsers/document_markdown_parsers.dart'; export 'parsers/markdown_parsers.dart'; export 'parsers/markdown_simple_table_parser.dart'; export 'quote/quote_block_shortcuts.dart'; diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/sub_page/sub_page_transaction_handler.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/sub_page/sub_page_transaction_handler.dart index b7d784d534..c5c7398bdb 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/sub_page/sub_page_transaction_handler.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/sub_page/sub_page_transaction_handler.dart @@ -21,6 +21,7 @@ class SubPageTransactionHandler extends BlockTransactionHandler { @override Future onTransaction( BuildContext context, + String viewId, EditorState editorState, List added, List removed, { diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/transaction_handler/editor_transaction_handler.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/transaction_handler/editor_transaction_handler.dart index 334f649341..243532e8ce 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/transaction_handler/editor_transaction_handler.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/transaction_handler/editor_transaction_handler.dart @@ -25,6 +25,7 @@ abstract class EditorTransactionHandler { Future onTransaction( BuildContext context, + String viewId, EditorState editorState, List added, List removed, { diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/transaction_handler/editor_transaction_service.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/transaction_handler/editor_transaction_service.dart index 29cc7435ff..b56066ae8b 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/transaction_handler/editor_transaction_service.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/transaction_handler/editor_transaction_service.dart @@ -2,20 +2,23 @@ import 'dart:async'; import 'package:appflowy/plugins/document/presentation/editor_notification.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/mention/child_page_transaction_handler.dart'; +import 'package:appflowy/plugins/document/presentation/editor_plugins/mention/date_transaction_handler.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/sub_page/sub_page_transaction_handler.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/transaction_handler/editor_transaction_handler.dart'; -import 'package:appflowy/plugins/document/presentation/editor_plugins/transaction_handler/mention_transaction_handler.dart'; import 'package:appflowy/shared/clipboard_state.dart'; import 'package:appflowy/shared/feature_flags.dart'; import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'mention_transaction_handler.dart'; + final _transactionHandlers = [ if (FeatureFlag.inlineSubPageMention.isOn) ...[ SubPageTransactionHandler(), ChildPageTransactionHandler(), ], + DateTransactionHandler(), ]; /// Handles delegating transactions to appropriate handlers. @@ -148,10 +151,16 @@ class _EditorTransactionServiceState extends State { handler.type: handler.livesInDelta ? [] : [], }; + // based on the type of the transaction handler + final uniqueTransactionHandlers = {}; + for (final handler in _transactionHandlers) { + uniqueTransactionHandlers.putIfAbsent(handler.type, () => handler); + } + for (final op in transaction.operations) { if (op is InsertOperation) { for (final n in op.nodes) { - for (final handler in _transactionHandlers) { + for (final handler in uniqueTransactionHandlers.values) { if (handler.livesInDelta) { added[handler.type]! .addAll(extractMentionsForType(n, handler.type)); @@ -163,7 +172,7 @@ class _EditorTransactionServiceState extends State { } } else if (op is DeleteOperation) { for (final n in op.nodes) { - for (final handler in _transactionHandlers) { + for (final handler in uniqueTransactionHandlers.values) { if (handler.livesInDelta) { removed[handler.type]!.addAll( extractMentionsForType(n, handler.type, false), @@ -191,8 +200,9 @@ class _EditorTransactionServiceState extends State { final (add, del) = diffDeltas(deltaBefore, deltaAfter); + bool fetchedMentions = false; for (final handler in _transactionHandlers) { - if (!handler.livesInDelta) { + if (!handler.livesInDelta || fetchedMentions) { continue; } @@ -212,6 +222,8 @@ class _EditorTransactionServiceState extends State { removed[handler.type]!.addAll(mentionBlockDatas); } + + fetchedMentions = true; } } } @@ -226,6 +238,7 @@ class _EditorTransactionServiceState extends State { handler.onTransaction( context, + widget.viewId, widget.editorState, additions, removals, diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/transaction_handler/mention_transaction_handler.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/transaction_handler/mention_transaction_handler.dart index 632561589e..d08ce05510 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/transaction_handler/mention_transaction_handler.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/transaction_handler/mention_transaction_handler.dart @@ -12,12 +12,6 @@ typedef MentionBlockData = (Node, Map, int); abstract class MentionTransactionHandler extends EditorTransactionHandler { - const MentionTransactionHandler({ - required this.subType, - }) + const MentionTransactionHandler() : super(type: MentionBlockKeys.mention, livesInDelta: true); - - final String subType; - - MentionType get mentionType => MentionType.fromString(subType); } diff --git a/frontend/appflowy_flutter/lib/plugins/inline_actions/inline_actions_menu.dart b/frontend/appflowy_flutter/lib/plugins/inline_actions/inline_actions_menu.dart index e31d7c2ef0..dadc4ebf6f 100644 --- a/frontend/appflowy_flutter/lib/plugins/inline_actions/inline_actions_menu.dart +++ b/frontend/appflowy_flutter/lib/plugins/inline_actions/inline_actions_menu.dart @@ -19,12 +19,14 @@ class InlineActionsMenu extends InlineActionsMenuService { required this.initialResults, required this.style, this.startCharAmount = 1, + this.cancelBySpaceHandler, }); final BuildContext context; final EditorState editorState; final InlineActionsService service; final List initialResults; + final bool Function()? cancelBySpaceHandler; @override final InlineActionsMenuStyle style; @@ -137,6 +139,7 @@ class InlineActionsMenu extends InlineActionsMenuService { onSelectionUpdate: _onSelectionUpdate, style: style, startCharAmount: startCharAmount, + cancelBySpaceHandler: cancelBySpaceHandler, ), ), ), diff --git a/frontend/appflowy_flutter/lib/plugins/inline_actions/widgets/inline_actions_handler.dart b/frontend/appflowy_flutter/lib/plugins/inline_actions/widgets/inline_actions_handler.dart index edf34a3dc1..be9a6c2f5f 100644 --- a/frontend/appflowy_flutter/lib/plugins/inline_actions/widgets/inline_actions_handler.dart +++ b/frontend/appflowy_flutter/lib/plugins/inline_actions/widgets/inline_actions_handler.dart @@ -62,6 +62,7 @@ class InlineActionsHandler extends StatefulWidget { required this.onSelectionUpdate, required this.style, this.startCharAmount = 1, + this.cancelBySpaceHandler, }); final InlineActionsService service; @@ -72,6 +73,7 @@ class InlineActionsHandler extends StatefulWidget { final VoidCallback onSelectionUpdate; final InlineActionsMenuStyle style; final int startCharAmount; + final bool Function()? cancelBySpaceHandler; @override State createState() => _InlineActionsHandlerState(); @@ -288,12 +290,17 @@ class _InlineActionsHandlerState extends State { /// that the selection change occurred from the handler. widget.onSelectionUpdate(); + if (event.logicalKey == LogicalKeyboardKey.space) { + final cancelBySpaceHandler = widget.cancelBySpaceHandler; + if (cancelBySpaceHandler != null && cancelBySpaceHandler()) { + return KeyEventResult.handled; + } + } + // Interpolation to avoid having a getter for private variable _insertCharacter(event.character!); return KeyEventResult.handled; - } - - if (moveKeys.contains(event.logicalKey)) { + } else if (moveKeys.contains(event.logicalKey)) { _moveSelection(event.logicalKey); return KeyEventResult.handled; } diff --git a/frontend/appflowy_flutter/lib/plugins/shared/share/share_bloc.dart b/frontend/appflowy_flutter/lib/plugins/shared/share/share_bloc.dart index c533f67ed8..c42bbda5a0 100644 --- a/frontend/appflowy_flutter/lib/plugins/shared/share/share_bloc.dart +++ b/frontend/appflowy_flutter/lib/plugins/shared/share/share_bloc.dart @@ -197,8 +197,23 @@ class ShareBloc extends Bloc { (p) => false, ); - Log.info( - 'get publish info: $publishInfo for view: ${view.name}(${view.id})', + // skip the "Record not found" error, it's because the view is not published yet + publishInfo.fold( + (s) { + Log.info( + 'get publish info success: $publishInfo for view: ${view.name}(${view.id})', + ); + }, + (f) { + if (![ + ErrorCode.RecordNotFound, + ErrorCode.LocalVersionNotSupport, + ].contains(f.code)) { + Log.info( + 'get publish info failed: $f for view: ${view.name}(${view.id})', + ); + } + }, ); String workspaceId = state.workspaceId; diff --git a/frontend/appflowy_flutter/lib/shared/markdown_to_document.dart b/frontend/appflowy_flutter/lib/shared/markdown_to_document.dart index 328c66eca9..b3e2034602 100644 --- a/frontend/appflowy_flutter/lib/shared/markdown_to_document.dart +++ b/frontend/appflowy_flutter/lib/shared/markdown_to_document.dart @@ -20,6 +20,8 @@ String customDocumentToMarkdown(Document document) { const ToggleListNodeParser(), const CustomImageNodeParser(), const SimpleTableNodeParser(), + const LinkPreviewNodeParser(), + const FileBlockNodeParser(), ], ); } diff --git a/frontend/appflowy_flutter/lib/shared/patterns/common_patterns.dart b/frontend/appflowy_flutter/lib/shared/patterns/common_patterns.dart index 20444f7205..ebd310a747 100644 --- a/frontend/appflowy_flutter/lib/shared/patterns/common_patterns.dart +++ b/frontend/appflowy_flutter/lib/shared/patterns/common_patterns.dart @@ -42,3 +42,6 @@ final appflowySharePageLinkRegex = RegExp(appflowySharePageLinkPattern); const _numberedListPattern = r'^(\d+)\.'; final numberedListRegex = RegExp(_numberedListPattern); + +const _localPathPattern = r'^(file:\/\/|\/|\\|[a-zA-Z]:[/\\]|\.{1,2}[/\\])'; +final localPathRegex = RegExp(_localPathPattern, caseSensitive: false); diff --git a/frontend/appflowy_flutter/lib/user/application/auth/af_cloud_auth_service.dart b/frontend/appflowy_flutter/lib/user/application/auth/af_cloud_auth_service.dart index ae7fe37982..149bddc951 100644 --- a/frontend/appflowy_flutter/lib/user/application/auth/af_cloud_auth_service.dart +++ b/frontend/appflowy_flutter/lib/user/application/auth/af_cloud_auth_service.dart @@ -56,7 +56,7 @@ class AppFlowyCloudAuthService implements AuthService { (data) async { // Open the webview with oauth url final uri = Uri.parse(data.oauthUrl); - final isSuccess = await afLaunchUrl( + final isSuccess = await afLaunchUri( uri, mode: LaunchMode.externalApplication, webOnlyWindowName: '_self', diff --git a/frontend/appflowy_flutter/lib/util/share_log_files.dart b/frontend/appflowy_flutter/lib/util/share_log_files.dart index 21955e6c05..d7e7b6ce87 100644 --- a/frontend/appflowy_flutter/lib/util/share_log_files.dart +++ b/frontend/appflowy_flutter/lib/util/share_log_files.dart @@ -1,11 +1,11 @@ import 'dart:io'; +import 'package:appflowy/core/helpers/url_launcher.dart'; import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/workspace/presentation/widgets/dialogs.dart'; import 'package:archive/archive_io.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; -import 'package:open_filex/open_filex.dart'; import 'package:path/path.dart' as p; import 'package:path_provider/path_provider.dart'; import 'package:share_plus/share_plus.dart'; @@ -67,7 +67,7 @@ Future shareLogFiles(BuildContext? context) async { await zipFile.delete(); } else { // open the directory - await OpenFilex.open(zipFile.path); + await afLaunchUri(zipFile.uri); } } catch (e) { if (context != null && context.mounted) { diff --git a/frontend/appflowy_flutter/lib/workspace/application/home/home_setting_bloc.dart b/frontend/appflowy_flutter/lib/workspace/application/home/home_setting_bloc.dart index 05418f3315..657f2592d7 100644 --- a/frontend/appflowy_flutter/lib/workspace/application/home/home_setting_bloc.dart +++ b/frontend/appflowy_flutter/lib/workspace/application/home/home_setting_bloc.dart @@ -86,7 +86,7 @@ class HomeSettingBloc extends Bloc { }, editPanelResized: (_EditPanelResized e) { final newPosition = - (e.offset + state.resizeStart).clamp(-50, 200).toDouble(); + (state.resizeStart + e.offset).clamp(0, 200).toDouble(); if (state.resizeOffset != newPosition) { emit(state.copyWith(resizeOffset: newPosition)); } diff --git a/frontend/appflowy_flutter/lib/workspace/application/settings/ai/plugin_state_bloc.dart b/frontend/appflowy_flutter/lib/workspace/application/settings/ai/plugin_state_bloc.dart index 41070e2fe4..4f24309bde 100644 --- a/frontend/appflowy_flutter/lib/workspace/application/settings/ai/plugin_state_bloc.dart +++ b/frontend/appflowy_flutter/lib/workspace/application/settings/ai/plugin_state_bloc.dart @@ -8,6 +8,7 @@ import 'package:appflowy_backend/protobuf/flowy-ai/entities.pb.dart'; import 'package:bloc/bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:url_launcher/url_launcher.dart' show launchUrl; + part 'plugin_state_bloc.freezed.dart'; class PluginStateBloc extends Bloc { @@ -91,7 +92,7 @@ class PluginStateBloc extends Bloc { final result = await AIEventGetModelStorageDirectory().send(); result.fold( (data) { - afLaunchUrl(Uri.file(data.filePath)); + afLaunchUri(Uri.file(data.filePath)); }, (err) => Log.error(err.toString()), ); diff --git a/frontend/appflowy_flutter/lib/workspace/application/sidebar/billing/sidebar_plan_bloc.dart b/frontend/appflowy_flutter/lib/workspace/application/sidebar/billing/sidebar_plan_bloc.dart index 7af2a547dd..1737494530 100644 --- a/frontend/appflowy_flutter/lib/workspace/application/sidebar/billing/sidebar_plan_bloc.dart +++ b/frontend/appflowy_flutter/lib/workspace/application/sidebar/billing/sidebar_plan_bloc.dart @@ -12,6 +12,7 @@ import 'package:appflowy_backend/protobuf/flowy-user/user_profile.pb.dart'; import 'package:appflowy_backend/protobuf/flowy-user/workspace.pb.dart'; import 'package:bloc/bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; + part 'sidebar_plan_bloc.freezed.dart'; class SidebarPlanBloc extends Bloc { @@ -113,7 +114,8 @@ class SidebarPlanBloc extends Bloc { } else if (error.code == ErrorCode.SingleUploadLimitExceeded) { emit( state.copyWith( - tierIndicator: const SidebarToastTierIndicator.singleFileLimitHit(), + tierIndicator: + const SidebarToastTierIndicator.singleFileLimitHit(), ), ); } else { @@ -184,13 +186,10 @@ class SidebarPlanBloc extends Bloc { if (state.workspaceId != null) { final payload = UserWorkspaceIdPB(workspaceId: state.workspaceId!); UserEventGetWorkspaceUsage(payload).send().then((result) { - result.fold( + result.onSuccess( (usage) { add(SidebarPlanEvent.updateWorkspaceUsage(usage)); }, - (error) { - Log.error("Failed to get workspace usage, error: $error"); - }, ); }); } @@ -231,7 +230,8 @@ class SidebarPlanState with _$SidebarPlanState { @freezed class SidebarToastTierIndicator with _$SidebarToastTierIndicator { const factory SidebarToastTierIndicator.storageLimitHit() = _StorageLimitHit; - const factory SidebarToastTierIndicator.singleFileLimitHit() = _SingleFileLimitHit; + const factory SidebarToastTierIndicator.singleFileLimitHit() = + _SingleFileLimitHit; const factory SidebarToastTierIndicator.aiMaxiLimitHit() = _aiMaxLimitHit; const factory SidebarToastTierIndicator.loading() = _Loading; } diff --git a/frontend/appflowy_flutter/lib/workspace/application/sidebar/space/space_bloc.dart b/frontend/appflowy_flutter/lib/workspace/application/sidebar/space/space_bloc.dart index 46d4943ddf..e1a825d631 100644 --- a/frontend/appflowy_flutter/lib/workspace/application/sidebar/space/space_bloc.dart +++ b/frontend/appflowy_flutter/lib/workspace/application/sidebar/space/space_bloc.dart @@ -329,14 +329,15 @@ class SpaceBloc extends Bloc { final (spaces, _, _) = await _getSpaces(); final currentSpace = await _getLastOpenedSpace(spaces); + Log.info( + 'receive space update, current space: ${currentSpace?.name}(${currentSpace?.id})', + ); + for (var i = 0; i < spaces.length; i++) { Log.info( - 'did receive space update[$i]: ${spaces[i].name}(${spaces[i].id})', + 'receive space update[$i]: ${spaces[i].name}(${spaces[i].id})', ); } - Log.info( - 'did receive space update, current space: ${currentSpace?.name}(${currentSpace?.id})', - ); emit( state.copyWith( diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/home/home_layout.dart b/frontend/appflowy_flutter/lib/workspace/presentation/home/home_layout.dart index fb108b702e..98139f1db7 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/home/home_layout.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/home/home_layout.dart @@ -14,10 +14,11 @@ class HomeLayout { HomeLayout(BuildContext context) { final homeSetting = context.read().state; showEditPanel = homeSetting.panelContext != null; - menuWidth = Sizes.sideBarWidth; - menuWidth += homeSetting.resizeOffset; - menuWidth = max(menuWidth, HomeSizes.minimumSidebarWidth); + menuWidth = max( + HomeSizes.minimumSidebarWidth + homeSetting.resizeOffset, + HomeSizes.minimumSidebarWidth, + ); final screenWidthPx = context.widthPx; context diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/settings/pages/settings_manage_data_view.dart b/frontend/appflowy_flutter/lib/workspace/presentation/settings/pages/settings_manage_data_view.dart index 3499580bbe..7c096b4b2f 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/settings/pages/settings_manage_data_view.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/settings/pages/settings_manage_data_view.dart @@ -1,8 +1,5 @@ import 'dart:async'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; - import 'package:appflowy/core/helpers/url_launcher.dart'; import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart'; @@ -28,6 +25,8 @@ import 'package:flowy_infra/file_picker/file_picker_service.dart'; import 'package:flowy_infra/theme_extension.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flowy_infra_ui/style_widget/hover.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:fluttertoast/fluttertoast.dart'; @@ -450,7 +449,7 @@ class _DataPathActions extends StatelessWidget { label: LocaleKeys.settings_manageDataPage_dataStorage_actions_open.tr(), icon: const FlowySvg(FlowySvgs.folder_m, size: Size.square(20)), - onPressed: () => afLaunchUrl(Uri.file(currentPath)), + onPressed: () => afLaunchUri(Uri.file(currentPath)), ), ], ); diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/theme_upload/theme_upload_learn_more_button.dart b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/theme_upload/theme_upload_learn_more_button.dart index d57d2d2a00..9e25dece0e 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/theme_upload/theme_upload_learn_more_button.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/theme_upload/theme_upload_learn_more_button.dart @@ -1,14 +1,13 @@ -import 'package:flutter/material.dart'; - import 'package:appflowy/core/helpers/url_launcher.dart'; import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/workspace/presentation/settings/widgets/theme_upload/theme_upload_view.dart'; import 'package:appflowy/workspace/presentation/widgets/dialogs.dart'; import 'package:easy_localization/easy_localization.dart'; +import 'package:flowy_infra/theme_extension.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flowy_infra_ui/widget/buttons/secondary_button.dart'; import 'package:flowy_infra_ui/widget/error_page.dart'; -import 'package:flowy_infra/theme_extension.dart'; +import 'package:flutter/material.dart'; class ThemeUploadLearnMoreButton extends StatelessWidget { const ThemeUploadLearnMoreButton({super.key}); @@ -32,7 +31,7 @@ class ThemeUploadLearnMoreButton extends StatelessWidget { ), onPressed: () async { final uri = Uri.parse(learnMoreURL); - await afLaunchUrl( + await afLaunchUri( uri, context: context, onFailure: (_) async { diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/widgets/image_viewer/interactive_image_toolbar.dart b/frontend/appflowy_flutter/lib/workspace/presentation/widgets/image_viewer/interactive_image_toolbar.dart index 223910ceac..c1d9823bd1 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/widgets/image_viewer/interactive_image_toolbar.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/widgets/image_viewer/interactive_image_toolbar.dart @@ -220,12 +220,12 @@ class InteractiveImageToolbar extends StatelessWidget { Future _locateOrDownloadImage(BuildContext context) async { if (currentImage.isLocal) { /// If the image type is local, we simply open the image - await afLaunchUrl(Uri.file(currentImage.url)); + await afLaunchUri(Uri.file(currentImage.url)); } else if (currentImage.isNotInternal) { // In case of eg. Unsplash images (images without extension type in URL), // we don't know their mimetype. In the future we can write a parser // using the Mime package and read the image to get the proper extension. - await afLaunchUrl(Uri.parse(currentImage.url)); + await afLaunchUri(Uri.parse(currentImage.url)); } else { if (userProfile == null) { return showSnapBar( diff --git a/frontend/appflowy_flutter/packages/flowy_infra/lib/size.dart b/frontend/appflowy_flutter/packages/flowy_infra/lib/size.dart index b9c6d565f8..f58dad95b5 100644 --- a/frontend/appflowy_flutter/packages/flowy_infra/lib/size.dart +++ b/frontend/appflowy_flutter/packages/flowy_infra/lib/size.dart @@ -57,8 +57,6 @@ class Sizes { static double get hit => 40 * hitScale; static double get iconMed => 20; - - static double get sideBarWidth => 250 * hitScale; } class Corners { diff --git a/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/style_widget/button.dart b/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/style_widget/button.dart index e1b9308e32..0e5b656a35 100644 --- a/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/style_widget/button.dart +++ b/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/style_widget/button.dart @@ -13,8 +13,8 @@ class FlowyIconTextButton extends StatelessWidget { final VoidCallback? onSecondaryTap; final void Function(bool)? onHover; final EdgeInsets? margin; - final Widget Function(bool onHover)? leftIconBuilder; - final Widget Function(bool onHover)? rightIconBuilder; + final Widget? Function(bool onHover)? leftIconBuilder; + final Widget? Function(bool onHover)? rightIconBuilder; final Color? hoverColor; final bool isSelected; final BorderRadius? radius; @@ -29,6 +29,7 @@ class FlowyIconTextButton extends StatelessWidget { final double iconPadding; final bool expand; final Color? borderColor; + final bool resetHoverOnRebuild; const FlowyIconTextButton({ super.key, @@ -53,6 +54,7 @@ class FlowyIconTextButton extends StatelessWidget { this.iconPadding = 6, this.expand = false, this.borderColor, + this.resetHoverOnRebuild = true, }); @override @@ -64,6 +66,7 @@ class FlowyIconTextButton extends StatelessWidget { onTap: disable ? null : onTap, onSecondaryTap: disable ? null : onSecondaryTap, child: FlowyHover( + resetHoverOnRebuild: resetHoverOnRebuild, cursor: disable ? SystemMouseCursors.forbidden : SystemMouseCursors.click, style: HoverStyle( @@ -81,11 +84,12 @@ class FlowyIconTextButton extends StatelessWidget { Widget _render(BuildContext context, bool onHover) { final List children = []; - if (leftIconBuilder != null) { + final Widget? leftIcon = leftIconBuilder?.call(onHover); + if (leftIcon != null) { children.add( SizedBox.fromSize( size: leftIconSize, - child: leftIconBuilder!(onHover), + child: leftIcon, ), ); children.add(HSpace(iconPadding)); @@ -97,10 +101,11 @@ class FlowyIconTextButton extends StatelessWidget { children.add(textBuilder(onHover)); } - if (rightIconBuilder != null) { + final Widget? rightIcon = rightIconBuilder?.call(onHover); + if (rightIcon != null) { children.add(HSpace(iconPadding)); // No need to define the size of rightIcon. Just use its intrinsic width - children.add(rightIconBuilder!(onHover)); + children.add(rightIcon); } Widget child = Row( diff --git a/frontend/appflowy_flutter/pubspec.lock b/frontend/appflowy_flutter/pubspec.lock index 71bc08bf67..c8dc56a973 100644 --- a/frontend/appflowy_flutter/pubspec.lock +++ b/frontend/appflowy_flutter/pubspec.lock @@ -61,8 +61,8 @@ packages: dependency: "direct main" description: path: "." - ref: ac3b090 - resolved-ref: ac3b0906ea2e7f2e7e3c2c7852e9ec3529e07512 + ref: "157ded3" + resolved-ref: "157ded3cd321b9a54d011c0cc27e270ded35d3aa" url: "https://github.com/AppFlowy-IO/appflowy-editor.git" source: git version: "4.0.0" diff --git a/frontend/appflowy_flutter/pubspec.yaml b/frontend/appflowy_flutter/pubspec.yaml index 29a3d57cf7..949d264bf2 100644 --- a/frontend/appflowy_flutter/pubspec.yaml +++ b/frontend/appflowy_flutter/pubspec.yaml @@ -4,7 +4,7 @@ description: Bring projects, wikis, and teams together with AI. AppFlowy is an your data. The best open source alternative to Notion. publish_to: "none" -version: 0.7.6 +version: 0.7.7 environment: flutter: ">=3.22.0" @@ -173,7 +173,7 @@ dependency_overrides: appflowy_editor: git: url: https://github.com/AppFlowy-IO/appflowy-editor.git - ref: "ac3b090" + ref: "157ded3" appflowy_editor_plugins: git: diff --git a/frontend/appflowy_flutter/test/unit_test/markdown/markdown_parser_test.dart b/frontend/appflowy_flutter/test/unit_test/markdown/markdown_parser_test.dart new file mode 100644 index 0000000000..70775612e2 --- /dev/null +++ b/frontend/appflowy_flutter/test/unit_test/markdown/markdown_parser_test.dart @@ -0,0 +1,37 @@ +import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart'; +import 'package:appflowy/shared/markdown_to_document.dart'; +import 'package:appflowy_editor/appflowy_editor.dart'; +import 'package:appflowy_editor_plugins/appflowy_editor_plugins.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('export markdown to document', () { + test('file block', () async { + final document = Document.blank() + ..insert( + [0], + [ + fileNode( + name: 'file.txt', + url: 'https://file.com', + ), + ], + ); + final markdown = customDocumentToMarkdown(document); + expect(markdown, '[file.txt](https://file.com)\n'); + }); + + test('link preview', () { + final document = Document.blank() + ..insert( + [0], + [linkPreviewNode(url: 'https://www.link_preview.com')], + ); + final markdown = customDocumentToMarkdown(document); + expect( + markdown, + '[https://www.link_preview.com](https://www.link_preview.com)\n', + ); + }); + }); +} diff --git a/frontend/appflowy_flutter/test/unit_test/url_launcher/url_launcher_test.dart b/frontend/appflowy_flutter/test/unit_test/url_launcher/url_launcher_test.dart new file mode 100644 index 0000000000..feac569127 --- /dev/null +++ b/frontend/appflowy_flutter/test/unit_test/url_launcher/url_launcher_test.dart @@ -0,0 +1,19 @@ +import 'package:appflowy/shared/patterns/common_patterns.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('url launcher unit test', () { + test('launch local uri', () async { + const localUris = [ + 'file://path/to/file.txt', + '/path/to/file.txt', + 'C:\\path\\to\\file.txt', + '../path/to/file.txt', + ]; + for (final uri in localUris) { + final result = localPathRegex.hasMatch(uri); + expect(result, true); + } + }); + }); +} diff --git a/frontend/resources/translations/en.json b/frontend/resources/translations/en.json index dc93847dd6..8b1d97dba5 100644 --- a/frontend/resources/translations/en.json +++ b/frontend/resources/translations/en.json @@ -2896,5 +2896,12 @@ "favoriteDisabledHint": "Cannot favorite this view", "pinTab": "Pin", "unpinTab": "Unpin" + }, + "openFileMessage": { + "success": "File opened successfully", + "fileNotFound": "File not found", + "noAppToOpenFile": "No app to open this file", + "permissionDenied": "No permission to open this file", + "unknownError": "File open failed" } -} \ No newline at end of file +} From d2b2f17b1cfd33c7e00b9adaa6ac00153d5c3783 Mon Sep 17 00:00:00 2001 From: "Nathan.fooo" <86001920+appflowy@users.noreply.github.com> Date: Tue, 10 Dec 2024 10:02:48 +0800 Subject: [PATCH 026/576] chore: diagnose sync issues with sync.log file (#6950) * chore: filter sync log * chore: filter sync log * chore: enable/disable sync log * chore: enable/disable sync log * chore: observer document and folder * fix: integration test --------- Co-authored-by: Lucas.Xu --- .../desktop/cloud/cloud_runner.dart | 7 +-- .../lib/core/config/kv_keys.dart | 1 + .../appflowy_flutter/lib/env/backend_env.dart | 3 ++ .../appflowy_flutter/lib/env/cloud_env.dart | 21 +++++++++ .../settings/appflowy_cloud_setting_bloc.dart | 20 +++++++- .../application/sidebar/space/space_bloc.dart | 4 +- .../widgets/setting_appflowy_cloud.dart | 43 +++++++++++++++++ frontend/resources/translations/en.json | 4 +- frontend/rust-lib/Cargo.lock | 42 +++++++++++------ .../event-integration-test/src/user_event.rs | 1 + frontend/rust-lib/flowy-core/src/config.rs | 16 ++++++- .../rust-lib/flowy-document/src/document.rs | 17 +++++-- frontend/rust-lib/flowy-folder/src/manager.rs | 2 +- .../flowy-folder/src/manager_observer.rs | 9 +++- .../src/native/af_cloud_config.rs | 16 +++++++ frontend/rust-lib/lib-infra/src/util.rs | 10 ++++ frontend/rust-lib/lib-log/Cargo.toml | 4 +- frontend/rust-lib/lib-log/src/layer.rs | 35 +++++++++++--- frontend/rust-lib/lib-log/src/lib.rs | 46 ++++++++++++++----- 19 files changed, 252 insertions(+), 49 deletions(-) diff --git a/frontend/appflowy_flutter/integration_test/desktop/cloud/cloud_runner.dart b/frontend/appflowy_flutter/integration_test/desktop/cloud/cloud_runner.dart index 621baff5d0..a0b6901f2d 100644 --- a/frontend/appflowy_flutter/integration_test/desktop/cloud/cloud_runner.dart +++ b/frontend/appflowy_flutter/integration_test/desktop/cloud/cloud_runner.dart @@ -1,16 +1,17 @@ +import 'data_migration/data_migration_test_runner.dart' + as data_migration_test_runner; import 'document/document_test_runner.dart' as document_test_runner; +import 'set_env.dart' as preset_af_cloud_env_test; import 'sidebar/sidebar_move_page_test.dart' as sidebar_move_page_test; import 'uncategorized/uncategorized_test_runner.dart' as uncategorized_test_runner; import 'workspace/workspace_test_runner.dart' as workspace_test_runner; -import 'data_migration/data_migration_test_runner.dart' - as data_migration_test_runner; -import 'set_env.dart' as preset_af_cloud_env_test; Future main() async { preset_af_cloud_env_test.main(); data_migration_test_runner.main(); + // uncategorized uncategorized_test_runner.main(); diff --git a/frontend/appflowy_flutter/lib/core/config/kv_keys.dart b/frontend/appflowy_flutter/lib/core/config/kv_keys.dart index 1d76e329b7..c0e7f63f6c 100644 --- a/frontend/appflowy_flutter/lib/core/config/kv_keys.dart +++ b/frontend/appflowy_flutter/lib/core/config/kv_keys.dart @@ -58,6 +58,7 @@ class KVKeys { static const String kCloudType = 'kCloudType'; static const String kAppflowyCloudBaseURL = 'kAppFlowyCloudBaseURL'; + static const String kAppFlowyEnableSyncTrace = 'kAppFlowyEnableSyncTrace'; /// The key for saving the text scale factor. /// diff --git a/frontend/appflowy_flutter/lib/env/backend_env.dart b/frontend/appflowy_flutter/lib/env/backend_env.dart index f8aa715a40..a3f45f9f60 100644 --- a/frontend/appflowy_flutter/lib/env/backend_env.dart +++ b/frontend/appflowy_flutter/lib/env/backend_env.dart @@ -39,6 +39,7 @@ class AppFlowyCloudConfiguration { required this.base_url, required this.ws_base_url, required this.gotrue_url, + required this.enable_sync_trace, }); factory AppFlowyCloudConfiguration.fromJson(Map json) => @@ -47,6 +48,7 @@ class AppFlowyCloudConfiguration { final String base_url; final String ws_base_url; final String gotrue_url; + final bool enable_sync_trace; Map toJson() => _$AppFlowyCloudConfigurationToJson(this); @@ -55,6 +57,7 @@ class AppFlowyCloudConfiguration { base_url: '', ws_base_url: '', gotrue_url: '', + enable_sync_trace: false, ); } diff --git a/frontend/appflowy_flutter/lib/env/cloud_env.dart b/frontend/appflowy_flutter/lib/env/cloud_env.dart index 83a5288359..86b9636a93 100644 --- a/frontend/appflowy_flutter/lib/env/cloud_env.dart +++ b/frontend/appflowy_flutter/lib/env/cloud_env.dart @@ -212,6 +212,7 @@ class AppFlowyCloudSharedEnv { base_url: Env.afCloudUrl, ws_base_url: await _getAppFlowyCloudWSUrl(Env.afCloudUrl), gotrue_url: await _getAppFlowyCloudGotrueUrl(Env.afCloudUrl), + enable_sync_trace: false, ); return AppFlowyCloudSharedEnv( @@ -241,12 +242,14 @@ Future configurationFromUri( base_url: "$baseUrl:8000", ws_base_url: "ws://${baseUri.host}:8000/ws/v1", gotrue_url: "$baseUrl:9999", + enable_sync_trace: true, ); } else { return AppFlowyCloudConfiguration( base_url: baseUrl, ws_base_url: await _getAppFlowyCloudWSUrl(baseUrl), gotrue_url: await _getAppFlowyCloudGotrueUrl(baseUrl), + enable_sync_trace: await getSyncLogEnabled(), ); } } @@ -271,6 +274,24 @@ Future getAppFlowyCloudUrl() async { return result ?? kAppflowyCloudUrl; } +Future getSyncLogEnabled() async { + final result = + await getIt().get(KVKeys.kAppFlowyEnableSyncTrace); + + if (result == null) { + return false; + } + + return result.toLowerCase() == "true"; +} + +Future setSyncLogEnabled(bool enable) async { + await getIt().set( + KVKeys.kAppFlowyEnableSyncTrace, + enable.toString().toLowerCase(), + ); +} + Future _getAppFlowyCloudWSUrl(String baseURL) async { try { final uri = Uri.parse(baseURL); diff --git a/frontend/appflowy_flutter/lib/workspace/application/settings/appflowy_cloud_setting_bloc.dart b/frontend/appflowy_flutter/lib/workspace/application/settings/appflowy_cloud_setting_bloc.dart index fdb53f4e43..febb89727a 100644 --- a/frontend/appflowy_flutter/lib/workspace/application/settings/appflowy_cloud_setting_bloc.dart +++ b/frontend/appflowy_flutter/lib/workspace/application/settings/appflowy_cloud_setting_bloc.dart @@ -1,3 +1,4 @@ +import 'package:appflowy/env/cloud_env.dart'; import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/workspace/application/settings/cloud_setting_listener.dart'; import 'package:appflowy_backend/dispatch/dispatch.dart'; @@ -14,7 +15,7 @@ class AppFlowyCloudSettingBloc extends Bloc { AppFlowyCloudSettingBloc(CloudSettingPB setting) : _listener = UserCloudConfigListener(), - super(AppFlowyCloudSettingState.initial(setting)) { + super(AppFlowyCloudSettingState.initial(setting, false)) { _dispatch(); } @@ -31,6 +32,10 @@ class AppFlowyCloudSettingBloc (event, emit) async { await event.when( initial: () async { + await getSyncLogEnabled().then((value) { + emit(state.copyWith(isSyncLogEnabled: value)); + }); + _listener.start( onSettingChanged: (result) { if (isClosed) { @@ -48,6 +53,10 @@ class AppFlowyCloudSettingBloc final config = UpdateCloudConfigPB.create()..enableSync = isEnable; await UserEventSetCloudConfig(config).send(); }, + enableSyncLog: (isEnable) async { + await setSyncLogEnabled(isEnable); + emit(state.copyWith(isSyncLogEnabled: isEnable)); + }, didReceiveSetting: (CloudSettingPB setting) { emit( state.copyWith( @@ -67,6 +76,8 @@ class AppFlowyCloudSettingEvent with _$AppFlowyCloudSettingEvent { const factory AppFlowyCloudSettingEvent.initial() = _Initial; const factory AppFlowyCloudSettingEvent.enableSync(bool isEnable) = _EnableSync; + const factory AppFlowyCloudSettingEvent.enableSyncLog(bool isEnable) = + _EnableSyncLog; const factory AppFlowyCloudSettingEvent.didReceiveSetting( CloudSettingPB setting, ) = _DidUpdateSetting; @@ -77,12 +88,17 @@ class AppFlowyCloudSettingState with _$AppFlowyCloudSettingState { const factory AppFlowyCloudSettingState({ required CloudSettingPB setting, required bool showRestartHint, + required bool isSyncLogEnabled, }) = _AppFlowyCloudSettingState; - factory AppFlowyCloudSettingState.initial(CloudSettingPB setting) => + factory AppFlowyCloudSettingState.initial( + CloudSettingPB setting, + bool isSyncLogEnabled, + ) => AppFlowyCloudSettingState( setting: setting, showRestartHint: setting.serverUrl.isNotEmpty, + isSyncLogEnabled: isSyncLogEnabled, ); } diff --git a/frontend/appflowy_flutter/lib/workspace/application/sidebar/space/space_bloc.dart b/frontend/appflowy_flutter/lib/workspace/application/sidebar/space/space_bloc.dart index e1a825d631..0f1dc4e987 100644 --- a/frontend/appflowy_flutter/lib/workspace/application/sidebar/space/space_bloc.dart +++ b/frontend/appflowy_flutter/lib/workspace/application/sidebar/space/space_bloc.dart @@ -102,7 +102,9 @@ class SpaceBloc extends Bloc { if (openFirstPage) { if (currentSpace != null) { - add(SpaceEvent.open(currentSpace)); + if (!isClosed) { + add(SpaceEvent.open(currentSpace)); + } } } }, diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/setting_appflowy_cloud.dart b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/setting_appflowy_cloud.dart index 081cb88f5f..d99a6cbf01 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/setting_appflowy_cloud.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/setting_appflowy_cloud.dart @@ -68,6 +68,7 @@ class AppFlowyCloudViewSetting extends StatelessWidget { return Column( children: [ const AppFlowyCloudEnableSync(), + const AppFlowyCloudSyncLogEnabled(), const VSpace(12), RestartButton( onClick: () { @@ -123,6 +124,7 @@ class CustomAppFlowyCloudView extends StatelessWidget { final List children = []; children.addAll([ const AppFlowyCloudEnableSync(), + const AppFlowyCloudSyncLogEnabled(), const VSpace(40), ]); @@ -331,6 +333,47 @@ class AppFlowyCloudEnableSync extends StatelessWidget { } } +class AppFlowyCloudSyncLogEnabled extends StatelessWidget { + const AppFlowyCloudSyncLogEnabled({super.key}); + + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + return Row( + children: [ + FlowyText.medium(LocaleKeys.settings_menu_enableSyncLog.tr()), + const Spacer(), + Toggle( + value: state.isSyncLogEnabled, + onChanged: (value) { + if (value) { + showCancelAndConfirmDialog( + context: context, + title: LocaleKeys.settings_menu_enableSyncLog.tr(), + description: + LocaleKeys.settings_menu_enableSyncLogWarning.tr(), + confirmLabel: LocaleKeys.button_confirm.tr(), + onConfirm: () { + context + .read() + .add(AppFlowyCloudSettingEvent.enableSyncLog(value)); + }, + ); + } else { + context + .read() + .add(AppFlowyCloudSettingEvent.enableSyncLog(value)); + } + }, + ), + ], + ); + }, + ); + } +} + class BillingGateGuard extends StatelessWidget { const BillingGateGuard({required this.builder, super.key}); diff --git a/frontend/resources/translations/en.json b/frontend/resources/translations/en.json index 8b1d97dba5..828114bf13 100644 --- a/frontend/resources/translations/en.json +++ b/frontend/resources/translations/en.json @@ -1036,6 +1036,8 @@ "syncSetting": "Sync Setting", "cloudSettings": "Cloud Settings", "enableSync": "Enable sync", + "enableSyncLog": "Enable sync logging", + "enableSyncLogWarning": "Thank you for helping diagnose sync issues. This will log your document edits to a local file. Please quit and reopen the app after enabling", "enableEncrypt": "Encrypt data", "cloudURL": "Base URL", "invalidCloudURLScheme": "Invalid Scheme", @@ -2904,4 +2906,4 @@ "permissionDenied": "No permission to open this file", "unknownError": "File open failed" } -} +} \ No newline at end of file diff --git a/frontend/rust-lib/Cargo.lock b/frontend/rust-lib/Cargo.lock index f2df3b0c53..ac5f27c453 100644 --- a/frontend/rust-lib/Cargo.lock +++ b/frontend/rust-lib/Cargo.lock @@ -1388,7 +1388,7 @@ dependencies = [ "cssparser-macros", "dtoa-short", "itoa", - "phf 0.8.0", + "phf 0.11.2", "smallvec", ] @@ -4369,7 +4369,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" dependencies = [ - "phf_macros", + "phf_macros 0.8.0", "phf_shared 0.8.0", "proc-macro-hack", ] @@ -4389,6 +4389,7 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" dependencies = [ + "phf_macros 0.11.2", "phf_shared 0.11.2", ] @@ -4456,6 +4457,19 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "phf_macros" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" +dependencies = [ + "phf_generator 0.11.2", + "phf_shared 0.11.2", + "proc-macro2", + "quote", + "syn 2.0.47", +] + [[package]] name = "phf_shared" version = "0.8.0" @@ -6589,9 +6603,9 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.40" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "log", "pin-project-lite", @@ -6613,9 +6627,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", @@ -6624,9 +6638,9 @@ dependencies = [ [[package]] name = "tracing-bunyan-formatter" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5c266b9ac83dedf0e0385ad78514949e6d89491269e7065bee51d2bb8ec7373" +checksum = "2d637245a0d8774bd48df6482e086c59a8b5348a910c3b0579354045a9d82411" dependencies = [ "ahash 0.8.6", "gethostname", @@ -6642,9 +6656,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", "valuable", @@ -6674,9 +6688,9 @@ dependencies = [ [[package]] name = "tracing-serde" -version = "0.1.3" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1" +checksum = "704b1aeb7be0d0a84fc9828cae51dab5970fee5088f83d1dd7ee6f6246fc6ff1" dependencies = [ "serde", "tracing-core", @@ -6684,9 +6698,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.18" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" dependencies = [ "matchers", "nu-ansi-term", diff --git a/frontend/rust-lib/event-integration-test/src/user_event.rs b/frontend/rust-lib/event-integration-test/src/user_event.rs index b06242ba78..1b82d9b83c 100644 --- a/frontend/rust-lib/event-integration-test/src/user_event.rs +++ b/frontend/rust-lib/event-integration-test/src/user_event.rs @@ -444,6 +444,7 @@ pub async fn use_localhost_af_cloud() { base_url, ws_base_url, gotrue_url, + enable_sync_trace: true, maximum_upload_file_size_in_bytes: None, } .write_env(); diff --git a/frontend/rust-lib/flowy-core/src/config.rs b/frontend/rust-lib/flowy-core/src/config.rs index e4e8c004d1..5c962df53f 100644 --- a/frontend/rust-lib/flowy-core/src/config.rs +++ b/frontend/rust-lib/flowy-core/src/config.rs @@ -40,6 +40,7 @@ impl fmt::Debug for AppFlowyCoreConfig { debug.field("base_url", &config.base_url); debug.field("ws_url", &config.ws_base_url); debug.field("gotrue_url", &config.gotrue_url); + debug.field("enable_sync_trace", &config.enable_sync_trace); } debug.finish() } @@ -83,11 +84,22 @@ impl AppFlowyCoreConfig { name: String, ) -> Self { let cloud_config = AFCloudConfiguration::from_env().ok(); + let mut log_crates = vec![]; let storage_path = match &cloud_config { None => custom_application_path, - Some(config) => make_user_data_folder(&custom_application_path, &config.base_url), + Some(config) => { + if config.enable_sync_trace { + log_crates.push("sync_trace_log".to_string()); + } + make_user_data_folder(&custom_application_path, &config.base_url) + }, }; - let log_filter = create_log_filter("info".to_owned(), vec![], OperatingSystem::from(&platform)); + + let log_filter = create_log_filter( + "info".to_owned(), + log_crates, + OperatingSystem::from(&platform), + ); AppFlowyCoreConfig { app_version, diff --git a/frontend/rust-lib/flowy-document/src/document.rs b/frontend/rust-lib/flowy-document/src/document.rs index da0007fbd9..ffdd0c900e 100644 --- a/frontend/rust-lib/flowy-document/src/document.rs +++ b/frontend/rust-lib/flowy-document/src/document.rs @@ -5,12 +5,17 @@ use crate::notification::{document_notification_builder, DocumentNotification}; use collab::preclude::Collab; use collab_document::document::Document; use futures::StreamExt; +use lib_infra::sync_trace; pub fn subscribe_document_changed(doc_id: &str, document: &mut Document) { let doc_id_clone_for_block_changed = doc_id.to_owned(); document.subscribe_block_changed("key", move |events, is_remote| { - #[cfg(feature = "verbose_log")] - tracing::trace!("subscribe_document_changed: {:?}", events); + sync_trace!( + "[Document] block changed in doc_id: {}, is_remote: {}, events: {:?}", + doc_id_clone_for_block_changed, + is_remote, + events + ); // send notification to the client. document_notification_builder( @@ -23,8 +28,12 @@ pub fn subscribe_document_changed(doc_id: &str, document: &mut Document) { let doc_id_clone_for_awareness_state = doc_id.to_owned(); document.subscribe_awareness_state("key", move |events| { - #[cfg(feature = "verbose_log")] - tracing::trace!("subscribe_awareness_state: {:?}", events); + sync_trace!( + "[Document] awareness state in doc_id: {}, events: {:?}", + doc_id_clone_for_awareness_state, + events + ); + document_notification_builder( &doc_id_clone_for_awareness_state, DocumentNotification::DidUpdateDocumentAwarenessState, diff --git a/frontend/rust-lib/flowy-folder/src/manager.rs b/frontend/rust-lib/flowy-folder/src/manager.rs index dcf8a7fdd8..6abc99229e 100644 --- a/frontend/rust-lib/flowy-folder/src/manager.rs +++ b/frontend/rust-lib/flowy-folder/src/manager.rs @@ -1188,7 +1188,7 @@ impl FolderManager { if let Some(view) = &view { let view_layout: ViewLayout = view.layout.clone().into(); if let Some(handle) = self.operation_handlers.get(&view_layout) { - info!("Open view: {}", view.id); + info!("Open view: {}-{}", view.name, view.id); if let Err(err) = handle.open_view(&view_id).await { error!("Open view error: {:?}", err); } diff --git a/frontend/rust-lib/flowy-folder/src/manager_observer.rs b/frontend/rust-lib/flowy-folder/src/manager_observer.rs index 12069e7317..dec4ff062d 100644 --- a/frontend/rust-lib/flowy-folder/src/manager_observer.rs +++ b/frontend/rust-lib/flowy-folder/src/manager_observer.rs @@ -10,6 +10,7 @@ use collab_folder::{ Folder, SectionChange, SectionChangeReceiver, TrashSectionChange, View, ViewChange, ViewChangeReceiver, }; +use lib_infra::sync_trace; use std::collections::HashSet; use std::sync::Weak; @@ -45,10 +46,14 @@ pub(crate) fn subscribe_folder_view_changed( ChildViewChangeReason::Create, ); let folder = lock.read().await; - notify_parent_view_did_change(&workspace_id, &folder, vec![view.parent_view_id]); + let parent_view_id = view.parent_view_id.clone(); + notify_parent_view_did_change(&workspace_id, &folder, vec![parent_view_id]); + sync_trace!("[Folder] create view: {:?}", view); }, ViewChange::DidDeleteView { views } => { for view in views { + sync_trace!("[Folder] delete view: {:?}", view); + notify_child_views_changed( view_pb_without_child_views(view.as_ref().clone()), ChildViewChangeReason::Delete, @@ -56,6 +61,8 @@ pub(crate) fn subscribe_folder_view_changed( } }, ViewChange::DidUpdate { view } => { + sync_trace!("[Folder] update view: {:?}", view); + notify_view_did_change(view.clone()); notify_child_views_changed( view_pb_without_child_views(view.clone()), diff --git a/frontend/rust-lib/flowy-server-pub/src/native/af_cloud_config.rs b/frontend/rust-lib/flowy-server-pub/src/native/af_cloud_config.rs index 690ea7c3f3..14a72c6ce6 100644 --- a/frontend/rust-lib/flowy-server-pub/src/native/af_cloud_config.rs +++ b/frontend/rust-lib/flowy-server-pub/src/native/af_cloud_config.rs @@ -7,6 +7,7 @@ use flowy_error::{ErrorCode, FlowyError}; pub const APPFLOWY_CLOUD_BASE_URL: &str = "APPFLOWY_CLOUD_ENV_APPFLOWY_CLOUD_BASE_URL"; pub const APPFLOWY_CLOUD_WS_BASE_URL: &str = "APPFLOWY_CLOUD_ENV_APPFLOWY_CLOUD_WS_BASE_URL"; pub const APPFLOWY_CLOUD_GOTRUE_URL: &str = "APPFLOWY_CLOUD_ENV_APPFLOWY_CLOUD_GOTRUE_URL"; +pub const APPFLOWY_ENABLE_SYNC_TRACE: &str = "APPFLOWY_ENABLE_SYNC_TRACE"; #[derive(Debug, Serialize, Deserialize, Clone, Default)] pub struct AFCloudConfiguration { @@ -14,6 +15,8 @@ pub struct AFCloudConfiguration { pub ws_base_url: String, pub gotrue_url: String, #[serde(default)] + pub enable_sync_trace: bool, + #[serde(default)] pub maximum_upload_file_size_in_bytes: Option, } @@ -55,10 +58,15 @@ impl AFCloudConfiguration { ); } + let enable_sync_trace = std::env::var(APPFLOWY_ENABLE_SYNC_TRACE) + .map(|v| v == "true" || v == "1") + .unwrap_or(false); + Ok(Self { base_url, ws_base_url, gotrue_url, + enable_sync_trace, maximum_upload_file_size_in_bytes: None, }) } @@ -68,5 +76,13 @@ impl AFCloudConfiguration { std::env::set_var(APPFLOWY_CLOUD_BASE_URL, &self.base_url); std::env::set_var(APPFLOWY_CLOUD_WS_BASE_URL, &self.ws_base_url); std::env::set_var(APPFLOWY_CLOUD_GOTRUE_URL, &self.gotrue_url); + std::env::set_var( + APPFLOWY_ENABLE_SYNC_TRACE, + if self.enable_sync_trace { + "true" + } else { + "false" + }, + ); } } diff --git a/frontend/rust-lib/lib-infra/src/util.rs b/frontend/rust-lib/lib-infra/src/util.rs index b67646e4be..18ff068a73 100644 --- a/frontend/rust-lib/lib-infra/src/util.rs +++ b/frontend/rust-lib/lib-infra/src/util.rs @@ -1,3 +1,6 @@ +#[allow(unused_imports)] +use tracing::info; + #[macro_export] macro_rules! if_native { ($($item:item)*) => {$( @@ -136,3 +139,10 @@ pub fn get_operating_system() -> OperatingSystem { } } } + +#[macro_export] +macro_rules! sync_trace { + ($($arg:tt)*) => { + tracing::info!(target: "sync_trace_log", $($arg)*); + }; +} diff --git a/frontend/rust-lib/lib-log/Cargo.toml b/frontend/rust-lib/lib-log/Cargo.toml index 1b8ebcb2bc..4316192f71 100644 --- a/frontend/rust-lib/lib-log/Cargo.toml +++ b/frontend/rust-lib/lib-log/Cargo.toml @@ -6,8 +6,8 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -tracing-subscriber = { version = "0.3.17", features = ["registry", "env-filter", "ansi", "json"] } -tracing-bunyan-formatter = "0.3.9" +tracing-subscriber = { version = "0.3.19", features = ["registry", "env-filter", "ansi", "json"] } +tracing-bunyan-formatter = "0.3.10" tracing-appender = "0.2.3" tracing-core = "0.1" tracing.workspace = true diff --git a/frontend/rust-lib/lib-log/src/layer.rs b/frontend/rust-lib/lib-log/src/layer.rs index 28bd54b01f..945db52a8b 100644 --- a/frontend/rust-lib/lib-log/src/layer.rs +++ b/frontend/rust-lib/lib-log/src/layer.rs @@ -7,6 +7,7 @@ use tracing::{Event, Id, Subscriber}; use tracing_bunyan_formatter::JsonStorage; use tracing_core::metadata::Level; use tracing_core::span::Attributes; +use tracing_core::Metadata; use tracing_subscriber::{fmt::MakeWriter, layer::Context, registry::SpanRef, Layer}; const LEVEL: &str = "level"; @@ -22,6 +23,8 @@ const IGNORE_FIELDS: [&str; 2] = [LOG_MODULE_PATH, LOG_TARGET_PATH]; pub struct FlowyFormattingLayer<'a, W: MakeWriter<'static> + 'static> { make_writer: W, with_target: bool, + #[allow(clippy::type_complexity)] + target_filter: Option bool + Send + Sync>>, phantom: std::marker::PhantomData<&'a ()>, } @@ -34,10 +37,26 @@ where Self { make_writer, with_target: true, + target_filter: None, phantom: std::marker::PhantomData, } } + pub fn with_target_filter(mut self, filter: F) -> Self + where + F: Fn(&str) -> bool + Send + Sync + 'static, + { + self.target_filter = Some(Box::new(filter)); + self + } + + fn should_log(&self, metadata: &Metadata<'_>) -> bool { + self + .target_filter + .as_ref() + .map_or(true, |f| f(metadata.target())) + } + fn serialize_fields( &self, map_serializer: &mut impl SerializeMap, @@ -45,7 +64,6 @@ where _level: &Level, ) -> Result<(), std::io::Error> { map_serializer.serialize_entry(MESSAGE, &message)?; - // map_serializer.serialize_entry(LEVEL, &format!("{}", level))?; map_serializer.serialize_entry(TIME, &Local::now().format("%m-%d %H:%M:%S").to_string())?; Ok(()) } @@ -160,6 +178,10 @@ where W: for<'writer> MakeWriter<'writer> + 'static, { fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) { + if !self.should_log(event.metadata()) { + return; + } + // Events do not necessarily happen in the context of a span, hence // lookup_current returns an `Option>` instead of a // `SpanRef<_>`. @@ -177,14 +199,9 @@ where let message = format_event_message(¤t_span, event, &event_visitor, &ctx); self.serialize_fields(&mut map_serializer, &message, event.metadata().level())?; - // Additional metadata useful for debugging - // They should be nested under `src` (see https://github.com/trentm/node-bunyan#src ) - // but `tracing` does not support nested values yet - if self.with_target { map_serializer.serialize_entry("target", event.metadata().target())?; } - // map_serializer.serialize_entry("line", &event.metadata().line())?; // map_serializer.serialize_entry("file", &event.metadata().file())?; @@ -224,6 +241,9 @@ where fn on_new_span(&self, _attrs: &Attributes, id: &Id, ctx: Context<'_, S>) { let span = ctx.span(id).expect("Span not found, this is a bug"); + if !self.should_log(span.metadata()) { + return; + } if let Ok(serialized) = self.serialize_span(&span, Type::EnterSpan, &ctx) { let _ = self.emit(serialized); } @@ -231,6 +251,9 @@ where fn on_close(&self, id: Id, ctx: Context<'_, S>) { let span = ctx.span(&id).expect("Span not found, this is a bug"); + if !self.should_log(span.metadata()) { + return; + } if let Ok(serialized) = self.serialize_span(&span, Type::ExitSpan, &ctx) { let _ = self.emit(serialized); } diff --git a/frontend/rust-lib/lib-log/src/lib.rs b/frontend/rust-lib/lib-log/src/lib.rs index ee5bcd8f89..07145b27ff 100644 --- a/frontend/rust-lib/lib-log/src/lib.rs +++ b/frontend/rust-lib/lib-log/src/lib.rs @@ -2,6 +2,8 @@ use std::io; use std::io::Write; use std::sync::{Arc, RwLock}; +use crate::layer::FlowyFormattingLayer; +use crate::stream_log::{StreamLog, StreamLogSender}; use chrono::Local; use lazy_static::lazy_static; use lib_infra::util::OperatingSystem; @@ -13,26 +15,26 @@ use tracing_subscriber::fmt::format::Writer; use tracing_subscriber::fmt::MakeWriter; use tracing_subscriber::{layer::SubscriberExt, EnvFilter}; -use crate::layer::FlowyFormattingLayer; -use crate::stream_log::{StreamLog, StreamLogSender}; - mod layer; pub mod stream_log; lazy_static! { - static ref LOG_GUARD: RwLock> = RwLock::new(None); + static ref APP_LOG_GUARD: RwLock> = RwLock::new(None); + static ref COLLAB_SYNC_LOG_GUARD: RwLock> = RwLock::new(None); } pub struct Builder { #[allow(dead_code)] name: String, env_filter: String, - file_appender: RollingFileAppender, + app_log_appender: RollingFileAppender, + sync_log_appender: RollingFileAppender, #[allow(dead_code)] platform: OperatingSystem, stream_log_sender: Option>, } +const SYNC_TARGET: &str = "sync_trace_log"; impl Builder { pub fn new( name: &str, @@ -40,17 +42,26 @@ impl Builder { platform: &OperatingSystem, stream_log_sender: Option>, ) -> Self { - let file_appender = RollingFileAppender::builder() + let app_log_appender = RollingFileAppender::builder() .rotation(Rotation::DAILY) .filename_prefix(name) .max_log_files(6) .build(directory) .unwrap_or(tracing_appender::rolling::daily(directory, name)); + let sync_log_name = "log.sync"; + let sync_log_appender = RollingFileAppender::builder() + .rotation(Rotation::HOURLY) + .filename_prefix(sync_log_name) + .max_log_files(24) + .build(directory) + .unwrap_or(tracing_appender::rolling::hourly(directory, sync_log_name)); + Builder { name: name.to_owned(), env_filter: "info".to_owned(), - file_appender, + app_log_appender, + sync_log_appender, platform: platform.clone(), stream_log_sender, } @@ -63,8 +74,18 @@ impl Builder { pub fn build(self) -> Result<(), String> { let env_filter = EnvFilter::new(self.env_filter); - let (non_blocking, guard) = tracing_appender::non_blocking(self.file_appender); - let file_layer = FlowyFormattingLayer::new(non_blocking); + let (appflowy_log_non_blocking, app_log_guard) = + tracing_appender::non_blocking(self.app_log_appender); + *APP_LOG_GUARD.write().unwrap() = Some(app_log_guard); + let app_file_layer = FlowyFormattingLayer::new(appflowy_log_non_blocking) + .with_target_filter(|target| target != SYNC_TARGET); + + let (sync_log_non_blocking, sync_log_guard) = + tracing_appender::non_blocking(self.sync_log_appender); + *COLLAB_SYNC_LOG_GUARD.write().unwrap() = Some(sync_log_guard); + + let collab_sync_file_layer = FlowyFormattingLayer::new(sync_log_non_blocking) + .with_target_filter(|target| target == SYNC_TARGET); if let Some(stream_log_sender) = &self.stream_log_sender { let subscriber = tracing_subscriber::fmt() @@ -79,7 +100,8 @@ impl Builder { .with_env_filter(env_filter) .finish() .with(JsonStorageLayer) - .with(file_layer); + .with(app_file_layer) + .with(collab_sync_file_layer); set_global_default(subscriber).map_err(|e| format!("{:?}", e))?; } else { let subscriber = tracing_subscriber::fmt() @@ -92,11 +114,11 @@ impl Builder { .finish() .with(FlowyFormattingLayer::new(DebugStdoutWriter)) .with(JsonStorageLayer) - .with(file_layer); + .with(app_file_layer) + .with(collab_sync_file_layer); set_global_default(subscriber).map_err(|e| format!("{:?}", e))?; }; - *LOG_GUARD.write().unwrap() = Some(guard); Ok(()) } } From 592390dc847ec990bc0d9d6c813a2e0edd350ead Mon Sep 17 00:00:00 2001 From: Lucas Date: Tue, 10 Dec 2024 15:20:12 +0800 Subject: [PATCH 027/576] feat: support multiple lines in table cell (#6931) * feat: support multiple lines in table cell * feat: add document validator * fix: unable to delete a code block in table cell * feat: drag to expand the table row * fix: integration test * feat: support drag to expand the table * fix: integration test --- .../document_with_simple_table_test.dart | 1 - .../application/document_validator.dart | 77 ++++ .../presentation/editor_configuration.dart | 46 ++- .../document/presentation/editor_page.dart | 5 +- .../editor_plugins/base/text_robot.dart | 5 - .../numbered_list/numbered_list_icon.dart | 15 +- .../shortcuts/character_shortcuts.dart | 2 + .../shortcuts/command_shortcuts.dart | 1 - .../simple_table/_shared_widget.dart | 352 ------------------ .../simple_table/simple_table.dart | 3 + .../simple_table/simple_table_constants.dart | 15 + .../simple_table_delete_operation.dart | 25 +- .../simple_table_insert_operation.dart | 28 +- .../simple_table_node_extension.dart | 116 +++++- .../simple_table_backspace_command.dart | 21 +- .../simple_table_commands.dart | 2 + .../simple_table_enter_command.dart | 24 ++ ...imple_table_add_column_and_row_button.dart | 91 +++++ .../simple_table_add_column_button.dart | 219 +++++++++++ .../simple_table_add_row_button.dart | 221 +++++++++++ .../simple_table_column_resize_handle.dart | 9 +- .../simple_table_widget.dart | 2 +- frontend/appflowy_flutter/pubspec.lock | 4 +- frontend/appflowy_flutter/pubspec.yaml | 2 +- 24 files changed, 887 insertions(+), 399 deletions(-) create mode 100644 frontend/appflowy_flutter/lib/plugins/document/application/document_validator.dart create mode 100644 frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_shortcuts/simple_table_enter_command.dart create mode 100644 frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_add_column_and_row_button.dart create mode 100644 frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_add_column_button.dart create mode 100644 frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_add_row_button.dart diff --git a/frontend/appflowy_flutter/integration_test/desktop/document/document_with_simple_table_test.dart b/frontend/appflowy_flutter/integration_test/desktop/document/document_with_simple_table_test.dart index 4a8436f8b6..3f1f53655c 100644 --- a/frontend/appflowy_flutter/integration_test/desktop/document/document_with_simple_table_test.dart +++ b/frontend/appflowy_flutter/integration_test/desktop/document/document_with_simple_table_test.dart @@ -1,5 +1,4 @@ import 'package:appflowy/generated/locale_keys.g.dart'; -import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_table/_shared_widget.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_table/simple_table.dart'; import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:easy_localization/easy_localization.dart'; diff --git a/frontend/appflowy_flutter/lib/plugins/document/application/document_validator.dart b/frontend/appflowy_flutter/lib/plugins/document/application/document_validator.dart new file mode 100644 index 0000000000..a49a0f9902 --- /dev/null +++ b/frontend/appflowy_flutter/lib/plugins/document/application/document_validator.dart @@ -0,0 +1,77 @@ +import 'package:appflowy_editor/appflowy_editor.dart'; + +class DocumentValidator { + const DocumentValidator({ + required this.editorState, + required this.rules, + }); + + final EditorState editorState; + final List rules; + + Future validate(Transaction transaction) async { + // deep copy the document + final root = this.editorState.document.root.copyWith(); + final dummyDocument = Document(root: root); + if (dummyDocument.isEmpty) { + return true; + } + + final editorState = EditorState(document: dummyDocument); + await editorState.apply(transaction); + + final iterator = NodeIterator( + document: editorState.document, + startNode: editorState.document.root, + ); + + for (final rule in rules) { + while (iterator.moveNext()) { + if (!rule.validate(iterator.current)) { + return false; + } + } + } + + return true; + } + + Future applyTransactionInDummyDocument(Transaction transaction) async { + // deep copy the document + final root = this.editorState.document.root.copyWith(); + final dummyDocument = Document(root: root); + if (dummyDocument.isEmpty) { + return true; + } + + final editorState = EditorState(document: dummyDocument); + await editorState.apply(transaction); + + final iterator = NodeIterator( + document: editorState.document, + startNode: editorState.document.root, + ); + + for (final rule in rules) { + while (iterator.moveNext()) { + if (!rule.validate(iterator.current)) { + return false; + } + } + } + + return true; + } +} + +class DocumentRule { + const DocumentRule({ + required this.type, + }); + + final String type; + + bool validate(Node node) { + return true; + } +} diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_configuration.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_configuration.dart index 7bb590079b..00682b6a52 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_configuration.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_configuration.dart @@ -15,6 +15,24 @@ import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:universal_platform/universal_platform.dart'; +/// The node types that support slash menu. +final Set supportSlashMenuNodeTypes = { + ParagraphBlockKeys.type, + HeadingBlockKeys.type, + + // Lists + TodoListBlockKeys.type, + BulletedListBlockKeys.type, + NumberedListBlockKeys.type, + QuoteBlockKeys.type, + ToggleListBlockKeys.type, + + // Simple table + // SimpleTableBlockKeys.type, + // SimpleTableRowBlockKeys.type, + // SimpleTableCellBlockKeys.type, +}; + /// Build the block component builders. /// /// Every block type should have a corresponding builder in the map. @@ -125,9 +143,13 @@ void _customBlockOptionActions( final actions = _buildOptionActions(context, entry.key); if (UniversalPlatform.isDesktop) { - builder.showActions = (node) => - node.parent?.type != TableCellBlockKeys.type && - node.parent?.type != SimpleTableCellBlockKeys.type; + builder.showActions = (node) { + final parentTableNode = node.parentTableNode; + if (node.type != SimpleTableBlockKeys.type && parentTableNode != null) { + return false; + } + return true; + }; builder.configuration = builder.configuration.copyWith( blockSelectionAreaMargin: (_) => const EdgeInsets.symmetric( vertical: 1, @@ -162,6 +184,7 @@ void _customBlockOptionActions( shouldInsertSlash: false, deleteKeywordsByDefault: true, style: styleCustomizer.selectionMenuStyleBuilder(), + supportSlashMenuNodeTypes: supportSlashMenuNodeTypes, ).handler.call(editorState) : () {}, ), @@ -429,10 +452,19 @@ NumberedListBlockComponentBuilder _buildNumberedListBlockComponentBuilder( return configuration.textStyle(node); }, ), - iconBuilder: (_, node, textDirection) => NumberedListIcon( - node: node, - textDirection: textDirection, - ), + iconBuilder: (_, node, textDirection) { + TextStyle? textStyle; + if (node.isInHeaderColumn || node.isInHeaderRow) { + textStyle = configuration.textStyle(node).copyWith( + fontWeight: FontWeight.bold, + ); + } + return NumberedListIcon( + node: node, + textDirection: textDirection, + textStyle: textStyle, + ); + }, ); } diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_page.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_page.dart index e39c9bbd4c..bb0cf4e156 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_page.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_page.dart @@ -122,6 +122,7 @@ class _AppFlowyEditorPageState extends State slashMenuItems, shouldInsertSlash: false, style: styleCustomizer.selectionMenuStyleBuilder(), + supportSlashMenuNodeTypes: supportSlashMenuNodeTypes, ).handler(editorState); AFFocusManager? focusManager; @@ -156,10 +157,6 @@ class _AppFlowyEditorPageState extends State scrollController: effectiveScrollController, ); - // keep the previous font style when typing new text. - supportSlashMenuNodeWhiteList.addAll([ - ToggleListBlockKeys.type, - ]); toolbarItemWhiteList.addAll([ ToggleListBlockKeys.type, CalloutBlockKeys.type, diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/base/text_robot.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/base/text_robot.dart index 68e1c8e5eb..a20ea9aec5 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/base/text_robot.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/base/text_robot.dart @@ -1,7 +1,6 @@ import 'dart:async'; import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:flutter/material.dart'; import 'package:synchronized/synchronized.dart'; enum TextRobotInputType { @@ -125,10 +124,6 @@ class TextRobot { transaction.insertText(node, selection.endIndex, text); await editorState.apply(transaction); await Future.delayed(delay); - - debugPrint( - 'AI insertText: path: ${selection.end.path}, index: ${selection.endIndex}, text: "$text"', - ); } } diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/numbered_list/numbered_list_icon.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/numbered_list/numbered_list_icon.dart index 211d6740b1..3c67f7ccca 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/numbered_list/numbered_list_icon.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/numbered_list/numbered_list_icon.dart @@ -8,17 +8,22 @@ class NumberedListIcon extends StatelessWidget { super.key, required this.node, required this.textDirection, + this.textStyle, }); final Node node; final TextDirection textDirection; + final TextStyle? textStyle; @override Widget build(BuildContext context) { - final textStyle = + final textStyleConfiguration = context.read().editorStyle.textStyleConfiguration; - final fontSize = textStyle.text.fontSize ?? 16.0; - final height = textStyle.text.height ?? textStyle.lineHeight; + final fontSize = textStyleConfiguration.text.fontSize ?? 16.0; + final height = + textStyleConfiguration.text.height ?? textStyleConfiguration.lineHeight; + final combinedTextStyle = textStyle?.combine(textStyleConfiguration.text) ?? + textStyleConfiguration.text; final size = fontSize * height; return Container( constraints: BoxConstraints( @@ -30,8 +35,8 @@ class NumberedListIcon extends StatelessWidget { child: Center( child: Text( node.levelString, - style: textStyle.text, - strutStyle: StrutStyle.fromTextStyle(textStyle.text), + style: combinedTextStyle, + strutStyle: StrutStyle.fromTextStyle(combinedTextStyle), textDirection: textDirection, ), ), diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/shortcuts/character_shortcuts.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/shortcuts/character_shortcuts.dart index 8888d3ea1b..c4affb7b69 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/shortcuts/character_shortcuts.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/shortcuts/character_shortcuts.dart @@ -1,4 +1,5 @@ import 'package:appflowy/plugins/document/application/document_bloc.dart'; +import 'package:appflowy/plugins/document/presentation/editor_configuration.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/base/format_arrow_character.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/base/page_reference_commands.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/callout/callout_block_shortcuts.dart'; @@ -38,6 +39,7 @@ List buildCharacterShortcutEvents( customSlashCommand( slashMenuItems, style: styleCustomizer.selectionMenuStyleBuilder(), + supportSlashMenuNodeTypes: supportSlashMenuNodeTypes, ), customFormatGreaterEqual, diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/shortcuts/command_shortcuts.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/shortcuts/command_shortcuts.dart index 025aac2584..b1cd0c1712 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/shortcuts/command_shortcuts.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/shortcuts/command_shortcuts.dart @@ -47,7 +47,6 @@ List commandShortcutEvents = [ undoCommand, redoCommand, exitEditingCommand, - // ...tableCommands, ].contains(shortcut), ), diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/_shared_widget.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/_shared_widget.dart index 6f2e124fd8..2855ac6f43 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/_shared_widget.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/_shared_widget.dart @@ -8,358 +8,6 @@ import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -/// Only displaying the add row / add column / add column and row button -/// when hovering on the last row / last column / last cell. -bool _enableHoveringLogicV2 = true; - -class SimpleTableAddRowHoverButton extends StatefulWidget { - const SimpleTableAddRowHoverButton({ - super.key, - required this.editorState, - required this.tableNode, - }); - - final EditorState editorState; - final Node tableNode; - - @override - State createState() => - _SimpleTableAddRowHoverButtonState(); -} - -class _SimpleTableAddRowHoverButtonState - extends State { - late final interceptorKey = - 'simple_table_add_row_hover_button_${widget.tableNode.id}'; - - SelectionGestureInterceptor? interceptor; - - @override - void initState() { - super.initState(); - - interceptor = SelectionGestureInterceptor( - key: interceptorKey, - canTap: (details) => !_isTapInBounds(details.globalPosition), - ); - widget.editorState.service.selectionService - .registerGestureInterceptor(interceptor!); - } - - @override - void dispose() { - widget.editorState.service.selectionService.unregisterGestureInterceptor( - interceptorKey, - ); - - super.dispose(); - } - - @override - Widget build(BuildContext context) { - assert(widget.tableNode.type == SimpleTableBlockKeys.type); - - if (widget.tableNode.type != SimpleTableBlockKeys.type) { - return const SizedBox.shrink(); - } - - final simpleTableContext = context.read(); - return ValueListenableBuilder( - valueListenable: simpleTableContext.isHoveringOnTableArea, - builder: (context, isHoveringOnTableArea, child) { - return ValueListenableBuilder( - valueListenable: simpleTableContext.hoveringTableCell, - builder: (context, hoveringTableCell, child) { - bool shouldShow = isHoveringOnTableArea; - if (hoveringTableCell != null && _enableHoveringLogicV2) { - shouldShow = - hoveringTableCell.rowIndex + 1 == hoveringTableCell.rowLength; - } - return shouldShow - ? Positioned( - bottom: 2 * SimpleTableConstants.addRowButtonPadding, - left: SimpleTableConstants.tableLeftPadding - - SimpleTableConstants.cellBorderWidth, - right: SimpleTableConstants.addRowButtonRightPadding, - child: SimpleTableAddRowButton( - onTap: () => widget.editorState.addRowInTable( - widget.tableNode, - ), - ), - ) - : const SizedBox.shrink(); - }, - ); - }, - ); - } - - bool _isTapInBounds(Offset offset) { - final renderBox = context.findRenderObject() as RenderBox?; - if (renderBox == null) { - return false; - } - - final localPosition = renderBox.globalToLocal(offset); - final result = renderBox.paintBounds.contains(localPosition); - - return result; - } -} - -class SimpleTableAddRowButton extends StatelessWidget { - const SimpleTableAddRowButton({ - super.key, - this.onTap, - }); - - final VoidCallback? onTap; - - @override - Widget build(BuildContext context) { - return FlowyTooltip( - message: LocaleKeys.document_plugins_simpleTable_clickToAddNewRow.tr(), - child: GestureDetector( - behavior: HitTestBehavior.opaque, - onTap: onTap, - child: MouseRegion( - cursor: SystemMouseCursors.click, - child: Container( - height: SimpleTableConstants.addRowButtonHeight, - margin: const EdgeInsets.symmetric( - vertical: SimpleTableConstants.addColumnButtonPadding, - ), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular( - SimpleTableConstants.addRowButtonRadius, - ), - color: context.simpleTableMoreActionBackgroundColor, - ), - child: const FlowySvg( - FlowySvgs.add_s, - ), - ), - ), - ), - ); - } -} - -class SimpleTableAddColumnHoverButton extends StatefulWidget { - const SimpleTableAddColumnHoverButton({ - super.key, - required this.editorState, - required this.node, - }); - - final EditorState editorState; - final Node node; - - @override - State createState() => - _SimpleTableAddColumnHoverButtonState(); -} - -class _SimpleTableAddColumnHoverButtonState - extends State { - late final interceptorKey = - 'simple_table_add_column_hover_button_${widget.node.id}'; - - SelectionGestureInterceptor? interceptor; - - @override - void initState() { - super.initState(); - - interceptor = SelectionGestureInterceptor( - key: interceptorKey, - canTap: (details) => !_isTapInBounds(details.globalPosition), - ); - widget.editorState.service.selectionService - .registerGestureInterceptor(interceptor!); - } - - @override - void dispose() { - widget.editorState.service.selectionService.unregisterGestureInterceptor( - interceptorKey, - ); - - super.dispose(); - } - - @override - Widget build(BuildContext context) { - assert(widget.node.type == SimpleTableBlockKeys.type); - - if (widget.node.type != SimpleTableBlockKeys.type) { - return const SizedBox.shrink(); - } - - return ValueListenableBuilder( - valueListenable: context.read().isHoveringOnTableArea, - builder: (context, isHoveringOnTableArea, _) { - return ValueListenableBuilder( - valueListenable: context.read().hoveringTableCell, - builder: (context, hoveringTableCell, _) { - bool shouldShow = isHoveringOnTableArea; - if (hoveringTableCell != null && _enableHoveringLogicV2) { - shouldShow = hoveringTableCell.columnIndex + 1 == - hoveringTableCell.columnLength; - } - return Positioned( - top: SimpleTableConstants.tableTopPadding - - SimpleTableConstants.cellBorderWidth, - bottom: SimpleTableConstants.addColumnButtonBottomPadding, - right: 0, - child: Opacity( - opacity: shouldShow ? 1.0 : 0.0, - child: SimpleTableAddColumnButton( - onTap: () { - widget.editorState.addColumnInTable(widget.node); - }, - ), - ), - ); - }, - ); - }, - ); - } - - bool _isTapInBounds(Offset offset) { - final renderBox = context.findRenderObject() as RenderBox?; - if (renderBox == null) { - return false; - } - - final localPosition = renderBox.globalToLocal(offset); - final result = renderBox.paintBounds.contains(localPosition); - - return result; - } -} - -class SimpleTableAddColumnButton extends StatelessWidget { - const SimpleTableAddColumnButton({ - super.key, - this.onTap, - }); - - final VoidCallback? onTap; - - @override - Widget build(BuildContext context) { - return FlowyTooltip( - message: LocaleKeys.document_plugins_simpleTable_clickToAddNewColumn.tr(), - child: GestureDetector( - behavior: HitTestBehavior.translucent, - onTap: onTap, - child: MouseRegion( - cursor: SystemMouseCursors.click, - child: Container( - width: SimpleTableConstants.addColumnButtonWidth, - margin: const EdgeInsets.symmetric( - horizontal: SimpleTableConstants.addColumnButtonPadding, - ), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular( - SimpleTableConstants.addColumnButtonRadius, - ), - color: context.simpleTableMoreActionBackgroundColor, - ), - child: const FlowySvg( - FlowySvgs.add_s, - ), - ), - ), - ), - ); - } -} - -class SimpleTableAddColumnAndRowHoverButton extends StatelessWidget { - const SimpleTableAddColumnAndRowHoverButton({ - super.key, - required this.editorState, - required this.node, - }); - - final EditorState editorState; - final Node node; - - @override - Widget build(BuildContext context) { - assert(node.type == SimpleTableBlockKeys.type); - - if (node.type != SimpleTableBlockKeys.type) { - return const SizedBox.shrink(); - } - - return ValueListenableBuilder( - valueListenable: context.read().isHoveringOnTableArea, - builder: (context, isHoveringOnTableArea, child) { - return ValueListenableBuilder( - valueListenable: context.read().hoveringTableCell, - builder: (context, hoveringTableCell, child) { - bool shouldShow = isHoveringOnTableArea; - if (hoveringTableCell != null && _enableHoveringLogicV2) { - shouldShow = hoveringTableCell.isLastCellInTable; - } - return shouldShow - ? Positioned( - bottom: - SimpleTableConstants.addColumnAndRowButtonBottomPadding, - right: SimpleTableConstants.addColumnButtonPadding, - child: SimpleTableAddColumnAndRowButton( - onTap: () => editorState.addColumnAndRowInTable(node), - ), - ) - : const SizedBox.shrink(); - }, - ); - }, - ); - } -} - -class SimpleTableAddColumnAndRowButton extends StatelessWidget { - const SimpleTableAddColumnAndRowButton({ - super.key, - this.onTap, - }); - - final VoidCallback? onTap; - - @override - Widget build(BuildContext context) { - return FlowyTooltip( - message: LocaleKeys.document_plugins_simpleTable_clickToAddNewRowAndColumn - .tr(), - child: GestureDetector( - behavior: HitTestBehavior.translucent, - onTap: onTap, - child: MouseRegion( - cursor: SystemMouseCursors.click, - child: Container( - width: SimpleTableConstants.addColumnAndRowButtonWidth, - height: SimpleTableConstants.addColumnAndRowButtonHeight, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular( - SimpleTableConstants.addColumnAndRowButtonCornerRadius, - ), - color: context.simpleTableMoreActionBackgroundColor, - ), - child: const FlowySvg( - FlowySvgs.add_s, - ), - ), - ), - ), - ); - } -} - class SimpleTableRowDivider extends StatelessWidget { const SimpleTableRowDivider({ super.key, diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table.dart index a4e3dabcbf..06abc4a283 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table.dart @@ -5,3 +5,6 @@ export 'simple_table_more_action.dart'; export 'simple_table_operations/simple_table_operations.dart'; export 'simple_table_row_block_component.dart'; export 'simple_table_shortcuts/simple_table_commands.dart'; +export 'simple_table_widgets/simple_table_add_column_and_row_button.dart'; +export 'simple_table_widgets/simple_table_add_column_button.dart'; +export 'simple_table_widgets/simple_table_add_row_button.dart'; diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_constants.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_constants.dart index 8cf73a8bcb..9c0952daa4 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_constants.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_constants.dart @@ -66,6 +66,12 @@ class SimpleTableContext { /// This value is only available when isReordering is true final ValueNotifier reorderingOffset = ValueNotifier(Offset.zero); + /// isDraggingRow to expand the rows of the table + bool isDraggingRow = false; + + /// isDraggingColumn to expand the columns of the table + bool isDraggingColumn = false; + bool get isReordering => isReorderingColumn.value.$1 || isReorderingRow.value.$1; @@ -166,6 +172,8 @@ class SimpleTableConstants { static const defaultColumnWidth = 120.0; static const minimumColumnWidth = 36.0; + static const defaultRowHeight = 36.0; + static const tableTopPadding = 8.0; static const tableLeftPadding = 8.0; @@ -223,6 +231,13 @@ class SimpleTableConstants { static const moreActionPadding = EdgeInsets.symmetric(vertical: 2.0); static const moreActionHorizontalMargin = EdgeInsets.symmetric(horizontal: 6.0); + + /// Only displaying the add row / add column / add column and row button + /// when hovering on the last row / last column / last cell. + static const enableHoveringLogicV2 = true; + + /// Enable the drag to expand the table + static const enableDragToExpandTable = false; } enum SimpleTableBorderRenderType { diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_delete_operation.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_delete_operation.dart index cb9c7f28dd..6598ad5a41 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_delete_operation.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_delete_operation.dart @@ -18,8 +18,9 @@ extension TableDeletionOperations on EditorState { /// Row 1: | | | | Future deleteRowInTable( Node node, - int index, - ) async { + int index, { + bool inMemoryUpdate = false, + }) async { assert(node.type == SimpleTableBlockKeys.type); if (node.type != SimpleTableBlockKeys.type) { @@ -48,7 +49,12 @@ extension TableDeletionOperations on EditorState { if (attributes != null) { transaction.updateNode(node, attributes); } - await apply(transaction); + await apply( + transaction, + options: ApplyOptions( + inMemoryUpdate: inMemoryUpdate, + ), + ); } /// Delete a column at the given index. @@ -64,7 +70,11 @@ extension TableDeletionOperations on EditorState { /// After: /// Row 1: | 0 | 1 | /// Row 2: | | | - Future deleteColumnInTable(Node node, int index) async { + Future deleteColumnInTable( + Node node, + int index, { + bool inMemoryUpdate = false, + }) async { assert(node.type == SimpleTableBlockKeys.type); if (node.type != SimpleTableBlockKeys.type) { @@ -96,6 +106,11 @@ extension TableDeletionOperations on EditorState { if (attributes != null) { transaction.updateNode(node, attributes); } - await apply(transaction); + await apply( + transaction, + options: ApplyOptions( + inMemoryUpdate: inMemoryUpdate, + ), + ); } } diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_insert_operation.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_insert_operation.dart index d430569dcf..7a92aa3c7e 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_insert_operation.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_insert_operation.dart @@ -1,8 +1,8 @@ import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_table/simple_table_block_component.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_table/simple_table_cell_block_component.dart'; -import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_table/simple_table_row_block_component.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_map_operation.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_node_extension.dart'; +import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_table/simple_table_row_block_component.dart'; import 'package:appflowy_backend/log.dart'; import 'package:appflowy_editor/appflowy_editor.dart'; @@ -90,7 +90,11 @@ extension TableInsertionOperations on EditorState { /// After: ↓ New column /// Row 1: | 0 | | 1 | /// Row 2: | | | | - Future insertColumnInTable(Node node, int index) async { + Future insertColumnInTable( + Node node, + int index, { + bool inMemoryUpdate = false, + }) async { assert(node.type == SimpleTableBlockKeys.type); if (node.type != SimpleTableBlockKeys.type) { @@ -133,7 +137,12 @@ extension TableInsertionOperations on EditorState { if (attributes != null) { transaction.updateNode(node, attributes); } - await apply(transaction); + await apply( + transaction, + options: ApplyOptions( + inMemoryUpdate: inMemoryUpdate, + ), + ); } /// Add a row at the given index. @@ -149,7 +158,11 @@ extension TableInsertionOperations on EditorState { /// Row 1: | | | /// Row 2: | | | /// Row 3: | | | ← New row - Future insertRowInTable(Node node, int index) async { + Future insertRowInTable( + Node node, + int index, { + bool inMemoryUpdate = false, + }) async { assert(node.type == SimpleTableBlockKeys.type); if (node.type != SimpleTableBlockKeys.type) { @@ -190,6 +203,11 @@ extension TableInsertionOperations on EditorState { if (attributes != null) { transaction.updateNode(node, attributes); } - await apply(transaction); + await apply( + transaction, + options: ApplyOptions( + inMemoryUpdate: inMemoryUpdate, + ), + ); } } diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_node_extension.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_node_extension.dart index c7e1f73ae2..ab95bdcf54 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_node_extension.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_node_extension.dart @@ -6,6 +6,7 @@ import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_tab import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_table/simple_table_row_block_component.dart'; import 'package:appflowy_backend/log.dart'; import 'package:appflowy_editor/appflowy_editor.dart'; +import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; typedef TableCellPosition = (int, int); @@ -95,6 +96,9 @@ extension TableNodeExtension on Node { int get rowIndex { if (type == SimpleTableCellBlockKeys.type) { + if (path.parent.isEmpty) { + return -1; + } return path.parent.last; } else if (type == SimpleTableRowBlockKeys.type) { return path.last; @@ -104,6 +108,9 @@ extension TableNodeExtension on Node { int get columnIndex { assert(type == SimpleTableCellBlockKeys.type); + if (path.isEmpty) { + return -1; + } return path.last; } @@ -191,6 +198,18 @@ extension TableNodeExtension on Node { return tableNode; } + Node? get parentTableCellNode { + Node? tableCellNode; + + if (type == SimpleTableCellBlockKeys.type) { + tableCellNode = this; + } else { + return parent?.parentTableCellNode; + } + + return tableCellNode; + } + double get columnWidth { final parentTableNode = this.parentTableNode; @@ -260,7 +279,8 @@ extension TableNodeExtension on Node { parentTableNode.type != SimpleTableBlockKeys.type) { return false; } - return parentTableNode.isHeaderColumnEnabled && parent?.columnIndex == 0; + return parentTableNode.isHeaderColumnEnabled && + parentTableCellNode?.columnIndex == 0; } /// Whether the current node is in the header row. @@ -272,7 +292,8 @@ extension TableNodeExtension on Node { parentTableNode.type != SimpleTableBlockKeys.type) { return false; } - return parentTableNode.isHeaderRowEnabled && parent?.rowIndex == 0; + return parentTableNode.isHeaderRowEnabled && + parentTableCellNode?.rowIndex == 0; } SimpleTableRowAlignMap get rowAligns { @@ -529,4 +550,95 @@ extension TableNodeExtension on Node { .join('\n'); return content; } + + /// Return the first empty row in the table from bottom to top. + /// + /// Example: + /// + /// | A | B | C | + /// | | | | + /// | E | F | G | + /// | H | I | J | + /// | | | | <--- The first empty row is the row at index 3. + /// | | | | + /// + /// The first empty row is the row at index 3. + (int, Node)? getFirstEmptyRowFromBottom() { + assert(type == SimpleTableBlockKeys.type); + + if (type != SimpleTableBlockKeys.type) { + return null; + } + + (int, Node)? result; + + for (var i = children.length - 1; i >= 0; i--) { + final row = children[i]; + + // Check if all cells in this row are empty + final hasContent = row.children.any((cell) { + final content = getTableCellContent( + rowIndex: i, + columnIndex: row.children.indexOf(cell), + ); + return content != null && content.isNotEmpty; + }); + + if (!hasContent) { + if (result != null) { + final (index, _) = result; + if (i <= index) { + result = (i, row); + } + } else { + result = (i, row); + } + } + } + + return result; + } + + /// Return the first empty column in the table from right to left. + /// + /// Example: + /// ↓ The first empty column is the column at index 3. + /// | A | C | | E | | | + /// | B | D | | F | | | + /// + /// The first empty column is the column at index 3. + int? getFirstEmptyColumnFromRight() { + assert(type == SimpleTableBlockKeys.type); + + if (type != SimpleTableBlockKeys.type) { + return null; + } + + int? result; + + for (var i = columnLength - 1; i >= 0; i--) { + bool hasContent = false; + for (var j = 0; j < rowLength; j++) { + final content = getTableCellContent( + rowIndex: j, + columnIndex: i, + ); + if (content != null && content.isNotEmpty) { + hasContent = true; + } + } + if (!hasContent) { + if (result != null) { + final index = result; + if (i <= index) { + result = i; + } + } else { + result = i; + } + } + } + + return result; + } } diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_shortcuts/simple_table_backspace_command.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_shortcuts/simple_table_backspace_command.dart index 0ebfd013e3..dedc59da47 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_shortcuts/simple_table_backspace_command.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_shortcuts/simple_table_backspace_command.dart @@ -1,5 +1,6 @@ import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_table/simple_table_shortcuts/simple_table_command_extension.dart'; import 'package:appflowy_editor/appflowy_editor.dart'; +import 'package:appflowy_editor_plugins/appflowy_editor_plugins.dart'; import 'package:flutter/material.dart'; final CommandShortcutEvent backspaceInTableCell = CommandShortcutEvent( @@ -22,11 +23,25 @@ KeyEventResult _backspaceInTableCellHandler(EditorState editorState) { final onlyContainsOneChild = tableCellNode.children.length == 1; final isParagraphNode = tableCellNode.children.first.type == ParagraphBlockKeys.type; + final isCodeBlock = tableCellNode.children.first.type == CodeBlockKeys.type; if (onlyContainsOneChild && selection.isCollapsed && - selection.end.offset == 0 && - isParagraphNode) { - return KeyEventResult.skipRemainingHandlers; + selection.end.offset == 0) { + if (isParagraphNode) { + return KeyEventResult.skipRemainingHandlers; + } else if (isCodeBlock) { + // replace the codeblock with a paragraph + final transaction = editorState.transaction; + transaction.insertNode(node.path, paragraphNode()); + transaction.deleteNode(node); + transaction.afterSelection = Selection.collapsed( + Position( + path: node.path, + ), + ); + editorState.apply(transaction); + return KeyEventResult.handled; + } } return KeyEventResult.ignored; diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_shortcuts/simple_table_commands.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_shortcuts/simple_table_commands.dart index b52adc6334..6fca0a5275 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_shortcuts/simple_table_commands.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_shortcuts/simple_table_commands.dart @@ -3,6 +3,7 @@ import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_tab import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_table/simple_table_shortcuts/simple_table_arrow_right_command.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_table/simple_table_shortcuts/simple_table_arrow_up_command.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_table/simple_table_shortcuts/simple_table_backspace_command.dart'; +import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_table/simple_table_shortcuts/simple_table_enter_command.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_table/simple_table_shortcuts/simple_table_select_all_command.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_table/simple_table_shortcuts/simple_table_tab_command.dart'; @@ -15,4 +16,5 @@ final simpleTableCommands = [ shiftTabInTableCell, backspaceInTableCell, selectAllInTableCellCommand, + enterInTableCell, ]; diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_shortcuts/simple_table_enter_command.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_shortcuts/simple_table_enter_command.dart new file mode 100644 index 0000000000..bc5a0a09c2 --- /dev/null +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_shortcuts/simple_table_enter_command.dart @@ -0,0 +1,24 @@ +import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_table/simple_table_shortcuts/simple_table_command_extension.dart'; +import 'package:appflowy_editor/appflowy_editor.dart'; +import 'package:flutter/material.dart'; + +final CommandShortcutEvent enterInTableCell = CommandShortcutEvent( + key: 'Press enter in table cell', + getDescription: () => 'Press the enter key in table cell', + command: 'enter', + handler: _enterInTableCellHandler, +); + +KeyEventResult _enterInTableCellHandler(EditorState editorState) { + final (isInTableCell, selection, tableCellNode, node) = + editorState.isCurrentSelectionInTableCell(); + if (!isInTableCell || + selection == null || + tableCellNode == null || + node == null) { + return KeyEventResult.ignored; + } + + // forward the enter command to the insertNewLine character command to support multi-line text in table cell + return KeyEventResult.skipRemainingHandlers; +} diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_add_column_and_row_button.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_add_column_and_row_button.dart new file mode 100644 index 0000000000..b859895c6d --- /dev/null +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_add_column_and_row_button.dart @@ -0,0 +1,91 @@ +import 'package:appflowy/generated/flowy_svgs.g.dart'; +import 'package:appflowy/generated/locale_keys.g.dart'; +import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.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/material.dart'; +import 'package:provider/provider.dart'; + +class SimpleTableAddColumnAndRowHoverButton extends StatelessWidget { + const SimpleTableAddColumnAndRowHoverButton({ + super.key, + required this.editorState, + required this.node, + }); + + final EditorState editorState; + final Node node; + + @override + Widget build(BuildContext context) { + assert(node.type == SimpleTableBlockKeys.type); + + if (node.type != SimpleTableBlockKeys.type) { + return const SizedBox.shrink(); + } + + return ValueListenableBuilder( + valueListenable: context.read().isHoveringOnTableArea, + builder: (context, isHoveringOnTableArea, child) { + return ValueListenableBuilder( + valueListenable: context.read().hoveringTableCell, + builder: (context, hoveringTableCell, child) { + bool shouldShow = isHoveringOnTableArea; + if (hoveringTableCell != null && + SimpleTableConstants.enableHoveringLogicV2) { + shouldShow = hoveringTableCell.isLastCellInTable; + } + return shouldShow + ? Positioned( + bottom: + SimpleTableConstants.addColumnAndRowButtonBottomPadding, + right: SimpleTableConstants.addColumnButtonPadding, + child: SimpleTableAddColumnAndRowButton( + onTap: () => editorState.addColumnAndRowInTable(node), + ), + ) + : const SizedBox.shrink(); + }, + ); + }, + ); + } +} + +class SimpleTableAddColumnAndRowButton extends StatelessWidget { + const SimpleTableAddColumnAndRowButton({ + super.key, + this.onTap, + }); + + final VoidCallback? onTap; + + @override + Widget build(BuildContext context) { + return FlowyTooltip( + message: LocaleKeys.document_plugins_simpleTable_clickToAddNewRowAndColumn + .tr(), + child: GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: onTap, + child: MouseRegion( + cursor: SystemMouseCursors.click, + child: Container( + width: SimpleTableConstants.addColumnAndRowButtonWidth, + height: SimpleTableConstants.addColumnAndRowButtonHeight, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular( + SimpleTableConstants.addColumnAndRowButtonCornerRadius, + ), + color: context.simpleTableMoreActionBackgroundColor, + ), + child: const FlowySvg( + FlowySvgs.add_s, + ), + ), + ), + ), + ); + } +} diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_add_column_button.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_add_column_button.dart new file mode 100644 index 0000000000..9287b78e88 --- /dev/null +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_add_column_button.dart @@ -0,0 +1,219 @@ +import 'package:appflowy/generated/flowy_svgs.g.dart'; +import 'package:appflowy/generated/locale_keys.g.dart'; +import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.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/material.dart'; +import 'package:provider/provider.dart'; + +class SimpleTableAddColumnHoverButton extends StatefulWidget { + const SimpleTableAddColumnHoverButton({ + super.key, + required this.editorState, + required this.tableNode, + }); + + final EditorState editorState; + final Node tableNode; + + @override + State createState() => + _SimpleTableAddColumnHoverButtonState(); +} + +class _SimpleTableAddColumnHoverButtonState + extends State { + late final interceptorKey = + 'simple_table_add_column_hover_button_${widget.tableNode.id}'; + + SelectionGestureInterceptor? interceptor; + + Offset? startDraggingOffset; + int? initialColumnCount; + + @override + void initState() { + super.initState(); + + interceptor = SelectionGestureInterceptor( + key: interceptorKey, + canTap: (details) => !_isTapInBounds(details.globalPosition), + ); + widget.editorState.service.selectionService + .registerGestureInterceptor(interceptor!); + } + + @override + void dispose() { + widget.editorState.service.selectionService.unregisterGestureInterceptor( + interceptorKey, + ); + + super.dispose(); + } + + @override + Widget build(BuildContext context) { + assert(widget.tableNode.type == SimpleTableBlockKeys.type); + + if (widget.tableNode.type != SimpleTableBlockKeys.type) { + return const SizedBox.shrink(); + } + + return ValueListenableBuilder( + valueListenable: context.read().isHoveringOnTableArea, + builder: (context, isHoveringOnTableArea, _) { + return ValueListenableBuilder( + valueListenable: context.read().hoveringTableCell, + builder: (context, hoveringTableCell, _) { + bool shouldShow = isHoveringOnTableArea; + if (hoveringTableCell != null && + SimpleTableConstants.enableHoveringLogicV2) { + shouldShow = hoveringTableCell.columnIndex + 1 == + hoveringTableCell.columnLength; + } + return Positioned( + top: SimpleTableConstants.tableTopPadding - + SimpleTableConstants.cellBorderWidth, + bottom: SimpleTableConstants.addColumnButtonBottomPadding, + right: 0, + child: Opacity( + opacity: shouldShow ? 1.0 : 0.0, + child: SimpleTableAddColumnButton( + onTap: () => + widget.editorState.addColumnInTable(widget.tableNode), + onHorizontalDragStart: (details) { + context.read().isDraggingColumn = true; + startDraggingOffset = details.globalPosition; + initialColumnCount = widget.tableNode.columnLength; + }, + onHorizontalDragEnd: (details) { + context.read().isDraggingColumn = false; + }, + onHorizontalDragUpdate: (details) { + _insertColumnInMemory(details); + }, + ), + ), + ); + }, + ); + }, + ); + } + + bool _isTapInBounds(Offset offset) { + final renderBox = context.findRenderObject() as RenderBox?; + if (renderBox == null) { + return false; + } + + final localPosition = renderBox.globalToLocal(offset); + final result = renderBox.paintBounds.contains(localPosition); + + return result; + } + + void _insertColumnInMemory(DragUpdateDetails details) { + if (!SimpleTableConstants.enableDragToExpandTable) { + return; + } + + if (startDraggingOffset == null || initialColumnCount == null) { + return; + } + + // calculate the horizontal offset from the start dragging offset + final horizontalOffset = + details.globalPosition.dx - startDraggingOffset!.dx; + + const columnWidth = SimpleTableConstants.defaultColumnWidth; + final columnDelta = (horizontalOffset / columnWidth).round(); + + // if the change is less than 1 column, skip the operation + if (columnDelta.abs() < 1) { + return; + } + + final firstEmptyColumnFromRight = + widget.tableNode.getFirstEmptyColumnFromRight(); + if (firstEmptyColumnFromRight == null) { + return; + } + + final currentColumnCount = widget.tableNode.columnLength; + final targetColumnCount = initialColumnCount! + columnDelta; + + // There're 3 cases that we don't want to proceed: + // 1. targetColumnCount < 0: the table at least has 1 column + // 2. targetColumnCount == currentColumnCount: the table has no change + // 3. targetColumnCount <= initialColumnCount: the table has less columns than the initial column count + if (targetColumnCount <= 0 || + targetColumnCount == currentColumnCount || + targetColumnCount <= firstEmptyColumnFromRight) { + return; + } + + if (targetColumnCount > currentColumnCount) { + widget.editorState.insertColumnInTable( + widget.tableNode, + targetColumnCount, + inMemoryUpdate: true, + ); + } else { + widget.editorState.deleteColumnInTable( + widget.tableNode, + targetColumnCount, + inMemoryUpdate: true, + ); + } + } +} + +class SimpleTableAddColumnButton extends StatelessWidget { + const SimpleTableAddColumnButton({ + super.key, + this.onTap, + required this.onHorizontalDragStart, + required this.onHorizontalDragEnd, + required this.onHorizontalDragUpdate, + }); + + final VoidCallback? onTap; + final void Function(DragStartDetails) onHorizontalDragStart; + final void Function(DragEndDetails) onHorizontalDragEnd; + final void Function(DragUpdateDetails) onHorizontalDragUpdate; + + @override + Widget build(BuildContext context) { + return FlowyTooltip( + message: LocaleKeys.document_plugins_simpleTable_clickToAddNewColumn.tr(), + child: GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: onTap, + onHorizontalDragStart: onHorizontalDragStart, + onHorizontalDragEnd: onHorizontalDragEnd, + onHorizontalDragUpdate: onHorizontalDragUpdate, + child: MouseRegion( + cursor: SystemMouseCursors.click, + child: Container( + width: SimpleTableConstants.addColumnButtonWidth, + margin: const EdgeInsets.symmetric( + horizontal: SimpleTableConstants.addColumnButtonPadding, + ), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular( + SimpleTableConstants.addColumnButtonRadius, + ), + color: context.simpleTableMoreActionBackgroundColor, + ), + child: const FlowySvg( + FlowySvgs.add_s, + ), + ), + ), + ), + ); + } +} diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_add_row_button.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_add_row_button.dart new file mode 100644 index 0000000000..f0037bd0af --- /dev/null +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_add_row_button.dart @@ -0,0 +1,221 @@ +import 'package:appflowy/generated/flowy_svgs.g.dart'; +import 'package:appflowy/generated/locale_keys.g.dart'; +import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.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/material.dart'; +import 'package:provider/provider.dart'; + +class SimpleTableAddRowHoverButton extends StatefulWidget { + const SimpleTableAddRowHoverButton({ + super.key, + required this.editorState, + required this.tableNode, + }); + + final EditorState editorState; + final Node tableNode; + + @override + State createState() => + _SimpleTableAddRowHoverButtonState(); +} + +class _SimpleTableAddRowHoverButtonState + extends State { + late final interceptorKey = + 'simple_table_add_row_hover_button_${widget.tableNode.id}'; + + SelectionGestureInterceptor? interceptor; + + Offset? startDraggingOffset; + int? initialRowCount; + + @override + void initState() { + super.initState(); + + interceptor = SelectionGestureInterceptor( + key: interceptorKey, + canTap: (details) => !_isTapInBounds(details.globalPosition), + ); + widget.editorState.service.selectionService + .registerGestureInterceptor(interceptor!); + } + + @override + void dispose() { + widget.editorState.service.selectionService.unregisterGestureInterceptor( + interceptorKey, + ); + + super.dispose(); + } + + @override + Widget build(BuildContext context) { + assert(widget.tableNode.type == SimpleTableBlockKeys.type); + + if (widget.tableNode.type != SimpleTableBlockKeys.type) { + return const SizedBox.shrink(); + } + + final simpleTableContext = context.read(); + return ValueListenableBuilder( + valueListenable: simpleTableContext.isHoveringOnTableArea, + builder: (context, isHoveringOnTableArea, child) { + return ValueListenableBuilder( + valueListenable: simpleTableContext.hoveringTableCell, + builder: (context, hoveringTableCell, _) { + bool shouldShow = isHoveringOnTableArea; + if (hoveringTableCell != null && + SimpleTableConstants.enableHoveringLogicV2) { + shouldShow = + hoveringTableCell.rowIndex + 1 == hoveringTableCell.rowLength; + } + if (simpleTableContext.isDraggingRow) { + shouldShow = true; + } + return shouldShow ? child! : const SizedBox.shrink(); + }, + ); + }, + child: Positioned( + bottom: 2 * SimpleTableConstants.addRowButtonPadding, + left: SimpleTableConstants.tableLeftPadding - + SimpleTableConstants.cellBorderWidth, + right: SimpleTableConstants.addRowButtonRightPadding, + child: SimpleTableAddRowButton( + onTap: () => widget.editorState.addRowInTable( + widget.tableNode, + ), + onVerticalDragStart: (details) { + context.read().isDraggingRow = true; + startDraggingOffset = details.globalPosition; + initialRowCount = widget.tableNode.children.length; + }, + onVerticalDragEnd: (details) { + context.read().isDraggingRow = false; + }, + onVerticalDragUpdate: (details) { + _insertRowInMemory(details); + }, + ), + ), + ); + } + + bool _isTapInBounds(Offset offset) { + final renderBox = context.findRenderObject() as RenderBox?; + if (renderBox == null) { + return false; + } + + final localPosition = renderBox.globalToLocal(offset); + final result = renderBox.paintBounds.contains(localPosition); + + return result; + } + + void _insertRowInMemory(DragUpdateDetails details) { + if (!SimpleTableConstants.enableDragToExpandTable) { + return; + } + + if (startDraggingOffset == null || initialRowCount == null) { + return; + } + + // calculate the vertical offset from the start dragging offset + final verticalOffset = details.globalPosition.dy - startDraggingOffset!.dy; + + const rowHeight = SimpleTableConstants.defaultRowHeight; + final rowDelta = (verticalOffset / rowHeight).round(); + + // if the change is less than 1 row, skip the operation + if (rowDelta.abs() < 1) { + return; + } + + final firstEmptyRowFromBottom = + widget.tableNode.getFirstEmptyRowFromBottom(); + if (firstEmptyRowFromBottom == null) { + return; + } + + final currentRowCount = widget.tableNode.children.length; + final targetRowCount = initialRowCount! + rowDelta; + + // There're 3 cases that we don't want to proceed: + // 1. targetRowCount < 0: the table at least has 1 row + // 2. targetRowCount == currentRowCount: the table has no change + // 3. targetRowCount <= initialRowCount: the table has less rows than the initial row count + if (targetRowCount <= 0 || + targetRowCount == currentRowCount || + targetRowCount <= firstEmptyRowFromBottom.$1) { + return; + } + + if (targetRowCount > currentRowCount) { + widget.editorState.insertRowInTable( + widget.tableNode, + targetRowCount, + inMemoryUpdate: true, + ); + } else { + widget.editorState.deleteRowInTable( + widget.tableNode, + targetRowCount, + inMemoryUpdate: true, + ); + } + } +} + +class SimpleTableAddRowButton extends StatelessWidget { + const SimpleTableAddRowButton({ + super.key, + this.onTap, + required this.onVerticalDragStart, + required this.onVerticalDragEnd, + required this.onVerticalDragUpdate, + }); + + final VoidCallback? onTap; + final void Function(DragStartDetails) onVerticalDragStart; + final void Function(DragEndDetails) onVerticalDragEnd; + final void Function(DragUpdateDetails) onVerticalDragUpdate; + + @override + Widget build(BuildContext context) { + return FlowyTooltip( + message: LocaleKeys.document_plugins_simpleTable_clickToAddNewRow.tr(), + child: GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: onTap, + onVerticalDragStart: onVerticalDragStart, + onVerticalDragEnd: onVerticalDragEnd, + onVerticalDragUpdate: onVerticalDragUpdate, + child: MouseRegion( + cursor: SystemMouseCursors.click, + child: Container( + height: SimpleTableConstants.addRowButtonHeight, + margin: const EdgeInsets.symmetric( + vertical: SimpleTableConstants.addColumnButtonPadding, + ), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular( + SimpleTableConstants.addRowButtonRadius, + ), + color: context.simpleTableMoreActionBackgroundColor, + ), + child: const FlowySvg( + FlowySvgs.add_s, + ), + ), + ), + ), + ); + } +} diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_column_resize_handle.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_column_resize_handle.dart index 5555c39de4..bfa1f316d7 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_column_resize_handle.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_column_resize_handle.dart @@ -20,12 +20,12 @@ class SimpleTableColumnResizeHandle extends StatefulWidget { class _SimpleTableColumnResizeHandleState extends State { + late final simpleTableContext = context.read(); + bool isStartDragging = false; @override Widget build(BuildContext context) { - final simpleTableContext = context.read(); - return MouseRegion( cursor: SystemMouseCursors.resizeColumn, onEnter: (_) => _onEnterHoverArea(), @@ -57,8 +57,7 @@ class _SimpleTableColumnResizeHandleState } void _onEnterHoverArea() { - context.read().hoveringOnResizeHandle.value = - widget.node; + simpleTableContext.hoveringOnResizeHandle.value = widget.node; } void _onExitHoverArea() { @@ -66,7 +65,7 @@ class _SimpleTableColumnResizeHandleState // the onExit event will be triggered before dragging started. // delay the hiding of the resize handle to avoid flickering. if (!isStartDragging) { - context.read().hoveringOnResizeHandle.value = null; + simpleTableContext.hoveringOnResizeHandle.value = null; } }); } diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_widget.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_widget.dart index 62acf509e1..0767ec4a47 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_widget.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_widget.dart @@ -134,7 +134,7 @@ class _SimpleTableWidgetState extends State { if (widget.enableAddColumnButton) SimpleTableAddColumnHoverButton( editorState: editorState, - node: widget.node, + tableNode: widget.node, ), if (widget.enableAddRowButton) SimpleTableAddRowHoverButton( diff --git a/frontend/appflowy_flutter/pubspec.lock b/frontend/appflowy_flutter/pubspec.lock index c8dc56a973..9e05d06614 100644 --- a/frontend/appflowy_flutter/pubspec.lock +++ b/frontend/appflowy_flutter/pubspec.lock @@ -61,8 +61,8 @@ packages: dependency: "direct main" description: path: "." - ref: "157ded3" - resolved-ref: "157ded3cd321b9a54d011c0cc27e270ded35d3aa" + ref: f32762e + resolved-ref: f32762eae0a703e15ae681947ad1ff110d2e477a url: "https://github.com/AppFlowy-IO/appflowy-editor.git" source: git version: "4.0.0" diff --git a/frontend/appflowy_flutter/pubspec.yaml b/frontend/appflowy_flutter/pubspec.yaml index f02b420acb..e452bdab82 100644 --- a/frontend/appflowy_flutter/pubspec.yaml +++ b/frontend/appflowy_flutter/pubspec.yaml @@ -173,7 +173,7 @@ dependency_overrides: appflowy_editor: git: url: https://github.com/AppFlowy-IO/appflowy-editor.git - ref: "157ded3" + ref: "f32762e" appflowy_editor_plugins: git: From d68212f4ce467521dd99312eaec7f9f85fe9d142 Mon Sep 17 00:00:00 2001 From: uxadax <66478248+uxadax@users.noreply.github.com> Date: Wed, 11 Dec 2024 08:30:52 +0100 Subject: [PATCH 028/576] =?UTF-8?q?chore(i18n):=20update=20de-DE=20transla?= =?UTF-8?q?tions=20=F0=9F=90=A6=20(#6938)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/resources/translations/de-DE.json | 134 ++++++++++++++++++++- 1 file changed, 130 insertions(+), 4 deletions(-) diff --git a/frontend/resources/translations/de-DE.json b/frontend/resources/translations/de-DE.json index 3ecced3c0e..62772137f6 100644 --- a/frontend/resources/translations/de-DE.json +++ b/frontend/resources/translations/de-DE.json @@ -130,7 +130,9 @@ "copyShareLink": "Link zum Teilen kopieren", "copyLinkFailed": "Link konnte nicht in die Zwischenablage kopiert werden", "copyLinkToBlockSuccess": "Blocklink in die Zwischenablage kopiert", - "copyLinkToBlockFailed": "Blocklink konnte nicht in die Zwischenablage kopiert werden" + "copyLinkToBlockFailed": "Blocklink konnte nicht in die Zwischenablage kopiert werden", + "manageAllSites": "Alle Seiten verwalten", + "updatePathName": "Pfadnamen aktualisieren" }, "moreAction": { "small": "klein", @@ -180,12 +182,15 @@ "relatedQuestion": "Verwandt", "serverUnavailable": "Dienst vorübergehend nicht verfügbar. Bitte versuche es später erneut.", "aiServerUnavailable": "Beim Generieren einer Antwort ist ein Fehler aufgetreten.", + "retry": "Wiederholen", "clickToRetry": "Erneut versuchen", "regenerateAnswer": "Regenerieren", "question1": "Wie verwendet man Kanban zur Aufgabenverwaltung?", "question2": "Erkläre mir die GTD-Methode", "question3": "Warum sollte ich Rust verwenden?", "question4": "Gebe mir ein Rezept mit dem, was in meiner Küche ist", + "question5": "Eine Illustration für meine Seite erstellen", + "question6": "Erstelle eine To-Do-Liste für meine kommende Woche", "aiMistakePrompt": "KI kann Fehler machen. Überprüfe wichtige Informationen.", "chatWithFilePrompt": "Möchtest du mit der Datei chatten?", "indexFileSuccess": "Datei erfolgreich indiziert", @@ -198,7 +203,8 @@ "clickToMention": "Klicke hier, um eine Seite zu erwähnen", "uploadFile": "Lade PDF-, md- oder txt-Dateien in den Chat hoch", "questionDetail": "Hallo {}! Wie kann ich dir heute helfen?", - "indexingFile": "Indizierung {}" + "indexingFile": "Indizierung {}", + "generatingResponse": "Antwort generieren" }, "trash": { "text": "Papierkorb", @@ -335,6 +341,7 @@ "askOwnerToUpgradeToAIMax": "In deinem Arbeitsbereich sind die kostenlosen KI-Antworten aufgebraucht. Bitte den Eigentümer deines Arbeitsbereichs, den Plan zu wechseln oder KI-Add-ons zu erwerben.", "askOwnerToUpgradeToAIMaxIOS": "In Ihrem Arbeitsbereich gehen die kostenlosen KI-Antworten aus.", "purchaseStorageSpace": "Speicherplatz kaufen", + "singleFileProPlanLimitationDescription": "Sie haben die maximal zulässige Datei-Uploadgröße im kostenlosen Plan überschritten. Bitte aktualisieren Sie auf den Pro-Plan, um größere Dateien hochzuladen", "purchaseAIResponse": "Kaufen ", "askOwnerToUpgradeToLocalAI": "Bitte den Arbeitsbereichsbesitzer, KI auf dem Gerät zu aktivieren.", "upgradeToAILocal": "KI offline auf Ihrem Gerät", @@ -375,6 +382,7 @@ "upload": "Hochladen", "edit": "Bearbeiten", "delete": "Löschen", + "copy": "kopieren", "duplicate": "Duplikat", "putback": "wieder zurückgeben", "update": "Update", @@ -410,6 +418,7 @@ "submit": "Einreichen", "download": "Herunterladen", "backToHome": "Zurück zur Startseite", + "viewing": "anschauen", "editing": "Bearbeiten", "gotIt": "Verstanden" }, @@ -441,6 +450,68 @@ "trash": "Müll", "helpAndSupport": "Hilfe & Unterstützung" }, + "sites": { + "title": "Seiten", + "namespaceTitle": "Namensraum", + "namespaceDescription": "Verwalten Sie Ihren Namespace und Ihre Startseite", + "namespaceHeader": "Namensraum", + "homepageHeader": "Startseite", + "updateNamespace": "Namespace aktualisieren", + "removeHomepage": "Startseite entfernen", + "selectHomePage": "Seite auswählen", + "clearHomePage": "Löschen Sie die Startseite für diesen Namensraum", + "customUrl": "Benutzerdefinierte URL", + "namespace": { + "description": "Diese Änderung gilt für alle veröffentlichten Seiten in diesem Namespace.", + "tooltip": "Wir behalten uns das Recht vor, unangemessene Namespaces zu entfernen", + "updateExistingNamespace": "Vorhandenen Namensraum aktualisieren", + "upgradeToPro": "Aktualisieren Sie auf den Pro-Plan, um eine Startseite einzurichten", + "redirectToPayment": "Weiterleitung zur Zahlungsseite ...", + "onlyWorkspaceOwnerCanSetHomePage": "Nur der Arbeitsbereichsbesitzer kann eine Startseite festlegen", + "pleaseAskOwnerToSetHomePage": "Bitten Sie den Arbeitsbereichsbesitzer, auf den Pro-Plan zu aktualisieren" + }, + "publishedPage": { + "title": "Alle veröffentlichten Seiten", + "description": "Verwalten Sie Ihre veröffentlichten Seiten", + "page": "Seite", + "pathName": "Pfadname", + "date": "Veröffentlichungsdatum", + "emptyHinText": "Sie haben keine veröffentlichten Seiten in diesem Arbeitsbereich", + "noPublishedPages": "Keine veröffentlichten Seiten", + "settings": "Veröffentlichungseinstellungen", + "clickToOpenPageInApp": "Seite in App öffnen", + "clickToOpenPageInBrowser": "Seite im Browser öffnen" + }, + "error": { + "failedToGeneratePaymentLink": "Zahlungslink für Pro Plan konnte nicht generiert werden", + "failedToUpdateNamespace": "Namensraum konnte nicht aktualisiert werden", + "proPlanLimitation": "Sie müssen auf den Pro-Plan upgraden, um den Namespace zu aktualisieren", + "namespaceAlreadyInUse": "Der Namespace ist bereits vergeben, bitte versuchen Sie es mit einem anderen", + "invalidNamespace": "Ungültiger Namensraum, bitte versuchen Sie einen anderen", + "namespaceLengthAtLeast2Characters": "Der Namensraum muss mindestens 2 Zeichen lang sein", + "onlyWorkspaceOwnerCanUpdateNamespace": "Nur der Arbeitsbereichsbesitzer kann den Namespace aktualisieren", + "onlyWorkspaceOwnerCanRemoveHomepage": "Nur der Arbeitsbereichsbesitzer kann die Homepage entfernen", + "setHomepageFailed": "Startseite konnte nicht eingerichtet werden", + "namespaceTooLong": "Der Namensraum ist zu lang. Bitte versuchen Sie es mit einem anderen.", + "namespaceTooShort": "Der Namensraum ist zu kurz, bitte versuchen Sie es mit einem anderen", + "namespaceIsReserved": "Der Namensraum ist reserviert, bitte versuchen Sie es mit einem anderen", + "updatePathNameFailed": "Pfadname konnte nicht aktualisiert werden", + "removeHomePageFailed": "Startseite konnte nicht entfernt werden", + "publishNameContainsInvalidCharacters": "Der Pfadname enthält ungültige Zeichen. Bitte versuchen Sie es mit einem anderen.", + "publishNameTooShort": "Der Pfadname ist zu kurz, bitte versuchen Sie es mit einem anderen", + "publishNameTooLong": "Der Pfadname ist zu lang, bitte versuchen Sie es mit einem anderen", + "publishNameAlreadyInUse": "Der Pfadname wird bereits verwendet. Bitte versuchen Sie einen anderen.", + "namespaceContainsInvalidCharacters": "Der Namespace enthält ungültige Zeichen. Bitte versuchen Sie es mit einem anderen.", + "publishPermissionDenied": "Nur der Arbeitsbereichsbesitzer oder Seitenherausgeber kann die Veröffentlichungseinstellungen verwalten", + "publishNameCannotBeEmpty": "Der Pfadname darf nicht leer sein. Bitte versuchen Sie es mit einem anderen." + }, + "success": { + "namespaceUpdated": "Namensraum erfolgreich aktualisiert", + "setHomepageSuccess": "Startseite erfolgreich eingerichtet", + "updatePathNameSuccess": "Pfadname erfolgreich aktualisiert", + "removeHomePageSuccess": "Startseite erfolgreich entfernt" + } + }, "accountPage": { "menuLabel": "Mein Konto", "title": "Mein Konto", @@ -905,9 +976,11 @@ "itemFive": "Speicher", "itemSix": "Zusammenarbeit in Echtzeit", "itemFileUpload": "Datei-Uploads", + "customNamespace": "Benutzerdefinierter Namensraum", "tooltipSix": "Lebenslang bedeutet, dass die Anzahl der Antworten nie zurückgesetzt wird", "intelligentSearch": "Intelligente Suche", "tooltipSeven": "Ermöglicht dir, einen Teil der URL für deinen Arbeitsbereich anzupassen", + "customNamespaceTooltip": "Benutzerdefinierte veröffentlichte Seiten-URL", "itemSeven": "Mobile App", "tooltipThree": "Gäste haben nur Leserechte für die speziell freigegebenen Inhalte", "tooltipFour": "Gäste werden als ein Sitzplatz abgerechnet", @@ -1017,6 +1090,7 @@ "selfHostContent": "Dokument", "selfHostEnd": "um einen einen eigenen Server aufzusetzen", "pleaseInputValidURL": "Bitte geben Sie eine gültige URL ein", + "changeUrl": "Ändern Sie die selbst gehostete URL in {}", "cloudURLHint": "Eingabe der Basis- URL Ihres Servers", "cloudWSURL": "Websocket URL", "cloudWSURLHint": "Eingbe der Websocket Adresse Ihres Servers", @@ -1329,7 +1403,9 @@ }, "filter": { "empty": "Keine aktiven Filter", - "addFilter": "Filter hinzufügen" + "addFilter": "Filter hinzufügen", + "cannotFindCreatableField": "Es wurde kein geeignetes Feld zum Filtern gefunden.", + "where": "Wo" }, "textFilter": { "contains": "Enthält", @@ -1376,9 +1452,12 @@ "between": "Ist zwischen", "empty": "Ist leer", "notEmpty": "Ist nicht leer", + "startDate": "Startdatum", + "endDate": "Enddatum", "choicechipPrefix": { "before": "Vorher", "after": "Danach", + "between": "Zwischen", "onOrBefore": "Am oder davor", "onOrAfter": "Während oder danach", "isEmpty": "leer", @@ -1404,6 +1483,7 @@ "delete": "Löschen", "wrapCellContent": "Zeilenumbruch", "clear": " Zelleninhalte löschen", + "switchPrimaryFieldTooltip": "Feldtyp des Primärfelds kann nicht geändert werden", "textFieldName": "Text", "checkboxFieldName": "Kontrollkästchen", "dateFieldName": "Datum", @@ -1417,6 +1497,7 @@ "relationFieldName": "Beziehung", "summaryFieldName": "KI-Zusammenfassung", "timeFieldName": "Zeit", + "mediaFieldName": "Dateien und Medien", "translateFieldName": "AI-Übersetzen", "translateTo": "Übersetzen in", "numberFormat": "Zahlenformat", @@ -1447,6 +1528,7 @@ "addOption": "Option hinzufügen", "editProperty": "Eigenschaft bearbeiten", "newProperty": "Neue Eigenschaft", + "openRowDocument": "Als Seite öffnen", "deleteFieldPromptMessage": "Sicher? Diese Eigenschaft wird gelöscht", "clearFieldPromptMessage": "Bist du dir sicher? Alle Zelleninhalte in dieser Spalte werden gelöscht!", "newColumn": "Neue Spalte", @@ -1482,6 +1564,7 @@ "fieldInUse": "Du sortierst bereits nach diesem Feld" }, "row": { + "label": "Reihe", "duplicate": "Duplikat", "delete": "Löschen", "titlePlaceholder": "Unbenannt", @@ -1497,7 +1580,10 @@ "dragAndClick": "Ziehen, um zu verschieben. Klicke, um das Menü zu öffnen", "insertRecordAbove": "Füge Datensatz oben ein", "insertRecordBelow": "Füge Datensatz unten ein", - "noContent": "Kein Inhalt" + "noContent": "Kein Inhalt", + "reorderRowDescription": "Zeile neu anordnen", + "createRowAboveDescription": "Erstelle oben eine Zeile", + "createRowBelowDescription": "Unten eine Zeile einfügen" }, "selectOption": { "create": "Erstellen", @@ -1558,6 +1644,23 @@ "countEmptyShort": "leer", "countNonEmpty": "Zahl nicht leer", "countNonEmptyShort": "nicht leer" + }, + "media": { + "rename": "Umbenennen", + "download": "Herunterladen", + "expand": "Erweitern", + "delete": "Löschen", + "moreFilesHint": "+{}", + "addFileOrImage": "Datei oder Link hinzufügen", + "attachmentsHint": "{}", + "addFileMobile": "Datei hinzufügen", + "extraCount": "+{}", + "deleteFileDescription": "Möchten Sie diese Datei wirklich löschen? Diese Aktion kann nicht rückgängig gemacht werden.", + "showFileNames": "Dateinamen anzeigen", + "downloadSuccess": "Datei heruntergeladen", + "downloadFailedToken": "Datei konnte nicht heruntergeladen werden, Benutzertoken nicht verfügbar", + "openInBrowser": "Im Browser öffnen", + "embedLink": "Dateilink einbetten" } }, "document": { @@ -1581,6 +1684,29 @@ }, "document": { "selectADocumentToLinkTo": "Eine Datentabelle zum Verknüpfen auswählen" + }, + "name": { + "text": "Text", + "heading1": "Überschrift 1", + "heading2": "Überschrift 2", + "heading3": "Überschrift 3", + "image": "Bild", + "bulletedList": "Aufzählungsliste", + "numberedList": "Nummerierte Liste", + "todoList": "Aufgabenliste", + "doc": "Dokument", + "linkedDoc": "Link zur Seite", + "grid": "Raster", + "linkedGrid": "Verknüpftes Raster", + "kanban": "Kanban", + "linkedKanban": "Verknüpftes Kanban", + "calendar": "Kalender", + "linkedCalendar": "Verknüpfter Kalender", + "quote": "Zitat", + "divider": "Trenner", + "outline": "Gliederung", + "mathEquation": "Mathematische Gleichung", + "code": "Code" } }, "selectionMenu": { From 8b672a159fc85088d096c07e82490dbd8215273f Mon Sep 17 00:00:00 2001 From: Lucas Date: Wed, 11 Dec 2024 16:37:07 +0800 Subject: [PATCH 029/576] feat: support multiple blocks operation (#6958) * feat: support multiple blocks operation * test: support multiple blocks operation --- .../document_with_image_block_test.dart | 7 -- .../actions/block_action_option_cubit.dart | 62 +++++++++++--- .../draggable_option_button.dart | 1 + .../actions/option/color_option_action.dart | 23 +++++- .../block_action_option_cubit_test.dart | 81 +++++++++++++++++++ 5 files changed, 152 insertions(+), 22 deletions(-) create mode 100644 frontend/appflowy_flutter/test/unit_test/document/option_menu/block_action_option_cubit_test.dart diff --git a/frontend/appflowy_flutter/integration_test/desktop/document/document_with_image_block_test.dart b/frontend/appflowy_flutter/integration_test/desktop/document/document_with_image_block_test.dart index 382a9d9134..2ad12e55aa 100644 --- a/frontend/appflowy_flutter/integration_test/desktop/document/document_with_image_block_test.dart +++ b/frontend/appflowy_flutter/integration_test/desktop/document/document_with_image_block_test.dart @@ -138,13 +138,6 @@ void main() { ); }); - testWidgets('insert a jpg image from network', (tester) async { - await testEmbedImage( - tester, - 'https://file-examples.com/storage/fe9566cb7d67345489a5a97/2017/10/file_example_JPG_100kB.jpg', - ); - }); - testWidgets('insert a bmp image from network', (tester) async { await testEmbedImage( tester, diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/block_action_option_cubit.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/block_action_option_cubit.dart index 7239876671..3f4f62ad34 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/block_action_option_cubit.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/block_action_option_cubit.dart @@ -28,12 +28,11 @@ class BlockActionOptionCubit extends Cubit { final transaction = editorState.transaction; switch (action) { case OptionAction.delete: - transaction.deleteNode(node); + _deleteBlocks(transaction, node); break; case OptionAction.duplicate: await _duplicateBlock(transaction, node); EditorNotification.paste().post(); - break; case OptionAction.moveUp: transaction.moveNode(node.path.previous, node); @@ -55,9 +54,40 @@ class BlockActionOptionCubit extends Cubit { await editorState.apply(transaction); } + /// If the selection is a block selection, delete the selected blocks. + /// Otherwise, delete the selected block. + void _deleteBlocks(Transaction transaction, Node selectedNode) { + final selection = editorState.selection; + final selectionType = editorState.selectionType; + if (selectionType == SelectionType.block && selection != null) { + final nodes = editorState.getNodesInSelection(selection.normalized); + transaction.deleteNodes(nodes); + } else { + transaction.deleteNode(selectedNode); + } + } + Future _duplicateBlock(Transaction transaction, Node node) async { + final selection = editorState.selection; + final selectionType = editorState.selectionType; + if (selectionType == SelectionType.block && selection != null) { + final nodes = editorState.getNodesInSelection(selection.normalized); + for (final node in nodes) { + _validateNode(node); + } + transaction.insertNodes( + selection.normalized.end.path.next, + nodes.map((e) => _copyBlock(e)).toList(), + ); + } else { + _validateNode(node); + transaction.insertNode(node.path.next, _copyBlock(node)); + } + } + + void _validateNode(Node node) { final type = node.type; - final builder = editorState.renderer.blockComponentBuilder(type); + final builder = blockComponentBuilder[type]; if (builder == null) { Log.error('Block type $type is not supported'); @@ -68,15 +98,13 @@ class BlockActionOptionCubit extends Cubit { if (!valid) { Log.error('Block type $type is not valid'); } - - transaction.insertNode(node.path.next, _copyBlock(node)); } Node _copyBlock(Node node) { Node copiedNode = node.copyWith(); final type = node.type; - final builder = editorState.renderer.blockComponentBuilder(type); + final builder = blockComponentBuilder[type]; if (builder == null) { Log.error('Block type $type is not supported'); @@ -182,6 +210,14 @@ class BlockActionOptionCubit extends Cubit { } Future _copyLinkToBlock(Node node) async { + List nodes = [node]; + + final selection = editorState.selection; + final selectionType = editorState.selectionType; + if (selectionType == SelectionType.block && selection != null) { + nodes = editorState.getNodesInSelection(selection.normalized); + } + final context = editorState.document.root.context; final viewId = context?.read().documentId; if (viewId == null) { @@ -200,13 +236,17 @@ class BlockActionOptionCubit extends Cubit { return; } - final link = ShareConstants.buildShareUrl( - workspaceId: workspaceId, - viewId: viewId, - blockId: node.id, + final blockIds = nodes.map((e) => e.id); + final links = blockIds.map( + (e) => ShareConstants.buildShareUrl( + workspaceId: workspaceId, + viewId: viewId, + blockId: e, + ), ); + await getIt().setData( - ClipboardServiceData(plainText: link), + ClipboardServiceData(plainText: links.join('\n')), ); emit(BlockActionOptionState()); // Emit a new state to trigger UI update diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/drag_to_reorder/draggable_option_button.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/drag_to_reorder/draggable_option_button.dart index 21c1998a3b..e5a8d9228e 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/drag_to_reorder/draggable_option_button.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/drag_to_reorder/draggable_option_button.dart @@ -25,6 +25,7 @@ class DraggableOptionButton extends StatefulWidget { final EditorState editorState; final BlockComponentContext blockComponentContext; final Map blockComponentBuilder; + @override State createState() => _DraggableOptionButtonState(); } diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/option/color_option_action.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/option/color_option_action.dart index 72f806ec68..cb1ec36b56 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/option/color_option_action.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/option/color_option_action.dart @@ -147,10 +147,25 @@ class _ColorOptionButtonState extends State { color: AFThemeExtension.of(context).onBackground, ), onTap: (option, index) async { - final transaction = widget.editorState.transaction; - transaction.updateNode(node, { - blockComponentBackgroundColor: option.id, - }); + final editorState = widget.editorState; + final transaction = editorState.transaction; + final selectionType = editorState.selectionType; + final selection = editorState.selection; + + // In multiple selection, we need to update all the nodes in the selection + if (selectionType == SelectionType.block && selection != null) { + final nodes = editorState.getNodesInSelection(selection.normalized); + for (final node in nodes) { + transaction.updateNode(node, { + blockComponentBackgroundColor: option.id, + }); + } + } else { + transaction.updateNode(node, { + blockComponentBackgroundColor: option.id, + }); + } + await widget.editorState.apply(transaction); innerController.close(); diff --git a/frontend/appflowy_flutter/test/unit_test/document/option_menu/block_action_option_cubit_test.dart b/frontend/appflowy_flutter/test/unit_test/document/option_menu/block_action_option_cubit_test.dart new file mode 100644 index 0000000000..413d830d73 --- /dev/null +++ b/frontend/appflowy_flutter/test/unit_test/document/option_menu/block_action_option_cubit_test.dart @@ -0,0 +1,81 @@ +import 'package:appflowy/plugins/document/presentation/editor_plugins/actions/block_action_option_cubit.dart'; +import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart'; +import 'package:appflowy_backend/log.dart'; +import 'package:appflowy_editor/appflowy_editor.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('block action option cubit:', () { + setUpAll(() { + Log.shared.disableLog = true; + }); + + tearDownAll(() { + Log.shared.disableLog = false; + }); + + test('delete blocks', () async { + const text = 'paragraph'; + final document = Document.blank() + ..insert([ + 0, + ], [ + paragraphNode(text: text), + paragraphNode(text: text), + paragraphNode(text: text), + ]); + + final editorState = EditorState(document: document); + final cubit = BlockActionOptionCubit( + editorState: editorState, + blockComponentBuilder: {}, + ); + + editorState.selection = Selection( + start: Position(path: [0]), + end: Position(path: [2], offset: text.length), + ); + editorState.selectionType = SelectionType.block; + + await cubit.handleAction(OptionAction.delete, document.nodeAtPath([0])!); + + // all the nodes should be deleted + expect(document.root.children, isEmpty); + + editorState.dispose(); + }); + + test('duplicate blocks', () async { + const text = 'paragraph'; + final document = Document.blank() + ..insert([ + 0, + ], [ + paragraphNode(text: text), + paragraphNode(text: text), + paragraphNode(text: text), + ]); + + final editorState = EditorState(document: document); + final cubit = BlockActionOptionCubit( + editorState: editorState, + blockComponentBuilder: {}, + ); + + editorState.selection = Selection( + start: Position(path: [0]), + end: Position(path: [2], offset: text.length), + ); + editorState.selectionType = SelectionType.block; + + await cubit.handleAction( + OptionAction.duplicate, + document.nodeAtPath([0])!, + ); + + expect(document.root.children, hasLength(6)); + + editorState.dispose(); + }); + }); +} From 62d5d66d2018a4445025f4efd25f839a7f00fb1a Mon Sep 17 00:00:00 2001 From: Lucas Date: Wed, 11 Dec 2024 16:37:28 +0800 Subject: [PATCH 030/576] feat: set table to page width (#6956) * feat: set table to page width * feat: expand the table based on the widht percentage * test: set to page width * feat: distribute columns evenly * test: distribute columns evenly * fix: border width --- .../document_with_simple_table_test.dart | 75 ++++++ .../simple_table/_shared_widget.dart | 251 ------------------ .../simple_table/simple_table.dart | 4 +- .../simple_table_cell_block_component.dart | 2 - .../simple_table/simple_table_constants.dart | 4 + .../simple_table_more_action.dart | 110 +++++--- .../simple_table_node_extension.dart | 10 + .../simple_table_style_operation.dart | 85 ++++++ .../simple_table_row_block_component.dart | 3 +- .../simple_table_align_button.dart | 81 ++++++ .../simple_table_background_menu.dart | 104 ++++++++ .../simple_table_basic_button.dart | 50 ++++ .../simple_table_border_builder.dart | 28 +- .../simple_table_divider.dart | 28 ++ .../simple_table_reorder_button.dart | 1 - .../simple_table_widget.dart | 1 - .../simple_table_widgets/widgets.dart | 11 + .../flowy_icons/16x/table_clear_content.svg | 11 +- .../16x/table_distribute_columns_evenly.svg | 9 + .../flowy_icons/16x/table_insert_above.svg | 5 +- .../flowy_icons/16x/table_insert_below.svg | 5 +- .../flowy_icons/16x/table_insert_left.svg | 7 +- .../flowy_icons/16x/table_insert_right.svg | 7 +- .../16x/table_set_to_page_width.svg | 3 + frontend/resources/translations/en.json | 6 +- 25 files changed, 577 insertions(+), 324 deletions(-) delete mode 100644 frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/_shared_widget.dart create mode 100644 frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_align_button.dart create mode 100644 frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_background_menu.dart create mode 100644 frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_basic_button.dart create mode 100644 frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_divider.dart create mode 100644 frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/widgets.dart create mode 100644 frontend/resources/flowy_icons/16x/table_distribute_columns_evenly.svg create mode 100644 frontend/resources/flowy_icons/16x/table_set_to_page_width.svg diff --git a/frontend/appflowy_flutter/integration_test/desktop/document/document_with_simple_table_test.dart b/frontend/appflowy_flutter/integration_test/desktop/document/document_with_simple_table_test.dart index 3f1f53655c..df473cc057 100644 --- a/frontend/appflowy_flutter/integration_test/desktop/document/document_with_simple_table_test.dart +++ b/frontend/appflowy_flutter/integration_test/desktop/document/document_with_simple_table_test.dart @@ -332,6 +332,81 @@ void main() { expect(tableNode.columnLength, 2); }); }); + + testWidgets('set column width to page width (1)', (tester) async { + await tester.initializeAppFlowy(); + await tester.tapAnonymousSignInButton(); + await tester.createNewPageWithNameUnderParent( + name: 'simple_table_test', + ); + + await tester.editor.tapLineOfEditorAt(0); + await tester.insertTableInDocument(); + + final tableNode = tester.editor.getNodeAtPath([0]); + final beforeWidth = tableNode.width; + + // set the column width to page width + await tester.clickMoreActionItemInTableMenu( + type: SimpleTableMoreActionType.column, + index: 0, + action: SimpleTableMoreAction.setToPageWidth, + ); + await tester.pumpAndSettle(); + + final afterWidth = tableNode.width; + expect(afterWidth, greaterThan(beforeWidth)); + }); + + testWidgets('set column width to page width (2)', (tester) async { + await tester.initializeAppFlowy(); + await tester.tapAnonymousSignInButton(); + await tester.createNewPageWithNameUnderParent( + name: 'simple_table_test', + ); + + await tester.editor.tapLineOfEditorAt(0); + await tester.insertTableInDocument(); + + final tableNode = tester.editor.getNodeAtPath([0]); + final beforeWidth = tableNode.width; + + // set the column width to page width + await tester.clickMoreActionItemInTableMenu( + type: SimpleTableMoreActionType.row, + index: 0, + action: SimpleTableMoreAction.setToPageWidth, + ); + await tester.pumpAndSettle(); + + final afterWidth = tableNode.width; + expect(afterWidth, greaterThan(beforeWidth)); + }); + + testWidgets('distribute columns evenly (1)', (tester) async { + await tester.initializeAppFlowy(); + await tester.tapAnonymousSignInButton(); + await tester.createNewPageWithNameUnderParent( + name: 'simple_table_test', + ); + + await tester.editor.tapLineOfEditorAt(0); + await tester.insertTableInDocument(); + + final tableNode = tester.editor.getNodeAtPath([0]); + final beforeWidth = tableNode.width; + + // set the column width to page width + await tester.clickMoreActionItemInTableMenu( + type: SimpleTableMoreActionType.row, + index: 0, + action: SimpleTableMoreAction.distributeColumnsEvenly, + ); + await tester.pumpAndSettle(); + + final afterWidth = tableNode.width; + expect(afterWidth, equals(beforeWidth)); + }); } extension on WidgetTester { diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/_shared_widget.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/_shared_widget.dart deleted file mode 100644 index 2855ac6f43..0000000000 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/_shared_widget.dart +++ /dev/null @@ -1,251 +0,0 @@ -import 'package:appflowy/generated/flowy_svgs.g.dart'; -import 'package:appflowy/generated/locale_keys.g.dart'; -import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart'; -import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:easy_localization/easy_localization.dart'; -import 'package:flowy_infra/theme_extension.dart'; -import 'package:flowy_infra_ui/flowy_infra_ui.dart'; -import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; - -class SimpleTableRowDivider extends StatelessWidget { - const SimpleTableRowDivider({ - super.key, - }); - - @override - Widget build(BuildContext context) { - return VerticalDivider( - color: context.simpleTableBorderColor, - width: 1.0, - ); - } -} - -class SimpleTableColumnDivider extends StatelessWidget { - const SimpleTableColumnDivider({super.key}); - - @override - Widget build(BuildContext context) { - return Divider( - color: context.simpleTableBorderColor, - height: 1.0, - ); - } -} - -class SimpleTableAlignMenu extends StatefulWidget { - const SimpleTableAlignMenu({ - super.key, - required this.type, - required this.tableCellNode, - this.mutex, - }); - - final SimpleTableMoreActionType type; - final Node tableCellNode; - final PopoverMutex? mutex; - - @override - State createState() => _SimpleTableAlignMenuState(); -} - -class _SimpleTableAlignMenuState extends State { - @override - Widget build(BuildContext context) { - final align = switch (widget.type) { - SimpleTableMoreActionType.column => widget.tableCellNode.columnAlign, - SimpleTableMoreActionType.row => widget.tableCellNode.rowAlign, - }; - return AppFlowyPopover( - mutex: widget.mutex, - child: SimpleTableBasicButton( - leftIconSvg: align.leftIconSvg, - text: LocaleKeys.document_plugins_simpleTable_moreActions_align.tr(), - onTap: () {}, - ), - popupBuilder: (popoverContext) { - void onClose() => PopoverContainer.of(popoverContext).closeAll(); - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - _buildAlignButton(context, TableAlign.left, onClose), - _buildAlignButton(context, TableAlign.center, onClose), - _buildAlignButton(context, TableAlign.right, onClose), - ], - ); - }, - ); - } - - Widget _buildAlignButton( - BuildContext context, - TableAlign align, - VoidCallback onClose, - ) { - return SimpleTableBasicButton( - leftIconSvg: align.leftIconSvg, - text: align.name, - onTap: () { - switch (widget.type) { - case SimpleTableMoreActionType.column: - context.read().updateColumnAlign( - tableCellNode: widget.tableCellNode, - align: align, - ); - break; - case SimpleTableMoreActionType.row: - context.read().updateRowAlign( - tableCellNode: widget.tableCellNode, - align: align, - ); - break; - } - - onClose(); - }, - ); - } -} - -class SimpleTableBasicButton extends StatelessWidget { - const SimpleTableBasicButton({ - super.key, - required this.text, - required this.onTap, - this.leftIconSvg, - this.leftIconBuilder, - this.rightIcon, - }); - - final FlowySvgData? leftIconSvg; - final String text; - final VoidCallback onTap; - final Widget Function(bool onHover)? leftIconBuilder; - final Widget? rightIcon; - - @override - Widget build(BuildContext context) { - return Container( - height: SimpleTableConstants.moreActionHeight, - padding: SimpleTableConstants.moreActionPadding, - child: FlowyIconTextButton( - margin: SimpleTableConstants.moreActionHorizontalMargin, - leftIconBuilder: _buildLeftIcon, - iconPadding: 10.0, - textBuilder: (onHover) => FlowyText.regular( - text, - fontSize: 14.0, - figmaLineHeight: 18.0, - ), - onTap: onTap, - rightIconBuilder: (onHover) => rightIcon ?? const SizedBox.shrink(), - ), - ); - } - - Widget _buildLeftIcon(bool onHover) { - if (leftIconBuilder != null) { - return leftIconBuilder!(onHover); - } - return leftIconSvg != null - ? FlowySvg(leftIconSvg!) - : const SizedBox.shrink(); - } -} - -class SimpleTableBackgroundColorMenu extends StatefulWidget { - const SimpleTableBackgroundColorMenu({ - super.key, - required this.type, - required this.tableCellNode, - this.mutex, - }); - - final SimpleTableMoreActionType type; - final Node tableCellNode; - final PopoverMutex? mutex; - - @override - State createState() => - _SimpleTableBackgroundColorMenuState(); -} - -class _SimpleTableBackgroundColorMenuState - extends State { - @override - Widget build(BuildContext context) { - final theme = AFThemeExtension.of(context); - final backgroundColor = switch (widget.type) { - SimpleTableMoreActionType.row => - widget.tableCellNode.buildRowColor(context), - SimpleTableMoreActionType.column => - widget.tableCellNode.buildColumnColor(context), - }; - return AppFlowyPopover( - mutex: widget.mutex, - popupBuilder: (popoverContext) { - return _buildColorOptionMenu( - context, - theme: theme, - onClose: () => PopoverContainer.of(popoverContext).closeAll(), - ); - }, - direction: PopoverDirection.rightWithCenterAligned, - child: SimpleTableBasicButton( - leftIconBuilder: (onHover) => ColorOptionIcon( - color: backgroundColor ?? Colors.transparent, - ), - text: LocaleKeys.document_plugins_simpleTable_moreActions_color.tr(), - onTap: () {}, - ), - ); - } - - Widget _buildColorOptionMenu( - BuildContext context, { - required AFThemeExtension theme, - required VoidCallback onClose, - }) { - final colors = [ - // reset to default background color - FlowyColorOption( - color: Colors.transparent, - i18n: LocaleKeys.document_plugins_optionAction_defaultColor.tr(), - id: optionActionColorDefaultColor, - ), - ...FlowyTint.values.map( - (e) => FlowyColorOption( - color: e.color(context, theme: theme), - i18n: e.tintName(AppFlowyEditorL10n.current), - id: e.id, - ), - ), - ]; - - return FlowyColorPicker( - colors: colors, - border: Border.all( - color: theme.onBackground, - ), - onTap: (option, index) { - switch (widget.type) { - case SimpleTableMoreActionType.column: - context.read().updateColumnBackgroundColor( - tableCellNode: widget.tableCellNode, - color: option.id, - ); - break; - case SimpleTableMoreActionType.row: - context.read().updateRowBackgroundColor( - tableCellNode: widget.tableCellNode, - color: option.id, - ); - break; - } - - onClose(); - }, - ); - } -} diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table.dart index 06abc4a283..4454e9efaf 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table.dart @@ -5,6 +5,4 @@ export 'simple_table_more_action.dart'; export 'simple_table_operations/simple_table_operations.dart'; export 'simple_table_row_block_component.dart'; export 'simple_table_shortcuts/simple_table_commands.dart'; -export 'simple_table_widgets/simple_table_add_column_and_row_button.dart'; -export 'simple_table_widgets/simple_table_add_column_button.dart'; -export 'simple_table_widgets/simple_table_add_row_button.dart'; +export 'simple_table_widgets/widgets.dart'; diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_cell_block_component.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_cell_block_component.dart index 70e618a186..b9954499c3 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_cell_block_component.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_cell_block_component.dart @@ -1,6 +1,4 @@ import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_table/simple_table.dart'; -import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_border_builder.dart'; -import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_column_resize_handle.dart'; import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_constants.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_constants.dart index 9c0952daa4..ca82866730 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_constants.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_constants.dart @@ -1,4 +1,5 @@ import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_table/simple_table.dart'; +import 'package:appflowy/plugins/document/presentation/editor_style.dart'; import 'package:appflowy/util/theme_extension.dart'; import 'package:appflowy_backend/log.dart'; import 'package:appflowy_editor/appflowy_editor.dart'; @@ -190,6 +191,9 @@ class SimpleTableConstants { right: tableRightPadding, ); + static double get tablePageOffset => + EditorStyleCustomizer.optionMenuWidth + 12; + // Add row button static const addRowButtonHeight = 16.0; static const addRowButtonPadding = 4.0; diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_more_action.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_more_action.dart index 380a62791e..4c682ca3b5 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_more_action.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_more_action.dart @@ -1,10 +1,6 @@ import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart'; -import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_table/_shared_widget.dart'; -import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_table/simple_table_block_component.dart'; -import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_table/simple_table_constants.dart'; -import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_operations.dart'; -import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_reorder_button.dart'; +import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_table/simple_table.dart'; import 'package:appflowy/workspace/presentation/widgets/toggle/toggle.dart'; import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:easy_localization/easy_localization.dart'; @@ -16,29 +12,45 @@ enum SimpleTableMoreActionType { column, row; - List get actions { + List buildActions({ + required int index, + required int columnLength, + required int rowLength, + }) { + // there're two special cases: + // 1. if the table only contains one row or one column, remove the delete action + // 2. if the index is 0, add the enable header action switch (this) { case SimpleTableMoreActionType.row: return [ SimpleTableMoreAction.insertAbove, SimpleTableMoreAction.insertBelow, + SimpleTableMoreAction.divider, + if (index == 0) SimpleTableMoreAction.enableHeaderRow, + SimpleTableMoreAction.backgroundColor, + SimpleTableMoreAction.align, + SimpleTableMoreAction.divider, + SimpleTableMoreAction.setToPageWidth, + SimpleTableMoreAction.distributeColumnsEvenly, + SimpleTableMoreAction.divider, SimpleTableMoreAction.duplicate, SimpleTableMoreAction.clearContents, - SimpleTableMoreAction.delete, - SimpleTableMoreAction.divider, - SimpleTableMoreAction.align, - SimpleTableMoreAction.backgroundColor, + if (rowLength > 1) SimpleTableMoreAction.delete, ]; case SimpleTableMoreActionType.column: return [ SimpleTableMoreAction.insertLeft, SimpleTableMoreAction.insertRight, + SimpleTableMoreAction.divider, + if (index == 0) SimpleTableMoreAction.enableHeaderColumn, + SimpleTableMoreAction.backgroundColor, + SimpleTableMoreAction.align, + SimpleTableMoreAction.divider, + SimpleTableMoreAction.setToPageWidth, + SimpleTableMoreAction.divider, SimpleTableMoreAction.duplicate, SimpleTableMoreAction.clearContents, - SimpleTableMoreAction.delete, - SimpleTableMoreAction.divider, - SimpleTableMoreAction.align, - SimpleTableMoreAction.backgroundColor, + if (columnLength > 1) SimpleTableMoreAction.delete, ]; } } @@ -73,6 +85,8 @@ enum SimpleTableMoreAction { backgroundColor, enableHeaderColumn, enableHeaderRow, + setToPageWidth, + distributeColumnsEvenly, divider; String get name { @@ -99,6 +113,11 @@ enum SimpleTableMoreAction { LocaleKeys.document_plugins_simpleTable_moreActions_delete.tr(), SimpleTableMoreAction.duplicate => LocaleKeys.document_plugins_simpleTable_moreActions_duplicate.tr(), + SimpleTableMoreAction.setToPageWidth => + LocaleKeys.document_plugins_simpleTable_moreActions_setToPageWidth.tr(), + SimpleTableMoreAction.distributeColumnsEvenly => LocaleKeys + .document_plugins_simpleTable_moreActions_distributeColumnsWidth + .tr(), SimpleTableMoreAction.divider => throw UnimplementedError(), }; } @@ -112,6 +131,10 @@ enum SimpleTableMoreAction { SimpleTableMoreAction.duplicate => FlowySvgs.duplicate_s, SimpleTableMoreAction.clearContents => FlowySvgs.table_clear_content_s, SimpleTableMoreAction.delete => FlowySvgs.trash_s, + SimpleTableMoreAction.setToPageWidth => + FlowySvgs.table_set_to_page_width_s, + SimpleTableMoreAction.distributeColumnsEvenly => + FlowySvgs.table_distribute_columns_evenly_s, SimpleTableMoreAction.enableHeaderColumn => FlowySvgs.table_header_column_s, SimpleTableMoreAction.enableHeaderRow => FlowySvgs.table_header_row_s, @@ -375,7 +398,12 @@ class _SimpleTableMoreActionListState extends State { Widget build(BuildContext context) { return Column( mainAxisSize: MainAxisSize.min, - children: _buildActions() + children: widget.type + .buildActions( + index: widget.index, + columnLength: widget.tableCellNode.columnLength, + rowLength: widget.tableCellNode.rowLength, + ) .map( (action) => SimpleTableMoreActionItem( type: widget.type, @@ -387,34 +415,6 @@ class _SimpleTableMoreActionListState extends State { .toList(), ); } - - List _buildActions() { - final actions = widget.type.actions; - - // if the index is 0, add the divider and enable header action - if (widget.index == 0) { - actions.addAll([ - SimpleTableMoreAction.divider, - if (widget.type == SimpleTableMoreActionType.column) - SimpleTableMoreAction.enableHeaderColumn, - if (widget.type == SimpleTableMoreActionType.row) - SimpleTableMoreAction.enableHeaderRow, - ]); - } - - // if the table only contains one row or one column, remove the delete action - if (widget.tableCellNode.rowLength == 1 && - widget.type == SimpleTableMoreActionType.row) { - actions.remove(SimpleTableMoreAction.delete); - } - - if (widget.tableCellNode.columnLength == 1 && - widget.type == SimpleTableMoreActionType.column) { - actions.remove(SimpleTableMoreAction.delete); - } - - return actions; - } } class SimpleTableMoreActionItem extends StatefulWidget { @@ -568,6 +568,10 @@ class _SimpleTableMoreActionItemState extends State { _duplicateRow(); break; } + case SimpleTableMoreAction.setToPageWidth: + _setToPageWidth(); + case SimpleTableMoreAction.distributeColumnsEvenly: + _distributeColumnsEvenly(); default: break; } @@ -575,6 +579,26 @@ class _SimpleTableMoreActionItemState extends State { PopoverContainer.of(context).close(); } + void _setToPageWidth() { + final value = _getTableAndTableCellAndCellPosition(); + if (value == null) { + return; + } + final (table, _, _) = value; + final editorState = context.read(); + editorState.setColumnWidthToPageWidth(tableNode: table); + } + + void _distributeColumnsEvenly() { + final value = _getTableAndTableCellAndCellPosition(); + if (value == null) { + return; + } + final (table, _, _) = value; + final editorState = context.read(); + editorState.distributeColumnWidthToPageWidth(tableNode: table); + } + void _duplicateRow() { final value = _getTableAndTableCellAndCellPosition(); if (value == null) { diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_node_extension.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_node_extension.dart index ab95bdcf54..cc7686fa72 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_node_extension.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_node_extension.dart @@ -366,6 +366,16 @@ extension TableNodeExtension on Node { } } + double get width { + double currentColumnWidth = 0; + for (var i = 0; i < columnLength; i++) { + final columnWidth = + columnWidths[i.toString()] ?? SimpleTableConstants.defaultColumnWidth; + currentColumnWidth += columnWidth; + } + return currentColumnWidth; + } + /// Get the previous cell in the same column. If the row index is 0, it will return the same cell. Node? getPreviousCellInSameColumn() { assert(type == SimpleTableCellBlockKeys.type); diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_style_operation.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_style_operation.dart index edcd30e608..0277ea3fb6 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_style_operation.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_style_operation.dart @@ -233,4 +233,89 @@ extension TableOptionOperation on EditorState { transaction.updateNode(parentTableNode, attributes); await apply(transaction); } + + /// Set the column width of the table to the page width. + /// + /// Example: + /// + /// Before: + /// | 0 | 1 | + /// | 3 | 4 | + /// + /// After: + /// | 0 | 1 | <- the column's width will be expanded based on the percentage of the page width + /// | 3 | 4 | + /// + /// This function will update the table width. + Future setColumnWidthToPageWidth({ + required Node tableNode, + }) async { + // Disable in mobile + if (UniversalPlatform.isMobile) { + return; + } + + final columnLength = tableNode.columnLength; + double? pageWidth = tableNode.renderBox?.size.width; + if (pageWidth == null) { + Log.warn('table node render box is null'); + return; + } + pageWidth -= SimpleTableConstants.tablePageOffset; + + final transaction = this.transaction; + final columnWidths = tableNode.columnWidths; + final ratio = pageWidth / tableNode.width; + for (var i = 0; i < columnLength; i++) { + final columnWidth = + columnWidths[i.toString()] ?? SimpleTableConstants.defaultColumnWidth; + columnWidths[i.toString()] = (columnWidth * ratio).clamp( + SimpleTableConstants.minimumColumnWidth, + double.infinity, + ); + } + transaction.updateNode(tableNode, { + SimpleTableBlockKeys.columnWidths: columnWidths, + }); + await apply(transaction); + } + + /// Distribute the column width of the table to the page width. + /// + /// Example: + /// + /// Before: + /// Before: + /// | 0 | 1 | + /// | 3 | 4 | + /// + /// After: + /// | 0 | 1 | <- the column's width will be expanded based on the percentage of the page width + /// | 3 | 4 | + /// + /// This function will not update table width. + Future distributeColumnWidthToPageWidth({ + required Node tableNode, + }) async { + // Disable in mobile + if (UniversalPlatform.isMobile) { + return; + } + + final columnLength = tableNode.columnLength; + final tableWidth = tableNode.width; + final columnWidth = (tableWidth / columnLength).clamp( + SimpleTableConstants.minimumColumnWidth, + double.infinity, + ); + final transaction = this.transaction; + final columnWidths = tableNode.columnWidths; + for (var i = 0; i < columnLength; i++) { + columnWidths[i.toString()] = columnWidth; + } + transaction.updateNode(tableNode, { + SimpleTableBlockKeys.columnWidths: columnWidths, + }); + await apply(transaction); + } } diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_row_block_component.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_row_block_component.dart index 3684beb9cd..9b7d6a5d16 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_row_block_component.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_row_block_component.dart @@ -1,5 +1,4 @@ -import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_table/_shared_widget.dart'; -import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_table/simple_table_constants.dart'; +import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_table/simple_table.dart'; import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_align_button.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_align_button.dart new file mode 100644 index 0000000000..a042e632ea --- /dev/null +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_align_button.dart @@ -0,0 +1,81 @@ +import 'package:appflowy/generated/locale_keys.g.dart'; +import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.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/material.dart'; +import 'package:provider/provider.dart'; + +class SimpleTableAlignMenu extends StatefulWidget { + const SimpleTableAlignMenu({ + super.key, + required this.type, + required this.tableCellNode, + this.mutex, + }); + + final SimpleTableMoreActionType type; + final Node tableCellNode; + final PopoverMutex? mutex; + + @override + State createState() => _SimpleTableAlignMenuState(); +} + +class _SimpleTableAlignMenuState extends State { + @override + Widget build(BuildContext context) { + final align = switch (widget.type) { + SimpleTableMoreActionType.column => widget.tableCellNode.columnAlign, + SimpleTableMoreActionType.row => widget.tableCellNode.rowAlign, + }; + return AppFlowyPopover( + mutex: widget.mutex, + child: SimpleTableBasicButton( + leftIconSvg: align.leftIconSvg, + text: LocaleKeys.document_plugins_simpleTable_moreActions_align.tr(), + onTap: () {}, + ), + popupBuilder: (popoverContext) { + void onClose() => PopoverContainer.of(popoverContext).closeAll(); + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + _buildAlignButton(context, TableAlign.left, onClose), + _buildAlignButton(context, TableAlign.center, onClose), + _buildAlignButton(context, TableAlign.right, onClose), + ], + ); + }, + ); + } + + Widget _buildAlignButton( + BuildContext context, + TableAlign align, + VoidCallback onClose, + ) { + return SimpleTableBasicButton( + leftIconSvg: align.leftIconSvg, + text: align.name, + onTap: () { + switch (widget.type) { + case SimpleTableMoreActionType.column: + context.read().updateColumnAlign( + tableCellNode: widget.tableCellNode, + align: align, + ); + break; + case SimpleTableMoreActionType.row: + context.read().updateRowAlign( + tableCellNode: widget.tableCellNode, + align: align, + ); + break; + } + + onClose(); + }, + ); + } +} diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_background_menu.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_background_menu.dart new file mode 100644 index 0000000000..ef55081a14 --- /dev/null +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_background_menu.dart @@ -0,0 +1,104 @@ +import 'package:appflowy/generated/locale_keys.g.dart'; +import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart'; +import 'package:appflowy_editor/appflowy_editor.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flowy_infra/theme_extension.dart'; +import 'package:flowy_infra_ui/flowy_infra_ui.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +class SimpleTableBackgroundColorMenu extends StatefulWidget { + const SimpleTableBackgroundColorMenu({ + super.key, + required this.type, + required this.tableCellNode, + this.mutex, + }); + + final SimpleTableMoreActionType type; + final Node tableCellNode; + final PopoverMutex? mutex; + + @override + State createState() => + _SimpleTableBackgroundColorMenuState(); +} + +class _SimpleTableBackgroundColorMenuState + extends State { + @override + Widget build(BuildContext context) { + final theme = AFThemeExtension.of(context); + final backgroundColor = switch (widget.type) { + SimpleTableMoreActionType.row => + widget.tableCellNode.buildRowColor(context), + SimpleTableMoreActionType.column => + widget.tableCellNode.buildColumnColor(context), + }; + return AppFlowyPopover( + mutex: widget.mutex, + popupBuilder: (popoverContext) { + return _buildColorOptionMenu( + context, + theme: theme, + onClose: () => PopoverContainer.of(popoverContext).closeAll(), + ); + }, + direction: PopoverDirection.rightWithCenterAligned, + child: SimpleTableBasicButton( + leftIconBuilder: (onHover) => ColorOptionIcon( + color: backgroundColor ?? Colors.transparent, + ), + text: LocaleKeys.document_plugins_simpleTable_moreActions_color.tr(), + onTap: () {}, + ), + ); + } + + Widget _buildColorOptionMenu( + BuildContext context, { + required AFThemeExtension theme, + required VoidCallback onClose, + }) { + final colors = [ + // reset to default background color + FlowyColorOption( + color: Colors.transparent, + i18n: LocaleKeys.document_plugins_optionAction_defaultColor.tr(), + id: optionActionColorDefaultColor, + ), + ...FlowyTint.values.map( + (e) => FlowyColorOption( + color: e.color(context, theme: theme), + i18n: e.tintName(AppFlowyEditorL10n.current), + id: e.id, + ), + ), + ]; + + return FlowyColorPicker( + colors: colors, + border: Border.all( + color: theme.onBackground, + ), + onTap: (option, index) { + switch (widget.type) { + case SimpleTableMoreActionType.column: + context.read().updateColumnBackgroundColor( + tableCellNode: widget.tableCellNode, + color: option.id, + ); + break; + case SimpleTableMoreActionType.row: + context.read().updateRowBackgroundColor( + tableCellNode: widget.tableCellNode, + color: option.id, + ); + break; + } + + onClose(); + }, + ); + } +} diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_basic_button.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_basic_button.dart new file mode 100644 index 0000000000..f9df88ccf0 --- /dev/null +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_basic_button.dart @@ -0,0 +1,50 @@ +import 'package:appflowy/generated/flowy_svgs.g.dart'; +import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart'; +import 'package:flowy_infra_ui/flowy_infra_ui.dart'; +import 'package:flutter/material.dart'; + +class SimpleTableBasicButton extends StatelessWidget { + const SimpleTableBasicButton({ + super.key, + required this.text, + required this.onTap, + this.leftIconSvg, + this.leftIconBuilder, + this.rightIcon, + }); + + final FlowySvgData? leftIconSvg; + final String text; + final VoidCallback onTap; + final Widget Function(bool onHover)? leftIconBuilder; + final Widget? rightIcon; + + @override + Widget build(BuildContext context) { + return Container( + height: SimpleTableConstants.moreActionHeight, + padding: SimpleTableConstants.moreActionPadding, + child: FlowyIconTextButton( + margin: SimpleTableConstants.moreActionHorizontalMargin, + leftIconBuilder: _buildLeftIcon, + iconPadding: 10.0, + textBuilder: (onHover) => FlowyText.regular( + text, + fontSize: 14.0, + figmaLineHeight: 18.0, + ), + onTap: onTap, + rightIconBuilder: (onHover) => rightIcon ?? const SizedBox.shrink(), + ), + ); + } + + Widget _buildLeftIcon(bool onHover) { + if (leftIconBuilder != null) { + return leftIconBuilder!(onHover); + } + return leftIconSvg != null + ? FlowySvg(leftIconSvg!) + : const SizedBox.shrink(); + } +} diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_border_builder.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_border_builder.dart index b3f7a4629c..f44c394694 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_border_builder.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_border_builder.dart @@ -185,12 +185,15 @@ class SimpleTableBorderBuilder { simpleTableContext.isReorderingColumn.value.$2 < node.columnIndex; return Border( - top: _buildDefaultBorderSide(), - bottom: _buildDefaultBorderSide(), - left: - isLeftSide ? _buildHighlightBorderSide() : _buildDefaultBorderSide(), + top: node.rowIndex == 0 + ? _buildDefaultBorderSide() + : _buildLightBorderSide(), + bottom: node.rowIndex + 1 == node.parentTableNode?.rowLength + ? _buildDefaultBorderSide() + : _buildLightBorderSide(), + left: isLeftSide ? _buildHighlightBorderSide() : _buildLightBorderSide(), right: - isRightSide ? _buildHighlightBorderSide() : _buildDefaultBorderSide(), + isRightSide ? _buildHighlightBorderSide() : _buildLightBorderSide(), ); } @@ -216,12 +219,15 @@ class SimpleTableBorderBuilder { simpleTableContext.isReorderingRow.value.$2 < node.rowIndex; return Border( - top: isTopSide ? _buildHighlightBorderSide() : _buildDefaultBorderSide(), - bottom: isBottomSide - ? _buildHighlightBorderSide() - : _buildDefaultBorderSide(), - left: _buildDefaultBorderSide(), - right: _buildDefaultBorderSide(), + top: isTopSide ? _buildHighlightBorderSide() : _buildLightBorderSide(), + bottom: + isBottomSide ? _buildHighlightBorderSide() : _buildLightBorderSide(), + left: node.columnIndex == 0 + ? _buildDefaultBorderSide() + : _buildLightBorderSide(), + right: node.columnIndex + 1 == node.parentTableNode?.columnLength + ? _buildDefaultBorderSide() + : _buildLightBorderSide(), ); } diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_divider.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_divider.dart new file mode 100644 index 0000000000..0de08d6e75 --- /dev/null +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_divider.dart @@ -0,0 +1,28 @@ +import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart'; +import 'package:flutter/material.dart'; + +class SimpleTableRowDivider extends StatelessWidget { + const SimpleTableRowDivider({ + super.key, + }); + + @override + Widget build(BuildContext context) { + return VerticalDivider( + color: context.simpleTableBorderColor, + width: 1.0, + ); + } +} + +class SimpleTableColumnDivider extends StatelessWidget { + const SimpleTableColumnDivider({super.key}); + + @override + Widget build(BuildContext context) { + return Divider( + color: context.simpleTableBorderColor, + height: 1.0, + ); + } +} diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_reorder_button.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_reorder_button.dart index 5a51103822..740c822ff5 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_reorder_button.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_reorder_button.dart @@ -1,6 +1,5 @@ import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart'; -import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_widget.dart'; import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_widget.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_widget.dart index 0767ec4a47..563570bf27 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_widget.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_widget.dart @@ -1,4 +1,3 @@ -import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_table/_shared_widget.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_table/simple_table.dart'; import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:flutter/material.dart'; diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/widgets.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/widgets.dart new file mode 100644 index 0000000000..8b9607e2ba --- /dev/null +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/widgets.dart @@ -0,0 +1,11 @@ +export 'simple_table_add_column_and_row_button.dart'; +export 'simple_table_add_column_button.dart'; +export 'simple_table_add_row_button.dart'; +export 'simple_table_align_button.dart'; +export 'simple_table_background_menu.dart'; +export 'simple_table_basic_button.dart'; +export 'simple_table_border_builder.dart'; +export 'simple_table_column_resize_handle.dart'; +export 'simple_table_divider.dart'; +export 'simple_table_reorder_button.dart'; +export 'simple_table_widget.dart'; diff --git a/frontend/resources/flowy_icons/16x/table_clear_content.svg b/frontend/resources/flowy_icons/16x/table_clear_content.svg index 8afeb2dd65..de1d42cabe 100644 --- a/frontend/resources/flowy_icons/16x/table_clear_content.svg +++ b/frontend/resources/flowy_icons/16x/table_clear_content.svg @@ -1,4 +1,11 @@ - - + + + + + + + + + diff --git a/frontend/resources/flowy_icons/16x/table_distribute_columns_evenly.svg b/frontend/resources/flowy_icons/16x/table_distribute_columns_evenly.svg new file mode 100644 index 0000000000..fe7acf8c98 --- /dev/null +++ b/frontend/resources/flowy_icons/16x/table_distribute_columns_evenly.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/frontend/resources/flowy_icons/16x/table_insert_above.svg b/frontend/resources/flowy_icons/16x/table_insert_above.svg index 16ffb6a33c..e3bcfe4ca6 100644 --- a/frontend/resources/flowy_icons/16x/table_insert_above.svg +++ b/frontend/resources/flowy_icons/16x/table_insert_above.svg @@ -1,3 +1,6 @@ - + + + + diff --git a/frontend/resources/flowy_icons/16x/table_insert_below.svg b/frontend/resources/flowy_icons/16x/table_insert_below.svg index acfa64a346..de1719edeb 100644 --- a/frontend/resources/flowy_icons/16x/table_insert_below.svg +++ b/frontend/resources/flowy_icons/16x/table_insert_below.svg @@ -1,3 +1,6 @@ - + + + + diff --git a/frontend/resources/flowy_icons/16x/table_insert_left.svg b/frontend/resources/flowy_icons/16x/table_insert_left.svg index 3da61207b4..47bbb0820c 100644 --- a/frontend/resources/flowy_icons/16x/table_insert_left.svg +++ b/frontend/resources/flowy_icons/16x/table_insert_left.svg @@ -1,3 +1,6 @@ - - + + + + + diff --git a/frontend/resources/flowy_icons/16x/table_insert_right.svg b/frontend/resources/flowy_icons/16x/table_insert_right.svg index 2852bd389d..f754f516d0 100644 --- a/frontend/resources/flowy_icons/16x/table_insert_right.svg +++ b/frontend/resources/flowy_icons/16x/table_insert_right.svg @@ -1,3 +1,6 @@ - - + + + + + diff --git a/frontend/resources/flowy_icons/16x/table_set_to_page_width.svg b/frontend/resources/flowy_icons/16x/table_set_to_page_width.svg new file mode 100644 index 0000000000..0787942325 --- /dev/null +++ b/frontend/resources/flowy_icons/16x/table_set_to_page_width.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/resources/translations/en.json b/frontend/resources/translations/en.json index 828114bf13..4a27f0f783 100644 --- a/frontend/resources/translations/en.json +++ b/frontend/resources/translations/en.json @@ -1725,7 +1725,9 @@ "insertBelow": "Insert below", "headerColumn": "Header column", "headerRow": "Header row", - "clearContents": "Clear contents" + "clearContents": "Clear contents", + "setToPageWidth": "Set to page width", + "distributeColumnsWidth": "Distribute columns evenly" }, "clickToAddNewRow": "Click to add a new row", "clickToAddNewColumn": "Click to add a new column", @@ -2906,4 +2908,4 @@ "permissionDenied": "No permission to open this file", "unknownError": "File open failed" } -} \ No newline at end of file +} From e8f2940024e6944d1d1e1d0fcb643d2f457b51cd Mon Sep 17 00:00:00 2001 From: Richard Shiue <71320345+richardshiue@users.noreply.github.com> Date: Wed, 11 Dec 2024 21:48:38 +0800 Subject: [PATCH 031/576] fix(flutter): some ai chat bugs (#6969) * chore: add hover effect and fix radius * chore: open ref page on mobile --- .../lib/plugins/ai_chat/chat_page.dart | 36 ++++++++++++------- .../chat_input/chat_mention_page_menu.dart | 29 +++++++-------- .../message/ai_message_bubble.dart | 4 +-- .../presentation/message/ai_text_message.dart | 6 +++- frontend/appflowy_flutter/pubspec.lock | 4 +-- 5 files changed, 45 insertions(+), 34 deletions(-) diff --git a/frontend/appflowy_flutter/lib/plugins/ai_chat/chat_page.dart b/frontend/appflowy_flutter/lib/plugins/ai_chat/chat_page.dart index 9c6ccc688d..11f772c60a 100644 --- a/frontend/appflowy_flutter/lib/plugins/ai_chat/chat_page.dart +++ b/frontend/appflowy_flutter/lib/plugins/ai_chat/chat_page.dart @@ -1,6 +1,8 @@ +import 'dart:async'; import 'dart:io'; import 'package:appflowy/generated/locale_keys.g.dart'; +import 'package:appflowy/mobile/application/mobile_router.dart'; import 'package:appflowy/plugins/ai_chat/application/chat_bloc.dart'; import 'package:appflowy/plugins/ai_chat/application/chat_entity.dart'; import 'package:appflowy/plugins/ai_chat/application/ai_prompt_input_bloc.dart'; @@ -219,7 +221,8 @@ class _ChatContentPage extends StatelessWidget { chatId: view.id, refSourceJsonString: refSourceJsonString, isLastMessage: isLastMessage, - onSelectedMetadata: _onSelectMetadata, + onSelectedMetadata: (metadata) => + _onSelectMetadata(context, metadata), ); }, ); @@ -320,7 +323,10 @@ class _ChatContentPage extends StatelessWidget { ); } - void _onSelectMetadata(ChatMessageRefSource metadata) async { + void _onSelectMetadata( + BuildContext context, + ChatMessageRefSource metadata, + ) async { if (isURL(metadata.name)) { late Uri uri; try { @@ -335,16 +341,22 @@ class _ChatContentPage extends StatelessWidget { Log.error("failed to open url $err"); } } else { - await ViewBackendService.getView(metadata.id).fold( - (sidebarView) { - getIt().add( - TabsEvent.openSecondaryPlugin( - plugin: sidebarView.plugin(), - ), - ); - }, - (err) => Log.error("Failed to get view: $err"), - ); + final sidebarView = + await ViewBackendService.getView(metadata.id).toNullable(); + if (sidebarView == null) { + return; + } + if (UniversalPlatform.isDesktop) { + getIt().add( + TabsEvent.openSecondaryPlugin( + plugin: sidebarView.plugin(), + ), + ); + } else { + if (context.mounted) { + unawaited(context.pushView(sidebarView)); + } + } } } } diff --git a/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_input/chat_mention_page_menu.dart b/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_input/chat_mention_page_menu.dart index 14db6e1a1c..10ce04f256 100644 --- a/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_input/chat_mention_page_menu.dart +++ b/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_input/chat_mention_page_menu.dart @@ -6,8 +6,8 @@ import 'package:appflowy/workspace/application/view_title/view_title_bar_bloc.da import 'package:appflowy/workspace/presentation/home/menu/sidebar/space/space_icon.dart'; import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart'; import 'package:easy_localization/easy_localization.dart' hide TextDirection; -import 'package:flowy_infra/theme_extension.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; +import 'package:flowy_infra_ui/style_widget/hover.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:scroll_to_index/scroll_to_index.dart'; @@ -273,21 +273,18 @@ class _ChatMentionPageItem extends StatelessWidget { child: GestureDetector( onTap: onTap, behavior: HitTestBehavior.opaque, - child: Container( - height: _itemHeight, - decoration: BoxDecoration( - color: isSelected - ? AFThemeExtension.of(context).lightGreyHover - : Colors.transparent, - borderRadius: BorderRadius.circular(4.0), - ), - padding: const EdgeInsets.all(4.0), - child: Row( - children: [ - MentionViewIcon(view: view), - const HSpace(8.0), - Expanded(child: MentionViewTitleAndAncestors(view: view)), - ], + child: FlowyHover( + isSelected: () => isSelected, + child: Container( + height: _itemHeight, + padding: const EdgeInsets.all(4.0), + child: Row( + children: [ + MentionViewIcon(view: view), + const HSpace(8.0), + Expanded(child: MentionViewTitleAndAncestors(view: view)), + ], + ), ), ), ), diff --git a/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/message/ai_message_bubble.dart b/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/message/ai_message_bubble.dart index 37c97015a8..9da09d8c1e 100644 --- a/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/message/ai_message_bubble.dart +++ b/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/message/ai_message_bubble.dart @@ -425,9 +425,7 @@ class ChatAIMessagePopup extends StatelessWidget { }, ); }, - child: IgnorePointer( - child: child, - ), + child: child, ); } } diff --git a/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/message/ai_text_message.dart b/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/message/ai_text_message.dart index 1b58869688..c3c831ac61 100644 --- a/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/message/ai_text_message.dart +++ b/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/message/ai_text_message.dart @@ -8,6 +8,7 @@ import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_chat_core/flutter_chat_core.dart'; +import 'package:universal_platform/universal_platform.dart'; import '../layout_define.dart'; import 'ai_markdown_text.dart'; @@ -82,7 +83,10 @@ class ChatAIMessageWidget extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - AIMarkdownText(markdown: state.text), + IgnorePointer( + ignoring: UniversalPlatform.isMobile, + child: AIMarkdownText(markdown: state.text), + ), if (state.sources.isNotEmpty) AIMessageMetadata( sources: state.sources, diff --git a/frontend/appflowy_flutter/pubspec.lock b/frontend/appflowy_flutter/pubspec.lock index 9e05d06614..6f27fb5484 100644 --- a/frontend/appflowy_flutter/pubspec.lock +++ b/frontend/appflowy_flutter/pubspec.lock @@ -318,10 +318,10 @@ packages: dependency: transitive description: name: charcode - sha256: fb98c0f6d12c920a02ee2d998da788bca066ca5f148492b7085ee23372b12306 + sha256: fb0f1107cac15a5ea6ef0a6ef71a807b9e4267c713bb93e00e92d737cc8dbd8a url: "https://pub.dev" source: hosted - version: "1.3.1" + version: "1.4.0" checked_yaml: dependency: transitive description: From 399b7dd682c08b1f84dad7b11d88b46b3619a1fe Mon Sep 17 00:00:00 2001 From: Richard Shiue <71320345+richardshiue@users.noreply.github.com> Date: Thu, 12 Dec 2024 08:16:58 +0800 Subject: [PATCH 032/576] test: attempt to fix flaky test (#6970) --- .../integration_test/desktop/database/database_field_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/appflowy_flutter/integration_test/desktop/database/database_field_test.dart b/frontend/appflowy_flutter/integration_test/desktop/database/database_field_test.dart index 9ca6a524a6..1422aa8aee 100644 --- a/frontend/appflowy_flutter/integration_test/desktop/database/database_field_test.dart +++ b/frontend/appflowy_flutter/integration_test/desktop/database/database_field_test.dart @@ -538,8 +538,8 @@ void main() { // edit the first date cell await tester.tapCellInGrid(rowIndex: 0, fieldType: FieldType.DateTime); - await tester.toggleIncludeTime(); final now = DateTime.now(); + await tester.toggleIncludeTime(); await tester.selectDay(content: now.day); await tester.dismissCellEditor(); From 0bf706f438663d53ec24168e03beb263ae9f8320 Mon Sep 17 00:00:00 2001 From: Morn Date: Thu, 12 Dec 2024 08:18:57 +0800 Subject: [PATCH 033/576] fix: Esc not working for Find-Replace menu(#6955) (#6965) --- .../document/document_find_menu_test.dart | 17 +++++ .../find_and_replace_menu.dart | 67 +++++++++++-------- 2 files changed, 57 insertions(+), 27 deletions(-) diff --git a/frontend/appflowy_flutter/integration_test/desktop/document/document_find_menu_test.dart b/frontend/appflowy_flutter/integration_test/desktop/document/document_find_menu_test.dart index 1603dc7937..adb2090563 100644 --- a/frontend/appflowy_flutter/integration_test/desktop/document/document_find_menu_test.dart +++ b/frontend/appflowy_flutter/integration_test/desktop/document/document_find_menu_test.dart @@ -139,6 +139,23 @@ void main() { ), findsOneWidget, ); + + /// press cmd/ctrl+F to display the find menu + await tester.simulateKeyEvent( + LogicalKeyboardKey.keyF, + isControlPressed: + UniversalPlatform.isLinux || UniversalPlatform.isWindows, + isMetaPressed: UniversalPlatform.isMacOS, + ); + await tester.pumpAndSettle(); + + expect(find.byType(FindAndReplaceMenuWidget), findsOneWidget); + + /// press esc to dismiss the find menu + await tester.simulateKeyEvent(LogicalKeyboardKey.escape); + await tester.pumpAndSettle(); + expect(find.byType(FindAndReplaceMenuWidget), findsNothing); + }, ); } diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/find_and_replace/find_and_replace_menu.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/find_and_replace/find_and_replace_menu.dart index 0fee679a42..2d65c602f5 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/find_and_replace/find_and_replace_menu.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/find_and_replace/find_and_replace_menu.dart @@ -4,6 +4,7 @@ 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/material.dart'; +import 'package:flutter/services.dart'; class FindAndReplaceMenuWidget extends StatefulWidget { const FindAndReplaceMenuWidget({ @@ -55,35 +56,47 @@ class _FindAndReplaceMenuWidgetState extends State { @override Widget build(BuildContext context) { - return TextFieldTapRegion( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.symmetric(vertical: 8.0), - child: FindMenu( - onDismiss: widget.onDismiss, - editorState: widget.editorState, - searchService: searchService, - focusNode: findFocusNode, - showReplaceMenu: showReplaceMenu, - onToggleShowReplace: () => setState(() { - showReplaceMenu = !showReplaceMenu; - }), - ), + return Shortcuts( + shortcuts: const { + SingleActivator(LogicalKeyboardKey.escape): DismissIntent(), + }, + child: Actions( + actions: { + DismissIntent: CallbackAction( + onInvoke: (t) => widget.onDismiss.call(), ), - if (showReplaceMenu) - Padding( - padding: const EdgeInsets.only( - bottom: 8.0, + }, + child: TextFieldTapRegion( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: FindMenu( + onDismiss: widget.onDismiss, + editorState: widget.editorState, + searchService: searchService, + focusNode: findFocusNode, + showReplaceMenu: showReplaceMenu, + onToggleShowReplace: () => setState(() { + showReplaceMenu = !showReplaceMenu; + }), + ), ), - child: ReplaceMenu( - editorState: widget.editorState, - searchService: searchService, - focusNode: replaceFocusNode, - ), - ), - ], + if (showReplaceMenu) + Padding( + padding: const EdgeInsets.only( + bottom: 8.0, + ), + child: ReplaceMenu( + editorState: widget.editorState, + searchService: searchService, + focusNode: replaceFocusNode, + ), + ), + ], + ), + ), ), ); } From 1d46923c478697716904e0e3c6d03d6d854a41e2 Mon Sep 17 00:00:00 2001 From: Mathias Mogensen <42929161+Xazin@users.noreply.github.com> Date: Thu, 12 Dec 2024 02:21:06 +0100 Subject: [PATCH 034/576] feat: shrinkWrap grid in document (#6925) * feat: shrinkWrap grid in document * fix: clean up code and minor fixes * test: add test w/ load more option * fix: reinstate pageview & clean unused code * fix: clean database tab bar view --- .../document/document_with_database_test.dart | 104 ++++++ .../board/presentation/board_page.dart | 30 +- .../database/grid/application/grid_bloc.dart | 60 ++- .../database/grid/presentation/grid_page.dart | 346 ++++++++++-------- .../widgets/footer/grid_footer.dart | 41 +++ .../grid/presentation/widgets/row/row.dart | 25 +- .../database/tab_bar/tab_bar_view.dart | 144 +++----- .../base/built_in_page_widget.dart | 7 +- .../database_view_block_component.dart | 7 +- .../workspace/application/view/view_ext.dart | 12 +- .../resources/flowy_icons/16x/load_more.svg | 1 + frontend/resources/translations/en.json | 1 + 12 files changed, 478 insertions(+), 300 deletions(-) create mode 100644 frontend/resources/flowy_icons/16x/load_more.svg diff --git a/frontend/appflowy_flutter/integration_test/desktop/document/document_with_database_test.dart b/frontend/appflowy_flutter/integration_test/desktop/document/document_with_database_test.dart index ede3627598..158eb501e3 100644 --- a/frontend/appflowy_flutter/integration_test/desktop/document/document_with_database_test.dart +++ b/frontend/appflowy_flutter/integration_test/desktop/document/document_with_database_test.dart @@ -1,12 +1,15 @@ import 'package:appflowy/plugins/database/board/presentation/board_page.dart'; import 'package:appflowy/plugins/database/calendar/presentation/calendar_page.dart'; import 'package:appflowy/plugins/database/grid/presentation/grid_page.dart'; +import 'package:appflowy/plugins/database/grid/presentation/widgets/footer/grid_footer.dart'; +import 'package:appflowy/plugins/database/grid/presentation/widgets/row/row.dart'; import 'package:appflowy/plugins/database/widgets/cell/editable_cell_skeleton/text.dart'; import 'package:appflowy/plugins/inline_actions/widgets/inline_actions_handler.dart'; import 'package:appflowy/workspace/presentation/home/menu/view/view_item.dart'; import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart'; import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:flowy_infra/uuid.dart'; +import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; @@ -174,9 +177,110 @@ void main() { findsOneWidget, ); }); + + testWidgets('insert a referenced grid with many rows (load more option)', + (tester) async { + await tester.initializeAppFlowy(); + await tester.tapAnonymousSignInButton(); + + await insertLinkedDatabase(tester, ViewLayoutPB.Grid); + + // validate the referenced grid is inserted + expect( + find.descendant( + of: find.byType(AppFlowyEditor), + matching: find.byType(GridPage), + ), + findsOneWidget, + ); + + // https://github.com/AppFlowy-IO/AppFlowy/issues/3533 + // test: the selection of editor should be clear when editing the grid + await tester.editor.updateSelection( + Selection.collapsed( + Position(path: [1]), + ), + ); + final gridTextCell = find.byType(EditableTextCell).first; + await tester.tapButton(gridTextCell); + + expect(tester.editor.getCurrentEditorState().selection, isNull); + + final editorScrollable = find + .descendant( + of: find.byType(AppFlowyEditor), + matching: find.byWidgetPredicate( + (w) => w is Scrollable && w.axis == Axis.vertical, + ), + ) + .first; + + // Add 100 Rows to the linked database + final addRowFinder = find.byType(GridAddRowButton); + for (var i = 0; i < 100; i++) { + await tester.scrollUntilVisible( + addRowFinder, + 100, + scrollable: editorScrollable, + ); + await tester.tapButton(addRowFinder); + await tester.pumpAndSettle(); + } + + // Since all rows visible are those we added, we should see all of them + expect(find.byType(GridRow), findsNWidgets(103)); + + // Navigate to getting started + await tester.openPage(gettingStarted); + + // Navigate back to the document + await tester.openPage('insert_a_reference_${ViewLayoutPB.Grid.name}'); + + // We see only 25 Grid Rows + expect(find.byType(GridRow), findsNWidgets(25)); + + // We see Add row and load more button + expect(find.byType(GridAddRowButton), findsOneWidget); + expect(find.byType(GridRowLoadMoreButton), findsOneWidget); + + // Load more rows, expect 50 visible + await _loadMoreRows(tester, editorScrollable, 50); + + // Load more rows, expect 75 visible + await _loadMoreRows(tester, editorScrollable, 75); + + // Load more rows, expect 100 visible + await _loadMoreRows(tester, editorScrollable, 100); + + // Load more rows, expect 103 visible + await _loadMoreRows(tester, editorScrollable, 103); + + // We no longer see load more option + expect(find.byType(GridRowLoadMoreButton), findsNothing); + }); }); } +Future _loadMoreRows( + WidgetTester tester, + Finder scrollable, [ + int? expectedRows, +]) async { + await tester.scrollUntilVisible( + find.byType(GridRowLoadMoreButton), + 100, + scrollable: scrollable, + ); + await tester.pumpAndSettle(); + + await tester.tap(find.byType(GridRowLoadMoreButton)); + await tester.pumpAndSettle(); + + if (expectedRows != null) { + expect(find.byType(GridRow), findsNWidgets(expectedRows)); + } +} + /// Insert a referenced database of [layout] into the document Future insertLinkedDatabase( WidgetTester tester, diff --git a/frontend/appflowy_flutter/lib/plugins/database/board/presentation/board_page.dart b/frontend/appflowy_flutter/lib/plugins/database/board/presentation/board_page.dart index ee6a2e9d7a..b373e33b2f 100644 --- a/frontend/appflowy_flutter/lib/plugins/database/board/presentation/board_page.dart +++ b/frontend/appflowy_flutter/lib/plugins/database/board/presentation/board_page.dart @@ -54,6 +54,7 @@ class BoardPageTabBarBuilderImpl extends DatabaseTabBarItemBuilder { key: _makeValueKey(controller), view: view, databaseController: controller, + shrinkWrap: shrinkWrap, ) : MobileBoardPage( key: _makeValueKey(controller), @@ -98,6 +99,7 @@ class DesktopBoardPage extends StatefulWidget { required this.view, required this.databaseController, this.onEditStateChanged, + this.shrinkWrap = false, }); final ViewPB view; @@ -107,6 +109,9 @@ class DesktopBoardPage extends StatefulWidget { /// Called when edit state changed final VoidCallback? onEditStateChanged; + /// If true, the board will shrink wrap its content + final bool shrinkWrap; + @override State createState() => _DesktopBoardPageState(); } @@ -187,24 +192,17 @@ class _DesktopBoardPageState extends State { Widget build(BuildContext context) { return MultiBlocProvider( providers: [ - BlocProvider.value( - value: _boardBloc, - ), - BlocProvider.value( - value: _boardActionsCubit, - ), + BlocProvider.value(value: _boardBloc), + BlocProvider.value(value: _boardActionsCubit), ], child: BlocBuilder( builder: (context, state) => state.maybeMap( loading: (_) => const Center( child: CircularProgressIndicator.adaptive(), ), - error: (err) => Center( - child: AppFlowyErrorPage( - error: err.error, - ), - ), + error: (err) => Center(child: AppFlowyErrorPage(error: err.error)), orElse: () => _BoardContent( + shrinkWrap: widget.shrinkWrap, onEditStateChanged: widget.onEditStateChanged, focusScope: _focusScope, boardController: _boardController, @@ -243,11 +241,13 @@ class _BoardContent extends StatefulWidget { required this.boardController, required this.focusScope, this.onEditStateChanged, + this.shrinkWrap = false, }); final AppFlowyBoardController boardController; final BoardFocusScope focusScope; final VoidCallback? onEditStateChanged; + final bool shrinkWrap; @override State<_BoardContent> createState() => _BoardContentState(); @@ -358,12 +358,8 @@ class _BoardContentState extends State<_BoardContent> { ), footerBuilder: (_, groupData) => MultiBlocProvider( providers: [ - BlocProvider.value( - value: context.read(), - ), - BlocProvider.value( - value: context.read(), - ), + BlocProvider.value(value: context.read()), + BlocProvider.value(value: context.read()), ], child: BoardColumnFooter( columnData: groupData, diff --git a/frontend/appflowy_flutter/lib/plugins/database/grid/application/grid_bloc.dart b/frontend/appflowy_flutter/lib/plugins/database/grid/application/grid_bloc.dart index a9aab132a0..9b59b997b1 100644 --- a/frontend/appflowy_flutter/lib/plugins/database/grid/application/grid_bloc.dart +++ b/frontend/appflowy_flutter/lib/plugins/database/grid/application/grid_bloc.dart @@ -20,13 +20,20 @@ import '../../application/database_controller.dart'; part 'grid_bloc.freezed.dart'; class GridBloc extends Bloc { - GridBloc({required ViewPB view, required this.databaseController}) - : super(GridState.initial(view.id)) { + GridBloc({ + required ViewPB view, + required this.databaseController, + this.shrinkWrapped = false, + }) : super(GridState.initial(view.id)) { _dispatch(); } final DatabaseController databaseController; + /// When true will emit the count of visible rows to show + /// + final bool shrinkWrapped; + String get viewId => databaseController.viewId; UserProfilePB? _userProfile; @@ -64,12 +71,22 @@ class GridBloc extends Bloc { ); }, createRow: (openRowDetail) async { - final result = await RowBackendService.createRow(viewId: viewId); + final lastVisibleRowId = + shrinkWrapped ? state.lastVisibleRow?.rowId : null; + + final result = await RowBackendService.createRow( + viewId: viewId, + position: lastVisibleRowId != null + ? OrderObjectPositionTypePB.After + : null, + targetRowId: lastVisibleRowId, + ); result.fold( (createdRow) => emit( state.copyWith( createdRow: createdRow, openRowDetail: openRowDetail ?? false, + visibleRows: state.visibleRows + 1, ), ), (err) => Log.error(err), @@ -93,11 +110,7 @@ class GridBloc extends Bloc { databaseController.moveRow(fromRowId: fromRow, toRowId: toRow); }, didReceiveFieldUpdate: (fields) { - emit( - state.copyWith( - fields: fields, - ), - ); + emit(state.copyWith(fields: fields)); }, didLoadRows: (newRowInfos, reason) { emit( @@ -109,17 +122,13 @@ class GridBloc extends Bloc { ); }, didReceveFilters: (filters) { - emit( - state.copyWith(filters: filters), - ); + emit(state.copyWith(filters: filters)); }, didReceveSorts: (sorts) { - emit( - state.copyWith( - reorderable: sorts.isEmpty, - sorts: sorts, - ), - ); + emit(state.copyWith(reorderable: sorts.isEmpty, sorts: sorts)); + }, + loadMoreRows: () { + emit(state.copyWith(visibleRows: state.visibleRows + 25)); }, ); }, @@ -204,11 +213,11 @@ class GridEvent with _$GridEvent { const factory GridEvent.didReceiveFieldUpdate( List fields, ) = _DidReceiveFieldUpdate; - const factory GridEvent.didReceveFilters(List filters) = _DidReceiveFilters; const factory GridEvent.didReceveSorts(List sorts) = _DidReceiveSorts; + const factory GridEvent.loadMoreRows() = _LoadMoreRows; } @freezed @@ -225,6 +234,7 @@ class GridState with _$GridState { required List sorts, required List filters, required bool openRowDetail, + @Default(0) int visibleRows, }) = _GridState; factory GridState.initial(String viewId) => GridState( @@ -239,5 +249,19 @@ class GridState with _$GridState { filters: [], sorts: [], openRowDetail: false, + visibleRows: 25, ); } + +extension _LastVisibleRow on GridState { + /// Returns the last visible [RowInfo] in the list of [rowInfos]. + /// Only returns if the visibleRows is less than the rowCount, otherwise returns null. + /// + RowInfo? get lastVisibleRow { + if (visibleRows < rowCount) { + return rowInfos[visibleRows - 1]; + } + + return null; + } +} diff --git a/frontend/appflowy_flutter/lib/plugins/database/grid/presentation/grid_page.dart b/frontend/appflowy_flutter/lib/plugins/database/grid/presentation/grid_page.dart index ae27893e8b..39f5052e8d 100755 --- a/frontend/appflowy_flutter/lib/plugins/database/grid/presentation/grid_page.dart +++ b/frontend/appflowy_flutter/lib/plugins/database/grid/presentation/grid_page.dart @@ -1,6 +1,7 @@ import 'dart:async'; -import 'dart:math'; +import 'package:appflowy/plugins/database/grid/presentation/widgets/toolbar/grid_setting_bar.dart'; +import 'package:appflowy/plugins/database/tab_bar/desktop/setting_menu.dart'; import 'package:flutter/material.dart'; import 'package:appflowy/generated/locale_keys.g.dart'; @@ -8,8 +9,6 @@ import 'package:appflowy/plugins/database/application/row/row_service.dart'; import 'package:appflowy/plugins/database/application/tab_bar_bloc.dart'; import 'package:appflowy/plugins/database/domain/sort_service.dart'; import 'package:appflowy/plugins/database/grid/presentation/widgets/calculations/calculations_row.dart'; -import 'package:appflowy/plugins/database/grid/presentation/widgets/toolbar/grid_setting_bar.dart'; -import 'package:appflowy/plugins/database/tab_bar/desktop/setting_menu.dart'; import 'package:appflowy/plugins/database/widgets/cell/editable_cell_builder.dart'; import 'package:appflowy/shared/flowy_error_page.dart'; import 'package:appflowy/workspace/application/action_navigation/action_navigation_bloc.dart'; @@ -67,6 +66,7 @@ class DesktopGridTabBarBuilderImpl extends DatabaseTabBarItemBuilder { view: view, databaseController: controller, initialRowId: initialRowId, + shrinkWrap: shrinkWrap, ); } @@ -110,12 +110,14 @@ class GridPage extends StatefulWidget { required this.databaseController, this.onDeleted, this.initialRowId, + this.shrinkWrap = false, }); final ViewPB view; final DatabaseController databaseController; final VoidCallback? onDeleted; final String? initialRowId; + final bool shrinkWrap; @override State createState() => _GridPageState(); @@ -124,13 +126,22 @@ class GridPage extends StatefulWidget { class _GridPageState extends State { bool _didOpenInitialRow = false; + late final GridBloc gridBloc = GridBloc( + view: widget.view, + databaseController: widget.databaseController, + shrinkWrapped: widget.shrinkWrap, + )..add(const GridEvent.initial()); + + @override + void dispose() { + gridBloc.close(); + super.dispose(); + } + @override Widget build(BuildContext context) { return BlocProvider( - create: (context) => GridBloc( - view: widget.view, - databaseController: widget.databaseController, - )..add(const GridEvent.initial()), + create: (_) => gridBloc, child: BlocListener( listener: (context, state) { final action = state.action; @@ -147,6 +158,7 @@ class _GridPageState extends State { child: BlocConsumer( listener: listener, builder: (context, state) => state.loadingState.map( + idle: (_) => const SizedBox.shrink(), loading: (_) => const Center( child: CircularProgressIndicator.adaptive(), ), @@ -155,25 +167,18 @@ class _GridPageState extends State { child: GridPageContent( key: ValueKey(widget.view.id), view: widget.view, + shrinkWrap: widget.shrinkWrap, ), ), - (err) => Center( - child: AppFlowyErrorPage( - error: err, - ), - ), + (err) => Center(child: AppFlowyErrorPage(error: err)), ), - idle: (_) => const SizedBox.shrink(), ), ), ), ); } - void _openRow( - BuildContext context, - String rowId, - ) { + void _openRow(BuildContext context, String rowId) { WidgetsBinding.instance.addPostFrameCallback((_) { final gridBloc = context.read(); final rowCache = gridBloc.rowCache; @@ -249,9 +254,11 @@ class GridPageContent extends StatefulWidget { const GridPageContent({ super.key, required this.view, + this.shrinkWrap = false, }); final ViewPB view; + final bool shrinkWrap; @override State createState() => _GridPageContentState(); @@ -279,13 +286,13 @@ class _GridPageContentState extends State { Widget build(BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, children: [ - _GridHeader( - headerScrollController: headerScrollController, - ), + _GridHeader(headerScrollController: headerScrollController), _GridRows( viewId: widget.view.id, scrollController: _scrollController, + shrinkWrap: widget.shrinkWrap, ), ], ); @@ -300,12 +307,10 @@ class _GridHeader extends StatelessWidget { @override Widget build(BuildContext context) { return BlocBuilder( - builder: (context, state) { - return GridHeaderSliverAdaptor( - viewId: state.viewId, - anchorScrollController: headerScrollController, - ); - }, + builder: (_, state) => GridHeaderSliverAdaptor( + viewId: state.viewId, + anchorScrollController: headerScrollController, + ), ); } } @@ -314,11 +319,17 @@ class _GridRows extends StatefulWidget { const _GridRows({ required this.viewId, required this.scrollController, + this.shrinkWrap = false, }); final String viewId; final GridScrollController scrollController; + /// When [shrinkWrap] is active, the Grid will show items according to + /// GridState.visibleRows and will not have a vertical scroll area. + /// + final bool shrinkWrap; + @override State<_GridRows> createState() => _GridRowsState(); } @@ -330,8 +341,10 @@ class _GridRowsState extends State<_GridRows> { @override void initState() { super.initState(); - _evaluateFloatingCalculations(); - widget.scrollController.verticalController.addListener(_onScrollChanged); + if (!widget.shrinkWrap) { + _evaluateFloatingCalculations(); + widget.scrollController.verticalController.addListener(_onScrollChanged); + } } void _onScrollChanged() { @@ -345,13 +358,16 @@ class _GridRowsState extends State<_GridRows> { @override void dispose() { - widget.scrollController.verticalController.removeListener(_onScrollChanged); + if (!widget.shrinkWrap) { + widget.scrollController.verticalController + .removeListener(_onScrollChanged); + } super.dispose(); } void _evaluateFloatingCalculations() { WidgetsBinding.instance.addPostFrameCallback((_) { - if (mounted) { + if (mounted && !widget.shrinkWrap) { setState(() { final verticalController = widget.scrollController.verticalController; // maxScrollExtent is 0.0 if scrolling is not possible @@ -367,121 +383,59 @@ class _GridRowsState extends State<_GridRows> { @override Widget build(BuildContext context) { - return BlocBuilder( - buildWhen: (previous, current) => previous.fields != current.fields, - builder: (context, state) { - return Flexible( - child: LayoutBuilder( - builder: (BuildContext context, BoxConstraints layoutConstraits) { - return _WrapScrollView( - scrollController: widget.scrollController, - contentWidth: GridLayout.headerWidth( - context - .read() - .horizontalPadding, - state.fields, - ), - child: BlocConsumer( - listenWhen: (previous, current) => - previous.rowCount != current.rowCount, - listener: (context, state) => _evaluateFloatingCalculations(), - builder: (context, state) { - return ScrollConfiguration( - behavior: ScrollConfiguration.of(context).copyWith( - scrollbars: false, - ), - child: _renderList(context, state, layoutConstraits), - ); - }, - ), - ); - }, - ), - ); - }, - ); - } - - Widget _renderList( - BuildContext context, - GridState state, - BoxConstraints layoutConstraints, - ) { - // 1. GridRowBottomBar - // 2. GridCalculationsRow - final itemCount = - state.rowInfos.length + (showFloatingCalculations ? 1 : 2); - return Column( - children: [ - Expanded( - child: ReorderableListView.builder( - /// This is a workaround related to - /// https://github.com/flutter/flutter/issues/25652 - cacheExtent: max(layoutConstraints.maxHeight, 500), - scrollController: widget.scrollController.verticalController, - physics: const ClampingScrollPhysics(), - buildDefaultDragHandles: false, - proxyDecorator: (child, _, __) => Provider.value( - value: context.read(), - child: Material( - color: Colors.white.withOpacity(.1), - child: Opacity(opacity: .5, child: child), - ), + Widget child; + if (widget.shrinkWrap) { + child = SingleChildScrollView( + scrollDirection: Axis.horizontal, + controller: widget.scrollController.horizontalController, + child: ConstrainedBox( + constraints: BoxConstraints( + maxWidth: GridLayout.headerWidth( + context.read().horizontalPadding, + context.read().state.fields, ), - onReorder: (fromIndex, newIndex) { - void moveRow() { - final toIndex = newIndex > fromIndex ? newIndex - 1 : newIndex; - if (fromIndex != toIndex) { - context - .read() - .add(GridEvent.moveRow(fromIndex, toIndex)); - } - } - - if (state.sorts.isNotEmpty) { - showCancelAndDeleteDialog( - context: context, - title: LocaleKeys.grid_sort_sortsActive.tr( - namedArgs: { - 'intention': - LocaleKeys.grid_row_reorderRowDescription.tr(), - }, - ), - description: LocaleKeys.grid_sort_removeSorting.tr(), - confirmLabel: LocaleKeys.button_remove.tr(), - closeOnAction: true, - onDelete: () { - SortBackendService(viewId: widget.viewId).deleteAllSorts(); - moveRow(); - }, - ); - } else { - moveRow(); - } - }, - itemCount: itemCount, - itemBuilder: (context, index) { - if (index == state.rowInfos.length) { - return const GridRowBottomBar(key: Key('grid_footer')); - } - - if (index == state.rowInfos.length + 1 && - !showFloatingCalculations) { - return GridCalculationsRow( - key: const Key('grid_calculations'), - viewId: widget.viewId, - ); - } - - return _renderRow( - context, - state.rowInfos[index].rowId, - index: index, - ); - }, + ), + child: _renderList(context), + ), + ); + } else { + child = _WrapScrollView( + scrollController: widget.scrollController, + contentWidth: GridLayout.headerWidth( + context.read().horizontalPadding, + context.read().state.fields, + ), + child: BlocListener( + listenWhen: (previous, current) => + previous.rowCount != current.rowCount, + listener: (context, state) => _evaluateFloatingCalculations(), + child: ScrollConfiguration( + behavior: + ScrollConfiguration.of(context).copyWith(scrollbars: false), + child: _renderList(context), ), ), - if (showFloatingCalculations) ...[ + ); + } + + if (widget.shrinkWrap) { + return child; + } + + return Flexible(child: child); + } + + Widget _renderList(BuildContext context) { + final state = context.read().state; + + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + widget.shrinkWrap + ? _reorderableListView(state) + : Expanded(child: _reorderableListView(state)), + if (showFloatingCalculations && !widget.shrinkWrap) ...[ _PositionedCalculationsRow( viewId: widget.viewId, isAtBottom: isAtBottom, @@ -491,6 +445,77 @@ class _GridRowsState extends State<_GridRows> { ); } + Widget _reorderableListView(GridState state) { + final List footer = [ + const GridRowBottomBar(), + if (widget.shrinkWrap && state.visibleRows < state.rowInfos.length) + const GridRowLoadMoreButton(), + if (!showFloatingCalculations) GridCalculationsRow(viewId: widget.viewId), + ]; + + // If we are using shrinkWrap, we need to show at most + // state.visibleRows + 1 items. The visibleRows can be larger + // than the actual rowInfos length. + final itemCount = widget.shrinkWrap + ? (state.visibleRows + 1).clamp(0, state.rowInfos.length + 1) + : state.rowInfos.length + 1; + + return ReorderableListView.builder( + cacheExtent: 500, + scrollController: widget.scrollController.verticalController, + physics: const ClampingScrollPhysics(), + buildDefaultDragHandles: false, + shrinkWrap: widget.shrinkWrap, + proxyDecorator: (child, _, __) => Provider.value( + value: context.read(), + child: Material( + color: Colors.white.withOpacity(.1), + child: Opacity(opacity: .5, child: child), + ), + ), + onReorder: (fromIndex, newIndex) { + final toIndex = newIndex > fromIndex ? newIndex - 1 : newIndex; + + if (state.sorts.isNotEmpty) { + showCancelAndDeleteDialog( + context: context, + title: LocaleKeys.grid_sort_sortsActive.tr( + namedArgs: { + 'intention': LocaleKeys.grid_row_reorderRowDescription.tr(), + }, + ), + description: LocaleKeys.grid_sort_removeSorting.tr(), + confirmLabel: LocaleKeys.button_remove.tr(), + closeOnAction: true, + onDelete: () { + SortBackendService(viewId: widget.viewId).deleteAllSorts(); + moveRow(fromIndex, toIndex); + }, + ); + } else { + moveRow(fromIndex, toIndex); + } + }, + itemCount: itemCount, + itemBuilder: (context, index) { + if (index == itemCount - 1) { + return Column( + key: const Key('grid_footer'), + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: footer, + ); + } + + return _renderRow( + context, + state.rowInfos[index].rowId, + index: index, + ); + }, + ); + } + Widget _renderRow( BuildContext context, RowId rowId, { @@ -509,6 +534,7 @@ class _GridRowsState extends State<_GridRows> { final child = GridRow( key: ValueKey("grid_row_$rowId"), + shrinkWrap: widget.shrinkWrap, fieldController: databaseController.fieldController, rowId: rowId, viewId: viewId, @@ -523,20 +549,22 @@ class _GridRowsState extends State<_GridRows> { context: rowDetailContext, builder: (_) { final rowMeta = rowCache.getRow(rowId)?.rowMeta; - return rowMeta == null - ? const SizedBox.shrink() - : BlocProvider.value( - value: context.read(), - child: RowDetailPage( - rowController: RowController( - viewId: viewId, - rowMeta: rowMeta, - rowCache: rowCache, - ), - databaseController: databaseController, - userProfile: context.read().userProfile, - ), - ); + if (rowMeta == null) { + return const SizedBox.shrink(); + } + + return BlocProvider.value( + value: context.read(), + child: RowDetailPage( + rowController: RowController( + viewId: viewId, + rowMeta: rowMeta, + rowCache: rowCache, + ), + databaseController: databaseController, + userProfile: context.read().userProfile, + ), + ); }, ), ); @@ -547,6 +575,12 @@ class _GridRowsState extends State<_GridRows> { return child; } + + void moveRow(int from, int to) { + if (from != to) { + context.read().add(GridEvent.moveRow(from, to)); + } + } } class _WrapScrollView extends StatelessWidget { diff --git a/frontend/appflowy_flutter/lib/plugins/database/grid/presentation/widgets/footer/grid_footer.dart b/frontend/appflowy_flutter/lib/plugins/database/grid/presentation/widgets/footer/grid_footer.dart index 219b82e027..1b973554cd 100755 --- a/frontend/appflowy_flutter/lib/plugins/database/grid/presentation/widgets/footer/grid_footer.dart +++ b/frontend/appflowy_flutter/lib/plugins/database/grid/presentation/widgets/footer/grid_footer.dart @@ -57,3 +57,44 @@ class GridRowBottomBar extends StatelessWidget { ); } } + +class GridRowLoadMoreButton extends StatelessWidget { + const GridRowLoadMoreButton({super.key}); + + @override + Widget build(BuildContext context) { + final padding = + context.read().horizontalPadding; + final color = Theme.of(context).brightness == Brightness.light + ? const Color(0xFF171717).withOpacity(0.4) + : const Color(0xFFFFFFFF).withOpacity(0.4); + + return Container( + padding: GridSize.footerContentInsets.copyWith(left: 0) + + EdgeInsets.only(left: padding), + height: GridSize.footerHeight, + child: FlowyButton( + radius: BorderRadius.zero, + decoration: BoxDecoration( + border: Border( + bottom: BorderSide(color: AFThemeExtension.of(context).borderColor), + ), + ), + text: FlowyText( + lineHeight: 1.0, + LocaleKeys.grid_row_loadMore.tr(), + color: color, + ), + margin: const EdgeInsets.symmetric(horizontal: 12), + hoverColor: AFThemeExtension.of(context).lightGreyHover, + onTap: () => context.read().add( + const GridEvent.loadMoreRows(), + ), + leftIcon: FlowySvg( + FlowySvgs.load_more_s, + color: color, + ), + ), + ); + } +} diff --git a/frontend/appflowy_flutter/lib/plugins/database/grid/presentation/widgets/row/row.dart b/frontend/appflowy_flutter/lib/plugins/database/grid/presentation/widgets/row/row.dart index 855c73ce8a..4de6f20bc2 100755 --- a/frontend/appflowy_flutter/lib/plugins/database/grid/presentation/widgets/row/row.dart +++ b/frontend/appflowy_flutter/lib/plugins/database/grid/presentation/widgets/row/row.dart @@ -34,6 +34,7 @@ class GridRow extends StatelessWidget { required this.cellBuilder, required this.openDetailPage, required this.index, + this.shrinkWrap = false, }); final FieldController fieldController; @@ -43,9 +44,20 @@ class GridRow extends StatelessWidget { final EditableCellBuilder cellBuilder; final void Function(BuildContext context) openDetailPage; final int index; + final bool shrinkWrap; @override Widget build(BuildContext context) { + Widget rowContent = RowContent( + fieldController: fieldController, + cellBuilder: cellBuilder, + onExpand: () => openDetailPage(context), + ); + + if (!shrinkWrap) { + rowContent = Expanded(child: rowContent); + } + return BlocProvider( create: (_) => RowBloc( fieldController: fieldController, @@ -56,17 +68,8 @@ class GridRow extends StatelessWidget { child: _RowEnterRegion( child: Row( children: [ - _RowLeading( - viewId: viewId, - index: index, - ), - Expanded( - child: RowContent( - fieldController: fieldController, - cellBuilder: cellBuilder, - onExpand: () => openDetailPage(context), - ), - ), + _RowLeading(viewId: viewId, index: index), + rowContent, ], ), ), diff --git a/frontend/appflowy_flutter/lib/plugins/database/tab_bar/tab_bar_view.dart b/frontend/appflowy_flutter/lib/plugins/database/tab_bar/tab_bar_view.dart index 8d8a1446ad..542938a574 100644 --- a/frontend/appflowy_flutter/lib/plugins/database/tab_bar/tab_bar_view.dart +++ b/frontend/appflowy_flutter/lib/plugins/database/tab_bar/tab_bar_view.dart @@ -55,7 +55,7 @@ abstract class DatabaseTabBarItemBuilder { void dispose() {} } -class DatabaseTabBarView extends StatefulWidget { +class DatabaseTabBarView extends StatelessWidget { const DatabaseTabBarView({ super.key, required this.view, @@ -70,108 +70,84 @@ class DatabaseTabBarView extends StatefulWidget { /// final String? initialRowId; - @override - State createState() => _DatabaseTabBarViewState(); -} - -class _DatabaseTabBarViewState extends State { - final PageController _pageController = PageController(); - late String? _initialRowId = widget.initialRowId; - - @override - void dispose() { - _pageController.dispose(); - super.dispose(); - } - @override Widget build(BuildContext context) { return MultiBlocProvider( providers: [ BlocProvider( - create: (_) => DatabaseTabBarBloc(view: widget.view) + create: (_) => DatabaseTabBarBloc(view: view) ..add(const DatabaseTabBarEvent.initial()), ), BlocProvider( - create: (_) => - ViewBloc(view: widget.view)..add(const ViewEvent.initial()), + create: (_) => ViewBloc(view: view)..add(const ViewEvent.initial()), ), ], - child: MultiBlocListener( - listeners: [ - BlocListener( - listenWhen: (p, c) => p.selectedIndex != c.selectedIndex, - listener: (_, state) { - _initialRowId = null; - _pageController.jumpToPage(state.selectedIndex); - }, - ), - ], - child: Column( - children: [ - if (UniversalPlatform.isMobile) const VSpace(12), - BlocBuilder( - builder: (context, state) { - return ValueListenableBuilder( - valueListenable: state - .tabBarControllerByViewId[state.parentView.id]! - .controller - .isLoading, - builder: (_, value, ___) { - if (value) { - return const SizedBox.shrink(); - } + child: BlocBuilder( + builder: (_, state) { + final layout = state.tabBars[state.selectedIndex].layout; + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (UniversalPlatform.isMobile) const VSpace(12), + ValueListenableBuilder( + valueListenable: state + .tabBarControllerByViewId[state.parentView.id]! + .controller + .isLoading, + builder: (_, value, ___) { + if (value) { + return const SizedBox.shrink(); + } - return UniversalPlatform.isDesktop - ? const TabBarHeader() - : const MobileTabBarHeader(); - }, - ); - }, - ), - BlocBuilder( - builder: (context, state) => - pageSettingBarExtensionFromState(state), - ), - BlocBuilder( - builder: (context, state) { - final content = PageView( - controller: _pageController, - pageSnapping: false, - physics: const NeverScrollableScrollPhysics(), - children: pageContentFromState(state), - ); - - if (widget.shrinkWrap) { - final layout = state.tabBars[state.selectedIndex].layout; - return SizedBox(height: layout.pluginHeight, child: content); - } - - return Expanded(child: content); - }, - ), - ], - ), + return UniversalPlatform.isDesktop + ? const TabBarHeader() + : const MobileTabBarHeader(); + }, + ), + pageSettingBarExtensionFromState(context, state), + wrapContent( + layout: layout, + child: pageContentFromState(context, state), + ), + ], + ); + }, ), ); } - List pageContentFromState(DatabaseTabBarState state) { - return state.tabBars.map((tabBar) { - final controller = - state.tabBarControllerByViewId[tabBar.viewId]!.controller; + Widget wrapContent({required ViewLayoutPB layout, required Widget child}) { + if (shrinkWrap) { + if (layout.shrinkWrappable) { + return child; + } - return tabBar.builder.content( - context, - tabBar.view, - controller, - widget.shrinkWrap, - _initialRowId, + return SizedBox( + height: layout.pluginHeight, + child: child, ); - }).toList(); + } + + return Expanded(child: child); } - Widget pageSettingBarExtensionFromState(DatabaseTabBarState state) { + Widget pageContentFromState(BuildContext context, DatabaseTabBarState state) { + final tab = state.tabBars[state.selectedIndex]; + final controller = state.tabBarControllerByViewId[tab.viewId]!.controller; + + return tab.builder.content( + context, + tab.view, + controller, + shrinkWrap, + initialRowId, + ); + } + + Widget pageSettingBarExtensionFromState( + BuildContext context, + DatabaseTabBarState state, + ) { if (state.tabBars.length < state.selectedIndex) { return const SizedBox.shrink(); } diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/base/built_in_page_widget.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/base/built_in_page_widget.dart index 1a673dd8aa..9f25e4745d 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/base/built_in_page_widget.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/base/built_in_page_widget.dart @@ -75,15 +75,16 @@ class _BuiltInPageWidgetState extends State { ); } - Widget _build(BuildContext context, ViewPB view) { + Widget _build(BuildContext context, ViewPB viewPB) { return MouseRegion( onEnter: (_) => widget.editorState.service.scrollService?.disable(), onExit: (_) => widget.editorState.service.scrollService?.enable(), child: Column( + mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ - _buildMenu(context, view), - _buildPage(context, view), + _buildMenu(context, viewPB), + Flexible(child: _buildPage(context, viewPB)), ], ), ); diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/database/database_view_block_component.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/database/database_view_block_component.dart index 630d612bcd..cd150ff1c1 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/database/database_view_block_component.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/database/database_view_block_component.dart @@ -71,12 +71,7 @@ class _DatabaseBlockComponentWidgetState Widget child = BuiltInPageWidget( node: widget.node, editorState: editorState, - builder: (viewPB) { - return DatabaseViewWidget( - key: ValueKey(viewPB.id), - view: viewPB, - ); - }, + builder: (view) => DatabaseViewWidget(key: ValueKey(view.id), view: view), ); child = Padding( diff --git a/frontend/appflowy_flutter/lib/workspace/application/view/view_ext.dart b/frontend/appflowy_flutter/lib/workspace/application/view/view_ext.dart index b35665af2c..375d8f4534 100644 --- a/frontend/appflowy_flutter/lib/workspace/application/view/view_ext.dart +++ b/frontend/appflowy_flutter/lib/workspace/application/view/view_ext.dart @@ -325,13 +325,15 @@ extension ViewLayoutExtension on ViewLayoutPB { _ => LocaleKeys.menuAppHeader_defaultNewPageName.tr(), }; + bool get shrinkWrappable => switch (this) { + ViewLayoutPB.Grid => true, + _ => false, + }; + double get pluginHeight => switch (this) { - ViewLayoutPB.Grid || - ViewLayoutPB.Board || - ViewLayoutPB.Document || - ViewLayoutPB.Chat => - 450, + ViewLayoutPB.Document || ViewLayoutPB.Board || ViewLayoutPB.Chat => 450, ViewLayoutPB.Calendar => 650, + ViewLayoutPB.Grid => double.infinity, _ => throw UnimplementedError(), }; } diff --git a/frontend/resources/flowy_icons/16x/load_more.svg b/frontend/resources/flowy_icons/16x/load_more.svg new file mode 100644 index 0000000000..078b8668b3 --- /dev/null +++ b/frontend/resources/flowy_icons/16x/load_more.svg @@ -0,0 +1 @@ +Arrow Down Streamline Icon: https://streamlinehq.com \ No newline at end of file diff --git a/frontend/resources/translations/en.json b/frontend/resources/translations/en.json index 4a27f0f783..2219f89c8b 100644 --- a/frontend/resources/translations/en.json +++ b/frontend/resources/translations/en.json @@ -1513,6 +1513,7 @@ "copyProperty": "Copied property to clipboard", "count": "Count", "newRow": "New row", + "loadMore": "Load more", "action": "Action", "add": "Click add to below", "drag": "Drag to move", From e4385adfa9aad6d44698aa850b35254c38d9b79f Mon Sep 17 00:00:00 2001 From: Mathias Mogensen <42929161+Xazin@users.noreply.github.com> Date: Thu, 12 Dec 2024 02:21:23 +0100 Subject: [PATCH 035/576] fix: rename untitled view (#6789) * fix: rename untitled view * test: add cloud test * chore: clean up code * test: use nameOrDefault * test: fix failing test * test: fix wrong assumption --- .../desktop/cloud/cloud_runner.dart | 3 + .../sidebar/sidebar_rename_untitled_test.dart | 55 +++++++++++++++++++ .../shared/common_operations.dart | 7 +-- .../tab_bar/desktop/tab_bar_header.dart | 6 +- .../tab_bar/mobile/mobile_tab_bar_header.dart | 6 +- .../favorites/favorite_more_actions.dart | 3 +- .../home/menu/view/view_item.dart | 2 +- 7 files changed, 66 insertions(+), 16 deletions(-) create mode 100644 frontend/appflowy_flutter/integration_test/desktop/cloud/sidebar/sidebar_rename_untitled_test.dart diff --git a/frontend/appflowy_flutter/integration_test/desktop/cloud/cloud_runner.dart b/frontend/appflowy_flutter/integration_test/desktop/cloud/cloud_runner.dart index a0b6901f2d..0b35cffe51 100644 --- a/frontend/appflowy_flutter/integration_test/desktop/cloud/cloud_runner.dart +++ b/frontend/appflowy_flutter/integration_test/desktop/cloud/cloud_runner.dart @@ -3,6 +3,8 @@ import 'data_migration/data_migration_test_runner.dart' import 'document/document_test_runner.dart' as document_test_runner; import 'set_env.dart' as preset_af_cloud_env_test; import 'sidebar/sidebar_move_page_test.dart' as sidebar_move_page_test; +import 'sidebar/sidebar_rename_untitled_test.dart' + as sidebar_rename_untitled_test; import 'uncategorized/uncategorized_test_runner.dart' as uncategorized_test_runner; import 'workspace/workspace_test_runner.dart' as workspace_test_runner; @@ -23,4 +25,5 @@ Future main() async { // sidebar sidebar_move_page_test.main(); + sidebar_rename_untitled_test.main(); } diff --git a/frontend/appflowy_flutter/integration_test/desktop/cloud/sidebar/sidebar_rename_untitled_test.dart b/frontend/appflowy_flutter/integration_test/desktop/cloud/sidebar/sidebar_rename_untitled_test.dart new file mode 100644 index 0000000000..8226b68b26 --- /dev/null +++ b/frontend/appflowy_flutter/integration_test/desktop/cloud/sidebar/sidebar_rename_untitled_test.dart @@ -0,0 +1,55 @@ +import 'package:appflowy/env/cloud_env.dart'; +import 'package:appflowy/generated/locale_keys.g.dart'; +import 'package:appflowy/workspace/application/view/view_ext.dart'; +import 'package:appflowy/workspace/presentation/widgets/dialogs.dart'; +import 'package:appflowy_backend/protobuf/flowy-folder/view.pbenum.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flowy_infra_ui/style_widget/text_input.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; + +import '../../../shared/constants.dart'; +import '../../../shared/util.dart'; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + testWidgets('Rename empty name view (untitled)', (tester) async { + await tester.initializeAppFlowy( + cloudType: AuthenticatorType.appflowyCloudSelfHost, + ); + await tester.tapGoogleLoginInButton(); + await tester.expectToSeeHomePageWithGetStartedPage(); + + await tester.createNewPageInSpace( + spaceName: Constants.generalSpaceName, + layout: ViewLayoutPB.Document, + ); + + // click the ... button and open rename dialog + await tester.hoverOnPageName( + ViewLayoutPB.Document.defaultName, + onHover: () async { + await tester.tapPageOptionButton(); + await tester.tapButtonWithName( + LocaleKeys.disclosureAction_rename.tr(), + ); + }, + ); + await tester.pumpAndSettle(); + + expect(find.byType(NavigatorTextFieldDialog), findsOneWidget); + + final textField = tester.widget( + find.descendant( + of: find.byType(NavigatorTextFieldDialog), + matching: find.byType(FlowyFormTextInput), + ), + ); + + expect( + textField.controller!.text, + LocaleKeys.menuAppHeader_defaultNewPageName.tr(), + ); + }); +} diff --git a/frontend/appflowy_flutter/integration_test/shared/common_operations.dart b/frontend/appflowy_flutter/integration_test/shared/common_operations.dart index 5aef1e34ec..0c4b05da4c 100644 --- a/frontend/appflowy_flutter/integration_test/shared/common_operations.dart +++ b/frontend/appflowy_flutter/integration_test/shared/common_operations.dart @@ -449,11 +449,8 @@ extension CommonOperations on WidgetTester { // open the page after created if (openAfterCreated) { - await openPage( - // if the name is null, use the default name - pageName ?? LocaleKeys.menuAppHeader_defaultNewPageName.tr(), - layout: layout, - ); + // if the name is null, use empty string + await openPage(pageName ?? '', layout: layout); await pumpAndSettle(); } } diff --git a/frontend/appflowy_flutter/lib/plugins/database/tab_bar/desktop/tab_bar_header.dart b/frontend/appflowy_flutter/lib/plugins/database/tab_bar/desktop/tab_bar_header.dart index 116e47349c..e9408660ee 100644 --- a/frontend/appflowy_flutter/lib/plugins/database/tab_bar/desktop/tab_bar_header.dart +++ b/frontend/appflowy_flutter/lib/plugins/database/tab_bar/desktop/tab_bar_header.dart @@ -219,9 +219,7 @@ class TabBarItemButton extends StatelessWidget { color: color, ), text: FlowyText( - view.name.isEmpty - ? LocaleKeys.document_title_placeholder.tr() - : view.name, + view.nameOrDefault, lineHeight: 1.0, textAlign: TextAlign.center, overflow: TextOverflow.ellipsis, @@ -236,7 +234,7 @@ class TabBarItemButton extends StatelessWidget { case TabBarViewAction.rename: NavigatorTextFieldDialog( title: LocaleKeys.menuAppHeader_renameDialog.tr(), - value: view.name, + value: view.nameOrDefault, onConfirm: (newValue, _) { context.read().add( DatabaseTabBarEvent.renameView(view.id, newValue), diff --git a/frontend/appflowy_flutter/lib/plugins/database/tab_bar/mobile/mobile_tab_bar_header.dart b/frontend/appflowy_flutter/lib/plugins/database/tab_bar/mobile/mobile_tab_bar_header.dart index 8796df14ef..3606ed82a3 100644 --- a/frontend/appflowy_flutter/lib/plugins/database/tab_bar/mobile/mobile_tab_bar_header.dart +++ b/frontend/appflowy_flutter/lib/plugins/database/tab_bar/mobile/mobile_tab_bar_header.dart @@ -1,6 +1,4 @@ -import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/plugins/database/grid/presentation/layout/sizes.dart'; -import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:appflowy/generated/flowy_svgs.g.dart'; @@ -107,9 +105,7 @@ class _DatabaseViewSelectorButton extends StatelessWidget { const HSpace(6), Flexible( child: FlowyText.medium( - tabBar.view.name.isEmpty - ? LocaleKeys.document_title_placeholder.tr() - : tabBar.view.name, + tabBar.view.nameOrDefault, fontSize: 14, overflow: TextOverflow.ellipsis, ), diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/favorites/favorite_more_actions.dart b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/favorites/favorite_more_actions.dart index 5f12ed1b04..09b8a44842 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/favorites/favorite_more_actions.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/favorites/favorite_more_actions.dart @@ -5,6 +5,7 @@ import 'package:appflowy/workspace/application/favorite/favorite_bloc.dart'; import 'package:appflowy/workspace/application/sidebar/folder/folder_bloc.dart'; import 'package:appflowy/workspace/application/tabs/tabs_bloc.dart'; import 'package:appflowy/workspace/application/view/view_bloc.dart'; +import 'package:appflowy/workspace/application/view/view_ext.dart'; import 'package:appflowy/workspace/application/view/view_service.dart'; import 'package:appflowy/workspace/presentation/home/menu/view/view_action_type.dart'; import 'package:appflowy/workspace/presentation/home/menu/view/view_more_action_button.dart'; @@ -43,7 +44,7 @@ class FavoriteMoreActions extends StatelessWidget { NavigatorTextFieldDialog( title: LocaleKeys.disclosureAction_rename.tr(), autoSelectAllText: true, - value: view.name, + value: view.nameOrDefault, maxLength: 256, onConfirm: (newValue, _) { // can not use bloc here because it has been disposed. diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/view/view_item.dart b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/view/view_item.dart index 6d6611b403..f54fc66c8d 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/view/view_item.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/view/view_item.dart @@ -739,7 +739,7 @@ class _SingleInnerViewItemState extends State { NavigatorTextFieldDialog( title: LocaleKeys.disclosureAction_rename.tr(), autoSelectAllText: true, - value: widget.view.name, + value: widget.view.nameOrDefault, maxLength: 256, onConfirm: (newValue, _) { context.read().add(ViewEvent.rename(newValue)); From 699ea150ce441a4a106f28458f0f4d9a4ddc346a Mon Sep 17 00:00:00 2001 From: Morn Date: Thu, 12 Dec 2024 09:27:24 +0800 Subject: [PATCH 036/576] feat: able to select language for the code block by arrow keys (#6905) (#6964) --- .../code_block_language_selector_test.dart | 75 +++++++++++ .../integration_test/desktop_runner_9.dart | 4 +- .../base/selectable_item_list_menu.dart | 12 +- .../code_block_language_selector.dart | 122 +++++++++++++----- 4 files changed, 176 insertions(+), 37 deletions(-) create mode 100644 frontend/appflowy_flutter/integration_test/desktop/uncategorized/code_block_language_selector_test.dart diff --git a/frontend/appflowy_flutter/integration_test/desktop/uncategorized/code_block_language_selector_test.dart b/frontend/appflowy_flutter/integration_test/desktop/uncategorized/code_block_language_selector_test.dart new file mode 100644 index 0000000000..13cd2b6b3b --- /dev/null +++ b/frontend/appflowy_flutter/integration_test/desktop/uncategorized/code_block_language_selector_test.dart @@ -0,0 +1,75 @@ +import 'package:appflowy/generated/locale_keys.g.dart'; +import 'package:appflowy/plugins/document/presentation/editor_plugins/code_block/code_block_language_selector.dart'; + +import 'package:appflowy_editor/appflowy_editor.dart'; +import 'package:appflowy_editor_plugins/appflowy_editor_plugins.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; + +import '../../shared/base.dart'; +import '../../shared/common_operations.dart'; +import '../../shared/document_test_operations.dart'; +import '../document/document_codeblock_paste_test.dart'; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + testWidgets('Code Block Language Selector Test', (tester) async { + await tester.initializeAppFlowy(); + await tester.tapAnonymousSignInButton(); + + /// create a new document + await tester.createNewPageWithNameUnderParent(); + + /// tap editor to get focus + await tester.tapButton(find.byType(AppFlowyEditor)); + + expect(find.byType(CodeBlockLanguageSelector), findsNothing); + await insertCodeBlockInDocument(tester); + + ///tap button + await tester.hoverOnWidget(find.byType(CodeBlockComponentWidget)); + await tester + .tapButtonWithName(LocaleKeys.document_codeBlock_language_auto.tr()); + expect(find.byType(CodeBlockLanguageSelector), findsOneWidget); + + for (var i = 0; i < 3; ++i) { + await onKey(tester, LogicalKeyboardKey.arrowDown); + } + for (var i = 0; i < 2; ++i) { + await onKey(tester, LogicalKeyboardKey.arrowUp); + } + + await onKey(tester, LogicalKeyboardKey.enter); + + final editorState = tester.editor.getCurrentEditorState(); + final language = + editorState.getNodeAtPath([0])!.attributes['language'].toString(); + expect( + language.toLowerCase(), + defaultCodeBlockSupportedLanguages.first.toLowerCase(), + ); + + await tester.hoverOnWidget(find.byType(CodeBlockComponentWidget)); + await tester.tapButtonWithName(language); + + await onKey(tester, LogicalKeyboardKey.arrowUp); + await onKey(tester, LogicalKeyboardKey.enter); + + expect( + editorState + .getNodeAtPath([0])! + .attributes['language'] + .toString() + .toLowerCase(), + defaultCodeBlockSupportedLanguages.last.toLowerCase(), + ); + }); +} + +Future onKey(WidgetTester tester, LogicalKeyboardKey key) async { + await tester.simulateKeyEvent(key); + await tester.pumpAndSettle(); +} diff --git a/frontend/appflowy_flutter/integration_test/desktop_runner_9.dart b/frontend/appflowy_flutter/integration_test/desktop_runner_9.dart index 819bd26817..3402db1fd9 100644 --- a/frontend/appflowy_flutter/integration_test/desktop_runner_9.dart +++ b/frontend/appflowy_flutter/integration_test/desktop_runner_9.dart @@ -1,6 +1,8 @@ import 'package:integration_test/integration_test.dart'; import 'desktop/uncategorized/tabs_test.dart' as tabs_test; +import 'desktop/uncategorized/code_block_language_selector_test.dart' + as code_language_selector; import 'desktop/first_test/first_test.dart' as first_test; Future main() async { @@ -11,6 +13,6 @@ Future runIntegration9OnDesktop() async { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); first_test.main(); - tabs_test.main(); + code_language_selector.main(); } diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/base/selectable_item_list_menu.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/base/selectable_item_list_menu.dart index b4447e1f01..6691eb2130 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/base/selectable_item_list_menu.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/base/selectable_item_list_menu.dart @@ -1,7 +1,9 @@ +import 'dart:math'; + import 'package:flutter/material.dart'; -import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; +import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; class SelectableItemListMenu extends StatelessWidget { const SelectableItemListMenu({ @@ -10,11 +12,13 @@ class SelectableItemListMenu extends StatelessWidget { required this.selectedIndex, required this.onSelected, this.shrinkWrap = false, + this.controller, }); final List items; final int selectedIndex; final void Function(int) onSelected; + final ItemScrollController? controller; /// shrinkWrapping is useful in cases where you have a list of /// limited amount of items. It will make the list take the minimum @@ -24,9 +28,11 @@ class SelectableItemListMenu extends StatelessWidget { @override Widget build(BuildContext context) { - return ListView.builder( + return ScrollablePositionedList.builder( shrinkWrap: shrinkWrap, itemCount: items.length, + itemScrollController: controller, + initialScrollIndex: max(0, selectedIndex), itemBuilder: (context, index) => SelectableItem( isSelected: index == selectedIndex, item: items[index], @@ -57,7 +63,7 @@ class SelectableItem extends StatelessWidget { item, lineHeight: 1.0, ), - rightIcon: isSelected ? const FlowySvg(FlowySvgs.check_s) : null, + isSelected: isSelected, onTap: onTap, ), ); diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/code_block/code_block_language_selector.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/code_block/code_block_language_selector.dart index a40cc59575..d8d8b5109a 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/code_block/code_block_language_selector.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/code_block/code_block_language_selector.dart @@ -8,7 +8,9 @@ import 'package:easy_localization/easy_localization.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/services.dart'; import 'package:go_router/go_router.dart'; +import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; import 'package:universal_platform/universal_platform.dart'; CodeBlockLanguagePickerBuilder codeBlockLanguagePickerBuilder = ( @@ -19,7 +21,7 @@ CodeBlockLanguagePickerBuilder codeBlockLanguagePickerBuilder = ( onMenuClose, onMenuOpen, }) => - _CodeBlockLanguageSelector( + CodeBlockLanguageSelector( editorState: editorState, language: selectedLanguage, supportedLanguages: supportedLanguages, @@ -28,8 +30,9 @@ CodeBlockLanguagePickerBuilder codeBlockLanguagePickerBuilder = ( onMenuOpen: onMenuOpen, ); -class _CodeBlockLanguageSelector extends StatefulWidget { - const _CodeBlockLanguageSelector({ +class CodeBlockLanguageSelector extends StatefulWidget { + const CodeBlockLanguageSelector({ + super.key, required this.editorState, required this.supportedLanguages, this.language, @@ -46,12 +49,11 @@ class _CodeBlockLanguageSelector extends StatefulWidget { final VoidCallback? onMenuClose; @override - State<_CodeBlockLanguageSelector> createState() => + State createState() => _CodeBlockLanguageSelectorState(); } -class _CodeBlockLanguageSelectorState - extends State<_CodeBlockLanguageSelector> { +class _CodeBlockLanguageSelectorState extends State { final controller = PopoverController(); @override @@ -136,7 +138,8 @@ class _LanguageSelectionPopoverState extends State<_LanguageSelectionPopover> { late List filteredLanguages = widget.supportedLanguages.map((e) => e.capitalize()).toList(); late int selectedIndex = - widget.supportedLanguages.indexOf(widget.language ?? ''); + widget.supportedLanguages.indexOf(widget.language?.toLowerCase() ?? ''); + final ItemScrollController languageListController = ItemScrollController(); @override void initState() { @@ -160,34 +163,87 @@ class _LanguageSelectionPopoverState extends State<_LanguageSelectionPopover> { @override Widget build(BuildContext context) { - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - FlowyTextField( - focusNode: focusNode, - autoFocus: false, - controller: searchController, - hintText: LocaleKeys.document_codeBlock_searchLanguageHint.tr(), - onChanged: (_) => setState(() { - filteredLanguages = widget.supportedLanguages - .where((e) => e.contains(searchController.text.toLowerCase())) - .map((e) => e.capitalize()) - .toList(); - selectedIndex = - widget.supportedLanguages.indexOf(widget.language ?? ''); - }), - ), - const VSpace(8), - Flexible( - child: SelectableItemListMenu( - shrinkWrap: true, - items: filteredLanguages, - selectedIndex: selectedIndex, - onSelected: (index) => - widget.onLanguageSelected(filteredLanguages[index]), + return Shortcuts( + shortcuts: const { + SingleActivator(LogicalKeyboardKey.arrowUp): + _DirectionIntent(AxisDirection.up), + SingleActivator(LogicalKeyboardKey.arrowDown): + _DirectionIntent(AxisDirection.down), + SingleActivator(LogicalKeyboardKey.enter): ActivateIntent(), + }, + child: Actions( + actions: { + _DirectionIntent: CallbackAction<_DirectionIntent>( + onInvoke: (intent) => onArrowKey(intent.direction), ), + ActivateIntent: CallbackAction( + onInvoke: (intent) { + if (selectedIndex < 0) return; + selectLanguage(selectedIndex); + return null; + }, + ), + }, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + FlowyTextField( + focusNode: focusNode, + autoFocus: false, + controller: searchController, + hintText: LocaleKeys.document_codeBlock_searchLanguageHint.tr(), + onChanged: (_) => setState(() { + filteredLanguages = widget.supportedLanguages + .where( + (e) => e.contains(searchController.text.toLowerCase()), + ) + .map((e) => e.capitalize()) + .toList(); + selectedIndex = + widget.supportedLanguages.indexOf(widget.language ?? ''); + }), + ), + const VSpace(8), + Flexible( + child: SelectableItemListMenu( + controller: languageListController, + shrinkWrap: true, + items: filteredLanguages, + selectedIndex: selectedIndex, + onSelected: selectLanguage, + ), + ), + ], ), - ], + ), ); } + + void onArrowKey(AxisDirection direction) { + if (filteredLanguages.isEmpty) return; + final isUp = direction == AxisDirection.up; + final length = filteredLanguages.length; + setState(() { + if (isUp) { + selectedIndex = selectedIndex == 0 ? length - 1 : selectedIndex - 1; + } else { + selectedIndex = selectedIndex == length - 1 ? 0 : selectedIndex + 1; + } + }); + languageListController.scrollTo( + index: selectedIndex, + duration: const Duration(milliseconds: 300), + ); + } + + void selectLanguage(int index) { + widget.onLanguageSelected(filteredLanguages[index]); + } +} + +/// [ScrollIntent] is not working, so using this custom Intent +class _DirectionIntent extends Intent { + const _DirectionIntent(this.direction); + + final AxisDirection direction; } From 3522569f97b3504e701d0dbea795f8b9ff8e0a2f Mon Sep 17 00:00:00 2001 From: Lucas Date: Thu, 12 Dec 2024 10:03:21 +0800 Subject: [PATCH 037/576] feat: support universal link / app link on mobile (#6973) * feat: support universal link/app link * feat: support app link on android --- .../android/app/src/main/AndroidManifest.xml | 8 ++++++-- frontend/appflowy_flutter/ios/Runner/Runner.entitlements | 7 +++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/frontend/appflowy_flutter/android/app/src/main/AndroidManifest.xml b/frontend/appflowy_flutter/android/app/src/main/AndroidManifest.xml index b9a349f2cb..49118a1532 100644 --- a/frontend/appflowy_flutter/android/app/src/main/AndroidManifest.xml +++ b/frontend/appflowy_flutter/android/app/src/main/AndroidManifest.xml @@ -36,7 +36,10 @@ - + + + + - + diff --git a/frontend/appflowy_flutter/ios/Runner/Runner.entitlements b/frontend/appflowy_flutter/ios/Runner/Runner.entitlements index 80b5221de7..e3bc137465 100644 --- a/frontend/appflowy_flutter/ios/Runner/Runner.entitlements +++ b/frontend/appflowy_flutter/ios/Runner/Runner.entitlements @@ -8,5 +8,12 @@ Default + com.apple.developer.associated-domains + + applinks:appflowy.com + applinks:appflowy.io + applinks:test.appflowy.com + applinks:test.appflowy.io + From 555b4b48bb154fd372a2d1d74ed1a93ce40f1c36 Mon Sep 17 00:00:00 2001 From: Richard Shiue <71320345+richardshiue@users.noreply.github.com> Date: Thu, 12 Dec 2024 11:57:00 +0800 Subject: [PATCH 038/576] fix(flutter_desktop): cannot copy ai response with ctrl c (#6976) --- .../plugins/ai_chat/presentation/message/ai_markdown_text.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/message/ai_markdown_text.dart b/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/message/ai_markdown_text.dart index 4e414a81bf..12c9cc7e68 100644 --- a/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/message/ai_markdown_text.dart +++ b/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/message/ai_markdown_text.dart @@ -8,6 +8,7 @@ import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:universal_platform/universal_platform.dart'; import '../chat_editor_style.dart'; @@ -106,7 +107,7 @@ class _AppFlowyEditorMarkdownState extends State<_AppFlowyEditorMarkdown> { shrinkWrap: true, // the editor is not editable in the chat editable: false, - disableKeyboardService: true, + disableKeyboardService: UniversalPlatform.isMobile, editorStyle: editorStyle, editorScrollController: scrollController, blockComponentBuilders: blockBuilders, From 6a6fac7f82f3087b45e4dcd5a10591b24da56789 Mon Sep 17 00:00:00 2001 From: Lucas Date: Thu, 12 Dec 2024 12:50:26 +0800 Subject: [PATCH 039/576] chore: bump version 0.7.8 (#6978) --- frontend/Makefile.toml | 2 +- frontend/appflowy_flutter/pubspec.lock | 4 ++-- frontend/appflowy_flutter/pubspec.yaml | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/frontend/Makefile.toml b/frontend/Makefile.toml index f86cb1befe..4a51b39ba5 100644 --- a/frontend/Makefile.toml +++ b/frontend/Makefile.toml @@ -26,7 +26,7 @@ CARGO_MAKE_EXTEND_WORKSPACE_MAKEFILE = true CARGO_MAKE_CRATE_FS_NAME = "dart_ffi" CARGO_MAKE_CRATE_NAME = "dart-ffi" LIB_NAME = "dart_ffi" -APPFLOWY_VERSION = "0.7.6" +APPFLOWY_VERSION = "0.7.8" FLUTTER_DESKTOP_FEATURES = "dart" PRODUCT_NAME = "AppFlowy" MACOSX_DEPLOYMENT_TARGET = "11.0" diff --git a/frontend/appflowy_flutter/pubspec.lock b/frontend/appflowy_flutter/pubspec.lock index 6f27fb5484..5fad77da82 100644 --- a/frontend/appflowy_flutter/pubspec.lock +++ b/frontend/appflowy_flutter/pubspec.lock @@ -61,8 +61,8 @@ packages: dependency: "direct main" description: path: "." - ref: f32762e - resolved-ref: f32762eae0a703e15ae681947ad1ff110d2e477a + ref: "954ffa5" + resolved-ref: "954ffa538e0788a5d25dc16555ff80a3afae5ea3" url: "https://github.com/AppFlowy-IO/appflowy-editor.git" source: git version: "4.0.0" diff --git a/frontend/appflowy_flutter/pubspec.yaml b/frontend/appflowy_flutter/pubspec.yaml index e452bdab82..c0ab20ff64 100644 --- a/frontend/appflowy_flutter/pubspec.yaml +++ b/frontend/appflowy_flutter/pubspec.yaml @@ -4,7 +4,7 @@ description: Bring projects, wikis, and teams together with AI. AppFlowy is an your data. The best open source alternative to Notion. publish_to: "none" -version: 0.7.6 +version: 0.7.8 environment: flutter: ">=3.22.0" @@ -173,7 +173,7 @@ dependency_overrides: appflowy_editor: git: url: https://github.com/AppFlowy-IO/appflowy-editor.git - ref: "f32762e" + ref: "954ffa5" appflowy_editor_plugins: git: From 25f9bee963599283063377f17ef8d13f3f126fba Mon Sep 17 00:00:00 2001 From: Richard Shiue <71320345+richardshiue@users.noreply.github.com> Date: Fri, 13 Dec 2024 10:20:56 +0800 Subject: [PATCH 040/576] fix: launch review issues 0.7.8 (#6983) * chore: mention popup height match design * chore: adding while scrolling through mention page popup * chore: don't add @ and restart creating mention when already in the middle of one * fix: hide orphan views from mention page * fix: arrow navigation wrap mention page popup breaks bloc --- .../application/chat_input_control_cubit.dart | 8 ++-- .../chat_mention_page_bottom_sheet.dart | 5 +-- .../chat_input/chat_mention_page_menu.dart | 15 ++++--- .../chat_input/desktop_ai_prompt_input.dart | 3 ++ .../chat_input/mobile_ai_prompt_input.dart | 2 +- .../view_title/view_title_bar_bloc.dart | 42 ++++++++++--------- .../presentation/widgets/view_title_bar.dart | 3 +- 7 files changed, 41 insertions(+), 37 deletions(-) diff --git a/frontend/appflowy_flutter/lib/plugins/ai_chat/application/chat_input_control_cubit.dart b/frontend/appflowy_flutter/lib/plugins/ai_chat/application/chat_input_control_cubit.dart index c663699e2c..1dfa75755a 100644 --- a/frontend/appflowy_flutter/lib/plugins/ai_chat/application/chat_input_control_cubit.dart +++ b/frontend/appflowy_flutter/lib/plugins/ai_chat/application/chat_input_control_cubit.dart @@ -44,9 +44,11 @@ class ChatInputControlCubit extends Cubit { void refreshViews() async { final newViews = await ViewBackendService.getAllViews().fold( - (result) => result.items - .where((v) => v.layout.isDocumentView && v.parentViewId.isNotEmpty) - .toList(), + (result) { + return result.items + .where((v) => v.layout.isDocumentView && v.parentViewId != v.id) + .toList(); + }, (err) { Log.error(err); return []; diff --git a/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_input/chat_mention_page_bottom_sheet.dart b/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_input/chat_mention_page_bottom_sheet.dart index d12ca5abd6..cb2e7248d4 100644 --- a/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_input/chat_mention_page_bottom_sheet.dart +++ b/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_input/chat_mention_page_bottom_sheet.dart @@ -2,7 +2,6 @@ import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/mobile/presentation/base/flowy_search_text_field.dart'; import 'package:appflowy/mobile/presentation/bottom_sheet/show_mobile_bottom_sheet.dart'; import 'package:appflowy/plugins/base/drag_handler.dart'; -import 'package:appflowy/workspace/application/view/view_ext.dart'; import 'package:appflowy/workspace/application/view/view_service.dart'; import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart'; import 'package:easy_localization/easy_localization.dart'; @@ -13,10 +12,8 @@ import 'chat_mention_page_menu.dart'; Future showPageSelectorSheet( BuildContext context, { - bool Function(ViewPB view)? filter, + required bool Function(ViewPB view) filter, }) async { - filter ??= (v) => !v.isSpace && v.parentViewId.isNotEmpty; - return showMobileBottomSheet( context, backgroundColor: Theme.of(context).colorScheme.surface, diff --git a/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_input/chat_mention_page_menu.dart b/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_input/chat_mention_page_menu.dart index 10ce04f256..180879b2b7 100644 --- a/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_input/chat_mention_page_menu.dart +++ b/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_input/chat_mention_page_menu.dart @@ -15,7 +15,7 @@ import 'package:scroll_to_index/scroll_to_index.dart'; const double _itemHeight = 44.0; const double _noPageHeight = 20.0; const double _fixedWidth = 360.0; -const double _maxHeight = 600.0; +const double _maxHeight = 328.0; class ChatInputAnchor { ChatInputAnchor(this.anchorKey, this.layerLink); @@ -154,8 +154,10 @@ class ChatMentionPageList extends StatefulWidget { } class _ChatMentionPageListState extends State { - final autoScrollController = AutoScrollController( + final autoScrollController = SimpleAutoScrollController( suggestedRowHeight: _itemHeight, + beginGetter: (rect) => rect.top + 8.0, + endGetter: (rect) => rect.bottom - 8.0, ); @override @@ -169,8 +171,8 @@ class _ChatMentionPageListState extends State { return BlocConsumer( listenWhen: (previous, current) { return previous.maybeWhen( - ready: (pVisibleViews, pFocusedViewIndex) => current.maybeWhen( - ready: (cIsibleViews, cFocusedViewIndex) => + ready: (_, pFocusedViewIndex) => current.maybeWhen( + ready: (_, cFocusedViewIndex) => pFocusedViewIndex != cFocusedViewIndex, orElse: () => false, ), @@ -226,8 +228,6 @@ class _ChatMentionPageListState extends State { return ListView.builder( shrinkWrap: true, - cacheExtent: 1, - addAutomaticKeepAlives: false, controller: autoScrollController, padding: const EdgeInsets.all(8.0), itemCount: views.length, @@ -344,8 +344,7 @@ class MentionViewTitleAndAncestors extends StatelessWidget { @override Widget build(BuildContext context) { return BlocProvider( - create: (_) => - ViewTitleBarBloc(view: view)..add(const ViewTitleBarEvent.initial()), + create: (_) => ViewTitleBarBloc(view: view), child: BlocBuilder( builder: (context, state) { final nonEmptyName = view.name.isEmpty diff --git a/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_input/desktop_ai_prompt_input.dart b/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_input/desktop_ai_prompt_input.dart index c31b7f37ca..b37d2e10d2 100644 --- a/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_input/desktop_ai_prompt_input.dart +++ b/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_input/desktop_ai_prompt_input.dart @@ -519,6 +519,9 @@ class _PromptBottomActions extends StatelessWidget { iconSize: DesktopAIPromptSizes.actionBarIconSize, buttonSize: DesktopAIPromptSizes.actionBarButtonSize, onTap: () { + if (overlayController.isShowing) { + return; + } if (!focusNode.hasFocus) { focusNode.requestFocus(); } diff --git a/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_input/mobile_ai_prompt_input.dart b/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_input/mobile_ai_prompt_input.dart index f7a657bc18..28b81b7659 100644 --- a/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_input/mobile_ai_prompt_input.dart +++ b/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_input/mobile_ai_prompt_input.dart @@ -198,7 +198,7 @@ class _MobileAIPromptInputState extends State { context, filter: (view) => view.layout.isDocumentView && - view.parentViewId.isNotEmpty && + view.parentViewId != view.id && !inputControlCubit.selectedViewIds.contains(view.id), ); if (selectedView != null) { diff --git a/frontend/appflowy_flutter/lib/workspace/application/view_title/view_title_bar_bloc.dart b/frontend/appflowy_flutter/lib/workspace/application/view_title/view_title_bar_bloc.dart index 12e22a4d1d..1530c96d32 100644 --- a/frontend/appflowy_flutter/lib/workspace/application/view_title/view_title_bar_bloc.dart +++ b/frontend/appflowy_flutter/lib/workspace/application/view_title/view_title_bar_bloc.dart @@ -10,27 +10,9 @@ part 'view_title_bar_bloc.freezed.dart'; class ViewTitleBarBloc extends Bloc { ViewTitleBarBloc({required this.view}) : super(ViewTitleBarState.initial()) { - trashService = TrashService(); - viewListener = ViewListener(viewId: view.id) - ..start( - onViewChildViewsUpdated: (_) => add(const ViewTitleBarEvent.reload()), - ); - trashListener = TrashListener() - ..start( - trashUpdated: (trashOrFailed) { - final trash = trashOrFailed.toNullable(); - if (trash != null) { - add(ViewTitleBarEvent.trashUpdated(trash: trash)); - } - }, - ); - on( (event, emit) async { await event.when( - initial: () async { - add(const ViewTitleBarEvent.reload()); - }, reload: () async { final List ancestors = await ViewBackendService.getViewAncestors(view.id).fold( @@ -53,6 +35,29 @@ class ViewTitleBarBloc extends Bloc { ); }, ); + + trashService = TrashService(); + viewListener = ViewListener(viewId: view.id) + ..start( + onViewChildViewsUpdated: (_) { + if (!isClosed) { + add(const ViewTitleBarEvent.reload()); + } + }, + ); + trashListener = TrashListener() + ..start( + trashUpdated: (trashOrFailed) { + final trash = trashOrFailed.toNullable(); + if (trash != null && !isClosed) { + add(ViewTitleBarEvent.trashUpdated(trash: trash)); + } + }, + ); + + if (!isClosed) { + add(const ViewTitleBarEvent.reload()); + } } final ViewPB view; @@ -70,7 +75,6 @@ class ViewTitleBarBloc extends Bloc { @freezed class ViewTitleBarEvent with _$ViewTitleBarEvent { - const factory ViewTitleBarEvent.initial() = Initial; const factory ViewTitleBarEvent.reload() = Reload; const factory ViewTitleBarEvent.trashUpdated({ required List trash, diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/widgets/view_title_bar.dart b/frontend/appflowy_flutter/lib/workspace/presentation/widgets/view_title_bar.dart index b7691f4239..63b498c2f2 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/widgets/view_title_bar.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/widgets/view_title_bar.dart @@ -28,8 +28,7 @@ class ViewTitleBar extends StatelessWidget { @override Widget build(BuildContext context) { return BlocProvider( - create: (_) => - ViewTitleBarBloc(view: view)..add(const ViewTitleBarEvent.initial()), + create: (_) => ViewTitleBarBloc(view: view), child: BlocBuilder( builder: (context, state) { final ancestors = state.ancestors; From ebb1b6dffb0c6b4248c24b8429f1c6b162cd62f2 Mon Sep 17 00:00:00 2001 From: "Kilu.He" <108015703+qinluhe@users.noreply.github.com> Date: Fri, 13 Dec 2024 10:34:19 +0800 Subject: [PATCH 041/576] fix: simple table bugs (#6981) --- .../appflowy_web_app/src/application/types.ts | 220 +++++++++--------- .../blocks/simple-table/SimpleTable.hooks.ts | 60 +++++ .../blocks/simple-table/SimpleTable.tsx | 21 +- .../blocks/simple-table/SimpleTableCell.tsx | 16 +- .../blocks/simple-table/SimpleTableRow.tsx | 5 +- .../components/blocks/simple-table/const.ts | 1 + .../blocks/simple-table/simple-table.scss | 15 +- 7 files changed, 211 insertions(+), 127 deletions(-) create mode 100644 frontend/appflowy_web_app/src/components/editor/components/blocks/simple-table/SimpleTable.hooks.ts create mode 100644 frontend/appflowy_web_app/src/components/editor/components/blocks/simple-table/const.ts diff --git a/frontend/appflowy_web_app/src/application/types.ts b/frontend/appflowy_web_app/src/application/types.ts index 9ccd1da92b..53425f821a 100644 --- a/frontend/appflowy_web_app/src/application/types.ts +++ b/frontend/appflowy_web_app/src/application/types.ts @@ -150,13 +150,13 @@ export enum TableAlignType { } export interface SimpleTableData extends BlockData { - column_widths: Record; - enable_header_row: boolean; - row_colors: Record; - enable_header_column: boolean; - column_aligns: Record; - column_colors: Record; - row_aligns: Record; + column_widths?: Record; + enable_header_row?: boolean; + row_colors?: Record; + enable_header_column?: boolean; + column_aligns?: Record; + column_colors?: Record; + row_aligns?: Record; } export interface TableCellBlockData extends BlockData { @@ -326,152 +326,152 @@ export enum YjsDatabaseKey { export interface YDoc extends Y.Doc { // eslint-disable-next-line @typescript-eslint/no-explicit-any - getMap (key: YjsEditorKey.data_section): YSharedRoot | any; + getMap(key: YjsEditorKey.data_section): YSharedRoot | any; } export interface YDatabaseRow extends Y.Map { - get (key: YjsDatabaseKey.id): RowId; + get(key: YjsDatabaseKey.id): RowId; - get (key: YjsDatabaseKey.height): string; + get(key: YjsDatabaseKey.height): string; - get (key: YjsDatabaseKey.visibility): boolean; + get(key: YjsDatabaseKey.visibility): boolean; - get (key: YjsDatabaseKey.cells): YDatabaseCells; + get(key: YjsDatabaseKey.cells): YDatabaseCells; - get (key: YjsDatabaseKey.created_at): CreatedAt; + get(key: YjsDatabaseKey.created_at): CreatedAt; - get (key: YjsDatabaseKey.last_modified): LastModified; + get(key: YjsDatabaseKey.last_modified): LastModified; } export interface YDatabaseCells extends Y.Map { - get (key: FieldId): YDatabaseCell; + get(key: FieldId): YDatabaseCell; } export type EndTimestamp = string; export type ReminderId = string; export interface YDatabaseCell extends Y.Map { - get (key: YjsDatabaseKey.created_at): CreatedAt; + get(key: YjsDatabaseKey.created_at): CreatedAt; - get (key: YjsDatabaseKey.last_modified): LastModified; + get(key: YjsDatabaseKey.last_modified): LastModified; - get (key: YjsDatabaseKey.field_type): string; + get(key: YjsDatabaseKey.field_type): string; - get (key: YjsDatabaseKey.data): object | string | boolean | number; + get(key: YjsDatabaseKey.data): object | string | boolean | number; - get (key: YjsDatabaseKey.end_timestamp): EndTimestamp; + get(key: YjsDatabaseKey.end_timestamp): EndTimestamp; - get (key: YjsDatabaseKey.include_time): boolean; + get(key: YjsDatabaseKey.include_time): boolean; // eslint-disable-next-line @typescript-eslint/unified-signatures - get (key: YjsDatabaseKey.is_range): boolean; + get(key: YjsDatabaseKey.is_range): boolean; - get (key: YjsDatabaseKey.reminder_id): ReminderId; + get(key: YjsDatabaseKey.reminder_id): ReminderId; } export interface YSharedRoot extends Y.Map { - get (key: YjsEditorKey.document): YDocument; + get(key: YjsEditorKey.document): YDocument; - get (key: YjsEditorKey.folder): YFolder; + get(key: YjsEditorKey.folder): YFolder; - get (key: YjsEditorKey.database): YDatabase; + get(key: YjsEditorKey.database): YDatabase; - get (key: YjsEditorKey.database_row): YDatabaseRow; + get(key: YjsEditorKey.database_row): YDatabaseRow; } export interface YFolder extends Y.Map { - get (key: YjsFolderKey.views): YViews; + get(key: YjsFolderKey.views): YViews; // eslint-disable-next-line @typescript-eslint/unified-signatures - get (key: YjsFolderKey.meta): YFolderMeta; + get(key: YjsFolderKey.meta): YFolderMeta; // eslint-disable-next-line @typescript-eslint/unified-signatures - get (key: YjsFolderKey.relation): YFolderRelation; + get(key: YjsFolderKey.relation): YFolderRelation; // eslint-disable-next-line @typescript-eslint/unified-signatures - get (key: YjsFolderKey.section): YFolderSection; + get(key: YjsFolderKey.section): YFolderSection; } export interface YViews extends Y.Map { - get (key: ViewId): YView; + get(key: ViewId): YView; } export interface YView extends Y.Map { - get (key: YjsFolderKey.id): ViewId; + get(key: YjsFolderKey.id): ViewId; - get (key: YjsFolderKey.bid): string; + get(key: YjsFolderKey.bid): string; // eslint-disable-next-line @typescript-eslint/unified-signatures - get (key: YjsFolderKey.name): string; + get(key: YjsFolderKey.name): string; // eslint-disable-next-line @typescript-eslint/unified-signatures - get (key: YjsFolderKey.icon | YjsFolderKey.extra): string; + get(key: YjsFolderKey.icon | YjsFolderKey.extra): string; // eslint-disable-next-line @typescript-eslint/unified-signatures - get (key: YjsFolderKey.layout): string; + get(key: YjsFolderKey.layout): string; } export interface YFolderRelation extends Y.Map { - get (key: ViewId): Y.Array; + get(key: ViewId): Y.Array; } export interface YFolderMeta extends Y.Map { - get (key: YjsFolderKey.current_view | YjsFolderKey.current_workspace): string; + get(key: YjsFolderKey.current_view | YjsFolderKey.current_workspace): string; } export interface YFolderSection extends Y.Map { - get (key: YjsFolderKey.favorite | YjsFolderKey.private | YjsFolderKey.recent | YjsFolderKey.trash): YFolderSectionItem; + get(key: YjsFolderKey.favorite | YjsFolderKey.private | YjsFolderKey.recent | YjsFolderKey.trash): YFolderSectionItem; } export interface YFolderSectionItem extends Y.Map { - get (key: string): Y.Array; + get(key: string): Y.Array; } export interface YDocument extends Y.Map { - get (key: YjsEditorKey.blocks | YjsEditorKey.page_id | YjsEditorKey.meta): YBlocks | YMeta | string; + get(key: YjsEditorKey.blocks | YjsEditorKey.page_id | YjsEditorKey.meta): YBlocks | YMeta | string; } export interface YBlocks extends Y.Map { - get (key: BlockId): YBlock; + get(key: BlockId): YBlock; } export interface YBlock extends Y.Map { - get (key: YjsEditorKey.block_id | YjsEditorKey.block_parent): BlockId; + get(key: YjsEditorKey.block_id | YjsEditorKey.block_parent): BlockId; - get (key: YjsEditorKey.block_type): BlockType; + get(key: YjsEditorKey.block_type): BlockType; - get (key: YjsEditorKey.block_data): string; + get(key: YjsEditorKey.block_data): string; - get (key: YjsEditorKey.block_children): ChildrenId; + get(key: YjsEditorKey.block_children): ChildrenId; - get (key: YjsEditorKey.block_external_id): ExternalId; + get(key: YjsEditorKey.block_external_id): ExternalId; } export interface YMeta extends Y.Map { - get (key: YjsEditorKey.children_map | YjsEditorKey.text_map): YChildrenMap | YTextMap; + get(key: YjsEditorKey.children_map | YjsEditorKey.text_map): YChildrenMap | YTextMap; } export interface YChildrenMap extends Y.Map { - get (key: ChildrenId): Y.Array; + get(key: ChildrenId): Y.Array; } export interface YTextMap extends Y.Map { - get (key: ExternalId): Y.Text; + get(key: ExternalId): Y.Text; } export interface YDatabase extends Y.Map { - get (key: YjsDatabaseKey.views): YDatabaseViews; + get(key: YjsDatabaseKey.views): YDatabaseViews; - get (key: YjsDatabaseKey.metas): YDatabaseMetas; + get(key: YjsDatabaseKey.metas): YDatabaseMetas; - get (key: YjsDatabaseKey.fields): YDatabaseFields; + get(key: YjsDatabaseKey.fields): YDatabaseFields; - get (key: YjsDatabaseKey.id): string; + get(key: YjsDatabaseKey.id): string; } export interface YDatabaseViews extends Y.Map { - get (key: ViewId): YDatabaseView; + get(key: ViewId): YDatabaseView; } export type DatabaseId = string; @@ -487,32 +487,32 @@ export enum DatabaseViewLayout { } export interface YDatabaseView extends Y.Map { - get (key: YjsDatabaseKey.database_id): DatabaseId; + get(key: YjsDatabaseKey.database_id): DatabaseId; - get (key: YjsDatabaseKey.name): string; + get(key: YjsDatabaseKey.name): string; - get (key: YjsDatabaseKey.created_at): CreatedAt; + get(key: YjsDatabaseKey.created_at): CreatedAt; - get (key: YjsDatabaseKey.modified_at): ModifiedAt; + get(key: YjsDatabaseKey.modified_at): ModifiedAt; // eslint-disable-next-line @typescript-eslint/unified-signatures - get (key: YjsDatabaseKey.layout): string; + get(key: YjsDatabaseKey.layout): string; - get (key: YjsDatabaseKey.layout_settings): YDatabaseLayoutSettings; + get(key: YjsDatabaseKey.layout_settings): YDatabaseLayoutSettings; - get (key: YjsDatabaseKey.filters): YDatabaseFilters; + get(key: YjsDatabaseKey.filters): YDatabaseFilters; - get (key: YjsDatabaseKey.groups): YDatabaseGroups; + get(key: YjsDatabaseKey.groups): YDatabaseGroups; - get (key: YjsDatabaseKey.sorts): YDatabaseSorts; + get(key: YjsDatabaseKey.sorts): YDatabaseSorts; - get (key: YjsDatabaseKey.field_settings): YDatabaseFieldSettings; + get(key: YjsDatabaseKey.field_settings): YDatabaseFieldSettings; - get (key: YjsDatabaseKey.field_orders): YDatabaseFieldOrders; + get(key: YjsDatabaseKey.field_orders): YDatabaseFieldOrders; - get (key: YjsDatabaseKey.row_orders): YDatabaseRowOrders; + get(key: YjsDatabaseKey.row_orders): YDatabaseRowOrders; - get (key: YjsDatabaseKey.calculations): YDatabaseCalculations; + get(key: YjsDatabaseKey.calculations): YDatabaseCalculations; } export type YDatabaseFieldOrders = Y.Array; // [ { id: FieldId } ] @@ -533,131 +533,131 @@ export type GroupId = string; export interface YDatabaseLayoutSettings extends Y.Map { // DatabaseViewLayout.Board - get (key: '1'): YDatabaseBoardLayoutSetting; + get(key: '1'): YDatabaseBoardLayoutSetting; // DatabaseViewLayout.Calendar - get (key: '2'): YDatabaseCalendarLayoutSetting; + get(key: '2'): YDatabaseCalendarLayoutSetting; } export interface YDatabaseBoardLayoutSetting extends Y.Map { - get (key: YjsDatabaseKey.hide_ungrouped_column | YjsDatabaseKey.collapse_hidden_groups): boolean; + get(key: YjsDatabaseKey.hide_ungrouped_column | YjsDatabaseKey.collapse_hidden_groups): boolean; } export interface YDatabaseCalendarLayoutSetting extends Y.Map { - get (key: YjsDatabaseKey.first_day_of_week | YjsDatabaseKey.field_id | YjsDatabaseKey.layout_ty): string; + get(key: YjsDatabaseKey.first_day_of_week | YjsDatabaseKey.field_id | YjsDatabaseKey.layout_ty): string; - get (key: YjsDatabaseKey.show_week_numbers | YjsDatabaseKey.show_weekends): boolean; + get(key: YjsDatabaseKey.show_week_numbers | YjsDatabaseKey.show_weekends): boolean; } export interface YDatabaseGroup extends Y.Map { - get (key: YjsDatabaseKey.id): GroupId; + get(key: YjsDatabaseKey.id): GroupId; - get (key: YjsDatabaseKey.field_id): FieldId; + get(key: YjsDatabaseKey.field_id): FieldId; // eslint-disable-next-line @typescript-eslint/unified-signatures - get (key: YjsDatabaseKey.content): string; + get(key: YjsDatabaseKey.content): string; - get (key: YjsDatabaseKey.groups): YDatabaseGroupColumns; + get(key: YjsDatabaseKey.groups): YDatabaseGroupColumns; } export type YDatabaseGroupColumns = Y.Array; export interface YDatabaseGroupColumn extends Y.Map { - get (key: YjsDatabaseKey.id): string; + get(key: YjsDatabaseKey.id): string; - get (key: YjsDatabaseKey.visible): boolean; + get(key: YjsDatabaseKey.visible): boolean; } export interface YDatabaseRowOrder extends Y.Map { - get (key: YjsDatabaseKey.id): SortId; + get(key: YjsDatabaseKey.id): SortId; - get (key: YjsDatabaseKey.height): number; + get(key: YjsDatabaseKey.height): number; } export interface YDatabaseSort extends Y.Map { - get (key: YjsDatabaseKey.id): SortId; + get(key: YjsDatabaseKey.id): SortId; - get (key: YjsDatabaseKey.field_id): FieldId; + get(key: YjsDatabaseKey.field_id): FieldId; - get (key: YjsDatabaseKey.condition): string; + get(key: YjsDatabaseKey.condition): string; } export type FilterId = string; export interface YDatabaseFilter extends Y.Map { - get (key: YjsDatabaseKey.id): FilterId; + get(key: YjsDatabaseKey.id): FilterId; - get (key: YjsDatabaseKey.field_id): FieldId; + get(key: YjsDatabaseKey.field_id): FieldId; - get (key: YjsDatabaseKey.type | YjsDatabaseKey.condition | YjsDatabaseKey.content | YjsDatabaseKey.filter_type): string; + get(key: YjsDatabaseKey.type | YjsDatabaseKey.condition | YjsDatabaseKey.content | YjsDatabaseKey.filter_type): string; } export interface YDatabaseCalculation extends Y.Map { - get (key: YjsDatabaseKey.field_id): FieldId; + get(key: YjsDatabaseKey.field_id): FieldId; - get (key: YjsDatabaseKey.id | YjsDatabaseKey.type | YjsDatabaseKey.calculation_value): string; + get(key: YjsDatabaseKey.id | YjsDatabaseKey.type | YjsDatabaseKey.calculation_value): string; } export interface YDatabaseFieldSettings extends Y.Map { - get (key: FieldId): YDatabaseFieldSetting; + get(key: FieldId): YDatabaseFieldSetting; } export interface YDatabaseFieldSetting extends Y.Map { - get (key: YjsDatabaseKey.visibility): string; + get(key: YjsDatabaseKey.visibility): string; - get (key: YjsDatabaseKey.wrap): boolean; + get(key: YjsDatabaseKey.wrap): boolean; // eslint-disable-next-line @typescript-eslint/unified-signatures - get (key: YjsDatabaseKey.width): string; + get(key: YjsDatabaseKey.width): string; } export interface YDatabaseMetas extends Y.Map { - get (key: YjsDatabaseKey.iid): string; + get(key: YjsDatabaseKey.iid): string; } export interface YDatabaseFields extends Y.Map { - get (key: FieldId): YDatabaseField; + get(key: FieldId): YDatabaseField; } export interface YDatabaseField extends Y.Map { - get (key: YjsDatabaseKey.name): string; + get(key: YjsDatabaseKey.name): string; - get (key: YjsDatabaseKey.id): FieldId; + get(key: YjsDatabaseKey.id): FieldId; // eslint-disable-next-line @typescript-eslint/unified-signatures - get (key: YjsDatabaseKey.icon): string; + get(key: YjsDatabaseKey.icon): string; // eslint-disable-next-line @typescript-eslint/unified-signatures - get (key: YjsDatabaseKey.type): string; + get(key: YjsDatabaseKey.type): string; - get (key: YjsDatabaseKey.type_option): YDatabaseFieldTypeOption; + get(key: YjsDatabaseKey.type_option): YDatabaseFieldTypeOption; - get (key: YjsDatabaseKey.is_primary): boolean; + get(key: YjsDatabaseKey.is_primary): boolean; - get (key: YjsDatabaseKey.last_modified): LastModified; + get(key: YjsDatabaseKey.last_modified): LastModified; } export interface YDatabaseFieldTypeOption extends Y.Map { // key is the field type - get (key: string): YMapFieldTypeOption; + get(key: string): YMapFieldTypeOption; } export interface YMapFieldTypeOption extends Y.Map { - get (key: YjsDatabaseKey.content): string; + get(key: YjsDatabaseKey.content): string; // eslint-disable-next-line @typescript-eslint/unified-signatures - get (key: YjsDatabaseKey.data): string; + get(key: YjsDatabaseKey.data): string; // eslint-disable-next-line @typescript-eslint/unified-signatures - get (key: YjsDatabaseKey.time_format): string; + get(key: YjsDatabaseKey.time_format): string; // eslint-disable-next-line @typescript-eslint/unified-signatures - get (key: YjsDatabaseKey.date_format): string; + get(key: YjsDatabaseKey.date_format): string; - get (key: YjsDatabaseKey.database_id): DatabaseId; + get(key: YjsDatabaseKey.database_id): DatabaseId; // eslint-disable-next-line @typescript-eslint/unified-signatures - get (key: YjsDatabaseKey.format): string; + get(key: YjsDatabaseKey.format): string; } export enum Types { diff --git a/frontend/appflowy_web_app/src/components/editor/components/blocks/simple-table/SimpleTable.hooks.ts b/frontend/appflowy_web_app/src/components/editor/components/blocks/simple-table/SimpleTable.hooks.ts new file mode 100644 index 0000000000..8a29ba72e4 --- /dev/null +++ b/frontend/appflowy_web_app/src/components/editor/components/blocks/simple-table/SimpleTable.hooks.ts @@ -0,0 +1,60 @@ +import { SimpleTableNode } from '@/components/editor/editor.type'; +import { createHotkey, HOT_KEY_NAME } from '@/utils/hotkeys'; +import { useEffect, useState } from 'react'; +import { Editor, Path, Range } from 'slate'; +import { ReactEditor, useSlateStatic } from 'slate-react'; + +export function useSimpleTable(node: SimpleTableNode) { + const editor = useSlateStatic(); + const [inCurrentTable, setInCurrentTable] = useState(false); + const [isIntersection, setIsIntersection] = useState(false); + + useEffect(() => { + const { onChange } = editor; + + editor.onChange = () => { + onChange(); + const { selection } = editor; + + if (!selection) return; + const path = ReactEditor.findPath(editor, node); + const [start, end] = Editor.edges(editor, selection); + const isAncestor = Path.isAncestor(path, end.path) && Path.isAncestor(path, start.path); + const isIntersection = !isAncestor && Range.intersection(selection, Editor.range(editor, path)); + + setIsIntersection(!!isIntersection); + setInCurrentTable(isAncestor); + }; + + return () => { + editor.onChange = onChange; + }; + }, [editor, node]); + + useEffect(() => { + const editorDom = ReactEditor.toDOMNode(editor, editor); + + const handleKeydown = (event: KeyboardEvent) => { + if (!inCurrentTable) return; + + switch (true) { + case createHotkey(HOT_KEY_NAME.UP)(event): { + event.stopPropagation(); + event.preventDefault(); + } + + } + + }; + + editorDom.addEventListener('keydown', handleKeydown); + + return () => { + editorDom.removeEventListener('keydown', handleKeydown); + }; + }, [editor, inCurrentTable]); + + return { + isIntersection, + }; +} \ No newline at end of file diff --git a/frontend/appflowy_web_app/src/components/editor/components/blocks/simple-table/SimpleTable.tsx b/frontend/appflowy_web_app/src/components/editor/components/blocks/simple-table/SimpleTable.tsx index da62087f27..4c75ca1039 100644 --- a/frontend/appflowy_web_app/src/components/editor/components/blocks/simple-table/SimpleTable.tsx +++ b/frontend/appflowy_web_app/src/components/editor/components/blocks/simple-table/SimpleTable.tsx @@ -1,11 +1,12 @@ +import { useSimpleTable } from '@/components/editor/components/blocks/simple-table/SimpleTable.hooks'; import { EditorElementProps, SimpleTableNode, SimpleTableRowNode } from '@/components/editor/editor.type'; import { useEditorContext } from '@/components/editor/EditorContext'; import TableContainer from '@/components/editor/components/table-container/TableContainer'; import isEqual from 'lodash-es/isEqual'; import React, { forwardRef, memo, useMemo } from 'react'; import './simple-table.scss'; - -const MIN_WIDTH = 120; +import { MIN_WIDTH } from '@/components/editor/components/blocks/simple-table/const'; +// import { useReadOnly } from 'slate-react'; const SimpleTable = memo( forwardRef>(({ @@ -29,8 +30,8 @@ const SimpleTable = memo( const columns = useMemo(() => { return Array.from({ length: columnCount }, (_, index) => { - const width = column_widths[index] || MIN_WIDTH; - const bgColor = column_colors[index] || 'transparent'; + const width = column_widths?.[index] || MIN_WIDTH; + const bgColor = column_colors?.[index] || 'transparent'; return { width, bgColor }; }); @@ -46,9 +47,10 @@ const SimpleTable = memo( ))} ; }, [columns]); + const { isIntersection } = useSimpleTable(node); const className = useMemo(() => { - const classList = ['simple-table', 'appflowy-scroller']; + const classList = ['simple-table', 'appflowy-scroller', 'select-none']; if (classNameProp) { classList.push(classNameProp); @@ -62,14 +64,21 @@ const SimpleTable = memo( classList.push('enable-header-row'); } + if (isIntersection) { + classList.push('selected'); + } + return classList.join(' '); - }, [classNameProp, enable_header_column, enable_header_row]); + }, [classNameProp, enable_header_column, enable_header_row, isIntersection]); + + // const readOnly = useReadOnly(); return (
>(({ @@ -49,20 +50,24 @@ const SimpleTableCell = return (parentElement.children as Element[]).findIndex((n) => n.blockId === node.blockId); }, [node, row]); - const { horizontalAlign, bgColor } = useMemo(() => { + const { horizontalAlign, bgColor, width } = useMemo(() => { if (!table || !row) return { bgColor: undefined, horizontalAlign: undefined, + width: undefined, }; const [parentElement] = table; - const horizontalAlign = (parentElement as SimpleTableNode).data.column_aligns[colIndex]; - const bgColor = (parentElement as SimpleTableNode).data.column_colors[colIndex]; + const horizontalAlign = (parentElement as SimpleTableNode).data.column_aligns?.[colIndex]; + const bgColor = (parentElement as SimpleTableNode).data.column_colors?.[colIndex]; + + const width = (parentElement as SimpleTableNode).data.column_widths?.[colIndex] || MIN_WIDTH; return { horizontalAlign, bgColor, + width, }; }, [colIndex, row, table]); @@ -79,9 +84,12 @@ const SimpleTableCell = style={{ ...attributes.style, backgroundColor: bgColor ? renderColor(bgColor) : undefined, + maxWidth: width ? `${width}px` : undefined, }} > -
+
{children}
diff --git a/frontend/appflowy_web_app/src/components/editor/components/blocks/simple-table/SimpleTableRow.tsx b/frontend/appflowy_web_app/src/components/editor/components/blocks/simple-table/SimpleTableRow.tsx index 25c0ee614f..0a48c96f68 100644 --- a/frontend/appflowy_web_app/src/components/editor/components/blocks/simple-table/SimpleTableRow.tsx +++ b/frontend/appflowy_web_app/src/components/editor/components/blocks/simple-table/SimpleTableRow.tsx @@ -45,8 +45,8 @@ const SimpleTableRow = const [parentElement] = parent; return { - align: (parentElement as SimpleTableNode).data.row_aligns[index], - bgColor: (parentElement as SimpleTableNode).data.row_colors[index], + align: (parentElement as SimpleTableNode).data.row_aligns?.[index], + bgColor: (parentElement as SimpleTableNode).data.row_colors?.[index], }; }, [index, parent]); @@ -57,6 +57,7 @@ const SimpleTableRow = ref={ref} {...attributes} data-table-row={blockId} + data-table-row-horizontal-align={align?.toLowerCase()} style={{ ...attributes.style, diff --git a/frontend/appflowy_web_app/src/components/editor/components/blocks/simple-table/const.ts b/frontend/appflowy_web_app/src/components/editor/components/blocks/simple-table/const.ts new file mode 100644 index 0000000000..d3edec50af --- /dev/null +++ b/frontend/appflowy_web_app/src/components/editor/components/blocks/simple-table/const.ts @@ -0,0 +1 @@ +export const MIN_WIDTH = 120; \ No newline at end of file diff --git a/frontend/appflowy_web_app/src/components/editor/components/blocks/simple-table/simple-table.scss b/frontend/appflowy_web_app/src/components/editor/components/blocks/simple-table/simple-table.scss index c992bf6d4a..cf648be5e1 100644 --- a/frontend/appflowy_web_app/src/components/editor/components/blocks/simple-table/simple-table.scss +++ b/frontend/appflowy_web_app/src/components/editor/components/blocks/simple-table/simple-table.scss @@ -1,15 +1,13 @@ .simple-table { - font-size: 14px; - &.enable-header-column { tr [data-block-type="simple_table_cell"]:first-child { - @apply font-semibold; + @apply font-semibold bg-fill-list-hover; } } &.enable-header-row { tr:first-child > [data-block-type="simple_table_cell"] { - @apply font-semibold; + @apply font-semibold bg-fill-list-hover; } } @@ -26,7 +24,7 @@ } td[data-block-type="simple_table_cell"] { - @apply border-r border-b border-line-divider; + @apply border-r border-b border-line-divider overflow-hidden whitespace-pre-wrap; .cell-children { .block-element { @apply m-0; @@ -97,4 +95,11 @@ td { @apply px-2 py-0.5; } + + &.selected { + table { + @apply bg-content-blue-50 border border-fill-default; + } + + } } \ No newline at end of file From e188552c1eb539c4aeb160ebce07506387c3cd10 Mon Sep 17 00:00:00 2001 From: Richard Shiue <71320345+richardshiue@users.noreply.github.com> Date: Fri, 13 Dec 2024 13:04:17 +0800 Subject: [PATCH 042/576] fix: ai chat initial rag_ids shouldn't include views that are not documents or in the trash (#6982) * chore: only add document views to initial rag_ids * chore: filter out views that are in the trash --- .../flowy-core/src/deps_resolve/chat_deps.rs | 6 +++++- .../src/deps_resolve/folder_deps.rs | 19 ++++++++++++++--- .../rust-lib/flowy-folder-pub/src/query.rs | 7 ++++++- frontend/rust-lib/flowy-folder/src/manager.rs | 21 +++++++++++++++++++ 4 files changed, 48 insertions(+), 5 deletions(-) diff --git a/frontend/rust-lib/flowy-core/src/deps_resolve/chat_deps.rs b/frontend/rust-lib/flowy-core/src/deps_resolve/chat_deps.rs index 82dd62b7d8..8b08109b13 100644 --- a/frontend/rust-lib/flowy-core/src/deps_resolve/chat_deps.rs +++ b/frontend/rust-lib/flowy-core/src/deps_resolve/chat_deps.rs @@ -1,6 +1,7 @@ use flowy_ai::ai_manager::{AIManager, AIQueryService, AIUserService}; use flowy_ai_pub::cloud::ChatCloudService; use flowy_error::FlowyError; +use flowy_folder::ViewLayout; use flowy_folder_pub::query::FolderQueryService; use flowy_sqlite::kv::KVStorePreferences; use flowy_sqlite::DBConnection; @@ -44,7 +45,10 @@ impl AIQueryService for ChatQueryServiceImpl { parent_view_id: &str, chat_id: &str, ) -> Result, FlowyError> { - let mut ids = self.folder_query.get_sibling_ids(parent_view_id).await; + let mut ids = self + .folder_query + .get_sibling_ids_with_view_layout(parent_view_id, ViewLayout::Document) + .await; if !ids.is_empty() { ids.retain(|id| id != chat_id); diff --git a/frontend/rust-lib/flowy-core/src/deps_resolve/folder_deps.rs b/frontend/rust-lib/flowy-core/src/deps_resolve/folder_deps.rs index 2a5747efce..f7f3692e12 100644 --- a/frontend/rust-lib/flowy-core/src/deps_resolve/folder_deps.rs +++ b/frontend/rust-lib/flowy-core/src/deps_resolve/folder_deps.rs @@ -676,16 +676,29 @@ impl FolderQueryServiceImpl { #[async_trait] impl FolderQueryService for FolderQueryServiceImpl { - async fn get_sibling_ids(&self, parent_view_id: &str) -> Vec { + async fn get_sibling_ids_with_view_layout( + &self, + parent_view_id: &str, + view_layout: ViewLayout, + ) -> Vec { match self.folder_manager.upgrade() { None => vec![], Some(folder_manager) => { if let Ok(parent_view) = folder_manager.get_view(parent_view_id).await { if parent_view.space_info().is_none() { - if let Ok(views) = folder_manager.get_views_belong_to(parent_view_id).await { + if let Ok(views) = folder_manager + .get_untrashed_views_belong_to(parent_view_id) + .await + { return views .into_iter() - .map(|child| child.id.clone()) + .filter_map(|child| { + if child.layout == view_layout { + Some(child.id.clone()) + } else { + None + } + }) .collect::>(); } } diff --git a/frontend/rust-lib/flowy-folder-pub/src/query.rs b/frontend/rust-lib/flowy-folder-pub/src/query.rs index 5ef69c8384..73058e1ef2 100644 --- a/frontend/rust-lib/flowy-folder-pub/src/query.rs +++ b/frontend/rust-lib/flowy-folder-pub/src/query.rs @@ -1,6 +1,11 @@ +use collab_folder::ViewLayout; use lib_infra::async_trait::async_trait; #[async_trait] pub trait FolderQueryService: Send + Sync + 'static { - async fn get_sibling_ids(&self, parent_view_id: &str) -> Vec; + async fn get_sibling_ids_with_view_layout( + &self, + parent_view_id: &str, + view_layout: ViewLayout, + ) -> Vec; } diff --git a/frontend/rust-lib/flowy-folder/src/manager.rs b/frontend/rust-lib/flowy-folder/src/manager.rs index 6abc99229e..30fd60ddfe 100644 --- a/frontend/rust-lib/flowy-folder/src/manager.rs +++ b/frontend/rust-lib/flowy-folder/src/manager.rs @@ -919,6 +919,27 @@ impl FolderManager { } } + /// Return a list of views that belong to the given parent view id, and not + /// in the trash section. + pub async fn get_untrashed_views_belong_to( + &self, + parent_view_id: &str, + ) -> FlowyResult>> { + match self.mutex_folder.load_full() { + Some(folder) => { + let folder = folder.read().await; + let views = folder + .get_views_belong_to(parent_view_id) + .into_iter() + .filter(|view| !folder.is_view_in_section(Section::Trash, &view.id)) + .collect(); + + Ok(views) + }, + None => Ok(vec![]), + } + } + pub async fn get_view(&self, view_id: &str) -> FlowyResult> { match self.mutex_folder.load_full() { Some(folder) => { From 67e93a12e6f75b1e0b440d4f860bdf053718a802 Mon Sep 17 00:00:00 2001 From: Mathias Mogensen <42929161+Xazin@users.noreply.github.com> Date: Mon, 16 Dec 2024 03:05:45 +0100 Subject: [PATCH 043/576] fix: inline grid launch review (#6992) --- .../database/grid/presentation/grid_page.dart | 21 ++++++++++++------- .../widgets/header/grid_header.dart | 8 ++----- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/frontend/appflowy_flutter/lib/plugins/database/grid/presentation/grid_page.dart b/frontend/appflowy_flutter/lib/plugins/database/grid/presentation/grid_page.dart index 39f5052e8d..19330221c6 100755 --- a/frontend/appflowy_flutter/lib/plugins/database/grid/presentation/grid_page.dart +++ b/frontend/appflowy_flutter/lib/plugins/database/grid/presentation/grid_page.dart @@ -385,17 +385,22 @@ class _GridRowsState extends State<_GridRows> { Widget build(BuildContext context) { Widget child; if (widget.shrinkWrap) { - child = SingleChildScrollView( - scrollDirection: Axis.horizontal, + child = Scrollbar( controller: widget.scrollController.horizontalController, - child: ConstrainedBox( - constraints: BoxConstraints( - maxWidth: GridLayout.headerWidth( - context.read().horizontalPadding, - context.read().state.fields, + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + controller: widget.scrollController.horizontalController, + child: ConstrainedBox( + constraints: BoxConstraints( + maxWidth: GridLayout.headerWidth( + context + .read() + .horizontalPadding, + context.read().state.fields, + ), ), + child: _renderList(context), ), - child: _renderList(context), ), ); } else { diff --git a/frontend/appflowy_flutter/lib/plugins/database/grid/presentation/widgets/header/grid_header.dart b/frontend/appflowy_flutter/lib/plugins/database/grid/presentation/widgets/header/grid_header.dart index 018e4f8a00..bcb9139406 100644 --- a/frontend/appflowy_flutter/lib/plugins/database/grid/presentation/widgets/header/grid_header.dart +++ b/frontend/appflowy_flutter/lib/plugins/database/grid/presentation/widgets/header/grid_header.dart @@ -7,7 +7,6 @@ import 'package:appflowy/plugins/database/grid/application/grid_header_bloc.dart import 'package:appflowy/plugins/database/tab_bar/tab_bar_view.dart'; import 'package:appflowy_backend/log.dart'; import 'package:easy_localization/easy_localization.dart'; -import 'package:flowy_infra/size.dart'; import 'package:flowy_infra/theme_extension.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flutter/material.dart'; @@ -154,11 +153,8 @@ class _CellTrailing extends StatelessWidget { @override Widget build(BuildContext context) { return Container( - constraints: BoxConstraints( - maxWidth: GridSize.newPropertyButtonWidth, - minHeight: GridSize.headerHeight, - ), - margin: EdgeInsets.only(right: GridSize.scrollBarSize + Insets.m), + width: GridSize.newPropertyButtonWidth, + height: GridSize.headerHeight, decoration: BoxDecoration( border: Border( bottom: BorderSide(color: AFThemeExtension.of(context).borderColor), From 792eec7d4bbbc686a4b7dc331f03aa370226de3b Mon Sep 17 00:00:00 2001 From: uxadax <66478248+uxadax@users.noreply.github.com> Date: Mon, 16 Dec 2024 03:12:07 +0100 Subject: [PATCH 044/576] chore(i18n): update de-De translations (#6984) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 --- frontend/resources/translations/de-DE.json | 125 +++++++++++++++++++-- 1 file changed, 114 insertions(+), 11 deletions(-) diff --git a/frontend/resources/translations/de-DE.json b/frontend/resources/translations/de-DE.json index 62772137f6..bd452929b5 100644 --- a/frontend/resources/translations/de-DE.json +++ b/frontend/resources/translations/de-DE.json @@ -1076,6 +1076,7 @@ "syncSetting": "Sync Einstellung", "cloudSettings": "Cloud Einstellungen", "enableSync": "Sync aktivieren", + "enableSyncLogWarning": "Vielen Dank für Ihre Hilfe bei der Diagnose von Synchronisierungsproblemen. Dadurch werden Ihre Dokumentänderungen in einer lokalen Datei protokolliert. Bitte beenden Sie die App und öffnen Sie sie erneut, nachdem Sie sie aktiviert haben.", "enableEncrypt": "Daten verschlüsseln", "cloudURL": "Basis URL", "invalidCloudURLScheme": "Ungültiges Format", @@ -1405,6 +1406,7 @@ "empty": "Keine aktiven Filter", "addFilter": "Filter hinzufügen", "cannotFindCreatableField": "Es wurde kein geeignetes Feld zum Filtern gefunden.", + "conditon": "Bedingung", "where": "Wo" }, "textFilter": { @@ -1475,6 +1477,7 @@ "isNotEmpty": "nicht leer" }, "field": { + "label": "Eigenschaft", "hide": "Verstecken", "show": "Anzeigen", "insertLeft": "Links einfügen", @@ -1572,6 +1575,7 @@ "copyProperty": "Eigenschaft in die Zwischenablage kopiert", "count": "Zählen", "newRow": "Neue Zeile", + "loadMore": "Mehr laden", "action": "Aktion", "add": "Klicken, um unten hinzuzufügen", "drag": "Ziehen, um zu verschieben", @@ -1659,6 +1663,7 @@ "showFileNames": "Dateinamen anzeigen", "downloadSuccess": "Datei heruntergeladen", "downloadFailedToken": "Datei konnte nicht heruntergeladen werden, Benutzertoken nicht verfügbar", + "setAsCover": "Als Cover festlegen", "openInBrowser": "Im Browser öffnen", "embedLink": "Dateilink einbetten" } @@ -1706,7 +1711,22 @@ "divider": "Trenner", "outline": "Gliederung", "mathEquation": "Mathematische Gleichung", - "code": "Code" + "code": "Code", + "emoji": "Emoji", + "aiWriter": "KI-Autor", + "dateOrReminder": "Datum oder Erinnerung", + "photoGallery": "Fotogalerie", + "file": "Datei" + }, + "subPage": { + "name": "Dokument", + "keyword1": "Unterseite", + "keyword2": "Seite", + "keyword3": "untergeordnete Seite", + "keyword4": "Seite einfügen", + "keyword6": "neue Seite", + "keyword7": "Seite erstellen", + "keyword8": "Dokument" } }, "selectionMenu": { @@ -1747,6 +1767,26 @@ "bulletedList": "Stichpunktliste", "todoList": "Offene Aufgaben Liste", "callout": "Hervorhebung", + "simpleTable": { + "moreActions": { + "color": "Farbe", + "align": "Ausrichten", + "delete": "Löschen", + "duplicate": "duplizieren", + "insertLeft": "Links einfügen", + "insertRight": "Rechts einfügen", + "insertAbove": "Oben einfügen", + "insertBelow": "Unten einfügen", + "headerColumn": "Kopfspalte", + "headerRow": "Kopfzeile", + "clearContents": "Klarer Inhalt", + "setToPageWidth": "Auf Seitenbreite einstellen", + "distributeColumnsWidth": "Spalten gleichmäßig verteilen" + }, + "clickToAddNewRow": "Klicken Sie hier, um eine neue Zeile hinzuzufügen", + "clickToAddNewColumn": "Klicken Sie hier, um eine neue Spalte hinzuzufügen", + "clickToAddNewRowAndColumn": "Klicken Sie hier, um eine neue Zeile und Spalte hinzuzufügen" + }, "cover": { "changeCover": "Titelbild wechseln", "colors": "Farben", @@ -1762,6 +1802,7 @@ "back": "Zurück", "saveToGallery": "In der Galerie speichern", "removeIcon": "Symbol entfernen", + "removeCover": "Cover entfernen", "pasteImageUrl": "Bild-URL einfügen", "or": "ODER", "pickFromFiles": "Dateien auswählen", @@ -1780,6 +1821,8 @@ "optionAction": { "click": "Klicken", "toOpenMenu": " um das Menü zu öffnen", + "drag": "Ziehen", + "toMove": " bewegen", "delete": "Löschen", "duplicate": "Duplikat", "turnInto": "Einbiegen in", @@ -1791,7 +1834,8 @@ "center": "Zentriert", "right": "Rechts", "defaultColor": "Standard", - "depth": "Tiefe" + "depth": "Tiefe", + "copyLinkToBlock": "Link zum Block kopieren" }, "image": { "addAnImage": "Ein Bild hinzufügen", @@ -1864,11 +1908,14 @@ "file": { "name": "Datei", "uploadTab": "Hochladen", + "uploadMobile": "Wählen Sie eine Datei", + "uploadMobileGallery": "Aus der Fotogalerie", "networkTab": "Link integrieren", "placeholderText": "Klicke oder ziehe eine Datei hoch, um sie hochzuladen", "placeholderDragging": "Lege die hochzuladende Datei hier ab", "dropFileToUpload": "Lege die hochzuladende Datei hier ab", "fileUploadHint": "Lege die hochzuladende Datei hier ab\noder klicke, um eine Datei auszuwählen.", + "fileUploadHintSuffix": "Durchsuchen", "networkHint": "Gebe einen Link zu einer Datei ein", "networkUrlInvalid": "Ungültige URL. Bitte korrigiere die URL und versuche es erneut.", "networkAction": "Dateilink einbetten", @@ -1879,7 +1926,17 @@ "nameEmptyError": "Der Dateiname darf nicht leer bleiben." }, "uploadedAt": "Hochgeladen am {}", - "linkedAt": "Link hinzugefügt am {}" + "linkedAt": "Link hinzugefügt am {}", + "failedToOpenMsg": "Öffnen fehlgeschlagen, Datei nicht gefunden" + }, + "subPage": { + "errors": { + "failedDeletePage": "Seite konnte nicht gelöscht werden", + "failedCreatePage": "Seite konnte nicht erstellt werden", + "failedMovePage": "Seite konnte nicht in dieses Dokument verschoben werden", + "failedDuplicatePage": "Seite konnte nicht dupliziert werden", + "failedDuplicateFindView": "Seite konnte nicht dupliziert werden - Originalansicht nicht gefunden" + } } }, "outlineBlock": { @@ -1982,7 +2039,10 @@ "tooltip": "Klicken, um die Seite zu öffnen" }, "deleted": "gelöscht", - "deletedContent": "Dieser Inhalt existiert nicht oder wurde gelöscht" + "deletedContent": "Dieser Inhalt existiert nicht oder wurde gelöscht", + "noAccess": "Kein Zugriff", + "deletedPage": "Gelöschte Seite", + "trashHint": " - im Papierkorb" }, "toolbar": { "resetToDefaultFont": "Auf den Standard zurücksetzen" @@ -1990,16 +2050,23 @@ "errorBlock": { "theBlockIsNotSupported": "Die aktuelle Version unterstützt diesen Block nicht.", "clickToCopyTheBlockContent": "Hier klicken, um den Blockinhalt zu kopieren", - "blockContentHasBeenCopied": "Der Blockinhalt wurde kopiert." + "blockContentHasBeenCopied": "Der Blockinhalt wurde kopiert.", + "copyBlockContent": "Blockinhalt kopieren" }, "mobilePageSelector": { "title": "Seite auswählen", "failedToLoad": "Seitenliste konnte nicht geladen werden", "noPagesFound": "Keine Seiten gefunden" + }, + "attachmentMenu": { + "choosePhoto": "Foto auswählen", + "takePicture": "Ein Foto machen", + "chooseFile": "Datei auswählen" } }, "board": { "column": { + "label": "Spalte", "createNewCard": "Neu", "renameGroupTooltip": "Drücken, um die Gruppe umzubenennen", "createNewColumn": "Eine neue Gruppe hinzufügen", @@ -2050,7 +2117,11 @@ "nextThirtyDays": "Nächste 30 Tage" }, "noGroup": "Keine Gruppierung nach Eigenschaft", - "noGroupDesc": "Board-Ansichten benötigen eine Eigenschaft zum Gruppieren, um angezeigt zu werden" + "noGroupDesc": "Board-Ansichten benötigen eine Eigenschaft zum Gruppieren, um angezeigt zu werden", + "media": { + "cardText": "{} {}", + "fallbackName": "Dateien" + } }, "calendar": { "menuName": "Kalender", @@ -2096,10 +2167,13 @@ "errorDialog": { "title": "@:appName-Fehler", "howToFixFallback": "Wir entschuldigen uns für die Unannehmlichkeiten! Reiche auf unserer GitHub-Seite ein Problem ein, das deinen Fehler beschreibt.", + "howToFixFallbackHint1": "Wir entschuldigen uns für die Unannehmlichkeiten! Melden Sie ein Problem auf unserer ", + "howToFixFallbackHint2": " Seite, die Ihren Fehler beschreibt.", "github": "Auf GitHub ansehen" }, "search": { "label": "Suchen", + "sidebarSearchIcon": "Suchen und schnell zu einer Seite springen", "placeholder": { "actions": "Suchaktionen..." } @@ -2249,7 +2323,9 @@ }, "error": { "weAreSorry": "Das tut uns leid", - "loadingViewError": "Wir haben Schwierigkeiten diese Ansicht zu laden. Bitte prüfe die Internetverbindung, lade die App neu und zögere nicht, das Team zu kontaktieren, falls das Problem weiterhin besteht." + "loadingViewError": "Wir haben Schwierigkeiten diese Ansicht zu laden. Bitte prüfe die Internetverbindung, lade die App neu und zögere nicht, das Team zu kontaktieren, falls das Problem weiterhin besteht.", + "syncError": "Daten werden nicht von einem anderen Gerät synchronisiert", + "clickToCopy": "Klicken Sie hier, um den Fehlercode zu kopieren" }, "editor": { "bold": "Fett", @@ -2421,7 +2497,13 @@ "deleteMyAccount": "Mein Benutzerkonto löschen", "dialogTitle": "Benutzerkonto löschen", "dialogContent1": "Bist du sicher, dass du dein Benutzerkonto unwiderruflich löschen möchtest?", - "dialogContent2": "Diese Aktion kann nicht rückgängig gemacht werden und führt dazu, dass der Zugriff auf alle Teambereiche aufgehoben wird, dein gesamtes Benutzerkonto, einschließlich privater Arbeitsbereiche, gelöscht wird und du aus allen freigegebenen Arbeitsbereichen entfernt wirst." + "dialogContent2": "Diese Aktion kann nicht rückgängig gemacht werden und führt dazu, dass der Zugriff auf alle Teambereiche aufgehoben wird, dein gesamtes Benutzerkonto, einschließlich privater Arbeitsbereiche, gelöscht wird und du aus allen freigegebenen Arbeitsbereichen entfernt wirst.", + "confirmHint1": "Geben Sie zur Bestätigung bitte „MEIN KONTO LÖSCHEN“ ein.", + "confirmHint3": "MEIN KONTO LÖSCHEN", + "checkToConfirmError": "Sie müssen das Kontrollkästchen aktivieren, um das Löschen zu bestätigen", + "failedToGetCurrentUser": "Aktuelle Benutzer-E-Mail konnte nicht abgerufen werden.", + "confirmTextValidationFailed": "Ihr Bestätigungstext stimmt nicht mit „MEIN KONTO LÖSCHEN“ überein.", + "deleteAccountSuccess": "Konto erfolgreich gelöscht" } }, "workplace": { @@ -2467,6 +2549,8 @@ "openSettings": "Einstellungen öffnen", "photoPermissionTitle": "@:appName möchte auf deine Fotobibliothek zugreifen", "photoPermissionDescription": "Erlaube den Zugriff auf die Fotobibliothek zum Hochladen von Bildern.", + "cameraPermissionTitle": "@:appName möchte auf Ihre Kamera zugreifen", + "cameraPermissionDescription": "@:appName benötigt Zugriff auf Ihre Kamera, damit Sie Bilder von der Kamera zu Ihren Dokumenten hinzufügen können.", "doNotAllow": "Nicht zulassen", "image": "Bild" }, @@ -2515,7 +2599,9 @@ "quicklySwitch": "Schnell zur nächsten Domäne wechseln", "duplicate": "Domäne duplizieren", "movePageToSpace": "Seite in die Domäne verschieben", - "switchSpace": "Domäne wechseln" + "cannotMovePageToDatabase": "Seite kann nicht in die Datenbank verschoben werden", + "switchSpace": "Domäne wechseln", + "spaceNameCannotBeEmpty": "Der Space-Name darf nicht leer sein" }, "publish": { "hasNotBeenPublished": "Diese Seite wurde noch nicht veröffentlicht", @@ -2561,7 +2647,8 @@ "one": "1 Mitglied", "many": "{count} Mitglieder", "other": "{count} Mitglieder" - } + }, + "useThisTemplate": "Verwenden Sie die Vorlage" }, "web": { "continue": "Weiter", @@ -2569,12 +2656,23 @@ "continueWithGoogle": "Weiter mit Google", "continueWithGithub": "Weiter mit GitHub", "continueWithDiscord": "Weiter mit Discord", + "continueWithApple": "Weiter mit Apple ", + "moreOptions": "Weitere Optionen", "signInAgreement": "Wenn du oben auf \"Weiter\" klickst, bestätigst du, dass\ndu folgende Dokumente gelesen, verstanden und akzeptiert hast:\nAppFlowys", "and": "und", "termOfUse": "Bedingungen", "privacyPolicy": "Datenschutzrichtlinie", "signInError": "Anmeldefehler", - "login": "Registrieren oder anmelden" + "login": "Registrieren oder anmelden", + "fileBlock": { + "uploadedAt": "Hochgeladen am {Zeit}", + "linkedAt": "Link hinzugefügt am {Zeit}", + "empty": "Hochladen oder Einbetten einer Datei" + }, + "importNotion": "Importieren aus Notion", + "import": "Import", + "importSuccess": "Erfolgreich hochgeladen", + "importFailed": "Import fehlgeschlagen, bitte überprüfen Sie das Dateiformat" }, "globalComment": { "comments": "Kommentare", @@ -2625,5 +2723,10 @@ "failedToAddComment": "Kommentar konnte nicht hinzugefügt werden", "commentAddedSuccessfully": "Kommentar erfolgreich hinzugefügt.", "commentAddedSuccessTip": "Du hast gerade einen Kommentar hinzugefügt oder darauf geantwortet. Möchtest du nach oben springen, um die neuesten Kommentare zu sehen?" + }, + "template": { + "asTemplate": "Als Vorlage speichern", + "name": "Vorlagenname", + "description": "Vorlagenbeschreibung" } } From d74e7b63ca535358c15bf51cd08581adb9bd56bd Mon Sep 17 00:00:00 2001 From: Henri Devigne Date: Mon, 16 Dec 2024 04:10:14 +0100 Subject: [PATCH 045/576] =?UTF-8?q?chore(i18n):=20update=20fr-FR=20transla?= =?UTF-8?q?tions=20=F0=9F=90=A6=20(#6987)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/resources/translations/fr-FR.json | 452 +++++++++++++++++++-- 1 file changed, 429 insertions(+), 23 deletions(-) diff --git a/frontend/resources/translations/fr-FR.json b/frontend/resources/translations/fr-FR.json index 308a0d0c80..7cbd1c4782 100644 --- a/frontend/resources/translations/fr-FR.json +++ b/frontend/resources/translations/fr-FR.json @@ -59,7 +59,7 @@ "signInWith": "Se connecter avec :", "signInWithEmail": "Se connecter via e-mail", "signInWithMagicLink": "Continuer", - "signUpWithMagicLink": "S'inscrire avec Magic Link", + "signUpWithMagicLink": "S'inscrire avec un lien magique", "pleaseInputYourEmail": "Veuillez entrer votre adresse e-mail", "settings": "Paramètres", "magicLinkSent": "Lien magique envoyé à votre email, veuillez vérifier votre boîte de réception", @@ -67,8 +67,8 @@ "alreadyHaveAnAccount": "Déjà un compte ?", "logIn": "Connexion", "generalError": "Une erreur s'est produite. Veuillez réessayer plus tard", - "limitRateError": "Pour des raisons de sécurité, vous ne pouvez demander un Magic Link que toutes les 60 secondes", - "magicLinkSentDescription": "Un Magic Link vous a été envoyé par e-mail. Cliquez sur le lien pour vous connecter. Le lien expirera dans 5 minutes.", + "limitRateError": "Pour des raisons de sécurité, vous ne pouvez demander un lien magique que toutes les 60 secondes", + "magicLinkSentDescription": "Un lien magique vous a été envoyé par e-mail. Cliquez sur le lien pour vous connecter. Le lien expirera dans 5 minutes.", "LogInWithGoogle": "Se connecter avec Google", "LogInWithGithub": "Se connecter avec Github", "LogInWithDiscord": "Se connecter avec Discord", @@ -77,9 +77,13 @@ }, "workspace": { "chooseWorkspace": "Choisissez votre espace de travail", + "defaultName": "Mon espace de travail", "create": "Créer un espace de travail", + "importFromNotion": "Importer depuis Notion", + "learnMore": "En savoir plus", "reset": "Réinitialiser l'espace de travail", "renameWorkspace": "Renommer l'espace de travail", + "workspaceNameCannotBeEmpty": "Le nom de l'espace de travail ne peut être vide", "resetWorkspacePrompt": "La réinitialisation de l'espace de travail supprimera toutes les pages et données qu'elles contiennent. Êtes-vous sûr de vouloir réinitialiser l'espace de travail ? Alternativement, vous pouvez contacter l'équipe d'assistance pour restaurer l'espace de travail", "hint": "Espace de travail", "notFoundError": "Espace de travail introuvable", @@ -123,7 +127,17 @@ "visitSite": "Visitez le site", "exportAsTab": "Exporter en tant que", "publishTab": "Publier", - "shareTab": "Partager" + "shareTab": "Partager", + "publishOnAppFlowy": "Publier sur AppFlowy", + "shareTabTitle": "Inviter à collaborer", + "shareTabDescription": "Pour faciliter la collaboration avec n'importe qui", + "copyLinkSuccess": "Lien copié", + "copyShareLink": "Copier le lien de partage", + "copyLinkFailed": "Impossible de copier le lien dans le presse-papiers", + "copyLinkToBlockSuccess": "Lien de bloc copié dans le presse-papiers", + "copyLinkToBlockFailed": "Impossible de copier le lien du bloc dans le presse-papiers", + "manageAllSites": "Gérer tous les sites", + "updatePathName": "Mettre à jour le nom du chemin" }, "moreAction": { "small": "petit", @@ -142,6 +156,7 @@ "textAndMarkdown": "Texte et Markdown", "documentFromV010": "Document de la v0.1.0", "databaseFromV010": "Base de données à partir de la v0.1.0", + "notionZip": "Fichier ZIP exporté depuis Notion", "csv": "CSV", "database": "Base de données" }, @@ -172,12 +187,15 @@ "relatedQuestion": "Questions Associées", "serverUnavailable": "Service temporairement indisponible. Veuillez réessayer ultérieurement.", "aiServerUnavailable": "🌈 Oh-oh ! 🌈. Une licorne a mangé notre réponse. Veuillez réessayer !", + "retry": "Réessayer", "clickToRetry": "Cliquez pour réessayer", "regenerateAnswer": "Régénérer", "question1": "Comment utiliser Kanban pour gérer les tâches", "question2": "Expliquez la méthode GTD", "question3": "Pourquoi utiliser Rust", "question4": "Recette avec ce qu'il y a dans ma cuisine", + "question5": "Créer une illustration pour ma page", + "question6": "Dresser une liste de choses à faire pour ma semaine à venir", "aiMistakePrompt": "L'IA peut faire des erreurs. Vérifiez les informations importantes.", "chatWithFilePrompt": "Voulez-vous discuter avec le fichier ?", "indexFileSuccess": "Indexation du fichier réussie", @@ -190,11 +208,13 @@ "clickToMention": "Cliquez pour mentionner une page", "uploadFile": "Téléchargez des fichiers PDF, MD ou TXT pour discuter avec", "questionDetail": "Bonjour {}! Comment puis-je vous aider aujourd'hui?", - "indexingFile": "Indexation {}" + "indexingFile": "Indexation {}", + "generatingResponse": "Générer une réponse" }, "trash": { "text": "Corbeille", "restoreAll": "Tout restaurer", + "restore": "Restaurer", "deleteAll": "Tout supprimer", "pageHeader": { "fileName": "Nom de fichier", @@ -209,6 +229,10 @@ "title": "Êtes-vous sûr de vouloir restaurer toutes les pages dans la corbeille ?", "caption": "Cette action ne peut pas être annulée." }, + "restorePage": { + "title": "Restaurer: {}", + "caption": "Etes-vous sûr de vouloir restaurer cette page ?" + }, "mobile": { "actions": "Actions de la corbeille", "empty": "La corbeille est vide", @@ -221,7 +245,8 @@ "deletePagePrompt": { "text": "Cette page se trouve dans la corbeille", "restore": "Restaurer la page", - "deletePermanent": "Supprimer définitivement" + "deletePermanent": "Supprimer définitivement", + "deletePermanentDescription": "Etes-vous sûr de vouloir supprimer définitivement cette page ? Cette action est irréversible." }, "dialogCreatePageNameHint": "Nom de la page", "questionBubble": { @@ -291,6 +316,7 @@ "today": "Aujourd'hui", "thisWeek": "Cette semaine", "others": "Favoris précédents", + "earlier": "Plus tôt", "justNow": "tout à l' heure", "minutesAgo": "Il y a {count} minutes", "lastViewed": "Dernière consultation", @@ -307,12 +333,17 @@ "upgradeToPro": "Passer à Pro", "upgradeToAIMax": "Débloquez une l'IA illimitée", "storageLimitDialogTitle": "Vous n'avez plus d'espace de stockage gratuit. Effectuez une mise à niveau pour débloquer un espace de stockage illimité", + "storageLimitDialogTitleIOS": "Vous n'avez plus d'espace de stockage gratuit.", "aiResponseLimitTitle": "Vous n'avez plus de réponses d'IA gratuites. Passez au plan Pro ou achetez un module complémentaire d'IA pour débloquer des réponses illimitées", + "aiResponseLimitTitleIOS": "Vous n'avez plus de réponses IA gratuites.", "aiResponseLimitDialogTitle": "La limite des réponses de l'IA a été atteinte", "aiResponseLimit": "Vous n'avez plus de réponses IA gratuites.\n\nAccédez à Paramètres -> Plans -> Cliquez sur AI Max ou Pro Plan pour obtenir plus de réponses AI", "askOwnerToUpgradeToPro": "Votre espace de stockage gratuit est presque plein. Demandez au propriétaire de votre espace de travail de passer au plan Pro", + "askOwnerToUpgradeToProIOS": "Votre espace de travail manque d’espace de stockage gratuit.", "askOwnerToUpgradeToAIMax": "Votre espace de travail est à court de réponses d'IA gratuites. Demandez au propriétaire de votre espace de travail de mettre à niveau le plan ou d'acheter des modules complémentaires d'IA", + "askOwnerToUpgradeToAIMaxIOS": "Votre espace de travail est à court de réponses IA gratuites.", "purchaseStorageSpace": "Acheter un espace de stockage", + "singleFileProPlanLimitationDescription": "Vous avez dépassé la taille maximale de téléchargement de fichiers autorisée dans le plan gratuit. Veuillez passer au plan Pro pour télécharger des fichiers plus volumineux", "purchaseAIResponse": "Acheter", "askOwnerToUpgradeToLocalAI": "Demander au propriétaire de l'espace de travail d'activer l'IA locale", "upgradeToAILocal": "Exécutez des modèles locaux sur votre appareil pour une confidentialité optimale", @@ -353,6 +384,7 @@ "upload": "Télécharger", "edit": "Modifier", "delete": "Supprimer", + "copy": "Copier", "duplicate": "Dupliquer", "putback": "Remettre", "update": "Mettre à jour", @@ -387,6 +419,8 @@ "previous": "Précédent", "submit": "Soumettre", "download": "Télécharger", + "backToHome": "Retour à l'accueil", + "gotIt": "Compris", "tryAGain": "Réessayer" }, "label": { @@ -417,6 +451,68 @@ "trash": "Corbeille", "helpAndSupport": "Aide & Support" }, + "sites": { + "title": "Sites", + "namespaceTitle": "Espace", + "namespaceDescription": "Gérez votre espace et votre page d'accueil", + "namespaceHeader": "Espace de nom", + "homepageHeader": "Page d'accueil", + "updateNamespace": "Mettre à jour l'espace", + "removeHomepage": "Supprimer la page d'accueil", + "selectHomePage": "Sélectionnez une page", + "clearHomePage": "Effacer la page d'accueil pour cet espace", + "customUrl": "URL personnalisée", + "namespace": { + "description": "Ce changement s'appliquera à toutes les pages publiées en direct sur cet espace", + "tooltip": "Nous nous réservons le droit de supprimer tout espace inapproprié", + "updateExistingNamespace": "Mettre à jour l'espace existant", + "upgradeToPro": "Passez au plan Pro pour définir une page d'accueil", + "redirectToPayment": "Redirection vers la page de paiement...", + "onlyWorkspaceOwnerCanSetHomePage": "Seul le propriétaire de l'espace de travail peut définir une page d'accueil", + "pleaseAskOwnerToSetHomePage": "Veuillez demander au propriétaire de l'espace de travail de passer au plan Pro" + }, + "publishedPage": { + "title": "Toutes les pages publiées", + "description": "Gérez vos pages publiées", + "page": "Page", + "pathName": "Nom du chemin", + "date": "Date de publication", + "emptyHinText": "Vous n'avez aucune page publiée dans cet espace de travail", + "noPublishedPages": "Aucune page publiée", + "settings": "Paramètres de publication", + "clickToOpenPageInApp": "Ouvrir la page dans l'application", + "clickToOpenPageInBrowser": "Ouvrir la page dans le navigateur" + }, + "error": { + "failedToGeneratePaymentLink": "Impossible de générer le lien de paiement pour le plan Pro", + "failedToUpdateNamespace": "Échec de la mise à jour de l'espace", + "proPlanLimitation": "Vous devez effectuer une mise à niveau vers le plan Pro pour mettre à jour l'espace", + "namespaceAlreadyInUse": "Ce nom d'espace déjà pris, veuillez en essayer un autre", + "invalidNamespace": "Nom d'espace invalide, veuillez en essayer un autre", + "namespaceLengthAtLeast2Characters": "Le nom de l'espace doit comporter au moins 2 caractères", + "onlyWorkspaceOwnerCanUpdateNamespace": "Seul le propriétaire de l'espace de travail peut mettre à jour l'espace", + "onlyWorkspaceOwnerCanRemoveHomepage": "Seul le propriétaire de l'espace de travail peut supprimer la page d'accueil", + "setHomepageFailed": "Impossible de définir la page d'accueil", + "namespaceTooLong": "Le nom de l'espace est trop long, veuillez en essayer un autre", + "namespaceTooShort": "Le nom de l'espace est trop court, veuillez en essayer un autre", + "namespaceIsReserved": "Ce nom d'espace est réservé, veuillez en essayer un autre", + "updatePathNameFailed": "Échec de la mise à jour du nom du chemin", + "removeHomePageFailed": "Impossible de supprimer la page d'accueil", + "publishNameContainsInvalidCharacters": "Le nom du chemin contient des caractères non valides, veuillez en essayer un autre", + "publishNameTooShort": "Le nom du chemin est trop court, veuillez en essayer un autre", + "publishNameTooLong": "Le nom du chemin est trop long, veuillez en essayer un autre", + "publishNameAlreadyInUse": "Le nom du chemin est déjà utilisé, veuillez en essayer un autre", + "namespaceContainsInvalidCharacters": "Le nom d'espace contient des caractères non valides, veuillez en essayer un autre", + "publishPermissionDenied": "Seul le propriétaire de l'espace de travail ou l'éditeur de la page peut gérer les paramètres de publication", + "publishNameCannotBeEmpty": "Le nom du chemin ne peut pas être vide, veuillez en essayer un autre" + }, + "success": { + "namespaceUpdated": "Espace mis à jour avec succès", + "setHomepageSuccess": "Définir la page d'accueil avec succès", + "updatePathNameSuccess": "Nom du chemin mis à jour avec succès", + "removeHomePageSuccess": "Supprimer la page d'accueil avec succès" + } + }, "accountPage": { "menuLabel": "Mon compte", "title": "Mon compte", @@ -465,6 +561,9 @@ "title": "Réinitialiser la couleur de sélection du document", "description": "Êtes-vous sûr de vouloir réinitialiser la couleur de sélection ?" }, + "resetWidth": { + "resetSuccess": "Réinitialisation réussie de la largeur du document" + }, "theme": { "title": "Thème", "description": "Sélectionnez un thème prédéfini ou téléchargez votre propre thème personnalisé.", @@ -508,7 +607,9 @@ }, "leaveWorkspacePrompt": { "title": "Quitter l'espace de travail", - "content": "Êtes-vous sûr de vouloir quitter cet espace de travail ? Vous allez perdre l’accès à toutes les pages et données qu’il contient." + "content": "Êtes-vous sûr de vouloir quitter cet espace de travail ? Vous allez perdre l’accès à toutes les pages et données qu’il contient.", + "success": "Vous avez quitté l'espace de travail avec succès.", + "fail": "Impossible de quitter l'espace de travail." }, "manageWorkspace": { "title": "Gérer l'espace de travail", @@ -713,6 +814,8 @@ } }, "planPage": { + "menuLabel": "Offre", + "title": "Tarif de l'offre", "planUsage": { "title": "Résumé de l'utilisation du plan", "storageLabel": "Stockage", @@ -725,6 +828,7 @@ "unlimitedAILabel": "Réponses illimitées", "proBadge": "Pro", "aiMaxBadge": "AI Max", + "aiOnDeviceBadge": "IA sur appareil pour Mac", "memberProToggle": "Plus de membres et une IA illimitée", "aiMaxToggle": "IA illimitée et accès à des modèles avancés", "aiOnDeviceToggle": "IA locale pour une confidentialité ultime", @@ -733,6 +837,7 @@ "price": "{}", "priceDescription": "pour 1 000 crédits", "purchase": "Acheter l'IA", + "info": "Ajoutez 1 000 crédits d'IA par espace de travail et intégrez de manière transparente une IA personnalisable dans votre flux de travail pour des résultats plus intelligents et plus rapides avec jusqu'à :", "infoItemOne": "10 000 réponses par base de données", "infoItemTwo": "1 000 réponses par espace de travail" }, @@ -753,10 +858,12 @@ "activeLabel": "Ajouté", "aiMax": { "title": "AI Max", + "description": "Réponses IA illimitées alimentées par GPT-4o, Claude 3.5 Sonnet et plus", "price": "{}", "priceInfo": "par utilisateur et par mois, facturé annuellement" }, "aiOnDevice": { + "title": "IA sur appareil pour Mac", "description": "Exécutez Mistral 7B, LLAMA 3 et d'autres modèles locaux sur votre machine", "price": "{}", "priceInfo": "par utilisateur et par mois, facturé annuellement", @@ -766,6 +873,7 @@ "deal": { "bannerLabel": "Offre de nouvelle année !", "title": "Développez votre équipe !", + "info": "Effectuez une mise à niveau et économisez 10 % sur les forfaits Pro et Team ! Boostez la productivité de votre espace de travail avec de nouvelles fonctionnalités puissantes, notamment l'IA @:appName .", "viewPlans": "Voir les plans" } } @@ -810,6 +918,7 @@ }, "currentPeriodBadge": "ACTUEL", "changePeriod": "Changer de période", + "planPeriod": "{} période", "monthlyInterval": "Mensuel", "monthlyPriceInfo": "par personne, facturé mensuellement", "annualInterval": "Annuellement", @@ -843,8 +952,12 @@ "itemFour": "Collaboration en temps réel", "itemFive": "Application mobile", "itemSix": "Réponses de l'IA", + "itemFileUpload": "Téléchargements de fichiers", + "customNamespace": "Nom d'espace personnalisé", + "tooltipSix": "La durée de vie signifie que le nombre de réponses n'est jamais réinitialisé", "intelligentSearch": "Recherche intelligente", - "tooltipSeven": "Vous permet de personnaliser une partie de l'URL de votre espace de travail" + "tooltipSeven": "Vous permet de personnaliser une partie de l'URL de votre espace de travail", + "customNamespaceTooltip": "URL de site publiée personnalisée" }, "freeLabels": { "itemOne": "facturé par espace de travail", @@ -852,6 +965,7 @@ "itemThree": "5 Go", "itemFour": "Oui", "itemFive": "Oui", + "itemSix": "20 à vie", "itemFileUpload": "Jusqu'à 7 Mo", "intelligentSearch": "Recherche intelligente" }, @@ -870,7 +984,9 @@ "description": "Votre paiement a été traité avec succès et votre forfait est mis à niveau vers @:appName {}. Vous pouvez consulter les détails de votre forfait sur la page Forfait" }, "downgradeDialog": { - "title": "Êtes-vous sûr de vouloir rétrograder votre forfait ?" + "title": "Êtes-vous sûr de vouloir rétrograder votre forfait ?", + "description": "La baisse de votre offre vous ramènera au forfait gratuit. Les membres peuvent perdre l'accès à cet espace de travail et vous devrez peut-être libérer de l'espace pour respecter les limites de stockage du forfait gratuit.", + "downgradeLabel": "Baisser l'offre" } }, "cancelSurveyDialog": { @@ -911,6 +1027,8 @@ } }, "common": { + "uploadingFile": "Le fichier est en cours de téléchargement. Veuillez ne pas quitter l'application", + "uploadNotionSuccess": "Votre fichier zip Notion a été téléchargé avec succès. Une fois l'importation terminée, vous recevrez un e-mail de confirmation", "reset": "Réinitialiser" }, "menu": { @@ -926,6 +1044,8 @@ "syncSetting": "Paramètres de synchronisation", "cloudSettings": "Paramètres cloud", "enableSync": "Activer la synchronisation", + "enableSyncLog": "Activer la journalisation de synchronisation", + "enableSyncLogWarning": "Merci de nous aider à diagnostiquer les problèmes de synchronisation. Cela enregistrera les modifications de votre document dans un fichier local. Veuillez quitter et rouvrir l'application après l'avoir activée", "enableEncrypt": "Chiffrer les données", "cloudURL": "URL de base", "invalidCloudURLScheme": "Schéma invalide", @@ -939,6 +1059,8 @@ "selfHostStart": "Si vous n'avez pas de serveur, veuillez vous référer au", "selfHostContent": "document", "selfHostEnd": "pour obtenir des conseils sur la façon d'auto-héberger votre propre serveur", + "pleaseInputValidURL": "Veuillez saisir une URL valide", + "changeUrl": "Changer l'URL auto-hébergée en {}", "cloudURLHint": "Saisissez l'URL de base de votre serveur", "cloudWSURL": "URL du websocket", "cloudWSURLHint": "Saisissez l'adresse websocket de votre serveur", @@ -1029,6 +1151,8 @@ "documentSettings": { "cursorColor": "Couleur du curseur du document", "selectionColor": "Couleur de sélection du document", + "width": "Largeur du document", + "changeWidth": "Changement", "pickColor": "Sélectionnez une couleur", "colorShade": "Nuance de couleur", "opacity": "Opacité", @@ -1233,6 +1357,7 @@ "typeAValue": "Tapez une valeur...", "layout": "Mise en page", "databaseLayout": "Mise en page", + "viewList": "Vues de base de données", "editView": "Modifier vue", "boardSettings": "Paramètres du tableau", "calendarSettings": "Paramètres du calendrier", @@ -1240,8 +1365,13 @@ "duplicateView": "Dupliquer la vue", "deleteView": "Supprimer la vue", "numberOfVisibleFields": "{} affiché(s)", - "Properties": "Propriétés", - "viewList": "Vues de base de données" + "Properties": "Propriétés" + }, + "filter": { + "empty": "Aucun filtre actif", + "cannotFindCreatableField": "Impossible de trouver un champ approprié pour filtrer", + "conditon": "Condition", + "where": "Où" }, "textFilter": { "contains": "Contient", @@ -1288,9 +1418,12 @@ "between": "Est entre", "empty": "Est vide", "notEmpty": "N'est pas vide", + "startDate": "Date de début", + "endDate": "Date de fin", "choicechipPrefix": { "before": "Avant", "after": "Après", + "between": "Entre", "onOrBefore": "Pendant ou avant", "onOrAfter": "Pendant ou après", "isEmpty": "Est vide", @@ -1308,6 +1441,7 @@ "isNotEmpty": "N'est pas vide" }, "field": { + "label": "Propriété", "hide": "Cacher", "show": "Afficher", "insertLeft": "Insérer à gauche", @@ -1316,6 +1450,7 @@ "delete": "Supprimer", "wrapCellContent": "Envelopper le texte", "clear": "Effacer les cellules", + "switchPrimaryFieldTooltip": "Impossible de modifier le type de champ du champ principal", "textFieldName": "Texte", "checkboxFieldName": "Case à cocher", "dateFieldName": "Date", @@ -1329,6 +1464,7 @@ "relationFieldName": "Relation", "summaryFieldName": "Résume IA", "timeFieldName": "Horaire", + "mediaFieldName": "Fichiers et médias", "translateFieldName": "Traduction IA", "translateTo": "Traduire en", "numberFormat": "Format du nombre", @@ -1391,11 +1527,13 @@ "cannotFindCreatableField": "Impossible de trouver un champ approprié pour trier", "deleteAllSorts": "Supprimer tous les tris", "addSort": "Ajouter un tri", + "sortsActive": "Impossible {intention} lors du tri", "removeSorting": "Voulez-vous supprimer le tri ?", "fieldInUse": "Vous êtes déjà en train de trier par ce champ", "deleteSort": "Supprimer le tri" }, "row": { + "label": "Ligne", "duplicate": "Dupliquer", "delete": "Supprimer", "titlePlaceholder": "Sans titre", @@ -1403,6 +1541,7 @@ "copyProperty": "Copie de la propriété dans le presse-papiers", "count": "Compte", "newRow": "Nouvelle ligne", + "loadMore": "Charger plus", "action": "Action", "add": "Cliquez sur ajouter ci-dessous", "drag": "Glisser pour déplacer", @@ -1411,7 +1550,9 @@ "dragAndClick": "Faites glisser pour déplacer, cliquez pour ouvrir le menu", "insertRecordAbove": "Insérer l'enregistrement ci-dessus", "insertRecordBelow": "Insérer l'enregistrement ci-dessous", - "noContent": "Aucun contenu" + "noContent": "Aucun contenu", + "createRowAboveDescription": "créer une ligne au dessus", + "createRowBelowDescription": "insérer une ligne ci-dessous" }, "selectOption": { "create": "Créer", @@ -1472,6 +1613,24 @@ "countEmptyShort": "VIDE", "countNonEmpty": "Compter les cellules non vides", "countNonEmptyShort": "REMPLI" + }, + "media": { + "rename": "Rebaptiser", + "download": "Télécharger", + "expand": "Développer", + "delete": "Supprimer", + "moreFilesHint": "+{}", + "addFileOrImage": "Ajouter un fichier ou un lien", + "attachmentsHint": "{}", + "addFileMobile": "Ajouter un fichier", + "extraCount": "+{}", + "deleteFileDescription": "Etes-vous sûr de vouloir supprimer ce fichier ? Cette action est irréversible.", + "showFileNames": "Afficher le nom du fichier", + "downloadSuccess": "Fichier téléchargé", + "downloadFailedToken": "Échec du téléchargement du fichier, jeton utilisateur indisponible", + "setAsCover": "Définir comme couverture", + "openInBrowser": "Ouvrir dans le navigateur", + "embedLink": "Intégrer le lien du fichier" } }, "document": { @@ -1504,6 +1663,7 @@ "image": "Image", "bulletedList": "Liste à puces", "numberedList": "Liste numérotée", + "todoList": "Liste de choses à faire", "doc": "Doc", "linkedDoc": "Lien vers la page", "grid": "Grille", @@ -1519,12 +1679,26 @@ "mathEquation": "Équation mathématique", "code": "Code", "toggleList": "Menu dépliant", + "toggleHeading1": "Basculer en titre 1", + "toggleHeading2": "Basculer en titre 2", + "toggleHeading3": "Basculer en titre 3", "emoji": "Émoji", "aiWriter": "Rédacteur IA", "dateOrReminder": "Date ou rappel", "photoGallery": "Galerie de photos", "file": "Fichier", "checkbox": "Case à cocher" + }, + "subPage": { + "name": "Document", + "keyword1": "sous-page", + "keyword2": "page", + "keyword3": "page enfant", + "keyword4": "insérer une page", + "keyword5": "page intégrée", + "keyword6": "nouvelle page", + "keyword7": "créer une page", + "keyword8": "document" } }, "selectionMenu": { @@ -1565,6 +1739,26 @@ "bulletedList": "Liste à puces", "todoList": "Liste de tâches", "callout": "Encadré", + "simpleTable": { + "moreActions": { + "color": "Couleur", + "align": "Aligner", + "delete": "Supprimer", + "duplicate": "Dupliquer", + "insertLeft": "Insérer à gauche", + "insertRight": "Insérer à droite", + "insertAbove": "Insérer ci-dessus", + "insertBelow": "Insérer ci-dessous", + "headerColumn": "Colonne d'en-tête", + "headerRow": "Ligne d'en-tête", + "clearContents": "Supprimer le contenu", + "setToPageWidth": "Définir sur la largeur de la page", + "distributeColumnsWidth": "Répartir les colonnes uniformément" + }, + "clickToAddNewRow": "Cliquez pour ajouter une nouvelle ligne", + "clickToAddNewColumn": "Cliquez pour ajouter une nouvelle colonne", + "clickToAddNewRowAndColumn": "Cliquez pour ajouter une nouvelle ligne et une nouvelle colonne" + }, "cover": { "changeCover": "Changer la couverture", "colors": "Couleurs", @@ -1580,6 +1774,7 @@ "back": "Dos", "saveToGallery": "Sauvegarder dans la gallerie", "removeIcon": "Supprimer l'icône", + "removeCover": "Supprimer la couverture", "pasteImageUrl": "Coller l'URL de l'image", "or": "OU", "pickFromFiles": "Choisissez parmi les fichiers", @@ -1598,6 +1793,7 @@ "optionAction": { "click": "Cliquez sur", "toOpenMenu": " pour ouvrir le menu", + "drag": "Glisser", "delete": "Supprimer", "duplicate": "Dupliquer", "turnInto": "Changer en", @@ -1609,7 +1805,8 @@ "center": "Centre", "right": "Droite", "defaultColor": "Défaut", - "depth": "Profond" + "depth": "Profond", + "copyLinkToBlock": "Copier le lien pour bloquer" }, "image": { "addAnImage": "Ajouter une image", @@ -1682,11 +1879,14 @@ "file": { "name": "Fichier", "uploadTab": "Télécharger", + "uploadMobile": "Choisissez un fichier", + "uploadMobileGallery": "Depuis la galerie photo", "networkTab": "Intégrer un lien", "placeholderText": "Télécharger ou intégrer un fichier", "placeholderDragging": "Glisser le fichier à télécharger", "dropFileToUpload": "Glisser le fichier à télécharger", "fileUploadHint": "Glisser un fichier ici pour le télécharger\nou cliquez pour parcourir", + "fileUploadHintSuffix": "Parcourir", "networkHint": "Coller un lien de fichier", "networkUrlInvalid": "URL non valide, veuillez corriger l'URL et réessayer", "fileTooBigError": "La taille du fichier est trop grande, veuillez télécharger un fichier d'une taille inférieure à 10 Mo", @@ -1696,8 +1896,19 @@ "nameEmptyError": "Le nom du fichier ne peut pas être laissé vide." }, "uploadedAt": "Mis en ligne le {}", - "linkedAt": "Lien ajouté le {}" - } + "linkedAt": "Lien ajouté le {}", + "failedToOpenMsg": "Impossible d'ouvrir, fichier non trouvé" + }, + "subPage": { + "errors": { + "failedDeletePage": "Impossible de supprimer la page", + "failedCreatePage": "Échec de la création de la page", + "failedMovePage": "Impossible de déplacer la page vers ce document", + "failedDuplicatePage": "Impossible de dupliquer la page", + "failedDuplicateFindView": "Impossible de dupliquer la page - vue d'origine non trouvée" + } + }, + "cannotMoveToItsChildren": "Ne peut pas se déplacer vers ses enfants" }, "outlineBlock": { "placeholder": "Table de contenu" @@ -1800,7 +2011,9 @@ }, "deleted": "Supprimer", "deletedContent": "Ce document n'existe pas ou a été supprimé", - "noAccess": "Pas d'accès" + "noAccess": "Pas d'accès", + "deletedPage": "Page supprimée", + "trashHint": " - à la corbeille" }, "toolbar": { "resetToDefaultFont": "Réinitialiser aux valeurs par défaut" @@ -1808,16 +2021,24 @@ "errorBlock": { "theBlockIsNotSupported": "La version actuelle ne prend pas en charge ce bloc.", "clickToCopyTheBlockContent": "Cliquez pour copier le contenu du bloc", - "blockContentHasBeenCopied": "Le contenu du bloc a été copié." + "blockContentHasBeenCopied": "Le contenu du bloc a été copié.", + "parseError": "Une erreur s'est produite lors de l'analyse du bloc {}.", + "copyBlockContent": "Copier le contenu du bloc" }, "mobilePageSelector": { "title": "Sélectionner une page", "failedToLoad": "Impossible de charger la liste des pages", "noPagesFound": "Aucune page trouvée" + }, + "attachmentMenu": { + "choosePhoto": "Choisir une photo", + "takePicture": "Prendre une photo", + "chooseFile": "Choisir le fichier" } }, "board": { "column": { + "label": "Colonne", "createNewCard": "Nouveau", "renameGroupTooltip": "Appuyez pour renommer le groupe", "createNewColumn": "Ajouter un nouveau groupe", @@ -1865,6 +2086,10 @@ "nextSevenDays": "7 prochains jours", "lastThirtyDays": "30 derniers jours", "nextThirtyDays": "30 prochains jours" + }, + "media": { + "cardText": "{} {}", + "fallbackName": "fichiers" } }, "calendar": { @@ -1894,11 +2119,11 @@ "layoutDateField": "Calendrier de mise en page par", "changeLayoutDateField": "Modifier le champ de mise en page", "noDateTitle": "Pas de date", + "noDateHint": "Les événements non planifiés s'afficheront ici", "unscheduledEventsTitle": "Événements non planifiés", "clickToAdd": "Cliquez pour ajouter au calendrier", "name": "Disposition du calendrier", - "clickToOpen": "Cliquez pour ouvrir l'évènement", - "noDateHint": "Les événements non planifiés s'afficheront ici" + "clickToOpen": "Cliquez pour ouvrir l'évènement" }, "referencedCalendarPrefix": "Vue", "quickJumpYear": "Sauter à", @@ -2234,10 +2459,18 @@ "deleteAccount": { "title": "Supprimer le compte", "subtitle": "Supprimez définitivement votre compte et toutes vos données.", + "description": "Supprimez définitivement votre compte et supprimez l'accès à tous les espaces de travail.", "deleteMyAccount": "Supprimer mon compte", "dialogTitle": "Supprimer le compte", "dialogContent1": "Êtes-vous sûr de vouloir supprimer définitivement votre compte ?", - "dialogContent2": "Cette action ne peut pas être annulée et supprimera l'accès à tous les espaces d'équipe, effaçant l'intégralité de votre compte, y compris les espaces de travail privés, et vous supprimant de tous les espaces de travail partagés." + "dialogContent2": "Cette action ne peut pas être annulée et supprimera l'accès à tous les espaces d'équipe, effaçant l'intégralité de votre compte, y compris les espaces de travail privés, et vous supprimant de tous les espaces de travail partagés.", + "confirmHint1": "Veuillez taper « SUPPRIMER MON COMPTE » pour confirmer.", + "confirmHint2": "Je comprends que cette action est irréversible et supprimera définitivement mon compte et toutes les données associées.", + "confirmHint3": "SUPPRIMER MON COMPTE", + "checkToConfirmError": "Vous devez cocher la case pour confirmer la suppression", + "failedToGetCurrentUser": "Impossible d'obtenir l'e-mail de l'utilisateur actuel", + "confirmTextValidationFailed": "Votre texte de confirmation ne correspond pas à « SUPPRIMER MON COMPTE »", + "deleteAccountSuccess": "Compte supprimé avec succès" } }, "workplace": { @@ -2283,6 +2516,8 @@ "openSettings": "Ouvrir les paramètres", "photoPermissionTitle": "@:appName souhaite accéder à votre photothèque", "photoPermissionDescription": "Autoriser l'accès à la photothèque pour le téléchargement d'images.", + "cameraPermissionTitle": "@:appName souhaite accéder à votre caméra", + "cameraPermissionDescription": "@:appName a besoin d'accéder à votre appareil photo pour vous permettre d'ajouter des images à vos documents à partir de l'appareil photo", "doNotAllow": "Ne pas autoriser", "image": "Image" }, @@ -2331,11 +2566,30 @@ "quicklySwitch": "Passer rapidement à l’espace suivant", "duplicate": "dupliquer l'espace ", "movePageToSpace": "Déplacer la page vers l'espace", + "cannotMovePageToDatabase": "Impossible de déplacer la page vers la base de données", "switchSpace": "Changer d'espace", - "spaceNameCannotBeEmpty": "Le nom de l'espace ne peut pas être vide" + "spaceNameCannotBeEmpty": "Le nom de l'espace ne peut pas être vide", + "success": { + "deleteSpace": "Espace supprimé avec succès", + "renameSpace": "Espace renommé avec succès", + "duplicateSpace": "Espace dupliqué avec succès", + "updateSpace": "Espace mis à jour avec succès" + }, + "error": { + "deleteSpace": "Impossible de supprimer l'espace", + "renameSpace": "Impossible de renommer l'espace", + "duplicateSpace": "Impossible de dupliquer l'espace", + "updateSpace": "Échec de la mise à jour de l'espace" + }, + "createSpace": "Créer de l'espace", + "manageSpace": "Gérer l'espace", + "renameSpace": "Renommer l'espace", + "mSpaceIconColor": "Couleur de l'icône de l'espace", + "mSpaceIcon": "Icône de l'espace" }, "publish": { "hasNotBeenPublished": "Cette page n'a pas encore été publiée", + "spaceHasNotBeenPublished": "Je n'ai pas encore pris en charge la publication d'un espace", "reportPage": "Page de rapport", "databaseHasNotBeenPublished": "La publication d'une base de données n'est pas encore prise en charge.", "createdWith": "Créé avec", @@ -2379,7 +2633,8 @@ "one": "1 membre", "many": "{count} membres", "other": "{count} membres" - } + }, + "useThisTemplate": "Utiliser le modèle" }, "web": { "continue": "Continuer", @@ -2387,6 +2642,10 @@ "continueWithGoogle": "Continuer avec Google", "continueWithGithub": "Continuer avec GitHub", "continueWithDiscord": "Continuer avec Discord", + "continueWithApple": "Continuer avec Apple ", + "moreOptions": "Plus d'options", + "collapse": "Réduire", + "signInAgreement": "En cliquant sur « Continuer » ci-dessus, vous avez accepté les conditions d'utilisation d'AppFlowy.", "and": "et", "termOfUse": "Termes", "privacyPolicy": "politique de confidentialité", @@ -2396,7 +2655,13 @@ "uploadedAt": "Mis en ligne le {time}", "linkedAt": "Lien ajouté le {time}", "empty": "Envoyer ou intégrer un fichier" - } + }, + "importNotion": "Importer depuis Notion", + "import": "Importer", + "importSuccess": "Téléchargé avec succès", + "importSuccessMessage": "Nous vous informerons lorsque l'importation sera terminée. Vous pourrez ensuite visualiser vos pages importées dans la barre latérale.", + "importFailed": "L'importation a échoué, veuillez vérifier le format du fichier", + "dropNotionFile": "Déposez votre fichier zip Notion ici pour le télécharger, ou cliquez pour parcourir" }, "globalComment": { "comments": "Commentaires", @@ -2453,6 +2718,7 @@ "name": "Nom du modèle", "description": "Description du modèle", "about": "À propos du modèle", + "deleteFromTemplate": "Supprimer des modèles", "preview": "Aperçu du modèle", "categories": "Catégories de modèles", "relatedTemplates": "Modèles associés", @@ -2492,6 +2758,7 @@ "uploadSuccessDescription": "Votre modèle a été envoyé avec succès. Vous pouvez maintenant le visualiser dans la galerie de modèles.", "viewTemplate": "Voir le modèle", "deleteTemplate": "Supprimer le modèle", + "deleteSuccess": "Modèle supprimé avec succès", "deleteTemplateDescription": "Êtes-vous sûr de vouloir supprimer ce modèle ?", "addRelatedTemplate": "Ajouter un modèle associé", "removeRelatedTemplate": "Supprimer le modèle associé", @@ -2507,5 +2774,144 @@ "uploadSuccessDescription": "Le fichier a été envoyé avec succès", "uploadFailedDescription": "L'envoi du fichier a échoué", "uploadingDescription": "Le fichier est en cours d'envoi" + }, + "gallery": { + "preview": "Ouvrir en plein écran", + "copy": "Copier", + "download": "Télécharger", + "prev": "Précédent", + "next": "Suivant", + "resetZoom": "Réinitialiser le zoom", + "zoomIn": "Agrandir", + "zoomOut": "Rétrécir" + }, + "invitation": { + "join": "Rejoindre", + "on": "sur", + "invitedBy": "Invité par", + "membersCount": { + "zero": "{count} membres", + "one": "{count} membre", + "many": "{count} membres", + "other": "{count} membres" + }, + "tip": "Vous avez été invité à rejoindre cet espace de travail avec les coordonnées ci-dessous. Si celles-ci sont incorrectes, contactez votre administrateur pour renvoyer l'invitation.", + "joinWorkspace": "Rejoindre l'espace de travail", + "success": "Vous avez rejoint avec succès l'espace de travail", + "successMessage": "Vous pouvez désormais accéder à toutes les pages et espaces de travail qu'il contient.", + "openWorkspace": "Ouvrir AppFlowy", + "alreadyAccepted": "Vous avez déjà accepté l'invitation", + "errorModal": { + "title": "Quelque chose s'est mal passé", + "description": "Il est possible que votre compte actuel {email} n'ait pas accès à cet espace de travail. Veuillez vous connecter avec le compte approprié ou contacter le propriétaire de l'espace de travail pour obtenir de l'aide.", + "contactOwner": "Contacter le propriétaire", + "close": "Retour à l'accueil", + "changeAccount": "Changer de compte" + } + }, + "requestAccess": { + "title": "Pas d'accès à cette page", + "subtitle": "Vous pouvez demander l'accès au propriétaire de cette page. Une fois approuvé, vous pourrez consulter la page.", + "requestAccess": "Demande d'accès", + "backToHome": "Retour à l'accueil", + "tip": "Vous êtes actuellement connecté en tant que .", + "mightBe": "Vous pourriez avoir besoin de avec un compte différent.", + "successful": "Demande envoyée avec succès", + "successfulMessage": "Vous serez averti une fois que le propriétaire aura approuvé votre demande.", + "requestError": "Échec de la demande d'accès", + "repeatRequestError": "Vous avez déjà demandé l'accès à cette page" + }, + "approveAccess": { + "title": "Approuver la demande d'adhésion à l'espace de travail", + "requestSummary": "demandes d'adhésion et accès", + "upgrade": "mise à niveau", + "downloadApp": "Télécharger AppFlowy", + "approveButton": "Approuver", + "approveSuccess": "Approuvé avec succès", + "approveError": "Échec de l'approbation, assurez-vous que la limite du plan d'espace de travail n'est pas dépassée", + "getRequestInfoError": "Impossible d'obtenir les informations de la demande", + "memberCount": { + "zero": "Aucun membre", + "one": "1 membre", + "many": "{count} membres", + "other": "{count} membres" + }, + "alreadyProTitle": "Vous avez atteint la limite du plan d'espace de travail", + "alreadyProMessage": "Demandez-leur de contacter pour débloquer plus de membres", + "repeatApproveError": "Vous avez déjà approuvé cette demande", + "ensurePlanLimit": "Assurez-vous que la limite du plan d'espace de travail n'est pas dépassée. Si la limite est dépassée, envisagez le plan de l'espace de travail ou .", + "requestToJoin": "demandé à rejoindre", + "asMember": "en tant que membre" + }, + "upgradePlanModal": { + "title": "Passer à Pro", + "message": "{name} a atteint la limite de membres gratuits. Passez au plan Pro pour inviter plus de membres.", + "upgradeSteps": "Comment mettre à niveau votre plan sur AppFlowy :", + "step1": "1. Accédez aux paramètres", + "step2": "2. Cliquez sur « Offre »", + "step3": "3. Sélectionnez « Changer d'offre »", + "appNote": "Note: ", + "actionButton": "Mettre à niveau", + "downloadLink": "Télécharger l'application", + "laterButton": "Plus tard", + "refreshNote": "Après une mise à niveau réussie, cliquez sur pour activer vos nouvelles fonctionnalités.", + "refresh": "ici" + }, + "breadcrumbs": { + "label": "Fil d'Ariane" + }, + "time": { + "justNow": "A l'instant", + "seconds": { + "one": "1 seconde", + "other": "{count} secondes" + }, + "minutes": { + "one": "1 minute", + "other": "{compter} minutes" + }, + "hours": { + "one": "1 heure", + "other": "{count} heures" + }, + "days": { + "one": "1 jour", + "other": "{count} jours" + }, + "weeks": { + "one": "1 semaine", + "other": "{count} semaines" + }, + "months": { + "one": "1 mois", + "other": "{count} mois" + }, + "years": { + "one": "1 an", + "other": "{count} années" + }, + "ago": "il y a", + "yesterday": "Hier", + "today": "Aujourd'hui" + }, + "members": { + "zero": "Aucun membre", + "one": "1 membre", + "many": "{count} membres", + "other": "{count} membres" + }, + "tabMenu": { + "close": "Fermer", + "closeDisabledHint": "Impossible de fermer un onglet épinglé, veuillez d'abord le désépingler", + "closeOthers": "Fermer les autres onglets", + "closeOthersHint": "Cela fermera tous les onglets non épinglés sauf celui-ci", + "closeOthersDisabledHint": "Tous les onglets sont épinglés, je ne trouve aucun onglet à fermer" + }, + "openFileMessage": { + "success": "Fichier ouvert avec succès", + "fileNotFound": "Fichier introuvable", + "noAppToOpenFile": "Aucune application pour ouvrir ce fichier", + "permissionDenied": "Aucune autorisation d'ouvrir ce fichier", + "unknownError": "Échec de l'ouverture du fichier" } } From f307300b96a24575c24006f99649f042453733aa Mon Sep 17 00:00:00 2001 From: Lucas Date: Mon, 16 Dec 2024 11:47:08 +0800 Subject: [PATCH 046/576] fix: simple table issues (#6985) * fix: list padding in table cell is too wide * feat: improve tab in table cell * feat: improve shift+tab in table cell * fix: unable to edit cell after deleting an image * fix: inline attribute issue * fix: disable dragging a block into table * feat: add distribute column evenly in column action menu * fix: numbered list icon align in table cell * feat: add setToPageWidth and distributeColumnEvenly in table menu * feat: support highlight color * chore: update editor version * test: add setToPageWidth and distributeColumnEvenly in table menu * test: inline attribute issues * test: add distribute column evenly in column action menu * test: select all in table * test: improve tab(+shift) shortcut in table cell * test: improve enter shortcut in table cell * feat: keep the same column width after using distribute column widths evenly * test: keep the same column width after using distribute column widths evenly * test: drag block to other block's child --- .../document_with_image_block_test.dart | 7 - ...cument_with_inline_math_equation_test.dart | 36 ++++ .../document_with_inline_page_test.dart | 14 ++ .../document_with_simple_table_test.dart | 154 ++++++++++++++++++ .../presentation/editor_configuration.dart | 11 +- .../document/presentation/editor_page.dart | 5 + .../actions/block_action_option_cubit.dart | 22 +++ .../draggable_option_button.dart | 1 + .../actions/drag_to_reorder/util.dart | 31 +++- .../drag_to_reorder/visual_drag_area.dart | 9 +- .../actions/option/option_actions.dart | 20 ++- .../numbered_list/numbered_list_icon.dart | 2 +- .../shortcuts/command_shortcuts.dart | 1 + .../simple_table_block_component.dart | 7 + .../simple_table_cell_block_component.dart | 50 ++++-- .../simple_table_more_action.dart | 1 + .../simple_table_map_operation.dart | 10 ++ .../simple_table_node_extension.dart | 5 + .../simple_table_style_operation.dart | 4 + .../simple_table_row_block_component.dart | 6 +- .../simple_table_backspace_command.dart | 8 +- .../simple_table_command_extension.dart | 8 + .../simple_table_enter_command.dart | 29 +++- frontend/appflowy_flutter/pubspec.lock | 4 +- frontend/appflowy_flutter/pubspec.yaml | 2 +- 25 files changed, 407 insertions(+), 40 deletions(-) diff --git a/frontend/appflowy_flutter/integration_test/desktop/document/document_with_image_block_test.dart b/frontend/appflowy_flutter/integration_test/desktop/document/document_with_image_block_test.dart index 2ad12e55aa..d963ff0d06 100644 --- a/frontend/appflowy_flutter/integration_test/desktop/document/document_with_image_block_test.dart +++ b/frontend/appflowy_flutter/integration_test/desktop/document/document_with_image_block_test.dart @@ -138,13 +138,6 @@ void main() { ); }); - testWidgets('insert a bmp image from network', (tester) async { - await testEmbedImage( - tester, - 'https://people.math.sc.edu/Burkardt/data/bmp/snail.bmp', - ); - }); - testWidgets('insert a jpg image from network', (tester) async { await testEmbedImage( tester, diff --git a/frontend/appflowy_flutter/integration_test/desktop/document/document_with_inline_math_equation_test.dart b/frontend/appflowy_flutter/integration_test/desktop/document/document_with_inline_math_equation_test.dart index c4a8e71a02..d437651672 100644 --- a/frontend/appflowy_flutter/integration_test/desktop/document/document_with_inline_math_equation_test.dart +++ b/frontend/appflowy_flutter/integration_test/desktop/document/document_with_inline_math_equation_test.dart @@ -113,5 +113,41 @@ void main() { tester.expectToSeeText(formula); }); + + testWidgets('insert a inline math equation and type something after it', + (tester) async { + await tester.initializeAppFlowy(); + await tester.tapAnonymousSignInButton(); + + // create a new document + await tester.createNewPageWithNameUnderParent( + name: 'math equation', + ); + + // tap the first line of the document + await tester.editor.tapLineOfEditorAt(0); + // insert a inline page + const formula = 'E = MC ^ 2'; + await tester.ime.insertText(formula); + await tester.editor.updateSelection( + Selection.single(path: [0], startOffset: 0, endOffset: formula.length), + ); + + // tap the inline math equation button + final inlineMathEquationButton = find.findFlowyTooltip( + LocaleKeys.document_plugins_createInlineMathEquation.tr(), + ); + await tester.tapButton(inlineMathEquationButton); + + // expect to see the math equation block + final inlineMathEquation = find.byType(InlineMathEquation); + expect(inlineMathEquation, findsOneWidget); + + await tester.editor.tapLineOfEditorAt(0); + const text = 'Hello World'; + await tester.ime.insertText(text); + + expect(find.textContaining(text, findRichText: true), findsOneWidget); + }); }); } diff --git a/frontend/appflowy_flutter/integration_test/desktop/document/document_with_inline_page_test.dart b/frontend/appflowy_flutter/integration_test/desktop/document/document_with_inline_page_test.dart index 2a552c0d22..12047bd37f 100644 --- a/frontend/appflowy_flutter/integration_test/desktop/document/document_with_inline_page_test.dart +++ b/frontend/appflowy_flutter/integration_test/desktop/document/document_with_inline_page_test.dart @@ -94,6 +94,20 @@ void main() { await tester.tapButton(finder); expect(find.byType(GridPage), findsOneWidget); }); + + testWidgets('insert a inline page and type something after the page', + (tester) async { + await tester.initializeAppFlowy(); + await tester.tapAnonymousSignInButton(); + + await insertInlinePage(tester, ViewLayoutPB.Grid); + + await tester.editor.tapLineOfEditorAt(0); + const text = 'Hello World'; + await tester.ime.insertText(text); + + expect(find.textContaining(text, findRichText: true), findsOneWidget); + }); }); } diff --git a/frontend/appflowy_flutter/integration_test/desktop/document/document_with_simple_table_test.dart b/frontend/appflowy_flutter/integration_test/desktop/document/document_with_simple_table_test.dart index df473cc057..9548aa4a29 100644 --- a/frontend/appflowy_flutter/integration_test/desktop/document/document_with_simple_table_test.dart +++ b/frontend/appflowy_flutter/integration_test/desktop/document/document_with_simple_table_test.dart @@ -406,6 +406,160 @@ void main() { final afterWidth = tableNode.width; expect(afterWidth, equals(beforeWidth)); + + final distributeColumnWidthsEvenly = + tableNode.attributes[SimpleTableBlockKeys.distributeColumnWidthsEvenly]; + expect(distributeColumnWidthsEvenly, isTrue); + }); + + testWidgets('distribute columns evenly (2)', (tester) async { + await tester.initializeAppFlowy(); + await tester.tapAnonymousSignInButton(); + await tester.createNewPageWithNameUnderParent( + name: 'simple_table_test', + ); + + await tester.editor.tapLineOfEditorAt(0); + await tester.insertTableInDocument(); + + final tableNode = tester.editor.getNodeAtPath([0]); + final beforeWidth = tableNode.width; + + // set the column width to page width + await tester.clickMoreActionItemInTableMenu( + type: SimpleTableMoreActionType.column, + index: 0, + action: SimpleTableMoreAction.distributeColumnsEvenly, + ); + await tester.pumpAndSettle(); + + final afterWidth = tableNode.width; + expect(afterWidth, equals(beforeWidth)); + + final distributeColumnWidthsEvenly = + tableNode.attributes[SimpleTableBlockKeys.distributeColumnWidthsEvenly]; + expect(distributeColumnWidthsEvenly, isTrue); + }); + + testWidgets('using option menu to set column width', (tester) async { + await tester.initializeAppFlowy(); + await tester.tapAnonymousSignInButton(); + await tester.createNewPageWithNameUnderParent( + name: 'simple_table_test', + ); + + await tester.editor.tapLineOfEditorAt(0); + await tester.insertTableInDocument(); + await tester.editor.hoverAndClickOptionMenuButton([0]); + + final editorState = tester.editor.getCurrentEditorState(); + final beforeWidth = editorState.document.nodeAtPath([0])!.width; + + await tester.tapButton( + find.text( + LocaleKeys.document_plugins_simpleTable_moreActions_setToPageWidth.tr(), + ), + ); + await tester.pumpAndSettle(); + + final afterWidth = editorState.document.nodeAtPath([0])!.width; + expect(afterWidth, greaterThan(beforeWidth)); + + await tester.editor.hoverAndClickOptionMenuButton([0]); + await tester.tapButton( + find.text( + LocaleKeys + .document_plugins_simpleTable_moreActions_distributeColumnsWidth + .tr(), + ), + ); + await tester.pumpAndSettle(); + + final afterWidth2 = editorState.document.nodeAtPath([0])!.width; + expect(afterWidth2, equals(afterWidth)); + }); + + testWidgets('insert a table and use select all the delete it', + (tester) async { + await tester.initializeAppFlowy(); + await tester.tapAnonymousSignInButton(); + await tester.createNewPageWithNameUnderParent( + name: 'simple_table_test', + ); + + await tester.editor.tapLineOfEditorAt(0); + await tester.insertTableInDocument(); + + await tester.editor.tapLineOfEditorAt(1); + await tester.ime.insertText('Hello World'); + + // select all + await tester.simulateKeyEvent( + LogicalKeyboardKey.keyA, + isMetaPressed: UniversalPlatform.isMacOS, + isControlPressed: !UniversalPlatform.isMacOS, + ); + + await tester.simulateKeyEvent(LogicalKeyboardKey.backspace); + await tester.pumpAndSettle(); + + final editorState = tester.editor.getCurrentEditorState(); + // only one paragraph left + expect(editorState.document.root.children.length, 1); + final paragraphNode = editorState.document.nodeAtPath([0])!; + expect(paragraphNode.delta, isNull); + }); + + testWidgets('use tab or shift+tab to navigate in table', (tester) async { + await tester.initializeAppFlowy(); + await tester.tapAnonymousSignInButton(); + await tester.createNewPageWithNameUnderParent( + name: 'simple_table_test', + ); + + await tester.editor.tapLineOfEditorAt(0); + await tester.insertTableInDocument(); + + await tester.simulateKeyEvent(LogicalKeyboardKey.tab); + await tester.pumpAndSettle(); + + final editorState = tester.editor.getCurrentEditorState(); + final selection = editorState.selection; + expect(selection, isNotNull); + expect(selection!.start.path, [0, 0, 1, 0]); + expect(selection.end.path, [0, 0, 1, 0]); + + await tester.simulateKeyEvent( + LogicalKeyboardKey.tab, + isShiftPressed: true, + ); + await tester.pumpAndSettle(); + + final selection2 = editorState.selection; + expect(selection2, isNotNull); + expect(selection2!.start.path, [0, 0, 0, 0]); + expect(selection2.end.path, [0, 0, 0, 0]); + }); + + testWidgets('shift+enter to insert a new line in table', (tester) async { + await tester.initializeAppFlowy(); + await tester.tapAnonymousSignInButton(); + await tester.createNewPageWithNameUnderParent( + name: 'simple_table_test', + ); + + await tester.editor.tapLineOfEditorAt(0); + await tester.insertTableInDocument(); + + await tester.simulateKeyEvent( + LogicalKeyboardKey.enter, + isShiftPressed: true, + ); + await tester.pumpAndSettle(); + + final editorState = tester.editor.getCurrentEditorState(); + final node = editorState.document.nodeAtPath([0, 0, 0])!; + expect(node.children.length, 1); }); } diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_configuration.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_configuration.dart index 00682b6a52..5dd9ab0a7b 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_configuration.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_configuration.dart @@ -125,6 +125,14 @@ List _buildOptionActions(BuildContext context, String type) { standardActions.addAll([OptionAction.divider, OptionAction.depth]); } + if (SimpleTableBlockKeys.type == type) { + standardActions.addAll([ + OptionAction.divider, + OptionAction.setToPageWidth, + OptionAction.distributeColumnsEvenly, + ]); + } + return standardActions; } @@ -150,6 +158,7 @@ void _customBlockOptionActions( } return true; }; + builder.configuration = builder.configuration.copyWith( blockSelectionAreaMargin: (_) => const EdgeInsets.symmetric( vertical: 1, @@ -163,7 +172,7 @@ void _customBlockOptionActions( if ((type == HeadingBlockKeys.type || type == ToggleListBlockKeys.type) && level > 0) { - final offset = [14.0, 11.0, 8.0, 6.0, 4.0, 2.0]; + final offset = [13.0, 11.0, 8.0, 6.0, 4.0, 2.0]; top += offset[level - 1]; } else if (type == SimpleTableBlockKeys.type) { top += 8.0; diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_page.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_page.dart index bb0cf4e156..af34c169f4 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_page.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_page.dart @@ -144,6 +144,11 @@ class _AppFlowyEditorPageState extends State _initEditorL10n(); _initializeShortcuts(); + AppFlowyRichTextKeys.partialSliced.addAll([ + MentionBlockKeys.mention, + InlineMathEquationKeys.formula, + ]); + indentableBlockTypes.add(ToggleListBlockKeys.type); convertibleBlockTypes.add(ToggleListBlockKeys.type); slashMenuItems = _customSlashMenuItems(); diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/block_action_option_cubit.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/block_action_option_cubit.dart index 3f4f62ad34..2d15479442 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/block_action_option_cubit.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/block_action_option_cubit.dart @@ -43,6 +43,12 @@ class BlockActionOptionCubit extends Cubit { case OptionAction.copyLinkToBlock: await _copyLinkToBlock(node); break; + case OptionAction.setToPageWidth: + await _setToPageWidth(node); + break; + case OptionAction.distributeColumnsEvenly: + await _distributeColumnsEvenly(node); + break; case OptionAction.align: case OptionAction.color: case OptionAction.divider: @@ -657,4 +663,20 @@ class BlockActionOptionCubit extends Cubit { // then updating the selection with the beforeSelection that may contains multiple blocks return beforeSelection; } + + Future _setToPageWidth(Node node) async { + if (node.type != SimpleTableBlockKeys.type) { + return; + } + + await editorState.setColumnWidthToPageWidth(tableNode: node); + } + + Future _distributeColumnsEvenly(Node node) async { + if (node.type != SimpleTableBlockKeys.type) { + return; + } + + await editorState.distributeColumnWidthToPageWidth(tableNode: node); + } } diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/drag_to_reorder/draggable_option_button.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/drag_to_reorder/draggable_option_button.dart index e5a8d9228e..53a8cd52fe 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/drag_to_reorder/draggable_option_button.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/drag_to_reorder/draggable_option_button.dart @@ -86,6 +86,7 @@ class _DraggableOptionButtonState extends State { details.globalPosition, builder: (context, data) { return VisualDragArea( + editorState: widget.editorState, data: data, dragNode: widget.blockComponentContext.node, ); diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/drag_to_reorder/util.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/drag_to_reorder/util.dart index b157d640b9..1a60d9c49c 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/drag_to_reorder/util.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/drag_to_reorder/util.dart @@ -1,3 +1,4 @@ +import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_table/simple_table.dart'; import 'package:appflowy_backend/log.dart'; import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:flutter/material.dart'; @@ -37,13 +38,24 @@ Future dragToMoveNode( // Determine the new path based on drop position // For VerticalPosition.top, we keep the target node's path if (verticalPosition == VerticalPosition.bottom) { - newPath = horizontalPosition == HorizontalPosition.left - ? newPath.next // Insert after target node - : newPath.child(0); // Insert as first child of target node + if (horizontalPosition == HorizontalPosition.left) { + newPath = newPath.next; + final node = editorState.document.nodeAtPath(newPath); + if (node == null) { + // if node is null, it means the node is the last one of the document. + newPath = targetNode.path; + } + } else { + newPath = newPath.child(0); + } } // Check if the drop should be ignored - if (shouldIgnoreDragTarget(node, newPath)) { + if (shouldIgnoreDragTarget( + editorState: editorState, + dragNode: node, + targetPath: newPath, + )) { Log.info( 'Drop ignored: node($node, ${node.path}), path($acceptedPath)', ); @@ -110,7 +122,11 @@ Future dragToMoveNode( return (verticalPosition, horizontalPosition, globalBlockRect); } -bool shouldIgnoreDragTarget(Node dragNode, Path? targetPath) { +bool shouldIgnoreDragTarget({ + required EditorState editorState, + required Node dragNode, + required Path? targetPath, +}) { if (targetPath == null) { return true; } @@ -123,5 +139,10 @@ bool shouldIgnoreDragTarget(Node dragNode, Path? targetPath) { return true; } + final targetNode = editorState.getNodeAtPath(targetPath); + if (targetNode != null && targetNode.isInTable) { + return true; + } + return false; } diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/drag_to_reorder/visual_drag_area.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/drag_to_reorder/visual_drag_area.dart index ede11b8b9e..f3499a9ea5 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/drag_to_reorder/visual_drag_area.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/drag_to_reorder/visual_drag_area.dart @@ -10,16 +10,21 @@ class VisualDragArea extends StatelessWidget { super.key, required this.data, required this.dragNode, + required this.editorState, }); final DragAreaBuilderData data; final Node dragNode; - + final EditorState editorState; @override Widget build(BuildContext context) { final targetNode = data.targetNode; - final ignore = shouldIgnoreDragTarget(dragNode, targetNode.path); + final ignore = shouldIgnoreDragTarget( + editorState: editorState, + dragNode: dragNode, + targetPath: targetNode.path, + ); if (ignore) { return const SizedBox.shrink(); } diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/option/option_actions.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/option/option_actions.dart index a654d2de84..5a9389514b 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/option/option_actions.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/option/option_actions.dart @@ -67,7 +67,13 @@ enum OptionAction { color, divider, align, - depth; + + // Outline block + depth, + + // Simple table + setToPageWidth, + distributeColumnsEvenly; FlowySvgData get svg { switch (this) { @@ -91,6 +97,10 @@ enum OptionAction { return FlowySvgs.tag_s; case OptionAction.copyLinkToBlock: return FlowySvgs.share_tab_copy_s; + case OptionAction.setToPageWidth: + return FlowySvgs.table_set_to_page_width_s; + case OptionAction.distributeColumnsEvenly: + return FlowySvgs.table_distribute_columns_evenly_s; } } @@ -116,6 +126,14 @@ enum OptionAction { return LocaleKeys.document_plugins_optionAction_copyLinkToBlock.tr(); case OptionAction.divider: throw UnsupportedError('Divider does not have description'); + case OptionAction.setToPageWidth: + return LocaleKeys + .document_plugins_simpleTable_moreActions_setToPageWidth + .tr(); + case OptionAction.distributeColumnsEvenly: + return LocaleKeys + .document_plugins_simpleTable_moreActions_distributeColumnsWidth + .tr(); } } } diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/numbered_list/numbered_list_icon.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/numbered_list/numbered_list_icon.dart index 3c67f7ccca..5ecb118bd0 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/numbered_list/numbered_list_icon.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/numbered_list/numbered_list_icon.dart @@ -30,7 +30,7 @@ class NumberedListIcon extends StatelessWidget { minWidth: size, minHeight: size, ), - margin: const EdgeInsets.only(right: 8.0), + margin: const EdgeInsets.only(top: 0.5, right: 8.0), alignment: Alignment.center, child: Center( child: Text( diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/shortcuts/command_shortcuts.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/shortcuts/command_shortcuts.dart index b1cd0c1712..a3d1630680 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/shortcuts/command_shortcuts.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/shortcuts/command_shortcuts.dart @@ -47,6 +47,7 @@ List commandShortcutEvents = [ undoCommand, redoCommand, exitEditingCommand, + ...tableCommands, ].contains(shortcut), ), diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_block_component.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_block_component.dart index f5eb5ea187..63e41ef872 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_block_component.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_block_component.dart @@ -48,6 +48,13 @@ class SimpleTableBlockKeys { // column widths // it's a `SimpleTableColumnWidthMap` value, {column_index: width, ...} static const String columnWidths = 'column_widths'; + + // distribute column widths evenly + // if the user distributed the column widths evenly before, the value should be true, + // and for the newly added column, using the width of the previous column. + // it's a bool value, default is false + static const String distributeColumnWidthsEvenly = + 'distribute_column_widths_evenly'; } Node simpleTableBlockNode({ diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_cell_block_component.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_cell_block_component.dart index b9954499c3..ca072b6e32 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_cell_block_component.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_cell_block_component.dart @@ -187,8 +187,17 @@ class SimpleTableCellBlockWidgetState extends State }, ); }, - child: Column( - children: node.children.map(_buildCellContent).toList(), + child: Container( + padding: SimpleTableConstants.cellEdgePadding, + constraints: const BoxConstraints( + minWidth: SimpleTableConstants.minimumColumnWidth, + ), + width: node.columnWidth, + child: node.children.isEmpty + ? _buildEmptyCellContent() + : Column( + children: node.children.map(_buildCellContent).toList(), + ), ), ), ); @@ -196,21 +205,33 @@ class SimpleTableCellBlockWidgetState extends State Widget _buildCellContent(Node childNode) { final alignment = _buildAlignment(); - return Container( - padding: SimpleTableConstants.cellEdgePadding, - constraints: const BoxConstraints( - minWidth: SimpleTableConstants.minimumColumnWidth, - ), - width: node.columnWidth, + + return Align( alignment: alignment, child: IntrinsicWidth( - child: IntrinsicHeight( - child: editorState.renderer.build(context, childNode), - ), + child: editorState.renderer.build(context, childNode), ), ); } + Widget _buildEmptyCellContent() { + // if the table cell is empty, we should allow the user to tap on it to create a new paragraph. + return GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () { + final transaction = editorState.transaction; + final path = node.path.child(0); + transaction + ..insertNode( + path, + paragraphNode(), + ) + ..afterSelection = Selection.collapsed(Position(path: path)); + editorState.apply(transaction); + }, + ); + } + Widget _buildRowMoreActionButton() { final rowIndex = node.rowIndex; @@ -252,7 +273,12 @@ class SimpleTableCellBlockWidgetState extends State } Color? _buildBackgroundColor() { - // Priority: column color > row color > header color > default color + // Priority: highlight color > column color > row color > header color > default color + final isSelectingTable = + simpleTableContext?.isSelectingTable.value ?? false; + if (isSelectingTable) { + return Theme.of(context).colorScheme.primary.withOpacity(0.1); + } final columnColor = node.buildColumnColor(context); if (columnColor != null && columnColor != Colors.transparent) { diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_more_action.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_more_action.dart index 4c682ca3b5..6d57be00b8 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_more_action.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_more_action.dart @@ -47,6 +47,7 @@ enum SimpleTableMoreActionType { SimpleTableMoreAction.align, SimpleTableMoreAction.divider, SimpleTableMoreAction.setToPageWidth, + SimpleTableMoreAction.distributeColumnsEvenly, SimpleTableMoreAction.divider, SimpleTableMoreAction.duplicate, SimpleTableMoreAction.clearContents, diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_map_operation.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_map_operation.dart index 5be39f5df4..cb42571704 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_map_operation.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_map_operation.dart @@ -167,6 +167,16 @@ extension TableMapOperation on Node { comparator: (iKey, index) => iKey >= index, ); + final bool distributeColumnWidthsEvenly = + attributes[SimpleTableBlockKeys.distributeColumnWidthsEvenly] ?? + false; + + if (distributeColumnWidthsEvenly) { + // if the distribute column widths evenly flag is true, + // we should distribute the column widths evenly + columnWidths[index.toString()] = columnWidths.values.firstOrNull; + } + return attributes .mergeValues( SimpleTableBlockKeys.columnColors, diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_node_extension.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_node_extension.dart index cc7686fa72..bbc35f7852 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_node_extension.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_node_extension.dart @@ -210,6 +210,11 @@ extension TableNodeExtension on Node { return tableCellNode; } + /// Whether the current node is in a table. + bool get isInTable { + return parentTableNode != null; + } + double get columnWidth { final parentTableNode = this.parentTableNode; diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_style_operation.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_style_operation.dart index 0277ea3fb6..e28bd9f2fe 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_style_operation.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_style_operation.dart @@ -95,6 +95,8 @@ extension TableOptionOperation on EditorState { double.infinity, ), }, + // reset the distribute column widths evenly flag + SimpleTableBlockKeys.distributeColumnWidthsEvenly: false, }); await apply(transaction); } @@ -276,6 +278,7 @@ extension TableOptionOperation on EditorState { } transaction.updateNode(tableNode, { SimpleTableBlockKeys.columnWidths: columnWidths, + SimpleTableBlockKeys.distributeColumnWidthsEvenly: false, }); await apply(transaction); } @@ -315,6 +318,7 @@ extension TableOptionOperation on EditorState { } transaction.updateNode(tableNode, { SimpleTableBlockKeys.columnWidths: columnWidths, + SimpleTableBlockKeys.distributeColumnWidthsEvenly: true, }); await apply(transaction); } diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_row_block_component.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_row_block_component.dart index 9b7d6a5d16..712df59b82 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_row_block_component.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_row_block_component.dart @@ -39,7 +39,7 @@ class SimpleTableRowBlockComponentBuilder extends BlockComponentBuilder { } @override - BlockComponentValidate get validate => (node) => node.children.isNotEmpty; + BlockComponentValidate get validate => (_) => true; } class SimpleTableRowBlockWidget extends BlockComponentStatefulWidget { @@ -72,6 +72,10 @@ class _SimpleTableRowBlockWidgetState extends State @override Widget build(BuildContext context) { + if (node.children.isEmpty) { + return const SizedBox.shrink(); + } + return IntrinsicHeight( child: Row( crossAxisAlignment: CrossAxisAlignment.stretch, diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_shortcuts/simple_table_backspace_command.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_shortcuts/simple_table_backspace_command.dart index dedc59da47..f4a9bd9946 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_shortcuts/simple_table_backspace_command.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_shortcuts/simple_table_backspace_command.dart @@ -21,13 +21,13 @@ KeyEventResult _backspaceInTableCellHandler(EditorState editorState) { } final onlyContainsOneChild = tableCellNode.children.length == 1; - final isParagraphNode = - tableCellNode.children.first.type == ParagraphBlockKeys.type; - final isCodeBlock = tableCellNode.children.first.type == CodeBlockKeys.type; + final firstChild = tableCellNode.children.first; + final isParagraphNode = firstChild.type == ParagraphBlockKeys.type; + final isCodeBlock = firstChild.type == CodeBlockKeys.type; if (onlyContainsOneChild && selection.isCollapsed && selection.end.offset == 0) { - if (isParagraphNode) { + if (isParagraphNode && firstChild.children.isEmpty) { return KeyEventResult.skipRemainingHandlers; } else if (isCodeBlock) { // replace the codeblock with a paragraph diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_shortcuts/simple_table_command_extension.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_shortcuts/simple_table_command_extension.dart index 980cfcdeca..1fb8e07603 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_shortcuts/simple_table_command_extension.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_shortcuts/simple_table_command_extension.dart @@ -65,6 +65,10 @@ extension TableCommandExtension on EditorState { return KeyEventResult.ignored; } + if (isOutdentable(editorState)) { + return outdentCommand.execute(editorState); + } + Selection? newSelection; final previousCell = tableCellNode.getPreviousCellInSameRow(); @@ -126,6 +130,10 @@ extension TableCommandExtension on EditorState { Selection? newSelection; + if (isIndentable(editorState)) { + return indentCommand.execute(editorState); + } + final nextCell = tableCellNode.getNextCellInSameRow(); if (nextCell != null && !nextCell.path.equals(tableCellNode.path)) { // get the first children of the next cell diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_shortcuts/simple_table_enter_command.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_shortcuts/simple_table_enter_command.dart index bc5a0a09c2..74bdc3fc62 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_shortcuts/simple_table_enter_command.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_shortcuts/simple_table_enter_command.dart @@ -1,6 +1,8 @@ +import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_table/simple_table_cell_block_component.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_table/simple_table_shortcuts/simple_table_command_extension.dart'; import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; final CommandShortcutEvent enterInTableCell = CommandShortcutEvent( key: 'Press enter in table cell', @@ -15,10 +17,31 @@ KeyEventResult _enterInTableCellHandler(EditorState editorState) { if (!isInTableCell || selection == null || tableCellNode == null || - node == null) { + node == null || + !selection.isCollapsed) { return KeyEventResult.ignored; } - // forward the enter command to the insertNewLine character command to support multi-line text in table cell - return KeyEventResult.skipRemainingHandlers; + // check if the shift key is pressed, if so, we should return false to let the system handle it. + final isShiftPressed = HardwareKeyboard.instance.isShiftPressed; + if (isShiftPressed) { + return KeyEventResult.ignored; + } + + final delta = node.delta; + if (!indentableBlockTypes.contains(node.type) || delta == null) { + return KeyEventResult.ignored; + } + + if (selection.startIndex == 0 && delta.isEmpty) { + // clear the style + if (node.parent?.type != SimpleTableCellBlockKeys.type) { + if (outdentCommand.execute(editorState) == KeyEventResult.handled) { + return KeyEventResult.handled; + } + } + return convertToParagraphCommand.execute(editorState); + } + + return KeyEventResult.ignored; } diff --git a/frontend/appflowy_flutter/pubspec.lock b/frontend/appflowy_flutter/pubspec.lock index 5fad77da82..d06a7de798 100644 --- a/frontend/appflowy_flutter/pubspec.lock +++ b/frontend/appflowy_flutter/pubspec.lock @@ -61,8 +61,8 @@ packages: dependency: "direct main" description: path: "." - ref: "954ffa5" - resolved-ref: "954ffa538e0788a5d25dc16555ff80a3afae5ea3" + ref: ca04a67 + resolved-ref: ca04a675c06071678ef5901d6000cc4d6ac745e8 url: "https://github.com/AppFlowy-IO/appflowy-editor.git" source: git version: "4.0.0" diff --git a/frontend/appflowy_flutter/pubspec.yaml b/frontend/appflowy_flutter/pubspec.yaml index c0ab20ff64..59e53c8e20 100644 --- a/frontend/appflowy_flutter/pubspec.yaml +++ b/frontend/appflowy_flutter/pubspec.yaml @@ -173,7 +173,7 @@ dependency_overrides: appflowy_editor: git: url: https://github.com/AppFlowy-IO/appflowy-editor.git - ref: "954ffa5" + ref: "ca04a67" appflowy_editor_plugins: git: From 381d94680846eecf15cee23360833dbe01fa2bf4 Mon Sep 17 00:00:00 2001 From: Richard Shiue <71320345+richardshiue@users.noreply.github.com> Date: Mon, 16 Dec 2024 12:25:31 +0800 Subject: [PATCH 047/576] chore: filter out spaces from mention page (#6994) --- .../ai_chat/application/chat_input_control_cubit.dart | 7 ++++++- .../presentation/chat_input/mobile_ai_prompt_input.dart | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/frontend/appflowy_flutter/lib/plugins/ai_chat/application/chat_input_control_cubit.dart b/frontend/appflowy_flutter/lib/plugins/ai_chat/application/chat_input_control_cubit.dart index 1dfa75755a..63acb8be6d 100644 --- a/frontend/appflowy_flutter/lib/plugins/ai_chat/application/chat_input_control_cubit.dart +++ b/frontend/appflowy_flutter/lib/plugins/ai_chat/application/chat_input_control_cubit.dart @@ -46,7 +46,12 @@ class ChatInputControlCubit extends Cubit { final newViews = await ViewBackendService.getAllViews().fold( (result) { return result.items - .where((v) => v.layout.isDocumentView && v.parentViewId != v.id) + .where( + (v) => + !v.isSpace && + v.layout.isDocumentView && + v.parentViewId != v.id, + ) .toList(); }, (err) { diff --git a/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_input/mobile_ai_prompt_input.dart b/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_input/mobile_ai_prompt_input.dart index 28b81b7659..e2ec9d5ab9 100644 --- a/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_input/mobile_ai_prompt_input.dart +++ b/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/chat_input/mobile_ai_prompt_input.dart @@ -197,6 +197,7 @@ class _MobileAIPromptInputState extends State { final selectedView = await showPageSelectorSheet( context, filter: (view) => + !view.isSpace && view.layout.isDocumentView && view.parentViewId != view.id && !inputControlCubit.selectedViewIds.contains(view.id), From 867d515a35ebce752e9c16e7c0b7a0f51a826a64 Mon Sep 17 00:00:00 2001 From: Lucas Date: Mon, 16 Dec 2024 17:59:38 +0800 Subject: [PATCH 048/576] feat: support readonly table (#6997) * feat: support readonly table * fix: cannot preview pasted images * chore: remove http schema on android --- .../android/app/src/main/AndroidManifest.xml | 4 -- .../copy_and_paste/paste_from_image.dart | 12 +++-- .../inline_math_equation.dart | 50 ++++++++++-------- .../simple_table_cell_block_component.dart | 39 +++++++------- .../simple_table_border_builder.dart | 9 +++- .../simple_table_widget.dart | 32 ++++++------ .../document/presentation/editor_style.dart | 4 +- .../widgets/image_viewer/image_provider.dart | 7 +-- .../interactive_image_toolbar.dart | 9 ++-- frontend/appflowy_flutter/pubspec.lock | 52 ++++++++++++++++++- frontend/appflowy_flutter/pubspec.yaml | 3 +- 11 files changed, 146 insertions(+), 75 deletions(-) diff --git a/frontend/appflowy_flutter/android/app/src/main/AndroidManifest.xml b/frontend/appflowy_flutter/android/app/src/main/AndroidManifest.xml index 49118a1532..74d5c5e494 100644 --- a/frontend/appflowy_flutter/android/app/src/main/AndroidManifest.xml +++ b/frontend/appflowy_flutter/android/app/src/main/AndroidManifest.xml @@ -36,10 +36,6 @@ - - - - /g, ''); +}; + +function MermaidChat ({ node }: { + node: CodeNode +}) { + const id = node.blockId; + const diagram = CustomEditor.getBlockTextContent(node); + const ref = useRef(null); + const [innerHtml, setInnerHtml] = React.useState(''); + const isDark = useContext(ThemeModeContext)?.isDark; + const [error, setError] = React.useState(null); + + useEffect(() => { + const element = ref.current; + + if (!element || !diagram) return; + + setError(null); + void (async () => { + const sanitizedDiagram = sanitizeDiagram(diagram); + const theme = isDark ? darkTheme : lightTheme; + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + mermaid.initialize({ + startOnLoad: true, + securityLevel: 'loose', + ...theme, + }); + try { + await mermaid.parse(sanitizedDiagram); + const { svg } = await mermaid.render(`mermaid-${id}`, diagram); + + setInnerHtml(svg); + } catch (e) { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + setError(e.message); + } + })(); + + }, [diagram, id, isDark]); + + if (error) { + return ( +
+ {error} +
+ ); + } + + return ( +
+ ); +} + +export default MermaidChat; \ No newline at end of file diff --git a/frontend/appflowy_web_app/src/components/editor/components/blocks/code/SelectLanguage.tsx b/frontend/appflowy_web_app/src/components/editor/components/blocks/code/SelectLanguage.tsx index c3c05ef36f..8c9d63c609 100644 --- a/frontend/appflowy_web_app/src/components/editor/components/blocks/code/SelectLanguage.tsx +++ b/frontend/appflowy_web_app/src/components/editor/components/blocks/code/SelectLanguage.tsx @@ -1,41 +1,159 @@ -import React, { useRef } from 'react'; -import { TextField } from '@mui/material'; +import { ReactComponent as SelectedIcon } from '@/assets/selected.svg'; +import { Popover } from '@/components/_shared/popover'; +import { supportLanguages } from '@/components/editor/components/blocks/code/constants'; +import { createHotkey, HOT_KEY_NAME } from '@/utils/hotkeys'; +import { Button, TextField } from '@mui/material'; +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; -function SelectLanguage({ +function SelectLanguage ({ readOnly, language = 'Auto', + onChangeLanguage, + onClose, }: { readOnly?: boolean; language: string; onChangeLanguage: (language: string) => void; - onBlur?: () => void; + onClose?: () => void; }) { const { t } = useTranslation(); - const ref = useRef(null); + const ref = useRef(null); + const [open, setOpen] = useState(false); + const [search, setSearch] = useState(''); + const [selectLanguage, setSelectLanguage] = useState(language); + const searchRef = useRef(null); + const scrollRef = useRef(null); + const options = useMemo(() => { + return supportLanguages + .map((item) => ({ + key: item.id, + content: item.title, + })) + .filter((item) => { + return item.content?.toLowerCase().includes(search?.toLowerCase()); + }); + }, [search]); + + const handleClose = useCallback(() => { + setOpen(false); + setSearch(''); + onClose?.(); + }, [onClose]); + + const handleConfirm = useCallback( + (key: string) => { + onChangeLanguage(key); + handleClose(); + }, + [onChangeLanguage, handleClose], + ); + + const selectedLanguage = useMemo(() => { + return supportLanguages.find((item) => item.id === language?.toLowerCase())?.title || 'Auto'; + }, [language]); + + useEffect(() => { + if (!open) return; + searchRef.current?.focus(); + }, [open]); + + useEffect(() => { + const container = scrollRef.current; + + if (!container) return; + + const el = container.querySelector(`[data-key="${selectLanguage}"]`); + + if (!el) return; + + el.scrollIntoView({ block: 'nearest' }); + }, [selectLanguage]); return ( <> - { if (readOnly) return; + setOpen(true); }} - InputProps={{ - readOnly: true, - }} - placeholder={t('document.codeBlock.language.placeholder')} - label={t('document.codeBlock.language.label')} - /> + > + {selectedLanguage} + + + +
+ setSearch(e.target.value)} + size={'small'} + autoFocus={true} + variant={'standard'} + className={'px-3 py-1 text-xs'} + placeholder={t('search.label')} + onKeyDown={(e) => { + if (createHotkey(HOT_KEY_NAME.ENTER)(e.nativeEvent)) { + e.preventDefault(); + handleConfirm(selectLanguage); + } + + if (createHotkey(HOT_KEY_NAME.UP)(e.nativeEvent)) { + const index = options.findIndex((item) => item.key === selectLanguage); + const prevIndex = (index - 1 + options.length) % options.length; + + setSelectLanguage(options[prevIndex].key); + } + + if (createHotkey(HOT_KEY_NAME.DOWN)(e.nativeEvent)) { + const index = options.findIndex((item) => item.key === selectLanguage); + const nextIndex = (index + 1) % options.length; + + setSelectLanguage(options[nextIndex].key); + } + + }} + /> +
+ {options.map((item) => ( +
handleConfirm(item.key)} + className={`p-2 ${selectLanguage === item.key ? 'bg-fill-list-hover' : ''} text-sm rounded-[8px] flex justify-between cursor-pointer hover:bg-fill-list-hover`} + > +
{item.content}
+ + {item.key === language && ( + + )} +
+ ))} +
+
+
); } diff --git a/frontend/appflowy_web_app/src/components/editor/components/blocks/code/constants.ts b/frontend/appflowy_web_app/src/components/editor/components/blocks/code/constants.ts index dee71624db..f801f60b85 100644 --- a/frontend/appflowy_web_app/src/components/editor/components/blocks/code/constants.ts +++ b/frontend/appflowy_web_app/src/components/editor/components/blocks/code/constants.ts @@ -1,4 +1,4 @@ -export const supportLanguage = [ +export const supportLanguages = [ { id: 'bash', title: 'Bash', @@ -151,4 +151,12 @@ export const supportLanguage = [ id: 'yaml', title: 'YAML', }, + { + id: 'mermaid', + title: 'Mermaid', + }, + { + id: 'delphi', + title: 'Delphi', + }, ]; diff --git a/frontend/appflowy_web_app/src/components/editor/components/blocks/database/DatabaseBlock.tsx b/frontend/appflowy_web_app/src/components/editor/components/blocks/database/DatabaseBlock.tsx index 4a84a9622b..694ac56a5e 100644 --- a/frontend/appflowy_web_app/src/components/editor/components/blocks/database/DatabaseBlock.tsx +++ b/frontend/appflowy_web_app/src/components/editor/components/blocks/database/DatabaseBlock.tsx @@ -1,45 +1,31 @@ -import { ReactComponent as ExpandMoreIcon } from '$icons/16x/full_view.svg'; -import { BlockType, View, YDoc } from '@/application/types'; +import { DatabaseViewLayout, UIVariant, View, YDoc, YjsDatabaseKey, YjsEditorKey } from '@/application/types'; import { Database } from '@/components/database'; +import TableContainer from '@/components/editor/components/table-container/TableContainer'; import { DatabaseNode, EditorElementProps } from '@/components/editor/editor.type'; -import { EditorVariant, useEditorContext } from '@/components/editor/EditorContext'; -import { Tooltip } from '@mui/material'; +import { useEditorContext } from '@/components/editor/EditorContext'; +import { getScrollParent } from '@/components/global-comment/utils'; import CircularProgress from '@mui/material/CircularProgress'; -import React, { forwardRef, memo, useCallback, useEffect, useMemo, useState } from 'react'; +import React, { forwardRef, memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; +import { ReactEditor, useReadOnly, useSlateStatic } from 'slate-react'; +import { Element } from 'slate'; +import { ReactComponent as TipIcon } from '@/assets/warning.svg'; export const DatabaseBlock = memo( forwardRef>(({ node, children, ...attributes }, ref) => { const { t } = useTranslation(); const viewId = node.data.view_id; - const type = node.type; - const navigateToView = useEditorContext()?.navigateToView; - const loadView = useEditorContext()?.loadView; - const createRowDoc = useEditorContext()?.createRowDoc; - const loadViewMeta = useEditorContext()?.loadViewMeta; - const variant = useEditorContext()?.variant; + const context = useEditorContext(); + const navigateToView = context?.navigateToView; + const loadView = context?.loadView; + const createRowDoc = context?.createRowDoc; + const loadViewMeta = context?.loadViewMeta; + const readSummary = context.readSummary; + const variant = context.variant; const [notFound, setNotFound] = useState(false); + const [showActions, setShowActions] = useState(false); const [doc, setDoc] = useState(null); - const [isHovering, setIsHovering] = useState(false); - const style = useMemo(() => { - const style = {}; - - switch (type) { - case BlockType.GridBlock: - Object.assign(style, { - height: 400, - }); - break; - case BlockType.CalendarBlock: - case BlockType.BoardBlock: - Object.assign(style, { - height: 560, - }); - } - - return style; - }, [type]); useEffect(() => { if (!viewId) return; @@ -96,21 +82,75 @@ export const DatabaseBlock = memo( }, [loadViewMeta, viewId]); const handleNavigateToRow = useCallback( - (rowId: string) => { - if (!viewId || variant !== 'app') return; - window.open(`${window.origin}/app/${viewId}?r=${rowId}`, '_blank'); + async (rowId: string) => { + if (!viewId) return; + await navigateToView?.(viewId, rowId); }, - [variant, viewId], + [navigateToView, viewId], ); + const editor = useSlateStatic(); + const readOnly = useReadOnly() || editor.isElementReadOnly(node as unknown as Element); + + const containerRef = useRef(null); + const selectedView = useMemo(() => { + const database = doc?.getMap(YjsEditorKey.data_section)?.get(YjsEditorKey.database); + + return database?.get(YjsDatabaseKey.views)?.get(selectedViewId); + }, [doc, selectedViewId]); + const handleRendered = useCallback(async (height: number) => { + const container = containerRef.current; + + if (!container) return; + if (height > 0) { + container.style.height = `${height}px`; + } + + const layout = Number(selectedView?.get(YjsDatabaseKey.layout)); + + if (layout !== DatabaseViewLayout.Calendar) { + container.style.maxHeight = '550px'; + } + + }, [selectedView]); + + const [scrollLeft, setScrollLeft] = useState(0); + + useEffect(() => { + const editorDom = ReactEditor.toDOMNode(editor, editor); + const scrollContainer = getScrollParent(editorDom) as HTMLElement; + const layout = Number(selectedView?.get(YjsDatabaseKey.layout)); + + const onResize = () => { + const scrollRect = scrollContainer.getBoundingClientRect(); + + setScrollLeft(Math.max(editorDom.getBoundingClientRect().left - scrollRect.left, layout === DatabaseViewLayout.Grid ? 64 : 0)); + }; + + onResize(); + + const resizeObserver = new ResizeObserver(onResize); + + resizeObserver.observe(scrollContainer); + return () => { + resizeObserver.disconnect(); + }; + }, [editor, selectedView]); return ( <>
setIsHovering(true)} - onMouseLeave={() => setIsHovering(false)} + contentEditable={readOnly ? false : undefined} + className={`relative w-full cursor-pointer`} + onMouseEnter={() => { + if (variant === UIVariant.App) { + + setShowActions(true); + } + }} + onMouseLeave={() => { + setShowActions(false); + }} >
{children}
-
- {selectedViewId && doc ? ( - <> - - {isHovering && ( -
- - - -
- )} - - ) : ( -
- {notFound ? ( - <> -
{t('publish.hasNotBeenPublished')}
- - ) : ( - - )} -
- )} + +
+ + Read-only: Use the AppFlowy app to create or edit database pages. This feature will be available soon.
+ +
+ {selectedViewId && doc ? ( + <> + + + ) : ( +
+ {notFound ? ( + <> +
{t('publish.hasNotBeenPublished')}
+ + ) : ( + + )} +
+ )} +
+
+
); diff --git a/frontend/appflowy_web_app/src/components/editor/components/blocks/divider/DividerNode.tsx b/frontend/appflowy_web_app/src/components/editor/components/blocks/divider/DividerNode.tsx index fe558fd3f8..2efc6ede13 100644 --- a/frontend/appflowy_web_app/src/components/editor/components/blocks/divider/DividerNode.tsx +++ b/frontend/appflowy_web_app/src/components/editor/components/blocks/divider/DividerNode.tsx @@ -1,26 +1,33 @@ import { EditorElementProps, DividerNode as DividerBlock } from '@/components/editor/editor.type'; import React, { forwardRef, memo, useMemo } from 'react'; +import { useReadOnly, useSlateStatic } from 'slate-react'; +import { Element } from 'slate'; export const DividerNode = memo( forwardRef>( - ({ node: _node, children: children, ...attributes }, ref) => { + ({ node: node, children: children, ...attributes }, ref) => { + const editor = useSlateStatic(); + const readOnly = useReadOnly() || editor.isElementReadOnly(node as unknown as Element); + const className = useMemo(() => { return `${attributes.className ?? ''} divider-node relative w-full rounded`; }, [attributes.className]); return ( -
-
+
{children}
diff --git a/frontend/appflowy_web_app/src/components/editor/components/blocks/file/FileBlock.tsx b/frontend/appflowy_web_app/src/components/editor/components/blocks/file/FileBlock.tsx index 9b772356d0..4f8c32de05 100644 --- a/frontend/appflowy_web_app/src/components/editor/components/blocks/file/FileBlock.tsx +++ b/frontend/appflowy_web_app/src/components/editor/components/blocks/file/FileBlock.tsx @@ -1,18 +1,38 @@ -import { notify } from '@/components/_shared/notify'; -import RightTopActionsToolbar from '@/components/editor/components/block-actions/RightTopActionsToolbar'; -import { EditorElementProps, FileNode } from '@/components/editor/editor.type'; -import { copyTextToClipboard } from '@/utils/copy'; -import { downloadFile } from '@/utils/download'; -import React, { forwardRef, memo, useCallback, useMemo, useState } from 'react'; +import { BlockType, FieldURLType, FileBlockData } from '@/application/types'; import { ReactComponent as FileIcon } from '@/assets/file_upload.svg'; +import { ReactComponent as ReloadIcon } from '@/assets/reload.svg'; + +import { notify } from '@/components/_shared/notify'; +import { usePopoverContext } from '@/components/editor/components/block-popover/BlockPopoverContext'; +import FileToolbar from '@/components/editor/components/blocks/file/FileToolbar'; +import { EditorElementProps, FileNode } from '@/components/editor/editor.type'; +import React, { forwardRef, memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; +import { useReadOnly, useSlateStatic } from 'slate-react'; +import { openUrl } from '@/utils/url'; +import { CircularProgress, IconButton, Tooltip } from '@mui/material'; +import { useEditorContext } from '@/components/editor/EditorContext'; +import { YjsEditor } from '@/application/slate-yjs'; +import { CustomEditor } from '@/application/slate-yjs/command'; +import { FileHandler } from '@/utils/file'; +import { Element } from 'slate'; export const FileBlock = memo( forwardRef>(({ node, children, ...attributes }, ref) => { - const { url, name } = useMemo(() => node.data || {}, [node.data]); + const { blockId, data } = node; + const { uploadFile } = useEditorContext(); + const editor = useSlateStatic() as YjsEditor; + const [needRetry, setNeedRetry] = useState(false); + const fileHandler = useMemo(() => new FileHandler(), []); + const [localUrl, setLocalUrl] = useState(undefined); + const [loading, setLoading] = useState(false); + const { url, name, retry_local_url } = useMemo(() => data || {}, [data]); + const readOnly = useReadOnly() || editor.isElementReadOnly(node as unknown as Element); + const emptyRef = useRef(null); + const [showToolbar, setShowToolbar] = useState(false); const className = useMemo(() => { - const classList = ['w-full bg-bg-body py-2']; + const classList = ['w-full']; if (url) { classList.push('cursor-pointer'); @@ -24,62 +44,143 @@ export const FileBlock = memo( classList.push(attributes.className); } - return classList.join(' '); - }, [attributes.className, url]); - const [showToolbar, setShowToolbar] = useState(false); - const { t } = useTranslation(); + if (!readOnly) { + classList.push('cursor-pointer'); + } - const handleDownload = useCallback(async () => { + return classList.join(' '); + }, [attributes.className, readOnly, url]); + + const { t } = useTranslation(); + const { + openPopover, + } = usePopoverContext(); + + const handleClick = useCallback(async () => { try { - if (!url) return; - await downloadFile(url, name); + if (!url && !needRetry) { + if (emptyRef.current && !readOnly) { + openPopover(blockId, BlockType.FileBlock, emptyRef.current); + } + + return; + } + + const link = url || localUrl; + + if (link) { + void openUrl(link, '_blank'); + } // eslint-disable-next-line } catch (e: any) { notify.error(e.message); } - }, [url, name]); + }, [url, needRetry, localUrl, readOnly, openPopover, blockId]); + + useEffect(() => { + if (readOnly) return; + void (async () => { + if (retry_local_url) { + const fileData = await fileHandler.getStoredFile(retry_local_url); + + setLocalUrl(fileData?.url); + setNeedRetry(!!fileData); + } else { + setNeedRetry(false); + } + })(); + }, [readOnly, retry_local_url, fileHandler]); + + const uploadFileRemote = useCallback(async (file: File) => { + + try { + if (uploadFile) { + return await uploadFile(file); + } + // eslint-disable-next-line + } catch (e: any) { + return; + } + }, [uploadFile]); + + const handleRetry = useCallback(async (e: React.MouseEvent) => { + e.stopPropagation(); + if (!retry_local_url) return; + const fileData = await fileHandler.getStoredFile(retry_local_url); + const file = fileData?.file; + + if (!file) return; + + const url = await uploadFileRemote(file); + + if (!url) { + return; + } + + setLoading(true); + try { + await fileHandler.cleanup(retry_local_url); + CustomEditor.setBlockData(editor, blockId, { + url, + name, + uploaded_at: Date.now(), + url_type: FieldURLType.Upload, + retry_local_url: '', + } as FileBlockData); + } catch (e) { + // do nothing + } finally { + setLoading(false); + } + + }, [blockId, editor, fileHandler, name, retry_local_url, uploadFileRemote]); return (
{ if (!url) return; setShowToolbar(true); }} onMouseLeave={() => setShowToolbar(false)} - onClick={handleDownload} + onClick={handleClick} >
- -
- {url ? - <> +
+ +
+ +
+ {url || needRetry ? +
{name?.trim() || t('document.title.placeholder')}
- : + {needRetry && +
{t('web.fileBlock.uploadFailed')}
} +
:
{t('web.fileBlock.empty')}
}
+ {needRetry && ( + loading ? () : + + + + + + )} {showToolbar && url && ( - { - if (!url) return; - try { - await copyTextToClipboard(url); - notify.success(t('publish.copy.fileBlock')); - } catch (_) { - // do nothing - } - }} - /> + )}
(false); + const [fileName, setFileName] = useState(name); + const onCopy = async () => { + await copyTextToClipboard(node.data.url || ''); + notify.success(t('publish.copy.fileBlock')); + }; + + const onDelete = () => { + CustomEditor.deleteBlock(editor, node.blockId); + }; + + const onDownload = () => { + if (!url) return; + + void downloadFile(url, name); + }; + + const onUpdateName = () => { + if (!fileName || fileName === name) return; + CustomEditor.setBlockData(editor, node.blockId, { name: fileName }); + setOpen(false); + }; + + const inputRef = React.useRef(null); + + useEffect(() => { + if (!open) { + inputRef.current = null; + } + }, [open]); + + return ( +
e.stopPropagation()} + className={'absolute z-10 top-2.5 right-2.5'} + > +
+ + + + + + + + + {!readOnly && <> + { + setOpen(true); + }} + tooltip={t('document.plugins.file.renameFile.title')} + > + + + + + + setOpen(false)} + okText={t('button.save')} + onOk={onUpdateName} + title={ +
{t('document.plugins.file.renameFile.title')}
+ } + > +
+
{t('trash.pageHeader.fileName')}
+ { + if (!input) return; + if (!inputRef.current) { + setTimeout(() => { + input.setSelectionRange(0, input.value.length); + }, 50); + + inputRef.current = input; + } + }} + onClick={(e) => { + if (e.detail > 2) { + const target = e.target as HTMLInputElement; + + // select all text on triple click + target.setSelectionRange(0, target.value.length); + } + }} + onChange={(e) => setFileName(e.target.value)} + size={'small'} + /> +
+
+ } + +
+ +
+ ); +} + +export default FileToolbar; \ No newline at end of file diff --git a/frontend/appflowy_web_app/src/components/editor/components/blocks/gallery/Carousel.tsx b/frontend/appflowy_web_app/src/components/editor/components/blocks/gallery/Carousel.tsx index e666747fe1..3d61208b9a 100644 --- a/frontend/appflowy_web_app/src/components/editor/components/blocks/gallery/Carousel.tsx +++ b/frontend/appflowy_web_app/src/components/editor/components/blocks/gallery/Carousel.tsx @@ -68,8 +68,11 @@ function Carousel ({ images, onPreview, autoplay }: { }, [handleAfterSlide, handleInit, images, rendered]); return ( -
-
+
+
{renderCarousel} diff --git a/frontend/appflowy_web_app/src/components/editor/components/blocks/gallery/GalleryBlock.tsx b/frontend/appflowy_web_app/src/components/editor/components/blocks/gallery/GalleryBlock.tsx index cc1c518817..c786691ba4 100644 --- a/frontend/appflowy_web_app/src/components/editor/components/blocks/gallery/GalleryBlock.tsx +++ b/frontend/appflowy_web_app/src/components/editor/components/blocks/gallery/GalleryBlock.tsx @@ -9,6 +9,7 @@ import { EditorElementProps, GalleryBlockNode } from '@/components/editor/editor import { copyTextToClipboard } from '@/utils/copy'; import React, { forwardRef, memo, Suspense, useCallback, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; +import { useReadOnly } from 'slate-react'; const GalleryBlock = memo( forwardRef>(({ @@ -73,11 +74,12 @@ const GalleryBlock = memo( const handlePreviewIndex = useCallback((index: number) => { previewIndexRef.current = index; }, []); + const readOnly = useReadOnly(); return (
{ if (!photos.length) return; @@ -85,31 +87,40 @@ const GalleryBlock = memo( }} onMouseLeave={() => setHovered(false)} > -
+
{children}
- {photos.length > 0 ? - (layout === GalleryLayout.Carousel ? - : - { - previewIndexRef.current = index; - handleOpenPreview(); - }} - images={photos} - /> - ) :
- - {t('document.plugins.image.addAnImageMobile')} -
} +
0 ? '!bg-transparent !border-none !rounded-none' : ''}`} + > + {photos.length > 0 ? + (layout === GalleryLayout.Carousel ? + : + { + previewIndexRef.current = index; + handleOpenPreview(); + }} + images={photos} + /> + ) :
+ + {t('document.plugins.image.addAnImageMobile')} +
} +
+ {hovered && >(({ node, children, ...attributes }, ref) => { const level = node.data.level; - const fontSizeCssProperty = getHeadingCssProperty(level); - const className = `${attributes.className ?? ''} ${fontSizeCssProperty} level-${level}`; + const className = `${attributes.className ?? ''} heading level-${level}`; return (
>(({ node, children, - className, ...attributes }, ref) => { + const { t } = useTranslation(); + + const { blockId, data } = node; + const retry_local_url = data?.retry_local_url; + const { uploadFile } = useEditorContext(); + const editor = useSlateStatic() as YjsEditor; + const [needRetry, setNeedRetry] = useState(false); + const [localUrl, setLocalUrl] = useState(undefined); + const [loading, setLoading] = useState(false); + + const fileHandler = useMemo(() => new FileHandler(), []); + const readOnly = useReadOnly() || editor.isElementReadOnly(node as unknown as Element); const selected = useSelected(); - const { url, align } = useMemo(() => node.data || {}, [node.data]); + const { url, align } = useMemo(() => data || {}, [data]); const containerRef = useRef(null); - const editor = useSlateStatic(); const onFocusNode = useCallback(() => { ReactEditor.focus(editor); const path = ReactEditor.findPath(editor, node); @@ -23,40 +43,125 @@ export const ImageBlock = memo( editor.select(path); }, [editor, node]); + const className = useMemo(() => { + const classList = ['w-full']; + + if (!readOnly) { + classList.push('cursor-pointer'); + } + + if (attributes.className) { + classList.push(attributes.className); + } + + return classList.join(' '); + }, [attributes.className, readOnly]); + const alignCss = useMemo(() => { if (!align) return ''; return align === AlignType.Center ? 'justify-center' : align === AlignType.Right ? 'justify-end' : 'justify-start'; }, [align]); const [showToolbar, setShowToolbar] = useState(false); + const { + openPopover, + } = usePopoverContext(); + + const handleClick = useCallback(async () => { + try { + if (!url && !needRetry) { + if (containerRef.current && !readOnly) { + openPopover(blockId, BlockType.ImageBlock, containerRef.current); + } + + return; + } + + // eslint-disable-next-line + } catch (e: any) { + notify.error(e.message); + } + }, [needRetry, url, readOnly, openPopover, blockId]); + + useEffect(() => { + if (readOnly) return; + void (async () => { + if (retry_local_url) { + const fileData = await fileHandler.getStoredFile(retry_local_url); + + setLocalUrl(fileData?.url); + setNeedRetry(!!fileData); + } else { + setNeedRetry(false); + } + })(); + }, [readOnly, retry_local_url, fileHandler]); + + const uploadFileRemote = useCallback(async (file: File) => { + + try { + if (uploadFile) { + return await uploadFile(file); + } + // eslint-disable-next-line + } catch (e: any) { + return; + } + }, [uploadFile]); + + const handleRetry = useCallback(async (e: React.MouseEvent) => { + e.stopPropagation(); + if (!retry_local_url) return; + const fileData = await fileHandler.getStoredFile(retry_local_url); + const file = fileData?.file; + + if (!file) return; + + const url = await uploadFileRemote(file); + + if (!url) { + return; + } + + setLoading(true); + try { + await fileHandler.cleanup(retry_local_url); + CustomEditor.setBlockData(editor, blockId, { + url, + image_type: ImageType.External, + retry_local_url: '', + } as ImageBlockData); + } catch (e) { + // do noting + } finally { + setLoading(false); + } + }, [blockId, editor, fileHandler, retry_local_url, uploadFileRemote]); return (
{ if (!url) return; setShowToolbar(true); }} onMouseLeave={() => setShowToolbar(false)} - className={`${className || ''} image-block relative w-full cursor-default`} + className={className} + onClick={handleClick} > -
- {children} -
+
- {url ? ( + {url || needRetry ? ( ) : ( )} + {needRetry &&
+ +
{t('button.uploadFailed')}
+ {loading ? : + } +
} +
+
+ {children}
); diff --git a/frontend/appflowy_web_app/src/components/editor/components/blocks/image/ImageEmpty.tsx b/frontend/appflowy_web_app/src/components/editor/components/blocks/image/ImageEmpty.tsx index 187c5615b4..9d23f625de 100644 --- a/frontend/appflowy_web_app/src/components/editor/components/blocks/image/ImageEmpty.tsx +++ b/frontend/appflowy_web_app/src/components/editor/components/blocks/image/ImageEmpty.tsx @@ -10,11 +10,11 @@ function ImageEmpty (_: { containerRef: React.RefObject; onEscap <>
- - {t('document.plugins.image.addAnImage')} + + {t('document.plugins.image.addAnImageDesktop')}
); diff --git a/frontend/appflowy_web_app/src/components/editor/components/blocks/image/ImageRender.tsx b/frontend/appflowy_web_app/src/components/editor/components/blocks/image/ImageRender.tsx index 1e529549df..fed4d596d0 100644 --- a/frontend/appflowy_web_app/src/components/editor/components/blocks/image/ImageRender.tsx +++ b/frontend/appflowy_web_app/src/components/editor/components/blocks/image/ImageRender.tsx @@ -1,11 +1,15 @@ -import { notify } from '@/components/_shared/notify'; -import RightTopActionsToolbar from '@/components/editor/components/block-actions/RightTopActionsToolbar'; +import { YjsEditor } from '@/application/slate-yjs'; +import { CustomEditor } from '@/application/slate-yjs/command'; +import ImageResizer from '@/components/editor/components/blocks/image/ImageResizer'; +import ImageToolbar from '@/components/editor/components/blocks/image/ImageToolbar'; import { ImageBlockNode } from '@/components/editor/editor.type'; -import { copyTextToClipboard } from '@/utils/copy'; +import { debounce } from 'lodash-es'; import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Skeleton } from '@mui/material'; import { ReactComponent as ErrorOutline } from '@/assets/error.svg'; +import { useReadOnly, useSlateStatic } from 'slate-react'; +import { Element } from 'slate'; const MIN_WIDTH = 100; @@ -13,20 +17,26 @@ function ImageRender({ selected, node, showToolbar, + localUrl, }: { selected: boolean; + localUrl?: string; node: ImageBlockNode; showToolbar?: boolean; }) { + const editor = useSlateStatic() as YjsEditor; + const readOnly = useReadOnly() || editor.isElementReadOnly(node as unknown as Element); + const [loading, setLoading] = useState(true); const [hasError, setHasError] = useState(false); const imgRef = useRef(null); - const { url = '', width: imageWidth } = useMemo(() => node.data || {}, [node.data]); + const { width: imageWidth } = useMemo(() => node.data || {}, [node.data]); + const url = node.data.url || localUrl; const { t } = useTranslation(); const blockId = node.blockId; const [initialWidth, setInitialWidth] = useState(null); - const [newWidth] = useState(imageWidth ?? null); + const [newWidth, setNewWidth] = useState(imageWidth ?? null); useEffect(() => { if (!loading && !hasError && initialWidth === null && imgRef.current) { @@ -62,12 +72,28 @@ function ImageRender({ 'flex h-[48px] w-full items-center justify-center gap-2 rounded border border-function-error bg-red-50' } > - +
{t('editor.imageLoadFailed')}
); }, [t]); + const debounceSubmitWidth = useMemo(() => { + return debounce((newWidth: number) => { + CustomEditor.setBlockData(editor, node.blockId, { + width: newWidth, + }); + }, 300); + }, [editor, node]); + + const handleWidthChange = useCallback( + (newWidth: number) => { + setNewWidth(newWidth); + debounceSubmitWidth(newWidth); + }, + [debounceSubmitWidth], + ); + if (!url) return null; return ( @@ -78,21 +104,31 @@ function ImageRender({ }} className={`image-render relative min-h-[48px] ${hasError ? 'w-full' : ''}`} > - {`image-${blockId}`} - {showToolbar && url && ( - { - if (!url) return; - try { - await copyTextToClipboard(url); - notify.success(t('publish.copy.imageBlock')); - } catch (_) { - // do nothing - } - }} - /> + {`image-${blockId}`} + {!readOnly && initialWidth && ( + <> + + + )} - {hasError ? renderErrorNode() : loading ? : null} + {showToolbar && } + {hasError ? renderErrorNode() : loading ? : null}
); } diff --git a/frontend/appflowy_web_app/src/components/editor/components/blocks/image/ImageResizer.tsx b/frontend/appflowy_web_app/src/components/editor/components/blocks/image/ImageResizer.tsx new file mode 100644 index 0000000000..e0d272acf3 --- /dev/null +++ b/frontend/appflowy_web_app/src/components/editor/components/blocks/image/ImageResizer.tsx @@ -0,0 +1,61 @@ +import React, { useCallback, useRef } from 'react'; + +function ImageResizer({ + minWidth, + width, + onWidthChange, + isLeft, +}: { + isLeft?: boolean; + minWidth: number; + width: number; + onWidthChange: (newWidth: number) => void; +}) { + const originalWidth = useRef(width); + const startX = useRef(0); + + const onResize = useCallback( + (e: MouseEvent) => { + e.preventDefault(); + const diff = isLeft ? startX.current - e.clientX : e.clientX - startX.current; + const newWidth = originalWidth.current + diff; + + if (newWidth < minWidth) { + return; + } + + onWidthChange(newWidth); + }, + [isLeft, minWidth, onWidthChange] + ); + + const onResizeEnd = useCallback(() => { + document.removeEventListener('mousemove', onResize); + document.removeEventListener('mouseup', onResizeEnd); + }, [onResize]); + + const onResizeStart = useCallback( + (e: React.MouseEvent) => { + startX.current = e.clientX; + originalWidth.current = width; + document.addEventListener('mousemove', onResize); + document.addEventListener('mouseup', onResizeEnd); + }, + [onResize, onResizeEnd, width] + ); + + return ( +
+
+
+ ); +} + +export default ImageResizer; diff --git a/frontend/appflowy_web_app/src/components/editor/components/blocks/image/ImageToolbar.tsx b/frontend/appflowy_web_app/src/components/editor/components/blocks/image/ImageToolbar.tsx new file mode 100644 index 0000000000..65771ef214 --- /dev/null +++ b/frontend/appflowy_web_app/src/components/editor/components/blocks/image/ImageToolbar.tsx @@ -0,0 +1,85 @@ +import { YjsEditor } from '@/application/slate-yjs'; +import { CustomEditor } from '@/application/slate-yjs/command'; +import { GalleryPreview } from '@/components/_shared/gallery-preview'; +import { notify } from '@/components/_shared/notify'; +import ActionButton from '@/components/editor/components/toolbar/selection-toolbar/actions/ActionButton'; +import Align from '@/components/editor/components/toolbar/selection-toolbar/actions/Align'; +import { ImageBlockNode } from '@/components/editor/editor.type'; +import { copyTextToClipboard } from '@/utils/copy'; +import { Divider } from '@mui/material'; +import React, { Suspense } from 'react'; +import { useTranslation } from 'react-i18next'; +import { ReactComponent as CopyIcon } from '@/assets/copy.svg'; +import { ReactComponent as PreviewIcon } from '@/assets/full_view.svg'; +import { ReactComponent as DeleteIcon } from '@/assets/trash.svg'; +import { useReadOnly, useSlateStatic } from 'slate-react'; +import { Element } from 'slate'; + +function ImageToolbar({ node }: { + node: ImageBlockNode +}) { + const editor = useSlateStatic() as YjsEditor; + const readOnly = useReadOnly() || editor.isElementReadOnly(node as unknown as Element); + const { t } = useTranslation(); + const [openPreview, setOpenPreview] = React.useState(false); + + const onOpenPreview = () => { + setOpenPreview(true); + }; + + const onCopy = async () => { + await copyTextToClipboard(node.data.url || ''); + notify.success(t('document.plugins.image.copiedToPasteBoard')); + }; + + const onDelete = () => { + CustomEditor.deleteBlock(editor, node.blockId); + }; + + return ( +
+
+ {!readOnly && + + } + + + + + + {!readOnly && <> + + + + + } + +
+ {openPreview && { + setOpenPreview(false); + }} + />} +
+ ); +} + +export default ImageToolbar; \ No newline at end of file diff --git a/frontend/appflowy_web_app/src/components/editor/components/blocks/link-preview/LinkPreview.tsx b/frontend/appflowy_web_app/src/components/editor/components/blocks/link-preview/LinkPreview.tsx index 4de2b248a2..559f4d8090 100644 --- a/frontend/appflowy_web_app/src/components/editor/components/blocks/link-preview/LinkPreview.tsx +++ b/frontend/appflowy_web_app/src/components/editor/components/blocks/link-preview/LinkPreview.tsx @@ -1,6 +1,8 @@ import { EditorElementProps, LinkPreviewNode } from '@/components/editor/editor.type'; import axios from 'axios'; import React, { forwardRef, memo, useEffect, useState } from 'react'; +import { useReadOnly } from 'slate-react'; +import emptyImageSrc from '@/assets/images/empty.png'; export const LinkPreview = memo( forwardRef>(({ node, children, ...attributes }, ref) => { @@ -34,25 +36,39 @@ export const LinkPreview = memo( } })(); }, [url]); + const readOnly = useReadOnly(); + return (
{ window.open(url, '_blank'); }} - contentEditable={false} + contentEditable={readOnly ? false : undefined} {...attributes} ref={ref} - className={`link-preview-block relative w-full cursor-pointer py-1`} + className={`link-preview-block relative w-full cursor-pointer`} >
{notFound ? ( -
-
Could not load preview
-
{url}
+
+
+ {'Empty +
+
+
+ The link cannot be previewed. Click to open in a new tab. +
+
+ {url} +
+
+
) : ( <> diff --git a/frontend/appflowy_web_app/src/components/editor/components/blocks/math-equation/MathEquation.tsx b/frontend/appflowy_web_app/src/components/editor/components/blocks/math-equation/MathEquation.tsx index 7ffc5f221e..3db427b2f3 100644 --- a/frontend/appflowy_web_app/src/components/editor/components/blocks/math-equation/MathEquation.tsx +++ b/frontend/appflowy_web_app/src/components/editor/components/blocks/math-equation/MathEquation.tsx @@ -1,74 +1,88 @@ -import { KatexMath } from '@/components/_shared/katex-math'; -import { notify } from '@/components/_shared/notify'; -import RightTopActionsToolbar from '@/components/editor/components/block-actions/RightTopActionsToolbar'; -import { EditorElementProps, MathEquationNode } from '@/components/editor/editor.type'; -import { copyTextToClipboard } from '@/utils/copy'; +import { BlockType } from '@/application/types'; import { ReactComponent as MathSvg } from '@/assets/math.svg'; +import { KatexMath } from '@/components/_shared/katex-math'; +import { usePopoverContext } from '@/components/editor/components/block-popover/BlockPopoverContext'; +import MathEquationToolbar from '@/components/editor/components/blocks/math-equation/MathEquationToolbar'; +import { EditorElementProps, MathEquationNode } from '@/components/editor/editor.type'; import React, { forwardRef, memo, Suspense, useMemo, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; +import { useReadOnly, useSlateStatic } from 'slate-react'; +import { Element } from 'slate'; export const MathEquation = memo( forwardRef>( ({ node, children, className, ...attributes }, ref) => { const formula = node.data.formula; + const editor = useSlateStatic(); + + const readOnly = useReadOnly() || editor.isElementReadOnly(node as unknown as Element); + const { t } = useTranslation(); const containerRef = useRef(null); const [showToolbar, setShowToolbar] = useState(false); const newClassName = useMemo(() => { const classList = [ className, - 'math-equation-block relative w-full container-bg w-full py-1 overflow-hidden select-none rounded-[8px]', + 'w-full', ]; - if (formula) { - classList.push('border border-transparent hover:border-line-divider hover:bg-fill-list-active cursor-pointer'); + if (!readOnly) { + classList.push('cursor-pointer'); } return classList.join(' '); - }, [formula, className]); + }, [className, readOnly]); + const { + openPopover, + } = usePopoverContext(); return ( <>
{ if (!formula) return; setShowToolbar(true); }} onMouseLeave={() => setShowToolbar(false)} className={newClassName} + onClick={e => { + if (!readOnly) { + e.preventDefault(); + e.stopPropagation(); + openPopover(node.blockId, BlockType.EquationBlock, e.currentTarget); + } + }} > - {formula ? ( - - - - ) : ( -
- - {t('document.plugins.mathEquation.addMathEquation')} -
- )} -
+
+ {formula ? ( +
+ + + +
+ + ) : ( +
+ + {t('document.plugins.mathEquation.addMathEquation')} +
+ )} +
+ +
{children}
{showToolbar && ( - { - if (!formula) return; - try { - await copyTextToClipboard(formula); - notify.success(t('publish.copy.mathBlock')); - } catch (_) { - // do nothing - } - }} - /> + )}
diff --git a/frontend/appflowy_web_app/src/components/editor/components/blocks/math-equation/MathEquationToolbar.tsx b/frontend/appflowy_web_app/src/components/editor/components/blocks/math-equation/MathEquationToolbar.tsx new file mode 100644 index 0000000000..4af8948f6d --- /dev/null +++ b/frontend/appflowy_web_app/src/components/editor/components/blocks/math-equation/MathEquationToolbar.tsx @@ -0,0 +1,40 @@ +import { notify } from '@/components/_shared/notify'; +import ActionButton from '@/components/editor/components/toolbar/selection-toolbar/actions/ActionButton'; +import { MathEquationNode } from '@/components/editor/editor.type'; +import { copyTextToClipboard } from '@/utils/copy'; +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import { ReactComponent as CopyIcon } from '@/assets/copy.svg'; + +function MathEquationToolbar ({ + node, +}: { + node: MathEquationNode +}) { + const { t } = useTranslation(); + const formula = node.data.formula || ''; + + const onCopy = async () => { + await copyTextToClipboard(formula); + notify.success(t('publish.copy.mathBlock')); + }; + + return ( +
e.stopPropagation()} + className={'absolute z-10 top-2 right-1'} + > +
+ + + +
+
+ ); +} + +export default MathEquationToolbar; \ No newline at end of file diff --git a/frontend/appflowy_web_app/src/components/editor/components/blocks/outline/Outline.tsx b/frontend/appflowy_web_app/src/components/editor/components/blocks/outline/Outline.tsx index af02fbe45c..ec3cb5e11a 100644 --- a/frontend/appflowy_web_app/src/components/editor/components/blocks/outline/Outline.tsx +++ b/frontend/appflowy_web_app/src/components/editor/components/blocks/outline/Outline.tsx @@ -2,14 +2,16 @@ import { extractHeadings, nestHeadings } from '@/components/editor/components/bl import { EditorElementProps, HeadingNode, OutlineNode } from '@/components/editor/editor.type'; import React, { forwardRef, memo, useCallback, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { useSlate } from 'slate-react'; +import { useReadOnly, useSlate } from 'slate-react'; import smoothScrollIntoViewIfNeeded from 'smooth-scroll-into-view-if-needed'; +import { Element } from 'slate'; export const Outline = memo( forwardRef>(({ node, children, className, ...attributes }, ref) => { const editor = useSlate(); const [root, setRoot] = useState([]); const { t } = useTranslation(); + const readOnly = useReadOnly() || editor.isElementReadOnly(node as unknown as Element); useEffect(() => { const root = nestHeadings(extractHeadings(editor, node.data.depth || 6)); @@ -56,11 +58,13 @@ export const Outline = memo( ); return ( -
{children} diff --git a/frontend/appflowy_web_app/src/components/editor/components/blocks/quote/Quote.tsx b/frontend/appflowy_web_app/src/components/editor/components/blocks/quote/Quote.tsx index fb8ee542e4..7dbe9a3d75 100644 --- a/frontend/appflowy_web_app/src/components/editor/components/blocks/quote/Quote.tsx +++ b/frontend/appflowy_web_app/src/components/editor/components/blocks/quote/Quote.tsx @@ -4,14 +4,16 @@ import React, { forwardRef, memo, useMemo } from 'react'; export const Quote = memo( forwardRef>(({ node: _, children, ...attributes }, ref) => { const className = useMemo(() => { - return `my-1 ${attributes.className ?? ''} pl-3 quote-block`; + return `${attributes.className ?? ''} pl-[9px] quote-block`; }, [attributes.className]); return ( -
-
+
{children}
diff --git a/frontend/appflowy_web_app/src/components/editor/components/blocks/simple-table/SimpleTableCell.tsx b/frontend/appflowy_web_app/src/components/editor/components/blocks/simple-table/SimpleTableCell.tsx index 4a6de0ee57..1fc1513152 100644 --- a/frontend/appflowy_web_app/src/components/editor/components/blocks/simple-table/SimpleTableCell.tsx +++ b/frontend/appflowy_web_app/src/components/editor/components/blocks/simple-table/SimpleTableCell.tsx @@ -84,7 +84,8 @@ const SimpleTableCell = style={{ ...attributes.style, backgroundColor: bgColor ? renderColor(bgColor) : undefined, - maxWidth: width ? `${width}px` : undefined, + minWidth: width ? `${width}px` : undefined, + width: width ? `${width}px` : undefined, }} >
div { + margin-top: 4px !important; + margin-bottom: 4px !important; + } } td { diff --git a/frontend/appflowy_web_app/src/components/editor/components/blocks/sub-page/SubPage.tsx b/frontend/appflowy_web_app/src/components/editor/components/blocks/sub-page/SubPage.tsx index f1f5cfd705..a23930916e 100644 --- a/frontend/appflowy_web_app/src/components/editor/components/blocks/sub-page/SubPage.tsx +++ b/frontend/appflowy_web_app/src/components/editor/components/blocks/sub-page/SubPage.tsx @@ -1,6 +1,7 @@ import MentionPage from '@/components/editor/components/leaf/mention/MentionPage'; import { EditorElementProps, SubpageNode } from '@/components/editor/editor.type'; import React, { forwardRef, memo, useMemo } from 'react'; +import { useReadOnly } from 'slate-react'; export const SubPage = memo( forwardRef>(({ node, children, ...attributes }, ref) => { @@ -11,11 +12,12 @@ export const SubPage = memo( }, [attributes.className]); const pageId = node.data.view_id; + const readOnly = useReadOnly(); return (
{children}
- +
); }), diff --git a/frontend/appflowy_web_app/src/components/editor/components/blocks/table/Table.tsx b/frontend/appflowy_web_app/src/components/editor/components/blocks/table/Table.tsx index 60a44dae4a..546b62b0d7 100644 --- a/frontend/appflowy_web_app/src/components/editor/components/blocks/table/Table.tsx +++ b/frontend/appflowy_web_app/src/components/editor/components/blocks/table/Table.tsx @@ -1,11 +1,10 @@ +import TableContainer from '@/components/editor/components/table-container/TableContainer'; import { EditorElementProps, TableCellNode, TableNode } from '@/components/editor/editor.type'; import { useEditorContext } from '@/components/editor/EditorContext'; -import { getScrollParent } from '@/components/global-comment/utils'; -import React, { forwardRef, memo, useEffect, useMemo, useRef, useCallback } from 'react'; +import React, { forwardRef, memo, useMemo } from 'react'; import { Grid } from '@atlaskit/primitives'; import './table.scss'; import isEqual from 'lodash-es/isEqual'; -import { ReactEditor, useSlateStatic } from 'slate-react'; const Table = memo( forwardRef>(({ node, children, className, ...attributes }, ref) => { @@ -13,25 +12,23 @@ const Table = memo( const readSummary = context.readSummary; const { rowsLen, colsLen, rowDefaultHeight, colsHeight } = node.data; const cells = node.children as TableCellNode[]; - const [width, setWidth] = React.useState(undefined); - const offsetLeftRef = useRef(0); const columnGroup = useMemo(() => { return Array.from({ length: colsLen }, (_, index) => { - return cells.filter((cell) => cell.data.colPosition === index); + return cells.filter((cell) => cell?.data.colPosition === index); }); }, [cells, colsLen]); const rowGroup = useMemo(() => { return Array.from({ length: rowsLen }, (_, index) => { - return cells.filter((cell) => cell.data.rowPosition === index); + return cells.filter((cell) => cell?.data.rowPosition === index); }); }, [cells, rowsLen]); const templateColumns = useMemo(() => { return columnGroup .map((group) => { - return `${group[0].data.width || colsHeight}px`; + return `${group[0]?.data.width || colsHeight}px`; }) .join(' '); }, [colsHeight, columnGroup]); @@ -39,69 +36,34 @@ const Table = memo( const templateRows = useMemo(() => { return rowGroup .map((group) => { - return `${group[0].data.height || rowDefaultHeight}px`; + return `${group[0]?.data.height || rowDefaultHeight}px`; }) .join(' '); }, [rowGroup, rowDefaultHeight]); - const editor = useSlateStatic(); - - const calcTableWidth = useCallback((editorDom: HTMLElement, scrollContainer: HTMLElement) => { - const scrollRect = scrollContainer.getBoundingClientRect(); - - setWidth(scrollRect.width); - offsetLeftRef.current = editorDom.getBoundingClientRect().left - scrollRect.left; - }, []); - - useEffect(() => { - if (readSummary) return; - const editorDom = ReactEditor.toDOMNode(editor, editor); - const scrollContainer = getScrollParent(editorDom) as HTMLElement; - - if (!scrollContainer) return; - calcTableWidth(editorDom, scrollContainer); - const onResize = () => { - calcTableWidth(editorDom, scrollContainer); - }; - - const resizeObserver = new ResizeObserver(onResize); - - resizeObserver.observe(scrollContainer); - return () => { - resizeObserver.disconnect(); - }; - }, [calcTableWidth, editor, readSummary]); - return (
-
{children} -
+
+ ); }), (prevProps, nextProps) => isEqual(prevProps.node, nextProps.node), diff --git a/frontend/appflowy_web_app/src/components/editor/components/blocks/table/TableCell.tsx b/frontend/appflowy_web_app/src/components/editor/components/blocks/table/TableCell.tsx index 1ee5e0af76..24e99776d7 100644 --- a/frontend/appflowy_web_app/src/components/editor/components/blocks/table/TableCell.tsx +++ b/frontend/appflowy_web_app/src/components/editor/components/blocks/table/TableCell.tsx @@ -10,8 +10,10 @@ const TableCell = memo( return (
{ const path = ReactEditor.findPath(editor, node); const match = Editor.above(editor, { @@ -28,21 +30,40 @@ function Placeholder({ node, ...attributes }: { node: Element; className?: strin }, [editor, node]); const className = useMemo(() => { - return `text-placeholder select-none ${attributes.className ?? ''}`; + const classList = attributes.className?.split(' ') ?? []; + + classList.push('text-placeholder select-none'); + + return classList.join(' '); }, [attributes.className]); const unSelectedPlaceholder = useMemo(() => { switch (block?.type) { case BlockType.Paragraph: { - if (editor.children.length === 1 && !readOnly) { - return t('editor.slashPlaceHolder'); - } - return ''; } - case BlockType.ToggleListBlock: - return t('blockPlaceholders.bulletList'); + case BlockType.ToggleListBlock: { + const level = (block as ToggleListNode).data.level; + + switch (level) { + case 1: + return t('editor.mobileHeading1'); + case 2: + return t('editor.mobileHeading2'); + case 3: + return t('editor.mobileHeading3'); + case 4: + return t('editor.mobileHeading4'); + case 5: + return t('editor.mobileHeading5'); + case 6: + return t('editor.mobileHeading6'); + default: + return t('blockPlaceholders.bulletList'); + } + } + case BlockType.QuoteBlock: return t('blockPlaceholders.quote'); case BlockType.TodoListBlock: @@ -61,37 +82,44 @@ function Placeholder({ node, ...attributes }: { node: Element; className?: strin return t('editor.mobileHeading2'); case 3: return t('editor.mobileHeading3'); + case 4: + return t('editor.mobileHeading4'); + case 5: + return t('editor.mobileHeading5'); + case 6: + return t('editor.mobileHeading6'); default: return ''; } } - case BlockType.Page: - return t('document.title.placeholder'); - case BlockType.CalloutBlock: case BlockType.CodeBlock: return t('editor.typeSomething'); default: return ''; } - }, [readOnly, block, t, editor.children.length]); + }, [block, t]); const selectedPlaceholder = useMemo(() => { + if (block?.type === BlockType.ToggleListBlock && (block?.data as ToggleListBlockData).level) { + return unSelectedPlaceholder; + } + switch (block?.type) { case BlockType.HeadingBlock: return unSelectedPlaceholder; - case BlockType.Page: - return t('document.title.placeholder'); - case BlockType.GridBlock: - case BlockType.EquationBlock: - case BlockType.CodeBlock: - case BlockType.DividerBlock: - return ''; + case BlockType.ToggleListBlock: + case BlockType.TodoListBlock: + case BlockType.Paragraph: + case BlockType.QuoteBlock: + case BlockType.BulletedListBlock: + case BlockType.NumberedListBlock: + return t('editor.slashPlaceHolder'); default: - return t('editor.slashPlaceHolder'); + return ''; } - }, [block?.type, t, unSelectedPlaceholder]); + }, [block?.data, block?.type, t, unSelectedPlaceholder]); useEffect(() => { if (!selected) return; diff --git a/frontend/appflowy_web_app/src/components/editor/components/blocks/text/StartIcon.hooks.tsx b/frontend/appflowy_web_app/src/components/editor/components/blocks/text/StartIcon.hooks.tsx index 136e9583d2..7d80310bb6 100644 --- a/frontend/appflowy_web_app/src/components/editor/components/blocks/text/StartIcon.hooks.tsx +++ b/frontend/appflowy_web_app/src/components/editor/components/blocks/text/StartIcon.hooks.tsx @@ -1,5 +1,6 @@ import { BlockType } from '@/application/types'; import { BulletedListIcon } from '@/components/editor/components/blocks/bulleted-list'; +import CalloutIcon from '@/components/editor/components/blocks/callout/CalloutIcon'; import { NumberListIcon } from '@/components/editor/components/blocks/numbered-list'; import ToggleIcon from '@/components/editor/components/blocks/toggle-list/ToggleIcon'; import { TextNode } from '@/components/editor/editor.type'; @@ -27,6 +28,8 @@ export function useStartIcon (node: TextNode) { return NumberListIcon; case BlockType.BulletedListBlock: return BulletedListIcon; + case BlockType.CalloutBlock: + return CalloutIcon; default: return null; } @@ -37,11 +40,12 @@ export function useStartIcon (node: TextNode) { return null; } - const classList = ['text-block-icon relative w-[24px]']; + const classList = ['text-block-icon relative select-none w-6 h-6']; - classList.push('h-6'); - - return ; + return ; }, [Component, block]); return { diff --git a/frontend/appflowy_web_app/src/components/editor/components/blocks/text/Text.tsx b/frontend/appflowy_web_app/src/components/editor/components/blocks/text/Text.tsx index e3a228c1b5..fb83490aef 100644 --- a/frontend/appflowy_web_app/src/components/editor/components/blocks/text/Text.tsx +++ b/frontend/appflowy_web_app/src/components/editor/components/blocks/text/Text.tsx @@ -10,7 +10,7 @@ export const Text = forwardRef>( const editor = useSlateStatic(); const isEmpty = editor.isEmpty(node); const className = useMemo(() => { - const classList = ['text-element', 'relative', 'flex', 'w-full', 'whitespace-pre-wrap', 'break-word', 'px-1']; + const classList = ['text-element', 'relative', 'flex', 'w-full', 'whitespace-pre-wrap', 'break-word']; if (classNameProp) classList.push(classNameProp); if (hasStartIcon) classList.push('has-start-icon'); @@ -19,18 +19,21 @@ export const Text = forwardRef>( const placeholder = useMemo(() => { if (!isEmpty) return null; - return ; + return ; }, [isEmpty, node]); const content = useMemo(() => { return <> - {placeholder} - {children} + + + {placeholder}{children} ; }, [placeholder, isEmpty, children]); return ( - + {renderIcon()} {content} diff --git a/frontend/appflowy_web_app/src/components/editor/components/blocks/todo-list/CheckboxIcon.tsx b/frontend/appflowy_web_app/src/components/editor/components/blocks/todo-list/CheckboxIcon.tsx index 4a76234f48..b47b7d62c3 100644 --- a/frontend/appflowy_web_app/src/components/editor/components/blocks/todo-list/CheckboxIcon.tsx +++ b/frontend/appflowy_web_app/src/components/editor/components/blocks/todo-list/CheckboxIcon.tsx @@ -1,32 +1,30 @@ import { YjsEditor } from '@/application/slate-yjs'; import { CustomEditor } from '@/application/slate-yjs/command'; import { TodoListNode } from '@/components/editor/editor.type'; -import { debounce } from 'lodash-es'; -import React, { useCallback, useMemo } from 'react'; +import React, { useCallback } from 'react'; import { ReactComponent as CheckboxCheckSvg } from '$icons/16x/check_filled.svg'; import { ReactComponent as CheckboxUncheckSvg } from '$icons/16x/uncheck.svg'; import { useReadOnly, useSlateStatic } from 'slate-react'; +import { Element } from 'slate'; -function CheckboxIcon ({ block, className }: { block: TodoListNode; className: string }) { +function CheckboxIcon({ block, className }: { block: TodoListNode; className: string }) { const { checked } = block.data; const editor = useSlateStatic(); - const readOnly = useReadOnly(); + const readOnly = useReadOnly() || editor.isElementReadOnly(block as unknown as Element); - const toggleChecked = useMemo(() => { + const handleClick = useCallback((e: React.MouseEvent) => { if (readOnly) { return; } - return debounce(() => { - CustomEditor.toggleTodoList(editor as YjsEditor, block.blockId); - }, 100); - }, [readOnly, editor, block.blockId]); - - const handleClick = useCallback((e: React.MouseEvent) => { e.stopPropagation(); e.preventDefault(); - toggleChecked?.(); - }, [toggleChecked]); + editor.collapse({ + edge: 'end', + }); + + CustomEditor.toggleTodoList(editor as YjsEditor, block.blockId, e.shiftKey); + }, [block, editor, readOnly]); return ( { + onMouseDown={e => { e.preventDefault(); }} className={`${className} ${readOnly ? '' : 'cursor-pointer hover:text-fill-default'} pr-1 text-xl`} > - {checked ? : } + {checked ? : } ); } diff --git a/frontend/appflowy_web_app/src/components/editor/components/blocks/toggle-list/ToggleIcon.tsx b/frontend/appflowy_web_app/src/components/editor/components/blocks/toggle-list/ToggleIcon.tsx index 70c4db447e..60c821bde3 100644 --- a/frontend/appflowy_web_app/src/components/editor/components/blocks/toggle-list/ToggleIcon.tsx +++ b/frontend/appflowy_web_app/src/components/editor/components/blocks/toggle-list/ToggleIcon.tsx @@ -1,43 +1,38 @@ import { YjsEditor } from '@/application/slate-yjs'; import { CustomEditor } from '@/application/slate-yjs/command'; import { ToggleListNode } from '@/components/editor/editor.type'; -import { debounce } from 'lodash-es'; -import React, { useCallback, useMemo } from 'react'; +import React, { useCallback } from 'react'; import { ReactComponent as ExpandSvg } from '$icons/16x/drop_menu_show.svg'; import { useReadOnly, useSlateStatic } from 'slate-react'; +import { Element } from 'slate'; -function ToggleIcon ({ block, className }: { block: ToggleListNode; className: string }) { +function ToggleIcon({ block, className }: { block: ToggleListNode; className: string }) { const { collapsed } = block.data; const editor = useSlateStatic(); - const readOnly = useReadOnly(); + const readOnly = useReadOnly() || editor.isElementReadOnly(block as unknown as Element); - const toggleCollapsed = useMemo(() => { + const handleClick = useCallback((e: React.MouseEvent) => { if (readOnly) { return; } - return debounce(() => { - CustomEditor.toggleToggleList(editor as YjsEditor, block.blockId); - }, 100); - }, [readOnly, editor, block.blockId]); - - const handleClick = useCallback((e: React.MouseEvent) => { e.stopPropagation(); e.preventDefault(); - toggleCollapsed?.(); - }, [toggleCollapsed]); + CustomEditor.toggleToggleList(editor as YjsEditor, block.blockId); + }, [block.blockId, editor, readOnly]); return ( { + draggable={false} + onMouseDown={e => { e.preventDefault(); }} - className={`${className} ${readOnly ? '' : 'cursor-pointer hover:text-fill-default'} pr-1 text-xl h-full`} + className={`${className} ${readOnly ? '' : 'cursor-pointer hover:text-fill-default'} pr-1 toggle-icon`} > - {collapsed ? : } + {collapsed ? : } ); } diff --git a/frontend/appflowy_web_app/src/components/editor/components/blocks/toggle-list/ToggleList.tsx b/frontend/appflowy_web_app/src/components/editor/components/blocks/toggle-list/ToggleList.tsx index ebe2e0792d..030669e045 100644 --- a/frontend/appflowy_web_app/src/components/editor/components/blocks/toggle-list/ToggleList.tsx +++ b/frontend/appflowy_web_app/src/components/editor/components/blocks/toggle-list/ToggleList.tsx @@ -1,9 +1,20 @@ +import { YjsEditor } from '@/application/slate-yjs'; +import { CustomEditor } from '@/application/slate-yjs/command'; +import { BlockType } from '@/application/types'; import React, { forwardRef, memo, useMemo } from 'react'; import { EditorElementProps, ToggleListNode } from '@/components/editor/editor.type'; +import { useTranslation } from 'react-i18next'; +import { useReadOnly, useSlateStatic } from 'slate-react'; +import { Element } from 'slate'; export const ToggleList = memo( forwardRef>(({ node, children, ...attributes }, ref) => { + const blockId = node.blockId; + const editor = useSlateStatic() as YjsEditor; const { collapsed, level = 0 } = useMemo(() => node.data || {}, [node.data]); + const { t } = useTranslation(); + const readOnly = useReadOnly() || editor.isElementReadOnly(node as unknown as Element); + const className = useMemo(() => { const classList = ['flex w-full flex-col']; @@ -32,6 +43,21 @@ export const ToggleList = memo( className={className} > {children} + {!readOnly && !collapsed && node.children.slice(1).length === 0 && +
{ + CustomEditor.addChildBlock(editor, blockId, BlockType.Paragraph, {}); + }} + contentEditable={false} + className={'text-text-caption select-none text-sm hover:bg-fill-list-hover rounded-[6px] cursor-pointer flex items-center h-[36px] px-[0.5em] ml-[1.45em]'} + > + { + level === 0 ? + t('document.plugins.emptyToggleList') : + t('document.plugins.emptyToggleHeadingWeb', { level }) + } +
+ }
); diff --git a/frontend/appflowy_web_app/src/components/editor/components/element/Element.tsx b/frontend/appflowy_web_app/src/components/editor/components/element/Element.tsx index f79e58fb53..f831eb8704 100644 --- a/frontend/appflowy_web_app/src/components/editor/components/element/Element.tsx +++ b/frontend/appflowy_web_app/src/components/editor/components/element/Element.tsx @@ -1,3 +1,4 @@ +import { CONTAINER_BLOCK_TYPES, SOFT_BREAK_TYPES } from '@/application/slate-yjs/command/const'; import { BlockData, BlockType, YjsEditorKey } from '@/application/types'; import { BulletedList } from '@/components/editor/components/blocks/bulleted-list'; import { Callout } from '@/components/editor/components/blocks/callout'; @@ -21,7 +22,7 @@ import { TableBlock, TableCellBlock } from '@/components/editor/components/block import { Text } from '@/components/editor/components/blocks/text'; import { useEditorContext } from '@/components/editor/EditorContext'; import { ElementFallbackRender } from '@/components/error/ElementFallbackRender'; -import { ErrorBoundary } from 'react-error-boundary'; +import { ErrorBoundary, FallbackProps } from 'react-error-boundary'; import smoothScrollIntoViewIfNeeded from 'smooth-scroll-into-view-if-needed'; import SubPage from 'src/components/editor/components/blocks/sub-page/SubPage'; import { TodoList } from 'src/components/editor/components/blocks/todo-list'; @@ -31,7 +32,7 @@ import { FileBlock } from '@/components/editor/components/blocks/file'; import { EditorElementProps, TextNode } from '@/components/editor/editor.type'; import { renderColor } from '@/utils/color'; import React, { FC, useEffect, useMemo } from 'react'; -import { ReactEditor, RenderElementProps, useSlateStatic } from 'slate-react'; +import { ReactEditor, RenderElementProps, useSelected, useSlateStatic } from 'slate-react'; export const Element = ({ element: node, @@ -43,8 +44,24 @@ export const Element = ({ const { jumpBlockId, onJumpedBlockId, + selectedBlockIds, } = useEditorContext(); + const { blockId, type } = node; + const isSelected = useSelected(); + const selected = useMemo(() => { + if (blockId && selectedBlockIds?.includes(blockId)) return true; + if ([ + ...CONTAINER_BLOCK_TYPES, + ...SOFT_BREAK_TYPES, + BlockType.HeadingBlock, + BlockType.TableBlock, + BlockType.TableCell, + BlockType.SimpleTableBlock, + ].includes(type as BlockType)) return false; + return isSelected; + }, [blockId, selectedBlockIds, type, isSelected]); + const editor = useSlateStatic(); const highlightTimeoutRef = React.useRef(); @@ -80,7 +97,7 @@ export const Element = ({ }; }, []); const Component = useMemo(() => { - switch (node.type) { + switch (type) { case BlockType.HeadingBlock: return Heading; case BlockType.TodoListBlock: @@ -134,25 +151,42 @@ export const Element = ({ default: return UnSupportedBlock; } - }, [node.type]) as FC; + }, [type]) as FC; const className = useMemo(() => { const data = (node.data as BlockData) || {}; const align = data.align; + const classList = ['block-element relative flex rounded-[4px]']; - return `block-element relative flex rounded-[8px] ${align ? `block-align-${align}` : ''}`; - }, [node.data]); + if (selected) { + classList.push('selected'); + } + + if (align) { + classList.push(`block-align-${align}`); + } + + return classList.join(' '); + }, [node.data, selected]); const style = useMemo(() => { const data = (node.data as BlockData) || {}; return { - backgroundColor: data.bgColor ? renderColor(data.bgColor) : undefined, + backgroundColor: !selected && data.bgColor ? renderColor(data.bgColor) : undefined, color: data.font_color ? renderColor(data.font_color) : undefined, }; - }, [node.data]); + }, [node.data, selected]); - if (node.type === YjsEditorKey.text) { + const fallbackRender = useMemo(() => { + return (props: FallbackProps) => { + return ( + + ); + }; + }, [node]); + + if (type === YjsEditorKey.text) { return ( {children} @@ -172,9 +206,11 @@ export const Element = ({ } return ( - -
+
{newChildren}; } @@ -35,6 +35,7 @@ export function Leaf ({ attributes, children, leaf, text }: RenderLeafProps) { const style: CSSProperties = {}; if (leaf.font_color) { + classList.push('text-color'); style['color'] = renderColor(leaf.font_color); } @@ -43,36 +44,36 @@ export function Leaf ({ attributes, children, leaf, text }: RenderLeafProps) { } if (leaf.href) { - newChildren = {newChildren}; + newChildren = {newChildren}; } if (leaf.font_family) { style['fontFamily'] = getFontFamily(leaf.font_family); } - if (leaf.mention || leaf.formula) { + if (text.text && (leaf.mention || leaf.formula)) { style['position'] = 'relative'; + if (leaf.mention) { + style['display'] = 'inline-block'; + } + const node = leaf.mention ? : leaf.formula ? + {newChildren} + : leaf.formula ? : null; + >{newChildren} : null; - newChildren = <> - {node} - - {newChildren} - - ; + newChildren = node; } return ( - {newChildren} diff --git a/frontend/appflowy_web_app/src/components/editor/components/leaf/formula/FormulaLeaf.tsx b/frontend/appflowy_web_app/src/components/editor/components/leaf/formula/FormulaLeaf.tsx index c3c85fb2aa..fc3aa8ad46 100644 --- a/frontend/appflowy_web_app/src/components/editor/components/leaf/formula/FormulaLeaf.tsx +++ b/frontend/appflowy_web_app/src/components/editor/components/leaf/formula/FormulaLeaf.tsx @@ -5,19 +5,20 @@ import FormulaPopover from '@/components/editor/components/leaf/formula/FormulaP import { useLeafSelected } from '@/components/editor/components/leaf/leaf.hooks'; import { PopoverPosition } from '@mui/material'; import React, { Suspense, useCallback, useMemo, useRef, useState } from 'react'; -import { Text } from 'slate'; +import { Text, Element } from 'slate'; import { ReactEditor, useReadOnly, useSlateStatic } from 'slate-react'; -function FormulaLeaf ({ formula, text }: { +function FormulaLeaf({ formula, text, children }: { formula: string; text: Text; + children: React.ReactNode; }) { const ref = useRef(null); const editor = useSlateStatic(); - const { isSelected, isCursorAfter, isCursorBefore } = useLeafSelected(text); + const { isSelected, isCursorBefore } = useLeafSelected(text); const [anchorPosition, setAnchorPosition] = useState(undefined); const open = Boolean(anchorPosition); - const readonly = useReadOnly(); + const readonly = useReadOnly() || editor.isElementReadOnly(text as unknown as Element); const className = useMemo(() => { const classList = ['formula-inline', 'relative', 'rounded', 'p-0.5']; @@ -28,24 +29,29 @@ function FormulaLeaf ({ formula, text }: { } if (isSelected || open) classList.push('selected'); - if (isCursorAfter) classList.push('cursor-after'); - if (isCursorBefore) classList.push('cursor-before'); return classList.join(' '); - }, [open, readonly, isSelected, isCursorAfter, isCursorBefore]); + }, [open, readonly, isSelected]); const handleClose = useCallback(() => { + window.getSelection()?.removeAllRanges(); + const path = ReactEditor.findPath(editor, text); + + editor.select(editor.end(path)); + ReactEditor.focus(editor); + setAnchorPosition(undefined); - }, []); + }, [editor, text]); const openPopover = useCallback(() => { if (readonly) return; + const rect = ref.current?.getBoundingClientRect(); if (!rect) return; setAnchorPosition({ top: rect.top + rect.height, - left: rect.left + rect.width / 2, + left: rect.left, }); }, [readonly]); @@ -75,11 +81,22 @@ function FormulaLeaf ({ formula, text }: { return ( <> + + {children} + + { - e.preventDefault(); - e.stopPropagation(); + onClick={() => { + editor.deselect(); openPopover(); }} contentEditable={false} diff --git a/frontend/appflowy_web_app/src/components/editor/components/leaf/formula/FormulaPopover.tsx b/frontend/appflowy_web_app/src/components/editor/components/leaf/formula/FormulaPopover.tsx index d27ff4b8b7..396e7cc2f5 100644 --- a/frontend/appflowy_web_app/src/components/editor/components/leaf/formula/FormulaPopover.tsx +++ b/frontend/appflowy_web_app/src/components/editor/components/leaf/formula/FormulaPopover.tsx @@ -5,7 +5,7 @@ import { useTranslation } from 'react-i18next'; import { ReactComponent as SelectCheck } from '@/assets/selected.svg'; import { ReactComponent as Clear } from '@/assets/trash.svg'; -function FormulaPopover ({ +function FormulaPopover({ open, onClose, defaultValue, @@ -27,14 +27,12 @@ function FormulaPopover ({
@@ -45,13 +43,21 @@ function FormulaPopover ({ value={value} spellCheck={false} placeholder={'E = mc^2'} + onClick={e => { + if (e.detail > 2) { + e.preventDefault(); + const target = e.target as HTMLInputElement; + + // select all text on triple click + target.setSelectionRange(0, target.value.length); + } + }} onChange={(e) => setValue(e.target.value)} fullWidth={true} onKeyDown={e => { if (e.key === 'Enter') { onDone(value); } - }} />
@@ -67,7 +73,7 @@ function FormulaPopover ({ onDone(value); }} > - + - +
diff --git a/frontend/appflowy_web_app/src/components/editor/components/leaf/href/Href.tsx b/frontend/appflowy_web_app/src/components/editor/components/leaf/href/Href.tsx index 83c0ed2297..f23fccc842 100644 --- a/frontend/appflowy_web_app/src/components/editor/components/leaf/href/Href.tsx +++ b/frontend/appflowy_web_app/src/components/editor/components/leaf/href/Href.tsx @@ -1,21 +1,131 @@ -import { useEditorContext } from '@/components/editor/EditorContext'; import { openUrl } from '@/utils/url'; -import React, { memo } from 'react'; -import { Text } from 'slate'; +import React, { memo, useEffect, useMemo, useRef } from 'react'; +import { Element, Text } from 'slate'; +import { ReactEditor, useReadOnly, useSlateStatic } from 'slate-react'; +import { Popover } from '@/components/_shared/popover'; +import { ReactComponent as CopyIcon } from '@/assets/copy.svg'; +import { ReactComponent as EditIcon } from '@/assets/edit.svg'; +import { IconButton } from '@mui/material'; +import { copyTextToClipboard } from '@/utils/copy'; +import { YjsEditor } from '@/application/slate-yjs'; +import { notify } from '@/components/_shared/notify'; +import { useTranslation } from 'react-i18next'; +import { debounce } from 'lodash-es'; +import { useLeafContext } from '@/components/editor/components/leaf/leaf.hooks'; -export const Href = memo(({ children, leaf }: { leaf: Text; children: React.ReactNode }) => { - const readonly = useEditorContext().readOnly; +export const Href = memo(({ text, children, leaf }: { leaf: Text; children: React.ReactNode; text: Text; }) => { + const editor = useSlateStatic() as YjsEditor; + + const readOnly = useReadOnly() || editor.isElementReadOnly(text as unknown as Element); + + const { + linkOpen, + openLinkPopover, + } = useLeafContext(); + const [hovered, setHovered] = React.useState(false); + const ref = useRef(null); + const [selected, setSelected] = React.useState(false); + const { t } = useTranslation(); + + const debounceShow = useMemo(() => { + return debounce(() => { + setHovered(true); + + }, 200); + }, []); + + const debounceHide = useMemo(() => { + return debounce(() => { + setHovered(false); + }, 200); + }, []); + + useEffect(() => { + setSelected(linkOpen === text); + }, [linkOpen, text]); return ( - { - if (readonly && leaf.href) { - void openUrl(leaf.href, '_blank'); - } - }} - className={`cursor-pointer select-auto px-1 py-0.5 text-fill-default underline`} - > - {children} - + <> + { + if (e.buttons > 0) return; + if (!readOnly) { + debounceHide.cancel(); + debounceShow(); + } + }} + onMouseLeave={() => { + debounceShow.cancel(); + debounceHide(); + }} + onClick={() => { + if (leaf.href) { + void openUrl(leaf.href, '_blank'); + } + }} + style={{ + backgroundColor: selected ? 'var(--content-blue-100)' : undefined, + }} + className={`cursor-pointer select-auto py-0.5 text-fill-default underline`} + > + {children} + {hovered && { + e.stopPropagation(); + }} + onClick={e => { + e.stopPropagation(); + }} + disableRestoreFocus={true} + disableAutoFocus={true} + open={hovered} + anchorEl={ref.current} + slotProps={{ + root: { + style: { + pointerEvents: 'none', + }, + }, + paper: { + style: { + pointerEvents: 'auto', + }, + }, + }} + onClose={() => setHovered(false)} + > +
+
{leaf.href}
+ { + if (!leaf.href) return; + void copyTextToClipboard(leaf.href); + notify.success(t('document.plugins.urlPreview.copiedToPasteBoard')); + }} size={'small'}> + + + { + if (!ref.current) return; + e.preventDefault(); + const path = ReactEditor.findPath(editor, text); + + editor.select({ + anchor: editor.start(path), + focus: editor.end(path), + }); + ReactEditor.focus(editor); + setSelected(true); + + setTimeout(() => { + openLinkPopover?.(text); + }, 50); + }} size={'small'}> + + +
+
} +
+ + ); }); diff --git a/frontend/appflowy_web_app/src/components/editor/components/leaf/href/HrefPopover.tsx b/frontend/appflowy_web_app/src/components/editor/components/leaf/href/HrefPopover.tsx new file mode 100644 index 0000000000..80b5721c09 --- /dev/null +++ b/frontend/appflowy_web_app/src/components/editor/components/leaf/href/HrefPopover.tsx @@ -0,0 +1,313 @@ +import React, { useCallback, useEffect, useMemo, useRef } from 'react'; +import { Button, Divider, OutlinedInput, PopoverPosition } from '@mui/material'; +import { CustomEditor } from '@/application/slate-yjs/command'; +import { EditorMarkFormat } from '@/application/slate-yjs/types'; +import { ReactComponent as RemoveIcon } from '@/assets/trash.svg'; +import { Popover } from '@/components/_shared/popover'; +import { ReactEditor, useSlateStatic } from 'slate-react'; +import { useTranslation } from 'react-i18next'; +import { getRangeRect } from '@/components/editor/components/toolbar/selection-toolbar/utils'; +import { YjsEditor } from '@/application/slate-yjs'; +import { createHotkey, HOT_KEY_NAME } from '@/utils/hotkeys'; +import { debounce } from 'lodash-es'; +import { PopoverOrigin } from '@mui/material/Popover/Popover'; +import { processUrl } from '@/utils/url'; + +const defaultOrigin: PopoverOrigin = { + vertical: 'top', + horizontal: 'center', +}; + +interface HrefPopoverProps { + open: boolean; + onClose: () => void; + updatedSelection?: () => void; +} + +function HrefPopover({ + open, + onClose, + updatedSelection, +}: HrefPopoverProps) { + const editor = useSlateStatic() as YjsEditor; + const { t } = useTranslation(); + const [isActivated, setIsActivated] = React.useState(CustomEditor.isMarkActive(editor, EditorMarkFormat.Href)); + const [popoverType, setPopoverType] = React.useState<'add' | 'update' | undefined>(undefined); + const [anchorPosition, setAnchorPosition] = React.useState(undefined); + + useEffect(() => { + if (isActivated) { + setPopoverType('update'); + } else { + setPopoverType('add'); + } + }, [isActivated]); + + useEffect(() => { + if (open) { + setIsActivated(CustomEditor.isMarkActive(editor, EditorMarkFormat.Href)); + const rect = getRangeRect(); + + if (!rect) return; + setUrlValid(true); + setAnchorPosition({ + top: rect.bottom + 10, + left: rect.left, + }); + } + }, [open, editor]); + + const handleClose = useCallback(() => { + setAnchorPosition(undefined); + onClose(); + + window.getSelection()?.removeAllRanges(); + + setTimeout(() => { + ReactEditor.focus(editor); + }, 50); + }, [editor, onClose]); + + const formatLink = useCallback((value: string) => { + CustomEditor.addMark(editor, { + key: EditorMarkFormat.Href, + value, + }); + handleClose(); + setIsActivated(true); + }, [editor, handleClose]); + + const updateText = useCallback((value: string) => { + const { selection } = editor; + + if (!selection) return; + const texts = CustomEditor.getTextNodes(editor); + const hrefNode = texts.find(n => { + if (n.href) return true; + }); + + if (!hrefNode) return; + const href = hrefNode.href; + + editor.delete(); + editor.insertText(value); + + const newSelection = editor.selection; + + if (!newSelection) return; + + const end = editor.end(newSelection); + + editor.select({ + anchor: { + path: end.path, + offset: end.offset - value.length, + }, + focus: end, + }); + + CustomEditor.addMark(editor, { + key: EditorMarkFormat.Href, + value: href || '', + }); + + updatedSelection?.(); + setIsActivated(true); + + }, [editor, updatedSelection]); + + const removeLink = useCallback(() => { + CustomEditor.removeMark(editor, EditorMarkFormat.Href); + handleClose(); + setIsActivated(false); + }, [editor, handleClose]); + + const urlRef = useRef(null); + const textRef = useRef(null); + const buttonRef = useRef(null); + const [urlValid, setUrlValid] = React.useState(true); + const addLink = useMemo(() => { + if (!open || popoverType !== 'add') return null; + return ( +
+ { + if (!urlValid) return; + if (createHotkey(HOT_KEY_NAME.ENTER)(e.nativeEvent)) { + e.preventDefault(); + e.stopPropagation(); + formatLink(e.currentTarget.value); + } + }} + onInput={e => { + const target = e.target as HTMLInputElement; + const url = processUrl(target.value); + + if (!url) { + setUrlValid(false); + return; + } + + setUrlValid(true); + }} + size={'small'} fullWidth={true} placeholder={t('toolbar.addLink')}/> + {urlValid ? null : ( +
+ {t('editor.incorrectLink')} +
+ )} +
+ ); + }, [formatLink, open, popoverType, t, urlValid]); + + const updateLink = useMemo(() => { + if (!open || popoverType !== 'update') return null; + const texts = CustomEditor.getTextNodes(editor); + const hrefNode = texts.find(n => { + if (n.href) return true; + }); + + if (!hrefNode || !editor.selection) return null; + + const text = editor.string(editor.selection); + + const saveLink = (value: string) => { + if (value === hrefNode.href || !urlValid) return; + CustomEditor.addMark(editor, { + key: EditorMarkFormat.Href, + value, + }); + }; + + return ( +
+
+
URL
+ { + urlRef.current = input; + }} + defaultValue={hrefNode.href} + onBlur={e => { + saveLink(e.currentTarget.value); + }} + onInput={e => { + const target = e.target as HTMLInputElement; + const url = processUrl(target.value); + + if (!url) { + setUrlValid(false); + return; + } + + setUrlValid(true); + }} + onKeyDown={e => { + if (!urlValid) return; + if (createHotkey(HOT_KEY_NAME.ENTER)(e.nativeEvent)) { + e.preventDefault(); + e.stopPropagation(); + formatLink(e.currentTarget.value); + } else if (createHotkey(HOT_KEY_NAME.DOWN)(e.nativeEvent)) { + e.preventDefault(); + e.stopPropagation(); + textRef.current?.focus(); + } else if (createHotkey(HOT_KEY_NAME.ESCAPE)(e.nativeEvent)) { + saveLink(e.currentTarget.value); + } + }} + size={'small'} + fullWidth={true} + placeholder={t('toolbar.addLink')} + /> + {urlValid ? null : ( +
+ {t('editor.incorrectLink')} +
+ )} +
+
+
{t('editor.text')}
+ { + textRef.current = input; + }} + onKeyDown={e => { + if (createHotkey(HOT_KEY_NAME.UP)(e.nativeEvent)) { + e.preventDefault(); + e.stopPropagation(); + urlRef.current?.focus(); + } + }} + defaultValue={text} + onInput={e => { + const target = e.target as HTMLInputElement; + + if (target.value) { + updateText(target.value); + } + }} + size={'small'} + fullWidth={true} placeholder={t('toolbar.addLink')}/> +
+ + +
+ ); + }, [urlValid, open, popoverType, editor, t, removeLink, formatLink, updateText]); + + const paperRef = useRef(null); + + const debouncePosition = useMemo(() => { + return debounce(() => { + + if (!anchorPosition || !paperRef.current) return; + const paperRect = paperRef.current.getBoundingClientRect(); + + if (anchorPosition.top + paperRect.height > window.innerHeight) { + setAnchorPosition({ + top: anchorPosition.top - paperRect.height - 30, + left: anchorPosition.left, + }); + return; + } + + }, 50); + }, [anchorPosition]); + + return ( + e.stopPropagation()} + onMouseUp={e => e.stopPropagation()} + disableRestoreFocus={true} + open={open && !!anchorPosition} + onClose={handleClose} + adjustOrigins={false} + anchorPosition={anchorPosition} + anchorReference={'anchorPosition'} + transformOrigin={defaultOrigin} + slotProps={{ + paper: { + className: 'p-4 min-w-[360px]', + ref: paperRef, + }, + }} + onTransitionEnd={debouncePosition} + > + {popoverType && ( + popoverType === 'add' ? addLink : updateLink + )} + + ); +} + +export default HrefPopover; \ No newline at end of file diff --git a/frontend/appflowy_web_app/src/components/editor/components/leaf/leaf.hooks.ts b/frontend/appflowy_web_app/src/components/editor/components/leaf/leaf.hooks.ts index 80c0fe1f74..eef5bd0adb 100644 --- a/frontend/appflowy_web_app/src/components/editor/components/leaf/leaf.hooks.ts +++ b/frontend/appflowy_web_app/src/components/editor/components/leaf/leaf.hooks.ts @@ -1,43 +1,28 @@ -import { useCallback, useMemo } from 'react'; -import { Editor, Range, Text, Transforms } from 'slate'; +import { createContext, useCallback, useMemo, useContext, useState } from 'react'; +import { Editor, Point, Range, Text, Transforms } from 'slate'; import { ReactEditor, useReadOnly, useSelected, useSlate } from 'slate-react'; -export function useLeafSelected (text: Text) { +export const LeafContext = createContext<{ + openLinkPopover?: (text: Text) => void; + closeLinkPopover?: () => void; + linkOpen?: Text; +} | undefined>(undefined); + +export function useLeafContext() { + return useContext(LeafContext) || { + openLinkPopover: () => undefined, + closeLinkPopover: () => undefined, + linkOpen: undefined, + }; +} + +export function useLeafSelected(text: Text) { const readonly = useReadOnly(); const editor = useSlate(); const elementIsSelected = useSelected(); const selection = editor.selection; - const isCursorBefore = useMemo(() => { - if (readonly || !selection || !text) return false; - - if (selection && Range.isCollapsed(selection)) { - const path = ReactEditor.findPath(editor, text); - const start = Editor.start(editor, path); - - return Range.equals(selection, { - anchor: start, - focus: start, - }); - } - - return false; - }, [editor, readonly, selection, text]); - - const isCursorAfter = useMemo(() => { - if (readonly || !selection || !text) return false; - if (selection && Range.isCollapsed(selection)) { - const path = ReactEditor.findPath(editor, text); - const end = Editor.end(editor, path); - - return Range.equals(selection, { - anchor: end, - focus: end, - }); - } - - return false; - }, [editor, readonly, selection, text]); + const [isCursorBefore, setIsCursorBefore] = useState(false); const isSelected = useMemo(() => { if (readonly || !selection || !elementIsSelected || !text) return false; @@ -45,17 +30,25 @@ export function useLeafSelected (text: Text) { try { const path = ReactEditor.findPath(editor, text); - if (Range.isCollapsed(selection)) return false; + if (Range.isCollapsed(selection)) { + const end = Editor.end(editor, path); + const isAnchorBefore = Point.isBefore(editor.end(selection), end); + + setIsCursorBefore(isAnchorBefore); + return false; + } // get the start and end point of the mention const start = Editor.start(editor, path); const end = Editor.end(editor, path); // check if the selection is inside the mention - return !!(Range.intersection(selection, { + const selected = !!(Range.intersection(selection, { anchor: start, focus: end, })); + + return selected; } catch (e) { return false; } @@ -80,6 +73,5 @@ export function useLeafSelected (text: Text) { isSelected, select, isCursorBefore, - isCursorAfter, }; } \ No newline at end of file diff --git a/frontend/appflowy_web_app/src/components/editor/components/leaf/mention/MentionDate.tsx b/frontend/appflowy_web_app/src/components/editor/components/leaf/mention/MentionDate.tsx index 95b2adbe52..fb0e95069c 100644 --- a/frontend/appflowy_web_app/src/components/editor/components/leaf/mention/MentionDate.tsx +++ b/frontend/appflowy_web_app/src/components/editor/components/leaf/mention/MentionDate.tsx @@ -3,17 +3,18 @@ import React, { useMemo } from 'react'; import { ReactComponent as DateSvg } from '@/assets/date.svg'; import { ReactComponent as ReminderSvg } from '@/assets/reminder_clock.svg'; -function MentionDate ({ date, reminder }: { date: string; reminder?: { id: string; option: string } }) { +function MentionDate({ date, reminder }: { date: string; reminder?: { id: string; option: string } }) { const dateFormat = useMemo(() => { + return renderDate(date, 'MMM D, YYYY'); }, [date]); return ( - - @{dateFormat} - {reminder ? : } + @{dateFormat} + {reminder ? : } ); diff --git a/frontend/appflowy_web_app/src/components/editor/components/leaf/mention/MentionLeaf.tsx b/frontend/appflowy_web_app/src/components/editor/components/leaf/mention/MentionLeaf.tsx index efe7d225a3..3bd3548bed 100644 --- a/frontend/appflowy_web_app/src/components/editor/components/leaf/mention/MentionLeaf.tsx +++ b/frontend/appflowy_web_app/src/components/editor/components/leaf/mention/MentionLeaf.tsx @@ -2,15 +2,17 @@ import { Mention, MentionType } from '@/application/types'; import { useLeafSelected } from '@/components/editor/components/leaf/leaf.hooks'; import MentionDate from '@/components/editor/components/leaf/mention/MentionDate'; import MentionPage from '@/components/editor/components/leaf/mention/MentionPage'; -import { useMemo } from 'react'; -import { Text } from 'slate'; -import { useReadOnly } from 'slate-react'; +import React, { useMemo } from 'react'; +import { Element, Text } from 'slate'; +import { useReadOnly, useSlateStatic } from 'slate-react'; -export function MentionLeaf ({ mention, text }: { +export function MentionLeaf({ mention, text, children }: { mention: Mention; text: Text; + children: React.ReactNode; }) { - const readonly = useReadOnly(); + const editor = useSlateStatic(); + const readonly = useReadOnly() || editor.isElementReadOnly(text as unknown as Element); const { type, date, page_id, reminder_id, reminder_option, block_id } = mention; const reminder = useMemo(() => { @@ -20,6 +22,7 @@ export function MentionLeaf ({ mention, text }: { const content = useMemo(() => { if ([MentionType.PageRef, MentionType.childPage].includes(type) && page_id) { return ; } - }, [date, page_id, reminder, type, block_id]); + }, [text, date, page_id, reminder, type, block_id]); // check if the mention is selected - const { isSelected, select, isCursorAfter, isCursorBefore } = useLeafSelected(text); + const { isSelected, select, isCursorBefore } = useLeafSelected(text); const className = useMemo(() => { - const classList = ['w-fit mention', 'relative', 'rounded', 'py-0.5']; + const classList = ['w-fit mention', 'relative', 'rounded-[2px]', 'py-0.5 px-1']; if (readonly) classList.push('cursor-default'); - else classList.push('cursor-pointer'); + else if (type !== MentionType.Date) classList.push('cursor-pointer'); - if (isSelected) classList.push('selected'); - if (isCursorAfter) classList.push('cursor-after'); - if (isCursorBefore) classList.push('cursor-before'); + if (isSelected && type !== MentionType.Date) classList.push('selected'); return classList.join(' '); - }, [readonly, isSelected, isCursorAfter, isCursorBefore]); + }, [type, readonly, isSelected]); - return - {content} - ; + const ref = React.useRef(null); + + return <> + + + {children} + + + + {content} + + + + ; } export default MentionLeaf; diff --git a/frontend/appflowy_web_app/src/components/editor/components/leaf/mention/MentionPage.tsx b/frontend/appflowy_web_app/src/components/editor/components/leaf/mention/MentionPage.tsx index 98c04410ef..c9a0f9cb6b 100644 --- a/frontend/appflowy_web_app/src/components/editor/components/leaf/mention/MentionPage.tsx +++ b/frontend/appflowy_web_app/src/components/editor/components/leaf/mention/MentionPage.tsx @@ -1,22 +1,32 @@ import { YjsEditor } from '@/application/slate-yjs'; import { CustomEditor } from '@/application/slate-yjs/command'; import { traverseBlock } from '@/application/slate-yjs/utils/convert'; -import { MentionType, View, ViewLayout, YjsEditorKey, YSharedRoot } from '@/application/types'; +import { MentionType, UIVariant, View, ViewLayout, YjsEditorKey, YSharedRoot } from '@/application/types'; import { ReactComponent as NorthEast } from '@/assets/north_east.svg'; import { ReactComponent as MarkIcon } from '@/assets/paragraph_mark.svg'; -import { ViewIcon } from '@/components/_shared/view-icon'; import { useEditorContext } from '@/components/editor/EditorContext'; -import { isFlagEmoji } from '@/utils/emoji'; -import React, { useEffect, useMemo, useState } from 'react'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { useSlateStatic } from 'slate-react'; +import { ReactEditor, useReadOnly, useSlate } from 'slate-react'; +import { Element, Text } from 'slate'; +import PageIcon from '@/components/_shared/view-icon/PageIcon'; +import { findSlateEntryByBlockId } from '@/application/slate-yjs/utils/editor'; +import smoothScrollIntoViewIfNeeded from 'smooth-scroll-into-view-if-needed'; -function MentionPage ({ pageId, blockId, type }: { pageId: string; blockId?: string; type?: MentionType }) { +function MentionPage({ text, pageId, blockId, type }: { + text: Text | Element; + pageId: string; + blockId?: string; + type?: MentionType +}) { const context = useEditorContext(); - const editor = useSlateStatic(); + const editor = useSlate(); + const selection = editor.selection; + const variant = context.variant; const currentViewId = context.viewId; - const { navigateToView, loadViewMeta, loadView } = context; + + const { navigateToView, loadViewMeta, loadView, openPageModal } = context; const [noAccess, setNoAccess] = useState(false); const [meta, setMeta] = useState(null); const [content, setContent] = useState(''); @@ -31,6 +41,9 @@ function MentionPage ({ pageId, blockId, type }: { pageId: string; blockId?: str setMeta(meta); } catch (e) { setNoAccess(true); + if (e && (e as View).name) { + setMeta(e as View); + } } } })(); @@ -42,10 +55,6 @@ function MentionPage ({ pageId, blockId, type }: { pageId: string; blockId?: str const { t } = useTranslation(); - const isFlag = useMemo(() => { - return icon ? isFlagEmoji(icon.value) : false; - }, [icon]); - useEffect(() => { void ( async () => { @@ -53,12 +62,13 @@ function MentionPage ({ pageId, blockId, type }: { pageId: string; blockId?: str if (blockId) { if (currentViewId === pageId) { - const entry = CustomEditor.getBlockEntry(editor as YjsEditor, blockId); + const entry = findSlateEntryByBlockId(editor as YjsEditor, blockId); if (entry) { const [node] = entry; + const text = CustomEditor.getBlockTextContent(node, 2); - setContent(CustomEditor.getBlockTextContent(node, 2)); + setContent(text || pageName); return; } @@ -70,11 +80,21 @@ function MentionPage ({ pageId, blockId, type }: { pageId: string; blockId?: str const sharedRoot = otherDoc.getMap(YjsEditorKey.data_section) as YSharedRoot; - const node = traverseBlock(blockId, sharedRoot); + const handleBlockChange = () => { + const node = traverseBlock(blockId, sharedRoot); - if (!node) return; + if (!node) { + setContent(pageName); + return; + } + + const text = CustomEditor.getBlockTextContent(node, 2); + + setContent(`${pageName}${text ? ` - ${text}` : ''}`); + }; + + handleBlockChange(); - setContent(`${pageName} - ${CustomEditor.getBlockTextContent(node, 2)}`); return; } catch (e) { @@ -87,46 +107,85 @@ function MentionPage ({ pageId, blockId, type }: { pageId: string; blockId?: str setContent(pageName); } )(); - }, [blockId, currentViewId, editor, loadView, meta?.name, pageId, t]); + }, [selection, blockId, currentViewId, editor, loadView, meta?.name, pageId, t]); const mentionIcon = useMemo(() => { if (pageId === currentViewId && blockId) { - return ; + return ; } return <> - {icon?.value || } + + {type === MentionType.PageRef && - - + + } ; - }, [blockId, currentViewId, icon?.value, meta?.layout, pageId, type]); + }, [blockId, currentViewId, icon, meta?.layout, pageId, type]); + + const readOnly = useReadOnly() || editor.isElementReadOnly(text as unknown as Element); + + const handleScrollToBlock = useCallback(async () => { + if (blockId) { + const entry = findSlateEntryByBlockId(editor as YjsEditor, blockId); + + if (entry) { + const [node] = entry; + const dom = ReactEditor.toDOMNode(editor, node); + + await smoothScrollIntoViewIfNeeded(dom, { + behavior: 'smooth', + scrollMode: 'if-needed', + }); + + dom.className += ' highlight-block'; + setTimeout(() => { + dom.className = dom.className.replace('highlight-block', ''); + }, 5000); + } + } + }, [blockId, editor]); return ( { e.stopPropagation(); - void navigateToView?.(pageId, blockId); + if (readOnly) { + void navigateToView?.(pageId, blockId); + } else { + if (noAccess) return; + if (pageId === currentViewId) { + void handleScrollToBlock(); + return; + } + + openPageModal?.(pageId); + } + }} + style={{ + cursor: noAccess ? 'default' : undefined, }} className={`mention-inline cursor-pointer pr-1 underline`} contentEditable={false} data-mention-id={pageId} > {noAccess ? ( - No Access + { + variant === UIVariant.App ? `${content}${t('document.mention.trashHint')}` : t('document.mention.noAccess') + } ) : ( <> - + {mentionIcon} - + {content} diff --git a/frontend/appflowy_web_app/src/components/editor/components/panels/Panels.hooks.ts b/frontend/appflowy_web_app/src/components/editor/components/panels/Panels.hooks.ts new file mode 100644 index 0000000000..9b93bed946 --- /dev/null +++ b/frontend/appflowy_web_app/src/components/editor/components/panels/Panels.hooks.ts @@ -0,0 +1,8 @@ +import { PanelContext } from '@/components/editor/components/panels/PanelsContext'; +import { useContext } from 'react'; + +export function usePanelContext () { + const panel = useContext(PanelContext); + + return panel; +} \ No newline at end of file diff --git a/frontend/appflowy_web_app/src/components/editor/components/panels/PanelsContext.tsx b/frontend/appflowy_web_app/src/components/editor/components/panels/PanelsContext.tsx new file mode 100644 index 0000000000..8e5caef2b9 --- /dev/null +++ b/frontend/appflowy_web_app/src/components/editor/components/panels/PanelsContext.tsx @@ -0,0 +1,257 @@ +import { getRangeRect } from '@/components/editor/components/toolbar/selection-toolbar/utils'; +import React, { createContext, useState, useCallback, useEffect, useRef } from 'react'; +import { BaseRange, Point } from 'slate'; +import { ReactEditor } from 'slate-react'; +import { TextInsertTextOptions } from 'slate/dist/interfaces/transforms/text'; + +export enum PanelType { + Slash = 'slash', + Mention = 'mention', + PageReference = 'pageReference', +} + +export interface PanelContextType { + activePanel?: PanelType; + panelPosition?: { top: number; left: number }; + setActivePanel: (panel: PanelType) => void; + closePanel: () => void; + openPanel: (panel: PanelType, position: { top: number; left: number }) => void; + isPanelOpen: (panel: PanelType) => boolean; + searchText?: string; + removeContent: () => void; +} + +export const PanelContext = createContext({ + setActivePanel: () => { + return; + }, + closePanel: () => { + return; + }, + openPanel: () => { + return; + }, + removeContent: () => { + return; + }, + isPanelOpen: () => false, +} as PanelContextType); + +const panelTypeChars = ['/', '@', '+']; + +export const PanelProvider = ({ children, editor }: { children: React.ReactNode; editor: ReactEditor }) => { + const [activePanel, setActivePanel] = useState(undefined); + const [panelPosition, setPanelPosition] = useState<{ top: number; left: number } | undefined>(undefined); + const startSelection = useRef(null); + const endSelection = useRef(null); + const [searchText, setSearchText] = useState(''); + + const closePanel = useCallback(() => { + setActivePanel(undefined); + startSelection.current = null; + endSelection.current = null; + setSearchText(''); + }, []); + + const removeContent = useCallback(() => { + const { selection } = editor; + + if (!selection) return; + + const start = startSelection.current?.anchor; + const end = endSelection.current?.focus; + + if (!start || !end) return; + const length = end.offset - start.offset; + + if (length === 0) return; + editor.delete({ + at: { + anchor: start, + focus: end, + }, + + }); + }, [editor]); + + const openPanel = useCallback((panel: PanelType, position: { top: number; left: number }) => { + setActivePanel(panel); + setPanelPosition(position); + const { selection } = editor; + + if (!selection) return; + startSelection.current = editor.selection; + endSelection.current = editor.selection; + }, [editor]); + + const isPanelOpen = useCallback((panel: PanelType) => { + return activePanel === panel; + }, [activePanel]); + + useEffect(() => { + const { insertText } = editor; + + editor.insertText = (text: string, options?: TextInsertTextOptions) => { + insertText(text, options); + const { selection } = editor; + + if (!selection) return; + if (activePanel !== undefined) return; + + if (panelTypeChars.includes(text)) { + const position = getRangeRect(); + + if (!position) return; + + const panelType = { '/': PanelType.Slash, '+': PanelType.PageReference, '@': PanelType.Mention }[text]; + + if (!panelType) return; + + openPanel(panelType, { top: position.top, left: position.left }); + + startSelection.current = { + anchor: { + path: selection.anchor.path, + offset: selection.anchor.offset - 1, + }, + focus: selection.focus, + }; + endSelection.current = editor.selection; + return; + } + + const rangeText = editor.string({ + anchor: { + path: selection.anchor.path, + offset: selection.anchor.offset - 2, + }, + focus: selection.focus, + }); + + if (rangeText === '[[') { + const position = getRangeRect(); + + if (!position) return; + + openPanel(PanelType.PageReference, { top: position.top, left: position.left }); + startSelection.current = { + anchor: { + path: selection.anchor.path, + offset: selection.anchor.offset - 2, + }, + focus: selection.focus, + }; + endSelection.current = editor.selection; + } + }; + + return () => { + editor.insertText = insertText; + }; + }, [activePanel, editor, openPanel]); + + useEffect(() => { + const { onChange } = editor; + + if (!activePanel) return; + + editor.onChange = () => { + onChange(); + const { selection } = editor; + let start = startSelection.current?.focus; + + if (!selection) return; + + if (!start) { + startSelection.current = selection; + start = selection.anchor; + } + + const text = editor.string({ + anchor: start, + focus: selection.focus, + }); + + if (Point.isBefore(selection.anchor, start)) { + closePanel(); + return; + } + + endSelection.current = selection; + + setSearchText(text); + }; + + return () => { + editor.onChange = onChange; + }; + + }, [editor, activePanel, closePanel]); + + const open = activePanel !== undefined; + + useEffect(() => { + const handleKeyDown = (e: KeyboardEvent) => { + if (!open) return; + const { key } = e; + + switch (key) { + case 'Escape': + e.stopPropagation(); + closePanel(); + break; + case 'ArrowLeft': + case 'ArrowRight': { + e.preventDefault(); + break; + } + + case 'Backspace': { + const { selection } = editor; + + if (!selection || !startSelection.current) return; + const text = editor.string({ + anchor: startSelection.current.focus, + focus: selection?.focus, + }); + + if (text === '') { + closePanel(); + } + + break; + } + + default: + break; + } + + }; + + const slateDom = ReactEditor.toDOMNode(editor, editor); + + slateDom.addEventListener('keydown', handleKeyDown); + + return () => { + slateDom.removeEventListener('keydown', handleKeyDown); + }; + }, [closePanel, editor, open]); + + return ( + + {children} + + ); +}; + diff --git a/frontend/appflowy_web_app/src/components/editor/components/panels/index.tsx b/frontend/appflowy_web_app/src/components/editor/components/panels/index.tsx new file mode 100644 index 0000000000..2cf624963a --- /dev/null +++ b/frontend/appflowy_web_app/src/components/editor/components/panels/index.tsx @@ -0,0 +1,68 @@ +import ChangeIconPopover from '@/components/_shared/view-icon/ChangeIconPopover'; +import { getRangeRect } from '@/components/editor/components/toolbar/selection-toolbar/utils'; +import { createHotkey, HOT_KEY_NAME } from '@/utils/hotkeys'; +import React, { useEffect } from 'react'; +import { ReactEditor, useSlateStatic } from 'slate-react'; +import { MentionPanel } from './mention-panel'; +import { SlashPanel } from './slash-panel'; + +function Panels () { + const [emojiPosition, setEmojiPosition] = React.useState<{ + top: number; + left: number + } | null>(null); + const showEmoji = Boolean(emojiPosition); + const editor = useSlateStatic(); + + useEffect(() => { + const handleKeyDown = (e: KeyboardEvent) => { + if (createHotkey(HOT_KEY_NAME.POP_EMOJI_PICKER)(e)) { + e.preventDefault(); + const rect = getRangeRect(); + + if (!rect) return; + setEmojiPosition({ + top: rect.top, + left: rect.left, + }); + } + }; + + const editorDom = ReactEditor.toDOMNode(editor, editor); + + editorDom.addEventListener('keydown', handleKeyDown); + return () => { + editorDom.removeEventListener('keydown', handleKeyDown); + }; + }, [editor]); + + return ( + <> + + + { + setEmojiPosition(null); + }} + iconEnabled={false} + defaultType={'emoji'} + onSelectIcon={({ value }) => { + editor.insertText(value); + setEmojiPosition(null); + ReactEditor.focus(editor); + }} + popoverProps={{ + transformOrigin: { + vertical: -32, + horizontal: -8, + }, + }} + /> + + ); +} + +export default Panels; \ No newline at end of file diff --git a/frontend/appflowy_web_app/src/components/editor/components/panels/mention-panel/MentionPanel.tsx b/frontend/appflowy_web_app/src/components/editor/components/panels/mention-panel/MentionPanel.tsx new file mode 100644 index 0000000000..3ab8e83d10 --- /dev/null +++ b/frontend/appflowy_web_app/src/components/editor/components/panels/mention-panel/MentionPanel.tsx @@ -0,0 +1,463 @@ +import { YjsEditor } from '@/application/slate-yjs'; +import { CustomEditor } from '@/application/slate-yjs/command'; +import { EditorMarkFormat } from '@/application/slate-yjs/types'; +import { Mention, MentionType, View, ViewLayout } from '@/application/types'; +import { flattenViews } from '@/components/_shared/outline/utils'; +import { usePanelContext } from '@/components/editor/components/panels/Panels.hooks'; +import { PanelType } from '@/components/editor/components/panels/PanelsContext'; +import { useEditorContext } from '@/components/editor/EditorContext'; +import { Button, Divider } from '@mui/material'; +import { sortBy, uniqBy } from 'lodash-es'; +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { Transforms } from 'slate'; +import { ReactEditor, useSlateStatic } from 'slate-react'; +import { ReactComponent as AddIcon } from '@/assets/add.svg'; +import { ReactComponent as ArrowIcon } from '@/assets/north_east.svg'; +import { ReactComponent as MoreIcon } from '@/assets/more.svg'; +import { Popover } from '@/components/_shared/popover'; +import dayjs from 'dayjs'; +import PageIcon from '@/components/_shared/view-icon/PageIcon'; + +enum MentionTag { + Reminder = 'reminder', + User = 'user', + Page = 'page', + LoadMore = 'loadMore', + NewPage = 'newPage', + Date = 'date', +} + +interface Option { + category: MentionTag; + index: number; +} + +function createMentionOptions({ + showMore, + viewsLength, + dateLength, + newPageLength, +}: { + showMore: boolean; + viewsLength: number; + dateLength: number; + newPageLength: number; +}) { + console.log('viewsLength', viewsLength); + const options = [ + ...Array(viewsLength).fill(0).map((_, index) => ({ + category: MentionTag.Page, + index, + })), + showMore && { + category: MentionTag.LoadMore, + index: 0, + }, + ...Array(dateLength).fill(0).map((_, index) => ({ + category: MentionTag.Date, + index, + })), + ...Array(newPageLength).fill(0).map((_, index) => ({ + category: MentionTag.NewPage, + index, + })), + ].filter(Boolean) as Option[]; + + return options; +} + +export function MentionPanel() { + const { + isPanelOpen, + panelPosition, + closePanel, + searchText, + removeContent, + activePanel, + } = usePanelContext(); + const showDate = activePanel === PanelType.Mention; + const { + viewId, + loadViews, + addPage, + openPageModal, + } = useEditorContext(); + const { t } = useTranslation(); + const ref = useRef(null); + const open = useMemo(() => { + return isPanelOpen(PanelType.Mention) || isPanelOpen(PanelType.PageReference); + }, [isPanelOpen]); + const selectedOptionRef = React.useRef
+ ); +} + +export default ControlActions; \ No newline at end of file diff --git a/frontend/appflowy_web_app/src/components/editor/components/toolbar/block-controls/ControlsMenu.tsx b/frontend/appflowy_web_app/src/components/editor/components/toolbar/block-controls/ControlsMenu.tsx new file mode 100644 index 0000000000..ff2cd1d573 --- /dev/null +++ b/frontend/appflowy_web_app/src/components/editor/components/toolbar/block-controls/ControlsMenu.tsx @@ -0,0 +1,155 @@ +import { YjsEditor } from '@/application/slate-yjs'; +import { CustomEditor } from '@/application/slate-yjs/command'; +import { BlockType } from '@/application/types'; +import { ReactComponent as DuplicateIcon } from '@/assets/duplicate.svg'; +import { ReactComponent as CopyLinkIcon } from '@/assets/link.svg'; +import { ReactComponent as DeleteIcon } from '@/assets/trash.svg'; +import { notify } from '@/components/_shared/notify'; +import { Popover } from '@/components/_shared/popover'; +import Depth from '@/components/editor/components/toolbar/block-controls/Depth'; +import { OutlineNode } from '@/components/editor/editor.type'; +import { useEditorContext } from '@/components/editor/EditorContext'; +import { copyTextToClipboard } from '@/utils/copy'; +import { Button } from '@mui/material'; +import { PopoverProps } from '@mui/material/Popover'; +import React, { useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; +import { ReactEditor, useSlateStatic } from 'slate-react'; +import { findSlateEntryByBlockId } from '@/application/slate-yjs/utils/editor'; + +const popoverProps: Partial = { + transformOrigin: { + vertical: 'center', + horizontal: 'right', + + }, + anchorOrigin: { + vertical: 'center', + horizontal: 'left', + }, + keepMounted: false, + disableRestoreFocus: true, + disableEnforceFocus: true, +}; + +function ControlsMenu({ open, onClose, anchorEl }: { + open: boolean; + onClose: () => void; + anchorEl: HTMLElement | null; +}) { + const { selectedBlockIds } = useEditorContext(); + const editor = useSlateStatic() as YjsEditor; + const onlySingleBlockSelected = selectedBlockIds?.length === 1; + const node = useMemo(() => { + const blockId = selectedBlockIds?.[0]; + + if (!blockId) return null; + + return findSlateEntryByBlockId(editor, blockId); + }, [selectedBlockIds, editor]); + + const { t } = useTranslation(); + const options = useMemo(() => { + return [{ + key: 'delete', + content: t('button.delete'), + icon: , + onClick: () => { + selectedBlockIds?.forEach((blockId) => { + CustomEditor.deleteBlock(editor, blockId); + }); + }, + }, { + key: 'duplicate', + content: t('button.duplicate'), + icon: , + onClick: () => { + const newBlockIds: string[] = []; + const prevId = selectedBlockIds?.[selectedBlockIds.length - 1]; + + selectedBlockIds?.forEach((blockId, index) => { + const newBlockId = CustomEditor.duplicateBlock(editor, blockId, index === 0 ? prevId : newBlockIds[index - 1]); + + newBlockId && newBlockIds.push(newBlockId); + }); + + ReactEditor.focus(editor); + const [, path] = findSlateEntryByBlockId(editor, newBlockIds[0]); + + editor.select(editor.start(path)); + + }, + }, onlySingleBlockSelected && { + key: 'copyLinkToBlock', + content: t('document.plugins.optionAction.copyLinkToBlock'), + icon: , + onClick: async () => { + const blockId = selectedBlockIds?.[0]; + + const url = new URL(window.location.href); + + url.searchParams.set('blockId', blockId); + + await copyTextToClipboard(url.toString()); + notify.success(t('shareAction.copyLinkToBlockSuccess')); + }, + }].filter(Boolean) as { + key: string; + content: string; + icon: JSX.Element; + onClick: () => void; + }[]; + }, [t, selectedBlockIds, editor, onlySingleBlockSelected]); + + return ( + { + const path = node?.[1]; + + if (path) { + window.getSelection()?.removeAllRanges(); + ReactEditor.focus(editor); + editor.select(editor.start(path)); + } + + onClose(); + }} + open={open} + + {...popoverProps} + > +
+ {options.map((option) => { + return ( + + ); + })} + + {node?.[0]?.type === BlockType.OutlineBlock && onlySingleBlockSelected && ( + + )} +
+
+ ); +} + +export default ControlsMenu; \ No newline at end of file diff --git a/frontend/appflowy_web_app/src/components/editor/components/toolbar/block-controls/Depth.tsx b/frontend/appflowy_web_app/src/components/editor/components/toolbar/block-controls/Depth.tsx new file mode 100644 index 0000000000..f63dc56d79 --- /dev/null +++ b/frontend/appflowy_web_app/src/components/editor/components/toolbar/block-controls/Depth.tsx @@ -0,0 +1,87 @@ +import { YjsEditor } from '@/application/slate-yjs'; +import { CustomEditor } from '@/application/slate-yjs/command'; +import { Origins, Popover } from '@/components/_shared/popover'; +import { OutlineNode } from '@/components/editor/editor.type'; +import { Button, Divider, IconButton } from '@mui/material'; +import React, { useCallback, useRef } from 'react'; +import { ReactComponent as HashtagIcon } from '@/assets/sign-hashtag.svg'; +import { useTranslation } from 'react-i18next'; +import { useSlateStatic } from 'slate-react'; + +const origins: Origins = { + anchorOrigin: { + vertical: 'top', + horizontal: 'right', + }, + transformOrigin: { + vertical: 'top', + horizontal: -16, + }, +}; + +function Depth ({ + node, +}: { + node: OutlineNode +}) { + const [open, setOpen] = React.useState(false); + const ref = useRef(null); + const { t } = useTranslation(); + const editor = useSlateStatic() as YjsEditor; + const blockId = node.blockId; + const [originalDepth, setOriginalDepth] = React.useState(node.data.depth || 1); + + const handleDepthChange = useCallback((depth: number) => { + if (depth === originalDepth) return; + CustomEditor.setBlockData(editor, blockId, { + depth, + }); + setOriginalDepth(depth); + }, [blockId, editor, originalDepth]); + + return ( + <> + + + setOpen(false)} + {...origins} + > +
+ { + ['H1', 'H2', 'H3', 'H4', 'H5', 'H6'].map((depth) => { + return ( + { + handleDepthChange(Number(depth[1])); + setOpen(false); + }} + > + {depth} + + ); + }) + } +
+ +
+ + ); +} + +export default Depth; \ No newline at end of file diff --git a/frontend/appflowy_web_app/src/components/editor/components/toolbar/block-controls/HoverControls.hooks.ts b/frontend/appflowy_web_app/src/components/editor/components/toolbar/block-controls/HoverControls.hooks.ts new file mode 100644 index 0000000000..2336198322 --- /dev/null +++ b/frontend/appflowy_web_app/src/components/editor/components/toolbar/block-controls/HoverControls.hooks.ts @@ -0,0 +1,159 @@ +import { YjsEditor } from '@/application/slate-yjs'; +import { BlockType } from '@/application/types'; +import { getScrollParent } from '@/components/global-comment/utils'; +import { useCallback, useEffect, useRef, useState } from 'react'; +import { Editor, Element, Range } from 'slate'; +import { ReactEditor, useSlateStatic } from 'slate-react'; +import { findEventNode, getBlockActionsPosition, getBlockCssProperty } from './utils'; +import { findSlateEntryByBlockId } from '@/application/slate-yjs/utils/editor'; + +export function useHoverControls({ disabled }: { disabled: boolean; }) { + const editor = useSlateStatic() as YjsEditor; + const ref = useRef(null); + const [hoveredBlockId, setHoveredBlockId] = useState(null); + const [cssProperty, setCssProperty] = useState(''); + + const recalculatePosition = useCallback( + (blockElement: HTMLElement) => { + const { top, left } = getBlockActionsPosition(editor, blockElement); + + const slateEditorDom = ReactEditor.toDOMNode(editor, editor); + + if (!ref.current) return; + + ref.current.style.top = `${top + slateEditorDom.offsetTop}px`; + ref.current.style.left = `${left + slateEditorDom.offsetLeft - 64}px`; + }, + [editor], + ); + + const close = useCallback(() => { + const el = ref.current; + + if (!el) return; + + el.style.opacity = '0'; + el.style.pointerEvents = 'none'; + setHoveredBlockId(null); + setCssProperty(''); + }, [ref]); + + useEffect(() => { + const handleMouseMove = (e: MouseEvent) => { + if (disabled) return; + const el = ref.current; + + if (!el) return; + + let range: Range | null = null; + let node: Element | null = null; + + try { + range = ReactEditor.findEventRange(editor, e); + if (!range) { + throw new Error('No range found'); + } + } catch { + const editorDom = ReactEditor.toDOMNode(editor, editor); + const rect = editorDom.getBoundingClientRect(); + const isOverLeftBoundary = e.clientX > rect.left; + const isOverRightBoundary = e.clientX > rect.right - 96 && e.clientX < rect.right; + let newX = e.clientX; + + if (isOverLeftBoundary || isOverRightBoundary) { + newX = rect.left + editorDom.clientWidth / 2; + } + + node = findEventNode(editor, { + x: newX, + y: e.clientY, + }); + + } + + if (!range && !node) { + console.warn('No range and node found'); + return; + } else if (range) { + const match = editor.above({ + match: (n) => { + return !Editor.isEditor(n) && Element.isElement(n) && n.blockId !== undefined; + }, + at: range, + }); + + if (!match) { + close(); + return; + } + + node = match[0]; + } + + if (!node) { + close(); + return; + } + + const blockElement = ReactEditor.toDOMNode(editor, node); + + if (!blockElement) return; + const shouldSkipTypes = [BlockType.TableBlock, BlockType.GridBlock, BlockType.CalendarBlock, BlockType.BoardBlock, BlockType.SimpleTableBlock]; + + if (shouldSkipTypes.some((type) => blockElement.closest(`[data-block-type="${type}"]`))) { + close(); + return; + + } else { + recalculatePosition(blockElement); + el.style.opacity = '1'; + el.style.pointerEvents = 'auto'; + + setCssProperty(getBlockCssProperty(node)); + setHoveredBlockId(node.blockId as string); + } + + }; + + const dom = ReactEditor.toDOMNode(editor, editor); + + if (!disabled) { + dom.addEventListener('mousemove', handleMouseMove); + dom.parentElement?.addEventListener('mouseleave', close); + getScrollParent(dom)?.addEventListener('scroll', close); + } + + return () => { + dom.removeEventListener('mousemove', handleMouseMove); + dom.parentElement?.removeEventListener('mouseleave', close); + getScrollParent(dom)?.removeEventListener('scroll', close); + }; + }, [close, editor, ref, recalculatePosition, disabled]); + + useEffect(() => { + let observer: MutationObserver | null = null; + + if (hoveredBlockId) { + const [node] = findSlateEntryByBlockId(editor, hoveredBlockId); + const dom = ReactEditor.toDOMNode(editor, node); + + if (dom.parentElement) { + observer = new MutationObserver(close); + + observer.observe(dom.parentElement, { + childList: true, + }); + } + } + + return () => { + observer?.disconnect(); + }; + }, [close, editor, hoveredBlockId]); + + return { + hoveredBlockId, + ref, + cssProperty, + }; +} \ No newline at end of file diff --git a/frontend/appflowy_web_app/src/components/editor/components/toolbar/block-controls/HoverControls.tsx b/frontend/appflowy_web_app/src/components/editor/components/toolbar/block-controls/HoverControls.tsx new file mode 100644 index 0000000000..02cf700f15 --- /dev/null +++ b/frontend/appflowy_web_app/src/components/editor/components/toolbar/block-controls/HoverControls.tsx @@ -0,0 +1,36 @@ +import ControlActions from '@/components/editor/components/toolbar/block-controls/ControlActions'; +import { useHoverControls } from '@/components/editor/components/toolbar/block-controls/HoverControls.hooks'; +import React, { useState } from 'react'; + +export function HoverControls () { + const [openMenu, setOpenMenu] = useState(false); + + const { ref, cssProperty, hoveredBlockId } = useHoverControls({ + disabled: openMenu, + }); + + return ( + <> +
{ + e.preventDefault(); + }} + className={`absolute hover-controls w-[64px] px-1 z-10 opacity-0 flex items-center justify-end ${cssProperty}`} + > + {/* Ensure the toolbar in middle */} +
$
+ +
+ + + ); +} + +export default HoverControls; \ No newline at end of file diff --git a/frontend/appflowy_web_app/src/components/editor/components/toolbar/block-controls/index.ts b/frontend/appflowy_web_app/src/components/editor/components/toolbar/block-controls/index.ts new file mode 100644 index 0000000000..2b0f2d7f68 --- /dev/null +++ b/frontend/appflowy_web_app/src/components/editor/components/toolbar/block-controls/index.ts @@ -0,0 +1 @@ +export * from './HoverControls'; \ No newline at end of file diff --git a/frontend/appflowy_web_app/src/components/editor/components/toolbar/block-controls/utils.ts b/frontend/appflowy_web_app/src/components/editor/components/toolbar/block-controls/utils.ts new file mode 100644 index 0000000000..f0d484e1f7 --- /dev/null +++ b/frontend/appflowy_web_app/src/components/editor/components/toolbar/block-controls/utils.ts @@ -0,0 +1,75 @@ +import { BlockType } from '@/application/types'; +import { HeadingNode } from '@/components/editor/editor.type'; +import { ReactEditor } from 'slate-react'; +import { Element } from 'slate'; + +export function getBlockActionsPosition (editor: ReactEditor, blockElement: HTMLElement) { + const editorDom = ReactEditor.toDOMNode(editor, editor); + const editorDomRect = editorDom.getBoundingClientRect(); + const blockDomRect = blockElement.getBoundingClientRect(); + const parentBlockDom = blockElement.parentElement?.closest('[data-block-type]'); + + const relativeTop = blockDomRect.top - editorDomRect.top; + let relativeLeft = blockDomRect.left - editorDomRect.left; + + if (parentBlockDom?.getAttribute('data-block-type') === BlockType.QuoteBlock) { + relativeLeft -= 16; + } + + return { + top: relativeTop, + left: relativeLeft, + height: blockDomRect.height, + }; +} + +export function getBlockCssProperty (node: Element) { + if ((node as HeadingNode).data.level) { + return `level-${(node as HeadingNode).data.level} mt-[3px]`; + } + + switch (node.type) { + case BlockType.Paragraph: + case BlockType.NumberedListBlock: + case BlockType.BulletedListBlock: + case BlockType.TodoListBlock: + case BlockType.ToggleListBlock: + case BlockType.QuoteBlock: + case BlockType.SubpageBlock: + case BlockType.CalloutBlock: + return 'leading-[2em]'; + case BlockType.OutlineBlock: + return 'py-[7px]'; + case BlockType.GridBlock: + case BlockType.TableBlock: + return 'my-3'; + case BlockType.GalleryBlock: + return 'my-4'; + case BlockType.DividerBlock: + return 'my-[-4px]'; + default: + return 'my-2'; + } +} + +export function findEventNode ( + editor: ReactEditor, + { + x, + y, + }: { + x: number; + y: number; + }, +): Element | null { + const element = document.elementFromPoint(x, y); + const nodeDom = element?.closest('[data-block-type]'); + + if (nodeDom) { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + return ReactEditor.toSlateNode(editor, nodeDom); + } + + return null; +} diff --git a/frontend/appflowy_web_app/src/components/editor/components/toolbar/index.tsx b/frontend/appflowy_web_app/src/components/editor/components/toolbar/index.tsx index 7b97324486..5a81df6f53 100644 --- a/frontend/appflowy_web_app/src/components/editor/components/toolbar/index.tsx +++ b/frontend/appflowy_web_app/src/components/editor/components/toolbar/index.tsx @@ -1,13 +1,13 @@ -import { ElementFallbackRender } from '@/components/error/ElementFallbackRender'; import React from 'react'; -import { ErrorBoundary } from 'react-error-boundary'; +import { HoverControls } from 'src/components/editor/components/toolbar/block-controls'; import { SelectionToolbar } from './selection-toolbar/SelectionToolbar'; function Toolbars () { return ( - + <> - + + ); } diff --git a/frontend/appflowy_web_app/src/components/editor/components/toolbar/selection-toolbar/SelectionToolbar.hooks.ts b/frontend/appflowy_web_app/src/components/editor/components/toolbar/selection-toolbar/SelectionToolbar.hooks.ts index 81dfee4dd0..b77ae633ea 100644 --- a/frontend/appflowy_web_app/src/components/editor/components/toolbar/selection-toolbar/SelectionToolbar.hooks.ts +++ b/frontend/appflowy_web_app/src/components/editor/components/toolbar/selection-toolbar/SelectionToolbar.hooks.ts @@ -1,17 +1,17 @@ -import { YjsEditor } from '@/application/slate-yjs'; -import { getOffsetPointFromSlateRange } from '@/application/slate-yjs/utils/yjsOperations'; -import { getRangeRect, getSelectionPosition } from '@/components/editor/components/toolbar/selection-toolbar/utils'; -import { useEditorContext } from '@/components/editor/EditorContext'; -import { PopoverPosition } from '@mui/material'; +import { CustomEditor } from '@/application/slate-yjs/command'; +import { EditorMarkFormat } from '@/application/slate-yjs/types'; +import { getSelectionPosition } from '@/components/editor/components/toolbar/selection-toolbar/utils'; +import { Decorate, useEditorContext } from '@/components/editor/EditorContext'; +import { createHotkey, HOT_KEY_NAME } from '@/utils/hotkeys'; import { debounce } from 'lodash-es'; -import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react'; +import { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react'; import { Range } from 'slate'; import { ReactEditor, useFocused, useSlate, useSlateStatic } from 'slate-react'; -export function useVisible () { +export function useVisible() { const editor = useSlate(); const selection = editor.selection; - const { addDecorate, removeDecorate } = useEditorContext(); + const { decorateState, addDecorate, removeDecorate } = useEditorContext(); const [forceShow, setForceShow] = useState(false); const [isDragging, setDragging] = useState(false); @@ -19,12 +19,22 @@ export function useVisible () { const isExpanded = selection ? Range.isExpanded(selection) : false; + const selectedText = useMemo(() => { + if (!selection) return 0; + + return CustomEditor.getTextNodes(editor).length; + }, [editor, selection]); + const visible = useMemo(() => { if (forceShow) return true; if (!focus) return false; - return isExpanded && !isDragging; - }, [focus, forceShow, isExpanded, isDragging]); + if (document.getSelection()?.isCollapsed) return false; + + const show = Boolean(selectedText && isExpanded && !isDragging); + + return show; + }, [forceShow, focus, selectedText, isExpanded, isDragging]); useEffect(() => { if (!visible) { @@ -37,6 +47,12 @@ export function useVisible () { useEffect(() => { const handleMouseDown = () => { + const { selection } = editor; + + if (selection && Range.isExpanded(selection)) { + window.getSelection()?.removeAllRanges(); + } + setDragging(true); }; @@ -56,7 +72,7 @@ export function useVisible () { document.removeEventListener('mouseup', handleMouseUp); }; - }, [removeDecorate]); + }, [editor, removeDecorate]); const handleForceShow = useCallback((show: boolean) => { if (show && editor.selection) { @@ -65,16 +81,107 @@ export function useVisible () { } else { setForceShow(false); } - }, [addDecorate, editor.selection]); + }, [addDecorate, editor]); + + const getDecorateState = useCallback(() => { + return decorateState?.['selection-toolbar']; + }, [decorateState]); + + useEffect(() => { + if (!visible) return; + const handleKeyDown = (event: KeyboardEvent) => { + + switch (true) { + case createHotkey(HOT_KEY_NAME.ESCAPE)(event): { + if (!editor.selection) break; + event.preventDefault(); + event.stopPropagation(); + const start = editor.start(editor.selection); + + editor.select(start); + ReactEditor.focus(editor); + + break; + } + + /** + * Bold: Mod + B + */ + case createHotkey(HOT_KEY_NAME.BOLD)(event): + event.preventDefault(); + CustomEditor.toggleMark(editor, { + key: EditorMarkFormat.Bold, + value: true, + }); + break; + /** + * Italic: Mod + I + */ + case createHotkey(HOT_KEY_NAME.ITALIC)(event): + event.preventDefault(); + CustomEditor.toggleMark(editor, { + key: EditorMarkFormat.Italic, + value: true, + }); + break; + /** + * Underline: Mod + U + */ + case createHotkey(HOT_KEY_NAME.UNDERLINE)(event): + event.preventDefault(); + CustomEditor.toggleMark(editor, { + key: EditorMarkFormat.Underline, + value: true, + }); + break; + /** + * Strikethrough: Mod + Shift + S / Mod + Shift + X + */ + case createHotkey(HOT_KEY_NAME.STRIKETHROUGH)(event): + event.preventDefault(); + CustomEditor.toggleMark(editor, { + key: EditorMarkFormat.StrikeThrough, + value: true, + }); + break; + /** + * Code: Mod + E + */ + case createHotkey(HOT_KEY_NAME.CODE)(event): + event.preventDefault(); + CustomEditor.toggleMark(editor, { + key: EditorMarkFormat.Code, + value: true, + }); + break; + /** + * Highlight: Mod + Shift + H + */ + case createHotkey(HOT_KEY_NAME.HIGH_LIGHT)(event): + event.preventDefault(); + CustomEditor.highlight(editor); + break; + } + }; + + const slateEditorDom = ReactEditor.toDOMNode(editor, editor); + + slateEditorDom.addEventListener('keydown', handleKeyDown); + + return () => { + slateEditorDom.removeEventListener('keydown', handleKeyDown); + }; + }, [editor, visible]); return { visible, forceShow: handleForceShow, + getDecorateState, }; } -export function useToolbarPosition () { +export function useToolbarPosition() { const editor = useSlateStatic(); const setPosition = useCallback((toolbarEl: HTMLDivElement, position: { @@ -89,16 +196,17 @@ export function useToolbarPosition () { const left = position.left + slateEditorDom.offsetLeft; // If toolbar is out of editor, move it to the left edge of the editor - if (left < 0) { - toolbarEl.style.left = '0'; + if (left <= 0) { + toolbarEl.style.left = '0px'; return; } const right = left + toolbarEl.offsetWidth; + const rightBound = slateEditorDom.offsetWidth + slateEditorDom.offsetLeft; // If toolbar is out of editor, move the right edge to the right edge of the editor - if (right > slateEditorDom.offsetWidth) { - toolbarEl.style.left = `${slateEditorDom.offsetWidth - toolbarEl.offsetWidth}px`; + if (right > rightBound) { + toolbarEl.style.left = `${rightBound - toolbarEl.offsetWidth}px`; return; } @@ -130,59 +238,15 @@ export function useToolbarPosition () { export const SelectionToolbarContext = createContext<{ visible: boolean; forceShow: (forceVisible: boolean) => void; + rePosition: () => void; + getDecorateState: () => Decorate | undefined; }>({ visible: false, forceShow: () => undefined, + rePosition: () => undefined, + getDecorateState: () => undefined, }); -export function useSelectionToolbarContext () { +export function useSelectionToolbarContext() { return useContext(SelectionToolbarContext); } - -interface RelativeRange { - offset: number; - textId: string; -} - -export function useToolbarPopover (editor: YjsEditor) { - const { forceShow } = useSelectionToolbarContext(); - const [anchorPosition, setAnchorPosition] = useState(undefined); - const rangeRef = useRef<{ start: RelativeRange; end: RelativeRange } | null>(null); - - const open = Boolean(anchorPosition); - - const handleClose = useCallback(() => { - setAnchorPosition(undefined); - forceShow(false); - }, [forceShow]); - - const openPopover = useCallback(() => { - if (!editor.selection) return; - const rect = getRangeRect(); - - if (!rect) return; - - try { - rangeRef.current = { - start: getOffsetPointFromSlateRange(editor, editor.start(editor.selection)), - end: getOffsetPointFromSlateRange(editor, editor.end(editor.selection)), - }; - } catch (e) { - rangeRef.current = null; - } - - forceShow(true); - - setAnchorPosition({ - top: rect.top + rect.height, - left: rect.left + rect.width / 2, - }); - }, [editor, forceShow]); - - return { - open, - anchorPosition, - handleClose, - openPopover, - }; -} \ No newline at end of file diff --git a/frontend/appflowy_web_app/src/components/editor/components/toolbar/selection-toolbar/SelectionToolbar.tsx b/frontend/appflowy_web_app/src/components/editor/components/toolbar/selection-toolbar/SelectionToolbar.tsx index fd884f3f71..7619ff676e 100644 --- a/frontend/appflowy_web_app/src/components/editor/components/toolbar/selection-toolbar/SelectionToolbar.tsx +++ b/frontend/appflowy_web_app/src/components/editor/components/toolbar/selection-toolbar/SelectionToolbar.tsx @@ -1,9 +1,9 @@ import ToolbarActions from '@/components/editor/components/toolbar/selection-toolbar/ToolbarActions'; import { SelectionToolbarContext, useToolbarPosition, useVisible } from './SelectionToolbar.hooks'; -import React, { useEffect, useRef } from 'react'; +import React, { useCallback, useEffect, useRef } from 'react'; -export function SelectionToolbar () { - const { visible, forceShow } = useVisible(); +export function SelectionToolbar() { + const { visible, forceShow, getDecorateState } = useVisible(); const ref = useRef(null); const { hideToolbar, @@ -14,20 +14,37 @@ export function SelectionToolbar () { const el = ref.current; if (!el) return; + + const onScroll = () => { + showToolbar(el); + }; + if (!visible) { hideToolbar(el); - return; + } else { + showToolbar(el); + window.addEventListener('scroll', onScroll, true); } - showToolbar(el); + return () => { + window.removeEventListener('scroll', onScroll, true); + }; }, [hideToolbar, showToolbar, visible]); + const rePosition = useCallback(() => { + const el = ref.current; + + if (!el) return; + + showToolbar(el); + }, [showToolbar]); + return ( - +
{ // prevent toolbar from taking focus away from editor @@ -35,7 +52,7 @@ export function SelectionToolbar () { e.stopPropagation(); }} > - +
); diff --git a/frontend/appflowy_web_app/src/components/editor/components/toolbar/selection-toolbar/SelectionToolbarPopoverContext.tsx b/frontend/appflowy_web_app/src/components/editor/components/toolbar/selection-toolbar/SelectionToolbarPopoverContext.tsx deleted file mode 100644 index 707d204434..0000000000 --- a/frontend/appflowy_web_app/src/components/editor/components/toolbar/selection-toolbar/SelectionToolbarPopoverContext.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import { useToolbarPopover } from './SelectionToolbar.hooks'; -import React, { createContext, useContext, ReactNode } from 'react'; -import { Popover } from '@/components/_shared/popover'; -import { YjsEditor } from '@/application/slate-yjs'; -import { useSlateStatic } from 'slate-react'; - -interface SelectionToolbarPopoverContextType { - open: boolean; - openPopover: () => void; - closePopover: () => void; -} - -const SelectionToolbarPopoverContext = createContext(null); - -export function useSelectionToolbarPopoverContext () { - const context = useContext(SelectionToolbarPopoverContext); - - if (!context) { - throw new Error('useSelectionToolbarPopoverContext must be used within a SelectionToolbarPopoverProvider'); - } - - return context; -} - -interface SelectionToolbarPopoverProviderProps { - children: ReactNode; - popoverContent: React.ReactNode; -} - -export function SelectionToolbarPopoverProvider ({ children, popoverContent }: SelectionToolbarPopoverProviderProps) { - const editor = useSlateStatic() as YjsEditor; - const { - open, - anchorPosition, - handleClose, - openPopover, - } = useToolbarPopover(editor); - - return ( - - {children} - {open && ( - { - e.preventDefault(); - e.stopPropagation(); - }} - onMouseUp={e => { - e.stopPropagation(); - }} - disableRestoreFocus={false} - open={open} - onClose={handleClose} - anchorPosition={anchorPosition} - anchorReference={'anchorPosition'} - transformOrigin={{ - vertical: 'top', - horizontal: 'center', - }} - > - {popoverContent} - - )} - - ); -} \ No newline at end of file diff --git a/frontend/appflowy_web_app/src/components/editor/components/toolbar/selection-toolbar/ToolbarActions.tsx b/frontend/appflowy_web_app/src/components/editor/components/toolbar/selection-toolbar/ToolbarActions.tsx index dabd6330be..a829bcc258 100644 --- a/frontend/appflowy_web_app/src/components/editor/components/toolbar/selection-toolbar/ToolbarActions.tsx +++ b/frontend/appflowy_web_app/src/components/editor/components/toolbar/selection-toolbar/ToolbarActions.tsx @@ -1,10 +1,10 @@ import { YjsEditor } from '@/application/slate-yjs'; -import { getBlockEntry } from '@/application/slate-yjs/utils/yjsOperations'; +import { getBlockEntry } from '@/application/slate-yjs/utils/editor'; import { BlockType } from '@/application/types'; import Align from '@/components/editor/components/toolbar/selection-toolbar/actions/Align'; import Bold from '@/components/editor/components/toolbar/selection-toolbar/actions/Bold'; import BulletedList from '@/components/editor/components/toolbar/selection-toolbar/actions/BulletedList'; -// import Href from '@/components/editor/components/toolbar/selection-toolbar/actions/Href'; +import Href from '@/components/editor/components/toolbar/selection-toolbar/actions/Href'; import Color from '@/components/editor/components/toolbar/selection-toolbar/actions/Color'; import Formula from '@/components/editor/components/toolbar/selection-toolbar/actions/Formula'; import Heading from '@/components/editor/components/toolbar/selection-toolbar/actions/Heading'; @@ -14,17 +14,25 @@ import NumberedList from '@/components/editor/components/toolbar/selection-toolb import Quote from '@/components/editor/components/toolbar/selection-toolbar/actions/Quote'; import StrikeThrough from '@/components/editor/components/toolbar/selection-toolbar/actions/StrikeThrough'; import Underline from '@/components/editor/components/toolbar/selection-toolbar/actions/Underline'; +import { + useSelectionToolbarContext, +} from '@/components/editor/components/toolbar/selection-toolbar/SelectionToolbar.hooks'; import { Divider } from '@mui/material'; -import { Editor, Element } from 'slate'; +import { Editor, Element, Path } from 'slate'; import Paragraph from './actions/Paragraph'; import React, { useMemo } from 'react'; import { useSlate } from 'slate-react'; -function ToolbarActions () { +function ToolbarActions() { const editor = useSlate() as YjsEditor; const selection = editor.selection; + const { + visible: toolbarVisible, + } = useSelectionToolbarContext(); + const start = useMemo(() => selection ? editor.start(selection) : null, [editor, selection]); const end = useMemo(() => selection ? editor.end(selection) : null, [editor, selection]); + const startBlock = useMemo(() => { if (!start) return null; try { @@ -43,6 +51,7 @@ function ToolbarActions () { }, [editor, end]); const isAcrossBlock = useMemo(() => { + if (startBlock && endBlock && Path.equals(startBlock[1], endBlock[1])) return false; return startBlock?.[0].blockId !== endBlock?.[0].blockId; }, [endBlock, startBlock]); @@ -59,35 +68,40 @@ function ToolbarActions () { }, [editor, end, start]); const groupTwo = <> - - - - + + + + ; const groupOne = <> - - + + ; const groupThree = <> - - - - {/**/} + + + + + ; - const groupFour = <>; + const groupFour = <>; return (
} - {!isCodeBlock && !isAcrossBlock && } + {!isCodeBlock && } + {!isCodeBlock && !isAcrossBlock && } { !isAcrossBlock && !isCodeBlock && groupThree } - {groupFour} - {!isCodeBlock && } + {!isCodeBlock && groupFour} +
); } diff --git a/frontend/appflowy_web_app/src/components/editor/components/toolbar/selection-toolbar/actions/Align.tsx b/frontend/appflowy_web_app/src/components/editor/components/toolbar/selection-toolbar/actions/Align.tsx index 8da66f24dc..969e180e1c 100644 --- a/frontend/appflowy_web_app/src/components/editor/components/toolbar/selection-toolbar/actions/Align.tsx +++ b/frontend/appflowy_web_app/src/components/editor/components/toolbar/selection-toolbar/actions/Align.tsx @@ -1,6 +1,6 @@ import { YjsEditor } from '@/application/slate-yjs'; import { CustomEditor } from '@/application/slate-yjs/command'; -import { getBlockEntry } from '@/application/slate-yjs/utils/yjsOperations'; +import { findSlateEntryByBlockId, getBlockEntry } from '@/application/slate-yjs/utils/editor'; import { AlignType, BlockData } from '@/application/types'; import { ReactComponent as AlignCenterSvg } from '@/assets/toolbar_align_center.svg'; import { ReactComponent as AlignLeftSvg } from '@/assets/toolbar_align_left.svg'; @@ -9,11 +9,11 @@ import { Popover } from '@/components/_shared/popover'; import { useSelectionToolbarContext, } from '@/components/editor/components/toolbar/selection-toolbar/SelectionToolbar.hooks'; - import { PopoverProps } from '@mui/material/Popover'; -import React, { useCallback, useRef, useState } from 'react'; +import React, { useCallback, useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useSlateStatic } from 'slate-react'; +import { Element } from 'slate'; import ActionButton from './ActionButton'; const popoverProps: Partial = { @@ -32,17 +32,34 @@ const popoverProps: Partial = { }, }; -export function Align () { +export function Align({ + blockId, + enabled = true, +}: { + blockId?: string; + enabled?: boolean; +}) { const [open, setOpen] = useState(false); - const { - visible: toolbarVisible, - } = useSelectionToolbarContext(); + const ref = useRef(null); const { t } = useTranslation(); const editor = useSlateStatic() as YjsEditor; + + const getNode = useCallback(() => { + let node: Element; + + if (!blockId) { + node = getBlockEntry(editor)[0]; + } else { + node = findSlateEntryByBlockId(editor, blockId)[0]; + } + + return node; + }, [editor, blockId]); + const getAlign = useCallback(() => { try { - const [node] = getBlockEntry(editor); + const node = getNode(); return (node.data as BlockData).align; @@ -50,7 +67,7 @@ export function Align () { return; } - }, [editor]); + }, [getNode]); const handleClose = useCallback(() => { setOpen(false); @@ -65,36 +82,45 @@ export function Align () { switch (align) { case AlignType.Left: - return ; + return ; case 'center': - return ; + return ; case 'right': - return ; + return ; default: - return ; + return ; } }, [getAlign]); + const { rePosition } = useSelectionToolbarContext(); + const toggleAlign = useCallback( (align: AlignType) => { return () => { try { - const [node] = getBlockEntry(editor); + const node = getNode(); CustomEditor.setBlockData(editor, node.blockId as string, { align, }); handleClose(); + rePosition(); } catch (e) { return; } }; }, - [editor, handleClose], + [getNode, editor, handleClose, rePosition], ); + useEffect(() => { + if (!enabled) { + setOpen(false); + } + }, [enabled]); + return ( <> { setOpen(false); }} - open={open && toolbarVisible} + open={open && enabled} anchorEl={ref.current} {...popoverProps} > @@ -127,21 +153,21 @@ export function Align () { tooltip={t('document.plugins.optionAction.left')} onClick={toggleAlign(AlignType.Left)} > - + - + - +
diff --git a/frontend/appflowy_web_app/src/components/editor/components/toolbar/selection-toolbar/actions/BulletedList.tsx b/frontend/appflowy_web_app/src/components/editor/components/toolbar/selection-toolbar/actions/BulletedList.tsx index 1e9700306e..1832b6a854 100644 --- a/frontend/appflowy_web_app/src/components/editor/components/toolbar/selection-toolbar/actions/BulletedList.tsx +++ b/frontend/appflowy_web_app/src/components/editor/components/toolbar/selection-toolbar/actions/BulletedList.tsx @@ -1,6 +1,6 @@ import { YjsEditor } from '@/application/slate-yjs'; import { CustomEditor } from '@/application/slate-yjs/command'; -import { getBlockEntry } from '@/application/slate-yjs/utils/yjsOperations'; +import { getBlockEntry } from '@/application/slate-yjs/utils/editor'; import { BlockType } from '@/application/types'; import React, { useCallback } from 'react'; import ActionButton from './ActionButton'; @@ -8,7 +8,7 @@ import { useTranslation } from 'react-i18next'; import { useSlateStatic } from 'slate-react'; import { ReactComponent as BulletedListSvg } from '@/assets/bulleted_list.svg'; -export function BulletedList () { +export function BulletedList() { const { t } = useTranslation(); const editor = useSlateStatic() as YjsEditor; const isActivated = CustomEditor.isBlockActive(editor, BlockType.BulletedListBlock); @@ -33,7 +33,7 @@ export function BulletedList () { return ( - + ); } diff --git a/frontend/appflowy_web_app/src/components/editor/components/toolbar/selection-toolbar/actions/Color.tsx b/frontend/appflowy_web_app/src/components/editor/components/toolbar/selection-toolbar/actions/Color.tsx index ca01671300..320469295e 100644 --- a/frontend/appflowy_web_app/src/components/editor/components/toolbar/selection-toolbar/actions/Color.tsx +++ b/frontend/appflowy_web_app/src/components/editor/components/toolbar/selection-toolbar/actions/Color.tsx @@ -1,43 +1,44 @@ import { YjsEditor } from '@/application/slate-yjs'; import { CustomEditor } from '@/application/slate-yjs/command'; import { EditorMarkFormat } from '@/application/slate-yjs/types'; -import { ColorPicker } from '@/components/_shared/color-picker'; +import { Popover } from '@/components/_shared/popover'; import { - SelectionToolbarPopoverProvider, - useSelectionToolbarPopoverContext, -} from '@/components/editor/components/toolbar/selection-toolbar/SelectionToolbarPopoverContext'; -import React, { useCallback } from 'react'; + useSelectionToolbarContext, +} from '@/components/editor/components/toolbar/selection-toolbar/SelectionToolbar.hooks'; +import { ColorEnum, renderColor } from '@/utils/color'; +import { Tooltip } from '@mui/material'; +import React, { useCallback, useEffect, useMemo } from 'react'; import ActionButton from './ActionButton'; import { useTranslation } from 'react-i18next'; import { useSlateStatic } from 'slate-react'; import { ReactComponent as ColorSvg } from '@/assets/color_theme.svg'; +import { ReactComponent as TextSvg } from '@/assets/format_text.svg'; -function ColorButton () { +function Color () { const { t } = useTranslation(); + const { + visible: toolbarVisible, + } = useSelectionToolbarContext(); const editor = useSlateStatic() as YjsEditor; - const { openPopover } = useSelectionToolbarPopoverContext(); const isActivated = CustomEditor.isMarkActive(editor, EditorMarkFormat.BgColor) || CustomEditor.isMarkActive(editor, EditorMarkFormat.FontColor); + const [anchorEl, setAnchorEl] = React.useState(null); + const open = Boolean(anchorEl); - const onClick = useCallback((e: React.MouseEvent) => { + useEffect(() => { + if (!toolbarVisible) { + setAnchorEl(null); + } + }, [toolbarVisible]); + + const onClick = useCallback((e: React.MouseEvent) => { e.stopPropagation(); e.preventDefault(); - openPopover(); - }, [openPopover]); + setAnchorEl(e.currentTarget); + }, []); - return ( - - - - ); -} - -function ColorPickerContent () { - const editor = useSlateStatic() as YjsEditor; - const { closePopover } = useSelectionToolbarPopoverContext(); + const handleClose = useCallback(() => { + setAnchorEl(null); + }, []); const handlePickedColor = useCallback((format: EditorMarkFormat, color: string) => { if (color) { @@ -50,20 +51,177 @@ function ColorPickerContent () { } }, [editor]); - return ( - - ); -} + const editorTextColors = useMemo(() => { + return [{ + label: t('editor.fontColorDefault'), + color: '', + }, { + label: t('editor.fontColorGray'), + color: 'rgb(120, 119, 116)', + }, { + label: t('editor.fontColorBrown'), + color: 'rgb(159, 107, 83)', + }, { + label: t('editor.fontColorOrange'), + color: 'rgb(217, 115, 13)', + }, { + label: t('editor.fontColorYellow'), + color: 'rgb(203, 145, 47)', + }, { + label: t('editor.fontColorGreen'), + color: 'rgb(68, 131, 97)', + }, { + label: t('editor.fontColorBlue'), + color: 'rgb(51, 126, 169)', + }, { + label: t('editor.fontColorPurple'), + color: 'rgb(144, 101, 176)', + }, { + label: t('editor.fontColorPink'), + color: 'rgb(193, 76, 138)', + }, { + label: t('editor.fontColorRed'), + color: 'rgb(212, 76, 71)', + }]; + }, [t]); + + const editorBgColors = useMemo(() => { + return [{ + label: t('editor.backgroundColorDefault'), + color: '', + }, { + label: t('editor.backgroundColorLime'), + color: ColorEnum.Lime, + }, { + label: t('editor.backgroundColorAqua'), + color: ColorEnum.Aqua, + }, { + label: t('editor.backgroundColorOrange'), + color: ColorEnum.Orange, + }, { + label: t('editor.backgroundColorYellow'), + color: ColorEnum.Yellow, + }, { + label: t('editor.backgroundColorGreen'), + color: ColorEnum.Green, + }, { + label: t('editor.backgroundColorBlue'), + color: ColorEnum.Blue, + }, { + label: t('editor.backgroundColorPurple'), + color: ColorEnum.Purple, + }, { + label: t('editor.backgroundColorPink'), + color: ColorEnum.Pink, + }, { + label: t('editor.backgroundColorRed'), + color: ColorEnum.LightPink, + }]; + }, [t]); + + const popoverContent = useMemo(() => { + return
+
+
{t('editor.textColor')}
+
+ {editorTextColors.map((color, index) => { + return +
handlePickedColor(EditorMarkFormat.FontColor, color.color)} + style={{ + color: color.color || 'var(--text-title)', + }} + > +
+ +
+ ; + })} +
+
+
+
{t('editor.backgroundColor')}
+
+ {editorBgColors.map((color, index) => { + return +
handlePickedColor(EditorMarkFormat.BgColor, color.color)} + > +
+
+
+ ; + })} +
+
+
; + }, [editorBgColors, editorTextColors, handlePickedColor, t]); -function Color () { return ( - }> - - + <> + + + + {toolbarVisible && { + e.preventDefault(); + e.stopPropagation(); + }} + onMouseUp={e => { + e.stopPropagation(); + }} + disableRestoreFocus={true} + disableAutoFocus={true} + disableEnforceFocus={true} + open={open} + onClose={handleClose} + anchorEl={anchorEl} + anchorOrigin={{ + vertical: 'bottom', + horizontal: 'center', + }} + transformOrigin={{ + vertical: -8, + horizontal: 'center', + }} + > + {popoverContent} + } + + ); } diff --git a/frontend/appflowy_web_app/src/components/editor/components/toolbar/selection-toolbar/actions/Formula.tsx b/frontend/appflowy_web_app/src/components/editor/components/toolbar/selection-toolbar/actions/Formula.tsx index 152f54a5bd..88f697acd8 100644 --- a/frontend/appflowy_web_app/src/components/editor/components/toolbar/selection-toolbar/actions/Formula.tsx +++ b/frontend/appflowy_web_app/src/components/editor/components/toolbar/selection-toolbar/actions/Formula.tsx @@ -2,24 +2,54 @@ import { YjsEditor } from '@/application/slate-yjs'; import { CustomEditor } from '@/application/slate-yjs/command'; import { EditorMarkFormat } from '@/application/slate-yjs/types'; import ActionButton from '@/components/editor/components/toolbar/selection-toolbar/actions/ActionButton'; -import React, { useCallback } from 'react'; +import { + useSelectionToolbarContext, +} from '@/components/editor/components/toolbar/selection-toolbar/SelectionToolbar.hooks'; +import React, { useCallback, useEffect } from 'react'; import { useTranslation } from 'react-i18next'; import { Transforms, Text, Editor } from 'slate'; -import { useSlateStatic } from 'slate-react'; +import { useSlate } from 'slate-react'; import { ReactComponent as MathSvg } from '@/assets/math.svg'; -function Formula () { +function Formula() { const { t } = useTranslation(); - const editor = useSlateStatic() as YjsEditor; - const isActivated = CustomEditor.isMarkActive(editor, EditorMarkFormat.Formula); + const editor = useSlate() as YjsEditor; + const { + visible, + } = useSelectionToolbarContext(); + const [state, setState] = React.useState({ + isActivated: false, + hasFormulaActivated: false, + hasMentionActivated: false, + }); + + const { isActivated, hasFormulaActivated, hasMentionActivated } = state; + + const getState = useCallback(() => { + const isActivated = CustomEditor.isMarkActive(editor, EditorMarkFormat.Formula); + const hasFormulaActivated = CustomEditor.hasMark(editor, EditorMarkFormat.Formula); + const hasMentionActivated = CustomEditor.hasMark(editor, EditorMarkFormat.Mention); + + return { + isActivated, + hasFormulaActivated, + hasMentionActivated, + }; + }, [editor]); + + useEffect(() => { + if (!visible) return; + setState(getState()); + }, [visible, getState, editor.selection]); const onClick = useCallback(() => { const { selection } = editor; if (!selection) return; + const isActivated = CustomEditor.isMarkActive(editor, EditorMarkFormat.Formula); + if (!isActivated) { - const start = editor.start(selection); const text = editor.string(selection); editor.delete(); @@ -34,9 +64,13 @@ function Formula () { } Transforms.select(editor, { - anchor: start, + anchor: { + path: newSelection.anchor.path, + offset: newSelection.anchor.offset - 1, + }, focus: newSelection.focus, }); + CustomEditor.addMark(editor, { key: EditorMarkFormat.Formula, value: text, @@ -49,24 +83,27 @@ function Formula () { if (!entry) return; - const [node] = entry; - const formula = (node as unknown as Text).formula || ''; + const [node, path] = entry; + const formula = (node as Text).formula; + if (!formula) return; + editor.select(path); CustomEditor.removeMark(editor, EditorMarkFormat.Formula); - editor.delete(); editor.insertText(formula); } - }, [editor, isActivated]); + setState(getState()); + }, [editor, getState]); return ( - + ); } diff --git a/frontend/appflowy_web_app/src/components/editor/components/toolbar/selection-toolbar/actions/Heading.tsx b/frontend/appflowy_web_app/src/components/editor/components/toolbar/selection-toolbar/actions/Heading.tsx index cf16d02282..a9c7ac68fb 100644 --- a/frontend/appflowy_web_app/src/components/editor/components/toolbar/selection-toolbar/actions/Heading.tsx +++ b/frontend/appflowy_web_app/src/components/editor/components/toolbar/selection-toolbar/actions/Heading.tsx @@ -1,22 +1,19 @@ import { YjsEditor } from '@/application/slate-yjs'; import { CustomEditor } from '@/application/slate-yjs/command'; -import { getBlockEntry } from '@/application/slate-yjs/utils/yjsOperations'; +import { getBlockEntry } from '@/application/slate-yjs/utils/editor'; import { BlockType, HeadingBlockData } from '@/application/types'; import { Popover } from '@/components/_shared/popover'; import { useSelectionToolbarContext, } from '@/components/editor/components/toolbar/selection-toolbar/SelectionToolbar.hooks'; import { PopoverProps } from '@mui/material/Popover'; -import React, { useCallback, useRef, useState } from 'react'; +import React, { useCallback, useEffect, useRef, useState } from 'react'; import ActionButton from './ActionButton'; import { useTranslation } from 'react-i18next'; import { useSlateStatic } from 'slate-react'; import { ReactComponent as Heading1Svg } from '@/assets/h1.svg'; import { ReactComponent as Heading2Svg } from '@/assets/h2.svg'; import { ReactComponent as Heading3Svg } from '@/assets/h3.svg'; -import { ReactComponent as Heading4Svg } from '@/assets/h4.svg'; -import { ReactComponent as Heading5Svg } from '@/assets/h5.svg'; -import { ReactComponent as Heading6Svg } from '@/assets/h6.svg'; import { ReactComponent as RightIcon } from '@/assets/arrow_right.svg'; const popoverProps: Partial = { @@ -35,7 +32,7 @@ const popoverProps: Partial = { }, }; -export function Heading () { +export function Heading() { const { t } = useTranslation(); const editor = useSlateStatic() as YjsEditor; const { @@ -84,35 +81,29 @@ export function Heading () { const getActiveButton = useCallback(() => { if (isActivated(1)) { - return ; + return ; } if (isActivated(2)) { - return ; + return ; } if (isActivated(3)) { - return ; + return ; } - if (isActivated(4)) { - return ; - } - - if (isActivated(5)) { - return ; - } - - if (isActivated(6)) { - return ; - } - - return ; + return ; }, [isActivated]); const [open, setOpen] = useState(false); const ref = useRef(null); + useEffect(() => { + if (!toolbarVisible) { + setOpen(false); + } + }, [toolbarVisible]); + return (
{getActiveButton()} - +
- { setOpen(false); }} - open={open && toolbarVisible} + open={open} anchorEl={ref.current} {...popoverProps} > @@ -147,24 +138,25 @@ export function Heading () { tooltip={t('editor.heading1')} onClick={toHeading(1)} > - + - + - +
- + } +
); diff --git a/frontend/appflowy_web_app/src/components/editor/components/toolbar/selection-toolbar/actions/Href.tsx b/frontend/appflowy_web_app/src/components/editor/components/toolbar/selection-toolbar/actions/Href.tsx index 8fe8c3886c..e72a9dfdfd 100644 --- a/frontend/appflowy_web_app/src/components/editor/components/toolbar/selection-toolbar/actions/Href.tsx +++ b/frontend/appflowy_web_app/src/components/editor/components/toolbar/selection-toolbar/actions/Href.tsx @@ -1,49 +1,58 @@ import { YjsEditor } from '@/application/slate-yjs'; import { CustomEditor } from '@/application/slate-yjs/command'; import { EditorMarkFormat } from '@/application/slate-yjs/types'; -import { Popover } from '@/components/_shared/popover'; import { useSelectionToolbarContext, } from '@/components/editor/components/toolbar/selection-toolbar/SelectionToolbar.hooks'; -import { getRangeRect } from '@/components/editor/components/toolbar/selection-toolbar/utils'; import { createHotkey, getModifier, HOT_KEY_NAME } from '@/utils/hotkeys'; -import { PopoverPosition } from '@mui/material'; import React, { useCallback, useEffect, useMemo } from 'react'; import ActionButton from './ActionButton'; import { useTranslation } from 'react-i18next'; -import { ReactEditor, useSlateStatic } from 'slate-react'; +import { ReactEditor, useSlate } from 'slate-react'; import { ReactComponent as LinkSvg } from '@/assets/link.svg'; +import HrefPopover from '@/components/editor/components/leaf/href/HrefPopover'; -export function Href () { +export function Href() { const { t } = useTranslation(); const { forceShow } = useSelectionToolbarContext(); - const editor = useSlateStatic() as YjsEditor; - const hasActivatedInline = CustomEditor.hasMark(editor, EditorMarkFormat.Formula) || CustomEditor.hasMark(editor, EditorMarkFormat.Mention); - const isActivated = CustomEditor.isMarkActive(editor, EditorMarkFormat.Href); - const [anchorPosition, setAnchorPosition] = React.useState(undefined); - const open = Boolean(anchorPosition); - const handleClose = useCallback(() => { - setAnchorPosition(undefined); - }, []); - const formatLink = useCallback(() => { - if (!editor.selection || hasActivatedInline) return; - const rect = getRangeRect(); + const editor = useSlate() as YjsEditor; - if (!rect) return; - forceShow(true); + const { + visible, + } = useSelectionToolbarContext(); + const [state, setState] = React.useState({ + isActivated: false, + hasFormulaActivated: false, + hasMentionActivated: false, + }); - setAnchorPosition({ - top: rect.top + rect.height, - left: rect.left + rect.width / 2, - }); - }, [editor, hasActivatedInline, forceShow]); + const [open, setOpen] = React.useState(false); + const { isActivated, hasFormulaActivated, hasMentionActivated } = state; + + const getState = useCallback(() => { + const isActivated = CustomEditor.isMarkActive(editor, EditorMarkFormat.Href); + const hasFormulaActivated = CustomEditor.hasMark(editor, EditorMarkFormat.Formula); + const hasMentionActivated = CustomEditor.hasMark(editor, EditorMarkFormat.Mention); + + return { + isActivated, + hasFormulaActivated, + hasMentionActivated, + }; + }, [editor]); + + useEffect(() => { + if (!visible) return; + setState(getState()); + }, [visible, getState, editor.selection]); const onClick = useCallback((e: React.MouseEvent) => { e.stopPropagation(); e.preventDefault(); - formatLink(); - }, [formatLink]); + setOpen(true); + forceShow(true); + }, [forceShow]); const tooltip = useMemo(() => { const modifier = getModifier(); @@ -56,11 +65,15 @@ export function Href () { ); }, [t]); + const disabled = hasFormulaActivated || hasMentionActivated; + useEffect(() => { + if (!visible || disabled) return; const onKeyDown = (e: KeyboardEvent) => { if (createHotkey(HOT_KEY_NAME.FORMAT_LINK)(e)) { e.preventDefault(); - formatLink(); + forceShow(true); + setOpen(true); } }; @@ -72,34 +85,32 @@ export function Href () { return () => { slateDom.removeEventListener('keydown', onKeyDown); }; - }, [editor, formatLink]); + }, [visible, editor, forceShow, disabled]); + + const handleUpdatedSelection = useCallback(() => { + forceShow(true); + }, [forceShow]); return ( <> - + - {open && e.stopPropagation()} - onMouseUp={e => e.stopPropagation()} - disableRestoreFocus={true} - open={open} - onClose={handleClose} - anchorPosition={anchorPosition} - anchorReference={'anchorPosition'} - transformOrigin={{ - vertical: 'top', - horizontal: 'center', - }} - > - - } + { + setOpen(false); + forceShow(false); + setState(getState()); + }} + /> ); } diff --git a/frontend/appflowy_web_app/src/components/editor/components/toolbar/selection-toolbar/actions/NumberedList.tsx b/frontend/appflowy_web_app/src/components/editor/components/toolbar/selection-toolbar/actions/NumberedList.tsx index 2d6320857d..b01aa12428 100644 --- a/frontend/appflowy_web_app/src/components/editor/components/toolbar/selection-toolbar/actions/NumberedList.tsx +++ b/frontend/appflowy_web_app/src/components/editor/components/toolbar/selection-toolbar/actions/NumberedList.tsx @@ -1,6 +1,6 @@ import { YjsEditor } from '@/application/slate-yjs'; import { CustomEditor } from '@/application/slate-yjs/command'; -import { getBlockEntry } from '@/application/slate-yjs/utils/yjsOperations'; +import { getBlockEntry } from '@/application/slate-yjs/utils/editor'; import { BlockType } from '@/application/types'; import React, { useCallback } from 'react'; import ActionButton from './ActionButton'; @@ -8,7 +8,7 @@ import { useTranslation } from 'react-i18next'; import { useSlateStatic } from 'slate-react'; import { ReactComponent as NumberedListSvg } from '@/assets/numbered_list.svg'; -export function NumberedList () { +export function NumberedList() { const { t } = useTranslation(); const editor = useSlateStatic() as YjsEditor; const isActivated = CustomEditor.isBlockActive(editor, BlockType.NumberedListBlock); @@ -33,7 +33,7 @@ export function NumberedList () { return ( - + ); } diff --git a/frontend/appflowy_web_app/src/components/editor/components/toolbar/selection-toolbar/actions/Paragraph.tsx b/frontend/appflowy_web_app/src/components/editor/components/toolbar/selection-toolbar/actions/Paragraph.tsx index a209e1483e..857d77a978 100644 --- a/frontend/appflowy_web_app/src/components/editor/components/toolbar/selection-toolbar/actions/Paragraph.tsx +++ b/frontend/appflowy_web_app/src/components/editor/components/toolbar/selection-toolbar/actions/Paragraph.tsx @@ -1,6 +1,6 @@ import { YjsEditor } from '@/application/slate-yjs'; import { CustomEditor } from '@/application/slate-yjs/command'; -import { getBlockEntry } from '@/application/slate-yjs/utils/yjsOperations'; +import { getBlockEntry } from '@/application/slate-yjs/utils/editor'; import { BlockType } from '@/application/types'; import { ReactComponent as ParagraphSvg } from '@/assets/paragraph.svg'; import React, { useCallback } from 'react'; @@ -8,7 +8,7 @@ import { useTranslation } from 'react-i18next'; import { useSlateStatic } from 'slate-react'; import ActionButton from './ActionButton'; -export function Paragraph () { +export function Paragraph() { const { t } = useTranslation(); const editor = useSlateStatic() as YjsEditor; @@ -24,7 +24,7 @@ export function Paragraph () { return ( - + ); } diff --git a/frontend/appflowy_web_app/src/components/editor/components/toolbar/selection-toolbar/actions/Quote.tsx b/frontend/appflowy_web_app/src/components/editor/components/toolbar/selection-toolbar/actions/Quote.tsx index a5d49fca9e..e426fd5d4d 100644 --- a/frontend/appflowy_web_app/src/components/editor/components/toolbar/selection-toolbar/actions/Quote.tsx +++ b/frontend/appflowy_web_app/src/components/editor/components/toolbar/selection-toolbar/actions/Quote.tsx @@ -1,6 +1,6 @@ import { YjsEditor } from '@/application/slate-yjs'; import { CustomEditor } from '@/application/slate-yjs/command'; -import { getBlockEntry } from '@/application/slate-yjs/utils/yjsOperations'; +import { getBlockEntry } from '@/application/slate-yjs/utils/editor'; import { BlockType } from '@/application/types'; import React, { useCallback } from 'react'; import ActionButton from './ActionButton'; @@ -8,7 +8,7 @@ import { useTranslation } from 'react-i18next'; import { useSlateStatic } from 'slate-react'; import { ReactComponent as QuoteSvg } from '@/assets/quote.svg'; -export function Quote () { +export function Quote() { const { t } = useTranslation(); const editor = useSlateStatic() as YjsEditor; const isActivated = CustomEditor.isBlockActive(editor, BlockType.QuoteBlock); @@ -34,7 +34,7 @@ export function Quote () { return ( - + ); } diff --git a/frontend/appflowy_web_app/src/components/editor/components/toolbar/selection-toolbar/utils.ts b/frontend/appflowy_web_app/src/components/editor/components/toolbar/selection-toolbar/utils.ts index ed396a81bb..79ce027034 100644 --- a/frontend/appflowy_web_app/src/components/editor/components/toolbar/selection-toolbar/utils.ts +++ b/frontend/appflowy_web_app/src/components/editor/components/toolbar/selection-toolbar/utils.ts @@ -6,22 +6,7 @@ export function getRangeRect () { if (!rangeCount) return null; - const anchorNode = domSelection.anchorNode; - const focusNode = domSelection.focusNode; - const focusOffset = domSelection.focusOffset; - let domRange = rangeCount > 0 ? domSelection.getRangeAt(0) : undefined; - - const anchorTop = anchorNode?.parentElement?.getBoundingClientRect().top; - const focusTop = focusNode?.parentElement?.getBoundingClientRect().top; - const diff = Math.abs((anchorTop || 0) - (focusTop || 0)); - - if (focusNode && anchorNode && diff > 20) { - const newRange = document.createRange(); - - newRange.setStart(focusNode, focusOffset); - - domRange = newRange; - } + const domRange = rangeCount > 0 ? domSelection.getRangeAt(0) : undefined; return domRange?.getBoundingClientRect(); } @@ -44,7 +29,7 @@ export function getSelectionPosition (editor: ReactEditor) { const relativeDomLeft = rect.left - domNodeRect.left; // if the range is above the window, move the toolbar to the bottom of range - if (rect.top < gap) { + if (rect.top < gap && rect.bottom > 48) { relativeDomTop = -domNodeRect.top + gap; } diff --git a/frontend/appflowy_web_app/src/components/editor/editor.scss b/frontend/appflowy_web_app/src/components/editor/editor.scss index e265eb723d..872905d75b 100644 --- a/frontend/appflowy_web_app/src/components/editor/editor.scss +++ b/frontend/appflowy_web_app/src/components/editor/editor.scss @@ -4,15 +4,87 @@ @apply my-[4px]; } +.block-element { + &:has(.embed-block) { + //@apply mx-1; + } + + .embed-block { + @apply z-[1] hover:bg-content-blue-50 flex relative w-full gap-4 overflow-hidden rounded-[8px] border border-line-divider bg-fill-list-active; + } + + .math-equation-block, .gallery-block { + .embed-block { + @apply hover:bg-fill-list-active; + } + } + +} + +.block-element[data-block-type="table/cell"] { + .block-element .text-placeholder { + @apply hidden; + } + +} + +[role="textbox"][contenteditable="false"] { + .block-element { + .embed-block { + @apply hover:bg-fill-list-active; + } + } +} + + +.block-element.selected { + &:not([data-block-type="table"]):not([data-block-type="simple_table"]):not([data-block-type="grid"]):not([data-block-type="board"]):not([data-block-type="calendar"]) { + @apply bg-content-blue-100; + .embed-block { + @apply bg-content-blue-50; + } + } + + .block-element[data-block-type="table/cell"] { + @apply bg-content-blue-50; + border-radius: 0 !important; + } + + &.block-element[data-block-type="grid"], &.block-element[data-block-type="board"], &.block-element[data-block-type="calendar"] { + .database-tabs, .grid-row-filed-cell, .column { + @apply bg-content-blue-50; + } + } + + &.block-element[data-block-type="simple_table"] { + table { + @apply bg-content-blue-50; + } + } +} + .block-element .block-element:not([data-block-type="table/cell"]) { @apply mb-0; margin-left: 24px; } + .block-element[data-block-type="quote"] { - .block-element { + .border-l-4 > .block-element { margin-left: 0 !important; } + +} + +.block-element[data-block-type="code"] { + //@apply mx-1; +} + +.block-element[data-block-type="callout"] { + //@apply mx-1; + > div > .block-element { + margin-left: 58px !important; + } } .block-element[data-block-type="table/cell"] { @@ -26,7 +98,7 @@ .block-element.block-align-left { > div > .text-element { - text-align: left; + text-align: left !important; justify-content: flex-start; } @@ -37,7 +109,7 @@ .block-element.block-align-right { > div > .text-element { - text-align: right; + text-align: right !important; justify-content: flex-end; } @@ -48,7 +120,7 @@ .block-element.block-align-center { > div > .text-element { - text-align: center; + text-align: center !important; justify-content: center; } @@ -61,6 +133,15 @@ .block-element[data-block-type="todo_list"] .checked > .text-element .text-content { text-decoration: line-through; color: var(--text-caption); + + .mention-content { + color: var(--text-caption); + text-decoration: line-through; + } + + .text-color span { + color: var(--text-caption) !important; + } } .block-element .collapsed .block-element { @@ -70,9 +151,16 @@ [role=textbox] { .text-element { @apply my-1; + &::selection { @apply bg-transparent; } + + .text-block-icon { + &::selection { + @apply bg-transparent; + } + } } } @@ -147,7 +235,7 @@ span[data-slate-placeholder="true"]:not(.inline-block-content) { } .text-placeholder { - @apply absolute left-[5px] transform -translate-y-1/2 pointer-events-none select-none whitespace-nowrap; + @apply absolute left-0 transform -translate-y-1/2 pointer-events-none select-none whitespace-nowrap; &:after { @apply text-text-placeholder absolute top-0; content: (attr(data-placeholder)); @@ -160,11 +248,6 @@ span[data-slate-placeholder="true"]:not(.inline-block-content) { } } -.has-start-icon .text-placeholder { - &:after { - @apply left-[24px]; - } -} .block-align-center { .text-placeholder { @@ -174,13 +257,6 @@ span[data-slate-placeholder="true"]:not(.inline-block-content) { } } - .has-start-icon .text-placeholder { - @apply left-[calc(50%+13px)]; - &:after { - @apply left-0; - } - } - } @@ -188,9 +264,9 @@ span[data-slate-placeholder="true"]:not(.inline-block-content) { .text-placeholder { - @apply relative w-fit h-0 order-2; + @apply hidden; &:after { - @apply relative w-fit top-1/2 left-[-6px]; + @apply relative w-fit; } } @@ -198,20 +274,22 @@ span[data-slate-placeholder="true"]:not(.inline-block-content) { @apply order-1; } - .has-start-icon .text-placeholder { - &:after { - @apply left-[-6px]; - } - } } .formula-inline, .mention { &.selected { - @apply rounded bg-content-blue-100; + @apply rounded-[2px] bg-content-blue-100; + .mention-inline { + @apply bg-content-blue-100 select-all; + } } } +.formula-inline { + @apply whitespace-pre; +} + .numbered-icon { &:after { @@ -252,6 +330,9 @@ span[data-slate-placeholder="true"]:not(.inline-block-content) { height: inherit; overflow: hidden; @apply inline-flex gap-1 relative truncate max-w-full; + text-underline-position: under; + //text-underline-offset: 5px; + .mention-icon { @apply absolute top-1/2 transform -translate-y-1/2; @@ -301,19 +382,6 @@ span[data-slate-placeholder="true"]:not(.inline-block-content) { } -.cursor-before::before, -.cursor-after::after { - content: ''; - position: absolute; - top: 0; - bottom: 0; - width: 1px; - margin-top: 2px; - margin-bottom: 2px; - background-color: var(--text-title); - animation: blink 1s step-end infinite; -} - .cursor-before::before { left: -1px; } @@ -345,40 +413,72 @@ span[data-slate-placeholder="true"]:not(.inline-block-content) { } } -.toggle-heading { +.toggle-heading, .heading, .hover-controls { &.level-1 { + @apply py-[8px]; + > span > .toggle-icon { + @apply relative top-3; + } + + > .hover-controls-placeholder, > .text-element { - @apply text-[1.75rem] max-md:text-[24px] pt-[10px] max-md:pt-[1.5vw] pb-[4px] max-md:pb-[1vw] font-bold; + @apply text-[2rem] max-md:text-[24px] font-semibold; } } &.level-2 { + @apply py-[6px]; + > span > .toggle-icon { + @apply relative top-2; + } + + > .hover-controls-placeholder, > .text-element { - @apply text-[1.55rem] max-md:text-[22px] pt-[8px] max-md:pt-[1vw] pb-[2px] max-md:pb-[0.5vw] font-bold; + @apply text-[1.75rem] max-md:text-[22px] font-semibold; } } &.level-3 { + @apply py-[4px]; + > span > .toggle-icon { + @apply relative top-1.5; + } + + > .hover-controls-placeholder, > .text-element { - @apply text-[1.35rem] max-md:text-[20px] pt-[4px] font-bold; + @apply text-[1.5rem] max-md:text-[20px] font-semibold; } } &.level-4 { + @apply py-[4px]; + > span > .toggle-icon { + @apply relative top-1; + } + + > .hover-controls-placeholder, > .text-element { - @apply text-[1.25rem] max-md:text-[16px] pt-[4px] font-bold; + @apply text-[1.25rem] max-md:text-[16px] font-semibold; } } &.level-5 { + @apply py-[2px]; + > span > .toggle-icon { + @apply relative top-0.5; + } + + > .hover-controls-placeholder, > .text-element { - @apply text-[1.15rem] pt-[4px] font-bold; + @apply text-[1.125rem] font-semibold; } } &.level-6 { + @apply py-[2px]; + > .hover-controls-placeholder, > .text-element { - @apply text-[1.05rem] pt-[4px] font-bold; + @apply text-[1rem] font-semibold; } } -} \ No newline at end of file +} diff --git a/frontend/appflowy_web_app/src/components/editor/plugins/index.ts b/frontend/appflowy_web_app/src/components/editor/plugins/index.ts index bf11248bd4..a2bc8f9a20 100644 --- a/frontend/appflowy_web_app/src/components/editor/plugins/index.ts +++ b/frontend/appflowy_web_app/src/components/editor/plugins/index.ts @@ -4,7 +4,10 @@ import { withInsertText } from '@/components/editor/plugins/withInsertText'; import { withMarkdown } from '@/components/editor/plugins/withMarkdown'; import { withPasted } from '@/components/editor/plugins/withPasted'; import { ReactEditor } from 'slate-react'; +import { withCopy } from '@/components/editor/plugins/withCopy'; +import { withInsertData } from '@/components/editor/plugins/withInsertData'; +import { withElement } from '@/components/editor/plugins/withElement'; -export function withPlugins (editor: ReactEditor) { - return withPasted(withMarkdown(withInsertBreak(withDelete(withInsertText(editor))))); +export function withPlugins(editor: ReactEditor) { + return withInsertData(withPasted(withCopy(withMarkdown(withInsertBreak(withDelete(withInsertText(withElement(editor)))))))); } diff --git a/frontend/appflowy_web_app/src/components/editor/plugins/withCopy.ts b/frontend/appflowy_web_app/src/components/editor/plugins/withCopy.ts new file mode 100644 index 0000000000..d48573aa27 --- /dev/null +++ b/frontend/appflowy_web_app/src/components/editor/plugins/withCopy.ts @@ -0,0 +1,38 @@ +import { ReactEditor } from 'slate-react'; +import { Range } from 'slate'; +import { getBlockEntry } from '@/application/slate-yjs/utils/editor'; +import { YjsEditor } from '@/application/slate-yjs'; +import { isEmbedBlockTypes } from '@/application/slate-yjs/command/const'; +import { BlockType } from '@/application/types'; + +export const clipboardFormatKey = 'x-appflowy-fragment'; + +export const withCopy = (editor: ReactEditor) => { + const { setFragmentData } = editor; + + editor.setFragmentData = (data: Pick) => { + const { selection } = editor; + + if (!selection) { + return; + } + + if (Range.isCollapsed(selection)) { + const [node] = getBlockEntry(editor as YjsEditor); + + if (node && isEmbedBlockTypes(node.type as BlockType)) { + const fragment = editor.getFragment(); + const string = JSON.stringify(fragment); + const encoded = window.btoa(encodeURIComponent(string)); + + data.setData(`application/${clipboardFormatKey}`, encoded); + } + + return; + } + + setFragmentData(data); + }; + + return editor; +}; diff --git a/frontend/appflowy_web_app/src/components/editor/plugins/withDelete.ts b/frontend/appflowy_web_app/src/components/editor/plugins/withDelete.ts index 58c359590c..782b935c79 100644 --- a/frontend/appflowy_web_app/src/components/editor/plugins/withDelete.ts +++ b/frontend/appflowy_web_app/src/components/editor/plugins/withDelete.ts @@ -5,12 +5,14 @@ import { isAtBlockEnd, isEntireDocumentSelected, getBlockEntry, -} from '@/application/slate-yjs/utils/yjsOperations'; +} from '@/application/slate-yjs/utils/editor'; import { TextUnit, Range, EditorFragmentDeletionOptions } from 'slate'; import { ReactEditor } from 'slate-react'; import { TextDeleteOptions } from 'slate/dist/interfaces/transforms/text'; +import { isEmbedBlockTypes } from '@/application/slate-yjs/command/const'; +import { BlockType } from '@/application/types'; -export function withDelete (editor: ReactEditor) { +export function withDelete(editor: ReactEditor) { const { deleteForward, deleteBackward, delete: deleteText } = editor; editor.delete = (options?: TextDeleteOptions) => { @@ -18,7 +20,15 @@ export function withDelete (editor: ReactEditor) { if (!selection) return; + const [node] = getBlockEntry(editor as YjsEditor); + if (Range.isCollapsed(selection)) { + if (isEmbedBlockTypes(node.type as BlockType) && node.blockId) { + + CustomEditor.deleteBlock(editor as YjsEditor, node.blockId); + return; + } + deleteText(options); return; } @@ -46,6 +56,7 @@ export function withDelete (editor: ReactEditor) { const { selection } = editor; if (!selection) return; + if (options?.direction === 'backward') { CustomEditor.deleteBlockBackward(editor as YjsEditor, selection); } else { @@ -72,6 +83,19 @@ export function withDelete (editor: ReactEditor) { return; } + const after = editor.after(editor.end(selection), { unit: 'block' }); + + if (!after) { + return; + } + + const nextBlock = getBlockEntry(editor as YjsEditor, after)[0]; + + if (isEmbedBlockTypes(nextBlock.type as BlockType) && nextBlock.blockId) { + CustomEditor.deleteBlock(editor as YjsEditor, nextBlock.blockId); + return; + } + CustomEditor.deleteBlockForward(editor as YjsEditor, selection); }; diff --git a/frontend/appflowy_web_app/src/components/editor/plugins/withElement.ts b/frontend/appflowy_web_app/src/components/editor/plugins/withElement.ts new file mode 100644 index 0000000000..c89ab903b0 --- /dev/null +++ b/frontend/appflowy_web_app/src/components/editor/plugins/withElement.ts @@ -0,0 +1,42 @@ +import { ReactEditor } from 'slate-react'; +import { isEmbedBlockTypes } from '@/application/slate-yjs/command/const'; +import { BlockType } from '@/application/types'; +import { Element, NodeEntry } from 'slate'; + +export const withElement = (editor: ReactEditor) => { + const { + isElementReadOnly, + isEmbed, + } = editor; + + editor.isEmbed = (element) => { + if (isEmbedBlockTypes(element.type as BlockType)) { + return true; + } + + return isEmbed(element); + }; + + editor.isElementReadOnly = (element) => { + + try { + const path = ReactEditor.findPath(editor, element); + const parent = editor.parent(path, { + depth: 2, + }) as NodeEntry; + + const readOnlyTypes = [BlockType.SimpleTableBlock, BlockType.TableBlock]; + + if (readOnlyTypes.includes(parent[0].type as BlockType)) { + return true; + } + + } catch (e) { + // + } + + return isElementReadOnly(element); + }; + + return editor; +}; \ No newline at end of file diff --git a/frontend/appflowy_web_app/src/components/editor/plugins/withInsertBreak.ts b/frontend/appflowy_web_app/src/components/editor/plugins/withInsertBreak.ts index 7731e5d41a..1cd628c562 100644 --- a/frontend/appflowy_web_app/src/components/editor/plugins/withInsertBreak.ts +++ b/frontend/appflowy_web_app/src/components/editor/plugins/withInsertBreak.ts @@ -1,9 +1,28 @@ import { YjsEditor } from '@/application/slate-yjs'; import { CustomEditor } from '@/application/slate-yjs/command'; +import { isEmbedBlockTypes } from '@/application/slate-yjs/command/const'; +import { getBlockEntry } from '@/application/slate-yjs/utils/editor'; +import { BlockType } from '@/application/types'; +import { Range } from 'slate'; import { ReactEditor } from 'slate-react'; -export function withInsertBreak (editor: ReactEditor) { - const { insertBreak } = editor; +export function withInsertBreak(editor: ReactEditor) { + const { insertBreak, insertSoftBreak } = editor; + + editor.insertSoftBreak = () => { + const { selection } = editor; + + if (!selection) return; + + const [node] = getBlockEntry(editor as YjsEditor); + + if (Range.isCollapsed(selection) && isEmbedBlockTypes(node.type as BlockType)) { + CustomEditor.addBelowBlock(editor as YjsEditor, node.blockId as string, BlockType.Paragraph, {}); + return; + } + + insertSoftBreak(); + }; editor.insertBreak = () => { if ((editor as YjsEditor).readOnly) { @@ -11,6 +30,19 @@ export function withInsertBreak (editor: ReactEditor) { return; } + const { selection } = editor; + + if (!selection) return; + + if (Range.isCollapsed(selection)) { + const [node] = getBlockEntry(editor as YjsEditor); + + if (isEmbedBlockTypes(node.type as BlockType)) { + CustomEditor.addBelowBlock(editor as YjsEditor, node.blockId as string, BlockType.Paragraph, {}); + return; + } + } + CustomEditor.insertBreak(editor as YjsEditor); }; diff --git a/frontend/appflowy_web_app/src/components/editor/plugins/withInsertData.ts b/frontend/appflowy_web_app/src/components/editor/plugins/withInsertData.ts new file mode 100644 index 0000000000..78dcee4659 --- /dev/null +++ b/frontend/appflowy_web_app/src/components/editor/plugins/withInsertData.ts @@ -0,0 +1,106 @@ +import { CustomEditor } from '@/application/slate-yjs/command'; +import { YjsEditor } from '@/application/slate-yjs'; +import { findSlateEntryByBlockId, getBlockEntry } from '@/application/slate-yjs/utils/editor'; +import { ReactEditor } from 'slate-react'; +import { BlockType, FieldURLType, FileBlockData, ImageBlockData, ImageType } from '@/application/types'; +import { FileHandler } from '@/utils/file'; +import { convertSlateFragmentTo } from '@/components/editor/utils/fragment'; +import { Node } from 'slate'; + +export const withInsertData = (editor: ReactEditor) => { + const { insertData } = editor; + + const e = editor as YjsEditor; + + editor.insertData = (data: DataTransfer) => { + const fragment = data.getData('application/x-slate-fragment'); + + if (fragment) { + const decoded = decodeURIComponent(window.atob(fragment)); + const parsed = JSON.parse(decoded) as Node[]; + const newFragment = convertSlateFragmentTo(parsed); + + return e.insertFragment(newFragment); + } + + // Do something with the data... + const fileArray = Array.from(data.files); + const { selection } = editor; + const entry = getBlockEntry(e); + const [node] = entry; + const blockId = node.blockId; + + insertData(data); + + if (blockId && fileArray.length > 0 && selection) { + void (async () => { + const text = CustomEditor.getBlockTextContent(node); + let newBlockId: string = blockId; + + for (const file of fileArray) { + const url = await e.uploadFile?.(file); + let fileId = ''; + + if (!url) { + const fileHandler = new FileHandler(); + const res = await fileHandler.handleFileUpload(file); + + fileId = res.id; + } + + const isImage = file.type.startsWith('image/'); + + if (isImage) { + const data = { + url: url, + image_type: ImageType.External, + } as ImageBlockData; + + if (fileId) { + data.retry_local_url = fileId; + } + + // Handle images... + newBlockId = CustomEditor.addBelowBlock(e, newBlockId, BlockType.ImageBlock, data) || newBlockId; + } else { + const data = { + url: url, + name: file.name, + uploaded_at: Date.now(), + url_type: FieldURLType.Upload, + } as FileBlockData; + + if (fileId) { + data.retry_local_url = fileId; + } + + // Handle files... + newBlockId = CustomEditor.addBelowBlock(e, newBlockId, BlockType.FileBlock, data) || newBlockId; + } + + } + + if (!text) { + CustomEditor.deleteBlock(e, blockId); + } + + const firstIsImage = fileArray[0].type.startsWith('image/'); + + if (newBlockId && firstIsImage) { + const id = CustomEditor.addBelowBlock(e, newBlockId, BlockType.Paragraph, {}); + + if (!id) return; + + const [, path] = findSlateEntryByBlockId(e, id); + + editor.select(editor.start(path)); + + } + + })(); + + } + }; + + return editor; +}; \ No newline at end of file diff --git a/frontend/appflowy_web_app/src/components/editor/plugins/withInsertText.ts b/frontend/appflowy_web_app/src/components/editor/plugins/withInsertText.ts index 44efc2ad2b..1a4dfdbb10 100644 --- a/frontend/appflowy_web_app/src/components/editor/plugins/withInsertText.ts +++ b/frontend/appflowy_web_app/src/components/editor/plugins/withInsertText.ts @@ -1,6 +1,10 @@ -import { BaseRange, NodeEntry, Range, Text, Transforms } from 'slate'; +import { BaseRange, NodeEntry, Element, Point, Range, Text, Transforms } from 'slate'; import { ReactEditor } from 'slate-react'; import { TextInsertTextOptions } from 'slate/dist/interfaces/transforms/text'; +import { getBlockEntry } from '@/application/slate-yjs/utils/editor'; +import { YjsEditor } from '@/application/slate-yjs'; +import { isEmbedBlockTypes } from '@/application/slate-yjs/command/const'; +import { BlockType } from '@/application/types'; export const withInsertText = (editor: ReactEditor) => { const { insertText } = editor; @@ -14,21 +18,41 @@ export const withInsertText = (editor: ReactEditor) => { } const point = newAt.anchor; + const [blockNode] = getBlockEntry(editor as YjsEditor, point) as NodeEntry; + + if (blockNode && isEmbedBlockTypes(blockNode.type as BlockType)) { + return; + } + const [textEntry] = editor.nodes({ at: point, match: n => { return Text.isText(n); }, }); - const [textNode] = textEntry as NodeEntry; + + if (!textEntry) { + return; + } + + const [textNode, textPath] = textEntry as NodeEntry; // If the text node is a formula or mention, split the node and insert the text - if (textNode.formula || textNode.mention || textNode.href) { + if (textNode.formula || textNode.mention) { console.log('Inserting text into formula or mention', newAt); Transforms.insertNodes(editor, { text }, { at: point, select: true, voids: false }); return; } + const [start, end] = editor.edges(textPath); + + const inMiddle = Point.isAfter(point, start) && Point.isBefore(point, end); + + if (!inMiddle && (textNode.code || textNode.href)) { + Transforms.insertNodes(editor, { text }, { at: point, select: true, voids: false }); + return; + } + insertText(text, options); }; diff --git a/frontend/appflowy_web_app/src/components/editor/plugins/withPasted.ts b/frontend/appflowy_web_app/src/components/editor/plugins/withPasted.ts index 2e8fee6898..d961a146ec 100644 --- a/frontend/appflowy_web_app/src/components/editor/plugins/withPasted.ts +++ b/frontend/appflowy_web_app/src/components/editor/plugins/withPasted.ts @@ -1,16 +1,19 @@ import { YjsEditor } from '@/application/slate-yjs'; import { slateContentInsertToYData } from '@/application/slate-yjs/utils/convert'; import { - assertDocExists, - getBlock, + beforePasted, + findSlateEntryByBlockId, getBlockEntry, - getChildrenArray, getSharedRoot, -} from '@/application/slate-yjs/utils/yjsOperations'; -import { YjsEditorKey } from '@/application/types'; +} from '@/application/slate-yjs/utils/editor'; +import { BlockType, LinkPreviewBlockData, MentionType, YjsEditorKey } from '@/application/types'; import { deserializeHTML } from '@/components/editor/utils/fragment'; -import { BasePoint, Range, Transforms, Node } from 'slate'; +import { BasePoint, Node, Transforms, Text, Element } from 'slate'; import { ReactEditor } from 'slate-react'; +import isURL from 'validator/lib/isURL'; +import { assertDocExists, deleteBlock, getBlock, getChildrenArray } from '@/application/slate-yjs/utils/yjs'; +import { CustomEditor } from '@/application/slate-yjs/command'; +import { processUrl } from '@/utils/url'; export const withPasted = (editor: ReactEditor) => { @@ -23,17 +26,62 @@ export const withPasted = (editor: ReactEditor) => { const lines = text.split(/\r\n|\r|\n/); - if (lines.filter(Boolean).length > 1) { - return insertHtmlData(editor, data); + const html = data.getData('text/html'); + + const lineLength = lines.filter(Boolean).length; + const point = editor.selection?.anchor as BasePoint; + const [node] = getBlockEntry(editor as YjsEditor, point); + + if (lineLength === 1) { + const isUrl = !!processUrl(text); + + if (isUrl) { + const isAppFlowyLinkUrl = isURL(text, { + host_whitelist: ['localhost', 'appflowy.com', 'test.appflowy.com', 'beta.appflowy.com'], + }); + + console.log('isAppFlowyLinkUrl', isAppFlowyLinkUrl); + if (isAppFlowyLinkUrl) { + const url = new URL(text); + const blockId = url.searchParams.get('blockId'); + + if (blockId) { + const pageId = url.pathname.split('/').pop(); + const point = editor.selection?.anchor as BasePoint; + + Transforms.insertNodes(editor, { + text: '@', mention: { + type: MentionType.PageRef, + page_id: pageId, + block_id: blockId, + }, + }, { at: point, select: true, voids: false }); + + return true; + } + } + + const currentBlockId = node.blockId as string; + + CustomEditor.addBelowBlock(editor as YjsEditor, currentBlockId, BlockType.LinkPreview, { url: text } as LinkPreviewBlockData); + + return true; + } } - console.log('insertTextData', text); + if (lineLength > 1 && html && node.type !== BlockType.CodeBlock) { + return insertHtmlData(editor, data); + } for (const line of lines) { const point = editor.selection?.anchor as BasePoint; if (line) { - Transforms.insertNodes(editor, { text: line }, { at: point, select: true, voids: false }); + Transforms.insertNodes(editor, { text: `${line}${lineLength > 1 ? `\n` : ''}` }, { + at: point, + select: true, + voids: false, + }); } } @@ -50,25 +98,7 @@ export const withPasted = (editor: ReactEditor) => { return editor; }; -function beforePasted (editor: ReactEditor) { - const { selection } = editor; - - if (!selection) { - return false; - } - - if (Range.isExpanded(selection)) { - Transforms.collapse(editor, { edge: 'start' }); - - editor.delete({ - at: selection, - }); - } - - return true; -} - -function insertHtmlData (editor: ReactEditor, data: DataTransfer) { +export function insertHtmlData(editor: ReactEditor, data: DataTransfer) { const html = data.getData('text/html'); if (html) { @@ -83,7 +113,7 @@ function insertHtmlData (editor: ReactEditor, data: DataTransfer) { return false; } -function insertFragment (editor: ReactEditor, fragment: Node[], options = {}) { +function insertFragment(editor: ReactEditor, fragment: Node[], options = {}) { console.log('insertFragment', fragment, options); if (!beforePasted(editor)) return; @@ -92,20 +122,59 @@ function insertFragment (editor: ReactEditor, fragment: Node[], options = {}) { const [node] = getBlockEntry(editor as YjsEditor, point); const blockId = node.blockId as string; const sharedRoot = getSharedRoot(editor as YjsEditor); + const isEmptyNode = CustomEditor.getBlockTextContent(node) === ''; const block = getBlock(blockId, sharedRoot); const parent = getBlock(block.get(YjsEditorKey.block_parent), sharedRoot); const parentChildren = getChildrenArray(parent.get(YjsEditorKey.block_children), sharedRoot); const index = parentChildren.toArray().findIndex((id) => id === block.get(YjsEditorKey.block_id)); const doc = assertDocExists(sharedRoot); + if (fragment.length === 1) { + const firstNode = fragment[0] as Element; + + const findTextNodes = (node: Node): Node[] => { + if (Text.isText(node)) { + return []; + } + + if (Element.isElement(node) && node.textId) { + return [node]; + } + + return node.children.flatMap(findTextNodes); + }; + + const textNodes = findTextNodes(firstNode); + + if (textNodes.length === 1) { + const textNode = textNodes[0] as Element; + const texts = textNode.children.filter((node) => Text.isText(node)); + + Transforms.insertNodes(editor, texts, { at: point, select: true, voids: false }); + return; + } + + } + + let lastBlockId = blockId; + doc.transact(() => { - slateContentInsertToYData(block.get(YjsEditorKey.block_parent), index + 1, fragment, doc); + const newBlockIds = slateContentInsertToYData(block.get(YjsEditorKey.block_parent), index + 1, fragment, doc); + + lastBlockId = newBlockIds[newBlockIds.length - 1]; + if (isEmptyNode) { + deleteBlock(sharedRoot, blockId); + } }); - Transforms.move(editor, { - distance: 1, - unit: 'line', - }); + setTimeout(() => { + const [, path] = findSlateEntryByBlockId(editor as YjsEditor, lastBlockId); + + const point = editor.end(path); + + editor.select(point); + + }, 50); return; } \ No newline at end of file diff --git a/frontend/appflowy_web_app/src/components/editor/shortcut.hooks.ts b/frontend/appflowy_web_app/src/components/editor/shortcut.hooks.ts index 5a9ce55bb1..9e43d7fe34 100644 --- a/frontend/appflowy_web_app/src/components/editor/shortcut.hooks.ts +++ b/frontend/appflowy_web_app/src/components/editor/shortcut.hooks.ts @@ -2,16 +2,42 @@ import { YjsEditor } from '@/application/slate-yjs'; import { CustomEditor } from '@/application/slate-yjs/command'; import { SOFT_BREAK_TYPES } from '@/application/slate-yjs/command/const'; import { EditorMarkFormat } from '@/application/slate-yjs/types'; -import { getBlockEntry } from '@/application/slate-yjs/utils/yjsOperations'; -import { BlockType } from '@/application/types'; +import { getBlockEntry } from '@/application/slate-yjs/utils/editor'; +import { AlignType, BlockType } from '@/application/types'; +import { useEditorContext } from '@/components/editor/EditorContext'; import { createHotkey, HOT_KEY_NAME } from '@/utils/hotkeys'; +import { openUrl } from '@/utils/url'; import { KeyboardEvent, useCallback } from 'react'; -import { Editor, Text, Range, Transforms, BasePoint } from 'slate'; +import { Editor, Text, Range, Transforms, BasePoint, Path } from 'slate'; import { ReactEditor, useReadOnly } from 'slate-react'; +import smoothScrollIntoViewIfNeeded from 'smooth-scroll-into-view-if-needed'; -export function useShortcuts (editor: ReactEditor) { +export function useShortcuts(editor: ReactEditor) { const yjsEditor = editor as YjsEditor; + const { viewId } = useEditorContext(); const readOnly = useReadOnly(); + + const focusedFocusableElement = useCallback((toStart?: boolean) => { + if (readOnly) return; + const title = document.getElementById(`editor-title-${viewId}`); + + if (!title) return; + + const selection = window.getSelection(); + const range = document.createRange(); + + const textNode = title.childNodes[0] as Node; + + if (textNode) { + range.setStart(textNode, toStart ? 0 : (textNode?.textContent?.length || 0)); + } else { + range.setStart(title, 0); + } + + selection?.removeAllRanges(); + selection?.addRange(range); + }, [readOnly, viewId]); + const onKeyDown = useCallback((event: KeyboardEvent) => { const e = event.nativeEvent; const { selection } = editor; @@ -31,31 +57,140 @@ export function useShortcuts (editor: ReactEditor) { } if (selection && Range.isCollapsed(selection)) { - if ( - createHotkey(HOT_KEY_NAME.UP)(e) - ) { - const before = Editor.before(editor, selection, { unit: 'offset' }); - const beforeText = findInlineTextNode(editor, before); + switch (true) { + case createHotkey(HOT_KEY_NAME.UP)(e): { + const path = editor.start(selection).path; - if (before && beforeText) { - e.preventDefault(); - Transforms.move(editor, { unit: 'line', reverse: true, distance: 2 }); - return; + if (Path.isAncestor([0, 0], path)) { + e.preventDefault(); + focusedFocusableElement(false); + break; + } + + const before = Editor.before(editor, selection, { unit: 'offset' }); + const beforeText = findInlineTextNode(editor, before); + + if (before && beforeText) { + e.preventDefault(); + Transforms.move(editor, { unit: 'line', reverse: true, distance: 2 }); + return; + } + + break; } + + case createHotkey(HOT_KEY_NAME.BACKSPACE)(e): { + const [node] = getBlockEntry(yjsEditor); + const type = node.type as BlockType; + + if (type !== BlockType.Paragraph) { + break; + } + + const path = editor.start(selection).path; + + const before = Editor.before(editor, selection, { unit: 'offset' }); + + if (!before && Path.isAncestor([0, 0], path)) { + focusedFocusableElement(true); + } + + break; + } + + case createHotkey(HOT_KEY_NAME.LEFT)(e): { + const path = editor.start(selection).path; + const before = Editor.before(editor, selection, { unit: 'offset' }); + + if (!before && Path.isAncestor([0, 0], path)) { + focusedFocusableElement(false); + } + + break; + } + + case createHotkey(HOT_KEY_NAME.DOWN)(e): { + const after = Editor.after(editor, selection, { unit: 'offset' }); + const afterText = findInlineTextNode(editor, after); + + if (afterText) { + e.preventDefault(); + Transforms.move(editor, { unit: 'line', distance: 2 }); + return; + } + + break; + } + + case createHotkey(HOT_KEY_NAME.OPEN_LINK)(e): { + event.preventDefault(); + const marks = CustomEditor.getAllMarks(editor); + + const link = marks.find((mark) => !!mark[EditorMarkFormat.Href])?.[EditorMarkFormat.Href]; + + if (link) { + void openUrl(link, '_blank'); + return; + } + + break; + } + + case createHotkey(HOT_KEY_NAME.DELETE_LEFT_SENTENCE)(e): { + event.preventDefault(); + const focus = editor.start(selection); + const anchor = Editor.before(editor, focus, { unit: 'line' }); + + if (anchor) { + editor.delete({ + at: { + anchor, + focus, + }, + }); + } + + break; + } + + case createHotkey(HOT_KEY_NAME.DELETE_LEFT_WORD)(e): { + event.preventDefault(); + const focus = editor.start(selection); + const anchor = Editor.before(editor, focus, { unit: 'word' }); + + if (anchor) { + editor.delete({ + at: { + anchor, + focus, + }, + }); + } + + break; + } + + case createHotkey(HOT_KEY_NAME.DELETE_RIGHT_WORD)(e): { + event.preventDefault(); + const focus = editor.start(selection); + const anchor = Editor.after(editor, focus, { unit: 'word' }); + + if (anchor) { + editor.delete({ + at: { + anchor, + focus, + }, + }); + } + + break; + } + + default: + break; } - if ( - createHotkey(HOT_KEY_NAME.DOWN)(e) - ) { - const after = Editor.after(editor, selection, { unit: 'offset' }); - const afterText = findInlineTextNode(editor, after); - - if (after && afterText) { - e.preventDefault(); - Transforms.move(editor, { unit: 'line', distance: 2 }); - return; - } - } } // Do not process shortcuts if editor is read-only or no selection @@ -102,7 +237,7 @@ export function useShortcuts (editor: ReactEditor) { editor.deleteBackward('character'); break; } - + CustomEditor.tabEvent(yjsEditor, e); break; /** @@ -143,71 +278,213 @@ export function useShortcuts (editor: ReactEditor) { if (node[0].type === BlockType.ToggleListBlock) { CustomEditor.toggleToggleList(yjsEditor, node[0].blockId as string); } else if (node[0].type === BlockType.TodoListBlock) { - CustomEditor.toggleTodoList(yjsEditor, node[0].blockId as string); + CustomEditor.toggleTodoList(yjsEditor, node[0].blockId as string, false); } break; + /** - * Bold: Mod + B + * Open link: Opt + SHIFT + Enter */ - case createHotkey(HOT_KEY_NAME.BOLD)(e): + case createHotkey(HOT_KEY_NAME.OPEN_LINKS)(e): { event.preventDefault(); - CustomEditor.toggleMark(editor, { - key: EditorMarkFormat.Bold, - value: true, + const marks = CustomEditor.getAllMarks(editor); + const links = marks.map((mark) => mark[EditorMarkFormat.Href]).filter(Boolean); + + if (links.length === 0) break; + links.forEach((link) => { + if (link) void openUrl(link, '_blank'); + }); + break; + } + + /** + * Extend document backward: Mod + Shift + Up + */ + case createHotkey(HOT_KEY_NAME.EXTEND_DOCUMENT_BACKWARD)(e): { + + event.preventDefault(); + const { selection } = editor; + + if (!selection) return; + const focus = editor.start(selection); + const anchor = editor.start([0, 0]); + + editor.select({ + anchor, + focus, + }); + break; + } + + /** + * Extend document forward: Mod + Shift + Down + */ + case createHotkey(HOT_KEY_NAME.EXTEND_DOCUMENT_FORWARD)(e): { + + event.preventDefault(); + const { selection } = editor; + + if (!selection) return; + const anchor = editor.end(selection); + const focus = editor.end([editor.children.length - 1, 0]); + + editor.select({ + anchor, + focus, + }); + break; + } + + /** + * Extend line backward: Opt + Shift + Left + */ + case createHotkey(HOT_KEY_NAME.EXTEND_LINE_BACKWARD)(e): + event.preventDefault(); + Transforms.move(editor, { + unit: 'word', + reverse: true, }); break; /** - * Italic: Mod + I + * Extend line forward: Opt + Shift + Right */ - case createHotkey(HOT_KEY_NAME.ITALIC)(e): + case createHotkey(HOT_KEY_NAME.EXTEND_LINE_FORWARD)(e): event.preventDefault(); - CustomEditor.toggleMark(editor, { - key: EditorMarkFormat.Italic, - value: true, + Transforms.move(editor, { unit: 'word' }); + break; + /** + * Paste Text: Mod + Shift + V + */ + case createHotkey(HOT_KEY_NAME.PASTE_PLAIN_TEXT)(e): + event.preventDefault(); + void navigator.clipboard.readText().then((text) => { + CustomEditor.pastedText(yjsEditor, text); }); break; /** - * Underline: Mod + U + * Scroll to top: Home */ - case createHotkey(HOT_KEY_NAME.UNDERLINE)(e): + case createHotkey(HOT_KEY_NAME.SCROLL_TO_TOP)(e): { event.preventDefault(); - CustomEditor.toggleMark(editor, { - key: EditorMarkFormat.Underline, - value: true, + const dom = ReactEditor.toDOMNode(editor, editor); + + void smoothScrollIntoViewIfNeeded(dom, { + behavior: 'smooth', + block: 'start', }); break; + } + /** - * Strikethrough: Mod + Shift + S / Mod + Shift + X + * Scroll to bottom: End */ - case createHotkey(HOT_KEY_NAME.STRIKETHROUGH)(e): + case createHotkey(HOT_KEY_NAME.SCROLL_TO_BOTTOM)(e): { event.preventDefault(); - CustomEditor.toggleMark(editor, { - key: EditorMarkFormat.StrikeThrough, - value: true, + const dom = ReactEditor.toDOMNode(editor, editor); + + void smoothScrollIntoViewIfNeeded(dom, { + behavior: 'smooth', + block: 'end', }); + break; + } + /** - * Code: Mod + E + * Align left: Control + Shift + L */ - case createHotkey(HOT_KEY_NAME.CODE)(e): + case createHotkey(HOT_KEY_NAME.ALIGN_LEFT)(e): { + event.preventDefault(); - CustomEditor.toggleMark(editor, { - key: EditorMarkFormat.Code, - value: true, + + const blockId = node[0].blockId as string; + + CustomEditor.setBlockData(yjsEditor, blockId, { + align: AlignType.Left, }); break; + } + + /** + * Align center: Control + Shift + E + */ + case createHotkey(HOT_KEY_NAME.ALIGN_CENTER)(e): { + + event.preventDefault(); + + const blockId = node[0].blockId as string; + + CustomEditor.setBlockData(yjsEditor, blockId, { + align: AlignType.Center, + }); + break; + } + + /** + * Align right: Control + Shift + R + */ + case createHotkey(HOT_KEY_NAME.ALIGN_RIGHT)(e): { + + event.preventDefault(); + + const blockId = node[0].blockId as string; + + CustomEditor.setBlockData(yjsEditor, blockId, { + align: AlignType.Right, + }); + break; + } + + case createHotkey(HOT_KEY_NAME.MOVE_CURSOR_TO_BOTTOM)(e): { + event.preventDefault(); + const point = Editor.end(editor, [editor.children.length - 1, 0]); + + if (!point) return; + const dom = ReactEditor.toDOMNode(editor, editor); + + void smoothScrollIntoViewIfNeeded(dom, { + behavior: 'smooth', + block: 'end', + }); + editor.select({ + anchor: point, + focus: point, + }); + + break; + } + + case createHotkey(HOT_KEY_NAME.MOVE_CURSOR_TO_TOP)(e): { + event.preventDefault(); + const point = Editor.start(editor, [0, 0]); + + if (!point) return; + const dom = ReactEditor.toDOMNode(editor, editor); + + void smoothScrollIntoViewIfNeeded(dom, { + behavior: 'smooth', + block: 'start', + }); + editor.select({ + anchor: point, + focus: point, + }); + + break; + } + default: break; } - }, [editor, yjsEditor, readOnly]); + }, [focusedFocusableElement, editor, yjsEditor, readOnly]); return { onKeyDown, }; } -function findInlineTextNode (editor: Editor, point?: BasePoint) { +function findInlineTextNode(editor: Editor, point?: BasePoint) { const [node] = editor.nodes({ at: point, match: (n) => { diff --git a/frontend/appflowy_web_app/src/components/editor/utils/__tests__/fragment.test.ts b/frontend/appflowy_web_app/src/components/editor/utils/__tests__/fragment.test.ts index 9086788825..02ce01b0ac 100644 --- a/frontend/appflowy_web_app/src/components/editor/utils/__tests__/fragment.test.ts +++ b/frontend/appflowy_web_app/src/components/editor/utils/__tests__/fragment.test.ts @@ -20,7 +20,7 @@ describe('deserializeHTML', () => { expect(result).toBeDefined(); expect(Array.isArray(result)).toBe(true); - expect(result.length).toBe(6); + expect(result.length).toBe(7); // Check paragraph let blockId = (result[0] as Element).blockId as string; @@ -52,9 +52,7 @@ describe('deserializeHTML', () => { blockId, type: BlockType.ImageBlock, children: [{ - type: 'text', - textId: blockId, - children: [{ text: '' }], + text: '', }], data: { url: 'https://example.com/image.jpg', image_type: ImageType.External }, })); diff --git a/frontend/appflowy_web_app/src/components/editor/utils/fragment.ts b/frontend/appflowy_web_app/src/components/editor/utils/fragment.ts index 5b831ca9d7..e71e268a59 100644 --- a/frontend/appflowy_web_app/src/components/editor/utils/fragment.ts +++ b/frontend/appflowy_web_app/src/components/editor/utils/fragment.ts @@ -1,26 +1,28 @@ +import { TEXT_BLOCK_TYPES } from '@/application/slate-yjs/command/const'; import { yDocToSlateContent } from '@/application/slate-yjs/utils/convert'; import { - createBlock, - createEmptyDocument, + AlignType, + BlockData, + BlockType, HeadingBlockData, + ImageBlockData, + ImageType, NumberedListBlockData, TodoListBlockData, + YBlock, + YjsEditorKey, + YSharedRoot, +} from '@/application/types'; +import { filter } from 'lodash-es'; +import { + createBlock, createEmptyDocument, generateBlockId, getBlock, getChildrenArray, getPageId, getText, updateBlockParent, -} from '@/application/slate-yjs/utils/yjsOperations'; -import { - AlignType, - BlockData, - BlockType, - ImageBlockData, - ImageType, - YBlock, - YjsEditorKey, - YSharedRoot, -} from '@/application/types'; -import { DeltaOperation } from 'quill'; +} from '@/application/slate-yjs/utils/yjs'; +import { Op } from 'quill-delta'; +import { Text as SlateText, Element as SlateElement, Node as SlateNode } from 'slate'; -export function deserialize (body: HTMLElement, sharedRoot: YSharedRoot) { +export function deserialize(body: HTMLElement, sharedRoot: YSharedRoot) { const pageId = getPageId(sharedRoot); const rootBlock = getBlock(pageId, sharedRoot); @@ -31,7 +33,7 @@ export function deserialize (body: HTMLElement, sharedRoot: YSharedRoot) { const BLOCK_TAGS = ['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'ul', 'ol', 'li', 'blockquote', 'pre', 'img', 'input']; -function deserializeNode (node: Node, parentBlock: YBlock, sharedRoot: YSharedRoot) { +function deserializeNode(node: Node, parentBlock: YBlock, sharedRoot: YSharedRoot) { let currentBlock = parentBlock; if (node.nodeType === Node.ELEMENT_NODE) { @@ -48,7 +50,9 @@ function deserializeNode (node: Node, parentBlock: YBlock, sharedRoot: YSharedRo } if (BLOCK_TAGS.includes(tagName)) { - const blockType = mapTagToBlockType(tagName, element); + let blockType = mapTagToBlockType(tagName, element); + + const textContent = element.textContent || ''; const blockData = mapToBlockData(element); if (blockType === BlockType.NumberedListBlock || blockType === BlockType.BulletedListBlock) { @@ -70,6 +74,36 @@ function deserializeNode (node: Node, parentBlock: YBlock, sharedRoot: YSharedRo return; } + if (blockType === BlockType.Paragraph) { + if (textContent === '---') { + blockType = BlockType.DividerBlock; + element.textContent = ''; + } else if (/^(-)?\[(x| )?\]\s/.test(textContent)) { + blockType = BlockType.TodoListBlock; + const match = textContent.match(/^(-)?\[(x| )?\]\s/); + + (blockData as TodoListBlockData).checked = match?.[2] === 'x'; + element.textContent = textContent.replace(/^(-)?\[(x| )?\]\s/, ''); + } else if (/^\d+\.\s/.test(textContent)) { + blockType = BlockType.NumberedListBlock; + (blockData as NumberedListBlockData).number = parseInt(textContent.split('.')[0]); + element.textContent = textContent.replace(/^\d+\.\s/, ''); + } else if (/^- /.test(textContent)) { + blockType = BlockType.BulletedListBlock; + element.textContent = textContent.replace(/^- /, ''); + } else if (/^> /.test(textContent)) { + blockType = BlockType.QuoteBlock; + element.textContent = textContent.replace(/^> /, ''); + } else if (/^```/.test(textContent)) { + blockType = BlockType.CodeBlock; + element.textContent = textContent.replace(/^```/, ''); + } else if (/^#{1,6}\s/.test(textContent)) { + blockType = BlockType.HeadingBlock; + element.textContent = textContent.replace(/^#{1,6}\s/, ''); + (blockData as HeadingBlockData).level = textContent.split(' ')[0].length; + } + } + currentBlock = createBlock(sharedRoot, { ty: blockType, data: blockData }); updateBlockParent(sharedRoot, currentBlock, parentBlock, getChildrenArray(parentBlock.get(YjsEditorKey.block_children), sharedRoot)?.length || 0); if (tagName === 'pre') { @@ -86,6 +120,25 @@ function deserializeNode (node: Node, parentBlock: YBlock, sharedRoot: YSharedRo } + if (tagName === 'span') { + const children = getChildrenArray(currentBlock.get(YjsEditorKey.block_children), sharedRoot); + const lastChildId = children?.toArray()[children?.length - 1]; + const lastChild = getBlock(lastChildId, sharedRoot); + const attributes = getInlineAttributes(element); + + if (lastChild && (filter(TEXT_BLOCK_TYPES, n => n !== BlockType.CodeBlock).includes(lastChild.get(YjsEditorKey.block_type)))) { + applyTextToDelta(lastChild, sharedRoot, element.textContent || '', attributes); + return; + } else { + const block = createBlock(sharedRoot, { ty: BlockType.Paragraph, data: {} }); + + applyTextToDelta(block, sharedRoot, element.textContent || '', attributes); + + updateBlockParent(sharedRoot, block, currentBlock, children?.length); + return; + } + } + Array.from(node.childNodes).forEach(childNode => { deserializeNode(childNode, currentBlock, sharedRoot); }); @@ -94,14 +147,119 @@ function deserializeNode (node: Node, parentBlock: YBlock, sharedRoot: YSharedRo if (textContent.trim()) { console.log('===textContent', node, textContent); - const attributes = getInlineAttributes(node.parentElement as HTMLElement); + const { ops } = textContentToDelta(textContent || ''); + + if (TEXT_BLOCK_TYPES.includes(currentBlock.get(YjsEditorKey.block_type))) { + const attributes = getInlineAttributes(node.parentElement as HTMLElement); + + ops.forEach(op => { + applyTextToDelta(currentBlock, sharedRoot, op.insert as string, { + ...op.attributes, + ...attributes, + }); + }); + // applyTextToDelta(currentBlock, sharedRoot, textContent, attributes); + } else { + const block = createBlock(sharedRoot, { ty: BlockType.Paragraph, data: {} }); + + applyTextToDelta(block, sharedRoot, textContent); + const index = getChildrenArray(currentBlock.get(YjsEditorKey.block_children), sharedRoot)?.length || 0; + + updateBlockParent(sharedRoot, block, currentBlock, index); + } - applyTextToDelta(currentBlock, sharedRoot, textContent, attributes); } } } -function isImageUrl (url: string): boolean { +function textContentToDelta(text: string) { + const ops: Op[] = []; + let currentIndex = 0; + + const patterns = [ + { regex: /\*\*(.*?)\*\*/g, format: 'bold' }, // **bold** + { regex: /\*(.*?)\*/g, format: 'italic' }, // *italic* + { regex: /__(.*?)__/g, format: 'underline' }, // __underline__ + { regex: /~~(.*?)~~/g, format: 'strike' }, // ~~strike~~ + ]; + + type Mark = { + start: number; + end: number; + text: string; + format: string; + length: number; + } + + const findMarks = (): Mark[] => { + const marks: Mark[] = []; + + patterns.forEach(({ regex, format }) => { + let match; + + while ((match = regex.exec(text)) !== null) { + marks.push({ + start: match.index, + end: match.index + match[0].length, + text: match[1], + format, + length: match[0].length, + }); + } + }); + + return marks.sort((a, b) => a.start - b.start); + }; + + const marks = findMarks(); + + const getFormatsAt = (index: number): Record => { + const formats: Record = {}; + + marks.forEach(mark => { + if (index >= mark.start && index < mark.end) { + formats[mark.format] = true; + } + }); + return formats; + }; + + const findNextBreakPoint = (currentIndex: number): number => { + const points = new Set(); + + marks.forEach(mark => { + if (mark.start > currentIndex) points.add(mark.start); + if (mark.end > currentIndex) points.add(mark.end); + }); + const nextPoints = Array.from(points).sort((a, b) => a - b); + + return nextPoints[0] || text.length; + }; + + while (currentIndex < text.length) { + const nextBreak = findNextBreakPoint(currentIndex); + const currentFormats = getFormatsAt(currentIndex); + + let segment = text.slice(currentIndex, nextBreak); + + patterns.forEach(({ regex }) => { + segment = segment.replace(regex, '$1'); + }); + + if (segment) { + ops.push({ + insert: segment, + ...(Object.keys(currentFormats).length > 0 ? { attributes: currentFormats } : {}), + }); + } + + currentIndex = nextBreak; + } + + return { ops }; +} + +function isImageUrl(url: string): boolean { if (url.startsWith('https://unsplash.com/') || url.startsWith('https://images.unsplash.com/')) { return true; } @@ -115,7 +273,7 @@ function isImageUrl (url: string): boolean { return false; } -function processTodoList (element: HTMLElement, sharedRoot: YSharedRoot, parentBlock: YBlock, blockData: BlockData) { +function processTodoList(element: HTMLElement, sharedRoot: YSharedRoot, parentBlock: YBlock, blockData: BlockData) { const checkboxBlock = createBlock(sharedRoot, { ty: BlockType.TodoListBlock, data: blockData }); @@ -128,14 +286,14 @@ function processTodoList (element: HTMLElement, sharedRoot: YSharedRoot, parentB } } -function processImage (sharedRoot: YSharedRoot, parentBlock: YBlock, data: ImageBlockData) { +function processImage(sharedRoot: YSharedRoot, parentBlock: YBlock, data: ImageBlockData) { const imageBlock = createBlock(sharedRoot, { ty: BlockType.ImageBlock, data }); updateBlockParent(sharedRoot, imageBlock, parentBlock, getChildrenArray(parentBlock.get(YjsEditorKey.block_children), sharedRoot)?.length || 0); } -function processList (parentEl: HTMLElement, sharedRoot: YSharedRoot, { +function processList(parentEl: HTMLElement, sharedRoot: YSharedRoot, { ty, data, parent, @@ -159,7 +317,7 @@ function processList (parentEl: HTMLElement, sharedRoot: YSharedRoot, { }); } -function getInlineAttributes (element: HTMLElement): Record { +function getInlineAttributes(element: HTMLElement): Record { const attributes: Record = {}; if (element.style.fontWeight === 'bold' || element.tagName.toLowerCase() === 'strong') { @@ -189,24 +347,16 @@ function getInlineAttributes (element: HTMLElement): Record (element: HTMLElement): T { +function mapToBlockData(element: HTMLElement): T { const data = {} as T; const tag = element.tagName.toLowerCase(); @@ -290,7 +440,7 @@ function mapToBlockData (element: HTMLElement): T { return data; } -function mapTagToBlockType (tag: string, el: HTMLElement): BlockType { +function mapTagToBlockType(tag: string, el: HTMLElement): BlockType { switch (tag) { case 'p': return BlockType.Paragraph; @@ -323,7 +473,7 @@ function mapTagToBlockType (tag: string, el: HTMLElement): BlockType { } } -export function deserializeHTML (html: string) { +export function deserializeHTML(html: string) { const parsed = new DOMParser().parseFromString(html, 'text/html'); const doc = createEmptyDocument(); const sharedRoot = doc.getMap(YjsEditorKey.data_section) as YSharedRoot; @@ -333,4 +483,44 @@ export function deserializeHTML (html: string) { const slateContent = yDocToSlateContent(doc); return slateContent?.children; -} \ No newline at end of file +} + +export function convertSlateFragmentTo(fragment: SlateNode[]) { + const traverse = (node: SlateNode) => { + + if (SlateText.isText(node)) { + return node; + } + + if (SlateElement.isElement(node)) { + const isTextChildren = node.children.every(SlateText.isText); + const children = node.children.map(traverse).filter(Boolean) as SlateText[]; + const blockId = generateBlockId(); + + let type = node.type as BlockType; + + if (!Object.values(BlockType).includes(type)) { + type = BlockType.Paragraph; + } + + const blockChildren = isTextChildren ? [{ + textId: blockId, + children: isTextChildren ? children : [], + }] : [{ + textId: blockId, + children: [{ text: '' }], + }, ...children]; + + return { + blockId, + data: node.data, + type, + children: blockChildren, + }; + } + + return null; + }; + + return fragment.map(traverse).filter(Boolean) as SlateElement[]; +} diff --git a/frontend/appflowy_web_app/src/components/editor/utils/markdown.ts b/frontend/appflowy_web_app/src/components/editor/utils/markdown.ts index 8e4b0122c3..28acdf6bab 100644 --- a/frontend/appflowy_web_app/src/components/editor/utils/markdown.ts +++ b/frontend/appflowy_web_app/src/components/editor/utils/markdown.ts @@ -1,7 +1,7 @@ import { YjsEditor } from '@/application/slate-yjs'; import { CustomEditor } from '@/application/slate-yjs/command'; import { EditorMarkFormat } from '@/application/slate-yjs/types'; -import { getBlock, getBlockEntry, getSharedRoot, getText } from '@/application/slate-yjs/utils/yjsOperations'; +import { findSlateEntryByBlockId, getBlockEntry, getSharedRoot } from '@/application/slate-yjs/utils/editor'; import { BlockData, BlockType, @@ -12,6 +12,7 @@ import { YjsEditorKey, } from '@/application/types'; import { Editor, Range, Transforms } from 'slate'; +import { getBlock, getText } from '@/application/slate-yjs/utils/yjs'; enum SpecialSymbol { EM_DASH = '—', @@ -53,7 +54,7 @@ type Rule = { filter?: (editor: YjsEditor, match: RegExpMatchArray) => boolean } -function deletePrefix (editor: YjsEditor, offset: number) { +function deletePrefix(editor: YjsEditor, offset: number) { const [, path] = getBlockEntry(editor); const { selection } = editor; @@ -66,19 +67,19 @@ function deletePrefix (editor: YjsEditor, offset: number) { editor.delete(); } -function getNodeType (editor: YjsEditor) { +function getNodeType(editor: YjsEditor) { const [node] = getBlockEntry(editor); return node.type as BlockType; } -function getBlockData (editor: YjsEditor) { +function getBlockData(editor: YjsEditor) { const [node] = getBlockEntry(editor); return node.data as BlockData; } -function getLineText (editor: YjsEditor) { +function getLineText(editor: YjsEditor) { const [node] = getBlockEntry(editor); const sharedRoot = getSharedRoot(editor); const block = getBlock(node.blockId as string, sharedRoot); @@ -227,10 +228,18 @@ const rules: Rule[] = [ return (['--', '**', '__', '—'].every(t => t !== text)) || getNodeType(editor) === BlockType.DividerBlock; }, - transform: (editor, match) => { + transform: (editor) => { + const newBlockId = CustomEditor.turnToBlock(editor, getBlockEntry(editor)[0].blockId as string, BlockType.DividerBlock, {}); - CustomEditor.turnToBlock(editor, getBlockEntry(editor)[0].blockId as string, BlockType.DividerBlock, {}); - deletePrefix(editor, match[0].length - 1); + if (!newBlockId) { + Transforms.move(editor, { distance: 1, reverse: true }); + } else { + const entry = findSlateEntryByBlockId(editor, newBlockId); + + if (entry) { + Transforms.select(editor, entry[1]); + } + } }, }, @@ -403,12 +412,10 @@ export const applyMarkdown = (editor: YjsEditor, insertText: string): boolean => anchor: { path, offset: start }, focus: { path, offset: start + formatText.length }, }); - - CustomEditor.addMark(editor, { key: rule.format as EditorMarkFormat, value: true }); + CustomEditor.toggleMark(editor, { key: rule.format as EditorMarkFormat, value: true }); } Transforms.collapse(editor, { edge: 'end' }); - } return true; @@ -432,8 +439,6 @@ export const applyMarkdown = (editor: YjsEditor, insertText: string): boolean => return true; } - - console.log('symbol text', text, match); } } diff --git a/frontend/appflowy_web_app/src/components/error/ElementFallbackRender.tsx b/frontend/appflowy_web_app/src/components/error/ElementFallbackRender.tsx index ddfdac39f1..c063c6e3ac 100644 --- a/frontend/appflowy_web_app/src/components/error/ElementFallbackRender.tsx +++ b/frontend/appflowy_web_app/src/components/error/ElementFallbackRender.tsx @@ -1,11 +1,19 @@ import { Alert } from '@mui/material'; import { FallbackProps } from 'react-error-boundary'; -export function ElementFallbackRender({ error }: FallbackProps) { +export function ElementFallbackRender({ error, description }: FallbackProps & { + description?: string; +}) { return ( - +

Something went wrong:

{error.message}
+ {description &&
{description}
}
); } diff --git a/frontend/appflowy_web_app/src/components/global-comment/GlobalComment.tsx b/frontend/appflowy_web_app/src/components/global-comment/GlobalComment.tsx index c9ae90f1a6..dbc793eb9e 100644 --- a/frontend/appflowy_web_app/src/components/global-comment/GlobalComment.tsx +++ b/frontend/appflowy_web_app/src/components/global-comment/GlobalComment.tsx @@ -14,7 +14,7 @@ function GlobalComment () {
{t('globalComment.comments')}
diff --git a/frontend/appflowy_web_app/src/components/global-comment/add-comment/AddCommentWrapper.tsx b/frontend/appflowy_web_app/src/components/global-comment/add-comment/AddCommentWrapper.tsx index 08380d5905..f896634e55 100644 --- a/frontend/appflowy_web_app/src/components/global-comment/add-comment/AddCommentWrapper.tsx +++ b/frontend/appflowy_web_app/src/components/global-comment/add-comment/AddCommentWrapper.tsx @@ -61,7 +61,11 @@ export function AddCommentWrapper () { return ( <> -
+
-
- +
+
diff --git a/frontend/appflowy_web_app/src/components/main/App.tsx b/frontend/appflowy_web_app/src/components/main/App.tsx index ed97d085f5..941db2d226 100644 --- a/frontend/appflowy_web_app/src/components/main/App.tsx +++ b/frontend/appflowy_web_app/src/components/main/App.tsx @@ -20,35 +20,35 @@ const AppMain = withAppWrapper(() => { } + element={} /> } + element={} /> } + element={} /> } + element={} /> } + element={} /> } + element={} /> } + element={} /> } + element={} /> { path="/app/*" element={ - + } /> } + element={} /> ); }); -function App () { +function App() { + const path = window.location.pathname; + + if (path.startsWith('/.well-known')) { + return null; + } + return ( - + ); } diff --git a/frontend/appflowy_web_app/src/components/main/AppConfig.tsx b/frontend/appflowy_web_app/src/components/main/AppConfig.tsx index 541c2d4176..2dbf3778aa 100644 --- a/frontend/appflowy_web_app/src/components/main/AppConfig.tsx +++ b/frontend/appflowy_web_app/src/components/main/AppConfig.tsx @@ -12,7 +12,7 @@ import { useLiveQuery } from 'dexie-react-hooks'; import { useSnackbar } from 'notistack'; import React, { Suspense, useCallback, useEffect, useMemo, useState } from 'react'; -function AppConfig ({ children }: { children: React.ReactNode }) { +function AppConfig({ children }: { children: React.ReactNode }) { const [appConfig] = useState(defaultConfig); const service = useMemo(() => getService(appConfig), [appConfig]); const [isAuthenticated, setIsAuthenticated] = React.useState(isTokenValid()); @@ -38,6 +38,7 @@ function AppConfig ({ children }: { children: React.ReactNode }) { useEffect(() => { return on(EventType.SESSION_VALID, () => { + console.log('session valid'); setIsAuthenticated(true); }); }, []); @@ -70,6 +71,7 @@ function AppConfig ({ children }: { children: React.ReactNode }) { }, []); useEffect(() => { return on(EventType.SESSION_INVALID, () => { + console.log('session invalid'); setIsAuthenticated(false); }); }, []); @@ -79,16 +81,16 @@ function AppConfig ({ children }: { children: React.ReactNode }) { useEffect(() => { window.toast = { - success: (message: string) => { + success: (message: string | React.ReactNode) => { enqueueSnackbar(message, { variant: 'success' }); }, - error: (message: string) => { + error: (message: string | React.ReactNode) => { enqueueSnackbar(message, { variant: 'error' }); }, - warning: (message: string) => { + warning: (message: string | React.ReactNode) => { enqueueSnackbar(message, { variant: 'warning' }); }, - default: (message: string) => { + default: (message: string | React.ReactNode) => { enqueueSnackbar(message, { variant: 'default' }); }, diff --git a/frontend/appflowy_web_app/src/components/main/AppTheme.tsx b/frontend/appflowy_web_app/src/components/main/AppTheme.tsx index 3330c09204..691a4921d2 100644 --- a/frontend/appflowy_web_app/src/components/main/AppTheme.tsx +++ b/frontend/appflowy_web_app/src/components/main/AppTheme.tsx @@ -2,12 +2,15 @@ import { ThemeModeContext, useAppThemeMode } from '@/components/main/useAppTheme import React, { useMemo } from 'react'; import createTheme from '@mui/material/styles/createTheme'; import ThemeProvider from '@mui/material/styles/ThemeProvider'; -import '@/i18n/config'; +import { + i18nInstance, +} from '@/i18n/config'; import 'src/styles/tailwind.css'; import 'src/styles/template.css'; +import { I18nextProvider } from 'react-i18next'; -function AppTheme ({ children }: { children: React.ReactNode; }) { +function AppTheme({ children }: { children: React.ReactNode; }) { const { isDark, setIsDark } = useAppThemeMode(); const theme = useMemo( @@ -47,7 +50,7 @@ function AppTheme ({ children }: { children: React.ReactNode; }) { MuiIconButton: { styleOverrides: { root: { - '&:hover': { + '&:hover, &:focus': { backgroundColor: 'var(--fill-list-hover)', }, borderRadius: '4px', @@ -55,6 +58,15 @@ function AppTheme ({ children }: { children: React.ReactNode; }) { '&.MuiIconButton-colorInherit': { color: 'var(--icon-primary)', }, + '&.MuiIconButton-colorPrimary': { + color: 'var(--fill-default)', + }, + }, + colorSecondary: { + color: 'var(--billing-primary)', + '&:hover': { + color: 'var(--billing-primary-hover)', + }, }, }, }, @@ -82,12 +94,34 @@ function AppTheme ({ children }: { children: React.ReactNode; }) { opacity: 0.3, color: 'var(--content-on-fill)', }, + '&.MuiButton-containedInherit': { + color: 'var(--text-title)', + backgroundColor: isDark ? 'rgba(0, 0, 0, 0.4)' : 'rgba(255, 255, 255, 0.4)', + '&:hover': { + backgroundColor: 'var(--bg-body)', + boxShadow: 'var(--shadow)', + }, + }, + '&.MuiButton-containedSecondary': { + backgroundColor: 'var(--billing-primary)', + '&:hover': { + backgroundColor: 'var(--billing-primary-hover)', + }, + }, }, outlined: { '&.MuiButton-outlinedInherit': { borderColor: 'var(--line-divider)', }, borderRadius: '8px', + '&.MuiButton-outlinedSecondary': { + color: 'var(--billing-primary)', + borderColor: 'var(--billing-primary)', + '&:hover': { + color: 'var(--billing-primary-hover)', + borderColor: 'var(--billing-primary-hover)', + }, + }, }, }, @@ -113,7 +147,6 @@ function AppTheme ({ children }: { children: React.ReactNode; }) { boxShadow: 'none !important', }, }, - }, MuiPaper: { styleOverrides: { @@ -156,11 +189,11 @@ function AppTheme ({ children }: { children: React.ReactNode; }) { MuiTooltip: { styleOverrides: { arrow: { - color: 'var(--bg-tips)', + color: 'var(--fill-toolbar)', }, tooltip: { - backgroundColor: 'var(--bg-tips)', - color: 'var(--text-title)', + backgroundColor: 'var(--fill-toolbar)', + color: 'white', fontSize: '0.85rem', borderRadius: '8px', fontWeight: 400, @@ -197,6 +230,10 @@ function AppTheme ({ children }: { children: React.ReactNode; }) { main: '#00BCF0', dark: '#00BCF0', }, + secondary: { + main: '#8427e0', + dark: '#601DAA', + }, error: { main: '#FB006D', dark: '#D32772', @@ -229,14 +266,16 @@ function AppTheme ({ children }: { children: React.ReactNode; }) { ); return ( - - {children} - + + + {children} + + ); } diff --git a/frontend/appflowy_web_app/src/components/main/withAppWrapper.tsx b/frontend/appflowy_web_app/src/components/main/withAppWrapper.tsx index 282436c90c..02c3ce2740 100644 --- a/frontend/appflowy_web_app/src/components/main/withAppWrapper.tsx +++ b/frontend/appflowy_web_app/src/components/main/withAppWrapper.tsx @@ -19,8 +19,8 @@ const StyledSnackbarProvider = styled(SnackbarProvider)` `; -export default function withAppWrapper (Component: React.FC): React.FC { - return function AppWrapper (): JSX.Element { +export default function withAppWrapper(Component: React.FC): React.FC { + return function AppWrapper(): JSX.Element { return ( @@ -30,6 +30,7 @@ export default function withAppWrapper (Component: React.FC): React.FC { horizontal: 'center', }} preventDuplicate + autoHideDuration={3000} Components={{ info: InfoSnackbar, success: CustomSnackbar, @@ -40,7 +41,7 @@ export default function withAppWrapper (Component: React.FC): React.FC { > - + diff --git a/frontend/appflowy_web_app/src/components/publish/CollabView.tsx b/frontend/appflowy_web_app/src/components/publish/CollabView.tsx index d0fa2fc639..07420aa8e5 100644 --- a/frontend/appflowy_web_app/src/components/publish/CollabView.tsx +++ b/frontend/appflowy_web_app/src/components/publish/CollabView.tsx @@ -1,8 +1,5 @@ import { - AppendBreadcrumb, - CreateRowDoc, - LoadView, - LoadViewMeta, + UIVariant, ViewComponentProps, ViewLayout, YDoc, } from '@/application/types'; @@ -15,7 +12,6 @@ import { Document } from '@/components/document'; import DatabaseView from '@/components/publish/DatabaseView'; import { useViewMeta } from '@/components/publish/useViewMeta'; import React, { useMemo, Suspense } from 'react'; -import { ViewMetaProps } from '@/components/view-meta'; const ViewHelmet = React.lazy(() => import('@/components/_shared/helmet/ViewHelmet')); @@ -37,19 +33,7 @@ function CollabView ({ doc }: CollabViewProps) { default: return null; } - }, [layout]) as React.FC<{ - doc: YDoc; - readOnly: boolean; - navigateToView?: (viewId: string, blockId?: string) => Promise; - loadViewMeta?: LoadViewMeta; - createRowDoc?: CreateRowDoc; - loadView?: LoadView; - viewMeta: ViewMetaProps; - isTemplateThumb?: boolean; - appendBreadcrumb?: AppendBreadcrumb; - variant?: 'publish' | 'app'; - onRendered?: () => void; - }>; + }, [layout]) as React.FC; const navigateToView = usePublishContext()?.toView; const loadViewMeta = usePublishContext()?.loadViewMeta; @@ -117,7 +101,7 @@ function CollabView ({ doc }: CollabViewProps) { loadView={loadView} isTemplateThumb={isTemplateThumb} appendBreadcrumb={appendBreadcrumb} - variant={'publish'} + variant={UIVariant.Publish} onRendered={onRendered} viewMeta={{ icon, diff --git a/frontend/appflowy_web_app/src/components/publish/DatabaseView.tsx b/frontend/appflowy_web_app/src/components/publish/DatabaseView.tsx index 1d75037e5e..25d5476c71 100644 --- a/frontend/appflowy_web_app/src/components/publish/DatabaseView.tsx +++ b/frontend/appflowy_web_app/src/components/publish/DatabaseView.tsx @@ -7,6 +7,7 @@ import { ViewLayout, YDatabase, YDoc, + ViewMetaProps, YjsEditorKey, } from '@/application/types'; import ComponentLoading from '@/components/_shared/progress/ComponentLoading'; @@ -15,10 +16,9 @@ import DocumentSkeleton from '@/components/_shared/skeleton/DocumentSkeleton'; import GridSkeleton from '@/components/_shared/skeleton/GridSkeleton'; import KanbanSkeleton from '@/components/_shared/skeleton/KanbanSkeleton'; import { Database } from '@/components/database'; -import DatabaseHeader from '@/components/database/components/header/DatabaseHeader'; -import { ViewMetaProps } from '@/components/view-meta'; import React, { Suspense, useCallback, useMemo } from 'react'; import { useSearchParams } from 'react-router-dom'; +import ViewMetaPreview from 'src/components/view-meta/ViewMetaPreview'; export interface DatabaseProps { doc: YDoc; @@ -90,9 +90,12 @@ function DatabaseView ({ viewMeta, ...props }: DatabaseProps) { minHeight: 'calc(100vh - 48px)', maxWidth: isTemplateThumb ? '964px' : undefined, }} - className={'relative flex h-full w-full flex-col px-6'} + className={'relative flex h-full w-full flex-col'} > - {rowId ? null : } + {rowId ? null : }
diff --git a/frontend/appflowy_web_app/src/components/publish/SideBar.tsx b/frontend/appflowy_web_app/src/components/publish/SideBar.tsx index 2798422033..d3d7f26994 100644 --- a/frontend/appflowy_web_app/src/components/publish/SideBar.tsx +++ b/frontend/appflowy_web_app/src/components/publish/SideBar.tsx @@ -22,7 +22,7 @@ function SideBar ({ return ( toggleOpenDrawer(false)} > diff --git a/frontend/appflowy_web_app/src/components/publish/header/duplicate/SpaceList.tsx b/frontend/appflowy_web_app/src/components/publish/header/duplicate/SpaceList.tsx index 662c64c4b4..1225de6abc 100644 --- a/frontend/appflowy_web_app/src/components/publish/header/duplicate/SpaceList.tsx +++ b/frontend/appflowy_web_app/src/components/publish/header/duplicate/SpaceList.tsx @@ -2,8 +2,7 @@ import { SpaceView } from '@/application/types'; import React, { useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import { ReactComponent as CheckIcon } from '@/assets/selected.svg'; -import { renderColor } from '@/utils/color'; -import SpaceIcon from '@/components/_shared/breadcrumb/SpaceIcon'; +import SpaceIcon from '@/components/_shared/view-icon/SpaceIcon'; import { Button, CircularProgress, Tooltip } from '@mui/material'; import { ReactComponent as LockSvg } from '@/assets/lock.svg'; @@ -12,9 +11,10 @@ export interface SpaceListProps { onChange?: (value: string) => void; spaceList: SpaceView[]; loading?: boolean; + title?: React.ReactNode; } -function SpaceList ({ loading, spaceList, value, onChange }: SpaceListProps) { +function SpaceList({ loading, spaceList, value, onChange, title }: SpaceListProps) { const { t } = useTranslation(); const getExtraObj = useCallback((extra: string) => { @@ -37,20 +37,15 @@ function SpaceList ({ loading, spaceList, value, onChange }: SpaceListProps) { return (
- - - + />
{space.name} - {space.isPrivate && } + {space.isPrivate && }
); @@ -60,10 +55,10 @@ function SpaceList ({ loading, spaceList, value, onChange }: SpaceListProps) { return (
-
{t('publish.addTo')}
+ {title ||
{t('publish.addTo')}
} {loading ? (
- +
) : (
@@ -71,7 +66,13 @@ function SpaceList ({ loading, spaceList, value, onChange }: SpaceListProps) { const isSelected = value === space.id; return ( - + diff --git a/frontend/appflowy_web_app/src/components/publish/useViewMeta.ts b/frontend/appflowy_web_app/src/components/publish/useViewMeta.ts index 3abe666673..c252feb8b2 100644 --- a/frontend/appflowy_web_app/src/components/publish/useViewMeta.ts +++ b/frontend/appflowy_web_app/src/components/publish/useViewMeta.ts @@ -1,8 +1,8 @@ import { usePublishContext } from '@/application/publish'; import { EditorLayoutStyle } from '@/components/editor/EditorContext'; -import { ViewMetaCover } from '@/components/view-meta'; import { getFontFamily } from '@/utils/font'; import { useEffect, useMemo } from 'react'; +import { ViewMetaCover } from '@/application/types'; export function useViewMeta () { const viewMeta = usePublishContext()?.viewMeta; diff --git a/frontend/appflowy_web_app/src/components/quick-note/AddNote.tsx b/frontend/appflowy_web_app/src/components/quick-note/AddNote.tsx new file mode 100644 index 0000000000..7f8966014f --- /dev/null +++ b/frontend/appflowy_web_app/src/components/quick-note/AddNote.tsx @@ -0,0 +1,39 @@ +import React from 'react'; +import { ReactComponent as AddIcon } from '@/assets/add.svg'; +import { useAddNode } from '@/components/quick-note/QuickNote.hooks'; +import { QuickNote } from '@/application/types'; +import { Button, CircularProgress } from '@mui/material'; +import { useTranslation } from 'react-i18next'; + +function AddNote({ + onEnterNote, + onAdd, +}: { + onEnterNote: (node: QuickNote) => void; + onAdd: (note: QuickNote) => void; +}) { + const { + handleAdd, + loading, + } = useAddNode({ + onEnterNote, + onAdd, + }); + + const { t } = useTranslation(); + + return ( + <> + + + ); +} + +export default AddNote; \ No newline at end of file diff --git a/frontend/appflowy_web_app/src/components/quick-note/DeleteNoteModal.tsx b/frontend/appflowy_web_app/src/components/quick-note/DeleteNoteModal.tsx new file mode 100644 index 0000000000..d18e0c0fc4 --- /dev/null +++ b/frontend/appflowy_web_app/src/components/quick-note/DeleteNoteModal.tsx @@ -0,0 +1,67 @@ +import React, { useContext } from 'react'; +import { ToastContext } from '@/components/quick-note/QuickNote.hooks'; +import { useCurrentWorkspaceId } from '@/components/app/app.hooks'; +import { useService } from '@/components/main/app.hooks'; +import { NormalModal } from '@/components/_shared/modal'; +import { useTranslation } from 'react-i18next'; +import { QuickNote } from '@/application/types'; +import { getTitle } from '@/components/quick-note/utils'; + +function DeleteNoteModal({ open, onClose, note, onDelete }: { + open: boolean; + onClose: () => void; + note: QuickNote; + onDelete: (id: string) => void; +}) { + const { t } = useTranslation(); + const toast = useContext(ToastContext); + + const [loading, setLoading] = React.useState(false); + const currentWorkspaceId = useCurrentWorkspaceId(); + const service = useService(); + const handleDelete = async () => { + if (!service || !currentWorkspaceId || loading) return; + setLoading(true); + try { + await service.deleteQuickNote(currentWorkspaceId, note.id); + + onDelete(note.id); + onClose(); + // eslint-disable-next-line + } catch (e: any) { + console.error(e); + toast.onOpen(e.message); + } finally { + setLoading(false); + } + }; + + return ( + + {`${t('button.delete')}: ${getTitle(note) || t('menuAppHeader.defaultNewPageName')}`} +
+ } + onOk={handleDelete} + PaperProps={{ + className: 'w-[420px] max-w-[70vw]', + }}> +
+ {t('quickNote.deleteNotePrompt')} +
+ + ); +} + +export default DeleteNoteModal; \ No newline at end of file diff --git a/frontend/appflowy_web_app/src/components/quick-note/Note.tsx b/frontend/appflowy_web_app/src/components/quick-note/Note.tsx new file mode 100644 index 0000000000..144eab48c4 --- /dev/null +++ b/frontend/appflowy_web_app/src/components/quick-note/Note.tsx @@ -0,0 +1,159 @@ +import React, { useCallback, useContext, useEffect, useMemo } from 'react'; +import { Editor, EditorData, EditorProvider, useEditor, FixedToolbar } from '@appflowyinc/editor'; +import '@appflowyinc/editor/style'; +import { ReactComponent as AddIcon } from '@/assets/add.svg'; + +import { useTranslation } from 'react-i18next'; +import { ThemeModeContext } from '@/components/main/useAppThemeMode'; +import { useCurrentWorkspaceId } from '@/components/app/app.hooks'; +import { useService } from '@/components/main/app.hooks'; +import { debounce } from 'lodash-es'; +import { QuickNote, QuickNoteEditorData } from '@/application/types'; +import dayjs from 'dayjs'; +import { useAddNode } from '@/components/quick-note/QuickNote.hooks'; +import { CircularProgress, IconButton, Tooltip } from '@mui/material'; +import { getTitle } from '@/components/quick-note/utils'; + +function Note({ + note, + onUpdateData, + onEnterNote, + onAdd, +}: { + note: QuickNote, + onUpdateData: (data: QuickNoteEditorData[]) => void; + onEnterNote: (node: QuickNote) => void; + onAdd: (note: QuickNote) => void; +}) { + + const ref = React.useRef(null); + + useEffect(() => { + const el = ref.current; + + if (!el) return; + + setTimeout(() => { + const editorDom = el.querySelector('.appflowy-editor div[role="textbox"]') as HTMLElement; + + if (!editorDom) return; + + const sel = window.getSelection(); + const range = document.createRange(); + + range.selectNodeContents(editorDom); + range.collapse(true); + sel?.removeAllRanges(); + sel?.addRange(range); + }, 200); + + //eslint-disable-next-line + }, [note.id]); + + return ( +
+ + + +
+ ); +} + +function NoteEditor({ + note, + onUpdateData, + onEnterNote, + onAdd, +}: { + note: QuickNote, + onUpdateData: (data: QuickNoteEditorData[]) => void; + onEnterNote: (node: QuickNote) => void; + onAdd: (note: QuickNote) => void; +}) { + const { i18n, t } = useTranslation(); + const locale = useMemo(() => ({ + lang: i18n.language, + }), [i18n.language]); + + const [, setClock] = React.useState(0); + const isDark = useContext(ThemeModeContext)?.isDark; + const theme = isDark ? 'dark' : 'light'; + + const editor = useEditor(); + + useEffect(() => { + editor.applyData(note.data as EditorData); + setClock(prev => prev + 1); + // eslint-disable-next-line + }, [editor, note.id]); + + const currentWorkspaceId = useCurrentWorkspaceId(); + const service = useService(); + + const handleUpdate = useCallback(async (data: EditorData) => { + if (!service || !currentWorkspaceId) return; + try { + await service.updateQuickNote(currentWorkspaceId, note.id, data as QuickNoteEditorData[]); + // eslint-disable-next-line + } catch (e: any) { + console.error(e); + } + }, [service, currentWorkspaceId, note.id]); + + const updatedAt = useMemo(() => { + const date = dayjs(note.last_updated_at); + + return date.format('MMMM D, YYYY') + ' at ' + date.format('h:mm A'); + }, [note.last_updated_at]); + + const debounceUpdate = useMemo(() => debounce(handleUpdate, 300), [handleUpdate]); + + const handleChange = useCallback((data: EditorData) => { + void debounceUpdate(data); + onUpdateData(data as QuickNoteEditorData[]); + }, [debounceUpdate, onUpdateData]); + + useEffect(() => { + return () => { + void debounceUpdate.flush(); + }; + }, [debounceUpdate]); + + const { + handleAdd, + loading, + } = useAddNode({ + onEnterNote, + onAdd, + }); + + const CustomToolbar = useCallback(() => { + return
+
+
+ +
+
+ + + {loading ? : } + + +
+
+ +
{updatedAt}
+
; + }, [handleAdd, loading, note, t, updatedAt]); + + return <> + + ; +} + +export default Note; \ No newline at end of file diff --git a/frontend/appflowy_web_app/src/components/quick-note/NoteHeader.tsx b/frontend/appflowy_web_app/src/components/quick-note/NoteHeader.tsx new file mode 100644 index 0000000000..16c5674e6f --- /dev/null +++ b/frontend/appflowy_web_app/src/components/quick-note/NoteHeader.tsx @@ -0,0 +1,47 @@ +import React, { useMemo } from 'react'; +import { ReactComponent as RightIcon } from '@/assets/arrow_right.svg'; +import { ReactComponent as OpenIcon } from '@/assets/full_view.svg'; +import { ReactComponent as CollapseIcon } from '@/assets/collapse_all_page.svg'; + +import { ReactComponent as CloseIcon } from '@/assets/close.svg'; + +import { IconButton } from '@mui/material'; +import { useTranslation } from 'react-i18next'; +import { QuickNote } from '@/application/types'; +import { getTitle } from '@/components/quick-note/utils'; + +function NoteHeader({ note, onBack, onClose, expand, onToggleExpand }: { + onBack: () => void; + onClose: () => void; + expand?: boolean; + onToggleExpand?: () => void; + note: QuickNote; +}) { + const { t } = useTranslation(); + + const title = useMemo(() => { + return getTitle(note) || t('menuAppHeader.defaultNewPageName'); + }, [note, t]); + + return ( +
+ + + +
+ {title} +
+ { + e.currentTarget.blur(); + onToggleExpand?.(); + }} size={'small'}> + {expand ? : } + + + + +
+ ); +} + +export default NoteHeader; \ No newline at end of file diff --git a/frontend/appflowy_web_app/src/components/quick-note/NoteList.tsx b/frontend/appflowy_web_app/src/components/quick-note/NoteList.tsx new file mode 100644 index 0000000000..3d0fa3d926 --- /dev/null +++ b/frontend/appflowy_web_app/src/components/quick-note/NoteList.tsx @@ -0,0 +1,114 @@ +import React, { useCallback } from 'react'; +import { QuickNote, QuickNote as QuickNoteType } from '@/application/types'; +import { useTranslation } from 'react-i18next'; +import { Divider, IconButton, Tooltip } from '@mui/material'; +import { ReactComponent as EditIcon } from '@/assets/edit.svg'; +import { ReactComponent as DeleteIcon } from '@/assets/trash.svg'; +import DeleteNoteModal from '@/components/quick-note/DeleteNoteModal'; +import { getSummary, getTitle, getUpdateTime } from '@/components/quick-note/utils'; +import AddNote from '@/components/quick-note/AddNote'; + +function NoteList({ + list, + onEnterNode, + onDelete, + onScroll, + onAdd, +}: { + list: QuickNoteType[]; + onEnterNode: (node: QuickNoteType) => void; + onAdd: (note: QuickNote) => void; + onDelete: (id: string) => void; + onScroll: (e: React.UIEvent) => void; +}) { + const { t } = useTranslation(); + const renderTitle = useCallback((note: QuickNote) => { + return getTitle(note).trim() || t('menuAppHeader.defaultNewPageName'); + }, [t]); + + const [openDeleteModal, setOpenDeleteModal] = React.useState(false); + const [selectedNote, setSelectedNote] = React.useState(null); + const [hoverId, setHoverId] = React.useState(null); + + return ( + <> + {list.length === 0 && (
{t('quickNote.quickNotesEmpty')}
)} + {list && +
+
+ { + list.map((note, index) => { + return ( + +
onEnterNode(note)} + onMouseEnter={() => setHoverId(note.id)} + onMouseLeave={() => setHoverId(null)} + key={note.id} + className={`px-5 relative hover:bg-content-blue-50 text-sm overflow-hidden cursor-pointer`} + > +
+
+ {renderTitle(note)} +
+
+ + {getUpdateTime(note)} + + {getSummary(note) || t('quickNote.noAdditionalText')} +
+
+ {hoverId === note.id ?
+ + { + e.stopPropagation(); + onEnterNode(note); + }} size={'small'}> + + + + + + { + e.stopPropagation(); + + setSelectedNote(note); + setOpenDeleteModal(true); + }} size={'small'}> + + + +
: null} +
+ +
+ ); + }) + } +
+ {selectedNote && { + setOpenDeleteModal(false); + setSelectedNote(null); + }} + note={selectedNote}/> + } + +
} + +
+ +
+ + + ); +} + +export default NoteList; \ No newline at end of file diff --git a/frontend/appflowy_web_app/src/components/quick-note/NoteListHeader.tsx b/frontend/appflowy_web_app/src/components/quick-note/NoteListHeader.tsx new file mode 100644 index 0000000000..cc7423524c --- /dev/null +++ b/frontend/appflowy_web_app/src/components/quick-note/NoteListHeader.tsx @@ -0,0 +1,94 @@ +import React, { useMemo } from 'react'; +import { ReactComponent as SearchIcon } from '@/assets/search.svg'; +import { ReactComponent as CloseIcon } from '@/assets/close.svg'; +import { ReactComponent as OpenIcon } from '@/assets/full_view.svg'; +import { ReactComponent as CollapseIcon } from '@/assets/collapse_all_page.svg'; + +import { IconButton, InputBase, Tooltip } from '@mui/material'; +import { useTranslation } from 'react-i18next'; +import { createHotkey, HOT_KEY_NAME } from '@/utils/hotkeys'; +import { debounce } from 'lodash-es'; + +function NoteListHeader({ + onSearch, + onClose, + expand, + onToggleExpand, +}: { + onSearch: (searchTerm: string) => void; + onClose: () => void; + expand?: boolean; + onToggleExpand?: () => void; +}) { + const { t } = useTranslation(); + const [activeSearch, setActiveSearch] = React.useState(false); + const inputRef = React.useRef(null); + + const debounceSearch = useMemo(() => debounce((searchTerm: string) => { + onSearch(searchTerm); + }, 300), [onSearch]); + + return ( +
+ + { + e.stopPropagation(); + if (!activeSearch) { + inputRef.current?.focus(); + setActiveSearch(true); + } + }} size={'small'}> + + + +
+ {activeSearch ? + { + if (activeSearch && createHotkey(HOT_KEY_NAME.ESCAPE)(e.nativeEvent)) { + e.stopPropagation(); + setActiveSearch(false); + } + }} + inputRef={inputRef} + size={'small'} + placeholder={t('quickNote.search')} + onInput={(e) => { + debounceSearch((e.target as HTMLInputElement).value); + }} + /> : +
{t('quickNote.quickNotes')}
+ } +
+ + { + e.currentTarget.blur(); + onToggleExpand?.(); + }} size={'small'}> + {expand ? : } + + + { + if (activeSearch) { + setActiveSearch(false); + debounceSearch(''); + } else { + onClose(); + } + }} size={'small'}> + + +
+ ); +} + +export default NoteListHeader; \ No newline at end of file diff --git a/frontend/appflowy_web_app/src/components/quick-note/QuickNote.hooks.ts b/frontend/appflowy_web_app/src/components/quick-note/QuickNote.hooks.ts new file mode 100644 index 0000000000..05dcc6f09f --- /dev/null +++ b/frontend/appflowy_web_app/src/components/quick-note/QuickNote.hooks.ts @@ -0,0 +1,59 @@ +import React, { useContext } from 'react'; +import { useCurrentWorkspaceId } from '@/components/app/app.hooks'; +import { useService } from '@/components/main/app.hooks'; +import { QuickNote } from '@/application/types'; + +export const ToastContext = React.createContext<{ + onOpen: (message: string) => void; + onClose: () => void; + open: boolean; +}>({ + onOpen: () => { + // + }, + onClose: () => { + // + }, + open: false, +}); + +export const LISI_LIMIT = 100; + +export function useAddNode({ + onEnterNote, + onAdd, +}: { + onEnterNote: (node: QuickNote) => void; + onAdd: (note: QuickNote) => void; +}) { + const toast = useContext(ToastContext); + + const [loading, setLoading] = React.useState(false); + const currentWorkspaceId = useCurrentWorkspaceId(); + const service = useService(); + const handleAdd = async () => { + if (!service || !currentWorkspaceId || loading) return; + setLoading(true); + try { + const note = await service.createQuickNote(currentWorkspaceId, [{ + type: 'paragraph', + delta: [{ insert: '' }], + children: [], + }]); + + onEnterNote(note); + onAdd(note); + // eslint-disable-next-line + } catch (e: any) { + console.error(e); + toast.onOpen(e.message); + } finally { + setLoading(false); + } + }; + + return { + handleAdd, + loading, + }; +} \ No newline at end of file diff --git a/frontend/appflowy_web_app/src/components/quick-note/QuickNote.tsx b/frontend/appflowy_web_app/src/components/quick-note/QuickNote.tsx new file mode 100644 index 0000000000..bacb741525 --- /dev/null +++ b/frontend/appflowy_web_app/src/components/quick-note/QuickNote.tsx @@ -0,0 +1,551 @@ +import React, { useCallback, useEffect, useMemo, useRef } from 'react'; +import { IconButton, Tooltip, Zoom, Snackbar, Portal } from '@mui/material'; +import { ReactComponent as EditIcon } from '@/assets/edit.svg'; +import { useTranslation } from 'react-i18next'; +import { createHotkey, createHotKeyLabel, HOT_KEY_NAME } from '@/utils/hotkeys'; +import Popover from '@mui/material/Popover'; +import NoteHeader from '@/components/quick-note/NoteHeader'; +import NoteListHeader from '@/components/quick-note/NoteListHeader'; +import { TransitionProps } from '@mui/material/transitions'; +import { LISI_LIMIT, ToastContext } from './QuickNote.hooks'; +import { useService } from '@/components/main/app.hooks'; +import { useCurrentWorkspaceId } from '@/components/app/app.hooks'; +import { QuickNote as QuickNoteType, QuickNoteEditorData } from '@/application/types'; +import NoteList from '@/components/quick-note/NoteList'; +import { getPopoverPosition, setPopoverPosition } from '@/components/quick-note/utils'; +import Note from '@/components/quick-note/Note'; + +const PAPER_SIZE = [480, 396]; +const Transition = React.forwardRef(function Transition( + props: TransitionProps & { + children: React.ReactElement; + }, + ref: React.Ref, +) { + return ; +}); + +enum QuickNoteRoute { + NOTE = 'note', + LIST = 'list', +} + +export function QuickNote() { + const { t } = useTranslation(); + const modifier = useMemo(() => createHotKeyLabel(HOT_KEY_NAME.QUICK_NOTE), []); + const [open, setOpen] = React.useState(false); + const [position, setPosition] = React.useState<{ x: number, y: number } | undefined>(undefined); + const [isDragging, setIsDragging] = React.useState(false); + const dragStartPos = useRef({ x: 0, y: 0 }); + const [route, setRoute] = React.useState(QuickNoteRoute.LIST); + const pageSizeRef = useRef(PAPER_SIZE); + const [expand, setExpand] = React.useState(false); + const paper = React.useRef(null); + const [toastMessage, setToastMessage] = React.useState(''); + const [openToast, setOpenToast] = React.useState(false); + + const [currentNote, setCurrentNote] = React.useState(undefined); + const [noteList, setNoteList] = React.useState([]); + const hasMoreRef = React.useRef(true); + const listParamsRef = useRef({ + offset: 0, + limit: LISI_LIMIT, + searchTerm: '', + }); + + const service = useService(); + const currentWorkspaceId = useCurrentWorkspaceId(); + + const handleOpenToast = useCallback((msg: string) => { + setOpenToast(true); + setToastMessage(msg); + }, []); + + const handleEnterNote = useCallback((note: QuickNoteType) => { + setCurrentNote(note); + setRoute(QuickNoteRoute.NOTE); + }, []); + + const handleAdd = useCallback(async () => { + if (!service || !currentWorkspaceId) return; + try { + const note = await service.createQuickNote(currentWorkspaceId, [{ + type: 'paragraph', + delta: [{ insert: '' }], + children: [], + }]); + + setNoteList(prev => [note, ...prev]); + + handleEnterNote(note); + // eslint-disable-next-line + } catch (e: any) { + console.error(e); + handleOpenToast(e.message); + } + }, [service, currentWorkspaceId, handleEnterNote, handleOpenToast]); + + const loadNoteList = useCallback(async (newParams: { + offset: number; + limit: number; + searchTerm: string; + }) => { + if (!service || !currentWorkspaceId) return; + try { + const notes = await service.getQuickNoteList(currentWorkspaceId, newParams); + + return notes; + // eslint-disable-next-line + } catch (e: any) { + console.error(e); + handleOpenToast(e.message); + } + }, [service, currentWorkspaceId, handleOpenToast]); + + const initNoteList = useCallback(async () => { + const params = { + offset: 0, + limit: LISI_LIMIT, + searchTerm: '', + }; + const notes = await loadNoteList(params); + + if (notes) { + setNoteList(notes.data); + hasMoreRef.current = notes.has_more; + } + + return notes; + }, [loadNoteList]); + + useEffect(() => { + setOpen(false); + setRoute(QuickNoteRoute.LIST); + setCurrentNote(undefined); + }, [currentWorkspaceId]); + + const handleLoadMore = useCallback(async () => { + const params = { + ...listParamsRef.current, + offset: noteList.length, + }; + const notes = await loadNoteList(params); + + if (notes) { + setNoteList(prev => [...prev, ...notes.data]); + hasMoreRef.current = notes.has_more; + } + }, [loadNoteList, noteList]); + + const handleSearch = useCallback(async (searchTerm: string) => { + const newParams = { + limit: LISI_LIMIT, + offset: 0, + searchTerm, + }; + + listParamsRef.current = newParams; + + const notes = await loadNoteList(newParams); + + if (notes) { + setNoteList(notes.data); + hasMoreRef.current = notes.has_more; + } + }, [loadNoteList]); + + const handleClose = () => { + setOpen(false); + }; + + const resetPosition = useCallback(() => { + const main = document.querySelector('.appflowy-layout'); + + if (!main) return; + + const localPosition = getPopoverPosition()[expand ? 'expand' : 'normal']; + + if (expand) { + pageSizeRef.current = [Math.min(window.innerWidth * 0.8, 840), Math.min(window.innerHeight * 0.9, 760)]; + + if (localPosition) { + setPosition(localPosition); + } else { + // center + setPosition({ + x: window.innerWidth / 2, + y: window.innerHeight / 2, + }); + } + + } else { + pageSizeRef.current = PAPER_SIZE; + const rect = main.getBoundingClientRect(); + + if (localPosition) { + setPosition(localPosition); + return; + } + + setPosition({ y: rect.bottom - PAPER_SIZE[1] / 2, x: rect.left + PAPER_SIZE[0] / 2 + 16 }); + } + }, [expand]); + + useEffect(() => { + resetPosition(); + }, [resetPosition]); + + const buttonRef = useRef(null); + const handleOpen = useCallback(async (forceCreate?: boolean) => { + + const el = buttonRef.current; + + if (!el) return; + const rect = el.getBoundingClientRect(); + + const localPosition = getPopoverPosition()[expand ? 'expand' : 'normal']; + + if (localPosition) { + setPosition(localPosition); + } else { + setPosition(prev => !prev ? { + x: rect.right + PAPER_SIZE[0] / 2, + y: rect.bottom - PAPER_SIZE[1] / 2, + } : prev); + } + + await initNoteList(); + + if (route === QuickNoteRoute.LIST || forceCreate) { + await handleAdd(); + } + + setOpen(true); + }, [expand, initNoteList, route, handleAdd]); + + useEffect(() => { + const handleKeyDown = (e: KeyboardEvent) => { + if (createHotkey(HOT_KEY_NAME.QUICK_NOTE)(e)) { + e.stopPropagation(); + e.preventDefault(); + + void (async () => { + try { + await handleOpen(true); + // eslint-disable-next-line + } catch (e: any) { + console.error(e); + handleOpenToast(e.message); + } + })(); + } else if (createHotkey(HOT_KEY_NAME.ESCAPE)(e)) { + handleClose(); + } + }; + + document.addEventListener('keydown', handleKeyDown); + + return () => { + document.removeEventListener('keydown', handleKeyDown); + }; + }, [handleOpen, handleOpenToast]); + + const handleMouseDown = (event: React.MouseEvent) => { + if (!position) return; + setIsDragging(true); + dragStartPos.current = { + x: event.clientX - position.x, + y: event.clientY - position.y, + }; + }; + + useEffect(() => { + const handleMouseMove = (event: MouseEvent) => { + if (isDragging) { + + const x = event.clientX - dragStartPos.current.x; + const y = event.clientY - dragStartPos.current.y; + + const newPos = { + x: Math.min( + Math.max(x, pageSizeRef.current[0] / 2), + window.innerWidth - pageSizeRef.current[0] / 2, + ), + y: Math.min( + Math.max(y, pageSizeRef.current[1] / 2), + window.innerHeight - pageSizeRef.current[1] / 2, + ), + }; + + setPosition(newPos); + const localPosition = getPopoverPosition(); + + setPopoverPosition({ + ...localPosition, + [expand ? 'expand' : 'normal']: newPos, + }); + } + }; + + const handleMouseUp = () => { + setIsDragging(false); + }; + + document.addEventListener('mousemove', handleMouseMove, true); + document.addEventListener('mouseup', handleMouseUp); + return () => { + document.removeEventListener('mousemove', handleMouseMove, true); + document.removeEventListener('mouseup', handleMouseUp); + + }; + }, [isDragging, expand]); + + const handleUpdateNodeData = useCallback((id: string, data: QuickNoteEditorData[]) => { + setNoteList(prev => { + return prev.map(note => { + if (note.id === id) { + return { + ...note, + data, + last_updated_at: new Date().toISOString(), + }; + } + + return note; + }).sort((a, b) => { + return new Date(b.last_updated_at).getTime() - new Date(a.last_updated_at).getTime(); + }); + }); + if (id === currentNote?.id) { + setCurrentNote(prev => { + if (prev) { + return { + ...prev, + data, + last_updated_at: new Date().toISOString(), + }; + } + + return prev; + }); + } + }, [currentNote?.id]); + + const handleToggleExpand = useCallback(() => { + setOpen(false); + setTimeout(() => { + setOpen(true); + setExpand(prev => !prev); + }, 200); + + }, []); + + const handleDeleteNotes = useCallback((notes: QuickNoteType[]) => { + if (!service || !currentWorkspaceId) return; + notes.forEach(note => { + void (async () => { + try { + await service.deleteQuickNote?.(currentWorkspaceId, note.id); + setNoteList(prev => prev.filter(n => n.id !== note.id)); + // eslint-disable-next-line + } catch (e: any) { + console.error(e); + handleOpenToast(e.message); + } + })(); + }); + }, [currentWorkspaceId, handleOpenToast, service]); + + const clearEmptyNotes = useCallback(() => { + + if (!currentNote) return; + + if (currentNote.last_updated_at === currentNote.created_at) { + handleDeleteNotes([currentNote]); + } + + }, [handleDeleteNotes, currentNote]); + + const handleBackList = useCallback(() => { + const search = listParamsRef.current.searchTerm; + + if (search) { + listParamsRef.current = { + offset: 0, + limit: LISI_LIMIT, + searchTerm: '', + }; + void handleSearch(''); + } + + setRoute(QuickNoteRoute.LIST); + clearEmptyNotes(); + }, [handleSearch, clearEmptyNotes]); + + const renderHeader = () => { + if (route === QuickNoteRoute.NOTE && currentNote) { + return ( + + ); + } + + return ; + }; + + const handleScrollList = useCallback((e: React.UIEvent) => { + const target = e.target as HTMLDivElement; + + const hasMore = hasMoreRef.current; + + if (!hasMore) return; + + if (target.scrollHeight - target.scrollTop === target.clientHeight && hasMoreRef.current) { + void handleLoadMore(); + } + }, [handleLoadMore]); + + const handleAddedNote = useCallback((note: QuickNoteType) => { + setNoteList(prev => [note, ...prev]); + }, []); + + return ( + <> + +
{t('quickNote.label')}
+
{modifier}
+ + }> + { + e.currentTarget.blur(); + if (open) { + handleClose(); + return; + } + + void handleOpen(); + }} + > + + +
+ + { + setToastMessage(''); + setOpenToast(false); + }, + open: openToast, + }}> +
+
{renderHeader()}
+
+
+ { + route === QuickNoteRoute.NOTE && currentNote ? + <> + { + handleUpdateNodeData(currentNote.id, data); + }}/> + : { + setNoteList(prev => prev.filter(note => note.id !== id)); + }} + /> + } +
+ + setOpenToast(false)} + message={toastMessage} + anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }} + /> + +
+
+ + ); +} + +export default QuickNote; \ No newline at end of file diff --git a/frontend/appflowy_web_app/src/components/quick-note/index.ts b/frontend/appflowy_web_app/src/components/quick-note/index.ts new file mode 100644 index 0000000000..37bd81a92b --- /dev/null +++ b/frontend/appflowy_web_app/src/components/quick-note/index.ts @@ -0,0 +1 @@ +export * from './QuickNote' \ No newline at end of file diff --git a/frontend/appflowy_web_app/src/components/quick-note/utils.ts b/frontend/appflowy_web_app/src/components/quick-note/utils.ts new file mode 100644 index 0000000000..f1ee831451 --- /dev/null +++ b/frontend/appflowy_web_app/src/components/quick-note/utils.ts @@ -0,0 +1,84 @@ +import { QuickNote } from '@/application/types'; +import dayjs from 'dayjs'; + +export function getTitle(note: QuickNote): string { + let text = ''; + + for (const item of note.data) { + let itemText = ''; + + item.delta.forEach((op) => { + if (typeof op.insert === 'string') { + itemText += op.insert; + } + }); + + if (itemText.length > 0) { + text += itemText; + break; + } + } + + return text; +} + +export function getUpdateTime(note: QuickNote): string { + const date = dayjs(note.last_updated_at); + const today = date.isSame(dayjs(), 'day'); + + if (today) { + return date.format('HH:mm'); + } + + return date.format('MMMM D, YYYY'); +} + +export function getSummary(note: QuickNote): string { + let text = ''; + + let start = false; + + for (const item of note.data) { + let itemText = ''; + + item.delta.forEach((op) => { + if (typeof op.insert === 'string') { + itemText += op.insert; + } + }); + + if (itemText.length > 0) { + if (start) { + text += itemText; + break; + } else { + start = true; + } + } + } + + return text.trim(); +} + +export function setPopoverPosition(position: { + expand: { x: number, y: number } | null, + normal: { x: number, y: number } | null, +}) { + localStorage.setItem('quick_note_popover_position', JSON.stringify(position)); +} + +export function getPopoverPosition(): { + expand: { x: number, y: number } | null, + normal: { x: number, y: number } | null, +} { + const position = localStorage.getItem('quick_note_popover_position'); + + if (position) { + return JSON.parse(position); + } + + return { + expand: null, + normal: null, + }; +} \ No newline at end of file diff --git a/frontend/appflowy_web_app/src/components/view-meta/AddIconCover.tsx b/frontend/appflowy_web_app/src/components/view-meta/AddIconCover.tsx new file mode 100644 index 0000000000..4a0a1b589b --- /dev/null +++ b/frontend/appflowy_web_app/src/components/view-meta/AddIconCover.tsx @@ -0,0 +1,90 @@ +import { ViewIconType } from '@/application/types'; +import ChangeIconPopover from '@/components/_shared/view-icon/ChangeIconPopover'; +import { Button } from '@mui/material'; +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import { ReactComponent as AddIcon } from '@/assets/add_icon.svg'; +import { ReactComponent as AddCover } from '@/assets/add_cover.svg'; + +function AddIconCover({ + hasIcon, + hasCover, + onUpdateIcon, + onAddCover, + iconAnchorEl, + setIconAnchorEl, + maxWidth, + visible, +}: { + visible: boolean; + hasIcon: boolean; + hasCover: boolean; + onUpdateIcon?: (icon: { ty: ViewIconType, value: string }) => void; + onAddCover?: () => void; + iconAnchorEl: HTMLElement | null; + setIconAnchorEl: (el: HTMLElement | null) => void; + maxWidth?: number; +}) { + const { t } = useTranslation(); + + return ( + <> +
+ {!hasIcon && } + {!hasCover && } + +
+ { + setIconAnchorEl(null); + }} + defaultType={'emoji'} + iconEnabled={true} + onSelectIcon={(icon) => { + setIconAnchorEl(null); + if (icon.ty === ViewIconType.Icon) { + onUpdateIcon?.({ + ty: ViewIconType.Icon, + value: JSON.stringify({ + color: icon.color, + groupName: icon.value.split('/')[0], + iconName: icon.value.split('/')[1], + iconContent: icon.content, + }), + }); + return; + } + + onUpdateIcon?.(icon); + }} + removeIcon={() => { + setIconAnchorEl(null); + onUpdateIcon?.({ ty: ViewIconType.Emoji, value: '' }); + }} + /> + + + ); +} + +export default AddIconCover; \ No newline at end of file diff --git a/frontend/appflowy_web_app/src/components/view-meta/CoverColors.tsx b/frontend/appflowy_web_app/src/components/view-meta/CoverColors.tsx new file mode 100644 index 0000000000..4434cd8509 --- /dev/null +++ b/frontend/appflowy_web_app/src/components/view-meta/CoverColors.tsx @@ -0,0 +1,26 @@ +import { IconButton } from '@mui/material'; +import React from 'react'; +import { colorMap } from '@/utils/color'; + +const colors = Object.entries(colorMap); + +function Colors ({ onDone }: { onDone?: (value: string) => void }) { + return ( +
+ {colors.map(([name, value]) => ( + onDone?.(name)} + > +
+ + ))} +
+ ); +} + +export default Colors; diff --git a/frontend/appflowy_web_app/src/components/view-meta/CoverPopover.tsx b/frontend/appflowy_web_app/src/components/view-meta/CoverPopover.tsx new file mode 100644 index 0000000000..62b514e51c --- /dev/null +++ b/frontend/appflowy_web_app/src/components/view-meta/CoverPopover.tsx @@ -0,0 +1,117 @@ +import { CoverType, ViewMetaCover } from '@/application/types'; +import { useAppHandlers, useAppViewId, useOpenModalViewId } from '@/components/app/app.hooks'; +import React, { useMemo } from 'react'; +import { PopoverOrigin, PopoverProps } from '@mui/material/Popover'; +import { EmbedLink, Unsplash, UploadTabs, TabOption, TAB_KEY, UploadImage } from '@/components/_shared/image-upload'; +import { useTranslation } from 'react-i18next'; +import Colors from './CoverColors'; + +const initialOrigin: { + anchorOrigin: PopoverOrigin; + transformOrigin: PopoverOrigin; +} = { + anchorOrigin: { + vertical: 'bottom', + horizontal: 'center', + }, + transformOrigin: { + vertical: -20, + horizontal: 'center', + }, +}; + +function CoverPopover ({ + anchorPosition, + open, + onClose, + onUpdateCover, +}: { + anchorPosition?: PopoverProps['anchorPosition']; + open: boolean; + onClose: () => void; + onUpdateCover?: (cover: ViewMetaCover) => void; +}) { + const { t } = useTranslation(); + const { + uploadFile, + } = useAppHandlers(); + const appViewId = useAppViewId(); + const modalViewId = useOpenModalViewId(); + const viewId = modalViewId || appViewId; + + const tabOptions: TabOption[] = useMemo(() => { + return [ + { + label: t('document.plugins.cover.colors'), + key: TAB_KEY.Colors, + Component: Colors, + onDone: (value: string) => { + onUpdateCover?.({ + type: CoverType.NormalColor, + value, + }); + }, + }, + { + label: t('button.upload'), + key: TAB_KEY.UPLOAD, + Component: UploadImage, + uploadAction: (file: File) => { + if (!viewId || !uploadFile) return Promise.reject(); + return uploadFile(viewId, file); + }, + onDone: (value: string) => { + onUpdateCover?.({ + type: CoverType.CustomImage, + value, + }); + onClose(); + }, + }, + { + label: t('document.imageBlock.embedLink.label'), + key: TAB_KEY.EMBED_LINK, + Component: EmbedLink, + onDone: (value: string) => { + onUpdateCover?.({ + type: CoverType.CustomImage, + value, + }); + onClose(); + }, + }, + { + key: TAB_KEY.UNSPLASH, + label: t('document.imageBlock.unsplash.label'), + Component: Unsplash, + onDone: (value: string) => { + onUpdateCover?.({ + type: CoverType.UpsplashImage, + value, + }); + }, + }, + ]; + }, [onClose, onUpdateCover, t, uploadFile, viewId]); + + return ( + + ); +} + +export default CoverPopover; diff --git a/frontend/appflowy_web_app/src/components/view-meta/TitleEditable.tsx b/frontend/appflowy_web_app/src/components/view-meta/TitleEditable.tsx new file mode 100644 index 0000000000..e9fa6a86f9 --- /dev/null +++ b/frontend/appflowy_web_app/src/components/view-meta/TitleEditable.tsx @@ -0,0 +1,142 @@ +import { debounce } from 'lodash-es'; +import React, { memo, useEffect, useMemo, useRef } from 'react'; +import { useTranslation } from 'react-i18next'; + +const isCursorAtEnd = (el: HTMLDivElement) => { + const selection = window.getSelection(); + + if (!selection) return false; + + const range = selection.getRangeAt(0); + const text = el.textContent || ''; + + return range.startOffset === text.length; +}; + +const getCursorOffset = () => { + const selection = window.getSelection(); + + if (!selection) return 0; + + const range = selection.getRangeAt(0); + + return range.startOffset; +}; + +function TitleEditable({ + viewId, + name, + onUpdateName, + onEnter, +}: { + viewId: string; + name: string; + onUpdateName: (name: string) => void; + onEnter?: (text: string) => void; +}) { + const { t } = useTranslation(); + const debounceUpdateName = useMemo(() => { + return debounce(onUpdateName, 300); + }, [onUpdateName]); + const contentRef = useRef(null); + + useEffect(() => { + if (contentRef.current) { + contentRef.current.textContent = name; + } + // eslint-disable-next-line + }, []); + + useEffect(() => { + if (contentRef.current) { + const activeElement = document.activeElement; + + if (activeElement === contentRef.current) { + return; + } + + contentRef.current.textContent = name; + } + }, [name]); + + const focusedTextbox = () => { + const contentBox = contentRef.current; + + if (!contentBox) return; + + const textbox = document.getElementById(`editor-${viewId}`) as HTMLElement; + + textbox?.focus(); + }; + + useEffect(() => { + const contentBox = contentRef.current; + + if (!contentBox) return; + contentBox.focus(); + if (contentBox.textContent !== '') { + const range = document.createRange(); + const sel = window.getSelection(); + + range.setStart(contentBox.childNodes[0], contentBox.textContent?.length || 0); + range.collapse(true); + sel?.removeAllRanges(); + sel?.addRange(range); + } + }, []); + + return ( +
{ + if (!contentRef.current) return; + debounceUpdateName(contentRef.current.textContent || ''); + if (contentRef.current.innerHTML === '
') { + contentRef.current.innerHTML = ''; + } + }} + onBlur={() => { + if (!contentRef.current) return; + onUpdateName(contentRef.current.textContent || ''); + }} + onKeyDown={(e) => { + if (!contentRef.current) return; + if (e.key === 'Enter' || e.key === 'Escape') { + e.preventDefault(); + if (e.key === 'Enter') { + const offset = getCursorOffset(); + const beforeText = contentRef.current.textContent?.slice(0, offset) || ''; + const afterText = contentRef.current.textContent?.slice(offset) || ''; + + contentRef.current.textContent = beforeText; + onUpdateName(beforeText); + onEnter?.(afterText); + + setTimeout(() => { + focusedTextbox(); + }, 0); + + } else { + onUpdateName(contentRef.current.textContent || ''); + } + } else if (e.key === 'ArrowDown' || (e.key === 'ArrowRight' && isCursorAtEnd(contentRef.current))) { + e.preventDefault(); + focusedTextbox(); + } + }} + /> + + ); +} + +export default memo(TitleEditable); \ No newline at end of file diff --git a/frontend/appflowy_web_app/src/components/view-meta/ViewCover.tsx b/frontend/appflowy_web_app/src/components/view-meta/ViewCover.tsx index 2ec873c46d..baa7ebb427 100644 --- a/frontend/appflowy_web_app/src/components/view-meta/ViewCover.tsx +++ b/frontend/appflowy_web_app/src/components/view-meta/ViewCover.tsx @@ -1,8 +1,17 @@ +import { ViewMetaCover } from '@/application/types'; import ImageRender from '@/components/_shared/image-render/ImageRender'; import { renderColor } from '@/utils/color'; -import React, { useCallback } from 'react'; +import React, { lazy, useCallback, useRef, useState, Suspense } from 'react'; -function ViewCover ({ coverValue, coverType }: { coverValue?: string; coverType?: string }) { +const ViewCoverActions = lazy(() => import('@/components/view-meta/ViewCoverActions')); + +function ViewCover({ coverValue, coverType, onUpdateCover, onRemoveCover, readOnly = true }: { + coverValue?: string; + coverType?: string; + onUpdateCover: (cover: ViewMetaCover) => void; + onRemoveCover: () => void; + readOnly?: boolean +}) { const renderCoverColor = useCallback((color: string) => { return (
{ return ( <> - + ); }, []); + const [showAction, setShowAction] = useState(false); + + const actionRef = useRef(null); + if (!coverType || !coverValue) { return null; } return (
{ + if (readOnly) return; + setShowAction(true); + }} + onMouseLeave={() => { + setShowAction(false); + }} style={{ height: '40vh', }} @@ -35,6 +60,17 @@ function ViewCover ({ coverValue, coverType }: { coverValue?: string; coverType? > {coverType === 'color' && renderCoverColor(coverValue)} {(coverType === 'custom' || coverType === 'built_in') && renderCoverImage(coverValue)} + {!readOnly && + setShowAction(false)} + onUpdateCover={onUpdateCover} + onRemove={onRemoveCover} + /> + + } +
); } diff --git a/frontend/appflowy_web_app/src/components/view-meta/ViewCoverActions.tsx b/frontend/appflowy_web_app/src/components/view-meta/ViewCoverActions.tsx new file mode 100644 index 0000000000..e64e627be1 --- /dev/null +++ b/frontend/appflowy_web_app/src/components/view-meta/ViewCoverActions.tsx @@ -0,0 +1,83 @@ +import { ViewMetaCover } from '@/application/types'; +import { PopoverProps } from '@mui/material/Popover'; +import React, { forwardRef, useState } from 'react'; +import Button from '@mui/material/Button'; +import { useTranslation } from 'react-i18next'; +import { ReactComponent as DeleteIcon } from '@/assets/trash.svg'; +import CoverPopover from '@/components/view-meta/CoverPopover'; + +function ViewCoverActions( + { show, onRemove, onUpdateCover, onClose }: { + show: boolean; + onRemove: () => void; + onUpdateCover: (cover: ViewMetaCover) => void; + onClose: () => void; + }, + ref: React.ForwardedRef, +) { + const { t } = useTranslation(); + const [anchorPosition, setAnchorPosition] = useState(undefined); + const showPopover = Boolean(anchorPosition); + + return ( + <> +
+
+
+ +
+
+ +
+ {showPopover && { + setAnchorPosition(undefined); + onClose(); + } + + } + onUpdateCover={onUpdateCover} + />} + + ); +} + +export default forwardRef(ViewCoverActions); diff --git a/frontend/appflowy_web_app/src/components/view-meta/ViewMetaPreview.tsx b/frontend/appflowy_web_app/src/components/view-meta/ViewMetaPreview.tsx index 4ffcce02b9..14bc935741 100644 --- a/frontend/appflowy_web_app/src/components/view-meta/ViewMetaPreview.tsx +++ b/frontend/appflowy_web_app/src/components/view-meta/ViewMetaPreview.tsx @@ -1,24 +1,36 @@ +import { CoverType, ViewIconType, ViewLayout, ViewMetaCover, ViewMetaIcon, ViewMetaProps } from '@/application/types'; +import { notify } from '@/components/_shared/notify'; +import TitleEditable from '@/components/view-meta/TitleEditable'; import ViewCover from '@/components/view-meta/ViewCover'; -import { isFlagEmoji } from '@/utils/emoji'; -import React, { useMemo } from 'react'; +import React, { lazy, Suspense, useEffect, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; -import { CoverType, ViewLayout, ViewMetaIcon } from '@/application/types'; +import PageIcon from '@/components/_shared/view-icon/PageIcon'; -export interface ViewMetaCover { - type: CoverType; - value: string; -} +const AddIconCover = lazy(() => import('@/components/view-meta/AddIconCover')); -export interface ViewMetaProps { - icon?: ViewMetaIcon; - cover?: ViewMetaCover; - name?: string; - viewId?: string; - layout?: ViewLayout; - visibleViewIds?: string[]; -} +export function ViewMetaPreview({ + icon: iconProp, + cover: coverProp, + name, + extra, + readOnly = true, + viewId, + updatePage, + onEnter, + maxWidth, +}: ViewMetaProps) { + const [iconAnchorEl, setIconAnchorEl] = React.useState(null); + const [cover, setCover] = React.useState(coverProp || null); + const [icon, setIcon] = React.useState(iconProp || null); + + useEffect(() => { + setCover(coverProp || null); + }, [coverProp]); + + useEffect(() => { + setIcon(iconProp || null); + }, [iconProp]); -export function ViewMetaPreview ({ icon, cover, name }: ViewMetaProps) { const coverType = useMemo(() => { if (cover && [CoverType.NormalColor, CoverType.GradientColor].includes(cover.type)) { return 'color'; @@ -34,7 +46,7 @@ export function ViewMetaPreview ({ icon, cover, name }: ViewMetaProps) { }, [cover]); const coverValue = useMemo(() => { - if (coverType === 'built_in') { + if (coverType === CoverType.BuildInImage) { return { 1: '/covers/m_cover_image_1.png', 2: '/covers/m_cover_image_2.png', @@ -49,30 +61,147 @@ export function ViewMetaPreview ({ icon, cover, name }: ViewMetaProps) { }, [coverType, cover?.value]); const { t } = useTranslation(); - const isFlag = useMemo(() => { - return icon ? isFlagEmoji(icon.value) : false; - }, [icon]); + const [isHover, setIsHover] = React.useState(false); + + const handleUpdateIcon = React.useCallback(async (icon: { ty: ViewIconType, value: string }) => { + if (!updatePage || !viewId) return; + setIcon(icon); + try { + await updatePage(viewId, { + icon, + name: name || '', + extra: extra || {}, + }); + // eslint-disable-next-line + } catch (e: any) { + notify.error(e.message); + } + }, [updatePage, viewId, name, extra]); + + const handleUpdateName = React.useCallback(async (newName: string) => { + if (!updatePage || !viewId) return; + try { + if (name === newName) return; + await updatePage(viewId, { + icon: icon || { + ty: ViewIconType.Emoji, + value: '', + }, + name: newName, + extra: extra || {}, + }); + // eslint-disable-next-line + } catch (e: any) { + notify.error(e.message); + } + }, [name, updatePage, viewId, icon, extra]); + + const handleUpdateCover = React.useCallback(async (cover?: { + type: CoverType; + value: string; + }) => { + if (!updatePage || !viewId) return; + setCover(cover ? cover : null); + + try { + await updatePage(viewId, { + icon: icon || { + ty: ViewIconType.Emoji, + value: '', + }, + name: name || '', + extra: { + ...extra, + cover: cover, + }, + }); + // eslint-disable-next-line + } catch (e: any) { + notify.error(e.message); + } + }, [extra, icon, name, updatePage, viewId]); return (
{cover && }
setIsHover(true)} + onMouseLeave={() => setIsHover(false)} + className={'flex mt-2 flex-col relative w-full overflow-hidden'} > -

- {icon?.value ?
{icon?.value}
: null} +
+ {!readOnly && { + void handleUpdateCover({ + type: CoverType.BuildInImage, + value: '1', + }); + }} + maxWidth={maxWidth} + iconAnchorEl={iconAnchorEl} + setIconAnchorEl={setIconAnchorEl} + />} -
- {name || {t('menuAppHeader.defaultNewPageName')}} -
-

+
+
+

+ + {icon?.value ? +
{ + if (readOnly) return; + setIconAnchorEl(e.currentTarget); + }} + className={`view-icon flex h-[1.25em] px-1.5 items-center justify-center ${readOnly ? 'cursor-default' : 'cursor-pointer hover:bg-fill-list-hover '}`} + > + +
+ : null + } + {!readOnly && viewId ? : +
+ {name} +
+ } +

+
); diff --git a/frontend/appflowy_web_app/src/components/view-meta/index.ts b/frontend/appflowy_web_app/src/components/view-meta/index.ts index 9272c11393..42f0f39ce3 100644 --- a/frontend/appflowy_web_app/src/components/view-meta/index.ts +++ b/frontend/appflowy_web_app/src/components/view-meta/index.ts @@ -1 +1,2 @@ export * from './ViewMetaPreview'; + diff --git a/frontend/appflowy_web_app/src/i18n/config.ts b/frontend/appflowy_web_app/src/i18n/config.ts index b2a116e0b6..a8fee3c781 100644 --- a/frontend/appflowy_web_app/src/i18n/config.ts +++ b/frontend/appflowy_web_app/src/i18n/config.ts @@ -12,4 +12,10 @@ void i18next defaultNS: 'translation', debug: false, fallbackLng: 'en', + cache: { + enabled: true, + prefix: `i18next_translation_`, + }, }); + +export const i18nInstance = i18next; diff --git a/frontend/appflowy_web_app/src/pages/AcceptInvitationPage.tsx b/frontend/appflowy_web_app/src/pages/AcceptInvitationPage.tsx index 5300a3c54c..f33e488ac1 100644 --- a/frontend/appflowy_web_app/src/pages/AcceptInvitationPage.tsx +++ b/frontend/appflowy_web_app/src/pages/AcceptInvitationPage.tsx @@ -4,15 +4,13 @@ import ChangeAccount from '@/components/_shared/modal/ChangeAccount'; import { notify } from '@/components/_shared/notify'; import { getAvatar } from '@/components/_shared/view-icon/utils'; import { AFConfigContext, useCurrentUser, useService } from '@/components/main/app.hooks'; -import { openOrDownload } from '@/utils/open_schema'; -import { openAppFlowySchema } from '@/utils/url'; import { EmailOutlined } from '@mui/icons-material'; import { Avatar, Button, Divider } from '@mui/material'; import React, { useCallback, useContext, useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useNavigate, useSearchParams } from 'react-router-dom'; -function AcceptInvitationPage () { +function AcceptInvitationPage() { const isAuthenticated = useContext(AFConfigContext)?.isAuthenticated; const currentUser = useCurrentUser(); const navigate = useNavigate(); @@ -58,6 +56,9 @@ function AcceptInvitationPage () { name: invitation.workspace_name, }); }, [invitation]); + const url = useMemo(() => { + return window.location.href; + }, []); const inviterIconProps = useMemo(() => { if (!invitation) return {}; @@ -78,7 +79,7 @@ function AcceptInvitationPage () { }} className={'flex w-full cursor-pointer max-md:justify-center max-md:h-32 h-20 items-center justify-between sticky'} > - +
AppFlowy
- +
- + {currentUser?.email}
@@ -142,7 +143,9 @@ function AcceptInvitationPage () { okText: t('invitation.openWorkspace'), onOk: () => { - openOrDownload(openAppFlowySchema + '#workspace_id=' + invitation?.workspace_id); + const origin = window.location.origin; + + window.open(`${origin}/app/${invitation?.workspace_id}`, '_current'); }, }); @@ -154,7 +157,7 @@ function AcceptInvitationPage () { {t('invitation.joinWorkspace')}
- + {isAuthenticated && }
); } diff --git a/frontend/appflowy_web_app/src/pages/AppPage.tsx b/frontend/appflowy_web_app/src/pages/AppPage.tsx index 0bc32ee304..b3916c3205 100644 --- a/frontend/appflowy_web_app/src/pages/AppPage.tsx +++ b/frontend/appflowy_web_app/src/pages/AppPage.tsx @@ -1,22 +1,25 @@ -import { AppendBreadcrumb, CreateRowDoc, LoadView, LoadViewMeta, ViewLayout, YDoc } from '@/application/types'; +import { UIVariant, ViewComponentProps, ViewLayout, ViewMetaProps, YDoc } from '@/application/types'; import Help from '@/components/_shared/help/Help'; import { findView } from '@/components/_shared/outline/utils'; -import CalendarSkeleton from '@/components/_shared/skeleton/CalendarSkeleton'; -import DocumentSkeleton from '@/components/_shared/skeleton/DocumentSkeleton'; -import GridSkeleton from '@/components/_shared/skeleton/GridSkeleton'; -import KanbanSkeleton from '@/components/_shared/skeleton/KanbanSkeleton'; -import { AppContext, useAppHandlers, useAppOutline, useAppViewId } from '@/components/app/app.hooks'; + +import { + AppContext, + useAppHandlers, + useAppOutline, + useAppViewId, +} from '@/components/app/app.hooks'; import DatabaseView from '@/components/app/DatabaseView'; import { Document } from '@/components/document'; import RecordNotFound from '@/components/error/RecordNotFound'; -import { ViewMetaProps } from '@/components/view-meta'; +import { getPlatform } from '@/utils/platform'; import React, { lazy, memo, Suspense, useCallback, useContext, useEffect, useMemo } from 'react'; const ViewHelmet = lazy(() => import('@/components/_shared/helmet/ViewHelmet')); -function AppPage () { +function AppPage() { const viewId = useAppViewId(); const outline = useAppOutline(); + const ref = React.useRef(null); const { toView, loadViewMeta, @@ -24,6 +27,13 @@ function AppPage () { loadView, appendBreadcrumb, onRendered, + updatePage, + addPage, + deletePage, + openPageModal, + loadViews, + setWordCount, + uploadFile, } = useAppHandlers(); const view = useMemo(() => { if (!outline || !viewId) return; @@ -40,16 +50,12 @@ function AppPage () { const [doc, setDoc] = React.useState(undefined); const [notFound, setNotFound] = React.useState(false); - const loadPageDoc = useCallback(async () => { - - if (!viewId) { - return; - } + const loadPageDoc = useCallback(async (id: string) => { setNotFound(false); setDoc(undefined); try { - const doc = await loadView(viewId); + const doc = await loadView(id); setDoc(doc); } catch (e) { @@ -57,11 +63,13 @@ function AppPage () { console.error(e); } - }, [loadView, viewId]); + }, [loadView]); useEffect(() => { - void loadPageDoc(); - }, [loadPageDoc]); + if (!viewId) return; + + void loadPageDoc(viewId); + }, [loadPageDoc, viewId]); const View = useMemo(() => { switch (view?.layout) { @@ -74,17 +82,7 @@ function AppPage () { default: return null; } - }, [view?.layout]) as React.FC<{ - doc: YDoc; - readOnly: boolean; - navigateToView?: (viewId: string, blockId?: string) => Promise; - loadViewMeta?: LoadViewMeta; - createRowDoc?: CreateRowDoc; - loadView?: LoadView; - viewMeta: ViewMetaProps; - appendBreadcrumb?: AppendBreadcrumb; - onRendered?: () => void; - }>; + }, [view?.layout]) as React.FC; const viewMeta: ViewMetaProps | null = useMemo(() => { return view ? { @@ -94,35 +92,25 @@ function AppPage () { layout: view.layout, visibleViewIds: [], viewId: view.view_id, + extra: view.extra, } : null; }, [view]); - const skeleton = useMemo(() => { - if (!viewMeta) { - return null; + const handleUploadFile = useCallback((file: File) => { + if (view && uploadFile) { + return uploadFile(view.view_id, file); } - switch (viewMeta.layout) { - case ViewLayout.Document: - return ; - case ViewLayout.Grid: - return ; - case ViewLayout.Board: - return ; - case ViewLayout.Calendar: - return ; - default: - return null; - } - - }, [viewMeta]); + return Promise.reject(); + }, [uploadFile, view]); const viewDom = useMemo(() => { + const isMobile = getPlatform().isMobile; return doc && viewMeta && View ? ( - ) : skeleton; - }, [onRendered, doc, viewMeta, View, toView, loadViewMeta, createRowDoc, appendBreadcrumb, loadView, skeleton]); + ) : null; + }, [addPage, handleUploadFile, loadViews, setWordCount, openPageModal, deletePage, updatePage, onRendered, doc, viewMeta, View, toView, loadViewMeta, createRowDoc, appendBreadcrumb, loadView]); useEffect(() => { if (!View || !viewId || !doc) return; @@ -141,17 +137,17 @@ function AppPage () { if (!viewId) return null; return ( -
+
{helmet} {notFound ? ( - + ) : (
{viewDom}
)} - {view && doc && } + {view && doc && }
); diff --git a/frontend/appflowy_web_app/src/pages/TrashPage.tsx b/frontend/appflowy_web_app/src/pages/TrashPage.tsx index 3ad3a31c8e..08af09f9d0 100644 --- a/frontend/appflowy_web_app/src/pages/TrashPage.tsx +++ b/frontend/appflowy_web_app/src/pages/TrashPage.tsx @@ -1,21 +1,56 @@ +import { View } from '@/application/types'; +import { NormalModal } from '@/components/_shared/modal'; import { notify } from '@/components/_shared/notify'; import TableSkeleton from '@/components/_shared/skeleton/TableSkeleton'; -import { useAppTrash, useCurrentWorkspaceId } from '@/components/app/app.hooks'; -import { TableContainer } from '@mui/material'; +import { useAppHandlers, useAppTrash, useCurrentWorkspaceId } from '@/components/app/app.hooks'; +import { Button, IconButton, TableContainer, Tooltip } from '@mui/material'; import dayjs from 'dayjs'; -import React, { useEffect, useMemo } from 'react'; +import React, { useCallback, useEffect, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import Table from '@mui/material/Table'; import TableBody from '@mui/material/TableBody'; import TableCell from '@mui/material/TableCell'; import TableHead from '@mui/material/TableHead'; import TableRow from '@mui/material/TableRow'; +import { ReactComponent as TrashIcon } from '@/assets/trash.svg'; +import { ReactComponent as RestoreIcon } from '@/assets/restore.svg'; -function TrashPage () { +function TrashPage() { const { t } = useTranslation(); const currentWorkspaceId = useCurrentWorkspaceId(); const { trashList, loadTrash } = useAppTrash(); + const [deleteViewId, setDeleteViewId] = React.useState(undefined); + const deleteView = useMemo(() => { + return trashList?.find((view) => view.view_id === deleteViewId); + }, [deleteViewId, trashList]); + const { + deleteTrash, + restorePage, + } = useAppHandlers(); + + const handleRestore = useCallback(async (viewId?: string) => { + if (!currentWorkspaceId) return; + try { + await restorePage?.(viewId); + void loadTrash?.(currentWorkspaceId); + // eslint-disable-next-line + } catch (e: any) { + notify.error(`Failed to restore page: ${e.message}`); + } + }, [restorePage, loadTrash, currentWorkspaceId]); + + const handleDelete = useCallback(async (viewId?: string) => { + if (!currentWorkspaceId) return; + try { + await deleteTrash?.(viewId); + setDeleteViewId(undefined); + void loadTrash?.(currentWorkspaceId); + // eslint-disable-next-line + } catch (e: any) { + notify.error(`Failed to delete page: ${e.message}`); + } + }, [deleteTrash, loadTrash, currentWorkspaceId]); useEffect(() => { void (async () => { @@ -33,26 +68,105 @@ function TrashPage () { { id: 'name', label: t('trash.pageHeader.fileName'), minWidth: 170 }, { id: 'last_edited_time', label: t('trash.pageHeader.lastModified'), minWidth: 170 }, { id: 'created_at', label: t('trash.pageHeader.created'), minWidth: 170 }, - + { id: 'actions', label: '', minWidth: 170 }, ]; }, [t]); + const renderCell = useCallback((column: typeof columns[0], row: View) => { + // eslint-disable-next-line + // @ts-ignore + const value = row[column.id]; + let content = null; + + if (column.id === 'actions') { + content =
+ + { + void handleRestore(row.view_id); + } + } + > + + + + + { + setDeleteViewId(row.view_id); + }} + className={'hover:text-function-error'} + > + + + +
; + } else if (column.id === 'created_at' || column.id === 'last_edited_time') { + content =
{dayjs(value).format('MMM D, YYYY h:mm A')}
; + } else { + content =
+ + {value || t('menuAppHeader.defaultNewPageName')} + +
; + } + + return ( + + {content} + + ); + }, [handleRestore, t]); + return (
-
+
{t('trash.text')} + {trashList?.length ?
+ + +
: null} +
- {!trashList ? : - - + {!trashList ? : + +
{columns.map((column) => ( @@ -71,17 +185,15 @@ function TrashPage () { {trashList .map((row) => { return ( - + {columns.map((column) => { - // eslint-disable-next-line - // @ts-ignore - const value = row[column.id]; - - return ( - - {column.id === 'created_at' || column.id === 'last_edited_time' ? dayjs(value).format('MM/DD/YYYY hh:mm A') : value} - - ); + return renderCell(column, row); })} ); @@ -93,6 +205,28 @@ function TrashPage () { + setDeleteViewId(undefined)} + title={ +
{`${t('button.delete')}: ${deleteView?.name || t('menuAppHeader.defaultNewPageName')}`}
+ } + onOk={() => { + void handleDelete(deleteViewId === 'all' ? undefined : deleteViewId); + }} + PaperProps={{ + className: 'w-[420px] max-w-[70vw]', + }} + > +
{deleteViewId === 'all' ? t('trash.confirmDeleteAll.caption') : t('trash.confirmDeleteTitle')}
+ +
); } diff --git a/frontend/appflowy_web_app/src/slate-editor.d.ts b/frontend/appflowy_web_app/src/slate-editor.d.ts index b967276057..f621d81c86 100644 --- a/frontend/appflowy_web_app/src/slate-editor.d.ts +++ b/frontend/appflowy_web_app/src/slate-editor.d.ts @@ -17,6 +17,7 @@ interface EditorInlineAttributes { type: string; // inline page ref id page_id?: string; + block_id?: string; // reminder date ref id date?: string; reminder_id?: string; diff --git a/frontend/appflowy_web_app/src/styles/app.scss b/frontend/appflowy_web_app/src/styles/app.scss index bae4980db3..2258fa25d2 100644 --- a/frontend/appflowy_web_app/src/styles/app.scss +++ b/frontend/appflowy_web_app/src/styles/app.scss @@ -15,6 +15,10 @@ border-color: var(--line-divider) !important; } +.custom-caret { + @apply caret-fill-default; +} + .sketch-picker [id^='rc-editable-input'] { background-color: var(--bg-body) !important; border-color: var(--line-divider) !important; @@ -73,8 +77,9 @@ body { } .view-icon { - @apply flex w-fit cursor-pointer rounded-lg text-[1.5em]; + @apply flex w-fit cursor-pointer rounded-lg; line-height: 1em; + font-size: 1.25em; white-space: nowrap; } @@ -167,3 +172,11 @@ body { scroll-margin-top: 60px; } +.board-card { + border-radius: 6px; + border: 1px solid var(--line-card); + background: var(--bg-body); + + box-shadow: 0px 2px 8px 2px rgba(31, 35, 41, 0.02), 0px 2px 4px 0px rgba(31, 35, 41, 0.02), 0px 1px 2px -2px rgba(31, 35, 41, 0.02); +} + diff --git a/frontend/appflowy_web_app/src/styles/variables/dark.variables.css b/frontend/appflowy_web_app/src/styles/variables/dark.variables.css index 67ac292421..907cf60a7f 100644 --- a/frontend/appflowy_web_app/src/styles/variables/dark.variables.css +++ b/frontend/appflowy_web_app/src/styles/variables/dark.variables.css @@ -14,6 +14,7 @@ --line-border: #59647a; --line-divider: #384967; --line-on-toolbar: #99a6b8; + --line-card: #384967; --fill-default: #00bcf0; --fill-hover: #005174; --fill-toolbar: #0F111C; @@ -76,4 +77,7 @@ --gradient7: linear-gradient(38.2deg, #F0C6CF 0%, #DECCE2 40.4754%, #CAD3F9 100%); --bg-header: #1a202ccc; --bg-footer: #00000000; + --note-header: #232b38; + --billing-primary: #601DAA; + --billing-primary-hover: #7A2EBF; } \ No newline at end of file diff --git a/frontend/appflowy_web_app/src/styles/variables/light.variables.css b/frontend/appflowy_web_app/src/styles/variables/light.variables.css index 3d1bc63969..8ff71b01aa 100644 --- a/frontend/appflowy_web_app/src/styles/variables/light.variables.css +++ b/frontend/appflowy_web_app/src/styles/variables/light.variables.css @@ -15,6 +15,7 @@ --line-border: #bdbdbd; --line-divider: #1F232923; --line-on-toolbar: #4f4f4f; + --line-card: #1F23291E; --fill-toolbar: #333333; --fill-default: #00bcf0; --fill-hover: #52d1f4; @@ -34,7 +35,7 @@ --content-on-fill: #ffffff; --content-on-tag: #4f4f4f; --bg-body: #ffffff; - --bg-base: #f9fafd; + --bg-base: #F7F8FC; --bg-mask: #0000008C; --bg-tips: #e0f8ff; --bg-brand: #2c144b; @@ -78,4 +79,7 @@ --gradient7: linear-gradient(38.2deg, #F0C6CF 0%, #DECCE2 40.4754%, #CAD3F9 100%); --bg-header: #FFFFFFCC; --bg-footer: #FFFFFFCC; + --note-header: #EDEFF3; + --billing-primary: #8427e0; + --billing-primary-hover: #9f3ae6; } \ No newline at end of file diff --git a/frontend/appflowy_web_app/src/utils/color.ts b/frontend/appflowy_web_app/src/utils/color.ts index 4b706fbf8b..54f4b1a985 100644 --- a/frontend/appflowy_web_app/src/utils/color.ts +++ b/frontend/appflowy_web_app/src/utils/color.ts @@ -161,3 +161,22 @@ export function stringAvatar (name: string, colorArray: string[] = colorDefaultA children: `${name.split('')[0]}`, }; } + +export const IconColors = [ + '0xFFA34AFD', + '0xFFFB006D', + '0xFF00C8FF', + '0xFFFFBA00', + '0xFFF254BC', + '0xFF2AC985', + '0xFFAAD93D', + '0xFF535CE4', + '0xFF808080', + '0xFFD2515F', + '0xFF409BF8', + '0xFFFF8933', +]; + +export function randomColor (colors: string[]): string { + return colors[Math.floor(Math.random() * colors.length)]; +} diff --git a/frontend/appflowy_web_app/src/utils/emoji.ts b/frontend/appflowy_web_app/src/utils/emoji.ts index 4eee13aab2..78282093aa 100644 --- a/frontend/appflowy_web_app/src/utils/emoji.ts +++ b/frontend/appflowy_web_app/src/utils/emoji.ts @@ -37,6 +37,14 @@ export enum ICON_CATEGORY { work_education = 'work_education', } +let icons: Record | undefined; + export async function loadIcons(): Promise< Record< ICON_CATEGORY, @@ -48,7 +56,14 @@ export async function loadIcons(): Promise< }[] > > { - return axios.get('/af_icons/icons.json').then((res) => res.data); + if (icons) { + return icons; + } + + return axios.get('/af_icons/icons.json').then((res) => { + icons = res.data; + return res.data; + }); } export async function getIconSvgEncodedContent(id: string, color: string) { @@ -63,3 +78,26 @@ export async function getIconSvgEncodedContent(id: string, color: string) { return null; } } + +export async function randomIcon() { + const icons = await loadIcons(); + const categories = Object.keys(icons); + const randomCategory = categories[Math.floor(Math.random() * categories.length)] as ICON_CATEGORY; + const randomIcon = icons[randomCategory][Math.floor(Math.random() * icons[randomCategory].length)]; + + return randomIcon; +} + +export async function getIcon(id: string) { + const icons = await loadIcons(); + + for (const category of Object.keys(icons)) { + for (const icon of icons[category as ICON_CATEGORY]) { + if (icon.id === id) { + return icon; + } + } + } + + return null; +} diff --git a/frontend/appflowy_web_app/src/utils/file.ts b/frontend/appflowy_web_app/src/utils/file.ts new file mode 100644 index 0000000000..6f3d8dfad1 --- /dev/null +++ b/frontend/appflowy_web_app/src/utils/file.ts @@ -0,0 +1,225 @@ +// Types for file storage +interface StoredFileData { + id: string; + file: File; + name: string; + type: string; + size: number; + lastModified: number; + timestamp: number; +} + +// Main class for IndexedDB file storage operations +class FileStorage { + private dbName: string; + private storeName: string; + private db: IDBDatabase | null; + + constructor(dbName: string = 'FileStorage', storeName: string = 'files') { + this.dbName = dbName; + this.storeName = storeName; + this.db = null; + } + + // Initialize IndexedDB connection + async init(): Promise { + if (this.db) return; + + return new Promise((resolve, reject) => { + const request = indexedDB.open(this.dbName, 1); + + request.onerror = () => reject(request.error); + request.onsuccess = () => { + this.db = request.result; + resolve(); + }; + + request.onupgradeneeded = (event: IDBVersionChangeEvent) => { + const db = (event.target as IDBOpenDBRequest).result; + + if (!db.objectStoreNames.contains(this.storeName)) { + db.createObjectStore(this.storeName, { keyPath: 'id' }); + } + }; + }); + } + + // Save file to IndexedDB + async saveFile(file: File, customId?: string): Promise { + await this.init(); + if (!this.db) throw new Error('Database not initialized'); + + const id = customId || crypto.randomUUID(); + const fileData: StoredFileData = { + id, + file, + name: file.name, + type: file.type, + size: file.size, + lastModified: file.lastModified, + timestamp: Date.now(), + }; + + return new Promise((resolve, reject) => { + const transaction = this.db!.transaction(this.storeName, 'readwrite'); + const store = transaction.objectStore(this.storeName); + const request = store.put(fileData); + + request.onsuccess = () => resolve(id); + request.onerror = () => reject(request.error); + }); + } + + // Retrieve file from IndexedDB by ID + async getFile(id: string): Promise { + await this.init(); + if (!this.db) throw new Error('Database not initialized'); + + return new Promise((resolve, reject) => { + const transaction = this.db!.transaction(this.storeName, 'readonly'); + const store = transaction.objectStore(this.storeName); + const request = store.get(id); + + request.onsuccess = () => resolve(request.result || null); + request.onerror = () => reject(request.error); + }); + } + + // Delete file from IndexedDB + async deleteFile(id: string): Promise { + await this.init(); + if (!this.db) throw new Error('Database not initialized'); + + return new Promise((resolve, reject) => { + const transaction = this.db!.transaction(this.storeName, 'readwrite'); + const store = transaction.objectStore(this.storeName); + const request = store.delete(id); + + request.onsuccess = () => resolve(); + request.onerror = () => reject(request.error); + }); + } + + // Get all stored files + async getAllFiles(): Promise { + await this.init(); + if (!this.db) throw new Error('Database not initialized'); + + return new Promise((resolve, reject) => { + const transaction = this.db!.transaction(this.storeName, 'readonly'); + const store = transaction.objectStore(this.storeName); + const request = store.getAll(); + + request.onsuccess = () => resolve(request.result); + request.onerror = () => reject(request.error); + }); + } + + // Clear all stored files + async clear(): Promise { + await this.init(); + if (!this.db) throw new Error('Database not initialized'); + + return new Promise((resolve, reject) => { + const transaction = this.db!.transaction(this.storeName, 'readwrite'); + const store = transaction.objectStore(this.storeName); + const request = store.clear(); + + request.onsuccess = () => resolve(); + request.onerror = () => reject(request.error); + }); + } +} + +// Interface for file handling results +interface FileHandlingResult { + id: string; + url: string; + name: string; + type: string; + size: number; +} + +// Main class for handling file operations including URL management +export class FileHandler { + private storage: FileStorage; + private fileUrls: Map; + + constructor() { + this.storage = new FileStorage(); + this.fileUrls = new Map(); + } + + // Handle file upload and create object URL + async handleFileUpload(file: File): Promise { + try { + const fileId = await this.storage.saveFile(file); + const url = URL.createObjectURL(file); + + this.fileUrls.set(fileId, url); + + return { + id: fileId, + url, + name: file.name, + type: file.type, + size: file.size, + }; + } catch (error) { + console.error('Error handling file upload:', error); + throw error; + } + } + + // Retrieve stored file and manage its object URL + async getStoredFile(id: string): Promise<(StoredFileData & { url: string }) | null> { + try { + const fileData = await this.storage.getFile(id); + + if (!fileData) return null; + + // Return existing URL if available + if (this.fileUrls.has(id)) { + return { + ...fileData, + url: this.fileUrls.get(id)!, + }; + } + + // Create new URL if needed + const url = URL.createObjectURL(fileData.file); + + this.fileUrls.set(id, url); + + return { + ...fileData, + url, + }; + } catch (error) { + console.error('Error getting stored file:', error); + throw error; + } + } + + // Clean up single file and its resources + async cleanup(id: string): Promise { + if (this.fileUrls.has(id)) { + URL.revokeObjectURL(this.fileUrls.get(id)!); + this.fileUrls.delete(id); + } + + await this.storage.deleteFile(id); + } + + // Clean up all files and resources + async cleanupAll(): Promise { + for (const url of this.fileUrls.values()) { + URL.revokeObjectURL(url); + } + + this.fileUrls.clear(); + await this.storage.clear(); + } +} + +export const MAX_FILE_SIZE = 7 * 1024 * 1024; diff --git a/frontend/appflowy_web_app/src/utils/hotkeys.ts b/frontend/appflowy_web_app/src/utils/hotkeys.ts index 957e6386b2..23c0f3e6ad 100644 --- a/frontend/appflowy_web_app/src/utils/hotkeys.ts +++ b/frontend/appflowy_web_app/src/utils/hotkeys.ts @@ -14,6 +14,7 @@ export const getModifier = () => { }; export enum HOT_KEY_NAME { + ENTER = 'enter', CLEAR_CACHE = 'clear-cache', UP = 'up', DOWN = 'down', @@ -49,11 +50,18 @@ export enum HOT_KEY_NAME { SCROLL_TO_BOTTOM = 'scroll-to-bottom', FORMAT_LINK = 'format-link', FIND_REPLACE = 'find-replace', + POP_EMOJI_PICKER = 'pop-emoji-picker', + DELETE_LEFT_SENTENCE = 'delete-left-sentence', + DELETE_LEFT_WORD = 'delete-left-word', + DELETE_RIGHT_WORD = 'delete-right-word', + MOVE_CURSOR_TO_BOTTOM = 'move-cursor-to-bottom', + MOVE_CURSOR_TO_TOP = 'move-cursor-to-top', /** * Navigation */ TOGGLE_THEME = 'toggle-theme', TOGGLE_SIDEBAR = 'toggle-sidebar', + QUICK_NOTE = 'quick-note', } const defaultHotKeys = { @@ -83,7 +91,7 @@ const defaultHotKeys = { [HOT_KEY_NAME.HIGH_LIGHT]: ['mod+shift+h'], [HOT_KEY_NAME.EXTEND_DOCUMENT_BACKWARD]: ['mod+shift+up'], [HOT_KEY_NAME.EXTEND_DOCUMENT_FORWARD]: ['mod+shift+down'], - [HOT_KEY_NAME.SCROLL_TO_TOP]: ['home'], + [HOT_KEY_NAME.SCROLL_TO_TOP]: ['Home'], [HOT_KEY_NAME.SCROLL_TO_BOTTOM]: ['end'], [HOT_KEY_NAME.TOGGLE_THEME]: ['mod+shift+l'], [HOT_KEY_NAME.TOGGLE_SIDEBAR]: ['mod+.'], @@ -94,6 +102,14 @@ const defaultHotKeys = { [HOT_KEY_NAME.DOWN]: ['down'], [HOT_KEY_NAME.FIND_REPLACE]: ['mod+f'], [HOT_KEY_NAME.CLEAR_CACHE]: ['mod+shift+r'], + [HOT_KEY_NAME.POP_EMOJI_PICKER]: ['mod+alt+e'], + [HOT_KEY_NAME.DELETE_LEFT_SENTENCE]: ['mod+alt+backspace'], + [HOT_KEY_NAME.DELETE_LEFT_WORD]: ['mod+backspace'], + [HOT_KEY_NAME.DELETE_RIGHT_WORD]: ['mod+delete'], + [HOT_KEY_NAME.MOVE_CURSOR_TO_BOTTOM]: ['mod+down'], + [HOT_KEY_NAME.MOVE_CURSOR_TO_TOP]: ['mod+up'], + [HOT_KEY_NAME.ENTER]: ['enter'], + [HOT_KEY_NAME.QUICK_NOTE]: ['mod+/'], }; const replaceModifier = (hotkey: string) => { @@ -110,6 +126,7 @@ export const createHotkey = (hotkeyName: HOT_KEY_NAME, customHotKeys?: Record { return hotkeys.some((hotkey) => { return isHotkey(hotkey, event); diff --git a/frontend/appflowy_web_app/src/utils/open_schema.ts b/frontend/appflowy_web_app/src/utils/open_schema.ts index 25879338f4..5a376105b3 100644 --- a/frontend/appflowy_web_app/src/utils/open_schema.ts +++ b/frontend/appflowy_web_app/src/utils/open_schema.ts @@ -101,15 +101,16 @@ export const openAppOrDownload = (config: AppConfig): void => { }; }; -export function openOnly (schema?: string) { +export function openOnly(schema?: string) { return openAppOrDownload({ appScheme: schema || openAppFlowySchema, }); } -export function openOrDownload (schema?: string) { +export function openOrDownload(schema?: string) { const os = getOS(); + const downloadUrl = os === 'ios' ? iosDownloadLink : os === 'android' ? androidDownloadLink : desktopDownloadLink; return openAppOrDownload({ diff --git a/frontend/appflowy_web_app/src/utils/platform.ts b/frontend/appflowy_web_app/src/utils/platform.ts index fc3a538750..c164280247 100644 --- a/frontend/appflowy_web_app/src/utils/platform.ts +++ b/frontend/appflowy_web_app/src/utils/platform.ts @@ -1,6 +1,6 @@ -export function getPlatform() { +export function getPlatform () { return { isTauri: !!import.meta.env.TAURI_PLATFORM, - isMobile: window.navigator.userAgent.match(/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i), + isMobile: /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent), }; } diff --git a/frontend/appflowy_web_app/src/utils/url.ts b/frontend/appflowy_web_app/src/utils/url.ts index 1a026d63f2..8f452cde7a 100644 --- a/frontend/appflowy_web_app/src/utils/url.ts +++ b/frontend/appflowy_web_app/src/utils/url.ts @@ -12,13 +12,13 @@ export const androidDownloadLink = 'https://play.google.com/store/apps/details?i export const desktopDownloadLink = 'https://appflowy.io/download/#pop'; -export function isValidUrl (input: string) { +export function isValidUrl(input: string) { return isURL(input, { require_protocol: true, require_host: false }); } // Process the URL to make sure it's a valid URL // If it's not a valid URL(eg: 'appflowy.io' or '192.168.1.2'), we'll add 'https://' to the URL -export function processUrl (input: string) { +export function processUrl(input: string) { let processedUrl = input; if (isValidUrl(input)) { @@ -45,7 +45,7 @@ export function processUrl (input: string) { return; } -export async function openUrl (url: string, target: string = '_current') { +export async function openUrl(url: string, target: string = '_current') { const platform = getPlatform(); const newUrl = processUrl(url); diff --git a/frontend/appflowy_web_app/src/utils/word.ts b/frontend/appflowy_web_app/src/utils/word.ts new file mode 100644 index 0000000000..b4ef67a4ce --- /dev/null +++ b/frontend/appflowy_web_app/src/utils/word.ts @@ -0,0 +1,26 @@ +import { Element, Node, Text } from 'slate'; +import { TextCount } from '@/application/types'; + +export function getTextCount(nodes: Node[]): TextCount { + let text = ''; + + const getAllText = (node: Node): void => { + if (Text.isText(node)) { + if (node.formula) { + text += node.formula; + } else { + text += node.text; + } + } else if (Element.isElement(node)) { + text += '\n'; + node.children.forEach(getAllText); + } + }; + + nodes.forEach(getAllText); + + return { + characters: text.replace(/\s/g, '').length, + words: text.trim().split(/\s+/).filter(word => word.length > 0).length, + }; +} diff --git a/frontend/appflowy_web_app/src/vite-env.d.ts b/frontend/appflowy_web_app/src/vite-env.d.ts index 8ecd467d45..cb7faf05b8 100644 --- a/frontend/appflowy_web_app/src/vite-env.d.ts +++ b/frontend/appflowy_web_app/src/vite-env.d.ts @@ -11,13 +11,17 @@ interface Window { load: (options: { google: { families: string[] } }) => void; }; toast: { - success: (message: string) => void; - error: (message: string) => void; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + success: (message: any) => void; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + error: (message: any) => void; // eslint-disable-next-line @typescript-eslint/no-explicit-any info: (props: any) => void; clear: () => void; - default: (message: string) => void; - warning: (message: string) => void; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + default: (message: any) => void; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + warning: (message: any) => void; }; Prism: { diff --git a/frontend/appflowy_web_app/tailwind/box-shadow.cjs b/frontend/appflowy_web_app/tailwind/box-shadow.cjs index 13984c1d12..510e8f3987 100644 --- a/frontend/appflowy_web_app/tailwind/box-shadow.cjs +++ b/frontend/appflowy_web_app/tailwind/box-shadow.cjs @@ -1,7 +1,7 @@ /** * Do not edit directly -* Generated on Tue, 22 Oct 2024 09:59:50 GMT +* Generated on Tue, 24 Dec 2024 08:57:39 GMT * Generated from $pnpm css:variables */ diff --git a/frontend/appflowy_web_app/tailwind/colors.cjs b/frontend/appflowy_web_app/tailwind/colors.cjs index 15c4fbe9a5..71c17594aa 100644 --- a/frontend/appflowy_web_app/tailwind/colors.cjs +++ b/frontend/appflowy_web_app/tailwind/colors.cjs @@ -1,7 +1,7 @@ /** * Do not edit directly -* Generated on Tue, 22 Oct 2024 09:59:50 GMT +* Generated on Tue, 24 Dec 2024 08:57:39 GMT * Generated from $pnpm css:variables */ @@ -26,7 +26,8 @@ module.exports = { "line": { "border": "var(--line-border)", "divider": "var(--line-divider)", - "on-toolbar": "var(--line-on-toolbar)" + "on-toolbar": "var(--line-on-toolbar)", + "card": "var(--line-card)" }, "fill": { "toolbar": "var(--fill-toolbar)", @@ -93,5 +94,12 @@ module.exports = { "scrollbar": { "thumb": "var(--scrollbar-thumb)", "track": "var(--scrollbar-track)" + }, + "note": { + "header": "var(--note-header)" + }, + "billing": { + "primary": "var(--billing-primary)", + "primary-hover": "var(--billing-primary-hover)" } }; diff --git a/frontend/appflowy_web_app/vite.config.ts b/frontend/appflowy_web_app/vite.config.ts index 22c7170bc3..1cc79429e5 100644 --- a/frontend/appflowy_web_app/vite.config.ts +++ b/frontend/appflowy_web_app/vite.config.ts @@ -132,11 +132,12 @@ export default defineConfig({ sourcemap: isDev, rollupOptions: isProd ? { + output: { chunkFileNames: 'static/js/[name]-[hash].js', entryFileNames: 'static/js/[name]-[hash].js', assetFileNames: 'static/[ext]/[name]-[hash].[ext]', - manualChunks (id) { + manualChunks(id) { if ( // id.includes('/react@') || // id.includes('/react-dom@') || @@ -151,7 +152,8 @@ export default defineConfig({ id.includes('/react-virtualized-auto-sizer') || id.includes('/react-window') || id.includes('/@popperjs') - || id.includes('/@mui/material/Dialog') + || id.includes('/@mui/material/Dialog') || + id.includes('/quill-delta') ) { return 'common'; } @@ -176,6 +178,6 @@ export default defineConfig({ }, optimizeDeps: { - include: ['react', 'react-dom', 'react-katex'], + include: ['react', 'react-dom', 'react-katex', '@appflowyinc/editor'], }, }); diff --git a/frontend/resources/translations/en.json b/frontend/resources/translations/en.json index 57f4897bbe..284d912d6f 100644 --- a/frontend/resources/translations/en.json +++ b/frontend/resources/translations/en.json @@ -145,7 +145,11 @@ "charCount": "Character count: {}", "createdAt": "Created: {}", "deleteView": "Delete", - "duplicateView": "Duplicate" + "duplicateView": "Duplicate", + "wordCountLabel": "Word count: ", + "charCountLabel": "Character count: ", + "createdAtLabel": "Created: ", + "syncedAtLabel": "Synced: " }, "importPanel": { "textAndMarkdown": "Text & Markdown", @@ -166,7 +170,9 @@ "addToFavorites": "Add to Favorites", "copyLink": "Copy link", "changeIcon": "Change icon", - "collapseAllPages": "Collapse all subpages" + "collapseAllPages": "Collapse all subpages", + "movePageTo": "Move page to", + "move": "Move" }, "blankPageTitle": "Blank page", "newPageText": "New page", @@ -423,7 +429,10 @@ "backToHome": "Back to Home", "viewing": "Viewing", "editing": "Editing", - "gotIt": "Got it" + "gotIt": "Got it", + "retry": "Retry", + "uploadFailed": "Upload failed.", + "copyLinkOriginal": "Copy link to original" }, "label": { "welcome": "Welcome!", @@ -1621,6 +1630,7 @@ "timeHintTextInTwelveHour": "01:00 PM", "timeHintTextInTwentyFourHour": "13:00" }, + "creating": "Creating...", "slashMenu": { "board": { "selectABoardToLinkTo": "Select a Board to link to", @@ -1718,6 +1728,7 @@ "toggleList": "Toggle list", "emptyToggleHeading": "Empty toggle h{}. Click to add content.", "emptyToggleList": "Empty toggle list. Click to add content.", + "emptyToggleHeadingWeb": "Empty toggle h{level}. Click to add content", "quoteList": "Quote list", "numberedList": "Numbered list", "bulletedList": "Bulleted list", @@ -2009,7 +2020,8 @@ "deletedContent": "This content does not exist or has been deleted", "noAccess": "No Access", "deletedPage": "Deleted page", - "trashHint": " - in trash" + "trashHint": " - in trash", + "morePages": "more pages" }, "toolbar": { "resetToDefaultFont": "Reset to default" @@ -2367,6 +2379,9 @@ "mobileHeading1": "Heading 1", "mobileHeading2": "Heading 2", "mobileHeading3": "Heading 3", + "mobileHeading4": "Heading 4", + "mobileHeading5": "Heading 5", + "mobileHeading6": "Heading 6", "textColor": "Text Color", "backgroundColor": "Background Color", "addYourLink": "Add your link", @@ -2661,14 +2676,19 @@ "fileBlock": { "uploadedAt": "Uploaded on {time}", "linkedAt": "Link added on {time}", - "empty": "Upload or embed a file" + "empty": "Upload or embed a file", + "uploadFailed": "Upload failed, please try again", + "retry": "Retry" }, "importNotion": "Import from Notion", "import": "Import", "importSuccess": "Uploaded successfully", "importSuccessMessage": "We'll notify you when the import is complete. After that, you can view your imported pages in the sidebar.", "importFailed": "Import failed, please check the file format", - "dropNotionFile": "Drop your Notion zip file here to upload, or click to browse" + "dropNotionFile": "Drop your Notion zip file here to upload, or click to browse", + "error": { + "pageNameIsEmpty": "The page name is empty, please try another one" + } }, "globalComment": { "comments": "Comments", @@ -2927,5 +2947,103 @@ "noAppToOpenFile": "No app to open this file", "permissionDenied": "No permission to open this file", "unknownError": "File open failed" + }, + "inviteMember": { + "requestInviteMembers": "Invite to your workspace", + "inviteFailedMemberLimit": "Member limit has been reached, please ", + "upgrade": "upgrade", + "addEmail": "email@example.com, email2@example.com...", + "requestInvites": "Send invites", + "inviteAlready": "You've already invited this email: {email}", + "inviteSuccess": "Invitation sent successfully", + "description": "Input emails below with commas between them. Charges are based on member count.", + "emails": "Email" + }, + "quickNote": { + "label": "Quick note", + "quickNotes": "Quick notes", + "search": "Search quick notes", + "collapseFullView": "Collapse full view", + "expandFullView": "Expand full view", + "createFailed": "Failed to create quick note", + "quickNotesEmpty": "No quick notes", + "emptyNote": "Empty note", + "deleteNotePrompt": "The selected note will be deleted permanently. Are you sure you want to delete it?", + "addNote": "New note", + "noAdditionalText": "No additional text" + }, + "subscribe": { + "upgradePlanTitle": "Compare & select plan", + "yearly": "Yearly", + "save": "Save {discount}%", + "monthly": "Monthly", + "priceIn": "Price in ", + "free": "Free", + "pro": "Pro", + "freeDescription": "For individuals up to 2 members to organize everything", + "proDescription": "For small teams to manage projects and team knowledge", + "proDuration": { + "monthly": "per member per month\nbilled monthly", + "yearly": "per member per month\nbilled annually" + }, + "cancel": "Downgrade", + "changePlan": "Upgrade to Pro Plan", + "everythingInFree": "Everything in Free +", + "currentPlan": "Current", + "freeDuration": "forever", + "freePoints": { + "first": "1 collaborative workspace up to 2 members", + "second": "Unlimited pages & blocks", + "three": "5 GB storage", + "four": "Intelligent search", + "five": "20 AI responses", + "six": "Mobile app", + "seven": "Real-time collaboration" + }, + "proPoints": { + "first": "Unlimited storage", + "second": "Up to 10 workspace members", + "three": "Unlimited AI responses", + "four": "Unlimited file uploads", + "five": "Custom namespace" + }, + "cancelPlan": { + "title": "Sorry to see you go", + "success": "Your subscription has been canceled successfully", + "description": "We're sorry to see you go. We'd love to hear your feedback to help us improve AppFlowy. Please take a moment to answer a few questions.", + "commonOther": "Other", + "otherHint": "Write your answer here", + "questionOne": { + "question": "What prompted you to cancel your AppFlowy Pro subscription?", + "answerOne": "Cost too high", + "answerTwo": "Features did not meet expectations", + "answerThree": "Found a better alternative", + "answerFour": "Did not use it enough to justify the expense", + "answerFive": "Service issue or technical difficulties" + }, + "questionTwo": { + "question": "How likely are you to consider re-subscribing to AppFlowy Pro in the future?", + "answerOne": "Very likely", + "answerTwo": "Somewhat likely", + "answerThree": "Not sure", + "answerFour": "Unlikely", + "answerFive": "Very unlikely" + }, + "questionThree": { + "question": "Which Pro feature did you value the most during your subscription?", + "answerOne": "Multi-user collaboration", + "answerTwo": "Longer time version history", + "answerThree": "Unlimited AI responses", + "answerFour": "Access to local AI models" + }, + "questionFour": { + "question": "How would you describe your overall experience with AppFlowy?", + "answerOne": "Great", + "answerTwo": "Good", + "answerThree": "Average", + "answerFour": "Below average", + "answerFive": "Unsatisfied" + } + } } } From a521541cb7f02ab1a3d151b5d901cebe117f2b55 Mon Sep 17 00:00:00 2001 From: "Nathan.fooo" <86001920+appflowy@users.noreply.github.com> Date: Mon, 30 Dec 2024 10:26:06 +0800 Subject: [PATCH 087/576] chore: only sync when doucment was changed (#7081) * chore: only sync when doucment was changed * chore: fmt --- frontend/appflowy_tauri/src-tauri/Cargo.lock | 20 +- frontend/appflowy_tauri/src-tauri/Cargo.toml | 16 +- .../appflowy_web_app/src-tauri/Cargo.lock | 20 +- .../appflowy_web_app/src-tauri/Cargo.toml | 16 +- frontend/rust-lib/Cargo.lock | 20 +- frontend/rust-lib/Cargo.toml | 16 +- frontend/rust-lib/collab-integrate/Cargo.toml | 3 + frontend/rust-lib/collab-integrate/src/lib.rs | 17 +- .../collab-integrate/src/native/mod.rs | 1 - .../src/native/plugin_provider.rs | 57 --- .../src/persistence/collab_metadata_sql.rs | 55 +++ .../collab-integrate/src/persistence/mod.rs | 1 + .../src/{wasm => }/plugin_provider.rs | 11 +- .../rust-lib/collab-integrate/src/wasm/mod.rs | 1 - frontend/rust-lib/flowy-ai/Cargo.toml | 1 + frontend/rust-lib/flowy-ai/src/ai_manager.rs | 61 ++- frontend/rust-lib/flowy-core/src/config.rs | 2 +- .../flowy-core/src/deps_resolve/chat_deps.rs | 80 ++- .../folder_deps/folder_deps_chat_impl.rs | 110 ++--- .../folder_deps/folder_deps_database_impl.rs | 462 +++++++++--------- .../folder_deps/folder_deps_doc_impl.rs | 268 +++++----- .../src/deps_resolve/folder_deps/mod.rs | 344 ++++++------- .../flowy-core/src/deps_resolve/mod.rs | 6 +- frontend/rust-lib/flowy-core/src/lib.rs | 10 +- frontend/rust-lib/flowy-document/Cargo.toml | 1 - frontend/rust-lib/flowy-error/Cargo.toml | 2 +- .../down.sql | 2 + .../2024-12-29-061706_collab_metadata/up.sql | 7 + frontend/rust-lib/flowy-sqlite/src/schema.rs | 10 + .../flowy-user/src/user_manager/manager.rs | 2 +- 30 files changed, 854 insertions(+), 768 deletions(-) delete mode 100644 frontend/rust-lib/collab-integrate/src/native/mod.rs delete mode 100644 frontend/rust-lib/collab-integrate/src/native/plugin_provider.rs create mode 100644 frontend/rust-lib/collab-integrate/src/persistence/collab_metadata_sql.rs create mode 100644 frontend/rust-lib/collab-integrate/src/persistence/mod.rs rename frontend/rust-lib/collab-integrate/src/{wasm => }/plugin_provider.rs (78%) delete mode 100644 frontend/rust-lib/collab-integrate/src/wasm/mod.rs create mode 100644 frontend/rust-lib/flowy-sqlite/migrations/2024-12-29-061706_collab_metadata/down.sql create mode 100644 frontend/rust-lib/flowy-sqlite/migrations/2024-12-29-061706_collab_metadata/up.sql diff --git a/frontend/appflowy_tauri/src-tauri/Cargo.lock b/frontend/appflowy_tauri/src-tauri/Cargo.lock index ad7462c11c..4b4d22625e 100644 --- a/frontend/appflowy_tauri/src-tauri/Cargo.lock +++ b/frontend/appflowy_tauri/src-tauri/Cargo.lock @@ -1031,7 +1031,7 @@ dependencies = [ [[package]] name = "collab" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=dd8a1d13e97322338f027888a71340575cd9ad8f#dd8a1d13e97322338f027888a71340575cd9ad8f" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=67b31e4bcc64d81332c37efecb0a8618681f1db9#67b31e4bcc64d81332c37efecb0a8618681f1db9" dependencies = [ "anyhow", "arc-swap", @@ -1056,7 +1056,7 @@ dependencies = [ [[package]] name = "collab-database" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=dd8a1d13e97322338f027888a71340575cd9ad8f#dd8a1d13e97322338f027888a71340575cd9ad8f" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=67b31e4bcc64d81332c37efecb0a8618681f1db9#67b31e4bcc64d81332c37efecb0a8618681f1db9" dependencies = [ "anyhow", "async-trait", @@ -1096,7 +1096,7 @@ dependencies = [ [[package]] name = "collab-document" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=dd8a1d13e97322338f027888a71340575cd9ad8f#dd8a1d13e97322338f027888a71340575cd9ad8f" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=67b31e4bcc64d81332c37efecb0a8618681f1db9#67b31e4bcc64d81332c37efecb0a8618681f1db9" dependencies = [ "anyhow", "arc-swap", @@ -1117,7 +1117,7 @@ dependencies = [ [[package]] name = "collab-entity" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=dd8a1d13e97322338f027888a71340575cd9ad8f#dd8a1d13e97322338f027888a71340575cd9ad8f" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=67b31e4bcc64d81332c37efecb0a8618681f1db9#67b31e4bcc64d81332c37efecb0a8618681f1db9" dependencies = [ "anyhow", "bytes", @@ -1137,7 +1137,7 @@ dependencies = [ [[package]] name = "collab-folder" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=dd8a1d13e97322338f027888a71340575cd9ad8f#dd8a1d13e97322338f027888a71340575cd9ad8f" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=67b31e4bcc64d81332c37efecb0a8618681f1db9#67b31e4bcc64d81332c37efecb0a8618681f1db9" dependencies = [ "anyhow", "arc-swap", @@ -1159,7 +1159,7 @@ dependencies = [ [[package]] name = "collab-importer" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=dd8a1d13e97322338f027888a71340575cd9ad8f#dd8a1d13e97322338f027888a71340575cd9ad8f" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=67b31e4bcc64d81332c37efecb0a8618681f1db9#67b31e4bcc64d81332c37efecb0a8618681f1db9" dependencies = [ "anyhow", "async-recursion", @@ -1209,6 +1209,9 @@ dependencies = [ "collab-folder", "collab-plugins", "collab-user", + "diesel", + "flowy-error", + "flowy-sqlite", "futures", "lib-infra", "serde", @@ -1220,7 +1223,7 @@ dependencies = [ [[package]] name = "collab-plugins" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=dd8a1d13e97322338f027888a71340575cd9ad8f#dd8a1d13e97322338f027888a71340575cd9ad8f" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=67b31e4bcc64d81332c37efecb0a8618681f1db9#67b31e4bcc64d81332c37efecb0a8618681f1db9" dependencies = [ "anyhow", "async-stream", @@ -1300,7 +1303,7 @@ dependencies = [ [[package]] name = "collab-user" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=dd8a1d13e97322338f027888a71340575cd9ad8f#dd8a1d13e97322338f027888a71340575cd9ad8f" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=67b31e4bcc64d81332c37efecb0a8618681f1db9#67b31e4bcc64d81332c37efecb0a8618681f1db9" dependencies = [ "anyhow", "collab", @@ -2144,6 +2147,7 @@ dependencies = [ "arc-swap", "base64 0.21.5", "bytes", + "collab-integrate", "dashmap 6.0.1", "flowy-ai-pub", "flowy-codegen", diff --git a/frontend/appflowy_tauri/src-tauri/Cargo.toml b/frontend/appflowy_tauri/src-tauri/Cargo.toml index 982c06a2e9..196a93699a 100644 --- a/frontend/appflowy_tauri/src-tauri/Cargo.toml +++ b/frontend/appflowy_tauri/src-tauri/Cargo.toml @@ -120,14 +120,14 @@ custom-protocol = ["tauri/custom-protocol"] # To switch to the local path, run: # scripts/tool/update_collab_source.sh # ⚠️⚠️⚠️️ -collab = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "dd8a1d13e97322338f027888a71340575cd9ad8f" } -collab-entity = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "dd8a1d13e97322338f027888a71340575cd9ad8f" } -collab-folder = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "dd8a1d13e97322338f027888a71340575cd9ad8f" } -collab-document = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "dd8a1d13e97322338f027888a71340575cd9ad8f" } -collab-database = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "dd8a1d13e97322338f027888a71340575cd9ad8f" } -collab-plugins = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "dd8a1d13e97322338f027888a71340575cd9ad8f" } -collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "dd8a1d13e97322338f027888a71340575cd9ad8f" } -collab-importer = { version = "0.1", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "dd8a1d13e97322338f027888a71340575cd9ad8f" } +collab = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "67b31e4bcc64d81332c37efecb0a8618681f1db9" } +collab-entity = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "67b31e4bcc64d81332c37efecb0a8618681f1db9" } +collab-folder = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "67b31e4bcc64d81332c37efecb0a8618681f1db9" } +collab-document = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "67b31e4bcc64d81332c37efecb0a8618681f1db9" } +collab-database = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "67b31e4bcc64d81332c37efecb0a8618681f1db9" } +collab-plugins = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "67b31e4bcc64d81332c37efecb0a8618681f1db9" } +collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "67b31e4bcc64d81332c37efecb0a8618681f1db9" } +collab-importer = { version = "0.1", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "67b31e4bcc64d81332c37efecb0a8618681f1db9" } # Working directory: frontend # To update the commit ID, run: diff --git a/frontend/appflowy_web_app/src-tauri/Cargo.lock b/frontend/appflowy_web_app/src-tauri/Cargo.lock index ed8976cffe..ea71138635 100644 --- a/frontend/appflowy_web_app/src-tauri/Cargo.lock +++ b/frontend/appflowy_web_app/src-tauri/Cargo.lock @@ -1029,7 +1029,7 @@ dependencies = [ [[package]] name = "collab" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=dd8a1d13e97322338f027888a71340575cd9ad8f#dd8a1d13e97322338f027888a71340575cd9ad8f" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=67b31e4bcc64d81332c37efecb0a8618681f1db9#67b31e4bcc64d81332c37efecb0a8618681f1db9" dependencies = [ "anyhow", "arc-swap", @@ -1054,7 +1054,7 @@ dependencies = [ [[package]] name = "collab-database" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=dd8a1d13e97322338f027888a71340575cd9ad8f#dd8a1d13e97322338f027888a71340575cd9ad8f" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=67b31e4bcc64d81332c37efecb0a8618681f1db9#67b31e4bcc64d81332c37efecb0a8618681f1db9" dependencies = [ "anyhow", "async-trait", @@ -1094,7 +1094,7 @@ dependencies = [ [[package]] name = "collab-document" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=dd8a1d13e97322338f027888a71340575cd9ad8f#dd8a1d13e97322338f027888a71340575cd9ad8f" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=67b31e4bcc64d81332c37efecb0a8618681f1db9#67b31e4bcc64d81332c37efecb0a8618681f1db9" dependencies = [ "anyhow", "arc-swap", @@ -1115,7 +1115,7 @@ dependencies = [ [[package]] name = "collab-entity" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=dd8a1d13e97322338f027888a71340575cd9ad8f#dd8a1d13e97322338f027888a71340575cd9ad8f" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=67b31e4bcc64d81332c37efecb0a8618681f1db9#67b31e4bcc64d81332c37efecb0a8618681f1db9" dependencies = [ "anyhow", "bytes", @@ -1135,7 +1135,7 @@ dependencies = [ [[package]] name = "collab-folder" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=dd8a1d13e97322338f027888a71340575cd9ad8f#dd8a1d13e97322338f027888a71340575cd9ad8f" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=67b31e4bcc64d81332c37efecb0a8618681f1db9#67b31e4bcc64d81332c37efecb0a8618681f1db9" dependencies = [ "anyhow", "arc-swap", @@ -1157,7 +1157,7 @@ dependencies = [ [[package]] name = "collab-importer" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=dd8a1d13e97322338f027888a71340575cd9ad8f#dd8a1d13e97322338f027888a71340575cd9ad8f" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=67b31e4bcc64d81332c37efecb0a8618681f1db9#67b31e4bcc64d81332c37efecb0a8618681f1db9" dependencies = [ "anyhow", "async-recursion", @@ -1207,6 +1207,9 @@ dependencies = [ "collab-folder", "collab-plugins", "collab-user", + "diesel", + "flowy-error", + "flowy-sqlite", "futures", "lib-infra", "serde", @@ -1218,7 +1221,7 @@ dependencies = [ [[package]] name = "collab-plugins" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=dd8a1d13e97322338f027888a71340575cd9ad8f#dd8a1d13e97322338f027888a71340575cd9ad8f" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=67b31e4bcc64d81332c37efecb0a8618681f1db9#67b31e4bcc64d81332c37efecb0a8618681f1db9" dependencies = [ "anyhow", "async-stream", @@ -1298,7 +1301,7 @@ dependencies = [ [[package]] name = "collab-user" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=dd8a1d13e97322338f027888a71340575cd9ad8f#dd8a1d13e97322338f027888a71340575cd9ad8f" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=67b31e4bcc64d81332c37efecb0a8618681f1db9#67b31e4bcc64d81332c37efecb0a8618681f1db9" dependencies = [ "anyhow", "collab", @@ -2189,6 +2192,7 @@ dependencies = [ "arc-swap", "base64 0.21.7", "bytes", + "collab-integrate", "dashmap 6.0.1", "flowy-ai-pub", "flowy-codegen", diff --git a/frontend/appflowy_web_app/src-tauri/Cargo.toml b/frontend/appflowy_web_app/src-tauri/Cargo.toml index da3ac7fd72..56fa4bc799 100644 --- a/frontend/appflowy_web_app/src-tauri/Cargo.toml +++ b/frontend/appflowy_web_app/src-tauri/Cargo.toml @@ -118,14 +118,14 @@ custom-protocol = ["tauri/custom-protocol"] # To switch to the local path, run: # scripts/tool/update_collab_source.sh # ⚠️⚠️⚠️️ -collab = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "dd8a1d13e97322338f027888a71340575cd9ad8f" } -collab-entity = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "dd8a1d13e97322338f027888a71340575cd9ad8f" } -collab-folder = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "dd8a1d13e97322338f027888a71340575cd9ad8f" } -collab-document = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "dd8a1d13e97322338f027888a71340575cd9ad8f" } -collab-database = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "dd8a1d13e97322338f027888a71340575cd9ad8f" } -collab-plugins = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "dd8a1d13e97322338f027888a71340575cd9ad8f" } -collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "dd8a1d13e97322338f027888a71340575cd9ad8f" } -collab-importer = { version = "0.1", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "dd8a1d13e97322338f027888a71340575cd9ad8f" } +collab = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "67b31e4bcc64d81332c37efecb0a8618681f1db9" } +collab-entity = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "67b31e4bcc64d81332c37efecb0a8618681f1db9" } +collab-folder = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "67b31e4bcc64d81332c37efecb0a8618681f1db9" } +collab-document = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "67b31e4bcc64d81332c37efecb0a8618681f1db9" } +collab-database = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "67b31e4bcc64d81332c37efecb0a8618681f1db9" } +collab-plugins = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "67b31e4bcc64d81332c37efecb0a8618681f1db9" } +collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "67b31e4bcc64d81332c37efecb0a8618681f1db9" } +collab-importer = { version = "0.1", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "67b31e4bcc64d81332c37efecb0a8618681f1db9" } # Working directory: frontend diff --git a/frontend/rust-lib/Cargo.lock b/frontend/rust-lib/Cargo.lock index 7be5520f1c..54282cd279 100644 --- a/frontend/rust-lib/Cargo.lock +++ b/frontend/rust-lib/Cargo.lock @@ -892,7 +892,7 @@ dependencies = [ [[package]] name = "collab" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=dd8a1d13e97322338f027888a71340575cd9ad8f#dd8a1d13e97322338f027888a71340575cd9ad8f" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=67b31e4bcc64d81332c37efecb0a8618681f1db9#67b31e4bcc64d81332c37efecb0a8618681f1db9" dependencies = [ "anyhow", "arc-swap", @@ -917,7 +917,7 @@ dependencies = [ [[package]] name = "collab-database" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=dd8a1d13e97322338f027888a71340575cd9ad8f#dd8a1d13e97322338f027888a71340575cd9ad8f" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=67b31e4bcc64d81332c37efecb0a8618681f1db9#67b31e4bcc64d81332c37efecb0a8618681f1db9" dependencies = [ "anyhow", "async-trait", @@ -957,7 +957,7 @@ dependencies = [ [[package]] name = "collab-document" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=dd8a1d13e97322338f027888a71340575cd9ad8f#dd8a1d13e97322338f027888a71340575cd9ad8f" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=67b31e4bcc64d81332c37efecb0a8618681f1db9#67b31e4bcc64d81332c37efecb0a8618681f1db9" dependencies = [ "anyhow", "arc-swap", @@ -978,7 +978,7 @@ dependencies = [ [[package]] name = "collab-entity" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=dd8a1d13e97322338f027888a71340575cd9ad8f#dd8a1d13e97322338f027888a71340575cd9ad8f" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=67b31e4bcc64d81332c37efecb0a8618681f1db9#67b31e4bcc64d81332c37efecb0a8618681f1db9" dependencies = [ "anyhow", "bytes", @@ -998,7 +998,7 @@ dependencies = [ [[package]] name = "collab-folder" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=dd8a1d13e97322338f027888a71340575cd9ad8f#dd8a1d13e97322338f027888a71340575cd9ad8f" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=67b31e4bcc64d81332c37efecb0a8618681f1db9#67b31e4bcc64d81332c37efecb0a8618681f1db9" dependencies = [ "anyhow", "arc-swap", @@ -1020,7 +1020,7 @@ dependencies = [ [[package]] name = "collab-importer" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=dd8a1d13e97322338f027888a71340575cd9ad8f#dd8a1d13e97322338f027888a71340575cd9ad8f" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=67b31e4bcc64d81332c37efecb0a8618681f1db9#67b31e4bcc64d81332c37efecb0a8618681f1db9" dependencies = [ "anyhow", "async-recursion", @@ -1070,6 +1070,9 @@ dependencies = [ "collab-folder", "collab-plugins", "collab-user", + "diesel", + "flowy-error", + "flowy-sqlite", "futures", "lib-infra", "serde", @@ -1081,7 +1084,7 @@ dependencies = [ [[package]] name = "collab-plugins" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=dd8a1d13e97322338f027888a71340575cd9ad8f#dd8a1d13e97322338f027888a71340575cd9ad8f" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=67b31e4bcc64d81332c37efecb0a8618681f1db9#67b31e4bcc64d81332c37efecb0a8618681f1db9" dependencies = [ "anyhow", "async-stream", @@ -1161,7 +1164,7 @@ dependencies = [ [[package]] name = "collab-user" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=dd8a1d13e97322338f027888a71340575cd9ad8f#dd8a1d13e97322338f027888a71340575cd9ad8f" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=67b31e4bcc64d81332c37efecb0a8618681f1db9#67b31e4bcc64d81332c37efecb0a8618681f1db9" dependencies = [ "anyhow", "collab", @@ -2001,6 +2004,7 @@ dependencies = [ "arc-swap", "base64 0.21.5", "bytes", + "collab-integrate", "dashmap 6.0.1", "dotenv", "flowy-ai-pub", diff --git a/frontend/rust-lib/Cargo.toml b/frontend/rust-lib/Cargo.toml index 96cc79ece2..1ddcb82c26 100644 --- a/frontend/rust-lib/Cargo.toml +++ b/frontend/rust-lib/Cargo.toml @@ -143,14 +143,14 @@ rocksdb = { git = "https://github.com/rust-rocksdb/rust-rocksdb", rev = "1710120 # To switch to the local path, run: # scripts/tool/update_collab_source.sh # ⚠️⚠️⚠️️ -collab = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "dd8a1d13e97322338f027888a71340575cd9ad8f" } -collab-entity = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "dd8a1d13e97322338f027888a71340575cd9ad8f" } -collab-folder = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "dd8a1d13e97322338f027888a71340575cd9ad8f" } -collab-document = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "dd8a1d13e97322338f027888a71340575cd9ad8f" } -collab-database = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "dd8a1d13e97322338f027888a71340575cd9ad8f" } -collab-plugins = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "dd8a1d13e97322338f027888a71340575cd9ad8f" } -collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "dd8a1d13e97322338f027888a71340575cd9ad8f" } -collab-importer = { version = "0.1", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "dd8a1d13e97322338f027888a71340575cd9ad8f" } +collab = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "67b31e4bcc64d81332c37efecb0a8618681f1db9" } +collab-entity = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "67b31e4bcc64d81332c37efecb0a8618681f1db9" } +collab-folder = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "67b31e4bcc64d81332c37efecb0a8618681f1db9" } +collab-document = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "67b31e4bcc64d81332c37efecb0a8618681f1db9" } +collab-database = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "67b31e4bcc64d81332c37efecb0a8618681f1db9" } +collab-plugins = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "67b31e4bcc64d81332c37efecb0a8618681f1db9" } +collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "67b31e4bcc64d81332c37efecb0a8618681f1db9" } +collab-importer = { version = "0.1", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "67b31e4bcc64d81332c37efecb0a8618681f1db9" } # Working directory: frontend # To update the commit ID, run: diff --git a/frontend/rust-lib/collab-integrate/Cargo.toml b/frontend/rust-lib/collab-integrate/Cargo.toml index 8b0a530b19..ab375c77fc 100644 --- a/frontend/rust-lib/collab-integrate/Cargo.toml +++ b/frontend/rust-lib/collab-integrate/Cargo.toml @@ -24,6 +24,9 @@ tokio = { workspace = true, features = ["sync"] } lib-infra = { workspace = true } futures = "0.3" arc-swap = "1.7" +flowy-sqlite = { workspace = true } +diesel.workspace = true +flowy-error.workspace = true [features] default = [] diff --git a/frontend/rust-lib/collab-integrate/src/lib.rs b/frontend/rust-lib/collab-integrate/src/lib.rs index d24700f8d5..afa6f0c2a8 100644 --- a/frontend/rust-lib/collab-integrate/src/lib.rs +++ b/frontend/rust-lib/collab-integrate/src/lib.rs @@ -1,24 +1,11 @@ pub use collab::preclude::Snapshot; pub use collab_plugins::local_storage::CollabPersistenceConfig; pub use collab_plugins::CollabKVDB; -use collab_plugins::{if_native, if_wasm}; pub mod collab_builder; pub mod config; - -if_native! { - mod native; - mod plugin_provider { - pub use crate::native::plugin_provider::*; - } -} - -if_wasm! { - mod wasm; - mod plugin_provider { - pub use crate::wasm::plugin_provider::*; - } -} +pub mod persistence; +mod plugin_provider; pub use collab_plugins::local_storage::kv::doc::CollabKVAction; pub use collab_plugins::local_storage::kv::error::PersistenceError; diff --git a/frontend/rust-lib/collab-integrate/src/native/mod.rs b/frontend/rust-lib/collab-integrate/src/native/mod.rs deleted file mode 100644 index 59b6b263d5..0000000000 --- a/frontend/rust-lib/collab-integrate/src/native/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod plugin_provider; diff --git a/frontend/rust-lib/collab-integrate/src/native/plugin_provider.rs b/frontend/rust-lib/collab-integrate/src/native/plugin_provider.rs deleted file mode 100644 index b5b3b1f6e6..0000000000 --- a/frontend/rust-lib/collab-integrate/src/native/plugin_provider.rs +++ /dev/null @@ -1,57 +0,0 @@ -use collab::preclude::CollabPlugin; - -use crate::collab_builder::{CollabPluginProviderContext, CollabPluginProviderType}; - -#[cfg(target_arch = "wasm32")] -pub trait CollabCloudPluginProvider: 'static { - fn provider_type(&self) -> CollabPluginProviderType; - - fn get_plugins(&self, context: CollabPluginProviderContext) -> Vec>; - - fn is_sync_enabled(&self) -> bool; -} - -#[cfg(target_arch = "wasm32")] -impl CollabCloudPluginProvider for std::rc::Rc -where - U: CollabCloudPluginProvider, -{ - fn provider_type(&self) -> CollabPluginProviderType { - (**self).provider_type() - } - - fn get_plugins(&self, context: CollabPluginProviderContext) -> Vec> { - (**self).get_plugins(context) - } - - fn is_sync_enabled(&self) -> bool { - (**self).is_sync_enabled() - } -} - -#[cfg(not(target_arch = "wasm32"))] -pub trait CollabCloudPluginProvider: Send + Sync + 'static { - fn provider_type(&self) -> CollabPluginProviderType; - - fn get_plugins(&self, context: CollabPluginProviderContext) -> Vec>; - - fn is_sync_enabled(&self) -> bool; -} - -#[cfg(not(target_arch = "wasm32"))] -impl CollabCloudPluginProvider for std::sync::Arc -where - U: CollabCloudPluginProvider, -{ - fn provider_type(&self) -> CollabPluginProviderType { - (**self).provider_type() - } - - fn get_plugins(&self, context: CollabPluginProviderContext) -> Vec> { - (**self).get_plugins(context) - } - - fn is_sync_enabled(&self) -> bool { - (**self).is_sync_enabled() - } -} diff --git a/frontend/rust-lib/collab-integrate/src/persistence/collab_metadata_sql.rs b/frontend/rust-lib/collab-integrate/src/persistence/collab_metadata_sql.rs new file mode 100644 index 0000000000..82e993fc49 --- /dev/null +++ b/frontend/rust-lib/collab-integrate/src/persistence/collab_metadata_sql.rs @@ -0,0 +1,55 @@ +use diesel::upsert::excluded; +use flowy_error::{FlowyError, FlowyResult}; +use flowy_sqlite::{ + diesel, insert_into, + query_dsl::*, + schema::{af_collab_metadata, af_collab_metadata::dsl}, + DBConnection, ExpressionMethods, Identifiable, Insertable, Queryable, +}; +use std::collections::HashMap; + +#[derive(Queryable, Insertable, Identifiable)] +#[diesel(table_name = af_collab_metadata)] +#[diesel(primary_key(object_id))] +pub struct AFCollabMetadata { + pub object_id: String, + pub updated_at: i64, + pub prev_sync_state_vector: Vec, + pub collab_type: i32, +} + +pub fn batch_insert_collab_metadata( + mut conn: DBConnection, + new_metadata: &[AFCollabMetadata], +) -> FlowyResult<()> { + conn.immediate_transaction(|conn| { + for metadata in new_metadata { + let _ = insert_into(af_collab_metadata::table) + .values(metadata) + .on_conflict(af_collab_metadata::object_id) + .do_update() + .set(( + af_collab_metadata::updated_at.eq(excluded(af_collab_metadata::updated_at)), + af_collab_metadata::prev_sync_state_vector + .eq(excluded(af_collab_metadata::prev_sync_state_vector)), + )) + .execute(conn)?; + } + Ok::<(), FlowyError>(()) + })?; + + Ok(()) +} + +pub fn batch_select_collab_metadata( + mut conn: DBConnection, + object_ids: &[String], +) -> FlowyResult> { + let metadata = dsl::af_collab_metadata + .filter(af_collab_metadata::object_id.eq_any(object_ids)) + .load::(&mut conn)? + .into_iter() + .map(|m| (m.object_id.clone(), m)) + .collect(); + Ok(metadata) +} diff --git a/frontend/rust-lib/collab-integrate/src/persistence/mod.rs b/frontend/rust-lib/collab-integrate/src/persistence/mod.rs new file mode 100644 index 0000000000..dc0eb77c28 --- /dev/null +++ b/frontend/rust-lib/collab-integrate/src/persistence/mod.rs @@ -0,0 +1 @@ +pub mod collab_metadata_sql; diff --git a/frontend/rust-lib/collab-integrate/src/wasm/plugin_provider.rs b/frontend/rust-lib/collab-integrate/src/plugin_provider.rs similarity index 78% rename from frontend/rust-lib/collab-integrate/src/wasm/plugin_provider.rs rename to frontend/rust-lib/collab-integrate/src/plugin_provider.rs index 545e6c461c..fcbcbbe9ee 100644 --- a/frontend/rust-lib/collab-integrate/src/wasm/plugin_provider.rs +++ b/frontend/rust-lib/collab-integrate/src/plugin_provider.rs @@ -1,9 +1,8 @@ -use crate::collab_builder::{CollabPluginProviderContext, CollabPluginProviderType}; use collab::preclude::CollabPlugin; -use lib_infra::future::Fut; -use std::rc::Rc; -pub trait CollabCloudPluginProvider: 'static { +use crate::collab_builder::{CollabPluginProviderContext, CollabPluginProviderType}; + +pub trait CollabCloudPluginProvider: Send + Sync + 'static { fn provider_type(&self) -> CollabPluginProviderType; fn get_plugins(&self, context: CollabPluginProviderContext) -> Vec>; @@ -11,9 +10,9 @@ pub trait CollabCloudPluginProvider: 'static { fn is_sync_enabled(&self) -> bool; } -impl CollabCloudPluginProvider for Rc +impl CollabCloudPluginProvider for std::sync::Arc where - T: CollabCloudPluginProvider, + U: CollabCloudPluginProvider, { fn provider_type(&self) -> CollabPluginProviderType { (**self).provider_type() diff --git a/frontend/rust-lib/collab-integrate/src/wasm/mod.rs b/frontend/rust-lib/collab-integrate/src/wasm/mod.rs deleted file mode 100644 index 59b6b263d5..0000000000 --- a/frontend/rust-lib/collab-integrate/src/wasm/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod plugin_provider; diff --git a/frontend/rust-lib/flowy-ai/Cargo.toml b/frontend/rust-lib/flowy-ai/Cargo.toml index a22484086a..c2f94d509f 100644 --- a/frontend/rust-lib/flowy-ai/Cargo.toml +++ b/frontend/rust-lib/flowy-ai/Cargo.toml @@ -45,6 +45,7 @@ zip = { workspace = true, features = ["deflate"] } zip-extensions = "0.8.0" pin-project = "1.1.5" flowy-storage-pub = { workspace = true } +collab-integrate.workspace = true [target.'cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))'.dependencies] notify = "6.1.1" diff --git a/frontend/rust-lib/flowy-ai/src/ai_manager.rs b/frontend/rust-lib/flowy-ai/src/ai_manager.rs index effa78b58b..a77764fcc9 100644 --- a/frontend/rust-lib/flowy-ai/src/ai_manager.rs +++ b/frontend/rust-lib/flowy-ai/src/ai_manager.rs @@ -5,6 +5,7 @@ use crate::entities::{ use crate::local_ai::local_llm_chat::LocalAIController; use crate::middleware::chat_service_mw::AICloudServiceMiddleware; use crate::persistence::{insert_chat, read_chat_metadata, ChatTable}; +use std::collections::HashMap; use appflowy_plugin::manager::PluginManager; use dashmap::DashMap; @@ -16,6 +17,9 @@ use flowy_sqlite::kv::KVStorePreferences; use flowy_sqlite::DBConnection; use crate::notification::{chat_notification_builder, ChatNotification}; +use collab_integrate::persistence::collab_metadata_sql::{ + batch_insert_collab_metadata, batch_select_collab_metadata, AFCollabMetadata, +}; use flowy_storage_pub::storage::StorageService; use lib_infra::async_trait::async_trait; use lib_infra::util::timestamp; @@ -31,7 +35,6 @@ pub trait AIUserService: Send + Sync + 'static { fn application_root_dir(&self) -> Result; } - /// AIExternalService is an interface for external services that AI plugin can interact with. #[async_trait] pub trait AIExternalService: Send + Sync + 'static { @@ -45,7 +48,8 @@ pub trait AIExternalService: Send + Sync + 'static { &self, workspace_id: &str, rag_ids: Vec, - ) -> Result<(), FlowyError>; + rag_metadata_map: HashMap, + ) -> Result, FlowyError>; async fn notify_did_send_message(&self, chat_id: &str, message: &str) -> Result<(), FlowyError>; } @@ -130,14 +134,7 @@ impl AIManager { .await { Ok(settings) => { - if settings.rag_ids.is_empty() { - return; - } - if let Ok(workspace_id) = user_service.workspace_id() { - let _ = external_service - .sync_rag_documents(&workspace_id, settings.rag_ids) - .await; - } + let _ = sync_chat_documents(user_service, external_service, settings.rag_ids).await; }, Err(err) => { error!("failed to refresh chat settings: {}", err); @@ -167,7 +164,8 @@ impl AIManager { } pub async fn get_chat_info(&self, chat_id: &str) -> FlowyResult { - let mut conn = self.user_service.sqlite_connection(0)?; + let uid = self.user_service.user_id()?; + let mut conn = self.user_service.sqlite_connection(uid)?; let metadata = read_chat_metadata(&mut conn, chat_id)?; let files = metadata .files @@ -397,17 +395,44 @@ impl AIManager { let user_service = self.user_service.clone(); let external_service = self.external_service.clone(); - tokio::spawn(async move { - if let Ok(workspace_id) = user_service.workspace_id() { - let _ = external_service - .sync_rag_documents(&workspace_id, rag_ids) - .await; - } - }); + sync_chat_documents(user_service, external_service, rag_ids).await?; Ok(()) } } +async fn sync_chat_documents( + user_service: Arc, + external_service: Arc, + rag_ids: Vec, +) -> FlowyResult<()> { + if rag_ids.is_empty() { + return Ok(()); + } + + let uid = user_service.user_id()?; + let conn = user_service.sqlite_connection(uid)?; + let metadata_map = batch_select_collab_metadata(conn, &rag_ids)?; + + let user_service = user_service.clone(); + tokio::spawn(async move { + if let Ok(workspace_id) = user_service.workspace_id() { + if let Ok(metadatas) = external_service + .sync_rag_documents(&workspace_id, rag_ids, metadata_map) + .await + { + if let Ok(uid) = user_service.user_id() { + if let Ok(conn) = user_service.sqlite_connection(uid) { + info!("sync rag documents success: {}", metadatas.len()); + batch_insert_collab_metadata(conn, &metadatas).unwrap(); + } + } + } + } + }); + + Ok(()) +} + fn save_chat(conn: DBConnection, chat_id: &str) -> FlowyResult<()> { let row = ChatTable { chat_id: chat_id.to_string(), diff --git a/frontend/rust-lib/flowy-core/src/config.rs b/frontend/rust-lib/flowy-core/src/config.rs index cdc23b5a28..2b379ab63a 100644 --- a/frontend/rust-lib/flowy-core/src/config.rs +++ b/frontend/rust-lib/flowy-core/src/config.rs @@ -5,11 +5,11 @@ use base64::Engine; use semver::Version; use tracing::{error, info}; +use crate::log_filter::create_log_filter; use flowy_server_pub::af_cloud_config::AFCloudConfiguration; use flowy_user::services::entities::URL_SAFE_ENGINE; use lib_infra::file_util::copy_dir_recursive; use lib_infra::util::OperatingSystem; -use crate::log_filter::create_log_filter; #[derive(Clone)] pub struct AppFlowyCoreConfig { diff --git a/frontend/rust-lib/flowy-core/src/deps_resolve/chat_deps.rs b/frontend/rust-lib/flowy-core/src/deps_resolve/chat_deps.rs index 85b9693f05..f9e4befeb0 100644 --- a/frontend/rust-lib/flowy-core/src/deps_resolve/chat_deps.rs +++ b/frontend/rust-lib/flowy-core/src/deps_resolve/chat_deps.rs @@ -1,4 +1,10 @@ +use collab::core::collab::DataSource; +use collab::core::origin::CollabOrigin; +use collab::preclude::updates::decoder::Decode; +use collab::preclude::{Collab, StateVector}; +use collab::util::is_change_since_sv; use collab_entity::CollabType; +use collab_integrate::persistence::collab_metadata_sql::AFCollabMetadata; use flowy_ai::ai_manager::{AIExternalService, AIManager, AIUserService}; use flowy_ai_pub::cloud::ChatCloudService; use flowy_error::FlowyError; @@ -10,6 +16,8 @@ use flowy_sqlite::DBConnection; use flowy_storage_pub::storage::StorageService; use flowy_user::services::authenticate_user::AuthenticateUser; use lib_infra::async_trait::async_trait; +use lib_infra::util::timestamp; +use std::collections::HashMap; use std::path::PathBuf; use std::sync::{Arc, Weak}; use tracing::{error, info}; @@ -62,36 +70,70 @@ impl AIExternalService for ChatQueryServiceImpl { Ok(ids) } - async fn sync_rag_documents( &self, workspace_id: &str, rag_ids: Vec, - ) -> Result<(), FlowyError> { - info!("sync_rag_documents: {:?}", rag_ids); + mut rag_metadata_map: HashMap, + ) -> Result, FlowyError> { + let mut result = Vec::new(); - for rag_id in rag_ids.iter() { - if let Some(query_collab) = self + for rag_id in rag_ids { + // Retrieve the collab object for the current rag_id + let query_collab = match self .folder_service - .get_collab(rag_id, CollabType::Document) + .get_collab(&rag_id, CollabType::Document) .await { - let params = FullSyncCollabParams { - object_id: rag_id.clone(), - collab_type: CollabType::Document, - encoded_collab: query_collab.encoded_collab, - }; - match self - .folder_cloud_service - .full_sync_collab_object(workspace_id, params) - .await - { - Ok(_) => info!("[Chat] full sync rag document: {}", rag_id), - Err(err) => error!("failed to sync rag document:{} error:{}", rag_id, err), + Some(collab) => collab, + None => { + continue; + }, + }; + + // Check if the state vector exists and detect changes + if let Some(metadata) = rag_metadata_map.remove(&rag_id) { + if let Ok(prev_sv) = StateVector::decode_v1(&metadata.prev_sync_state_vector) { + let collab = Collab::new_with_source( + CollabOrigin::Empty, + &rag_id, + DataSource::DocStateV1(query_collab.encoded_collab.doc_state.to_vec()), + vec![], + false, + )?; + + if !is_change_since_sv(&collab, &prev_sv) { + info!("[Chat] no change since sv: {}", rag_id); + continue; + } } } + + // Perform full sync if changes are detected or no state vector is found + let params = FullSyncCollabParams { + object_id: rag_id.clone(), + collab_type: CollabType::Document, + encoded_collab: query_collab.encoded_collab.clone(), + }; + + if let Err(err) = self + .folder_cloud_service + .full_sync_collab_object(workspace_id, params) + .await + { + error!("Failed to sync rag document: {} error: {}", rag_id, err); + } else { + info!("[Chat] full sync rag document: {}", rag_id); + result.push(AFCollabMetadata { + object_id: rag_id, + updated_at: timestamp(), + prev_sync_state_vector: query_collab.encoded_collab.state_vector.to_vec(), + collab_type: CollabType::Document as i32, + }); + } } - Ok(()) + + Ok(result) } async fn notify_did_send_message(&self, chat_id: &str, message: &str) -> Result<(), FlowyError> { diff --git a/frontend/rust-lib/flowy-core/src/deps_resolve/folder_deps/folder_deps_chat_impl.rs b/frontend/rust-lib/flowy-core/src/deps_resolve/folder_deps/folder_deps_chat_impl.rs index 7f230a17f2..40a7657967 100644 --- a/frontend/rust-lib/flowy-core/src/deps_resolve/folder_deps/folder_deps_chat_impl.rs +++ b/frontend/rust-lib/flowy-core/src/deps_resolve/folder_deps/folder_deps_chat_impl.rs @@ -1,4 +1,3 @@ -use std::sync::Arc; use bytes::Bytes; use collab::entity::EncodedCollab; use collab_folder::ViewLayout; @@ -8,71 +7,72 @@ use flowy_folder::entities::CreateViewParams; use flowy_folder::share::ImportType; use flowy_folder::view_operation::{FolderOperationHandler, ImportedData}; use lib_infra::async_trait::async_trait; +use std::sync::Arc; pub struct ChatFolderOperation(pub Arc); #[async_trait] impl FolderOperationHandler for ChatFolderOperation { - fn name(&self) -> &str { - "ChatFolderOperationHandler" - } + fn name(&self) -> &str { + "ChatFolderOperationHandler" + } - async fn open_view(&self, view_id: &str) -> Result<(), FlowyError> { - self.0.open_chat(view_id).await - } + async fn open_view(&self, view_id: &str) -> Result<(), FlowyError> { + self.0.open_chat(view_id).await + } - async fn close_view(&self, view_id: &str) -> Result<(), FlowyError> { - self.0.close_chat(view_id).await - } + async fn close_view(&self, view_id: &str) -> Result<(), FlowyError> { + self.0.close_chat(view_id).await + } - async fn delete_view(&self, view_id: &str) -> Result<(), FlowyError> { - self.0.delete_chat(view_id).await - } + async fn delete_view(&self, view_id: &str) -> Result<(), FlowyError> { + self.0.delete_chat(view_id).await + } - async fn duplicate_view(&self, _view_id: &str) -> Result { - Err(FlowyError::not_support()) - } + async fn duplicate_view(&self, _view_id: &str) -> Result { + Err(FlowyError::not_support()) + } - async fn create_view_with_view_data( - &self, - _user_id: i64, - _params: CreateViewParams, - ) -> Result, FlowyError> { - Err(FlowyError::not_support()) - } + async fn create_view_with_view_data( + &self, + _user_id: i64, + _params: CreateViewParams, + ) -> Result, FlowyError> { + Err(FlowyError::not_support()) + } - async fn create_default_view( - &self, - user_id: i64, - parent_view_id: &str, - view_id: &str, - _name: &str, - _layout: ViewLayout, - ) -> Result<(), FlowyError> { - self - .0 - .create_chat(&user_id, parent_view_id, view_id) - .await?; - Ok(()) - } + async fn create_default_view( + &self, + user_id: i64, + parent_view_id: &str, + view_id: &str, + _name: &str, + _layout: ViewLayout, + ) -> Result<(), FlowyError> { + self + .0 + .create_chat(&user_id, parent_view_id, view_id) + .await?; + Ok(()) + } - async fn import_from_bytes( - &self, - _uid: i64, - _view_id: &str, - _name: &str, - _import_type: ImportType, - _bytes: Vec, - ) -> Result, FlowyError> { - Err(FlowyError::not_support()) - } + async fn import_from_bytes( + &self, + _uid: i64, + _view_id: &str, + _name: &str, + _import_type: ImportType, + _bytes: Vec, + ) -> Result, FlowyError> { + Err(FlowyError::not_support()) + } - async fn import_from_file_path( - &self, - _view_id: &str, - _name: &str, - _path: String, - ) -> Result<(), FlowyError> { - Err(FlowyError::not_support()) - } + async fn import_from_file_path( + &self, + _view_id: &str, + _name: &str, + _path: String, + ) -> Result<(), FlowyError> { + Err(FlowyError::not_support()) + } } diff --git a/frontend/rust-lib/flowy-core/src/deps_resolve/folder_deps/folder_deps_database_impl.rs b/frontend/rust-lib/flowy-core/src/deps_resolve/folder_deps/folder_deps_database_impl.rs index 2164b8b1f0..d7d0c4d0cc 100644 --- a/frontend/rust-lib/flowy-core/src/deps_resolve/folder_deps/folder_deps_database_impl.rs +++ b/frontend/rust-lib/flowy-core/src/deps_resolve/folder_deps/folder_deps_database_impl.rs @@ -1,86 +1,88 @@ -use std::collections::HashMap; -use std::path::Path; -use std::sync::Arc; use bytes::Bytes; use collab::entity::EncodedCollab; use collab_entity::CollabType; use collab_folder::{View, ViewLayout}; use collab_plugins::local_storage::kv::KVTransactionDB; -use flowy_database2::DatabaseManager; use flowy_database2::entities::DatabaseLayoutPB; use flowy_database2::services::share::csv::CSVFormat; use flowy_database2::template::{make_default_board, make_default_calendar, make_default_grid}; +use flowy_database2::DatabaseManager; use flowy_error::FlowyError; use flowy_folder::entities::{CreateViewParams, ViewLayoutPB}; use flowy_folder::manager::FolderUser; use flowy_folder::share::ImportType; -use flowy_folder::view_operation::{DatabaseEncodedCollab, FolderOperationHandler, GatherEncodedCollab, ImportedData, ViewData}; +use flowy_folder::view_operation::{ + DatabaseEncodedCollab, FolderOperationHandler, GatherEncodedCollab, ImportedData, ViewData, +}; use flowy_user::services::data_import::{load_collab_by_object_id, load_collab_by_object_ids}; use lib_infra::async_trait::async_trait; +use std::collections::HashMap; +use std::path::Path; +use std::sync::Arc; pub struct DatabaseFolderOperation(pub Arc); #[async_trait] impl FolderOperationHandler for DatabaseFolderOperation { - async fn open_view(&self, view_id: &str) -> Result<(), FlowyError> { - self.0.open_database_view(view_id).await?; - Ok(()) + async fn open_view(&self, view_id: &str) -> Result<(), FlowyError> { + self.0.open_database_view(view_id).await?; + Ok(()) + } + + async fn close_view(&self, view_id: &str) -> Result<(), FlowyError> { + self.0.close_database_view(view_id).await?; + Ok(()) + } + + async fn delete_view(&self, view_id: &str) -> Result<(), FlowyError> { + match self.0.delete_database_view(view_id).await { + Ok(_) => tracing::trace!("Delete database view: {}", view_id), + Err(e) => tracing::error!("🔴delete database failed: {}", e), } + Ok(()) + } - async fn close_view(&self, view_id: &str) -> Result<(), FlowyError> { - self.0.close_database_view(view_id).await?; - Ok(()) - } + async fn gather_publish_encode_collab( + &self, + user: &Arc, + view_id: &str, + ) -> Result { + let workspace_id = user.workspace_id()?; + // get the collab_object_id for the database. + // + // the collab object_id for the database is not the view_id, + // we should use the view_id to get the database_id + let oid = self.0.get_database_id_with_view_id(view_id).await?; + let row_oids = self.0.get_database_row_ids_with_view_id(view_id).await?; + let row_metas = self + .0 + .get_database_row_metas_with_view_id(view_id, row_oids.clone()) + .await?; + let row_document_ids = row_metas + .iter() + .filter_map(|meta| meta.document_id.clone()) + .collect::>(); + let row_oids = row_oids + .into_iter() + .map(|oid| oid.into_inner()) + .collect::>(); + let database_metas = self.0.get_all_databases_meta().await; - async fn delete_view(&self, view_id: &str) -> Result<(), FlowyError> { - match self.0.delete_database_view(view_id).await { - Ok(_) => tracing::trace!("Delete database view: {}", view_id), - Err(e) => tracing::error!("🔴delete database failed: {}", e), - } - Ok(()) - } + let uid = user + .user_id() + .map_err(|e| e.with_context("unable to get the uid: {}"))?; - async fn gather_publish_encode_collab( - &self, - user: &Arc, - view_id: &str, - ) -> Result { - let workspace_id = user.workspace_id()?; - // get the collab_object_id for the database. - // - // the collab object_id for the database is not the view_id, - // we should use the view_id to get the database_id - let oid = self.0.get_database_id_with_view_id(view_id).await?; - let row_oids = self.0.get_database_row_ids_with_view_id(view_id).await?; - let row_metas = self - .0 - .get_database_row_metas_with_view_id(view_id, row_oids.clone()) - .await?; - let row_document_ids = row_metas - .iter() - .filter_map(|meta| meta.document_id.clone()) - .collect::>(); - let row_oids = row_oids - .into_iter() - .map(|oid| oid.into_inner()) - .collect::>(); - let database_metas = self.0.get_all_databases_meta().await; + // get the collab db + let collab_db = user + .collab_db(uid) + .map_err(|e| e.with_context("unable to get the collab"))?; + let collab_db = collab_db.upgrade().ok_or_else(|| { + FlowyError::internal().with_context( + "The collab db has been dropped, indicating that the user has switched to a new account", + ) + })?; - let uid = user - .user_id() - .map_err(|e| e.with_context("unable to get the uid: {}"))?; - - // get the collab db - let collab_db = user - .collab_db(uid) - .map_err(|e| e.with_context("unable to get the collab"))?; - let collab_db = collab_db.upgrade().ok_or_else(|| { - FlowyError::internal().with_context( - "The collab db has been dropped, indicating that the user has switched to a new account", - ) - })?; - - tokio::task::spawn_blocking(move || { + tokio::task::spawn_blocking(move || { let collab_read_txn = collab_db.read_txn(); let database_collab = load_collab_by_object_id(uid, &collab_read_txn, &workspace_id, &oid) .map_err(|e| { @@ -143,196 +145,194 @@ impl FolderOperationHandler for DatabaseFolderOperation { })) }) .await? - } + } - async fn duplicate_view(&self, view_id: &str) -> Result { - Ok(Bytes::from(view_id.to_string())) - } + async fn duplicate_view(&self, view_id: &str) -> Result { + Ok(Bytes::from(view_id.to_string())) + } - /// Create a database view with duplicated data. - /// If the ext contains the {"database_id": "xx"}, then it will link - /// to the existing database. - async fn create_view_with_view_data( - &self, - _user_id: i64, - params: CreateViewParams, - ) -> Result, FlowyError> { - match CreateDatabaseExtParams::from_map(params.meta.clone()) { - None => match params.initial_data { - ViewData::DuplicateData(data) => { - let duplicated_view_id = - String::from_utf8(data.to_vec()).map_err(|_| FlowyError::invalid_data())?; - let encoded_collab = self - .0 - .duplicate_database(&duplicated_view_id, ¶ms.view_id) - .await?; - Ok(Some(encoded_collab)) - }, - ViewData::Data(data) => { - let encoded_collab = self - .0 - .create_database_with_data(¶ms.view_id, data.to_vec()) - .await?; - Ok(Some(encoded_collab)) - }, - ViewData::Empty => Ok(None), - }, - Some(database_params) => { - let layout = match params.layout { - ViewLayoutPB::Board => DatabaseLayoutPB::Board, - ViewLayoutPB::Calendar => DatabaseLayoutPB::Calendar, - ViewLayoutPB::Grid => DatabaseLayoutPB::Grid, - ViewLayoutPB::Document | ViewLayoutPB::Chat => { - return Err(FlowyError::not_support()); - }, - }; - let name = params.name.to_string(); - let database_view_id = params.view_id.to_string(); - let database_parent_view_id = params.parent_view_id.to_string(); - self - .0 - .create_linked_view( - name, - layout.into(), - database_params.database_id, - database_view_id, - database_parent_view_id, - ) - .await?; - Ok(None) - }, - } - } - - /// Create a database view with build-in data. - /// If the ext contains the {"database_id": "xx"}, then it will link to - /// the existing database. The data of the database will be shared within - /// these references views. - async fn create_default_view( - &self, - _user_id: i64, - _parent_view_id: &str, - view_id: &str, - name: &str, - layout: ViewLayout, - ) -> Result<(), FlowyError> { - let name = name.to_string(); - let data = match layout { - ViewLayout::Grid => make_default_grid(view_id, &name), - ViewLayout::Board => make_default_board(view_id, &name), - ViewLayout::Calendar => make_default_calendar(view_id, &name), - ViewLayout::Document | ViewLayout::Chat => { - return Err( - FlowyError::internal().with_context(format!("Can't handle {:?} layout type", layout)), - ); - }, - }; - let result = self.0.import_database(data).await; - match result { - Ok(_) => Ok(()), - Err(err) => { - if err.is_already_exists() { - Ok(()) - } else { - Err(err) - } - }, - } - } - - async fn import_from_bytes( - &self, - _uid: i64, - view_id: &str, - _name: &str, - import_type: ImportType, - bytes: Vec, - ) -> Result, FlowyError> { - let format = match import_type { - ImportType::CSV => CSVFormat::Original, - ImportType::AFDatabase => CSVFormat::META, - _ => CSVFormat::Original, - }; - let content = tokio::task::spawn_blocking(move || { - String::from_utf8(bytes).map_err(|err| FlowyError::internal().with_context(err)) - }) - .await??; - let result = self + /// Create a database view with duplicated data. + /// If the ext contains the {"database_id": "xx"}, then it will link + /// to the existing database. + async fn create_view_with_view_data( + &self, + _user_id: i64, + params: CreateViewParams, + ) -> Result, FlowyError> { + match CreateDatabaseExtParams::from_map(params.meta.clone()) { + None => match params.initial_data { + ViewData::DuplicateData(data) => { + let duplicated_view_id = + String::from_utf8(data.to_vec()).map_err(|_| FlowyError::invalid_data())?; + let encoded_collab = self .0 - .import_csv(view_id.to_string(), content, format) + .duplicate_database(&duplicated_view_id, ¶ms.view_id) .await?; - Ok( - result - .encoded_collabs - .into_iter() - .map(|encoded| { - ( - encoded.object_id, - encoded.collab_type, - encoded.encoded_collab, - ) - }) - .collect(), - ) - } - - async fn import_from_file_path( - &self, - view_id: &str, - _name: &str, - path: String, - ) -> Result<(), FlowyError> { - let file_path = Path::new(&path); - if !file_path.exists() { - return Err(FlowyError::record_not_found().with_context("File not found")); - } - - let data = tokio::fs::read(file_path).await?; - let content = - String::from_utf8(data).map_err(|e| FlowyError::invalid_data().with_context(e))?; - let _ = self + Ok(Some(encoded_collab)) + }, + ViewData::Data(data) => { + let encoded_collab = self .0 - .import_csv(view_id.to_string(), content, CSVFormat::Original) + .create_database_with_data(¶ms.view_id, data.to_vec()) .await?; - Ok(()) - } - - async fn did_update_view(&self, old: &View, new: &View) -> Result<(), FlowyError> { - let database_layout = match new.layout { - ViewLayout::Document | ViewLayout::Chat => { - return Err(FlowyError::internal().with_context("Can't handle document layout type")); - }, - ViewLayout::Grid => DatabaseLayoutPB::Grid, - ViewLayout::Board => DatabaseLayoutPB::Board, - ViewLayout::Calendar => DatabaseLayoutPB::Calendar, + Ok(Some(encoded_collab)) + }, + ViewData::Empty => Ok(None), + }, + Some(database_params) => { + let layout = match params.layout { + ViewLayoutPB::Board => DatabaseLayoutPB::Board, + ViewLayoutPB::Calendar => DatabaseLayoutPB::Calendar, + ViewLayoutPB::Grid => DatabaseLayoutPB::Grid, + ViewLayoutPB::Document | ViewLayoutPB::Chat => { + return Err(FlowyError::not_support()); + }, }; + let name = params.name.to_string(); + let database_view_id = params.view_id.to_string(); + let database_parent_view_id = params.parent_view_id.to_string(); + self + .0 + .create_linked_view( + name, + layout.into(), + database_params.database_id, + database_view_id, + database_parent_view_id, + ) + .await?; + Ok(None) + }, + } + } - if old.layout != new.layout { - self - .0 - .update_database_layout(&new.id, database_layout) - .await?; - Ok(()) + /// Create a database view with build-in data. + /// If the ext contains the {"database_id": "xx"}, then it will link to + /// the existing database. The data of the database will be shared within + /// these references views. + async fn create_default_view( + &self, + _user_id: i64, + _parent_view_id: &str, + view_id: &str, + name: &str, + layout: ViewLayout, + ) -> Result<(), FlowyError> { + let name = name.to_string(); + let data = match layout { + ViewLayout::Grid => make_default_grid(view_id, &name), + ViewLayout::Board => make_default_board(view_id, &name), + ViewLayout::Calendar => make_default_calendar(view_id, &name), + ViewLayout::Document | ViewLayout::Chat => { + return Err( + FlowyError::internal().with_context(format!("Can't handle {:?} layout type", layout)), + ); + }, + }; + let result = self.0.import_database(data).await; + match result { + Ok(_) => Ok(()), + Err(err) => { + if err.is_already_exists() { + Ok(()) } else { - Ok(()) + Err(err) } + }, + } + } + + async fn import_from_bytes( + &self, + _uid: i64, + view_id: &str, + _name: &str, + import_type: ImportType, + bytes: Vec, + ) -> Result, FlowyError> { + let format = match import_type { + ImportType::CSV => CSVFormat::Original, + ImportType::AFDatabase => CSVFormat::META, + _ => CSVFormat::Original, + }; + let content = tokio::task::spawn_blocking(move || { + String::from_utf8(bytes).map_err(|err| FlowyError::internal().with_context(err)) + }) + .await??; + let result = self + .0 + .import_csv(view_id.to_string(), content, format) + .await?; + Ok( + result + .encoded_collabs + .into_iter() + .map(|encoded| { + ( + encoded.object_id, + encoded.collab_type, + encoded.encoded_collab, + ) + }) + .collect(), + ) + } + + async fn import_from_file_path( + &self, + view_id: &str, + _name: &str, + path: String, + ) -> Result<(), FlowyError> { + let file_path = Path::new(&path); + if !file_path.exists() { + return Err(FlowyError::record_not_found().with_context("File not found")); } - fn name(&self) -> &str { - "DatabaseFolderOperationHandler" + let data = tokio::fs::read(file_path).await?; + let content = + String::from_utf8(data).map_err(|e| FlowyError::invalid_data().with_context(e))?; + let _ = self + .0 + .import_csv(view_id.to_string(), content, CSVFormat::Original) + .await?; + Ok(()) + } + + async fn did_update_view(&self, old: &View, new: &View) -> Result<(), FlowyError> { + let database_layout = match new.layout { + ViewLayout::Document | ViewLayout::Chat => { + return Err(FlowyError::internal().with_context("Can't handle document layout type")); + }, + ViewLayout::Grid => DatabaseLayoutPB::Grid, + ViewLayout::Board => DatabaseLayoutPB::Board, + ViewLayout::Calendar => DatabaseLayoutPB::Calendar, + }; + + if old.layout != new.layout { + self + .0 + .update_database_layout(&new.id, database_layout) + .await?; + Ok(()) + } else { + Ok(()) } + } + + fn name(&self) -> &str { + "DatabaseFolderOperationHandler" + } } - #[derive(Debug, serde::Deserialize)] struct CreateDatabaseExtParams { - database_id: String, + database_id: String, } impl CreateDatabaseExtParams { - pub fn from_map(map: HashMap) -> Option { - let value = serde_json::to_value(map).ok()?; - serde_json::from_value::(value).ok() - } + pub fn from_map(map: HashMap) -> Option { + let value = serde_json::to_value(map).ok()?; + serde_json::from_value::(value).ok() + } } - diff --git a/frontend/rust-lib/flowy-core/src/deps_resolve/folder_deps/folder_deps_doc_impl.rs b/frontend/rust-lib/flowy-core/src/deps_resolve/folder_deps/folder_deps_doc_impl.rs index bf2ce648af..af95b8987d 100644 --- a/frontend/rust-lib/flowy-core/src/deps_resolve/folder_deps/folder_deps_doc_impl.rs +++ b/frontend/rust-lib/flowy-core/src/deps_resolve/folder_deps/folder_deps_doc_impl.rs @@ -1,11 +1,9 @@ -use std::convert::TryFrom; -use std::sync::Arc; +use crate::deps_resolve::folder_deps::get_encoded_collab_v1_from_disk; use bytes::Bytes; use collab::entity::EncodedCollab; use collab_entity::CollabType; use collab_folder::hierarchy_builder::NestedViewBuilder; use collab_folder::ViewLayout; -use tokio::sync::RwLock; use flowy_document::entities::DocumentDataPB; use flowy_document::manager::DocumentManager; use flowy_document::parser::json::parser::JsonToDocumentParser; @@ -13,148 +11,152 @@ use flowy_error::FlowyError; use flowy_folder::entities::{CreateViewParams, ViewLayoutPB}; use flowy_folder::manager::FolderUser; use flowy_folder::share::ImportType; -use flowy_folder::view_operation::{FolderOperationHandler, GatherEncodedCollab, ImportedData, ViewData}; +use flowy_folder::view_operation::{ + FolderOperationHandler, GatherEncodedCollab, ImportedData, ViewData, +}; use lib_dispatch::prelude::ToBytes; use lib_infra::async_trait::async_trait; -use crate::deps_resolve::folder_deps::get_encoded_collab_v1_from_disk; +use std::convert::TryFrom; +use std::sync::Arc; +use tokio::sync::RwLock; pub struct DocumentFolderOperation(pub Arc); #[async_trait] impl FolderOperationHandler for DocumentFolderOperation { - fn name(&self) -> &str { - "DocumentFolderOperationHandler" - } + fn name(&self) -> &str { + "DocumentFolderOperationHandler" + } - async fn create_workspace_view( - &self, - uid: i64, - workspace_view_builder: Arc>, - ) -> Result<(), FlowyError> { - let manager = self.0.clone(); - let mut write_guard = workspace_view_builder.write().await; - // Create a view named "Getting started" with an icon ⭐️ and the built-in README data. - // Don't modify this code unless you know what you are doing. - write_guard - .with_view_builder(|view_builder| async { - let view = view_builder - .with_name("Getting started") - .with_icon("⭐️") - .build(); - // create a empty document - let json_str = include_str!("../../../assets/read_me.json"); - let document_pb = JsonToDocumentParser::json_str_to_document(json_str).unwrap(); - manager - .create_document(uid, &view.view.id, Some(document_pb.into())) - .await - .unwrap(); - view - }) - .await; - Ok(()) - } + async fn create_workspace_view( + &self, + uid: i64, + workspace_view_builder: Arc>, + ) -> Result<(), FlowyError> { + let manager = self.0.clone(); + let mut write_guard = workspace_view_builder.write().await; + // Create a view named "Getting started" with an icon ⭐️ and the built-in README data. + // Don't modify this code unless you know what you are doing. + write_guard + .with_view_builder(|view_builder| async { + let view = view_builder + .with_name("Getting started") + .with_icon("⭐️") + .build(); + // create a empty document + let json_str = include_str!("../../../assets/read_me.json"); + let document_pb = JsonToDocumentParser::json_str_to_document(json_str).unwrap(); + manager + .create_document(uid, &view.view.id, Some(document_pb.into())) + .await + .unwrap(); + view + }) + .await; + Ok(()) + } - async fn open_view(&self, view_id: &str) -> Result<(), FlowyError> { - self.0.open_document(view_id).await?; - Ok(()) - } + async fn open_view(&self, view_id: &str) -> Result<(), FlowyError> { + self.0.open_document(view_id).await?; + Ok(()) + } - /// Close the document view. - async fn close_view(&self, view_id: &str) -> Result<(), FlowyError> { - self.0.close_document(view_id).await?; - Ok(()) - } + /// Close the document view. + async fn close_view(&self, view_id: &str) -> Result<(), FlowyError> { + self.0.close_document(view_id).await?; + Ok(()) + } - async fn delete_view(&self, view_id: &str) -> Result<(), FlowyError> { - match self.0.delete_document(view_id).await { - Ok(_) => tracing::trace!("Delete document: {}", view_id), - Err(e) => tracing::error!("🔴delete document failed: {}", e), + async fn delete_view(&self, view_id: &str) -> Result<(), FlowyError> { + match self.0.delete_document(view_id).await { + Ok(_) => tracing::trace!("Delete document: {}", view_id), + Err(e) => tracing::error!("🔴delete document failed: {}", e), + } + Ok(()) + } + + async fn duplicate_view(&self, view_id: &str) -> Result { + let data: DocumentDataPB = self.0.get_document_data(view_id).await?.into(); + let data_bytes = data.into_bytes().map_err(|_| FlowyError::invalid_data())?; + Ok(data_bytes) + } + + async fn gather_publish_encode_collab( + &self, + user: &Arc, + view_id: &str, + ) -> Result { + let encoded_collab = + get_encoded_collab_v1_from_disk(user, view_id, CollabType::Document).await?; + Ok(GatherEncodedCollab::Document(encoded_collab)) + } + + async fn create_view_with_view_data( + &self, + user_id: i64, + params: CreateViewParams, + ) -> Result, FlowyError> { + debug_assert_eq!(params.layout, ViewLayoutPB::Document); + let data = match params.initial_data { + ViewData::DuplicateData(data) => Some(DocumentDataPB::try_from(data)?), + ViewData::Data(data) => Some(DocumentDataPB::try_from(data)?), + ViewData::Empty => None, + }; + let encoded_collab = self + .0 + .create_document(user_id, ¶ms.view_id, data.map(|d| d.into())) + .await?; + Ok(Some(encoded_collab)) + } + + /// Create a view with built-in data. + async fn create_default_view( + &self, + user_id: i64, + _parent_view_id: &str, + view_id: &str, + _name: &str, + layout: ViewLayout, + ) -> Result<(), FlowyError> { + debug_assert_eq!(layout, ViewLayout::Document); + match self.0.create_document(user_id, view_id, None).await { + Ok(_) => Ok(()), + Err(err) => { + if err.is_already_exists() { + Ok(()) + } else { + Err(err) } - Ok(()) + }, } + } - async fn duplicate_view(&self, view_id: &str) -> Result { - let data: DocumentDataPB = self.0.get_document_data(view_id).await?.into(); - let data_bytes = data.into_bytes().map_err(|_| FlowyError::invalid_data())?; - Ok(data_bytes) - } + async fn import_from_bytes( + &self, + uid: i64, + view_id: &str, + _name: &str, + _import_type: ImportType, + bytes: Vec, + ) -> Result, FlowyError> { + let data = DocumentDataPB::try_from(Bytes::from(bytes))?; + let encoded_collab = self + .0 + .create_document(uid, view_id, Some(data.into())) + .await?; + Ok(vec![( + view_id.to_string(), + CollabType::Document, + encoded_collab, + )]) + } - async fn gather_publish_encode_collab( - &self, - user: &Arc, - view_id: &str, - ) -> Result { - let encoded_collab = - get_encoded_collab_v1_from_disk(user, view_id, CollabType::Document).await?; - Ok(GatherEncodedCollab::Document(encoded_collab)) - } - - async fn create_view_with_view_data( - &self, - user_id: i64, - params: CreateViewParams, - ) -> Result, FlowyError> { - debug_assert_eq!(params.layout, ViewLayoutPB::Document); - let data = match params.initial_data { - ViewData::DuplicateData(data) => Some(DocumentDataPB::try_from(data)?), - ViewData::Data(data) => Some(DocumentDataPB::try_from(data)?), - ViewData::Empty => None, - }; - let encoded_collab = self - .0 - .create_document(user_id, ¶ms.view_id, data.map(|d| d.into())) - .await?; - Ok(Some(encoded_collab)) - } - - /// Create a view with built-in data. - async fn create_default_view( - &self, - user_id: i64, - _parent_view_id: &str, - view_id: &str, - _name: &str, - layout: ViewLayout, - ) -> Result<(), FlowyError> { - debug_assert_eq!(layout, ViewLayout::Document); - match self.0.create_document(user_id, view_id, None).await { - Ok(_) => Ok(()), - Err(err) => { - if err.is_already_exists() { - Ok(()) - } else { - Err(err) - } - }, - } - } - - async fn import_from_bytes( - &self, - uid: i64, - view_id: &str, - _name: &str, - _import_type: ImportType, - bytes: Vec, - ) -> Result, FlowyError> { - let data = DocumentDataPB::try_from(Bytes::from(bytes))?; - let encoded_collab = self - .0 - .create_document(uid, view_id, Some(data.into())) - .await?; - Ok(vec![( - view_id.to_string(), - CollabType::Document, - encoded_collab, - )]) - } - - async fn import_from_file_path( - &self, - _view_id: &str, - _name: &str, - _path: String, - ) -> Result<(), FlowyError> { - // TODO(lucas): import file from local markdown file - Ok(()) - } + async fn import_from_file_path( + &self, + _view_id: &str, + _name: &str, + _path: String, + ) -> Result<(), FlowyError> { + // TODO(lucas): import file from local markdown file + Ok(()) + } } diff --git a/frontend/rust-lib/flowy-core/src/deps_resolve/folder_deps/mod.rs b/frontend/rust-lib/flowy-core/src/deps_resolve/folder_deps/mod.rs index d8bb3a6865..9bed61d918 100644 --- a/frontend/rust-lib/flowy-core/src/deps_resolve/folder_deps/mod.rs +++ b/frontend/rust-lib/flowy-core/src/deps_resolve/folder_deps/mod.rs @@ -2,6 +2,7 @@ mod folder_deps_chat_impl; mod folder_deps_database_impl; mod folder_deps_doc_impl; +use crate::server_layer::ServerProvider; use collab_entity::{CollabType, EncodedCollab}; use collab_integrate::collab_builder::AppFlowyCollabBuilder; use collab_integrate::CollabKVDB; @@ -9,239 +10,238 @@ use flowy_ai::ai_manager::AIManager; use flowy_database2::DatabaseManager; use flowy_document::manager::DocumentManager; use flowy_error::{internal_error, FlowyError, FlowyResult}; -use flowy_folder::entities::{ UpdateViewParams}; +use flowy_folder::entities::UpdateViewParams; use flowy_folder::manager::{FolderManager, FolderUser}; use flowy_folder::ViewLayout; use flowy_search::folder::indexer::FolderIndexManagerImpl; use flowy_sqlite::kv::KVStorePreferences; use flowy_user::services::authenticate_user::AuthenticateUser; -use flowy_user::services::data_import::{load_collab_by_object_id}; +use flowy_user::services::data_import::load_collab_by_object_id; use std::sync::{Arc, Weak}; -use crate::server_layer::ServerProvider; -use collab_plugins::local_storage::kv::KVTransactionDB; -use flowy_folder_pub::query::{FolderQueryService, FolderService, FolderViewEdit, QueryCollab}; -use lib_infra::async_trait::async_trait; use crate::deps_resolve::folder_deps::folder_deps_chat_impl::ChatFolderOperation; use crate::deps_resolve::folder_deps::folder_deps_database_impl::DatabaseFolderOperation; use crate::deps_resolve::folder_deps::folder_deps_doc_impl::DocumentFolderOperation; +use collab_plugins::local_storage::kv::KVTransactionDB; +use flowy_folder_pub::query::{FolderQueryService, FolderService, FolderViewEdit, QueryCollab}; +use lib_infra::async_trait::async_trait; pub struct FolderDepsResolver(); #[allow(clippy::too_many_arguments)] impl FolderDepsResolver { - pub async fn resolve( - authenticate_user: Weak, - collab_builder: Arc, - server_provider: Arc, - folder_indexer: Arc, - store_preferences: Arc, - ) -> Arc { - let user: Arc = Arc::new(FolderUserImpl { - authenticate_user: authenticate_user.clone(), - }); + pub async fn resolve( + authenticate_user: Weak, + collab_builder: Arc, + server_provider: Arc, + folder_indexer: Arc, + store_preferences: Arc, + ) -> Arc { + let user: Arc = Arc::new(FolderUserImpl { + authenticate_user: authenticate_user.clone(), + }); - Arc::new( - FolderManager::new( - user.clone(), - collab_builder, - server_provider.clone(), - folder_indexer, - store_preferences, - ) - .unwrap(), - ) - } + Arc::new( + FolderManager::new( + user.clone(), + collab_builder, + server_provider.clone(), + folder_indexer, + store_preferences, + ) + .unwrap(), + ) + } } pub fn register_handlers( - folder_manager: &Arc, - document_manager: Arc, - database_manager: Arc, - chat_manager: Arc, + folder_manager: &Arc, + document_manager: Arc, + database_manager: Arc, + chat_manager: Arc, ) { - let document_folder_operation = Arc::new(DocumentFolderOperation(document_manager)); - folder_manager.register_operation_handler(ViewLayout::Document, document_folder_operation); + let document_folder_operation = Arc::new(DocumentFolderOperation(document_manager)); + folder_manager.register_operation_handler(ViewLayout::Document, document_folder_operation); - let database_folder_operation = Arc::new(DatabaseFolderOperation(database_manager)); - let chat_folder_operation = Arc::new(ChatFolderOperation(chat_manager)); - folder_manager.register_operation_handler(ViewLayout::Board, database_folder_operation.clone()); - folder_manager.register_operation_handler(ViewLayout::Grid, database_folder_operation.clone()); - folder_manager.register_operation_handler(ViewLayout::Calendar, database_folder_operation); - folder_manager.register_operation_handler(ViewLayout::Chat, chat_folder_operation); + let database_folder_operation = Arc::new(DatabaseFolderOperation(database_manager)); + let chat_folder_operation = Arc::new(ChatFolderOperation(chat_manager)); + folder_manager.register_operation_handler(ViewLayout::Board, database_folder_operation.clone()); + folder_manager.register_operation_handler(ViewLayout::Grid, database_folder_operation.clone()); + folder_manager.register_operation_handler(ViewLayout::Calendar, database_folder_operation); + folder_manager.register_operation_handler(ViewLayout::Chat, chat_folder_operation); } struct FolderUserImpl { - authenticate_user: Weak, + authenticate_user: Weak, } impl FolderUserImpl { - fn upgrade_user(&self) -> Result, FlowyError> { - let user = self - .authenticate_user - .upgrade() - .ok_or(FlowyError::internal().with_context("Unexpected error: UserSession is None"))?; - Ok(user) - } + fn upgrade_user(&self) -> Result, FlowyError> { + let user = self + .authenticate_user + .upgrade() + .ok_or(FlowyError::internal().with_context("Unexpected error: UserSession is None"))?; + Ok(user) + } } impl FolderUser for FolderUserImpl { - fn user_id(&self) -> Result { - self.upgrade_user()?.user_id() - } + fn user_id(&self) -> Result { + self.upgrade_user()?.user_id() + } - fn workspace_id(&self) -> Result { - self.upgrade_user()?.workspace_id() - } + fn workspace_id(&self) -> Result { + self.upgrade_user()?.workspace_id() + } - fn collab_db(&self, uid: i64) -> Result, FlowyError> { - self.upgrade_user()?.get_collab_db(uid) - } + fn collab_db(&self, uid: i64) -> Result, FlowyError> { + self.upgrade_user()?.get_collab_db(uid) + } - fn is_folder_exist_on_disk(&self, uid: i64, workspace_id: &str) -> FlowyResult { - self.upgrade_user()?.is_collab_on_disk(uid, workspace_id) - } + fn is_folder_exist_on_disk(&self, uid: i64, workspace_id: &str) -> FlowyResult { + self.upgrade_user()?.is_collab_on_disk(uid, workspace_id) + } } #[derive(Clone)] pub struct FolderServiceImpl { - folder_manager: Weak, - user: Arc, + folder_manager: Weak, + user: Arc, } impl FolderService for FolderServiceImpl {} impl FolderServiceImpl { - pub fn new( - folder_manager: Weak, - authenticate_user: Weak, - ) -> Self { - let user: Arc = Arc::new(FolderUserImpl { authenticate_user }); - Self { - folder_manager, - user, - } + pub fn new( + folder_manager: Weak, + authenticate_user: Weak, + ) -> Self { + let user: Arc = Arc::new(FolderUserImpl { authenticate_user }); + Self { + folder_manager, + user, } + } } #[async_trait] impl FolderViewEdit for FolderServiceImpl { - async fn set_view_title_if_empty(&self, view_id: &str, title: &str) -> FlowyResult<()> { - if title.is_empty() { - return Ok(()); - } - - if let Some(folder_manager) = self.folder_manager.upgrade() { - if let Ok(view) = folder_manager.get_view(view_id).await { - if view.name.is_empty() { - let title = if title.len() > 50 { - title.chars().take(50).collect() - } else { - title.to_string() - }; - - folder_manager - .update_view_with_params(UpdateViewParams { - view_id: view_id.to_string(), - name: Some(title), - desc: None, - thumbnail: None, - layout: None, - is_favorite: None, - extra: None, - }) - .await?; - } - } - } - Ok(()) + async fn set_view_title_if_empty(&self, view_id: &str, title: &str) -> FlowyResult<()> { + if title.is_empty() { + return Ok(()); } + + if let Some(folder_manager) = self.folder_manager.upgrade() { + if let Ok(view) = folder_manager.get_view(view_id).await { + if view.name.is_empty() { + let title = if title.len() > 50 { + title.chars().take(50).collect() + } else { + title.to_string() + }; + + folder_manager + .update_view_with_params(UpdateViewParams { + view_id: view_id.to_string(), + name: Some(title), + desc: None, + thumbnail: None, + layout: None, + is_favorite: None, + extra: None, + }) + .await?; + } + } + } + Ok(()) + } } #[async_trait] impl FolderQueryService for FolderServiceImpl { - async fn get_surrounding_view_ids_with_view_layout( - &self, - parent_view_id: &str, - view_layout: ViewLayout, - ) -> Vec { - let folder_manager = match self.folder_manager.upgrade() { - Some(folder_manager) => folder_manager, - None => return vec![], - }; + async fn get_surrounding_view_ids_with_view_layout( + &self, + parent_view_id: &str, + view_layout: ViewLayout, + ) -> Vec { + let folder_manager = match self.folder_manager.upgrade() { + Some(folder_manager) => folder_manager, + None => return vec![], + }; - if let Ok(view) = folder_manager.get_view(parent_view_id).await { - if view.space_info().is_some() { - return vec![]; + if let Ok(view) = folder_manager.get_view(parent_view_id).await { + if view.space_info().is_some() { + return vec![]; + } + } + + match folder_manager + .get_untrashed_views_belong_to(parent_view_id) + .await + { + Ok(views) => { + let mut children = views + .into_iter() + .filter_map(|child| { + if child.layout == view_layout { + Some(child.id.clone()) + } else { + None } - } - - match folder_manager - .get_untrashed_views_belong_to(parent_view_id) - .await - { - Ok(views) => { - let mut children = views - .into_iter() - .filter_map(|child| { - if child.layout == view_layout { - Some(child.id.clone()) - } else { - None - } - }) - .collect::>(); - children.push(parent_view_id.to_string()); - children - }, - _ => vec![], - } + }) + .collect::>(); + children.push(parent_view_id.to_string()); + children + }, + _ => vec![], } + } - async fn get_collab(&self, object_id: &str, collab_type: CollabType) -> Option { - let encode_collab = get_encoded_collab_v1_from_disk(&self.user, object_id, collab_type.clone()) - .await - .ok(); + async fn get_collab(&self, object_id: &str, collab_type: CollabType) -> Option { + let encode_collab = get_encoded_collab_v1_from_disk(&self.user, object_id, collab_type.clone()) + .await + .ok(); - encode_collab.map(|encoded_collab| QueryCollab { - collab_type, - encoded_collab, - }) - } + encode_collab.map(|encoded_collab| QueryCollab { + collab_type, + encoded_collab, + }) + } } #[inline] async fn get_encoded_collab_v1_from_disk( - user: &Arc, - view_id: &str, - collab_type: CollabType, + user: &Arc, + view_id: &str, + collab_type: CollabType, ) -> Result { - let workspace_id = user.workspace_id()?; - let uid = user - .user_id() - .map_err(|e| e.with_context("unable to get the uid: {}"))?; + let workspace_id = user.workspace_id()?; + let uid = user + .user_id() + .map_err(|e| e.with_context("unable to get the uid: {}"))?; - // get the collab db - let collab_db = user - .collab_db(uid) - .map_err(|e| e.with_context("unable to get the collab"))?; - let collab_db = collab_db.upgrade().ok_or_else(|| { - FlowyError::internal().with_context( - "The collab db has been dropped, indicating that the user has switched to a new account", - ) + // get the collab db + let collab_db = user + .collab_db(uid) + .map_err(|e| e.with_context("unable to get the collab"))?; + let collab_db = collab_db.upgrade().ok_or_else(|| { + FlowyError::internal().with_context( + "The collab db has been dropped, indicating that the user has switched to a new account", + ) + })?; + let collab_read_txn = collab_db.read_txn(); + let collab = + load_collab_by_object_id(uid, &collab_read_txn, &workspace_id, view_id).map_err(|e| { + FlowyError::internal().with_context(format!("load document collab failed: {}", e)) })?; - let collab_read_txn = collab_db.read_txn(); - let collab = - load_collab_by_object_id(uid, &collab_read_txn, &workspace_id, view_id).map_err(|e| { - FlowyError::internal().with_context(format!("load document collab failed: {}", e)) - })?; - tokio::task::spawn_blocking(move || { - let data = collab - .encode_collab_v1(|collab| collab_type.validate_require_data(collab)) - .map_err(|e| { - FlowyError::internal().with_context(format!("encode document collab failed: {}", e)) - })?; - Ok::<_, FlowyError>(data) - }) - .await - .map_err(internal_error)? + tokio::task::spawn_blocking(move || { + let data = collab + .encode_collab_v1(|collab| collab_type.validate_require_data(collab)) + .map_err(|e| { + FlowyError::internal().with_context(format!("encode document collab failed: {}", e)) + })?; + Ok::<_, FlowyError>(data) + }) + .await + .map_err(internal_error)? } diff --git a/frontend/rust-lib/flowy-core/src/deps_resolve/mod.rs b/frontend/rust-lib/flowy-core/src/deps_resolve/mod.rs index 1c8a17792c..7e1f8b942f 100644 --- a/frontend/rust-lib/flowy-core/src/deps_resolve/mod.rs +++ b/frontend/rust-lib/flowy-core/src/deps_resolve/mod.rs @@ -10,10 +10,10 @@ mod collab_deps; mod document_deps; mod chat_deps; +mod cloud_service_impl; mod database_deps; pub mod file_storage_deps; +mod folder_deps; +pub(crate) mod reminder_deps; mod search_deps; mod user_deps; -mod folder_deps; -mod cloud_service_impl; -pub(crate) mod reminder_deps; diff --git a/frontend/rust-lib/flowy-core/src/lib.rs b/frontend/rust-lib/flowy-core/src/lib.rs index b1ac66d925..4c84e73c08 100644 --- a/frontend/rust-lib/flowy-core/src/lib.rs +++ b/frontend/rust-lib/flowy-core/src/lib.rs @@ -32,17 +32,17 @@ use module::make_plugins; use crate::config::AppFlowyCoreConfig; use crate::deps_resolve::file_storage_deps::FileStorageResolver; use crate::deps_resolve::*; -use deps_resolve::reminder_deps::CollabInteractImpl; -use user_state_callback::UserStatusCallbackImpl; use crate::log_filter::init_log; use crate::server_layer::{current_server_type, Server, ServerProvider}; +use deps_resolve::reminder_deps::CollabInteractImpl; +use user_state_callback::UserStatusCallbackImpl; pub mod config; mod deps_resolve; -pub mod module; -pub(crate) mod user_state_callback; -pub(crate) mod server_layer; mod log_filter; +pub mod module; +pub(crate) mod server_layer; +pub(crate) mod user_state_callback; /// This name will be used as to identify the current [AppFlowyCore] instance. /// Don't change this. diff --git a/frontend/rust-lib/flowy-document/Cargo.toml b/frontend/rust-lib/flowy-document/Cargo.toml index e981fb9acb..77aa321d3c 100644 --- a/frontend/rust-lib/flowy-document/Cargo.toml +++ b/frontend/rust-lib/flowy-document/Cargo.toml @@ -43,7 +43,6 @@ getrandom = { version = "0.2", features = ["js"] } [dev-dependencies] tempfile = "3.4.0" tracing-subscriber = { version = "0.3.3", features = ["env-filter"] } -collab-integrate = { workspace = true } tokio = { workspace = true, features = ["rt", "rt-multi-thread"] } [build-dependencies] diff --git a/frontend/rust-lib/flowy-error/Cargo.toml b/frontend/rust-lib/flowy-error/Cargo.toml index d8ed359065..d521e26f4d 100644 --- a/frontend/rust-lib/flowy-error/Cargo.toml +++ b/frontend/rust-lib/flowy-error/Cargo.toml @@ -36,7 +36,7 @@ client-api = { workspace = true, optional = true } tantivy = { version = "0.22.0", optional = true } [features] -default = ["impl_from_dispatch_error", "impl_from_serde", "impl_from_reqwest"] +default = ["impl_from_dispatch_error", "impl_from_serde", "impl_from_reqwest", "impl_from_sqlite"] impl_from_dispatch_error = ["lib-dispatch"] impl_from_serde = [] impl_from_reqwest = ["reqwest"] diff --git a/frontend/rust-lib/flowy-sqlite/migrations/2024-12-29-061706_collab_metadata/down.sql b/frontend/rust-lib/flowy-sqlite/migrations/2024-12-29-061706_collab_metadata/down.sql new file mode 100644 index 0000000000..ebe24324fe --- /dev/null +++ b/frontend/rust-lib/flowy-sqlite/migrations/2024-12-29-061706_collab_metadata/down.sql @@ -0,0 +1,2 @@ +-- This file should undo anything in `up.sql` +DROP TABLE af_collab_metadata; diff --git a/frontend/rust-lib/flowy-sqlite/migrations/2024-12-29-061706_collab_metadata/up.sql b/frontend/rust-lib/flowy-sqlite/migrations/2024-12-29-061706_collab_metadata/up.sql new file mode 100644 index 0000000000..84668f23fa --- /dev/null +++ b/frontend/rust-lib/flowy-sqlite/migrations/2024-12-29-061706_collab_metadata/up.sql @@ -0,0 +1,7 @@ +-- Your SQL goes here +CREATE TABLE af_collab_metadata ( + object_id TEXT PRIMARY KEY NOT NULL, + updated_at BIGINT NOT NULL, + prev_sync_state_vector BLOB NOT NULL, + collab_type INTEGER NOT NULL +); \ No newline at end of file diff --git a/frontend/rust-lib/flowy-sqlite/src/schema.rs b/frontend/rust-lib/flowy-sqlite/src/schema.rs index efd0089757..4ff70bf3c6 100644 --- a/frontend/rust-lib/flowy-sqlite/src/schema.rs +++ b/frontend/rust-lib/flowy-sqlite/src/schema.rs @@ -1,5 +1,14 @@ // @generated automatically by Diesel CLI. +diesel::table! { + af_collab_metadata (object_id) { + object_id -> Text, + updated_at -> BigInt, + prev_sync_state_vector -> Binary, + collab_type -> Integer, + } +} + diesel::table! { chat_local_setting_table (chat_id) { chat_id -> Text, @@ -119,6 +128,7 @@ diesel::table! { } diesel::allow_tables_to_appear_in_same_query!( + af_collab_metadata, chat_local_setting_table, chat_message_table, chat_table, diff --git a/frontend/rust-lib/flowy-user/src/user_manager/manager.rs b/frontend/rust-lib/flowy-user/src/user_manager/manager.rs index cd5c6e1dc9..f04c988f5c 100644 --- a/frontend/rust-lib/flowy-user/src/user_manager/manager.rs +++ b/frontend/rust-lib/flowy-user/src/user_manager/manager.rs @@ -36,7 +36,7 @@ use crate::migrations::workspace_trash_v1::WorkspaceTrashMapToSectionMigration; use crate::migrations::AnonUser; use crate::services::authenticate_user::AuthenticateUser; use crate::services::cloud_config::get_cloud_config; -use crate::services::collab_interact::{UserReminder, DefaultCollabInteract}; +use crate::services::collab_interact::{DefaultCollabInteract, UserReminder}; use super::manager_user_workspace::save_user_workspace; use crate::migrations::doc_key_with_workspace::CollabDocKeyWithWorkspaceIdMigration; From 06d5bc734b69f46d7cc2b7be3f70fac396f804db Mon Sep 17 00:00:00 2001 From: Richard Shiue <71320345+richardshiue@users.noreply.github.com> Date: Mon, 30 Dec 2024 12:38:56 +0800 Subject: [PATCH 088/576] fix: try to fix message regeneration being appended instead of replaced (#7084) --- .../ai_chat/application/chat_bloc.dart | 34 ++++++++++++------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/frontend/appflowy_flutter/lib/plugins/ai_chat/application/chat_bloc.dart b/frontend/appflowy_flutter/lib/plugins/ai_chat/application/chat_bloc.dart index 58146c42f1..c18389e262 100644 --- a/frontend/appflowy_flutter/lib/plugins/ai_chat/application/chat_bloc.dart +++ b/frontend/appflowy_flutter/lib/plugins/ai_chat/application/chat_bloc.dart @@ -272,15 +272,19 @@ class ChatBloc extends Bloc { // 3 mean message response from AI if (pb.authorType == 3 && answerStreamMessageId.isNotEmpty) { - temporaryMessageIDMap[pb.messageId.toString()] = - answerStreamMessageId; + temporaryMessageIDMap.putIfAbsent( + pb.messageId.toString(), + () => answerStreamMessageId, + ); answerStreamMessageId = ''; } // 1 mean message response from User if (pb.authorType == 1 && questionStreamMessageId.isNotEmpty) { - temporaryMessageIDMap[pb.messageId.toString()] = - questionStreamMessageId; + temporaryMessageIDMap.putIfAbsent( + pb.messageId.toString(), + () => questionStreamMessageId, + ); questionStreamMessageId = ''; } @@ -422,8 +426,9 @@ class ChatBloc extends Bloc { (question) { if (!isClosed) { final streamAnswer = _createAnswerStreamMessage( - answerStream!, - question.messageId, + stream: answerStream!, + questionMessageId: question.messageId, + fakeQuestionMessageId: questionStreamMessage.id, ); lastSentMessage = question; @@ -479,8 +484,8 @@ class ChatBloc extends Bloc { (success) { if (!isClosed) { final streamAnswer = _createAnswerStreamMessage( - answerStream!, - answerMessageId - 1, + stream: answerStream!, + questionMessageId: answerMessageId - 1, ).copyWith(id: answerMessageIdString); add(ChatEvent.receiveMessage(streamAnswer)); @@ -491,11 +496,14 @@ class ChatBloc extends Bloc { ); } - Message _createAnswerStreamMessage( - AnswerStream stream, - Int64 questionMessageId, - ) { - answerStreamMessageId = (questionMessageId + 1).toString(); + Message _createAnswerStreamMessage({ + required AnswerStream stream, + required Int64 questionMessageId, + String? fakeQuestionMessageId, + }) { + answerStreamMessageId = fakeQuestionMessageId == null + ? (questionMessageId + 1).toString() + : "${fakeQuestionMessageId}_ans"; return TextMessage( id: answerStreamMessageId, From 38c2937f64383758eadfd0280a0bf5ca4268c893 Mon Sep 17 00:00:00 2001 From: Lucas Date: Mon, 30 Dec 2024 13:35:37 +0800 Subject: [PATCH 089/576] fix: docker ci (#7087) --- frontend/scripts/docker-buildfiles/Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frontend/scripts/docker-buildfiles/Dockerfile b/frontend/scripts/docker-buildfiles/Dockerfile index a7226d4e02..19f7565953 100644 --- a/frontend/scripts/docker-buildfiles/Dockerfile +++ b/frontend/scripts/docker-buildfiles/Dockerfile @@ -39,7 +39,8 @@ RUN dart pub global activate protoc_plugin 21.1.2 # Install build dependencies for AppFlowy using pacman RUN sudo pacman -S --needed --noconfirm jemalloc git libkeybinder3 sqlite clang rsync libnotify rocksdb zstd mpv RUN sudo ln -s /usr/bin/sha1sum /usr/bin/shasum -RUN source ~/.cargo/env && cargo install cargo-make cargo-binstall --locked +RUN source ~/.cargo/env && cargo install cargo-make --version 0.37.18 --locked +RUN source ~/.cargo/env && cargo install cargo-binstall --version 1.10.17 --locked RUN source ~/.cargo/env && cargo binstall duckscript_cli --locked -y # Build AppFlowy From 09fa75f5ecbecd217c3d82a142a2ca23aac6b12d Mon Sep 17 00:00:00 2001 From: "Nathan.fooo" <86001920+appflowy@users.noreply.github.com> Date: Mon, 30 Dec 2024 14:25:05 +0800 Subject: [PATCH 090/576] chore: remove crates (#7085) * chore: remove crate * chore: use rust 1.81 * chore: remove build flag * fix: macos build error * chore: upgrade super_clipboard * chore: fix pb lint warning * chore: fix clippy --------- Co-authored-by: Lucas.Xu --- .github/workflows/android_ci.yaml.bak | 2 +- .github/workflows/flutter_ci.yaml | 22 ++--- .github/workflows/ios_ci.yaml | 2 +- .github/workflows/release.yml | 22 ++--- .github/workflows/rust_ci.yaml | 2 +- .github/workflows/rust_coverage.yml | 2 +- .github/workflows/tauri2_ci.yaml | 2 +- .github/workflows/tauri_ci.yaml | 4 +- .github/workflows/tauri_release.yml | 2 +- frontend/appflowy_flutter/macos/Podfile.lock | 42 ++++----- .../macos/Runner.xcodeproj/project.pbxproj | 4 + .../lib/dispatch/dispatch.dart | 3 - frontend/appflowy_flutter/pubspec.lock | 8 +- frontend/appflowy_flutter/pubspec.yaml | 2 +- frontend/appflowy_tauri/src-tauri/Cargo.toml | 4 +- .../appflowy_web_app/src-tauri/Cargo.toml | 3 +- frontend/rust-lib/Cargo.lock | 91 +++++++++---------- frontend/rust-lib/Cargo.toml | 4 - .../build-tool/flowy-codegen/Cargo.toml | 39 ++++---- .../flowy-codegen/src/protobuf_file/mod.rs | 35 ++++++- frontend/rust-lib/dart-ffi/Cargo.toml | 1 - .../event-integration-test/Cargo.toml | 1 - frontend/rust-lib/flowy-config/Cargo.toml | 24 ----- frontend/rust-lib/flowy-config/Flowy.toml | 3 - frontend/rust-lib/flowy-config/build.rs | 23 ----- .../rust-lib/flowy-config/src/entities.rs | 16 ---- .../flowy-config/src/event_handler.rs | 56 ------------ .../rust-lib/flowy-config/src/event_map.rs | 31 ------- frontend/rust-lib/flowy-config/src/lib.rs | 4 - frontend/rust-lib/flowy-core/Cargo.toml | 2 - frontend/rust-lib/flowy-core/src/module.rs | 6 -- frontend/rust-lib/flowy-encrypt/Cargo.toml | 20 ---- frontend/rust-lib/flowy-encrypt/src/lib.rs | 3 - frontend/rust-lib/flowy-server/Cargo.toml | 11 +-- frontend/rust-lib/flowy-user/Cargo.toml | 3 +- .../flowy-user/src/services/cloud_config.rs | 2 +- .../user_manager/manager_user_encryption.rs | 7 +- frontend/rust-lib/lib-infra/Cargo.toml | 9 ++ .../src/encryption/mod.rs} | 7 +- frontend/rust-lib/lib-infra/src/lib.rs | 2 + frontend/rust-lib/rust-toolchain.toml | 2 +- frontend/scripts/docker-buildfiles/Dockerfile | 4 +- frontend/scripts/makefile/desktop.toml | 5 +- frontend/scripts/makefile/web.toml | 34 +++---- 44 files changed, 208 insertions(+), 363 deletions(-) delete mode 100644 frontend/rust-lib/flowy-config/Cargo.toml delete mode 100644 frontend/rust-lib/flowy-config/Flowy.toml delete mode 100644 frontend/rust-lib/flowy-config/build.rs delete mode 100644 frontend/rust-lib/flowy-config/src/entities.rs delete mode 100644 frontend/rust-lib/flowy-config/src/event_handler.rs delete mode 100644 frontend/rust-lib/flowy-config/src/event_map.rs delete mode 100644 frontend/rust-lib/flowy-config/src/lib.rs delete mode 100644 frontend/rust-lib/flowy-encrypt/Cargo.toml delete mode 100644 frontend/rust-lib/flowy-encrypt/src/lib.rs rename frontend/rust-lib/{flowy-encrypt/src/encrypt.rs => lib-infra/src/encryption/mod.rs} (98%) diff --git a/.github/workflows/android_ci.yaml.bak b/.github/workflows/android_ci.yaml.bak index 002255d159..a45ef67711 100644 --- a/.github/workflows/android_ci.yaml.bak +++ b/.github/workflows/android_ci.yaml.bak @@ -20,7 +20,7 @@ on: env: CARGO_TERM_COLOR: always FLUTTER_VERSION: "3.22.3" - RUST_TOOLCHAIN: "1.80.1" + RUST_TOOLCHAIN: "1.81.0" CARGO_MAKE_VERSION: "0.37.18" CLOUD_VERSION: 0.6.54-amd64 diff --git a/.github/workflows/flutter_ci.yaml b/.github/workflows/flutter_ci.yaml index 86d5b7ef30..42daeca881 100644 --- a/.github/workflows/flutter_ci.yaml +++ b/.github/workflows/flutter_ci.yaml @@ -26,7 +26,7 @@ on: env: CARGO_TERM_COLOR: always FLUTTER_VERSION: "3.22.2" - RUST_TOOLCHAIN: "1.80.1" + RUST_TOOLCHAIN: "1.81.0" CARGO_MAKE_VERSION: "0.37.18" CLOUD_VERSION: 0.6.54-amd64 @@ -40,7 +40,7 @@ jobs: strategy: fail-fast: true matrix: - os: [ubuntu-latest] + os: [ ubuntu-latest ] include: - os: ubuntu-latest flutter_profile: development-linux-x86_64 @@ -74,7 +74,7 @@ jobs: strategy: fail-fast: true matrix: - os: [windows-latest] + os: [ windows-latest ] include: - os: windows-latest flutter_profile: development-windows-x86 @@ -101,7 +101,7 @@ jobs: strategy: fail-fast: true matrix: - os: [macos-latest] + os: [ macos-latest ] include: - os: macos-latest flutter_profile: development-mac-x86_64 @@ -123,12 +123,12 @@ jobs: flutter_profile: ${{ matrix.flutter_profile }} unit_test: - needs: [prepare-linux] + needs: [ prepare-linux ] if: github.event.pull_request.draft != true strategy: fail-fast: false matrix: - os: [ubuntu-latest] + os: [ ubuntu-latest ] include: - os: ubuntu-latest flutter_profile: development-linux-x86_64 @@ -217,11 +217,11 @@ jobs: shell: bash cloud_integration_test: - needs: [prepare-linux] + needs: [ prepare-linux ] strategy: fail-fast: false matrix: - os: [ubuntu-latest] + os: [ ubuntu-latest ] include: - os: ubuntu-latest flutter_profile: development-linux-x86_64 @@ -340,13 +340,13 @@ jobs: shell: bash integration_test: - needs: [prepare-linux] + needs: [ prepare-linux ] if: github.event.pull_request.draft != true strategy: fail-fast: false matrix: - os: [ubuntu-latest] - test_number: [1, 2, 3, 4, 5, 6, 7, 8, 9] + os: [ ubuntu-latest ] + test_number: [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ] include: - os: ubuntu-latest target: "x86_64-unknown-linux-gnu" diff --git a/.github/workflows/ios_ci.yaml b/.github/workflows/ios_ci.yaml index 1cdc997180..bd71c77b3b 100644 --- a/.github/workflows/ios_ci.yaml +++ b/.github/workflows/ios_ci.yaml @@ -21,7 +21,7 @@ on: env: FLUTTER_VERSION: "3.22.3" - RUST_TOOLCHAIN: "1.80.1" + RUST_TOOLCHAIN: "1.81.0" concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 86ad99c6dc..91e6b99565 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -7,7 +7,7 @@ on: env: FLUTTER_VERSION: "3.22.0" - RUST_TOOLCHAIN: "1.80.1" + RUST_TOOLCHAIN: "1.81.0" jobs: create-release: @@ -232,10 +232,10 @@ jobs: matrix: job: - { - targets: "aarch64-apple-darwin,x86_64-apple-darwin", - os: macos-latest, - extra-build-args: "", - } + targets: "aarch64-apple-darwin,x86_64-apple-darwin", + os: macos-latest, + extra-build-args: "", + } steps: - name: Checkout source code uses: actions/checkout@v4 @@ -336,12 +336,12 @@ jobs: matrix: job: - { - arch: x86_64, - target: x86_64-unknown-linux-gnu, - os: ubuntu-20.04, - extra-build-args: "", - flutter_profile: production-linux-x86_64, - } + arch: x86_64, + target: x86_64-unknown-linux-gnu, + os: ubuntu-20.04, + extra-build-args: "", + flutter_profile: production-linux-x86_64, + } steps: - name: Checkout source code uses: actions/checkout@v4 diff --git a/.github/workflows/rust_ci.yaml b/.github/workflows/rust_ci.yaml index a67fd9d6ef..36c2e82064 100644 --- a/.github/workflows/rust_ci.yaml +++ b/.github/workflows/rust_ci.yaml @@ -19,7 +19,7 @@ on: env: CARGO_TERM_COLOR: always CLOUD_VERSION: 0.8.3-amd64 - RUST_TOOLCHAIN: "1.80.1" + RUST_TOOLCHAIN: "1.81.0" jobs: ubuntu-job: diff --git a/.github/workflows/rust_coverage.yml b/.github/workflows/rust_coverage.yml index 94a80aa95a..916f19d9fc 100644 --- a/.github/workflows/rust_coverage.yml +++ b/.github/workflows/rust_coverage.yml @@ -11,7 +11,7 @@ on: env: CARGO_TERM_COLOR: always FLUTTER_VERSION: "3.22.0" - RUST_TOOLCHAIN: "1.80.1" + RUST_TOOLCHAIN: "1.81.0" jobs: tests: diff --git a/.github/workflows/tauri2_ci.yaml b/.github/workflows/tauri2_ci.yaml index b4b9183d01..8ce4dac12b 100644 --- a/.github/workflows/tauri2_ci.yaml +++ b/.github/workflows/tauri2_ci.yaml @@ -12,7 +12,7 @@ on: env: NODE_VERSION: "18.16.0" PNPM_VERSION: "8.5.0" - RUST_TOOLCHAIN: "1.80.1" + RUST_TOOLCHAIN: "1.81.0" CARGO_MAKE_VERSION: "0.36.6" CI: true diff --git a/.github/workflows/tauri_ci.yaml b/.github/workflows/tauri_ci.yaml index 7d9c67e25a..fb2e874204 100644 --- a/.github/workflows/tauri_ci.yaml +++ b/.github/workflows/tauri_ci.yaml @@ -7,7 +7,7 @@ on: env: NODE_VERSION: "18.16.0" PNPM_VERSION: "8.5.0" - RUST_TOOLCHAIN: "1.80.1" + RUST_TOOLCHAIN: "1.81.0" concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} @@ -19,7 +19,7 @@ jobs: strategy: fail-fast: false matrix: - platform: [ubuntu-20.04] + platform: [ ubuntu-20.04 ] runs-on: ${{ matrix.platform }} diff --git a/.github/workflows/tauri_release.yml b/.github/workflows/tauri_release.yml index 4612d5c3aa..e88af2190e 100644 --- a/.github/workflows/tauri_release.yml +++ b/.github/workflows/tauri_release.yml @@ -14,7 +14,7 @@ on: env: NODE_VERSION: "18.16.0" PNPM_VERSION: "8.5.0" - RUST_TOOLCHAIN: "1.80.1" + RUST_TOOLCHAIN: "1.81.0" jobs: publish-tauri: diff --git a/frontend/appflowy_flutter/macos/Podfile.lock b/frontend/appflowy_flutter/macos/Podfile.lock index d5ac7975b9..2d9b24cdaa 100644 --- a/frontend/appflowy_flutter/macos/Podfile.lock +++ b/frontend/appflowy_flutter/macos/Podfile.lock @@ -130,31 +130,31 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral/.symlinks/plugins/window_manager/macos SPEC CHECKSUMS: - app_links: 10e0a0ab602ffaf34d142cd4862f29d34b303b2a - appflowy_backend: 865496343de667fc8c600e04b9fd05234e130cf9 - bitsdojo_window_macos: 44e3b8fe3dd463820e0321f6256c5b1c16bb6a00 - connectivity_plus: 18d3c32514c886e046de60e9c13895109866c747 - desktop_drop: 69eeff437544aa619c8db7f4481b3a65f7696898 - device_info_plus: ce1b7762849d3ec103d0e0517299f2db7ad60720 - file_selector_macos: 54fdab7caa3ac3fc43c9fac4d7d8d231277f8cf2 - flowy_infra_ui: 03301a39ad118771adbf051a664265c61c507f38 + app_links: 9028728e32c83a0831d9db8cf91c526d16cc5468 + appflowy_backend: 464aeb3e5c6966a41641a2111e5ead72ce2695f7 + bitsdojo_window_macos: 7959fb0ca65a3ccda30095c181ecb856fae48ea9 + connectivity_plus: e74b9f74717d2d99d45751750e266e55912baeb5 + desktop_drop: e0b672a7d84c0a6cbc378595e82cdb15f2970a43 + device_info_plus: a56e6e74dbbd2bb92f2da12c64ddd4f67a749041 + file_selector_macos: 585232b688707857504f9cb5f985a7c97fe4dd30 + flowy_infra_ui: 8760ff42a789de40bf5007a5f176b454722a341e FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 HotKey: e96d8a2ddbf4591131e2bb3f54e69554d90cdca6 - hotkey_manager: c32bf0bfe8f934b7bc17ab4ad5c4c142960b023c - irondash_engine_context: da62996ee25616d2f01bbeb85dc115d813359478 - local_notifier: e9506bc66fc70311e8bc7291fb70f743c081e4ff - package_info_plus: fa739dd842b393193c5ca93c26798dff6e3d0e0c - path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 + hotkey_manager: b443f35f4d772162937aa73fd8995e579f8ac4e2 + irondash_engine_context: 893c7d96d20ce361d7e996f39d360c4c2f9869ba + local_notifier: ebf072651e35ae5e47280ad52e2707375cb2ae4e + package_info_plus: a8a591e70e87ce97ce5d21b2594f69cea9e0312f + path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564 ReachabilitySwift: 7f151ff156cea1481a8411701195ac6a984f4979 - screen_retriever: 59634572a57080243dd1bf715e55b6c54f241a38 + screen_retriever: 4f97c103641aab8ce183fa5af3b87029df167936 Sentry: 1fe34e9c2cbba1e347623610d26db121dcb569f1 - sentry_flutter: a39c2a2d67d5e5b9cb0b94a4985c76dd5b3fc737 - share_plus: 36537c04ce0c3e3f5bd297ce4318b6d5ee5fd6cf - shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 - sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec - super_native_extensions: 85efee3a7495b46b04befcfc86ed12069264ebf3 - url_launcher_macos: 5f437abeda8c85500ceb03f5c1938a8c5a705399 - window_manager: 3a1844359a6295ab1e47659b1a777e36773cd6e8 + sentry_flutter: e24b397f9a61fa5bbefd8279c3b2242ca86faa90 + share_plus: 11c7b7fa7020465584eca3ff6392c5bc1e399d6e + shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7 + sqflite: c35dad70033b8862124f8337cc994a809fcd9fa3 + super_native_extensions: c2795d6d9aedf4a79fae25cb6160b71b50549189 + url_launcher_macos: de10e46d8d8b9e3a7b8a133e8de92b104379f05e + window_manager: 1d01fa7ac65a6e6f83b965471b1a7fdd3f06166c PODFILE CHECKSUM: 0532f3f001ca3110b8be345d6491fff690e95823 diff --git a/frontend/appflowy_flutter/macos/Runner.xcodeproj/project.pbxproj b/frontend/appflowy_flutter/macos/Runner.xcodeproj/project.pbxproj index 1c3367e430..88c451bdd9 100644 --- a/frontend/appflowy_flutter/macos/Runner.xcodeproj/project.pbxproj +++ b/frontend/appflowy_flutter/macos/Runner.xcodeproj/project.pbxproj @@ -29,6 +29,7 @@ 706E045829F286F600B789F4 /* libc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 706E045729F286EC00B789F4 /* libc++.tbd */; }; D7360C6D6177708F7B2D3C9D /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1CD81A6C7244B2318E0BA2E8 /* Pods_Runner.framework */; }; FB4E0E4F2CC9F3F900C57E87 /* libbz2.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = FB4E0E4E2CC9F3E900C57E87 /* libbz2.tbd */; }; + FB54062C2D22665000223D60 /* liblzma.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = FB54062B2D22664200223D60 /* liblzma.tbd */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -77,6 +78,7 @@ 7D41C30A3910C3A40B6085E3 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; FB4E0E4E2CC9F3E900C57E87 /* libbz2.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libbz2.tbd; path = usr/lib/libbz2.tbd; sourceTree = SDKROOT; }; + FB54062B2D22664200223D60 /* liblzma.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = liblzma.tbd; path = usr/lib/liblzma.tbd; sourceTree = SDKROOT; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -86,6 +88,7 @@ files = ( FB4E0E4F2CC9F3F900C57E87 /* libbz2.tbd in Frameworks */, 706E045829F286F600B789F4 /* libc++.tbd in Frameworks */, + FB54062C2D22665000223D60 /* liblzma.tbd in Frameworks */, D7360C6D6177708F7B2D3C9D /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -171,6 +174,7 @@ D73912EC22F37F3D000D13A0 /* Frameworks */ = { isa = PBXGroup; children = ( + FB54062B2D22664200223D60 /* liblzma.tbd */, FB4E0E4E2CC9F3E900C57E87 /* libbz2.tbd */, 706E045729F286EC00B789F4 /* libc++.tbd */, 1CD81A6C7244B2318E0BA2E8 /* Pods_Runner.framework */, diff --git a/frontend/appflowy_flutter/packages/appflowy_backend/lib/dispatch/dispatch.dart b/frontend/appflowy_flutter/packages/appflowy_backend/lib/dispatch/dispatch.dart index 552c6a268f..12fdd60ccf 100644 --- a/frontend/appflowy_flutter/packages/appflowy_backend/lib/dispatch/dispatch.dart +++ b/frontend/appflowy_flutter/packages/appflowy_backend/lib/dispatch/dispatch.dart @@ -24,8 +24,6 @@ import 'package:isolates/isolates.dart'; import 'package:isolates/ports.dart'; import 'package:protobuf/protobuf.dart'; -import '../protobuf/flowy-config/entities.pb.dart'; -import '../protobuf/flowy-config/event_map.pb.dart'; import '../protobuf/flowy-date/entities.pb.dart'; import '../protobuf/flowy-date/event_map.pb.dart'; @@ -35,7 +33,6 @@ part 'dart_event/flowy-folder/dart_event.dart'; part 'dart_event/flowy-user/dart_event.dart'; part 'dart_event/flowy-database2/dart_event.dart'; part 'dart_event/flowy-document/dart_event.dart'; -part 'dart_event/flowy-config/dart_event.dart'; part 'dart_event/flowy-date/dart_event.dart'; part 'dart_event/flowy-search/dart_event.dart'; part 'dart_event/flowy-ai/dart_event.dart'; diff --git a/frontend/appflowy_flutter/pubspec.lock b/frontend/appflowy_flutter/pubspec.lock index 87a5128960..c0df44a207 100644 --- a/frontend/appflowy_flutter/pubspec.lock +++ b/frontend/appflowy_flutter/pubspec.lock @@ -1989,18 +1989,18 @@ packages: dependency: "direct main" description: name: super_clipboard - sha256: cfeb142360fac67e0da1ca339accb892eb790c6528a218a008eef1709d96ed0f + sha256: "4a6ae6dfaa282ec1f2bff750976f535517ed8ca842d5deae13985eb11c00ac1f" url: "https://pub.dev" source: hosted - version: "0.8.22" + version: "0.8.24" super_native_extensions: dependency: transitive description: name: super_native_extensions - sha256: "6a7cfb7d212da7023b86fb99c736081e9c2cd982265d15dc5fe6381a32dbc875" + sha256: a433bba8186cd6b707560c42535bf284804665231c00bca86faf1aa4968b7637 url: "https://pub.dev" source: hosted - version: "0.8.22" + version: "0.8.24" sync_http: dependency: transitive description: diff --git a/frontend/appflowy_flutter/pubspec.yaml b/frontend/appflowy_flutter/pubspec.yaml index 759c16e474..60ab502b2b 100644 --- a/frontend/appflowy_flutter/pubspec.yaml +++ b/frontend/appflowy_flutter/pubspec.yaml @@ -129,7 +129,7 @@ dependencies: sized_context: ^1.0.0+4 string_validator: ^1.0.0 styled_widget: ^0.4.1 - super_clipboard: ^0.8.4 + super_clipboard: ^0.8.24 synchronized: ^3.1.0+1 table_calendar: ^3.0.9 time: ^2.1.3 diff --git a/frontend/appflowy_tauri/src-tauri/Cargo.toml b/frontend/appflowy_tauri/src-tauri/Cargo.toml index 196a93699a..f85e7c9151 100644 --- a/frontend/appflowy_tauri/src-tauri/Cargo.toml +++ b/frontend/appflowy_tauri/src-tauri/Cargo.toml @@ -19,7 +19,8 @@ tracing = "0.1.40" bytes = "1.5.0" serde = "1.0" serde_json = "1.0.108" -protobuf = { version = "2.28.0" } +protobuf.workspace = true + diesel = { version = "2.1.0", features = [ "sqlite", "chrono", @@ -78,7 +79,6 @@ lib-dispatch = { path = "../../rust-lib/lib-dispatch", features = [ ] } flowy-core = { path = "../../rust-lib/flowy-core", features = ["ts"] } flowy-user = { path = "../../rust-lib/flowy-user", features = ["tauri_ts"] } -flowy-config = { path = "../../rust-lib/flowy-config", features = ["tauri_ts"] } flowy-date = { path = "../../rust-lib/flowy-date", features = ["tauri_ts"] } flowy-ai = { path = "../../rust-lib/flowy-ai", features = ["tauri_ts"] } flowy-error = { path = "../../rust-lib/flowy-error", features = [ diff --git a/frontend/appflowy_web_app/src-tauri/Cargo.toml b/frontend/appflowy_web_app/src-tauri/Cargo.toml index 56fa4bc799..90479b42d1 100644 --- a/frontend/appflowy_web_app/src-tauri/Cargo.toml +++ b/frontend/appflowy_web_app/src-tauri/Cargo.toml @@ -19,7 +19,7 @@ tracing = "0.1.40" bytes = "1.5.0" serde = "1.0" serde_json = "1.0.108" -protobuf = { version = "2.28.0" } +protobuf.workspace = true diesel = { version = "2.1.0", features = [ "sqlite", "chrono", @@ -77,7 +77,6 @@ lib-dispatch = { path = "../../rust-lib/lib-dispatch", features = [ ] } flowy-core = { path = "../../rust-lib/flowy-core", features = ["ts"] } flowy-user = { path = "../../rust-lib/flowy-user", features = ["tauri_ts"] } -flowy-config = { path = "../../rust-lib/flowy-config", features = ["tauri_ts"] } flowy-date = { path = "../../rust-lib/flowy-date", features = ["tauri_ts"] } flowy-error = { path = "../../rust-lib/flowy-error", features = [ "impl_from_sqlite", diff --git a/frontend/rust-lib/Cargo.lock b/frontend/rust-lib/Cargo.lock index 54282cd279..34d76452a6 100644 --- a/frontend/rust-lib/Cargo.lock +++ b/frontend/rust-lib/Cargo.lock @@ -1393,7 +1393,7 @@ dependencies = [ "cssparser-macros", "dtoa-short", "itoa", - "phf 0.8.0", + "phf 0.11.2", "smallvec", ] @@ -1482,7 +1482,6 @@ dependencies = [ "collab-integrate", "crossbeam-utils", "flowy-codegen", - "flowy-config", "flowy-core", "flowy-date", "flowy-derive", @@ -1821,7 +1820,6 @@ dependencies = [ "flowy-database2", "flowy-document", "flowy-document-pub", - "flowy-encrypt", "flowy-folder", "flowy-folder-pub", "flowy-notification", @@ -2085,20 +2083,6 @@ dependencies = [ "walkdir", ] -[[package]] -name = "flowy-config" -version = "0.1.0" -dependencies = [ - "bytes", - "flowy-codegen", - "flowy-derive", - "flowy-error", - "flowy-sqlite", - "lib-dispatch", - "protobuf", - "strum_macros 0.21.1", -] - [[package]] name = "flowy-core" version = "0.1.0" @@ -2119,7 +2103,6 @@ dependencies = [ "diesel", "flowy-ai", "flowy-ai-pub", - "flowy-config", "flowy-database-pub", "flowy-database2", "flowy-date", @@ -2296,20 +2279,6 @@ dependencies = [ "lib-infra", ] -[[package]] -name = "flowy-encrypt" -version = "0.1.0" -dependencies = [ - "aes-gcm", - "anyhow", - "base64 0.21.5", - "getrandom 0.2.10", - "hmac", - "pbkdf2 0.12.2", - "rand 0.8.5", - "sha2", -] - [[package]] name = "flowy-error" version = "0.1.0" @@ -2483,7 +2452,6 @@ dependencies = [ "flowy-ai-pub", "flowy-database-pub", "flowy-document-pub", - "flowy-encrypt", "flowy-error", "flowy-folder-pub", "flowy-search-pub", @@ -2622,7 +2590,6 @@ dependencies = [ "fancy-regex 0.11.0", "flowy-codegen", "flowy-derive", - "flowy-encrypt", "flowy-error", "flowy-folder-pub", "flowy-notification", @@ -3675,10 +3642,12 @@ dependencies = [ name = "lib-infra" version = "0.1.0" dependencies = [ + "aes-gcm", "allo-isolate", "anyhow", "async-trait", "atomic_refcell", + "base64 0.22.1", "brotli", "bytes", "cfg-if", @@ -3686,9 +3655,12 @@ dependencies = [ "futures", "futures-core", "futures-util", + "hmac", "md5", + "pbkdf2 0.12.2", "pin-project", "rand 0.8.5", + "sha2", "tempfile", "tokio", "tracing", @@ -4504,7 +4476,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" dependencies = [ - "phf_macros", + "phf_macros 0.8.0", "phf_shared 0.8.0", "proc-macro-hack", ] @@ -4524,6 +4496,7 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" dependencies = [ + "phf_macros 0.11.2", "phf_shared 0.11.2", ] @@ -4591,6 +4564,19 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "phf_macros" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" +dependencies = [ + "phf_generator 0.11.2", + "phf_shared 0.11.2", + "proc-macro2", + "quote", + "syn 2.0.47", +] + [[package]] name = "phf_shared" version = "0.8.0" @@ -4908,53 +4894,60 @@ dependencies = [ [[package]] name = "protoc-bin-vendored" -version = "3.0.0" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "005ca8623e5633e298ad1f917d8be0a44bcf406bf3cde3b80e63003e49a3f27d" +checksum = "dd89a830d0eab2502c81a9b8226d446a52998bb78e5e33cb2637c0cdd6068d99" dependencies = [ "protoc-bin-vendored-linux-aarch_64", "protoc-bin-vendored-linux-ppcle_64", "protoc-bin-vendored-linux-x86_32", "protoc-bin-vendored-linux-x86_64", + "protoc-bin-vendored-macos-aarch_64", "protoc-bin-vendored-macos-x86_64", "protoc-bin-vendored-win32", ] [[package]] name = "protoc-bin-vendored-linux-aarch_64" -version = "3.0.0" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fb9fc9cce84c8694b6ea01cc6296617b288b703719b725b8c9c65f7c5874435" +checksum = "f563627339f1653ea1453dfbcb4398a7369b768925eb14499457aeaa45afe22c" [[package]] name = "protoc-bin-vendored-linux-ppcle_64" -version = "3.0.0" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02d2a07dcf7173a04d49974930ccbfb7fd4d74df30ecfc8762cf2f895a094516" +checksum = "5025c949a02cd3b60c02501dd0f348c16e8fff464f2a7f27db8a9732c608b746" [[package]] name = "protoc-bin-vendored-linux-x86_32" -version = "3.0.0" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54fef0b04fcacba64d1d80eed74a20356d96847da8497a59b0a0a436c9165b0" +checksum = "9c9500ce67d132c2f3b572504088712db715755eb9adf69d55641caa2cb68a07" [[package]] name = "protoc-bin-vendored-linux-x86_64" -version = "3.0.0" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8782f2ce7d43a9a5c74ea4936f001e9e8442205c244f7a3d4286bd4c37bc924" +checksum = "5462592380cefdc9f1f14635bcce70ba9c91c1c2464c7feb2ce564726614cc41" + +[[package]] +name = "protoc-bin-vendored-macos-aarch_64" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c637745681b68b4435484543667a37606c95ddacf15e917710801a0877506030" [[package]] name = "protoc-bin-vendored-macos-x86_64" -version = "3.0.0" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5de656c7ee83f08e0ae5b81792ccfdc1d04e7876b1d9a38e6876a9e09e02537" +checksum = "38943f3c90319d522f94a6dfd4a134ba5e36148b9506d2d9723a82ebc57c8b55" [[package]] name = "protoc-bin-vendored-win32" -version = "3.0.0" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9653c3ed92974e34c5a6e0a510864dab979760481714c172e0a34e437cb98804" +checksum = "7dc55d7dec32ecaf61e0bd90b3d2392d721a28b95cfd23c3e176eccefbeab2f2" [[package]] name = "protoc-rust" diff --git a/frontend/rust-lib/Cargo.toml b/frontend/rust-lib/Cargo.toml index 1ddcb82c26..fd9b7ac1e9 100644 --- a/frontend/rust-lib/Cargo.toml +++ b/frontend/rust-lib/Cargo.toml @@ -18,8 +18,6 @@ members = [ "flowy-database-pub", "flowy-server", "flowy-server-pub", - "flowy-config", - "flowy-encrypt", "flowy-storage", "collab-integrate", "flowy-date", @@ -58,8 +56,6 @@ flowy-database2 = { path = "flowy-database2" } flowy-database-pub = { path = "flowy-database-pub" } flowy-server = { path = "flowy-server" } flowy-server-pub = { path = "flowy-server-pub" } -flowy-config = { path = "flowy-config" } -flowy-encrypt = { path = "flowy-encrypt" } flowy-storage = { path = "flowy-storage" } flowy-storage-pub = { path = "flowy-storage-pub" } flowy-search = { path = "flowy-search" } diff --git a/frontend/rust-lib/build-tool/flowy-codegen/Cargo.toml b/frontend/rust-lib/build-tool/flowy-codegen/Cargo.toml index 92a273ad04..27d36c310c 100644 --- a/frontend/rust-lib/build-tool/flowy-codegen/Cargo.toml +++ b/frontend/rust-lib/build-tool/flowy-codegen/Cargo.toml @@ -7,41 +7,42 @@ edition = "2021" [dependencies] log = "0.4.17" -serde = { workspace = true, features = ["derive"]} +serde = { workspace = true, features = ["derive"] } serde_json.workspace = true flowy-ast.workspace = true quote = "1.0" cmd_lib = { version = "1.3.0", optional = true } -protoc-rust = { version = "2", optional = true } +protoc-rust = { version = "2.28.0", optional = true } +#protobuf-codegen = { version = "3.7.1" } walkdir = { version = "2", optional = true } similar = { version = "1.3.0", optional = true } syn = { version = "1.0.109", features = ["extra-traits", "parsing", "derive", "full"] } fancy-regex = { version = "0.10.0", optional = true } lazy_static = { version = "1.4.0", optional = true } -tera = { version = "1.17.1", optional = true} +tera = { version = "1.17.1", optional = true } itertools = { version = "0.10", optional = true } phf = { version = "0.8.0", features = ["macros"], optional = true } -console = {version = "0.14.1", optional = true} -protoc-bin-vendored = { version = "3.0", optional = true } -toml = {version = "0.5.11", optional = true} +console = { version = "0.14.1", optional = true } +protoc-bin-vendored = { version = "3.1.0", optional = true } +toml = { version = "0.5.11", optional = true } [features] proto_gen = [ - "similar", - "fancy-regex", - "lazy_static", - "tera", - "itertools", - "phf", - "walkdir", - "console", - "toml", - "cmd_lib", - "protoc-rust", - "walkdir", - "protoc-bin-vendored", + "similar", + "fancy-regex", + "lazy_static", + "tera", + "itertools", + "phf", + "walkdir", + "console", + "toml", + "cmd_lib", + "protoc-rust", + "walkdir", + "protoc-bin-vendored", ] dart_event = ["walkdir", "tera", ] dart = ["proto_gen", "dart_event"] diff --git a/frontend/rust-lib/build-tool/flowy-codegen/src/protobuf_file/mod.rs b/frontend/rust-lib/build-tool/flowy-codegen/src/protobuf_file/mod.rs index 75b4b8eb4f..1ddb1bdeb0 100644 --- a/frontend/rust-lib/build-tool/flowy-codegen/src/protobuf_file/mod.rs +++ b/frontend/rust-lib/build-tool/flowy-codegen/src/protobuf_file/mod.rs @@ -12,8 +12,9 @@ use itertools::Itertools; use log::info; pub use proto_gen::*; pub use proto_info::*; +use std::fs; use std::fs::File; -use std::io::Write; +use std::io::{BufRead, BufReader, Write}; use std::path::{Path, PathBuf}; use std::process::Command; use walkdir::WalkDir; @@ -147,6 +148,38 @@ fn generate_rust_protobuf_files( .include(proto_file_output_path) .run() .expect("Running rust protoc failed."); + remove_box_pointers_lint_from_all_except_mod(protobuf_output_path); +} +fn remove_box_pointers_lint_from_all_except_mod(dir_path: &str) { + let dir = fs::read_dir(dir_path).expect("Failed to read directory"); + for entry in dir { + let entry = entry.expect("Failed to read directory entry"); + let path = entry.path(); + + // Skip directories and mod.rs + if path.is_file() { + if let Some(file_name) = path.file_name().and_then(|f| f.to_str()) { + if file_name != "mod.rs" { + remove_box_pointers_lint(&path); + } + } + } + } +} + +fn remove_box_pointers_lint(file_path: &Path) { + let file = File::open(file_path).expect("Failed to open file"); + let reader = BufReader::new(file); + let lines: Vec = reader + .lines() + .map_while(Result::ok) + .filter(|line| !line.contains("#![allow(box_pointers)]")) + .collect(); + + let mut file = File::create(file_path).expect("Failed to create file"); + for line in lines { + writeln!(file, "{}", line).expect("Failed to write line"); + } } #[cfg(feature = "ts")] diff --git a/frontend/rust-lib/dart-ffi/Cargo.toml b/frontend/rust-lib/dart-ffi/Cargo.toml index 91ed0d9bf6..8ad17c028f 100644 --- a/frontend/rust-lib/dart-ffi/Cargo.toml +++ b/frontend/rust-lib/dart-ffi/Cargo.toml @@ -36,7 +36,6 @@ flowy-core = { workspace = true } flowy-notification = { workspace = true, features = ["dart"] } flowy-document = { workspace = true, features = ["dart"] } -flowy-config = { workspace = true, features = ["dart"] } flowy-user = { workspace = true, features = ["dart"] } flowy-date = { workspace = true, features = ["dart"] } flowy-server = { workspace = true } diff --git a/frontend/rust-lib/event-integration-test/Cargo.toml b/frontend/rust-lib/event-integration-test/Cargo.toml index 33e4f4b184..392d82d858 100644 --- a/frontend/rust-lib/event-integration-test/Cargo.toml +++ b/frontend/rust-lib/event-integration-test/Cargo.toml @@ -15,7 +15,6 @@ flowy-database2 = { path = "../flowy-database2" } flowy-database-pub = { workspace = true } flowy-document = { path = "../flowy-document" } flowy-document-pub = { workspace = true } -flowy-encrypt = { workspace = true } flowy-ai = { workspace = true } lib-dispatch = { workspace = true } lib-infra = { workspace = true } diff --git a/frontend/rust-lib/flowy-config/Cargo.toml b/frontend/rust-lib/flowy-config/Cargo.toml deleted file mode 100644 index 821fcda2fc..0000000000 --- a/frontend/rust-lib/flowy-config/Cargo.toml +++ /dev/null @@ -1,24 +0,0 @@ -[package] -name = "flowy-config" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -# workspace -flowy-sqlite = { workspace = true } -lib-dispatch = { workspace = true } -flowy-error = { workspace = true } - -flowy-derive.workspace = true -protobuf.workspace = true -bytes.workspace = true -strum_macros = "0.21" - -[build-dependencies] -flowy-codegen.workspace = true - -[features] -dart = ["flowy-codegen/dart"] -tauri_ts = ["flowy-codegen/ts"] diff --git a/frontend/rust-lib/flowy-config/Flowy.toml b/frontend/rust-lib/flowy-config/Flowy.toml deleted file mode 100644 index 0dbe74b3e3..0000000000 --- a/frontend/rust-lib/flowy-config/Flowy.toml +++ /dev/null @@ -1,3 +0,0 @@ -# Check out the FlowyConfig (located in flowy_toml.rs) for more details. -proto_input = ["src/event_map.rs", "src/entities.rs"] -event_files = ["src/event_map.rs"] \ No newline at end of file diff --git a/frontend/rust-lib/flowy-config/build.rs b/frontend/rust-lib/flowy-config/build.rs deleted file mode 100644 index e015eb2580..0000000000 --- a/frontend/rust-lib/flowy-config/build.rs +++ /dev/null @@ -1,23 +0,0 @@ -fn main() { - #[cfg(feature = "dart")] - { - flowy_codegen::protobuf_file::dart_gen(env!("CARGO_PKG_NAME")); - flowy_codegen::dart_event::gen(env!("CARGO_PKG_NAME")); - } - - #[cfg(feature = "tauri_ts")] - { - flowy_codegen::ts_event::gen(env!("CARGO_PKG_NAME"), flowy_codegen::Project::Tauri); - flowy_codegen::protobuf_file::ts_gen( - env!("CARGO_PKG_NAME"), - env!("CARGO_PKG_NAME"), - flowy_codegen::Project::Tauri, - ); - flowy_codegen::ts_event::gen(env!("CARGO_PKG_NAME"), flowy_codegen::Project::TauriApp); - flowy_codegen::protobuf_file::ts_gen( - env!("CARGO_PKG_NAME"), - env!("CARGO_PKG_NAME"), - flowy_codegen::Project::TauriApp, - ); - } -} diff --git a/frontend/rust-lib/flowy-config/src/entities.rs b/frontend/rust-lib/flowy-config/src/entities.rs deleted file mode 100644 index 931724d542..0000000000 --- a/frontend/rust-lib/flowy-config/src/entities.rs +++ /dev/null @@ -1,16 +0,0 @@ -use flowy_derive::ProtoBuf; - -#[derive(Default, ProtoBuf)] -pub struct KeyValuePB { - #[pb(index = 1)] - pub key: String, - - #[pb(index = 2, one_of)] - pub value: Option, -} - -#[derive(Default, ProtoBuf)] -pub struct KeyPB { - #[pb(index = 1)] - pub key: String, -} diff --git a/frontend/rust-lib/flowy-config/src/event_handler.rs b/frontend/rust-lib/flowy-config/src/event_handler.rs deleted file mode 100644 index 3bd97f8cb5..0000000000 --- a/frontend/rust-lib/flowy-config/src/event_handler.rs +++ /dev/null @@ -1,56 +0,0 @@ -use std::sync::Weak; - -use flowy_error::{FlowyError, FlowyResult}; -use flowy_sqlite::kv::KVStorePreferences; -use lib_dispatch::prelude::{data_result_ok, AFPluginData, AFPluginState, DataResult}; - -use crate::entities::{KeyPB, KeyValuePB}; - -pub(crate) async fn set_key_value_handler( - store_preferences: AFPluginState>, - data: AFPluginData, -) -> FlowyResult<()> { - let data = data.into_inner(); - - if let Some(store_preferences) = store_preferences.upgrade() { - match data.value { - None => store_preferences.remove(&data.key), - Some(value) => { - store_preferences.set_str(&data.key, value); - }, - } - } - - Ok(()) -} - -pub(crate) async fn get_key_value_handler( - store_preferences: AFPluginState>, - data: AFPluginData, -) -> DataResult { - match store_preferences.upgrade() { - None => Err(FlowyError::internal().with_context("The store preferences is already drop"))?, - Some(store_preferences) => { - let data = data.into_inner(); - let value = store_preferences.get_str(&data.key); - data_result_ok(KeyValuePB { - key: data.key, - value, - }) - }, - } -} - -pub(crate) async fn remove_key_value_handler( - store_preferences: AFPluginState>, - data: AFPluginData, -) -> FlowyResult<()> { - match store_preferences.upgrade() { - None => Err(FlowyError::internal().with_context("The store preferences is already drop"))?, - Some(store_preferences) => { - let data = data.into_inner(); - store_preferences.remove(&data.key); - Ok(()) - }, - } -} diff --git a/frontend/rust-lib/flowy-config/src/event_map.rs b/frontend/rust-lib/flowy-config/src/event_map.rs deleted file mode 100644 index 801dbdd75d..0000000000 --- a/frontend/rust-lib/flowy-config/src/event_map.rs +++ /dev/null @@ -1,31 +0,0 @@ -use std::sync::Weak; - -use strum_macros::Display; - -use flowy_derive::{Flowy_Event, ProtoBuf_Enum}; -use flowy_sqlite::kv::KVStorePreferences; -use lib_dispatch::prelude::AFPlugin; - -use crate::event_handler::*; - -pub fn init(store_preferences: Weak) -> AFPlugin { - AFPlugin::new() - .name(env!("CARGO_PKG_NAME")) - .state(store_preferences) - .event(ConfigEvent::SetKeyValue, set_key_value_handler) - .event(ConfigEvent::GetKeyValue, get_key_value_handler) - .event(ConfigEvent::RemoveKeyValue, remove_key_value_handler) -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash, Display, ProtoBuf_Enum, Flowy_Event)] -#[event_err = "FlowyError"] -pub enum ConfigEvent { - #[event(input = "KeyValuePB")] - SetKeyValue = 0, - - #[event(input = "KeyPB", output = "KeyValuePB")] - GetKeyValue = 1, - - #[event(input = "KeyPB")] - RemoveKeyValue = 2, -} diff --git a/frontend/rust-lib/flowy-config/src/lib.rs b/frontend/rust-lib/flowy-config/src/lib.rs deleted file mode 100644 index e08a6c9ce6..0000000000 --- a/frontend/rust-lib/flowy-config/src/lib.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod entities; -mod event_handler; -pub mod event_map; -mod protobuf; diff --git a/frontend/rust-lib/flowy-core/Cargo.toml b/frontend/rust-lib/flowy-core/Cargo.toml index 09fd59d071..d1668524cb 100644 --- a/frontend/rust-lib/flowy-core/Cargo.toml +++ b/frontend/rust-lib/flowy-core/Cargo.toml @@ -20,7 +20,6 @@ flowy-document-pub = { workspace = true } flowy-error = { workspace = true } flowy-server = { workspace = true, features = ["enable_supabase"] } flowy-server-pub = { workspace = true } -flowy-config = { workspace = true } flowy-date = { workspace = true } collab-integrate = { workspace = true } flowy-search = { workspace = true } @@ -80,7 +79,6 @@ ts = [ "flowy-folder/tauri_ts", "flowy-search/tauri_ts", "flowy-database2/ts", - "flowy-config/tauri_ts", "flowy-ai/tauri_ts", "flowy-storage/tauri_ts", ] diff --git a/frontend/rust-lib/flowy-core/src/module.rs b/frontend/rust-lib/flowy-core/src/module.rs index 196fbce935..2e657bd9ca 100644 --- a/frontend/rust-lib/flowy-core/src/module.rs +++ b/frontend/rust-lib/flowy-core/src/module.rs @@ -18,15 +18,10 @@ pub fn make_plugins( ai_manager: Weak, file_storage_manager: Weak, ) -> Vec { - let store_preferences = user_session - .upgrade() - .map(|session| session.get_store_preferences()) - .unwrap(); let user_plugin = flowy_user::event_map::init(user_session); let folder_plugin = flowy_folder::event_map::init(folder_manager); let database_plugin = flowy_database2::event_map::init(database_manager); let document_plugin2 = flowy_document::event_map::init(document_manager2); - let config_plugin = flowy_config::event_map::init(store_preferences); let date_plugin = flowy_date::event_map::init(); let search_plugin = flowy_search::event_map::init(search_manager); let ai_plugin = flowy_ai::event_map::init(ai_manager); @@ -36,7 +31,6 @@ pub fn make_plugins( folder_plugin, database_plugin, document_plugin2, - config_plugin, date_plugin, search_plugin, ai_plugin, diff --git a/frontend/rust-lib/flowy-encrypt/Cargo.toml b/frontend/rust-lib/flowy-encrypt/Cargo.toml deleted file mode 100644 index 5ea42c60e2..0000000000 --- a/frontend/rust-lib/flowy-encrypt/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "flowy-encrypt" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html -[lib] -crate-type = ["cdylib", "rlib"] - -[dependencies] -aes-gcm = "0.10.2" -rand = "0.8" -pbkdf2 = "0.12.2" -hmac = "0.12.1" -sha2 = "0.10.7" -anyhow.workspace = true -base64 = "0.21.2" - -[target.'cfg(target_arch = "wasm32")'.dependencies] -getrandom = { version = "0.2", features = ["js"]} \ No newline at end of file diff --git a/frontend/rust-lib/flowy-encrypt/src/lib.rs b/frontend/rust-lib/flowy-encrypt/src/lib.rs deleted file mode 100644 index a72d275af9..0000000000 --- a/frontend/rust-lib/flowy-encrypt/src/lib.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub use encrypt::*; - -mod encrypt; diff --git a/frontend/rust-lib/flowy-server/Cargo.toml b/frontend/rust-lib/flowy-server/Cargo.toml index e5eb124fad..9e67081eb7 100644 --- a/frontend/rust-lib/flowy-server/Cargo.toml +++ b/frontend/rust-lib/flowy-server/Cargo.toml @@ -43,7 +43,6 @@ flowy-document-pub = { workspace = true } flowy-error = { workspace = true, features = ["impl_from_serde", "impl_from_reqwest", "impl_from_url", "impl_from_appflowy_cloud"] } flowy-server-pub = { workspace = true } flowy-search-pub = { workspace = true } -flowy-encrypt = { workspace = true } flowy-storage = { workspace = true } flowy-storage-pub = { workspace = true } flowy-ai-pub = { workspace = true } @@ -59,11 +58,11 @@ semver = "1.0.23" [dependencies.client-api] workspace = true features = [ - "collab-sync", - "test_util", - "enable_brotli", - # Uncomment the following line to enable verbose logging for sync - # "sync_verbose_log", + "collab-sync", + "test_util", + "enable_brotli", + # Uncomment the following line to enable verbose logging for sync + # "sync_verbose_log", ] [dev-dependencies] diff --git a/frontend/rust-lib/flowy-user/Cargo.toml b/frontend/rust-lib/flowy-user/Cargo.toml index 8eb7f06ee8..2a02043f38 100644 --- a/frontend/rust-lib/flowy-user/Cargo.toml +++ b/frontend/rust-lib/flowy-user/Cargo.toml @@ -8,10 +8,9 @@ edition = "2018" [dependencies] flowy-derive.workspace = true flowy-sqlite = { workspace = true } -flowy-encrypt = { workspace = true } flowy-error = { workspace = true, features = ["impl_from_dispatch_error", "impl_from_sqlite", "impl_from_collab_folder", "impl_from_collab_persistence", "impl_from_collab_document"] } flowy-folder-pub = { workspace = true } -lib-infra = { workspace = true } +lib-infra = { workspace = true, features = ["encryption"] } flowy-notification = { workspace = true } flowy-server-pub = { workspace = true } lib-dispatch = { workspace = true } diff --git a/frontend/rust-lib/flowy-user/src/services/cloud_config.rs b/frontend/rust-lib/flowy-user/src/services/cloud_config.rs index d4b4afc7a8..30f5725f9c 100644 --- a/frontend/rust-lib/flowy-user/src/services/cloud_config.rs +++ b/frontend/rust-lib/flowy-user/src/services/cloud_config.rs @@ -1,9 +1,9 @@ use std::sync::Arc; -use flowy_encrypt::generate_encryption_secret; use flowy_error::FlowyResult; use flowy_sqlite::kv::KVStorePreferences; use flowy_user_pub::cloud::UserCloudConfig; +use lib_infra::encryption::generate_encryption_secret; const CLOUD_CONFIG_KEY: &str = "af_user_cloud_config"; diff --git a/frontend/rust-lib/flowy-user/src/user_manager/manager_user_encryption.rs b/frontend/rust-lib/flowy-user/src/user_manager/manager_user_encryption.rs index 4288260899..2bfba3422e 100644 --- a/frontend/rust-lib/flowy-user/src/user_manager/manager_user_encryption.rs +++ b/frontend/rust-lib/flowy-user/src/user_manager/manager_user_encryption.rs @@ -1,13 +1,12 @@ use crate::entities::{AuthStateChangedPB, AuthStatePB}; +use crate::notification::send_auth_state_notification; +use crate::services::cloud_config::get_encrypt_secret; use crate::user_manager::UserManager; -use flowy_encrypt::{decrypt_text, encrypt_text}; use flowy_error::{ErrorCode, FlowyError, FlowyResult}; use flowy_user_pub::entities::{ EncryptionType, UpdateUserProfileParams, UserCredentials, UserProfile, }; - -use crate::notification::send_auth_state_notification; -use crate::services::cloud_config::get_encrypt_secret; +use lib_infra::encryption::{decrypt_text, encrypt_text}; impl UserManager { pub async fn set_encrypt_secret( diff --git a/frontend/rust-lib/lib-infra/Cargo.toml b/frontend/rust-lib/lib-infra/Cargo.toml index 7dc6f85bb3..12c805862b 100644 --- a/frontend/rust-lib/lib-infra/Cargo.toml +++ b/frontend/rust-lib/lib-infra/Cargo.toml @@ -26,6 +26,14 @@ futures = "0.3.30" cfg-if = "1.0.0" futures-util = "0.3.30" + +aes-gcm = { version = "0.10.2", optional = true } +rand = { version = "0.8.5", optional = true } +pbkdf2 = { version = "0.12.2", optional = true } +hmac = { version = "0.12.1", optional = true } +sha2 = { version = "0.10.7", optional = true } +base64 = { version = "0.22.1" } + [dev-dependencies] rand = "0.8.5" futures = "0.3.30" @@ -37,3 +45,4 @@ brotli = { version = "3.4.0", optional = true } [features] compression = ["brotli"] isolate_flutter = ["allo-isolate"] +encryption = ["aes-gcm", "rand", "pbkdf2", "hmac", "sha2"] \ No newline at end of file diff --git a/frontend/rust-lib/flowy-encrypt/src/encrypt.rs b/frontend/rust-lib/lib-infra/src/encryption/mod.rs similarity index 98% rename from frontend/rust-lib/flowy-encrypt/src/encrypt.rs rename to frontend/rust-lib/lib-infra/src/encryption/mod.rs index 88658858bb..6caabdc5cc 100644 --- a/frontend/rust-lib/flowy-encrypt/src/encrypt.rs +++ b/frontend/rust-lib/lib-infra/src/encryption/mod.rs @@ -46,7 +46,12 @@ pub fn encrypt_data>(data: T, combined_passphrase_salt: &str) -> .encrypt(GenericArray::from_slice(&nonce), data.as_ref()) .unwrap(); - Ok(nonce.into_iter().chain(ciphertext).collect()) + let result = nonce + .iter() + .copied() + .chain(ciphertext.iter().copied()) + .collect(); + Ok(result) } /// Decrypt a byte slice using AES-GCM. diff --git a/frontend/rust-lib/lib-infra/src/lib.rs b/frontend/rust-lib/lib-infra/src/lib.rs index 3b46e162fb..dc2ed5263c 100644 --- a/frontend/rust-lib/lib-infra/src/lib.rs +++ b/frontend/rust-lib/lib-infra/src/lib.rs @@ -19,6 +19,8 @@ if_wasm! { } } +#[cfg(feature = "encryption")] +pub mod encryption; #[cfg(feature = "isolate_flutter")] pub mod isolate_stream; pub mod priority_task; diff --git a/frontend/rust-lib/rust-toolchain.toml b/frontend/rust-lib/rust-toolchain.toml index 4d2dee853e..1de01fa45c 100644 --- a/frontend/rust-lib/rust-toolchain.toml +++ b/frontend/rust-lib/rust-toolchain.toml @@ -1,2 +1,2 @@ [toolchain] -channel = "1.80.0" +channel = "1.81.0" diff --git a/frontend/scripts/docker-buildfiles/Dockerfile b/frontend/scripts/docker-buildfiles/Dockerfile index 19f7565953..960c1ba625 100644 --- a/frontend/scripts/docker-buildfiles/Dockerfile +++ b/frontend/scripts/docker-buildfiles/Dockerfile @@ -22,8 +22,8 @@ RUN sudo pacman -S --needed --noconfirm curl base-devel openssl clang cmake ninj RUN xdg-user-dirs-update RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y RUN source ~/.cargo/env && \ - rustup toolchain install 1.80.1 && \ - rustup default 1.80.1 + rustup toolchain install 1.81 && \ + rustup default 1.81 # Install Flutter RUN sudo pacman -S --noconfirm git tar gtk3 diff --git a/frontend/scripts/makefile/desktop.toml b/frontend/scripts/makefile/desktop.toml index f1f0aa0219..f972aa5cd4 100644 --- a/frontend/scripts/makefile/desktop.toml +++ b/frontend/scripts/makefile/desktop.toml @@ -1,4 +1,3 @@ - [tasks.env_check] dependencies = ["echo_env", "install_flutter_protobuf"] condition = { env_set = [ @@ -100,7 +99,7 @@ dependencies = ["set-app-version"] script = [ """ cd rust-lib/ - cargo build --profile ${CARGO_PROFILE} --${BUILD_FLAG} --package=dart-ffi --target ${RUST_COMPILE_TARGET} --features "${FLUTTER_DESKTOP_FEATURES}" + cargo build --profile ${CARGO_PROFILE} --package=dart-ffi --target ${RUST_COMPILE_TARGET} --features "${FLUTTER_DESKTOP_FEATURES}" cd ../ """, ] @@ -111,7 +110,7 @@ dependencies = ["set-app-version"] script = [ """ cd rust-lib/ - cargo build --profile ${CARGO_PROFILE} --${BUILD_FLAG} --package=dart-ffi --target ${RUST_COMPILE_TARGET} --features "${FLUTTER_DESKTOP_FEATURES}" + cargo build --profile ${CARGO_PROFILE} --package=dart-ffi --target ${RUST_COMPILE_TARGET} --features "${FLUTTER_DESKTOP_FEATURES}" cd ../ """, ] diff --git a/frontend/scripts/makefile/web.toml b/frontend/scripts/makefile/web.toml index d00dbaf8d1..52c09ea6bb 100644 --- a/frontend/scripts/makefile/web.toml +++ b/frontend/scripts/makefile/web.toml @@ -1,31 +1,31 @@ [tasks.wasm_build] script_runner = "bash" script = [ - """ - #!/usr/bin/env bash - BASE_DIR=$(pwd) - crates=("lib-dispatch" "flowy-encrypt" "lib-infra" "flowy-notification" "flowy-date" "flowy-error" "collab-integrate" "flowy-document") + """ + #!/usr/bin/env bash + BASE_DIR=$(pwd) + crates=("lib-dispatch" "lib-infra" "flowy-notification" "flowy-date" "flowy-error" "collab-integrate" "flowy-document") - # Iterate over each crate and build it - for crate in "${crates[@]}"; do - echo "🔥🔥🔥 Building $crate with wasm-pack..." - cd "$BASE_DIR/rust-lib/$crate" || { echo "Failed to enter directory $crate"; exit 1; } + # Iterate over each crate and build it + for crate in "${crates[@]}"; do + echo "🔥🔥🔥 Building $crate with wasm-pack..." + cd "$BASE_DIR/rust-lib/$crate" || { echo "Failed to enter directory $crate"; exit 1; } - wasm-pack build || { echo "Build failed for $crate"; exit 1; } - done - """ + wasm-pack build || { echo "Build failed for $crate"; exit 1; } + done + """ ] [tasks.web_clean] description = "Remove all the building artifacts" run_task = { name = [ - "rust_lib_clean", - "rm_macro_build_cache", - "rm_rust_generated_files", - "rm_web_generated_protobuf_files", - "rm_web_generated_event_files", - "rm_pkg", + "rust_lib_clean", + "rm_macro_build_cache", + "rm_rust_generated_files", + "rm_web_generated_protobuf_files", + "rm_web_generated_event_files", + "rm_pkg", ] } [tasks.rm_web_generated_protobuf_files] From d9b3f3f6c6f85e8eec6182eb60f30cbe1c0d76fa Mon Sep 17 00:00:00 2001 From: "Nathan.fooo" <86001920+appflowy@users.noreply.github.com> Date: Mon, 30 Dec 2024 15:41:23 +0800 Subject: [PATCH 091/576] chore: remove tarui (#7089) * chore: remove tarui * chore: remove tarui config --- .github/workflows/android_ci.yaml.bak | 1 - .github/workflows/ios_ci.yaml | 2 - frontend/.vscode/launch.json | 15 - frontend/.vscode/tasks.json | 45 - frontend/appflowy_tauri/.eslintignore | 7 - frontend/appflowy_tauri/.eslintrc.cjs | 73 - frontend/appflowy_tauri/.gitignore | 33 - frontend/appflowy_tauri/.prettierignore | 19 - frontend/appflowy_tauri/.prettierrc.cjs | 20 - .../appflowy_tauri/.vscode/extensions.json | 3 - frontend/appflowy_tauri/README.md | 7 - frontend/appflowy_tauri/index.html | 14 - frontend/appflowy_tauri/jest.config.cjs | 21 - frontend/appflowy_tauri/package.json | 126 - frontend/appflowy_tauri/pnpm-lock.yaml | 7264 ------------- frontend/appflowy_tauri/postcss.config.cjs | 6 - .../public/google_fonts/Poppins/OFL.txt | 93 - .../google_fonts/Poppins/Poppins-Black.ttf | Bin 151396 -> 0 bytes .../Poppins/Poppins-BlackItalic.ttf | Bin 171604 -> 0 bytes .../google_fonts/Poppins/Poppins-Bold.ttf | Bin 153944 -> 0 bytes .../Poppins/Poppins-BoldItalic.ttf | Bin 176588 -> 0 bytes .../Poppins/Poppins-ExtraBold.ttf | Bin 152764 -> 0 bytes .../Poppins/Poppins-ExtraBoldItalic.ttf | Bin 173916 -> 0 bytes .../Poppins/Poppins-ExtraLight.ttf | Bin 161456 -> 0 bytes .../Poppins/Poppins-ExtraLightItalic.ttf | Bin 186168 -> 0 bytes .../google_fonts/Poppins/Poppins-Italic.ttf | Bin 182012 -> 0 bytes .../google_fonts/Poppins/Poppins-Light.ttf | Bin 159892 -> 0 bytes .../Poppins/Poppins-LightItalic.ttf | Bin 184460 -> 0 bytes .../google_fonts/Poppins/Poppins-Medium.ttf | Bin 156520 -> 0 bytes .../Poppins/Poppins-MediumItalic.ttf | Bin 180444 -> 0 bytes .../google_fonts/Poppins/Poppins-Regular.ttf | Bin 158240 -> 0 bytes .../google_fonts/Poppins/Poppins-SemiBold.ttf | Bin 155232 -> 0 bytes .../Poppins/Poppins-SemiBoldItalic.ttf | Bin 178584 -> 0 bytes .../google_fonts/Poppins/Poppins-Thin.ttf | Bin 161652 -> 0 bytes .../Poppins/Poppins-ThinItalic.ttf | Bin 187044 -> 0 bytes .../google_fonts/Roboto_Mono/LICENSE.txt | 202 - .../Roboto_Mono/RobotoMono-Italic.ttf | Bin 94372 -> 0 bytes .../Roboto_Mono/RobotoMono-Regular.ttf | Bin 87236 -> 0 bytes .../appflowy_tauri/public/launch_splash.jpg | Bin 1104537 -> 0 bytes frontend/appflowy_tauri/public/tauri.svg | 6 - frontend/appflowy_tauri/public/vite.svg | 1 - .../appflowy_tauri/scripts/i18n/index.cjs | 63 - .../appflowy_tauri/scripts/update_version.cjs | 31 - .../src-tauri/.cargo/config.toml | 2 - frontend/appflowy_tauri/src-tauri/.gitignore | 4 - frontend/appflowy_tauri/src-tauri/Cargo.lock | 9227 ----------------- frontend/appflowy_tauri/src-tauri/Cargo.toml | 137 - frontend/appflowy_tauri/src-tauri/Info.plist | 19 - frontend/appflowy_tauri/src-tauri/build.rs | 3 - .../appflowy_tauri/src-tauri/env.development | 4 - .../appflowy_tauri/src-tauri/env.production | 4 - .../src-tauri/icons/128x128.png | Bin 9256 -> 0 bytes .../src-tauri/icons/128x128@2x.png | Bin 19737 -> 0 bytes .../appflowy_tauri/src-tauri/icons/32x32.png | Bin 1922 -> 0 bytes .../src-tauri/icons/Square107x107Logo.png | Bin 7695 -> 0 bytes .../src-tauri/icons/Square142x142Logo.png | Bin 10254 -> 0 bytes .../src-tauri/icons/Square150x150Logo.png | Bin 11031 -> 0 bytes .../src-tauri/icons/Square284x284Logo.png | Bin 22113 -> 0 bytes .../src-tauri/icons/Square30x30Logo.png | Bin 1746 -> 0 bytes .../src-tauri/icons/Square310x310Logo.png | Bin 24225 -> 0 bytes .../src-tauri/icons/Square44x44Logo.png | Bin 2808 -> 0 bytes .../src-tauri/icons/Square71x71Logo.png | Bin 4873 -> 0 bytes .../src-tauri/icons/Square89x89Logo.png | Bin 6233 -> 0 bytes .../src-tauri/icons/StoreLogo.png | Bin 3221 -> 0 bytes .../appflowy_tauri/src-tauri/icons/icon.icns | Bin 181898 -> 0 bytes .../appflowy_tauri/src-tauri/icons/icon.ico | Bin 32830 -> 0 bytes .../appflowy_tauri/src-tauri/icons/icon.png | Bin 41650 -> 0 bytes .../src-tauri/rust-toolchain.toml | 2 - .../appflowy_tauri/src-tauri/rustfmt.toml | 12 - frontend/appflowy_tauri/src-tauri/src/init.rs | 78 - frontend/appflowy_tauri/src-tauri/src/main.rs | 72 - .../src-tauri/src/notification.rs | 35 - .../appflowy_tauri/src-tauri/src/request.rs | 45 - .../appflowy_tauri/src-tauri/tauri.conf.json | 113 - .../src/appflowy_app/@types/i18next.d.ts | 8 - .../src/appflowy_app/@types/resources.ts | 7 - .../appflowy_tauri/src/appflowy_app/App.tsx | 25 - .../src/appflowy_app/AppMain.hooks.ts | 69 - .../src/appflowy_app/AppMain.tsx | 31 - .../database/cell/cell_listeners.ts | 48 - .../application/database/cell/cell_service.ts | 140 - .../application/database/cell/cell_types.ts | 163 - .../application/database/cell/index.ts | 3 - .../database/database/database_service.ts | 84 - .../database/database/database_types.ts | 21 - .../application/database/database/index.ts | 2 - .../database_view/database_view_service.ts | 75 - .../database/database_view/index.ts | 1 - .../database/field/field_listeners.ts | 40 - .../database/field/field_service.ts | 215 - .../application/database/field/field_types.ts | 51 - .../application/database/field/index.ts | 5 - .../database/field/select_option/index.ts | 2 - .../select_option/select_option_service.ts | 58 - .../select_option/select_option_types.ts | 15 - .../database/field/type_option/index.ts | 2 - .../field/type_option/type_option_service.ts | 26 - .../field/type_option/type_option_types.ts | 148 - .../database/filter/filter_data.ts | 42 - .../database/filter/filter_listeners.ts | 8 - .../database/filter/filter_service.ts | 88 - .../database/filter/filter_types.ts | 202 - .../application/database/filter/index.ts | 3 - .../database/group/group_service.ts | 64 - .../application/database/group/group_types.ts | 32 - .../application/database/group/index.ts | 2 - .../application/database/index.ts | 8 - .../application/database/row/index.ts | 3 - .../application/database/row/row_listeners.ts | 95 - .../application/database/row/row_service.ts | 129 - .../application/database/row/row_types.ts | 29 - .../application/database/sort/index.ts | 3 - .../database/sort/sort_listeners.ts | 35 - .../application/database/sort/sort_service.ts | 73 - .../application/database/sort/sort_types.ts | 15 - .../application/document/document.service.ts | 284 - .../application/document/document.types.ts | 242 - .../application/folder/page.service.ts | 166 - .../application/folder/trash.service.ts | 68 - .../application/folder/workspace.service.ts | 141 - .../appflowy_app/application/notification.ts | 157 - .../application/user/auth.service.ts | 92 - .../application/user/user.service.ts | 68 - .../src/appflowy_app/assets/add.svg | 3 - .../src/appflowy_app/assets/align-center.svg | 5 - .../src/appflowy_app/assets/align-left.svg | 5 - .../src/appflowy_app/assets/align-right.svg | 5 - .../src/appflowy_app/assets/arrow-left.svg | 3 - .../src/appflowy_app/assets/arrow-right.svg | 3 - .../src/appflowy_app/assets/board.svg | 16 - .../src/appflowy_app/assets/bold.svg | 3 - .../src/appflowy_app/assets/close.svg | 4 - .../src/appflowy_app/assets/copy.svg | 4 - .../src/appflowy_app/assets/dark-logo.svg | 73 - .../assets/database/checkbox-check.svg | 4 - .../assets/database/checkbox-uncheck.svg | 3 - .../assets/database/field-type-attach.svg | 3 - .../assets/database/field-type-checkbox.svg | 4 - .../assets/database/field-type-checklist.svg | 4 - .../assets/database/field-type-date.svg | 6 - .../database/field-type-last-edited-time.svg | 4 - .../database/field-type-multi-select.svg | 8 - .../assets/database/field-type-number.svg | 3 - .../assets/database/field-type-person.svg | 4 - .../assets/database/field-type-relation.svg | 8 - .../database/field-type-single-select.svg | 4 - .../assets/database/field-type-text.svg | 4 - .../assets/database/field-type-url.svg | 3 - .../src/appflowy_app/assets/date.svg | 6 - .../src/appflowy_app/assets/delete.svg | 6 - .../src/appflowy_app/assets/details.svg | 5 - .../src/appflowy_app/assets/document.svg | 14 - .../src/appflowy_app/assets/drag.svg | 8 - .../src/appflowy_app/assets/dropdown.svg | 6 - .../src/appflowy_app/assets/edit.svg | 9 - .../src/appflowy_app/assets/eye_close.svg | 9 - .../src/appflowy_app/assets/eye_open.svg | 16 - .../src/appflowy_app/assets/grid.svg | 6 - .../src/appflowy_app/assets/h1.svg | 4 - .../src/appflowy_app/assets/h2.svg | 4 - .../src/appflowy_app/assets/h3.svg | 4 - .../src/appflowy_app/assets/hide-menu.svg | 6 - .../src/appflowy_app/assets/hide.svg | 4 - .../src/appflowy_app/assets/image.svg | 5 - .../assets/images/default_cover.jpg | Bin 281498 -> 0 bytes .../src/appflowy_app/assets/information.svg | 10 - .../src/appflowy_app/assets/inline-code.svg | 4 - .../src/appflowy_app/assets/italic.svg | 3 - .../src/appflowy_app/assets/left.svg | 5 - .../src/appflowy_app/assets/light-logo.svg | 51 - .../src/appflowy_app/assets/link.svg | 4 - .../src/appflowy_app/assets/list-dropdown.svg | 4 - .../src/appflowy_app/assets/list.svg | 8 - .../src/appflowy_app/assets/logo.svg | 38 - .../src/appflowy_app/assets/mention.svg | 3 - .../src/appflowy_app/assets/more.svg | 3 - .../src/appflowy_app/assets/numbers.svg | 3 - .../src/appflowy_app/assets/open.svg | 6 - .../src/appflowy_app/assets/quote.svg | 4 - .../src/appflowy_app/assets/react.svg | 1 - .../src/appflowy_app/assets/right.svg | 5 - .../src/appflowy_app/assets/search.svg | 4 - .../src/appflowy_app/assets/select-check.svg | 3 - .../src/appflowy_app/assets/settings.svg | 4 - .../appflowy_app/assets/settings/account.svg | 3 - .../assets/settings/check_circle.svg | 8 - .../src/appflowy_app/assets/settings/dark.png | Bin 16280 -> 0 bytes .../appflowy_app/assets/settings/discord.png | Bin 1533 -> 0 bytes .../appflowy_app/assets/settings/github.png | Bin 1105 -> 0 bytes .../appflowy_app/assets/settings/google.png | Bin 1744 -> 0 bytes .../appflowy_app/assets/settings/light.png | Bin 13240 -> 0 bytes .../assets/settings/workplace.svg | 10 - .../src/appflowy_app/assets/show-menu.svg | 6 - .../src/appflowy_app/assets/sort.svg | 4 - .../src/appflowy_app/assets/strikethrough.svg | 4 - .../src/appflowy_app/assets/text.svg | 4 - .../src/appflowy_app/assets/todo-list.svg | 4 - .../src/appflowy_app/assets/underline.svg | 4 - .../src/appflowy_app/assets/up.svg | 3 - .../_shared/avatar/ProfileAvatar.tsx | 33 - .../_shared/avatar/WorkplaceAvatar.tsx | 34 - .../components/_shared/avatar/index.ts | 2 - .../confirm_dialog/DeleteConfirmDialog.tsx | 77 - .../_shared/confirm_dialog/RenameDialog.tsx | 81 - .../_shared/devtool/AppFlowyDevTool.tsx | 61 - .../_shared/devtool/ManualSignInDialog.tsx | 114 - .../_shared/drag_block/drag.hooks.ts | 87 - .../components/_shared/drag_block/index.ts | 1 - .../_shared/emoji_picker/EmojiPicker.hooks.ts | 165 - .../_shared/emoji_picker/EmojiPicker.tsx | 35 - .../emoji_picker/EmojiPickerCategories.tsx | 354 - .../emoji_picker/EmojiPickerHeader.tsx | 134 - .../_shared/error_boundary/withError.tsx | 16 - .../_shared/image_upload/EmbedLink.tsx | 70 - .../_shared/image_upload/LocalImage.tsx | 62 - .../_shared/image_upload/Unsplash.tsx | 154 - .../_shared/image_upload/UploadImage.tsx | 93 - .../_shared/image_upload/UploadTabs.tsx | 128 - .../components/_shared/image_upload/index.ts | 5 - .../_shared/katex_math/KatexMath.tsx | 24 - .../components/_shared/katex_math/index.css | 4 - .../KeyboardNavigation.tsx | 317 - .../_shared/keyboard_navigation/utils.ts | 32 - .../components/_shared/notify/index.ts | 27 - .../_shared/popover/Popover.hooks.ts | 237 - .../components/_shared/popover/utils.ts | 45 - .../_shared/scroller/AFScroller.tsx | 55 - .../components/_shared/scroller/index.ts | 1 - .../_shared/view_title/ViewBanner.tsx | 53 - .../_shared/view_title/ViewIcon.tsx | 62 - .../_shared/view_title/ViewIconGroup.tsx | 57 - .../_shared/view_title/ViewTitle.tsx | 69 - .../_shared/view_title/ViewTitleInput.tsx | 31 - .../_shared/view_title/cover/Colors.tsx | 21 - .../_shared/view_title/cover/CoverPopover.tsx | 112 - .../_shared/view_title/cover/ViewCover.tsx | 80 - .../view_title/cover/ViewCoverActions.tsx | 44 - .../_shared/view_title/cover/index.ts | 1 - .../components/auth/LoginButtonGroup.tsx | 51 - .../components/auth/ProtectedRoutes.tsx | 127 - .../appflowy_app/components/auth/Welcome.tsx | 55 - .../components/auth/auth.hooks.ts | 186 - .../components/database/Database.hooks.ts | 204 - .../components/database/Database.tsx | 202 - .../components/database/DatabaseLoader.tsx | 18 - .../components/database/DatabaseTitle.tsx | 32 - .../components/database/DatabaseView.tsx | 23 - .../components/database/_shared/CellText.tsx | 17 - .../_shared/LinearProgressWithLabel.tsx | 47 - .../components/database/_shared/constants.ts | 9 - .../database/_shared/dnd/dnd.context.ts | 17 - .../database/_shared/dnd/drag.hooks.ts | 114 - .../database/_shared/dnd/drop.hooks.ts | 88 - .../components/database/_shared/dnd/index.ts | 7 - .../components/database/_shared/dnd/utils.ts | 170 - .../components/database/_shared/index.ts | 5 - .../components/database/board/Board.tsx | 5 - .../components/database/board/index.ts | 1 - .../components/database/calendar/Calendar.tsx | 5 - .../components/database/calendar/index.ts | 1 - .../database/components/cell/Cell.hooks.ts | 63 - .../database/components/cell/Cell.tsx | 65 - .../database/components/cell/CheckboxCell.tsx | 26 - .../components/cell/ChecklistCell.tsx | 74 - .../database/components/cell/DateTimeCell.tsx | 125 - .../database/components/cell/NumberCell.tsx | 50 - .../database/components/cell/SelectCell.tsx | 108 - .../database/components/cell/TextCell.tsx | 59 - .../components/cell/TimestampCell.tsx | 13 - .../database/components/cell/URLCell.tsx | 81 - .../database/components/cell/index.ts | 1 - .../database_settings/DatabaseCollection.tsx | 18 - .../database_settings/DatabaseSettings.tsx | 33 - .../database_settings/FilterSettings.tsx | 46 - .../database_settings/Properties.tsx | 113 - .../database_settings/SettingsMenu.tsx | 98 - .../database_settings/SortSettings.tsx | 55 - .../components/database_settings/index.ts | 1 - .../components/edit_record/EditRecord.tsx | 63 - .../edit_record/ExpandRecordModal.tsx | 54 - .../components/edit_record/RecordActions.tsx | 63 - .../components/edit_record/RecordDocument.tsx | 12 - .../components/edit_record/RecordHeader.tsx | 39 - .../components/edit_record/RecordTitle.tsx | 71 - .../record_properties/Property.tsx | 55 - .../record_properties/PropertyList.tsx | 55 - .../record_properties/PropertyName.tsx | 32 - .../record_properties/PropertyValue.tsx | 33 - .../record_properties/RecordProperties.tsx | 97 - .../SwitchPropertiesVisible.tsx | 38 - .../field_types/checklist/AddNewOption.tsx | 63 - .../checklist/ChecklistCellActions.tsx | 76 - .../field_types/checklist/ChecklistItem.tsx | 117 - .../checklist/LinearProgressWithLabel.tsx | 17 - .../field_types/date/CustomCalendar.tsx | 117 - .../field_types/date/DateFormat.tsx | 96 - .../field_types/date/DateTimeCellActions.tsx | 197 - .../field_types/date/DateTimeFieldActions.tsx | 17 - .../field_types/date/DateTimeFormat.tsx | 75 - .../field_types/date/DateTimeFormatSelect.tsx | 55 - .../field_types/date/DateTimeInput.tsx | 86 - .../field_types/date/DateTimeSet.tsx | 54 - .../field_types/date/IncludeTimeSwitch.tsx | 30 - .../field_types/date/RangeSwitch.tsx | 30 - .../field_types/date/TimeFormat.tsx | 82 - .../components/field_types/date/calendar.scss | 82 - .../components/field_types/date/utils.ts | 29 - .../number/EditNumberCellInput.tsx | 68 - .../field_types/number/NumberFieldActions.tsx | 34 - .../field_types/number/NumberFormatMenu.tsx | 64 - .../field_types/number/NumberFormatSelect.tsx | 49 - .../components/field_types/number/const.ts | 14 - .../select/SelectOptionModifyMenu.tsx | 165 - .../components/field_types/select/Tag.tsx | 27 - .../field_types/select/constants.ts | 25 - .../select_cell_actions/SearchInput.tsx | 38 - .../select_cell_actions/SelectCellActions.tsx | 162 - .../select_cell_actions/SelectOptionItem.tsx | 55 - .../select_field_actions/AddAnOption.tsx | 76 - .../select/select_field_actions/Option.tsx | 47 - .../select/select_field_actions/Options.tsx | 19 - .../SelectFieldActions.tsx | 26 - .../field_types/text/EditTextCellInput.tsx | 69 - .../components/filter/ConditionSelect.tsx | 78 - .../database/components/filter/Filter.tsx | 200 - .../components/filter/FilterActions.tsx | 84 - .../filter/FilterConditionSelect.tsx | 224 - .../components/filter/FilterFieldsMenu.tsx | 59 - .../database/components/filter/Filters.tsx | 51 - .../filter/date_filter/DateFilter.tsx | 94 - .../filter/date_filter/DateFilterValue.tsx | 52 - .../number_filter/NumberFilterValue.tsx | 39 - .../filter/select_filter/SelectFilter.tsx | 92 - .../select_filter/SelectFilterValue.tsx | 38 - .../filter/text_filter/TextFilter.tsx | 50 - .../filter/text_filter/TextFilterValue.tsx | 34 - .../components/database/components/index.ts | 2 - .../components/property/NewProperty.tsx | 41 - .../components/property/PropertiesList.tsx | 92 - .../database/components/property/Property.tsx | 95 - .../components/property/PropertyActions.tsx | 271 - .../components/property/PropertyMenu.tsx | 89 - .../components/property/PropertyNameInput.tsx | 49 - .../components/property/PropertySelect.tsx | 84 - .../database/components/property/index.ts | 4 - .../property_type/PropertyTypeMenu.tsx | 121 - .../PropertyTypeMenuExtension.tsx | 26 - .../property_type/PropertyTypeSelect.tsx | 59 - .../property_type/PropertyTypeText.tsx | 27 - .../property_type/ProppertyTypeSvg.tsx | 32 - .../components/sort/SortConditionSelect.tsx | 78 - .../components/sort/SortFieldsMenu.tsx | 53 - .../database/components/sort/SortItem.tsx | 55 - .../database/components/sort/SortMenu.tsx | 90 - .../database/components/sort/Sorts.tsx | 45 - .../database/components/sort/index.ts | 2 - .../components/tab_bar/AddViewBtn.tsx | 29 - .../components/tab_bar/DatabaseTabBar.tsx | 110 - .../components/tab_bar/TextButton.tsx | 9 - .../components/tab_bar/ViewActions.tsx | 103 - .../database/components/tab_bar/ViewTabs.tsx | 48 - .../database/components/tab_bar/index.ts | 1 - .../components/database/database.scss | 19 - .../components/database/grid/Grid.tsx | 6 - .../components/database/grid/constants.ts | 86 - .../grid/grid_calculate/GridCalculate.tsx | 36 - .../database/grid/grid_calculate/index.ts | 1 - .../database/grid/grid_cell/GridCell.tsx | 66 - .../database/grid/grid_cell/PrimaryCell.tsx | 51 - .../database/grid/grid_cell/index.ts | 2 - .../database/grid/grid_field/GridField.tsx | 179 - .../grid/grid_field/GridFieldMenu.tsx | 59 - .../database/grid/grid_field/GridNewField.tsx | 35 - .../database/grid/grid_field/GridResizer.tsx | 84 - .../database/grid/grid_field/index.ts | 4 - .../database/grid/grid_new_row/GridNewRow.tsx | 68 - .../grid/grid_overlay/GridTableOverlay.tsx | 75 - .../grid_row_actions/GridRowActions.hooks.ts | 244 - .../grid/grid_row_actions/GridRowActions.tsx | 135 - .../grid_row_actions/GridRowContextMenu.tsx | 72 - .../grid_row_actions/GridRowDragButton.tsx | 59 - .../grid/grid_row_actions/GridRowMenu.tsx | 160 - .../database/grid/grid_row_actions/index.ts | 5 - .../GridStickyHeader.hooks.ts | 9 - .../grid_sticky_header/GridStickyHeader.tsx | 112 - .../grid/grid_table/GridTable.hooks.ts | 67 - .../database/grid/grid_table/GridTable.tsx | 161 - .../database/grid/grid_table/index.ts | 2 - .../components/database/grid/index.ts | 1 - .../appflowy_app/components/database/index.ts | 3 - .../components/document/Document.tsx | 53 - .../document_header/DocumentHeader.tsx | 60 - .../document/document_header/index.ts | 1 - .../appflowy_app/components/document/index.ts | 1 - .../components/editor/Editor.hooks.ts | 9 - .../appflowy_app/components/editor/Editor.tsx | 19 - .../components/editor/command/formula.ts | 95 - .../components/editor/command/index.ts | 715 -- .../components/editor/command/mark.ts | 137 - .../components/editor/command/tab.ts | 82 - .../components/blocks/_shared/Placeholder.tsx | 13 - .../blocks/_shared/PlaceholderContent.tsx | 130 - .../blocks/_shared/unSupportBlock.tsx | 12 - .../blocks/bulleted_list/BulletedList.tsx | 14 - .../blocks/bulleted_list/BulletedListIcon.tsx | 51 - .../components/blocks/bulleted_list/index.ts | 1 - .../components/blocks/callout/Callout.tsx | 20 - .../components/blocks/callout/CalloutIcon.tsx | 69 - .../editor/components/blocks/callout/index.ts | 1 - .../components/blocks/code/Code.hooks.ts | 27 - .../editor/components/blocks/code/Code.tsx | 39 - .../components/blocks/code/SelectLanguage.tsx | 168 - .../components/blocks/code/constants.ts | 154 - .../editor/components/blocks/code/index.ts | 1 - .../editor/components/blocks/code/utils.ts | 132 - .../blocks/database/DatabaseEmpty.tsx | 42 - .../blocks/database/DatabaseList.hooks.ts | 26 - .../blocks/database/DatabaseList.tsx | 104 - .../components/blocks/database/Drawer.tsx | 73 - .../components/blocks/database/GridBlock.tsx | 31 - .../components/blocks/database/GridView.tsx | 71 - .../components/blocks/database/index.ts | 1 - .../components/blocks/database/utils.ts | 12 - .../components/blocks/divider/DividerNode.tsx | 28 - .../editor/components/blocks/divider/index.ts | 1 - .../components/blocks/heading/Heading.tsx | 18 - .../editor/components/blocks/heading/index.ts | 1 - .../components/blocks/image/ImageActions.tsx | 163 - .../components/blocks/image/ImageBlock.tsx | 49 - .../components/blocks/image/ImageEmpty.tsx | 63 - .../components/blocks/image/ImageRender.tsx | 136 - .../components/blocks/image/ImageResizer.tsx | 61 - .../components/blocks/image/UploadPopover.tsx | 112 - .../editor/components/blocks/image/index.ts | 1 - .../blocks/math_equation/EditPopover.tsx | 165 - .../blocks/math_equation/MathEquation.tsx | 88 - .../components/blocks/math_equation/index.ts | 1 - .../blocks/numbered_list/NumberListIcon.tsx | 87 - .../blocks/numbered_list/NumberedList.tsx | 14 - .../components/blocks/numbered_list/index.ts | 1 - .../editor/components/blocks/page/Page.tsx | 18 - .../editor/components/blocks/page/index.ts | 1 - .../components/blocks/paragraph/Paragraph.tsx | 14 - .../components/blocks/paragraph/index.ts | 1 - .../editor/components/blocks/quote/Quote.tsx | 16 - .../editor/components/blocks/quote/index.ts | 1 - .../blocks/text/StartIcon.hooks.tsx | 46 - .../editor/components/blocks/text/Text.tsx | 29 - .../editor/components/blocks/text/index.ts | 1 - .../blocks/todo_list/CheckboxIcon.tsx | 49 - .../components/blocks/todo_list/TodoList.tsx | 19 - .../components/blocks/todo_list/index.ts | 1 - .../blocks/toggle_list/ToggleIcon.tsx | 30 - .../blocks/toggle_list/ToggleList.tsx | 17 - .../components/blocks/toggle_list/index.ts | 1 - .../components/editor/CollaborativeEditor.tsx | 93 - .../components/editor/CustomEditable.tsx | 42 - .../editor/components/editor/Editor.hooks.ts | 137 - .../editor/components/editor/Editor.tsx | 78 - .../editor/components/editor/Element.hooks.ts | 30 - .../editor/components/editor/Element.tsx | 132 - .../editor/components/editor/Leaf.tsx | 59 - .../editor/components/editor/index.ts | 3 - .../editor/components/editor/utils.ts | 31 - .../inline_nodes/InlineChromiumBugfix.tsx | 16 - .../editor/components/inline_nodes/index.ts | 2 - .../inline_formula/FormulaEditPopover.tsx | 87 - .../inline_formula/FormulaLeaf.tsx | 16 - .../inline_formula/InlineFormula.tsx | 133 - .../inline_nodes/inline_formula/index.ts | 2 - .../components/inline_nodes/link/Link.tsx | 48 - .../inline_nodes/link/LinkEditContent.tsx | 179 - .../inline_nodes/link/LinkEditInput.tsx | 47 - .../inline_nodes/link/LinkEditPopover.tsx | 69 - .../components/inline_nodes/link/index.ts | 3 - .../inline_nodes/mention/Mention.tsx | 18 - .../inline_nodes/mention/MentionLeaf.tsx | 141 - .../components/inline_nodes/mention/index.ts | 1 - .../components/inline_nodes/withInline.ts | 31 - .../components/tools/_shared/ColorPicker.tsx | 174 - .../tools/_shared/CustomColorPicker.tsx | 27 - .../editor/components/tools/_shared/index.ts | 2 - .../tools/block_actions/AddBlockBelow.tsx | 56 - .../tools/block_actions/BlockActions.tsx | 30 - .../BlockActionsToolbar.hooks.ts | 146 - .../block_actions/BlockActionsToolbar.tsx | 141 - .../block_actions/BlockOperationMenu.tsx | 209 - .../tools/block_actions/color/Color.tsx | 90 - .../tools/block_actions/color/index.ts | 1 - .../components/tools/block_actions/index.ts | 2 - .../components/tools/block_actions/utils.ts | 58 - .../tools/command_panel/Command.hooks.ts | 259 - .../tools/command_panel/CommandPanel.tsx | 17 - .../components/tools/command_panel/index.ts | 2 - .../mention_panel/MentionPanel.hooks.tsx | 114 - .../mention_panel/MentionPanel.tsx | 81 - .../mention_panel/MentionPanelContent.tsx | 45 - .../command_panel/mention_panel/index.ts | 2 - .../SlashCommandPanel.hooks.tsx | 245 - .../slash_command_panel/SlashCommandPanel.tsx | 73 - .../SlashCommandPanelContent.tsx | 89 - .../slash_command_panel/const.ts | 174 - .../slash_command_panel/index.ts | 2 - .../components/tools/command_panel/utils.ts | 31 - .../editor/components/tools/popover.ts | 34 - .../selection_toolbar/SelectionActions.tsx | 73 - .../SelectionToolbar.hooks.ts | 239 - .../selection_toolbar/SelectionToolbar.tsx | 33 - .../actions/_shared/ActionButton.tsx | 35 - .../selection_toolbar/actions/align/Align.tsx | 98 - .../selection_toolbar/actions/align/index.ts | 1 - .../selection_toolbar/actions/bold/Bold.tsx | 39 - .../selection_toolbar/actions/bold/index.ts | 1 - .../actions/bulleted_list/BulletedList.tsx | 27 - .../actions/bulleted_list/index.ts | 1 - .../selection_toolbar/actions/color/Color.tsx | 53 - .../actions/color/ColorPopover.tsx | 92 - .../selection_toolbar/actions/color/index.ts | 1 - .../actions/formula/Formula.tsx | 51 - .../actions/formula/index.ts | 1 - .../actions/heading/Heading.tsx | 56 - .../actions/heading/index.ts | 1 - .../selection_toolbar/actions/href/Href.tsx | 47 - .../actions/href/LinkActions.tsx | 59 - .../selection_toolbar/actions/href/index.ts | 2 - .../actions/inline_code/InlineCode.tsx | 39 - .../actions/inline_code/index.ts | 1 - .../actions/italic/Italic.tsx | 39 - .../selection_toolbar/actions/italic/index.ts | 1 - .../actions/numbered_list/NumberedList.tsx | 33 - .../actions/numbered_list/index.ts | 1 - .../actions/paragraph/Paragraph.tsx | 33 - .../actions/paragraph/index.ts | 1 - .../selection_toolbar/actions/quote/Quote.tsx | 33 - .../selection_toolbar/actions/quote/index.ts | 1 - .../actions/strikethrough/StrikeThrough.tsx | 39 - .../actions/strikethrough/index.ts | 1 - .../actions/todo_list/TodoList.tsx | 37 - .../actions/todo_list/index.ts | 1 - .../actions/toggle_list/ToggleList.tsx | 36 - .../actions/toggle_list/index.ts | 1 - .../actions/underline/Underline.tsx | 39 - .../actions/underline/index.ts | 1 - .../tools/selection_toolbar/index.ts | 1 - .../tools/selection_toolbar/utils.ts | 40 - .../components/editor/editor.scss | 231 - .../appflowy_app/components/editor/index.ts | 1 - .../components/editor/plugins/constants.ts | 3 - .../editor/plugins/copyPasted/index.ts | 2 - .../editor/plugins/copyPasted/utils.ts | 311 - .../editor/plugins/copyPasted/withCopy.ts | 40 - .../editor/plugins/copyPasted/withPasted.ts | 59 - .../editor/plugins/shortcuts/index.ts | 2 - .../editor/plugins/shortcuts/markdown.ts | 172 - .../plugins/shortcuts/shortcuts.hooks.ts | 349 - .../editor/plugins/shortcuts/withMarkdown.ts | 239 - .../components/editor/plugins/utils.ts | 38 - .../editor/plugins/withBlockDelete.ts | 228 - .../editor/plugins/withBlockInsertBreak.ts | 94 - .../editor/plugins/withBlockMove.ts | 134 - .../editor/plugins/withBlockPlugins.ts | 30 - .../editor/plugins/withSplitNodes.ts | 139 - .../editor/provider/__tests__/action.test.ts | 124 - .../editor/provider/__tests__/observe.test.ts | 43 - .../editor/provider/__tests__/read_me.ts | 437 - .../provider/__tests__/utils/convert.ts | 76 - .../__tests__/utils/mockBackendService.ts | 21 - .../components/editor/provider/data_client.ts | 74 - .../components/editor/provider/index.ts | 1 - .../components/editor/provider/provider.ts | 74 - .../editor/provider/types/y_event.ts | 12 - .../editor/provider/utils/action.ts | 301 - .../editor/provider/utils/convert.ts | 154 - .../components/editor/provider/utils/delta.ts | 54 - .../editor/provider/utils/relation.ts | 24 - .../components/editor/stores/block.ts | 70 - .../components/editor/stores/decorate.ts | 89 - .../components/editor/stores/index.ts | 28 - .../components/editor/stores/inline_node.ts | 60 - .../components/editor/stores/selected.ts | 58 - .../components/editor/stores/slash.ts | 30 - .../components/error/Error.hooks.ts | 39 - .../components/error/ErrorHandlerPage.tsx | 8 - .../components/error/ErrorModal.tsx | 26 - .../src/appflowy_app/components/index.ts | 1 - .../components/layout/FooterPanel.tsx | 12 - .../components/layout/Layout.hooks.ts | 54 - .../appflowy_app/components/layout/Layout.tsx | 66 - .../layout/bread_crumb/BreadCrumb.tsx | 64 - .../layout/bread_crumb/Breadcrumb.hooks.ts | 38 - .../CollapseMenuButton.tsx | 37 - .../components/layout/layout.scss | 81 - .../layout/nested_page/AddButton.tsx | 65 - .../layout/nested_page/DeleteDialog.tsx | 41 - .../layout/nested_page/MoreButton.tsx | 130 - .../layout/nested_page/NestedPage.hooks.ts | 147 - .../layout/nested_page/NestedPage.tsx | 126 - .../layout/nested_page/NestedPageTitle.tsx | 102 - .../layout/nested_page/OperationMenu.tsx | 103 - .../components/layout/share/Share.hooks.ts | 12 - .../components/layout/share/Share.tsx | 14 - .../components/layout/side_bar/Resizer.tsx | 55 - .../components/layout/side_bar/SideBar.tsx | 73 - .../components/layout/side_bar/UserInfo.tsx | 41 - .../layout/top_bar/DeletePageSnackbar.tsx | 104 - .../layout/top_bar/FontSizeConfig.tsx | 26 - .../components/layout/top_bar/MoreButton.tsx | 32 - .../layout/top_bar/MoreOptions.hooks.ts | 30 - .../components/layout/top_bar/MoreOptions.tsx | 11 - .../components/layout/top_bar/TopBar.tsx | 27 - .../layout/workspace_manager/NestedPages.tsx | 19 - .../workspace_manager/NewPageButton.tsx | 31 - .../layout/workspace_manager/TrashButton.tsx | 43 - .../workspace_manager/Workspace.hooks.ts | 136 - .../layout/workspace_manager/Workspace.tsx | 85 - .../workspace_manager/WorkspaceManager.tsx | 38 - .../components/settings/Login.tsx | 22 - .../components/settings/Settings.tsx | 92 - .../components/settings/SettingsDialog.tsx | 108 - .../appflowy_app/components/settings/index.ts | 1 - .../settings/my_account/AccountLogin.tsx | 39 - .../settings/my_account/DeleteAccount.tsx | 43 - .../my_account/DeleteAccountDialog.tsx | 50 - .../settings/my_account/MyAccount.tsx | 24 - .../settings/my_account/Profile.tsx | 180 - .../components/settings/my_account/index.ts | 1 - .../settings/workplace/Appearance.tsx | 20 - .../settings/workplace/Workplace.tsx | 23 - .../settings/workplace/WorkplaceDisplay.tsx | 155 - .../workplace/appearance/LanguageSetting.tsx | 115 - .../workplace/appearance/ThemeModeSwitch.tsx | 93 - .../components/settings/workplace/const.ts | 3 - .../components/settings/workplace/index.ts | 1 - .../components/trash/Trash.hooks.ts | 79 - .../appflowy_app/components/trash/Trash.tsx | 87 - .../components/trash/TrashItem.tsx | 65 - .../src/appflowy_app/hooks/ViewId.hooks.ts | 6 - .../src/appflowy_app/hooks/index.ts | 2 - .../appflowy_app/hooks/notification.hooks.ts | 20 - .../src/appflowy_app/hooks/page.hooks.tsx | 26 - .../src/appflowy_app/i18n/config.ts | 15 - .../src/appflowy_app/slate-editor.d.ts | 44 - .../stores/reducers/current-user/slice.ts | 99 - .../stores/reducers/error/slice.ts | 32 - .../stores/reducers/pages/async_actions.ts | 106 - .../stores/reducers/pages/slice.ts | 223 - .../stores/reducers/sidebar/slice.ts | 37 - .../stores/reducers/trash/slice.ts | 41 - .../stores/reducers/workspace/slice.ts | 46 - .../src/appflowy_app/stores/store.ts | 51 - .../src/appflowy_app/utils/async_queue.ts | 51 - .../src/appflowy_app/utils/avatar.ts | 26 - .../src/appflowy_app/utils/change_notifier.ts | 30 - .../src/appflowy_app/utils/color.ts | 50 - .../src/appflowy_app/utils/emoji.ts | 9 - .../src/appflowy_app/utils/env.ts | 11 - .../src/appflowy_app/utils/hotkeys.ts | 134 - .../src/appflowy_app/utils/list.ts | 45 - .../src/appflowy_app/utils/log.ts | 20 - .../src/appflowy_app/utils/mui.ts | 168 - .../src/appflowy_app/utils/open_url.ts | 23 - .../src/appflowy_app/utils/tool.ts | 57 - .../src/appflowy_app/utils/upload_image.ts | 9 - .../src/appflowy_app/views/DatabasePage.tsx | 24 - .../src/appflowy_app/views/DocumentPage.tsx | 14 - .../src/appflowy_app/views/TrashPage.tsx | 12 - .../src/appflowy_app/vite-env.d.ts | 2 - frontend/appflowy_tauri/src/main.tsx | 8 - .../src/services/backend/index.ts | 9 - frontend/appflowy_tauri/src/styles/font.css | 125 - .../appflowy_tauri/src/styles/tailwind.css | 3 - .../appflowy_tauri/src/styles/template.css | 60 - .../src/styles/variables/dark.variables.css | 121 - .../src/styles/variables/index.css | 7 - .../src/styles/variables/light.variables.css | 124 - .../appflowy_tauri/src/tests/helpers/init.ts | 1 - .../style-dictionary/config.cjs | 114 - .../style-dictionary/tailwind/box-shadow.cjs | 9 - .../style-dictionary/tailwind/colors.cjs | 75 - .../style-dictionary/tokens/base.json | 290 - .../style-dictionary/tokens/dark.json | 221 - .../style-dictionary/tokens/light.json | 233 - frontend/appflowy_tauri/tailwind.config.cjs | 20 - frontend/appflowy_tauri/tsconfig.json | 30 - frontend/appflowy_tauri/tsconfig.node.json | 9 - frontend/appflowy_tauri/vite.config.ts | 70 - .../webdriver/selenium/package.json | 13 - .../webdriver/selenium/test/test.cjs | 76 - frontend/scripts/makefile/env.toml | 9 - frontend/scripts/makefile/tauri.toml | 7 - .../scripts/tool/update_client_api_rev.sh | 2 +- frontend/scripts/tool/update_collab_rev.sh | 2 +- frontend/scripts/tool/update_collab_source.sh | 4 - frontend/scripts/tool/update_local_ai_rev.sh | 2 +- 694 files changed, 3 insertions(+), 52664 deletions(-) delete mode 100644 frontend/appflowy_tauri/.eslintignore delete mode 100644 frontend/appflowy_tauri/.eslintrc.cjs delete mode 100644 frontend/appflowy_tauri/.gitignore delete mode 100644 frontend/appflowy_tauri/.prettierignore delete mode 100644 frontend/appflowy_tauri/.prettierrc.cjs delete mode 100644 frontend/appflowy_tauri/.vscode/extensions.json delete mode 100644 frontend/appflowy_tauri/README.md delete mode 100644 frontend/appflowy_tauri/index.html delete mode 100644 frontend/appflowy_tauri/jest.config.cjs delete mode 100644 frontend/appflowy_tauri/package.json delete mode 100644 frontend/appflowy_tauri/pnpm-lock.yaml delete mode 100644 frontend/appflowy_tauri/postcss.config.cjs delete mode 100644 frontend/appflowy_tauri/public/google_fonts/Poppins/OFL.txt delete mode 100644 frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-Black.ttf delete mode 100644 frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-BlackItalic.ttf delete mode 100644 frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-Bold.ttf delete mode 100644 frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-BoldItalic.ttf delete mode 100644 frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-ExtraBold.ttf delete mode 100644 frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-ExtraBoldItalic.ttf delete mode 100644 frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-ExtraLight.ttf delete mode 100644 frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-ExtraLightItalic.ttf delete mode 100644 frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-Italic.ttf delete mode 100644 frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-Light.ttf delete mode 100644 frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-LightItalic.ttf delete mode 100644 frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-Medium.ttf delete mode 100644 frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-MediumItalic.ttf delete mode 100644 frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-Regular.ttf delete mode 100644 frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-SemiBold.ttf delete mode 100644 frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-SemiBoldItalic.ttf delete mode 100644 frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-Thin.ttf delete mode 100644 frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-ThinItalic.ttf delete mode 100644 frontend/appflowy_tauri/public/google_fonts/Roboto_Mono/LICENSE.txt delete mode 100644 frontend/appflowy_tauri/public/google_fonts/Roboto_Mono/RobotoMono-Italic.ttf delete mode 100644 frontend/appflowy_tauri/public/google_fonts/Roboto_Mono/RobotoMono-Regular.ttf delete mode 100644 frontend/appflowy_tauri/public/launch_splash.jpg delete mode 100644 frontend/appflowy_tauri/public/tauri.svg delete mode 100644 frontend/appflowy_tauri/public/vite.svg delete mode 100644 frontend/appflowy_tauri/scripts/i18n/index.cjs delete mode 100644 frontend/appflowy_tauri/scripts/update_version.cjs delete mode 100644 frontend/appflowy_tauri/src-tauri/.cargo/config.toml delete mode 100644 frontend/appflowy_tauri/src-tauri/.gitignore delete mode 100644 frontend/appflowy_tauri/src-tauri/Cargo.lock delete mode 100644 frontend/appflowy_tauri/src-tauri/Cargo.toml delete mode 100644 frontend/appflowy_tauri/src-tauri/Info.plist delete mode 100644 frontend/appflowy_tauri/src-tauri/build.rs delete mode 100644 frontend/appflowy_tauri/src-tauri/env.development delete mode 100644 frontend/appflowy_tauri/src-tauri/env.production delete mode 100644 frontend/appflowy_tauri/src-tauri/icons/128x128.png delete mode 100644 frontend/appflowy_tauri/src-tauri/icons/128x128@2x.png delete mode 100644 frontend/appflowy_tauri/src-tauri/icons/32x32.png delete mode 100644 frontend/appflowy_tauri/src-tauri/icons/Square107x107Logo.png delete mode 100644 frontend/appflowy_tauri/src-tauri/icons/Square142x142Logo.png delete mode 100644 frontend/appflowy_tauri/src-tauri/icons/Square150x150Logo.png delete mode 100644 frontend/appflowy_tauri/src-tauri/icons/Square284x284Logo.png delete mode 100644 frontend/appflowy_tauri/src-tauri/icons/Square30x30Logo.png delete mode 100644 frontend/appflowy_tauri/src-tauri/icons/Square310x310Logo.png delete mode 100644 frontend/appflowy_tauri/src-tauri/icons/Square44x44Logo.png delete mode 100644 frontend/appflowy_tauri/src-tauri/icons/Square71x71Logo.png delete mode 100644 frontend/appflowy_tauri/src-tauri/icons/Square89x89Logo.png delete mode 100644 frontend/appflowy_tauri/src-tauri/icons/StoreLogo.png delete mode 100644 frontend/appflowy_tauri/src-tauri/icons/icon.icns delete mode 100644 frontend/appflowy_tauri/src-tauri/icons/icon.ico delete mode 100644 frontend/appflowy_tauri/src-tauri/icons/icon.png delete mode 100644 frontend/appflowy_tauri/src-tauri/rust-toolchain.toml delete mode 100644 frontend/appflowy_tauri/src-tauri/rustfmt.toml delete mode 100644 frontend/appflowy_tauri/src-tauri/src/init.rs delete mode 100644 frontend/appflowy_tauri/src-tauri/src/main.rs delete mode 100644 frontend/appflowy_tauri/src-tauri/src/notification.rs delete mode 100644 frontend/appflowy_tauri/src-tauri/src/request.rs delete mode 100644 frontend/appflowy_tauri/src-tauri/tauri.conf.json delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/@types/i18next.d.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/@types/resources.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/App.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/AppMain.hooks.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/AppMain.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/application/database/cell/cell_listeners.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/application/database/cell/cell_service.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/application/database/cell/cell_types.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/application/database/cell/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/application/database/database/database_service.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/application/database/database/database_types.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/application/database/database/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/application/database/database_view/database_view_service.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/application/database/database_view/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/application/database/field/field_listeners.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/application/database/field/field_service.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/application/database/field/field_types.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/application/database/field/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/application/database/field/select_option/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/application/database/field/select_option/select_option_service.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/application/database/field/select_option/select_option_types.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/application/database/field/type_option/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/application/database/field/type_option/type_option_service.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/application/database/field/type_option/type_option_types.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/application/database/filter/filter_data.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/application/database/filter/filter_listeners.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/application/database/filter/filter_service.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/application/database/filter/filter_types.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/application/database/filter/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/application/database/group/group_service.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/application/database/group/group_types.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/application/database/group/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/application/database/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/application/database/row/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/application/database/row/row_listeners.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/application/database/row/row_service.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/application/database/row/row_types.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/application/database/sort/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/application/database/sort/sort_listeners.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/application/database/sort/sort_service.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/application/database/sort/sort_types.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/application/document/document.service.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/application/document/document.types.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/application/folder/page.service.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/application/folder/trash.service.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/application/folder/workspace.service.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/application/notification.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/application/user/auth.service.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/application/user/user.service.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/add.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/align-center.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/align-left.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/align-right.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/arrow-left.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/arrow-right.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/board.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/bold.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/close.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/copy.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/dark-logo.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/database/checkbox-check.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/database/checkbox-uncheck.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/database/field-type-attach.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/database/field-type-checkbox.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/database/field-type-checklist.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/database/field-type-date.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/database/field-type-last-edited-time.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/database/field-type-multi-select.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/database/field-type-number.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/database/field-type-person.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/database/field-type-relation.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/database/field-type-single-select.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/database/field-type-text.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/database/field-type-url.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/date.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/delete.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/details.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/document.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/drag.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/dropdown.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/edit.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/eye_close.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/eye_open.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/grid.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/h1.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/h2.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/h3.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/hide-menu.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/hide.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/image.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/images/default_cover.jpg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/information.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/inline-code.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/italic.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/left.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/light-logo.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/link.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/list-dropdown.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/list.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/logo.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/mention.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/more.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/numbers.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/open.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/quote.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/react.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/right.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/search.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/select-check.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/settings.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/settings/account.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/settings/check_circle.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/settings/dark.png delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/settings/discord.png delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/settings/github.png delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/settings/google.png delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/settings/light.png delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/settings/workplace.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/show-menu.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/sort.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/strikethrough.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/text.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/todo-list.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/underline.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/assets/up.svg delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/avatar/ProfileAvatar.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/avatar/WorkplaceAvatar.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/avatar/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/confirm_dialog/DeleteConfirmDialog.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/confirm_dialog/RenameDialog.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/devtool/AppFlowyDevTool.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/devtool/ManualSignInDialog.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/drag_block/drag.hooks.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/drag_block/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/emoji_picker/EmojiPicker.hooks.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/emoji_picker/EmojiPicker.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/emoji_picker/EmojiPickerCategories.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/emoji_picker/EmojiPickerHeader.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/error_boundary/withError.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/image_upload/EmbedLink.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/image_upload/LocalImage.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/image_upload/Unsplash.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/image_upload/UploadImage.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/image_upload/UploadTabs.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/image_upload/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/katex_math/KatexMath.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/katex_math/index.css delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/keyboard_navigation/KeyboardNavigation.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/keyboard_navigation/utils.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/notify/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/popover/Popover.hooks.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/popover/utils.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/scroller/AFScroller.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/scroller/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/view_title/ViewBanner.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/view_title/ViewIcon.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/view_title/ViewIconGroup.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/view_title/ViewTitle.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/view_title/ViewTitleInput.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/view_title/cover/Colors.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/view_title/cover/CoverPopover.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/view_title/cover/ViewCover.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/view_title/cover/ViewCoverActions.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/_shared/view_title/cover/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/auth/LoginButtonGroup.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/auth/ProtectedRoutes.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/auth/Welcome.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/auth/auth.hooks.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/Database.hooks.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/Database.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/DatabaseLoader.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/DatabaseTitle.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/DatabaseView.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/_shared/CellText.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/_shared/LinearProgressWithLabel.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/_shared/constants.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/_shared/dnd/dnd.context.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/_shared/dnd/drag.hooks.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/_shared/dnd/drop.hooks.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/_shared/dnd/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/_shared/dnd/utils.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/_shared/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/board/Board.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/board/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/calendar/Calendar.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/calendar/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/Cell.hooks.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/Cell.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/CheckboxCell.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/ChecklistCell.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/DateTimeCell.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/NumberCell.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/SelectCell.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/TextCell.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/TimestampCell.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/URLCell.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/database_settings/DatabaseCollection.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/database_settings/DatabaseSettings.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/database_settings/FilterSettings.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/database_settings/Properties.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/database_settings/SettingsMenu.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/database_settings/SortSettings.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/database_settings/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/EditRecord.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/ExpandRecordModal.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/RecordActions.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/RecordDocument.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/RecordHeader.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/RecordTitle.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/record_properties/Property.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/record_properties/PropertyList.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/record_properties/PropertyName.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/record_properties/PropertyValue.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/record_properties/RecordProperties.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/record_properties/SwitchPropertiesVisible.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/checklist/AddNewOption.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/checklist/ChecklistCellActions.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/checklist/ChecklistItem.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/checklist/LinearProgressWithLabel.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/CustomCalendar.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/DateFormat.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/DateTimeCellActions.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/DateTimeFieldActions.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/DateTimeFormat.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/DateTimeFormatSelect.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/DateTimeInput.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/DateTimeSet.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/IncludeTimeSwitch.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/RangeSwitch.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/TimeFormat.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/calendar.scss delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/utils.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/number/EditNumberCellInput.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/number/NumberFieldActions.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/number/NumberFormatMenu.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/number/NumberFormatSelect.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/number/const.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/SelectOptionModifyMenu.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/Tag.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/constants.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/select_cell_actions/SearchInput.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/select_cell_actions/SelectCellActions.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/select_cell_actions/SelectOptionItem.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/select_field_actions/AddAnOption.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/select_field_actions/Option.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/select_field_actions/Options.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/select_field_actions/SelectFieldActions.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/text/EditTextCellInput.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/ConditionSelect.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/Filter.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/FilterActions.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/FilterConditionSelect.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/FilterFieldsMenu.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/Filters.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/date_filter/DateFilter.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/date_filter/DateFilterValue.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/number_filter/NumberFilterValue.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/select_filter/SelectFilter.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/select_filter/SelectFilterValue.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/text_filter/TextFilter.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/text_filter/TextFilterValue.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/NewProperty.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/PropertiesList.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/Property.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/PropertyActions.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/PropertyMenu.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/PropertyNameInput.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/PropertySelect.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/property_type/PropertyTypeMenu.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/property_type/PropertyTypeMenuExtension.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/property_type/PropertyTypeSelect.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/property_type/PropertyTypeText.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/property_type/ProppertyTypeSvg.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/sort/SortConditionSelect.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/sort/SortFieldsMenu.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/sort/SortItem.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/sort/SortMenu.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/sort/Sorts.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/sort/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/tab_bar/AddViewBtn.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/tab_bar/DatabaseTabBar.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/tab_bar/TextButton.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/tab_bar/ViewActions.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/tab_bar/ViewTabs.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/components/tab_bar/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/database.scss delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/grid/Grid.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/grid/constants.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_calculate/GridCalculate.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_calculate/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_cell/GridCell.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_cell/PrimaryCell.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_cell/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_field/GridField.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_field/GridFieldMenu.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_field/GridNewField.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_field/GridResizer.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_field/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_new_row/GridNewRow.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_overlay/GridTableOverlay.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_row_actions/GridRowActions.hooks.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_row_actions/GridRowActions.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_row_actions/GridRowContextMenu.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_row_actions/GridRowDragButton.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_row_actions/GridRowMenu.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_row_actions/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_sticky_header/GridStickyHeader.hooks.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_sticky_header/GridStickyHeader.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_table/GridTable.hooks.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_table/GridTable.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_table/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/grid/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/database/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/document/Document.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/document/document_header/DocumentHeader.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/document/document_header/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/document/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/Editor.hooks.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/Editor.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/command/formula.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/command/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/command/mark.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/command/tab.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/_shared/Placeholder.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/_shared/PlaceholderContent.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/_shared/unSupportBlock.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/bulleted_list/BulletedList.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/bulleted_list/BulletedListIcon.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/bulleted_list/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/callout/Callout.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/callout/CalloutIcon.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/callout/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/code/Code.hooks.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/code/Code.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/code/SelectLanguage.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/code/constants.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/code/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/code/utils.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/database/DatabaseEmpty.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/database/DatabaseList.hooks.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/database/DatabaseList.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/database/Drawer.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/database/GridBlock.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/database/GridView.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/database/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/database/utils.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/divider/DividerNode.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/divider/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/heading/Heading.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/heading/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/image/ImageActions.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/image/ImageBlock.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/image/ImageEmpty.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/image/ImageRender.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/image/ImageResizer.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/image/UploadPopover.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/image/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/math_equation/EditPopover.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/math_equation/MathEquation.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/math_equation/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/numbered_list/NumberListIcon.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/numbered_list/NumberedList.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/numbered_list/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/page/Page.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/page/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/paragraph/Paragraph.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/paragraph/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/quote/Quote.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/quote/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/text/StartIcon.hooks.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/text/Text.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/text/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/todo_list/CheckboxIcon.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/todo_list/TodoList.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/todo_list/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/toggle_list/ToggleIcon.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/toggle_list/ToggleList.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/toggle_list/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/editor/CollaborativeEditor.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/editor/CustomEditable.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/editor/Editor.hooks.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/editor/Editor.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/editor/Element.hooks.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/editor/Element.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/editor/Leaf.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/editor/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/editor/utils.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/InlineChromiumBugfix.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/inline_formula/FormulaEditPopover.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/inline_formula/FormulaLeaf.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/inline_formula/InlineFormula.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/inline_formula/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/link/Link.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/link/LinkEditContent.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/link/LinkEditInput.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/link/LinkEditPopover.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/link/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/mention/Mention.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/mention/MentionLeaf.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/mention/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/withInline.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/_shared/ColorPicker.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/_shared/CustomColorPicker.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/_shared/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/block_actions/AddBlockBelow.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/block_actions/BlockActions.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/block_actions/BlockActionsToolbar.hooks.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/block_actions/BlockActionsToolbar.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/block_actions/BlockOperationMenu.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/block_actions/color/Color.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/block_actions/color/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/block_actions/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/block_actions/utils.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/command_panel/Command.hooks.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/command_panel/CommandPanel.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/command_panel/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/command_panel/mention_panel/MentionPanel.hooks.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/command_panel/mention_panel/MentionPanel.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/command_panel/mention_panel/MentionPanelContent.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/command_panel/mention_panel/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/command_panel/slash_command_panel/SlashCommandPanel.hooks.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/command_panel/slash_command_panel/SlashCommandPanel.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/command_panel/slash_command_panel/SlashCommandPanelContent.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/command_panel/slash_command_panel/const.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/command_panel/slash_command_panel/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/command_panel/utils.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/popover.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/SelectionActions.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/SelectionToolbar.hooks.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/SelectionToolbar.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/_shared/ActionButton.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/align/Align.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/align/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/bold/Bold.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/bold/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/bulleted_list/BulletedList.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/bulleted_list/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/color/Color.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/color/ColorPopover.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/color/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/formula/Formula.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/formula/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/heading/Heading.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/heading/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/href/Href.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/href/LinkActions.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/href/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/inline_code/InlineCode.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/inline_code/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/italic/Italic.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/italic/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/numbered_list/NumberedList.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/numbered_list/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/paragraph/Paragraph.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/paragraph/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/quote/Quote.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/quote/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/strikethrough/StrikeThrough.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/strikethrough/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/todo_list/TodoList.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/todo_list/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/toggle_list/ToggleList.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/toggle_list/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/underline/Underline.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/underline/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/utils.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/editor.scss delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/plugins/constants.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/plugins/copyPasted/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/plugins/copyPasted/utils.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/plugins/copyPasted/withCopy.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/plugins/copyPasted/withPasted.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/plugins/shortcuts/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/plugins/shortcuts/markdown.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/plugins/shortcuts/shortcuts.hooks.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/plugins/shortcuts/withMarkdown.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/plugins/utils.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/plugins/withBlockDelete.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/plugins/withBlockInsertBreak.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/plugins/withBlockMove.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/plugins/withBlockPlugins.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/plugins/withSplitNodes.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/provider/__tests__/action.test.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/provider/__tests__/observe.test.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/provider/__tests__/read_me.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/provider/__tests__/utils/convert.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/provider/__tests__/utils/mockBackendService.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/provider/data_client.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/provider/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/provider/provider.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/provider/types/y_event.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/provider/utils/action.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/provider/utils/convert.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/provider/utils/delta.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/provider/utils/relation.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/stores/block.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/stores/decorate.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/stores/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/stores/inline_node.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/stores/selected.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/editor/stores/slash.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/error/Error.hooks.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/error/ErrorHandlerPage.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/error/ErrorModal.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/layout/FooterPanel.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/layout/Layout.hooks.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/layout/Layout.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/layout/bread_crumb/BreadCrumb.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/layout/bread_crumb/Breadcrumb.hooks.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/layout/collapse_menu_button/CollapseMenuButton.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/layout/layout.scss delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/layout/nested_page/AddButton.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/layout/nested_page/DeleteDialog.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/layout/nested_page/MoreButton.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/layout/nested_page/NestedPage.hooks.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/layout/nested_page/NestedPage.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/layout/nested_page/NestedPageTitle.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/layout/nested_page/OperationMenu.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/layout/share/Share.hooks.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/layout/share/Share.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/layout/side_bar/Resizer.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/layout/side_bar/SideBar.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/layout/side_bar/UserInfo.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/layout/top_bar/DeletePageSnackbar.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/layout/top_bar/FontSizeConfig.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/layout/top_bar/MoreButton.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/layout/top_bar/MoreOptions.hooks.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/layout/top_bar/MoreOptions.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/layout/top_bar/TopBar.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/layout/workspace_manager/NestedPages.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/layout/workspace_manager/NewPageButton.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/layout/workspace_manager/TrashButton.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/layout/workspace_manager/Workspace.hooks.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/layout/workspace_manager/Workspace.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/layout/workspace_manager/WorkspaceManager.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/settings/Login.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/settings/Settings.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/settings/SettingsDialog.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/settings/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/settings/my_account/AccountLogin.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/settings/my_account/DeleteAccount.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/settings/my_account/DeleteAccountDialog.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/settings/my_account/MyAccount.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/settings/my_account/Profile.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/settings/my_account/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/settings/workplace/Appearance.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/settings/workplace/Workplace.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/settings/workplace/WorkplaceDisplay.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/settings/workplace/appearance/LanguageSetting.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/settings/workplace/appearance/ThemeModeSwitch.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/settings/workplace/const.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/settings/workplace/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/trash/Trash.hooks.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/trash/Trash.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/components/trash/TrashItem.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/hooks/ViewId.hooks.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/hooks/index.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/hooks/notification.hooks.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/hooks/page.hooks.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/i18n/config.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/slate-editor.d.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/stores/reducers/current-user/slice.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/stores/reducers/error/slice.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/stores/reducers/pages/async_actions.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/stores/reducers/pages/slice.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/stores/reducers/sidebar/slice.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/stores/reducers/trash/slice.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/stores/reducers/workspace/slice.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/stores/store.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/utils/async_queue.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/utils/avatar.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/utils/change_notifier.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/utils/color.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/utils/emoji.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/utils/env.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/utils/hotkeys.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/utils/list.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/utils/log.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/utils/mui.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/utils/open_url.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/utils/tool.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/utils/upload_image.ts delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/views/DatabasePage.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/views/DocumentPage.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/views/TrashPage.tsx delete mode 100644 frontend/appflowy_tauri/src/appflowy_app/vite-env.d.ts delete mode 100644 frontend/appflowy_tauri/src/main.tsx delete mode 100644 frontend/appflowy_tauri/src/services/backend/index.ts delete mode 100644 frontend/appflowy_tauri/src/styles/font.css delete mode 100644 frontend/appflowy_tauri/src/styles/tailwind.css delete mode 100644 frontend/appflowy_tauri/src/styles/template.css delete mode 100644 frontend/appflowy_tauri/src/styles/variables/dark.variables.css delete mode 100644 frontend/appflowy_tauri/src/styles/variables/index.css delete mode 100644 frontend/appflowy_tauri/src/styles/variables/light.variables.css delete mode 100644 frontend/appflowy_tauri/src/tests/helpers/init.ts delete mode 100644 frontend/appflowy_tauri/style-dictionary/config.cjs delete mode 100644 frontend/appflowy_tauri/style-dictionary/tailwind/box-shadow.cjs delete mode 100644 frontend/appflowy_tauri/style-dictionary/tailwind/colors.cjs delete mode 100644 frontend/appflowy_tauri/style-dictionary/tokens/base.json delete mode 100644 frontend/appflowy_tauri/style-dictionary/tokens/dark.json delete mode 100644 frontend/appflowy_tauri/style-dictionary/tokens/light.json delete mode 100644 frontend/appflowy_tauri/tailwind.config.cjs delete mode 100644 frontend/appflowy_tauri/tsconfig.json delete mode 100644 frontend/appflowy_tauri/tsconfig.node.json delete mode 100644 frontend/appflowy_tauri/vite.config.ts delete mode 100644 frontend/appflowy_tauri/webdriver/selenium/package.json delete mode 100644 frontend/appflowy_tauri/webdriver/selenium/test/test.cjs diff --git a/.github/workflows/android_ci.yaml.bak b/.github/workflows/android_ci.yaml.bak index a45ef67711..4e159e845b 100644 --- a/.github/workflows/android_ci.yaml.bak +++ b/.github/workflows/android_ci.yaml.bak @@ -7,7 +7,6 @@ on: paths: - ".github/workflows/mobile_ci.yaml" - "frontend/**" - - "!frontend/appflowy_tauri/**" pull_request: branches: diff --git a/.github/workflows/ios_ci.yaml b/.github/workflows/ios_ci.yaml index bd71c77b3b..d9bee0242a 100644 --- a/.github/workflows/ios_ci.yaml +++ b/.github/workflows/ios_ci.yaml @@ -7,7 +7,6 @@ on: paths: - ".github/workflows/mobile_ci.yaml" - "frontend/**" - - "!frontend/appflowy_tauri/**" - "!frontend/appflowy_web_app/**" pull_request: @@ -16,7 +15,6 @@ on: paths: - ".github/workflows/mobile_ci.yaml" - "frontend/**" - - "!frontend/appflowy_tauri/**" - "!frontend/appflowy_web_app/**" env: diff --git a/frontend/.vscode/launch.json b/frontend/.vscode/launch.json index 09965baee1..4c4f7aa8e6 100644 --- a/frontend/.vscode/launch.json +++ b/frontend/.vscode/launch.json @@ -121,20 +121,5 @@ // "request": "launch", // "program": "[YOUR_APPLICATION_PATH]", }, - { - // https://tauri.app/v1/guides/debugging/vs-code - "type": "lldb", - "request": "launch", - "name": "AF-tauri: Debug backend", - "cargo": { - "args": [ - "build", - "--manifest-path=./appflowy_tauri/src-tauri/Cargo.toml", - "--no-default-features" - ] - }, - "preLaunchTask": "AF: Tauri UI Dev", - "cwd": "${workspaceRoot}/appflowy_tauri/" - }, ] } diff --git a/frontend/.vscode/tasks.json b/frontend/.vscode/tasks.json index d940eef0a8..0be167fb12 100644 --- a/frontend/.vscode/tasks.json +++ b/frontend/.vscode/tasks.json @@ -245,51 +245,6 @@ "problemMatcher": [], "detail": "appflowy_flutter" }, - { - "label": "AF: Tauri UI Build", - "type": "shell", - "command": "pnpm run build", - "options": { - "cwd": "${workspaceFolder}/appflowy_tauri" - } - }, - { - "label": "AF: Tauri UI Dev", - "type": "shell", - "isBackground": true, - "command": "pnpm sync:i18n && pnpm run dev", - "options": { - "cwd": "${workspaceFolder}/appflowy_tauri" - } - }, - { - "label": "AF: Tauri Clean", - "type": "shell", - "command": "cargo make tauri_clean", - "options": { - "cwd": "${workspaceFolder}" - } - }, - { - "label": "AF: Tauri Clean + Dev", - "type": "shell", - "dependsOrder": "sequence", - "dependsOn": [ - "AF: Tauri Clean", - "AF: Tauri UI Dev" - ], - "options": { - "cwd": "${workspaceFolder}" - } - }, - { - "label": "AF: Tauri ESLint", - "type": "shell", - "command": "npx eslint --fix src", - "options": { - "cwd": "${workspaceFolder}/appflowy_tauri" - } - }, { "label": "AF: Generate Env File", "type": "shell", diff --git a/frontend/appflowy_tauri/.eslintignore b/frontend/appflowy_tauri/.eslintignore deleted file mode 100644 index e0ff674834..0000000000 --- a/frontend/appflowy_tauri/.eslintignore +++ /dev/null @@ -1,7 +0,0 @@ -src/services -src/styles -node_modules/ -dist/ -src-tauri/ -.eslintrc.cjs -tsconfig.json \ No newline at end of file diff --git a/frontend/appflowy_tauri/.eslintrc.cjs b/frontend/appflowy_tauri/.eslintrc.cjs deleted file mode 100644 index a1160f0bd3..0000000000 --- a/frontend/appflowy_tauri/.eslintrc.cjs +++ /dev/null @@ -1,73 +0,0 @@ -module.exports = { - // https://eslint.org/docs/latest/use/configure/configuration-files - env: { - browser: true, - es6: true, - node: true, - }, - extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'], - parser: '@typescript-eslint/parser', - parserOptions: { - project: 'tsconfig.json', - sourceType: 'module', - tsconfigRootDir: __dirname, - extraFileExtensions: ['.json'], - }, - plugins: ['@typescript-eslint', "react-hooks"], - rules: { - "react-hooks/rules-of-hooks": "error", - "react-hooks/exhaustive-deps": "error", - '@typescript-eslint/adjacent-overload-signatures': 'error', - '@typescript-eslint/no-empty-function': 'error', - '@typescript-eslint/no-empty-interface': 'error', - '@typescript-eslint/no-floating-promises': 'error', - '@typescript-eslint/await-thenable': 'error', - '@typescript-eslint/no-namespace': 'error', - '@typescript-eslint/no-unnecessary-type-assertion': 'error', - '@typescript-eslint/no-redeclare': 'error', - '@typescript-eslint/prefer-for-of': 'error', - '@typescript-eslint/triple-slash-reference': 'error', - '@typescript-eslint/unified-signatures': 'error', - 'no-shadow': 'off', - '@typescript-eslint/no-shadow': 'off', - 'constructor-super': 'error', - eqeqeq: ['error', 'always'], - 'no-cond-assign': 'error', - 'no-duplicate-case': 'error', - 'no-duplicate-imports': 'error', - 'no-empty': [ - 'error', - { - allowEmptyCatch: true, - }, - ], - 'no-invalid-this': 'error', - 'no-new-wrappers': 'error', - 'no-param-reassign': 'error', - 'no-sequences': 'error', - 'no-throw-literal': 'error', - 'no-unsafe-finally': 'error', - 'no-unused-labels': 'error', - 'no-var': 'error', - 'no-void': 'off', - 'prefer-const': 'error', - 'prefer-spread': 'off', - '@typescript-eslint/no-unused-vars': [ - 'error', - { - argsIgnorePattern: '^_', - } - ], - 'padding-line-between-statements': [ - "error", - { blankLine: "always", prev: ["const", "let", "var"], next: "*"}, - { blankLine: "any", prev: ["const", "let", "var"], next: ["const", "let", "var"]}, - { blankLine: "always", prev: "import", next: "*" }, - { blankLine: "any", prev: "import", next: "import" }, - { blankLine: "always", prev: "block-like", next: "*" }, - { blankLine: "always", prev: "block", next: "*" }, - - ] - }, - ignorePatterns: ['src/**/*.test.ts', '**/__tests__/**/*.json', 'package.json'] -}; diff --git a/frontend/appflowy_tauri/.gitignore b/frontend/appflowy_tauri/.gitignore deleted file mode 100644 index 32a3d59bc2..0000000000 --- a/frontend/appflowy_tauri/.gitignore +++ /dev/null @@ -1,33 +0,0 @@ -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -pnpm-debug.log* -lerna-debug.log* - -node_modules -dist -dist-ssr -*.local - -# Editor directories and files -.vscode/* -!.vscode/extensions.json -.idea -.DS_Store -*.suo -*.ntvs* -*.njsproj -*.sln -*.sw? - -**/src/services/backend/models/ -**/src/services/backend/events/ -**/src/appflowy_app/i18n/translations/ - -coverage -**/AppFlowy-Collab - -.env \ No newline at end of file diff --git a/frontend/appflowy_tauri/.prettierignore b/frontend/appflowy_tauri/.prettierignore deleted file mode 100644 index d515c1c2f2..0000000000 --- a/frontend/appflowy_tauri/.prettierignore +++ /dev/null @@ -1,19 +0,0 @@ -.DS_Store -node_modules -/build -/public -/.svelte-kit -/package -/.vscode -.env -.env.* -!.env.example - -# rust and generated ts code -/src-tauri -/src/services - -# Ignore files for PNPM, NPM and YARN -pnpm-lock.yaml -package-lock.json -yarn.lock diff --git a/frontend/appflowy_tauri/.prettierrc.cjs b/frontend/appflowy_tauri/.prettierrc.cjs deleted file mode 100644 index f283db53a2..0000000000 --- a/frontend/appflowy_tauri/.prettierrc.cjs +++ /dev/null @@ -1,20 +0,0 @@ -module.exports = { - arrowParens: 'always', - bracketSpacing: true, - endOfLine: 'lf', - htmlWhitespaceSensitivity: 'css', - insertPragma: false, - jsxBracketSameLine: false, - jsxSingleQuote: true, - printWidth: 121, - plugins: [require('prettier-plugin-tailwindcss')], - proseWrap: 'preserve', - quoteProps: 'as-needed', - requirePragma: false, - semi: true, - singleQuote: true, - tabWidth: 2, - trailingComma: 'es5', - useTabs: false, - vueIndentScriptAndStyle: false, -}; diff --git a/frontend/appflowy_tauri/.vscode/extensions.json b/frontend/appflowy_tauri/.vscode/extensions.json deleted file mode 100644 index 24d7cc6de8..0000000000 --- a/frontend/appflowy_tauri/.vscode/extensions.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "recommendations": ["tauri-apps.tauri-vscode", "rust-lang.rust-analyzer"] -} diff --git a/frontend/appflowy_tauri/README.md b/frontend/appflowy_tauri/README.md deleted file mode 100644 index 102e366893..0000000000 --- a/frontend/appflowy_tauri/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# Tauri + React + Typescript - -This template should help get you started developing with Tauri, React and Typescript in Vite. - -## Recommended IDE Setup - -- [VS Code](https://code.visualstudio.com/) + [Tauri](https://marketplace.visualstudio.com/items?itemName=tauri-apps.tauri-vscode) + [rust-analyzer](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer) diff --git a/frontend/appflowy_tauri/index.html b/frontend/appflowy_tauri/index.html deleted file mode 100644 index 4983fb648b..0000000000 --- a/frontend/appflowy_tauri/index.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - AppFlowy: The Open Source Alternative To Notion - - - -
- - - diff --git a/frontend/appflowy_tauri/jest.config.cjs b/frontend/appflowy_tauri/jest.config.cjs deleted file mode 100644 index 4939478165..0000000000 --- a/frontend/appflowy_tauri/jest.config.cjs +++ /dev/null @@ -1,21 +0,0 @@ -const { compilerOptions } = require('./tsconfig.json'); -const { pathsToModuleNameMapper } = require("ts-jest"); -const esModules = ["lodash-es", "nanoid"].join("|"); - -/** @type {import('ts-jest').JestConfigWithTsJest} */ -module.exports = { - preset: 'ts-jest', - testEnvironment: 'node', - roots: [''], - modulePaths: [compilerOptions.baseUrl], - moduleNameMapper: { - ...pathsToModuleNameMapper(compilerOptions.paths), - "^lodash-es(/(.*)|$)": "lodash$1", - "^nanoid(/(.*)|$)": "nanoid$1", - }, - "transform": { - "(.*)/node_modules/nanoid/.+\\.(j|t)sx?$": "ts-jest" - }, - "transformIgnorePatterns": [`/node_modules/(?!${esModules})`], - "testRegex": "(/__tests__/.*\.(test|spec))\\.(jsx?|tsx?)$", -}; \ No newline at end of file diff --git a/frontend/appflowy_tauri/package.json b/frontend/appflowy_tauri/package.json deleted file mode 100644 index 30c7978771..0000000000 --- a/frontend/appflowy_tauri/package.json +++ /dev/null @@ -1,126 +0,0 @@ -{ - "name": "appflowy_tauri", - "private": true, - "version": "0.0.0", - "type": "module", - "scripts": { - "dev": "vite", - "build": "pnpm sync:i18n && tsc && vite build", - "preview": "vite preview", - "format": "prettier --write .", - "test:code": "eslint --max-warnings=0 --ext .js,.ts,.tsx .", - "test:errors": "pnpm sync:i18n && tsc --noEmit && eslint --ext .js,.ts,.tsx .", - "test:prettier": "pnpm prettier --list-different src", - "tauri:clean": "cargo make --cwd .. tauri_clean", - "tauri:dev": "pnpm sync:i18n && tauri dev", - "sync:i18n": "node scripts/i18n/index.cjs", - "css:variables": "node style-dictionary/config.cjs", - "test": "jest" - }, - "dependencies": { - "@emoji-mart/data": "^1.1.2", - "@emoji-mart/react": "^1.1.1", - "@emotion/react": "^11.10.6", - "@emotion/styled": "^11.10.6", - "@mui/icons-material": "^5.11.11", - "@mui/material": "^5.11.12", - "@mui/system": "^5.14.4", - "@mui/x-date-pickers-pro": "^6.18.2", - "@reduxjs/toolkit": "2.0.0", - "@slate-yjs/core": "^1.0.2", - "@tauri-apps/api": "^1.2.0", - "@types/react-swipeable-views": "^0.13.4", - "dayjs": "^1.11.9", - "emoji-mart": "^5.5.2", - "emoji-regex": "^10.2.1", - "events": "^3.3.0", - "google-protobuf": "^3.15.12", - "i18next": "^22.4.10", - "i18next-browser-languagedetector": "^7.0.1", - "i18next-resources-to-backend": "^1.1.4", - "is-hotkey": "^0.2.0", - "jest": "^29.5.0", - "js-base64": "^3.7.5", - "katex": "^0.16.7", - "lodash-es": "^4.17.21", - "nanoid": "^4.0.0", - "prismjs": "^1.29.0", - "protoc-gen-ts": "0.8.7", - "quill": "^1.3.7", - "quill-delta": "^5.1.0", - "react": "^18.2.0", - "react-beautiful-dnd": "^13.1.1", - "react-big-calendar": "^1.8.5", - "react-color": "^2.19.3", - "react-custom-scrollbars": "^4.2.1", - "react-datepicker": "^4.23.0", - "react-dom": "^18.2.0", - "react-error-boundary": "^3.1.4", - "react-hot-toast": "^2.4.1", - "react-i18next": "^12.2.0", - "react-katex": "^3.0.1", - "react-redux": "^8.0.5", - "react-router-dom": "^6.8.0", - "react-swipeable-views": "^0.14.0", - "react-transition-group": "^4.4.5", - "react-virtualized-auto-sizer": "^1.0.20", - "react-vtree": "^2.0.4", - "react-window": "^1.8.10", - "react18-input-otp": "^1.1.2", - "redux": "^4.2.1", - "rxjs": "^7.8.0", - "sass": "^1.70.0", - "slate": "^0.101.4", - "slate-history": "^0.100.0", - "slate-react": "^0.101.3", - "ts-results": "^3.3.0", - "unsplash-js": "^7.0.19", - "utf8": "^3.0.0", - "valtio": "^1.12.1", - "yjs": "^13.5.51" - }, - "devDependencies": { - "@svgr/plugin-svgo": "^8.0.1", - "@tauri-apps/cli": "^1.5.6", - "@types/google-protobuf": "^3.15.12", - "@types/is-hotkey": "^0.1.7", - "@types/jest": "^29.5.3", - "@types/katex": "^0.16.0", - "@types/lodash-es": "^4.17.11", - "@types/node": "^18.7.10", - "@types/prismjs": "^1.26.0", - "@types/quill": "^2.0.10", - "@types/react": "^18.0.15", - "@types/react-beautiful-dnd": "^13.1.3", - "@types/react-color": "^3.0.6", - "@types/react-custom-scrollbars": "^4.0.13", - "@types/react-datepicker": "^4.19.3", - "@types/react-dom": "^18.0.6", - "@types/react-katex": "^3.0.0", - "@types/react-transition-group": "^4.4.6", - "@types/react-window": "^1.8.8", - "@types/utf8": "^3.0.1", - "@types/uuid": "^9.0.1", - "@typescript-eslint/eslint-plugin": "^5.51.0", - "@typescript-eslint/parser": "^5.51.0", - "@vitejs/plugin-react": "^3.0.0", - "autoprefixer": "^10.4.13", - "babel-jest": "^29.6.2", - "eslint": "^8.34.0", - "eslint-plugin-react": "^7.32.2", - "eslint-plugin-react-hooks": "^4.6.0", - "jest-environment-jsdom": "^29.6.2", - "postcss": "^8.4.21", - "prettier": "2.8.4", - "prettier-plugin-tailwindcss": "^0.2.2", - "style-dictionary": "^3.8.0", - "tailwindcss": "^3.2.7", - "ts-jest": "^29.1.1", - "ts-node-dev": "^2.0.0", - "tsconfig-paths-jest": "^0.0.1", - "typescript": "^4.6.4", - "uuid": "^9.0.0", - "vite": "^4.0.0", - "vite-plugin-svgr": "^3.2.0" - } -} diff --git a/frontend/appflowy_tauri/pnpm-lock.yaml b/frontend/appflowy_tauri/pnpm-lock.yaml deleted file mode 100644 index d670b8b312..0000000000 --- a/frontend/appflowy_tauri/pnpm-lock.yaml +++ /dev/null @@ -1,7264 +0,0 @@ -lockfileVersion: '6.0' - -dependencies: - '@emoji-mart/data': - specifier: ^1.1.2 - version: 1.1.2 - '@emoji-mart/react': - specifier: ^1.1.1 - version: 1.1.1(emoji-mart@5.5.2)(react@18.2.0) - '@emotion/react': - specifier: ^11.10.6 - version: 11.11.0(@types/react@18.2.6)(react@18.2.0) - '@emotion/styled': - specifier: ^11.10.6 - version: 11.11.0(@emotion/react@11.11.0)(@types/react@18.2.6)(react@18.2.0) - '@mui/icons-material': - specifier: ^5.11.11 - version: 5.11.16(@mui/material@5.13.0)(@types/react@18.2.6)(react@18.2.0) - '@mui/material': - specifier: ^5.11.12 - version: 5.13.0(@emotion/react@11.11.0)(@emotion/styled@11.11.0)(@types/react@18.2.6)(react-dom@18.2.0)(react@18.2.0) - '@mui/system': - specifier: ^5.14.4 - version: 5.14.4(@emotion/react@11.11.0)(@emotion/styled@11.11.0)(@types/react@18.2.6)(react@18.2.0) - '@mui/x-date-pickers-pro': - specifier: ^6.18.2 - version: 6.18.2(@emotion/react@11.11.0)(@emotion/styled@11.11.0)(@mui/material@5.13.0)(@mui/system@5.14.4)(@types/react@18.2.6)(dayjs@1.11.9)(react-dom@18.2.0)(react@18.2.0) - '@reduxjs/toolkit': - specifier: 2.0.0 - version: 2.0.0(react-redux@8.0.5)(react@18.2.0) - '@slate-yjs/core': - specifier: ^1.0.2 - version: 1.0.2(slate@0.101.4)(yjs@13.6.1) - '@tauri-apps/api': - specifier: ^1.2.0 - version: 1.3.0 - '@types/react-swipeable-views': - specifier: ^0.13.4 - version: 0.13.4 - dayjs: - specifier: ^1.11.9 - version: 1.11.9 - emoji-mart: - specifier: ^5.5.2 - version: 5.5.2 - emoji-regex: - specifier: ^10.2.1 - version: 10.2.1 - events: - specifier: ^3.3.0 - version: 3.3.0 - google-protobuf: - specifier: ^3.15.12 - version: 3.21.2 - i18next: - specifier: ^22.4.10 - version: 22.4.15 - i18next-browser-languagedetector: - specifier: ^7.0.1 - version: 7.0.1 - i18next-resources-to-backend: - specifier: ^1.1.4 - version: 1.1.4 - is-hotkey: - specifier: ^0.2.0 - version: 0.2.0 - jest: - specifier: ^29.5.0 - version: 29.5.0(@types/node@18.16.9) - js-base64: - specifier: ^3.7.5 - version: 3.7.5 - katex: - specifier: ^0.16.7 - version: 0.16.7 - lodash-es: - specifier: ^4.17.21 - version: 4.17.21 - nanoid: - specifier: ^4.0.0 - version: 4.0.2 - prismjs: - specifier: ^1.29.0 - version: 1.29.0 - protoc-gen-ts: - specifier: 0.8.7 - version: 0.8.7 - quill: - specifier: ^1.3.7 - version: 1.3.7 - quill-delta: - specifier: ^5.1.0 - version: 5.1.0 - react: - specifier: ^18.2.0 - version: 18.2.0 - react-beautiful-dnd: - specifier: ^13.1.1 - version: 13.1.1(react-dom@18.2.0)(react@18.2.0) - react-big-calendar: - specifier: ^1.8.5 - version: 1.8.5(react-dom@18.2.0)(react@18.2.0) - react-color: - specifier: ^2.19.3 - version: 2.19.3(react@18.2.0) - react-custom-scrollbars: - specifier: ^4.2.1 - version: 4.2.1(react-dom@18.2.0)(react@18.2.0) - react-datepicker: - specifier: ^4.23.0 - version: 4.23.0(react-dom@18.2.0)(react@18.2.0) - react-dom: - specifier: ^18.2.0 - version: 18.2.0(react@18.2.0) - react-error-boundary: - specifier: ^3.1.4 - version: 3.1.4(react@18.2.0) - react-hot-toast: - specifier: ^2.4.1 - version: 2.4.1(csstype@3.1.2)(react-dom@18.2.0)(react@18.2.0) - react-i18next: - specifier: ^12.2.0 - version: 12.2.2(i18next@22.4.15)(react-dom@18.2.0)(react@18.2.0) - react-katex: - specifier: ^3.0.1 - version: 3.0.1(prop-types@15.8.1)(react@18.2.0) - react-redux: - specifier: ^8.0.5 - version: 8.0.5(@types/react-dom@18.2.4)(@types/react@18.2.6)(react-dom@18.2.0)(react@18.2.0)(redux@4.2.1) - react-router-dom: - specifier: ^6.8.0 - version: 6.11.1(react-dom@18.2.0)(react@18.2.0) - react-swipeable-views: - specifier: ^0.14.0 - version: 0.14.0(react@18.2.0) - react-transition-group: - specifier: ^4.4.5 - version: 4.4.5(react-dom@18.2.0)(react@18.2.0) - react-virtualized-auto-sizer: - specifier: ^1.0.20 - version: 1.0.20(react-dom@18.2.0)(react@18.2.0) - react-vtree: - specifier: ^2.0.4 - version: 2.0.4(@types/react-window@1.8.8)(react-dom@18.2.0)(react-window@1.8.10)(react@18.2.0) - react-window: - specifier: ^1.8.10 - version: 1.8.10(react-dom@18.2.0)(react@18.2.0) - react18-input-otp: - specifier: ^1.1.2 - version: 1.1.3(react-dom@18.2.0)(react@18.2.0) - redux: - specifier: ^4.2.1 - version: 4.2.1 - rxjs: - specifier: ^7.8.0 - version: 7.8.1 - sass: - specifier: ^1.70.0 - version: 1.70.0 - slate: - specifier: ^0.101.4 - version: 0.101.4 - slate-history: - specifier: ^0.100.0 - version: 0.100.0(slate@0.101.4) - slate-react: - specifier: ^0.101.3 - version: 0.101.3(react-dom@18.2.0)(react@18.2.0)(slate@0.101.4) - ts-results: - specifier: ^3.3.0 - version: 3.3.0 - unsplash-js: - specifier: ^7.0.19 - version: 7.0.19 - utf8: - specifier: ^3.0.0 - version: 3.0.0 - valtio: - specifier: ^1.12.1 - version: 1.12.1(@types/react@18.2.6)(react@18.2.0) - yjs: - specifier: ^13.5.51 - version: 13.6.1 - -devDependencies: - '@svgr/plugin-svgo': - specifier: ^8.0.1 - version: 8.0.1(@svgr/core@7.0.0) - '@tauri-apps/cli': - specifier: ^1.5.6 - version: 1.5.6 - '@types/google-protobuf': - specifier: ^3.15.12 - version: 3.15.12 - '@types/is-hotkey': - specifier: ^0.1.7 - version: 0.1.7 - '@types/jest': - specifier: ^29.5.3 - version: 29.5.3 - '@types/katex': - specifier: ^0.16.0 - version: 0.16.0 - '@types/lodash-es': - specifier: ^4.17.11 - version: 4.17.11 - '@types/node': - specifier: ^18.7.10 - version: 18.16.9 - '@types/prismjs': - specifier: ^1.26.0 - version: 1.26.0 - '@types/quill': - specifier: ^2.0.10 - version: 2.0.10 - '@types/react': - specifier: ^18.0.15 - version: 18.2.6 - '@types/react-beautiful-dnd': - specifier: ^13.1.3 - version: 13.1.4 - '@types/react-color': - specifier: ^3.0.6 - version: 3.0.6 - '@types/react-custom-scrollbars': - specifier: ^4.0.13 - version: 4.0.13 - '@types/react-datepicker': - specifier: ^4.19.3 - version: 4.19.3(react-dom@18.2.0)(react@18.2.0) - '@types/react-dom': - specifier: ^18.0.6 - version: 18.2.4 - '@types/react-katex': - specifier: ^3.0.0 - version: 3.0.0 - '@types/react-transition-group': - specifier: ^4.4.6 - version: 4.4.6 - '@types/react-window': - specifier: ^1.8.8 - version: 1.8.8 - '@types/utf8': - specifier: ^3.0.1 - version: 3.0.1 - '@types/uuid': - specifier: ^9.0.1 - version: 9.0.1 - '@typescript-eslint/eslint-plugin': - specifier: ^5.51.0 - version: 5.59.5(@typescript-eslint/parser@5.59.5)(eslint@8.40.0)(typescript@4.9.5) - '@typescript-eslint/parser': - specifier: ^5.51.0 - version: 5.59.5(eslint@8.40.0)(typescript@4.9.5) - '@vitejs/plugin-react': - specifier: ^3.0.0 - version: 3.1.0(vite@4.3.5) - autoprefixer: - specifier: ^10.4.13 - version: 10.4.14(postcss@8.4.23) - babel-jest: - specifier: ^29.6.2 - version: 29.6.2(@babel/core@7.21.8) - eslint: - specifier: ^8.34.0 - version: 8.40.0 - eslint-plugin-react: - specifier: ^7.32.2 - version: 7.32.2(eslint@8.40.0) - eslint-plugin-react-hooks: - specifier: ^4.6.0 - version: 4.6.0(eslint@8.40.0) - jest-environment-jsdom: - specifier: ^29.6.2 - version: 29.6.2 - postcss: - specifier: ^8.4.21 - version: 8.4.23 - prettier: - specifier: 2.8.4 - version: 2.8.4 - prettier-plugin-tailwindcss: - specifier: ^0.2.2 - version: 0.2.8(prettier@2.8.4) - style-dictionary: - specifier: ^3.8.0 - version: 3.8.0 - tailwindcss: - specifier: ^3.2.7 - version: 3.3.2 - ts-jest: - specifier: ^29.1.1 - version: 29.1.1(@babel/core@7.21.8)(babel-jest@29.6.2)(jest@29.5.0)(typescript@4.9.5) - ts-node-dev: - specifier: ^2.0.0 - version: 2.0.0(@types/node@18.16.9)(typescript@4.9.5) - tsconfig-paths-jest: - specifier: ^0.0.1 - version: 0.0.1 - typescript: - specifier: ^4.6.4 - version: 4.9.5 - uuid: - specifier: ^9.0.0 - version: 9.0.0 - vite: - specifier: ^4.0.0 - version: 4.3.5(@types/node@18.16.9)(sass@1.70.0) - vite-plugin-svgr: - specifier: ^3.2.0 - version: 3.2.0(vite@4.3.5) - -packages: - - /@alloc/quick-lru@5.2.0: - resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} - engines: {node: '>=10'} - dev: true - - /@ampproject/remapping@2.2.1: - resolution: {integrity: sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==} - engines: {node: '>=6.0.0'} - dependencies: - '@jridgewell/gen-mapping': 0.3.3 - '@jridgewell/trace-mapping': 0.3.18 - - /@babel/code-frame@7.21.4: - resolution: {integrity: sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/highlight': 7.18.6 - - /@babel/code-frame@7.23.5: - resolution: {integrity: sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/highlight': 7.23.4 - chalk: 2.4.2 - - /@babel/compat-data@7.21.7: - resolution: {integrity: sha512-KYMqFYTaenzMK4yUtf4EW9wc4N9ef80FsbMtkwool5zpwl4YrT1SdWYSTRcT94KO4hannogdS+LxY7L+arP3gA==} - engines: {node: '>=6.9.0'} - - /@babel/core@7.21.8: - resolution: {integrity: sha512-YeM22Sondbo523Sz0+CirSPnbj9bG3P0CdHcBZdqUuaeOaYEFbOLoGU7lebvGP6P5J/WE9wOn7u7C4J9HvS1xQ==} - engines: {node: '>=6.9.0'} - dependencies: - '@ampproject/remapping': 2.2.1 - '@babel/code-frame': 7.21.4 - '@babel/generator': 7.21.5 - '@babel/helper-compilation-targets': 7.21.5(@babel/core@7.21.8) - '@babel/helper-module-transforms': 7.21.5 - '@babel/helpers': 7.21.5 - '@babel/parser': 7.21.8 - '@babel/template': 7.20.7 - '@babel/traverse': 7.23.7 - '@babel/types': 7.21.5 - convert-source-map: 1.9.0 - debug: 4.3.4 - gensync: 1.0.0-beta.2 - json5: 2.2.3 - semver: 6.3.0 - transitivePeerDependencies: - - supports-color - - /@babel/generator@7.21.5: - resolution: {integrity: sha512-SrKK/sRv8GesIW1bDagf9cCG38IOMYZusoe1dfg0D8aiUe3Amvoj1QtjTPAWcfrZFvIwlleLb0gxzQidL9w14w==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.21.5 - '@jridgewell/gen-mapping': 0.3.3 - '@jridgewell/trace-mapping': 0.3.18 - jsesc: 2.5.2 - - /@babel/generator@7.23.6: - resolution: {integrity: sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.23.6 - '@jridgewell/gen-mapping': 0.3.3 - '@jridgewell/trace-mapping': 0.3.18 - jsesc: 2.5.2 - - /@babel/helper-compilation-targets@7.21.5(@babel/core@7.21.8): - resolution: {integrity: sha512-1RkbFGUKex4lvsB9yhIfWltJM5cZKUftB2eNajaDv3dCMEp49iBG0K14uH8NnX9IPux2+mK7JGEOB0jn48/J6w==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - dependencies: - '@babel/compat-data': 7.21.7 - '@babel/core': 7.21.8 - '@babel/helper-validator-option': 7.21.0 - browserslist: 4.21.5 - lru-cache: 5.1.1 - semver: 6.3.0 - - /@babel/helper-environment-visitor@7.21.5: - resolution: {integrity: sha512-IYl4gZ3ETsWocUWgsFZLM5i1BYx9SoemminVEXadgLBa9TdeorzgLKm8wWLA6J1N/kT3Kch8XIk1laNzYoHKvQ==} - engines: {node: '>=6.9.0'} - - /@babel/helper-environment-visitor@7.22.20: - resolution: {integrity: sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==} - engines: {node: '>=6.9.0'} - - /@babel/helper-function-name@7.23.0: - resolution: {integrity: sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/template': 7.22.15 - '@babel/types': 7.23.6 - - /@babel/helper-hoist-variables@7.22.5: - resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.23.6 - - /@babel/helper-module-imports@7.21.4: - resolution: {integrity: sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.21.5 - - /@babel/helper-module-transforms@7.21.5: - resolution: {integrity: sha512-bI2Z9zBGY2q5yMHoBvJ2a9iX3ZOAzJPm7Q8Yz6YeoUjU/Cvhmi2G4QyTNyPBqqXSgTjUxRg3L0xV45HvkNWWBw==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-environment-visitor': 7.21.5 - '@babel/helper-module-imports': 7.21.4 - '@babel/helper-simple-access': 7.21.5 - '@babel/helper-split-export-declaration': 7.18.6 - '@babel/helper-validator-identifier': 7.19.1 - '@babel/template': 7.20.7 - '@babel/traverse': 7.23.7 - '@babel/types': 7.21.5 - transitivePeerDependencies: - - supports-color - - /@babel/helper-plugin-utils@7.21.5: - resolution: {integrity: sha512-0WDaIlXKOX/3KfBK/dwP1oQGiPh6rjMkT7HIRv7i5RR2VUMwrx5ZL0dwBkKx7+SW1zwNdgjHd34IMk5ZjTeHVg==} - engines: {node: '>=6.9.0'} - - /@babel/helper-simple-access@7.21.5: - resolution: {integrity: sha512-ENPDAMC1wAjR0uaCUwliBdiSl1KBJAVnMTzXqi64c2MG8MPR6ii4qf7bSXDqSFbr4W6W028/rf5ivoHop5/mkg==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.21.5 - - /@babel/helper-split-export-declaration@7.18.6: - resolution: {integrity: sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.21.5 - - /@babel/helper-split-export-declaration@7.22.6: - resolution: {integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.23.6 - - /@babel/helper-string-parser@7.21.5: - resolution: {integrity: sha512-5pTUx3hAJaZIdW99sJ6ZUUgWq/Y+Hja7TowEnLNMm1VivRgZQL3vpBY3qUACVsvw+yQU6+YgfBVmcbLaZtrA1w==} - engines: {node: '>=6.9.0'} - - /@babel/helper-string-parser@7.23.4: - resolution: {integrity: sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==} - engines: {node: '>=6.9.0'} - - /@babel/helper-validator-identifier@7.19.1: - resolution: {integrity: sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==} - engines: {node: '>=6.9.0'} - - /@babel/helper-validator-identifier@7.22.20: - resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==} - engines: {node: '>=6.9.0'} - - /@babel/helper-validator-option@7.21.0: - resolution: {integrity: sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==} - engines: {node: '>=6.9.0'} - - /@babel/helpers@7.21.5: - resolution: {integrity: sha512-BSY+JSlHxOmGsPTydUkPf1MdMQ3M81x5xGCOVgWM3G8XH77sJ292Y2oqcp0CbbgxhqBuI46iUz1tT7hqP7EfgA==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/template': 7.20.7 - '@babel/traverse': 7.23.7 - '@babel/types': 7.21.5 - transitivePeerDependencies: - - supports-color - - /@babel/highlight@7.18.6: - resolution: {integrity: sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-validator-identifier': 7.19.1 - chalk: 2.4.2 - js-tokens: 4.0.0 - - /@babel/highlight@7.23.4: - resolution: {integrity: sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-validator-identifier': 7.22.20 - chalk: 2.4.2 - js-tokens: 4.0.0 - - /@babel/parser@7.21.8: - resolution: {integrity: sha512-6zavDGdzG3gUqAdWvlLFfk+36RilI+Pwyuuh7HItyeScCWP3k6i8vKclAQ0bM/0y/Kz/xiwvxhMv9MgTJP5gmA==} - engines: {node: '>=6.0.0'} - hasBin: true - dependencies: - '@babel/types': 7.21.5 - - /@babel/parser@7.23.6: - resolution: {integrity: sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==} - engines: {node: '>=6.0.0'} - hasBin: true - dependencies: - '@babel/types': 7.23.6 - - /@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.21.8): - resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.8 - '@babel/helper-plugin-utils': 7.21.5 - - /@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.21.8): - resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.8 - '@babel/helper-plugin-utils': 7.21.5 - - /@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.21.8): - resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.8 - '@babel/helper-plugin-utils': 7.21.5 - - /@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.21.8): - resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.8 - '@babel/helper-plugin-utils': 7.21.5 - - /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.21.8): - resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.8 - '@babel/helper-plugin-utils': 7.21.5 - - /@babel/plugin-syntax-jsx@7.21.4(@babel/core@7.21.8): - resolution: {integrity: sha512-5hewiLct5OKyh6PLKEYaFclcqtIgCb6bmELouxjF6up5q3Sov7rOayW4RwhbaBL0dit8rA80GNfY+UuDp2mBbQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.8 - '@babel/helper-plugin-utils': 7.21.5 - - /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.21.8): - resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.8 - '@babel/helper-plugin-utils': 7.21.5 - - /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.21.8): - resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.8 - '@babel/helper-plugin-utils': 7.21.5 - - /@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.21.8): - resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.8 - '@babel/helper-plugin-utils': 7.21.5 - - /@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.21.8): - resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.8 - '@babel/helper-plugin-utils': 7.21.5 - - /@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.21.8): - resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.8 - '@babel/helper-plugin-utils': 7.21.5 - - /@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.21.8): - resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.8 - '@babel/helper-plugin-utils': 7.21.5 - - /@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.21.8): - resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.8 - '@babel/helper-plugin-utils': 7.21.5 - - /@babel/plugin-syntax-typescript@7.21.4(@babel/core@7.21.8): - resolution: {integrity: sha512-xz0D39NvhQn4t4RNsHmDnnsaQizIlUkdtYvLs8La1BlfjQ6JEwxkJGeqJMW2tAXx+q6H+WFuUTXNdYVpEya0YA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.8 - '@babel/helper-plugin-utils': 7.21.5 - - /@babel/plugin-transform-react-jsx-self@7.21.0(@babel/core@7.21.8): - resolution: {integrity: sha512-f/Eq+79JEu+KUANFks9UZCcvydOOGMgF7jBrcwjHa5jTZD8JivnhCJYvmlhR/WTXBWonDExPoW0eO/CR4QJirA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.8 - '@babel/helper-plugin-utils': 7.21.5 - dev: true - - /@babel/plugin-transform-react-jsx-source@7.19.6(@babel/core@7.21.8): - resolution: {integrity: sha512-RpAi004QyMNisst/pvSanoRdJ4q+jMCWyk9zdw/CyLB9j8RXEahodR6l2GyttDRyEVWZtbN+TpLiHJ3t34LbsQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.8 - '@babel/helper-plugin-utils': 7.21.5 - dev: true - - /@babel/runtime@7.0.0: - resolution: {integrity: sha512-7hGhzlcmg01CvH1EHdSPVXYX1aJ8KCEyz6I9xYIi/asDtzBPMyMhVibhM/K6g/5qnKBwjZtp10bNZIEFTRW1MA==} - dependencies: - regenerator-runtime: 0.12.1 - dev: false - - /@babel/runtime@7.21.5: - resolution: {integrity: sha512-8jI69toZqqcsnqGGqwGS4Qb1VwLOEp4hz+CXPywcvjs60u3B4Pom/U/7rm4W8tMOYEB+E9wgD0mW1l3r8qlI9Q==} - engines: {node: '>=6.9.0'} - dependencies: - regenerator-runtime: 0.13.11 - dev: false - - /@babel/runtime@7.22.10: - resolution: {integrity: sha512-21t/fkKLMZI4pqP2wlmsQAWnYW1PDyKyyUV4vCi+B25ydmdaYTKXPwCj0BzSUnZf4seIiYvSA3jcZ3gdsMFkLQ==} - engines: {node: '>=6.9.0'} - dependencies: - regenerator-runtime: 0.14.0 - dev: false - - /@babel/runtime@7.23.4: - resolution: {integrity: sha512-2Yv65nlWnWlSpe3fXEyX5i7fx5kIKo4Qbcj+hMO0odwaneFjfXw5fdum+4yL20O0QiaHpia0cYQ9xpNMqrBwHg==} - engines: {node: '>=6.9.0'} - dependencies: - regenerator-runtime: 0.14.0 - - /@babel/template@7.20.7: - resolution: {integrity: sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/code-frame': 7.21.4 - '@babel/parser': 7.21.8 - '@babel/types': 7.21.5 - - /@babel/template@7.22.15: - resolution: {integrity: sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/code-frame': 7.23.5 - '@babel/parser': 7.23.6 - '@babel/types': 7.23.6 - - /@babel/traverse@7.23.7: - resolution: {integrity: sha512-tY3mM8rH9jM0YHFGyfC0/xf+SB5eKUu7HPj7/k3fpi9dAlsMc5YbQvDi0Sh2QTPXqMhyaAtzAr807TIyfQrmyg==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/code-frame': 7.23.5 - '@babel/generator': 7.23.6 - '@babel/helper-environment-visitor': 7.22.20 - '@babel/helper-function-name': 7.23.0 - '@babel/helper-hoist-variables': 7.22.5 - '@babel/helper-split-export-declaration': 7.22.6 - '@babel/parser': 7.23.6 - '@babel/types': 7.23.6 - debug: 4.3.4 - globals: 11.12.0 - transitivePeerDependencies: - - supports-color - - /@babel/types@7.21.5: - resolution: {integrity: sha512-m4AfNvVF2mVC/F7fDEdH2El3HzUg9It/XsCxZiOTTA3m3qYfcSVSbTfM6Q9xG+hYDniZssYhlXKKUMD5m8tF4Q==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-string-parser': 7.21.5 - '@babel/helper-validator-identifier': 7.19.1 - to-fast-properties: 2.0.0 - - /@babel/types@7.23.6: - resolution: {integrity: sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-string-parser': 7.23.4 - '@babel/helper-validator-identifier': 7.22.20 - to-fast-properties: 2.0.0 - - /@bcoe/v8-coverage@0.2.3: - resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} - - /@cspotcode/source-map-support@0.8.1: - resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} - engines: {node: '>=12'} - dependencies: - '@jridgewell/trace-mapping': 0.3.9 - dev: true - - /@emoji-mart/data@1.1.2: - resolution: {integrity: sha512-1HP8BxD2azjqWJvxIaWAMyTySeZY0Osr83ukYjltPVkNXeJvTz7yDrPLBtnrD5uqJ3tg4CcLuuBW09wahqL/fg==} - dev: false - - /@emoji-mart/react@1.1.1(emoji-mart@5.5.2)(react@18.2.0): - resolution: {integrity: sha512-NMlFNeWgv1//uPsvLxvGQoIerPuVdXwK/EUek8OOkJ6wVOWPUizRBJU0hDqWZCOROVpfBgCemaC3m6jDOXi03g==} - peerDependencies: - emoji-mart: ^5.2 - react: ^16.8 || ^17 || ^18 - dependencies: - emoji-mart: 5.5.2 - react: 18.2.0 - dev: false - - /@emotion/babel-plugin@11.11.0: - resolution: {integrity: sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==} - dependencies: - '@babel/helper-module-imports': 7.21.4 - '@babel/runtime': 7.21.5 - '@emotion/hash': 0.9.1 - '@emotion/memoize': 0.8.1 - '@emotion/serialize': 1.1.2 - babel-plugin-macros: 3.1.0 - convert-source-map: 1.9.0 - escape-string-regexp: 4.0.0 - find-root: 1.1.0 - source-map: 0.5.7 - stylis: 4.2.0 - dev: false - - /@emotion/cache@11.11.0: - resolution: {integrity: sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==} - dependencies: - '@emotion/memoize': 0.8.1 - '@emotion/sheet': 1.2.2 - '@emotion/utils': 1.2.1 - '@emotion/weak-memoize': 0.3.1 - stylis: 4.2.0 - dev: false - - /@emotion/hash@0.9.1: - resolution: {integrity: sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==} - dev: false - - /@emotion/is-prop-valid@1.2.1: - resolution: {integrity: sha512-61Mf7Ufx4aDxx1xlDeOm8aFFigGHE4z+0sKCa+IHCeZKiyP9RLD0Mmx7m8b9/Cf37f7NAvQOOJAbQQGVr5uERw==} - dependencies: - '@emotion/memoize': 0.8.1 - dev: false - - /@emotion/memoize@0.8.1: - resolution: {integrity: sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==} - dev: false - - /@emotion/react@11.11.0(@types/react@18.2.6)(react@18.2.0): - resolution: {integrity: sha512-ZSK3ZJsNkwfjT3JpDAWJZlrGD81Z3ytNDsxw1LKq1o+xkmO5pnWfr6gmCC8gHEFf3nSSX/09YrG67jybNPxSUw==} - peerDependencies: - '@types/react': '*' - react: '>=16.8.0' - peerDependenciesMeta: - '@types/react': - optional: true - dependencies: - '@babel/runtime': 7.21.5 - '@emotion/babel-plugin': 11.11.0 - '@emotion/cache': 11.11.0 - '@emotion/serialize': 1.1.2 - '@emotion/use-insertion-effect-with-fallbacks': 1.0.1(react@18.2.0) - '@emotion/utils': 1.2.1 - '@emotion/weak-memoize': 0.3.1 - '@types/react': 18.2.6 - hoist-non-react-statics: 3.3.2 - react: 18.2.0 - dev: false - - /@emotion/serialize@1.1.2: - resolution: {integrity: sha512-zR6a/fkFP4EAcCMQtLOhIgpprZOwNmCldtpaISpvz348+DP4Mz8ZoKaGGCQpbzepNIUWbq4w6hNZkwDyKoS+HA==} - dependencies: - '@emotion/hash': 0.9.1 - '@emotion/memoize': 0.8.1 - '@emotion/unitless': 0.8.1 - '@emotion/utils': 1.2.1 - csstype: 3.1.2 - dev: false - - /@emotion/sheet@1.2.2: - resolution: {integrity: sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==} - dev: false - - /@emotion/styled@11.11.0(@emotion/react@11.11.0)(@types/react@18.2.6)(react@18.2.0): - resolution: {integrity: sha512-hM5Nnvu9P3midq5aaXj4I+lnSfNi7Pmd4EWk1fOZ3pxookaQTNew6bp4JaCBYM4HVFZF9g7UjJmsUmC2JlxOng==} - peerDependencies: - '@emotion/react': ^11.0.0-rc.0 - '@types/react': '*' - react: '>=16.8.0' - peerDependenciesMeta: - '@types/react': - optional: true - dependencies: - '@babel/runtime': 7.21.5 - '@emotion/babel-plugin': 11.11.0 - '@emotion/is-prop-valid': 1.2.1 - '@emotion/react': 11.11.0(@types/react@18.2.6)(react@18.2.0) - '@emotion/serialize': 1.1.2 - '@emotion/use-insertion-effect-with-fallbacks': 1.0.1(react@18.2.0) - '@emotion/utils': 1.2.1 - '@types/react': 18.2.6 - react: 18.2.0 - dev: false - - /@emotion/unitless@0.8.1: - resolution: {integrity: sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==} - dev: false - - /@emotion/use-insertion-effect-with-fallbacks@1.0.1(react@18.2.0): - resolution: {integrity: sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==} - peerDependencies: - react: '>=16.8.0' - dependencies: - react: 18.2.0 - dev: false - - /@emotion/utils@1.2.1: - resolution: {integrity: sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg==} - dev: false - - /@emotion/weak-memoize@0.3.1: - resolution: {integrity: sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==} - dev: false - - /@esbuild/android-arm64@0.17.19: - resolution: {integrity: sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==} - engines: {node: '>=12'} - cpu: [arm64] - os: [android] - requiresBuild: true - dev: true - optional: true - - /@esbuild/android-arm@0.17.19: - resolution: {integrity: sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==} - engines: {node: '>=12'} - cpu: [arm] - os: [android] - requiresBuild: true - dev: true - optional: true - - /@esbuild/android-x64@0.17.19: - resolution: {integrity: sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==} - engines: {node: '>=12'} - cpu: [x64] - os: [android] - requiresBuild: true - dev: true - optional: true - - /@esbuild/darwin-arm64@0.17.19: - resolution: {integrity: sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==} - engines: {node: '>=12'} - cpu: [arm64] - os: [darwin] - requiresBuild: true - dev: true - optional: true - - /@esbuild/darwin-x64@0.17.19: - resolution: {integrity: sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==} - engines: {node: '>=12'} - cpu: [x64] - os: [darwin] - requiresBuild: true - dev: true - optional: true - - /@esbuild/freebsd-arm64@0.17.19: - resolution: {integrity: sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==} - engines: {node: '>=12'} - cpu: [arm64] - os: [freebsd] - requiresBuild: true - dev: true - optional: true - - /@esbuild/freebsd-x64@0.17.19: - resolution: {integrity: sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [freebsd] - requiresBuild: true - dev: true - optional: true - - /@esbuild/linux-arm64@0.17.19: - resolution: {integrity: sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==} - engines: {node: '>=12'} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@esbuild/linux-arm@0.17.19: - resolution: {integrity: sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==} - engines: {node: '>=12'} - cpu: [arm] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@esbuild/linux-ia32@0.17.19: - resolution: {integrity: sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==} - engines: {node: '>=12'} - cpu: [ia32] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@esbuild/linux-loong64@0.17.19: - resolution: {integrity: sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==} - engines: {node: '>=12'} - cpu: [loong64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@esbuild/linux-mips64el@0.17.19: - resolution: {integrity: sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==} - engines: {node: '>=12'} - cpu: [mips64el] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@esbuild/linux-ppc64@0.17.19: - resolution: {integrity: sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==} - engines: {node: '>=12'} - cpu: [ppc64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@esbuild/linux-riscv64@0.17.19: - resolution: {integrity: sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==} - engines: {node: '>=12'} - cpu: [riscv64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@esbuild/linux-s390x@0.17.19: - resolution: {integrity: sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==} - engines: {node: '>=12'} - cpu: [s390x] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@esbuild/linux-x64@0.17.19: - resolution: {integrity: sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==} - engines: {node: '>=12'} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@esbuild/netbsd-x64@0.17.19: - resolution: {integrity: sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==} - engines: {node: '>=12'} - cpu: [x64] - os: [netbsd] - requiresBuild: true - dev: true - optional: true - - /@esbuild/openbsd-x64@0.17.19: - resolution: {integrity: sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==} - engines: {node: '>=12'} - cpu: [x64] - os: [openbsd] - requiresBuild: true - dev: true - optional: true - - /@esbuild/sunos-x64@0.17.19: - resolution: {integrity: sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==} - engines: {node: '>=12'} - cpu: [x64] - os: [sunos] - requiresBuild: true - dev: true - optional: true - - /@esbuild/win32-arm64@0.17.19: - resolution: {integrity: sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==} - engines: {node: '>=12'} - cpu: [arm64] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /@esbuild/win32-ia32@0.17.19: - resolution: {integrity: sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==} - engines: {node: '>=12'} - cpu: [ia32] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /@esbuild/win32-x64@0.17.19: - resolution: {integrity: sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==} - engines: {node: '>=12'} - cpu: [x64] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /@eslint-community/eslint-utils@4.4.0(eslint@8.40.0): - resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - dependencies: - eslint: 8.40.0 - eslint-visitor-keys: 3.4.1 - dev: true - - /@eslint-community/regexpp@4.5.1: - resolution: {integrity: sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==} - engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - dev: true - - /@eslint/eslintrc@2.0.3: - resolution: {integrity: sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dependencies: - ajv: 6.12.6 - debug: 4.3.4 - espree: 9.5.2 - globals: 13.20.0 - ignore: 5.2.4 - import-fresh: 3.3.0 - js-yaml: 4.1.0 - minimatch: 3.1.2 - strip-json-comments: 3.1.1 - transitivePeerDependencies: - - supports-color - dev: true - - /@eslint/js@8.40.0: - resolution: {integrity: sha512-ElyB54bJIhXQYVKjDSvCkPO1iU1tSAeVQJbllWJq1XQSmmA4dgFk8CbiBGpiOPxleE48vDogxCtmMYku4HSVLA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dev: true - - /@floating-ui/core@1.5.0: - resolution: {integrity: sha512-kK1h4m36DQ0UHGj5Ah4db7R0rHemTqqO0QLvUqi1/mUUp3LuAWbWxdxSIf/XsnH9VS6rRVPLJCncjRzUvyCLXg==} - dependencies: - '@floating-ui/utils': 0.1.6 - dev: false - - /@floating-ui/dom@1.5.3: - resolution: {integrity: sha512-ClAbQnEqJAKCJOEbbLo5IUlZHkNszqhuxS4fHAVxRPXPya6Ysf2G8KypnYcOTpx6I8xcgF9bbHb6g/2KpbV8qA==} - dependencies: - '@floating-ui/core': 1.5.0 - '@floating-ui/utils': 0.1.6 - dev: false - - /@floating-ui/react-dom@2.0.4(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-CF8k2rgKeh/49UrnIBs4BdxPUV6vize/Db1d/YbCLyp9GiVZ0BEwf5AiDSxJRCr6yOkGqTFHtmrULxkEfYZ7dQ==} - peerDependencies: - react: '>=16.8.0' - react-dom: '>=16.8.0' - dependencies: - '@floating-ui/dom': 1.5.3 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - dev: false - - /@floating-ui/utils@0.1.6: - resolution: {integrity: sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A==} - dev: false - - /@humanwhocodes/config-array@0.11.8: - resolution: {integrity: sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==} - engines: {node: '>=10.10.0'} - dependencies: - '@humanwhocodes/object-schema': 1.2.1 - debug: 4.3.4 - minimatch: 3.1.2 - transitivePeerDependencies: - - supports-color - dev: true - - /@humanwhocodes/module-importer@1.0.1: - resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} - engines: {node: '>=12.22'} - dev: true - - /@humanwhocodes/object-schema@1.2.1: - resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} - dev: true - - /@icons/material@0.2.4(react@18.2.0): - resolution: {integrity: sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw==} - peerDependencies: - react: '*' - dependencies: - react: 18.2.0 - dev: false - - /@istanbuljs/load-nyc-config@1.1.0: - resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} - engines: {node: '>=8'} - dependencies: - camelcase: 5.3.1 - find-up: 4.1.0 - get-package-type: 0.1.0 - js-yaml: 3.14.1 - resolve-from: 5.0.0 - - /@istanbuljs/schema@0.1.3: - resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} - engines: {node: '>=8'} - - /@jest/console@29.5.0: - resolution: {integrity: sha512-NEpkObxPwyw/XxZVLPmAGKE89IQRp4puc6IQRPru6JKd1M3fW9v1xM1AnzIJE65hbCkzQAdnL8P47e9hzhiYLQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/types': 29.5.0 - '@types/node': 18.16.9 - chalk: 4.1.2 - jest-message-util: 29.5.0 - jest-util: 29.5.0 - slash: 3.0.0 - - /@jest/core@29.5.0: - resolution: {integrity: sha512-28UzQc7ulUrOQw1IsN/kv1QES3q2kkbl/wGslyhAclqZ/8cMdB5M68BffkIdSJgKBUt50d3hbwJ92XESlE7LiQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - dependencies: - '@jest/console': 29.5.0 - '@jest/reporters': 29.5.0 - '@jest/test-result': 29.5.0 - '@jest/transform': 29.5.0 - '@jest/types': 29.5.0 - '@types/node': 18.16.9 - ansi-escapes: 4.3.2 - chalk: 4.1.2 - ci-info: 3.8.0 - exit: 0.1.2 - graceful-fs: 4.2.11 - jest-changed-files: 29.5.0 - jest-config: 29.5.0(@types/node@18.16.9) - jest-haste-map: 29.5.0 - jest-message-util: 29.5.0 - jest-regex-util: 29.4.3 - jest-resolve: 29.5.0 - jest-resolve-dependencies: 29.5.0 - jest-runner: 29.5.0 - jest-runtime: 29.5.0 - jest-snapshot: 29.5.0 - jest-util: 29.5.0 - jest-validate: 29.5.0 - jest-watcher: 29.5.0 - micromatch: 4.0.5 - pretty-format: 29.5.0 - slash: 3.0.0 - strip-ansi: 6.0.1 - transitivePeerDependencies: - - supports-color - - ts-node - - /@jest/environment@29.5.0: - resolution: {integrity: sha512-5FXw2+wD29YU1d4I2htpRX7jYnAyTRjP2CsXQdo9SAM8g3ifxWPSV0HnClSn71xwctr0U3oZIIH+dtbfmnbXVQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/fake-timers': 29.5.0 - '@jest/types': 29.5.0 - '@types/node': 18.16.9 - jest-mock: 29.5.0 - - /@jest/environment@29.6.4: - resolution: {integrity: sha512-sQ0SULEjA1XUTHmkBRl7A1dyITM9yb1yb3ZNKPX3KlTd6IG7mWUe3e2yfExtC2Zz1Q+mMckOLHmL/qLiuQJrBQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/fake-timers': 29.6.4 - '@jest/types': 29.6.3 - '@types/node': 18.16.9 - jest-mock: 29.6.3 - - /@jest/expect-utils@29.5.0: - resolution: {integrity: sha512-fmKzsidoXQT2KwnrwE0SQq3uj8Z763vzR8LnLBwC2qYWEFpjX8daRsk6rHUM1QvNlEW/UJXNXm59ztmJJWs2Mg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - jest-get-type: 29.4.3 - - /@jest/expect@29.5.0: - resolution: {integrity: sha512-PueDR2HGihN3ciUNGr4uelropW7rqUfTiOn+8u0leg/42UhblPxHkfoh0Ruu3I9Y1962P3u2DY4+h7GVTSVU6g==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - expect: 29.5.0 - jest-snapshot: 29.5.0 - transitivePeerDependencies: - - supports-color - - /@jest/fake-timers@29.5.0: - resolution: {integrity: sha512-9ARvuAAQcBwDAqOnglWq2zwNIRUDtk/SCkp/ToGEhFv5r86K21l+VEs0qNTaXtyiY0lEePl3kylijSYJQqdbDg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/types': 29.5.0 - '@sinonjs/fake-timers': 10.1.0 - '@types/node': 18.16.9 - jest-message-util: 29.5.0 - jest-mock: 29.5.0 - jest-util: 29.5.0 - - /@jest/fake-timers@29.6.4: - resolution: {integrity: sha512-6UkCwzoBK60edXIIWb0/KWkuj7R7Qq91vVInOe3De6DSpaEiqjKcJw4F7XUet24Wupahj9J6PlR09JqJ5ySDHw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/types': 29.6.3 - '@sinonjs/fake-timers': 10.1.0 - '@types/node': 18.16.9 - jest-message-util: 29.6.3 - jest-mock: 29.6.3 - jest-util: 29.6.3 - - /@jest/globals@29.5.0: - resolution: {integrity: sha512-S02y0qMWGihdzNbUiqSAiKSpSozSuHX5UYc7QbnHP+D9Lyw8DgGGCinrN9uSuHPeKgSSzvPom2q1nAtBvUsvPQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/environment': 29.5.0 - '@jest/expect': 29.5.0 - '@jest/types': 29.5.0 - jest-mock: 29.5.0 - transitivePeerDependencies: - - supports-color - - /@jest/reporters@29.5.0: - resolution: {integrity: sha512-D05STXqj/M8bP9hQNSICtPqz97u7ffGzZu+9XLucXhkOFBqKcXe04JLZOgIekOxdb73MAoBUFnqvf7MCpKk5OA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - dependencies: - '@bcoe/v8-coverage': 0.2.3 - '@jest/console': 29.5.0 - '@jest/test-result': 29.5.0 - '@jest/transform': 29.5.0 - '@jest/types': 29.5.0 - '@jridgewell/trace-mapping': 0.3.18 - '@types/node': 18.16.9 - chalk: 4.1.2 - collect-v8-coverage: 1.0.1 - exit: 0.1.2 - glob: 7.2.3 - graceful-fs: 4.2.11 - istanbul-lib-coverage: 3.2.0 - istanbul-lib-instrument: 5.2.1 - istanbul-lib-report: 3.0.0 - istanbul-lib-source-maps: 4.0.1 - istanbul-reports: 3.1.5 - jest-message-util: 29.5.0 - jest-util: 29.5.0 - jest-worker: 29.5.0 - slash: 3.0.0 - string-length: 4.0.2 - strip-ansi: 6.0.1 - v8-to-istanbul: 9.1.0 - transitivePeerDependencies: - - supports-color - - /@jest/schemas@29.4.3: - resolution: {integrity: sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@sinclair/typebox': 0.25.24 - - /@jest/schemas@29.6.3: - resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@sinclair/typebox': 0.27.8 - - /@jest/source-map@29.4.3: - resolution: {integrity: sha512-qyt/mb6rLyd9j1jUts4EQncvS6Yy3PM9HghnNv86QBlV+zdL2inCdK1tuVlL+J+lpiw2BI67qXOrX3UurBqQ1w==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jridgewell/trace-mapping': 0.3.18 - callsites: 3.1.0 - graceful-fs: 4.2.11 - - /@jest/test-result@29.5.0: - resolution: {integrity: sha512-fGl4rfitnbfLsrfx1uUpDEESS7zM8JdgZgOCQuxQvL1Sn/I6ijeAVQWGfXI9zb1i9Mzo495cIpVZhA0yr60PkQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/console': 29.5.0 - '@jest/types': 29.5.0 - '@types/istanbul-lib-coverage': 2.0.4 - collect-v8-coverage: 1.0.1 - - /@jest/test-sequencer@29.5.0: - resolution: {integrity: sha512-yPafQEcKjkSfDXyvtgiV4pevSeyuA6MQr6ZIdVkWJly9vkqjnFfcfhRQqpD5whjoU8EORki752xQmjaqoFjzMQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/test-result': 29.5.0 - graceful-fs: 4.2.11 - jest-haste-map: 29.6.4 - slash: 3.0.0 - - /@jest/transform@29.5.0: - resolution: {integrity: sha512-8vbeZWqLJOvHaDfeMuoHITGKSz5qWc9u04lnWrQE3VyuSw604PzQM824ZeX9XSjUCeDiE3GuxZe5UKa8J61NQw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@babel/core': 7.21.8 - '@jest/types': 29.5.0 - '@jridgewell/trace-mapping': 0.3.18 - babel-plugin-istanbul: 6.1.1 - chalk: 4.1.2 - convert-source-map: 2.0.0 - fast-json-stable-stringify: 2.1.0 - graceful-fs: 4.2.11 - jest-haste-map: 29.5.0 - jest-regex-util: 29.4.3 - jest-util: 29.5.0 - micromatch: 4.0.5 - pirates: 4.0.5 - slash: 3.0.0 - write-file-atomic: 4.0.2 - transitivePeerDependencies: - - supports-color - - /@jest/transform@29.6.4: - resolution: {integrity: sha512-8thgRSiXUqtr/pPGY/OsyHuMjGyhVnWrFAwoxmIemlBuiMyU1WFs0tXoNxzcr4A4uErs/ABre76SGmrr5ab/AA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@babel/core': 7.21.8 - '@jest/types': 29.6.3 - '@jridgewell/trace-mapping': 0.3.18 - babel-plugin-istanbul: 6.1.1 - chalk: 4.1.2 - convert-source-map: 2.0.0 - fast-json-stable-stringify: 2.1.0 - graceful-fs: 4.2.11 - jest-haste-map: 29.6.4 - jest-regex-util: 29.6.3 - jest-util: 29.6.3 - micromatch: 4.0.5 - pirates: 4.0.5 - slash: 3.0.0 - write-file-atomic: 4.0.2 - transitivePeerDependencies: - - supports-color - - /@jest/types@29.5.0: - resolution: {integrity: sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/schemas': 29.4.3 - '@types/istanbul-lib-coverage': 2.0.4 - '@types/istanbul-reports': 3.0.1 - '@types/node': 18.16.9 - '@types/yargs': 17.0.24 - chalk: 4.1.2 - - /@jest/types@29.6.3: - resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/schemas': 29.6.3 - '@types/istanbul-lib-coverage': 2.0.4 - '@types/istanbul-reports': 3.0.1 - '@types/node': 18.16.9 - '@types/yargs': 17.0.24 - chalk: 4.1.2 - - /@jridgewell/gen-mapping@0.3.3: - resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==} - engines: {node: '>=6.0.0'} - dependencies: - '@jridgewell/set-array': 1.1.2 - '@jridgewell/sourcemap-codec': 1.4.15 - '@jridgewell/trace-mapping': 0.3.18 - - /@jridgewell/resolve-uri@3.1.0: - resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==} - engines: {node: '>=6.0.0'} - - /@jridgewell/set-array@1.1.2: - resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} - engines: {node: '>=6.0.0'} - - /@jridgewell/sourcemap-codec@1.4.14: - resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==} - - /@jridgewell/sourcemap-codec@1.4.15: - resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} - - /@jridgewell/trace-mapping@0.3.18: - resolution: {integrity: sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==} - dependencies: - '@jridgewell/resolve-uri': 3.1.0 - '@jridgewell/sourcemap-codec': 1.4.14 - - /@jridgewell/trace-mapping@0.3.9: - resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} - dependencies: - '@jridgewell/resolve-uri': 3.1.0 - '@jridgewell/sourcemap-codec': 1.4.15 - dev: true - - /@juggle/resize-observer@3.4.0: - resolution: {integrity: sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==} - dev: false - - /@mui/base@5.0.0-beta.0(@types/react@18.2.6)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-ap+juKvt8R8n3cBqd/pGtZydQ4v2I/hgJKnvJRGjpSh3RvsvnDHO4rXov8MHQlH6VqpOekwgilFLGxMZjNTucA==} - engines: {node: '>=12.0.0'} - peerDependencies: - '@types/react': ^17.0.0 || ^18.0.0 - react: ^17.0.0 || ^18.0.0 - react-dom: ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - '@types/react': - optional: true - dependencies: - '@babel/runtime': 7.21.5 - '@emotion/is-prop-valid': 1.2.1 - '@mui/types': 7.2.4(@types/react@18.2.6) - '@mui/utils': 5.12.3(react@18.2.0) - '@popperjs/core': 2.11.7 - '@types/react': 18.2.6 - clsx: 1.2.1 - prop-types: 15.8.1 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - react-is: 18.2.0 - dev: false - - /@mui/base@5.0.0-beta.24(@types/react@18.2.6)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-bKt2pUADHGQtqWDZ8nvL2Lvg2GNJyd/ZUgZAJoYzRgmnxBL9j36MSlS3+exEdYkikcnvVafcBtD904RypFKb0w==} - engines: {node: '>=12.0.0'} - peerDependencies: - '@types/react': ^17.0.0 || ^18.0.0 - react: ^17.0.0 || ^18.0.0 - react-dom: ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - '@types/react': - optional: true - dependencies: - '@babel/runtime': 7.23.4 - '@floating-ui/react-dom': 2.0.4(react-dom@18.2.0)(react@18.2.0) - '@mui/types': 7.2.9(@types/react@18.2.6) - '@mui/utils': 5.14.18(@types/react@18.2.6)(react@18.2.0) - '@popperjs/core': 2.11.8 - '@types/react': 18.2.6 - clsx: 2.0.0 - prop-types: 15.8.1 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - dev: false - - /@mui/core-downloads-tracker@5.13.0: - resolution: {integrity: sha512-5nXz2k8Rv2ZjtQY6kXirJVyn2+ODaQuAJmXSJtLDUQDKWp3PFUj6j3bILqR0JGOs9R5ejgwz3crLKsl6GwjwkQ==} - dev: false - - /@mui/icons-material@5.11.16(@mui/material@5.13.0)(@types/react@18.2.6)(react@18.2.0): - resolution: {integrity: sha512-oKkx9z9Kwg40NtcIajF9uOXhxiyTZrrm9nmIJ4UjkU2IdHpd4QVLbCc/5hZN/y0C6qzi2Zlxyr9TGddQx2vx2A==} - engines: {node: '>=12.0.0'} - peerDependencies: - '@mui/material': ^5.0.0 - '@types/react': ^17.0.0 || ^18.0.0 - react: ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - '@types/react': - optional: true - dependencies: - '@babel/runtime': 7.21.5 - '@mui/material': 5.13.0(@emotion/react@11.11.0)(@emotion/styled@11.11.0)(@types/react@18.2.6)(react-dom@18.2.0)(react@18.2.0) - '@types/react': 18.2.6 - react: 18.2.0 - dev: false - - /@mui/material@5.13.0(@emotion/react@11.11.0)(@emotion/styled@11.11.0)(@types/react@18.2.6)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-ckS+9tCpAzpdJdaTF+btF0b6mF9wbXg/EVKtnoAWYi0UKXoXBAVvEUMNpLGA5xdpCdf+A6fPbVUEHs9TsfU+Yw==} - engines: {node: '>=12.0.0'} - peerDependencies: - '@emotion/react': ^11.5.0 - '@emotion/styled': ^11.3.0 - '@types/react': ^17.0.0 || ^18.0.0 - react: ^17.0.0 || ^18.0.0 - react-dom: ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - '@emotion/react': - optional: true - '@emotion/styled': - optional: true - '@types/react': - optional: true - dependencies: - '@babel/runtime': 7.21.5 - '@emotion/react': 11.11.0(@types/react@18.2.6)(react@18.2.0) - '@emotion/styled': 11.11.0(@emotion/react@11.11.0)(@types/react@18.2.6)(react@18.2.0) - '@mui/base': 5.0.0-beta.0(@types/react@18.2.6)(react-dom@18.2.0)(react@18.2.0) - '@mui/core-downloads-tracker': 5.13.0 - '@mui/system': 5.14.4(@emotion/react@11.11.0)(@emotion/styled@11.11.0)(@types/react@18.2.6)(react@18.2.0) - '@mui/types': 7.2.4(@types/react@18.2.6) - '@mui/utils': 5.12.3(react@18.2.0) - '@types/react': 18.2.6 - '@types/react-transition-group': 4.4.6 - clsx: 1.2.1 - csstype: 3.1.2 - prop-types: 15.8.1 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - react-is: 18.2.0 - react-transition-group: 4.4.5(react-dom@18.2.0)(react@18.2.0) - dev: false - - /@mui/private-theming@5.14.4(@types/react@18.2.6)(react@18.2.0): - resolution: {integrity: sha512-ISXsHDiQ3z1XA4IuKn+iXDWvDjcz/UcQBiFZqtdoIsEBt8CB7wgdQf3LwcwqO81dl5ofg/vNQBEnXuKfZHrnYA==} - engines: {node: '>=12.0.0'} - peerDependencies: - '@types/react': ^17.0.0 || ^18.0.0 - react: ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - '@types/react': - optional: true - dependencies: - '@babel/runtime': 7.22.10 - '@mui/utils': 5.14.4(react@18.2.0) - '@types/react': 18.2.6 - prop-types: 15.8.1 - react: 18.2.0 - dev: false - - /@mui/styled-engine@5.13.2(@emotion/react@11.11.0)(@emotion/styled@11.11.0)(react@18.2.0): - resolution: {integrity: sha512-VCYCU6xVtXOrIN8lcbuPmoG+u7FYuOERG++fpY74hPpEWkyFQG97F+/XfTQVYzlR2m7nPjnwVUgATcTCMEaMvw==} - engines: {node: '>=12.0.0'} - peerDependencies: - '@emotion/react': ^11.4.1 - '@emotion/styled': ^11.3.0 - react: ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - '@emotion/react': - optional: true - '@emotion/styled': - optional: true - dependencies: - '@babel/runtime': 7.22.10 - '@emotion/cache': 11.11.0 - '@emotion/react': 11.11.0(@types/react@18.2.6)(react@18.2.0) - '@emotion/styled': 11.11.0(@emotion/react@11.11.0)(@types/react@18.2.6)(react@18.2.0) - csstype: 3.1.2 - prop-types: 15.8.1 - react: 18.2.0 - dev: false - - /@mui/system@5.14.4(@emotion/react@11.11.0)(@emotion/styled@11.11.0)(@types/react@18.2.6)(react@18.2.0): - resolution: {integrity: sha512-oPgfWS97QNfHcDBapdkZIs4G5i85BJt69Hp6wbXF6s7vi3Evcmhdk8AbCRW6n0sX4vTj8oe0mh0RIm1G2A1KDA==} - engines: {node: '>=12.0.0'} - peerDependencies: - '@emotion/react': ^11.5.0 - '@emotion/styled': ^11.3.0 - '@types/react': ^17.0.0 || ^18.0.0 - react: ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - '@emotion/react': - optional: true - '@emotion/styled': - optional: true - '@types/react': - optional: true - dependencies: - '@babel/runtime': 7.22.10 - '@emotion/react': 11.11.0(@types/react@18.2.6)(react@18.2.0) - '@emotion/styled': 11.11.0(@emotion/react@11.11.0)(@types/react@18.2.6)(react@18.2.0) - '@mui/private-theming': 5.14.4(@types/react@18.2.6)(react@18.2.0) - '@mui/styled-engine': 5.13.2(@emotion/react@11.11.0)(@emotion/styled@11.11.0)(react@18.2.0) - '@mui/types': 7.2.4(@types/react@18.2.6) - '@mui/utils': 5.14.4(react@18.2.0) - '@types/react': 18.2.6 - clsx: 2.0.0 - csstype: 3.1.2 - prop-types: 15.8.1 - react: 18.2.0 - dev: false - - /@mui/types@7.2.4(@types/react@18.2.6): - resolution: {integrity: sha512-LBcwa8rN84bKF+f5sDyku42w1NTxaPgPyYKODsh01U1fVstTClbUoSA96oyRBnSNyEiAVjKm6Gwx9vjR+xyqHA==} - peerDependencies: - '@types/react': '*' - peerDependenciesMeta: - '@types/react': - optional: true - dependencies: - '@types/react': 18.2.6 - dev: false - - /@mui/types@7.2.9(@types/react@18.2.6): - resolution: {integrity: sha512-k1lN/PolaRZfNsRdAqXtcR71sTnv3z/VCCGPxU8HfdftDkzi335MdJ6scZxvofMAd/K/9EbzCZTFBmlNpQVdCg==} - peerDependencies: - '@types/react': ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - '@types/react': - optional: true - dependencies: - '@types/react': 18.2.6 - dev: false - - /@mui/utils@5.12.3(react@18.2.0): - resolution: {integrity: sha512-D/Z4Ub3MRl7HiUccid7sQYclTr24TqUAQFFlxHQF8FR177BrCTQ0JJZom7EqYjZCdXhwnSkOj2ph685MSKNtIA==} - engines: {node: '>=12.0.0'} - peerDependencies: - react: ^17.0.0 || ^18.0.0 - dependencies: - '@babel/runtime': 7.21.5 - '@types/prop-types': 15.7.5 - '@types/react-is': 17.0.4 - prop-types: 15.8.1 - react: 18.2.0 - react-is: 18.2.0 - dev: false - - /@mui/utils@5.14.18(@types/react@18.2.6)(react@18.2.0): - resolution: {integrity: sha512-HZDRsJtEZ7WMSnrHV9uwScGze4wM/Y+u6pDVo+grUjt5yXzn+wI8QX/JwTHh9YSw/WpnUL80mJJjgCnWj2VrzQ==} - engines: {node: '>=12.0.0'} - peerDependencies: - '@types/react': ^17.0.0 || ^18.0.0 - react: ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - '@types/react': - optional: true - dependencies: - '@babel/runtime': 7.23.4 - '@types/prop-types': 15.7.11 - '@types/react': 18.2.6 - prop-types: 15.8.1 - react: 18.2.0 - react-is: 18.2.0 - dev: false - - /@mui/utils@5.14.4(react@18.2.0): - resolution: {integrity: sha512-4ANV0txPD3x0IcTCSEHKDWnsutg1K3m6Vz5IckkbLXVYu17oOZCVUdOKsb/txUmaCd0v0PmSRe5PW+Mlvns5dQ==} - engines: {node: '>=12.0.0'} - peerDependencies: - react: ^17.0.0 || ^18.0.0 - dependencies: - '@babel/runtime': 7.22.10 - '@types/prop-types': 15.7.5 - '@types/react-is': 18.2.1 - prop-types: 15.8.1 - react: 18.2.0 - react-is: 18.2.0 - dev: false - - /@mui/x-date-pickers-pro@6.18.2(@emotion/react@11.11.0)(@emotion/styled@11.11.0)(@mui/material@5.13.0)(@mui/system@5.14.4)(@types/react@18.2.6)(dayjs@1.11.9)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-8lEVEOtCQssKWel4Ey1pRulGPXUQ73TnkHKzHWsjdv03FjiUs3eYB+Ej0Uk5yWPmsqlShWhOzOlOGDpzsYJsUg==} - engines: {node: '>=14.0.0'} - peerDependencies: - '@emotion/react': ^11.9.0 - '@emotion/styled': ^11.8.1 - '@mui/material': ^5.8.6 - '@mui/system': ^5.8.0 - date-fns: ^2.25.0 - date-fns-jalali: ^2.13.0-0 - dayjs: ^1.10.7 - luxon: ^3.0.2 - moment: ^2.29.4 - moment-hijri: ^2.1.2 - moment-jalaali: ^0.7.4 || ^0.8.0 || ^0.9.0 || ^0.10.0 - react: ^17.0.0 || ^18.0.0 - react-dom: ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - '@emotion/react': - optional: true - '@emotion/styled': - optional: true - date-fns: - optional: true - date-fns-jalali: - optional: true - dayjs: - optional: true - luxon: - optional: true - moment: - optional: true - moment-hijri: - optional: true - moment-jalaali: - optional: true - dependencies: - '@babel/runtime': 7.23.4 - '@emotion/react': 11.11.0(@types/react@18.2.6)(react@18.2.0) - '@emotion/styled': 11.11.0(@emotion/react@11.11.0)(@types/react@18.2.6)(react@18.2.0) - '@mui/base': 5.0.0-beta.24(@types/react@18.2.6)(react-dom@18.2.0)(react@18.2.0) - '@mui/material': 5.13.0(@emotion/react@11.11.0)(@emotion/styled@11.11.0)(@types/react@18.2.6)(react-dom@18.2.0)(react@18.2.0) - '@mui/system': 5.14.4(@emotion/react@11.11.0)(@emotion/styled@11.11.0)(@types/react@18.2.6)(react@18.2.0) - '@mui/utils': 5.14.18(@types/react@18.2.6)(react@18.2.0) - '@mui/x-date-pickers': 6.18.2(@emotion/react@11.11.0)(@emotion/styled@11.11.0)(@mui/material@5.13.0)(@mui/system@5.14.4)(@types/react@18.2.6)(dayjs@1.11.9)(react-dom@18.2.0)(react@18.2.0) - '@mui/x-license-pro': 6.10.2(@types/react@18.2.6)(react@18.2.0) - clsx: 2.0.0 - dayjs: 1.11.9 - prop-types: 15.8.1 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - react-transition-group: 4.4.5(react-dom@18.2.0)(react@18.2.0) - transitivePeerDependencies: - - '@types/react' - dev: false - - /@mui/x-date-pickers@6.18.2(@emotion/react@11.11.0)(@emotion/styled@11.11.0)(@mui/material@5.13.0)(@mui/system@5.14.4)(@types/react@18.2.6)(dayjs@1.11.9)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-HJq4uoFQSu5isa/mesWw2BKh8KBRYUQb+KaSlVlWfJNgP3YhPvWZ6yqCNYyxOAiPMxb0n3nBjS9ErO27OHjFMA==} - engines: {node: '>=14.0.0'} - peerDependencies: - '@emotion/react': ^11.9.0 - '@emotion/styled': ^11.8.1 - '@mui/material': ^5.8.6 - '@mui/system': ^5.8.0 - date-fns: ^2.25.0 - date-fns-jalali: ^2.13.0-0 - dayjs: ^1.10.7 - luxon: ^3.0.2 - moment: ^2.29.4 - moment-hijri: ^2.1.2 - moment-jalaali: ^0.7.4 || ^0.8.0 || ^0.9.0 || ^0.10.0 - react: ^17.0.0 || ^18.0.0 - react-dom: ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - '@emotion/react': - optional: true - '@emotion/styled': - optional: true - date-fns: - optional: true - date-fns-jalali: - optional: true - dayjs: - optional: true - luxon: - optional: true - moment: - optional: true - moment-hijri: - optional: true - moment-jalaali: - optional: true - dependencies: - '@babel/runtime': 7.23.4 - '@emotion/react': 11.11.0(@types/react@18.2.6)(react@18.2.0) - '@emotion/styled': 11.11.0(@emotion/react@11.11.0)(@types/react@18.2.6)(react@18.2.0) - '@mui/base': 5.0.0-beta.24(@types/react@18.2.6)(react-dom@18.2.0)(react@18.2.0) - '@mui/material': 5.13.0(@emotion/react@11.11.0)(@emotion/styled@11.11.0)(@types/react@18.2.6)(react-dom@18.2.0)(react@18.2.0) - '@mui/system': 5.14.4(@emotion/react@11.11.0)(@emotion/styled@11.11.0)(@types/react@18.2.6)(react@18.2.0) - '@mui/utils': 5.14.18(@types/react@18.2.6)(react@18.2.0) - '@types/react-transition-group': 4.4.9 - clsx: 2.0.0 - dayjs: 1.11.9 - prop-types: 15.8.1 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - react-transition-group: 4.4.5(react-dom@18.2.0)(react@18.2.0) - transitivePeerDependencies: - - '@types/react' - dev: false - - /@mui/x-license-pro@6.10.2(@types/react@18.2.6)(react@18.2.0): - resolution: {integrity: sha512-Baw3shilU+eHgU+QYKNPFUKvfS5rSyNJ98pQx02E0gKA22hWp/XAt88K1qUfUMPlkPpvg/uci6gviQSSLZkuKw==} - engines: {node: '>=14.0.0'} - peerDependencies: - react: ^17.0.0 || ^18.0.0 - dependencies: - '@babel/runtime': 7.23.4 - '@mui/utils': 5.14.18(@types/react@18.2.6)(react@18.2.0) - react: 18.2.0 - transitivePeerDependencies: - - '@types/react' - dev: false - - /@nodelib/fs.scandir@2.1.5: - resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} - engines: {node: '>= 8'} - dependencies: - '@nodelib/fs.stat': 2.0.5 - run-parallel: 1.2.0 - dev: true - - /@nodelib/fs.stat@2.0.5: - resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} - engines: {node: '>= 8'} - dev: true - - /@nodelib/fs.walk@1.2.8: - resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} - engines: {node: '>= 8'} - dependencies: - '@nodelib/fs.scandir': 2.1.5 - fastq: 1.15.0 - dev: true - - /@popperjs/core@2.11.7: - resolution: {integrity: sha512-Cr4OjIkipTtcXKjAsm8agyleBuDHvxzeBoa1v543lbv1YaIwQjESsVcmjiWiPEbC1FIeHOG/Op9kdCmAmiS3Kw==} - dev: false - - /@popperjs/core@2.11.8: - resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} - - /@reduxjs/toolkit@2.0.0(react-redux@8.0.5)(react@18.2.0): - resolution: {integrity: sha512-Kq/a+aO28adYdPoNEu9p800MYPKoUc0tlkYfv035Ief9J7MPq8JvmT7UdpYhvXsoMtOdt567KwZjc9H3Rf8yjg==} - peerDependencies: - react: ^16.9.0 || ^17.0.0 || ^18 - react-redux: ^7.2.1 || ^8.1.3 || ^9.0.0 - peerDependenciesMeta: - react: - optional: true - react-redux: - optional: true - dependencies: - immer: 10.0.3 - react: 18.2.0 - react-redux: 8.0.5(@types/react-dom@18.2.4)(@types/react@18.2.6)(react-dom@18.2.0)(react@18.2.0)(redux@4.2.1) - redux: 5.0.0 - redux-thunk: 3.1.0(redux@5.0.0) - reselect: 5.0.1 - dev: false - - /@remix-run/router@1.6.1: - resolution: {integrity: sha512-YUkWj+xs0oOzBe74OgErsuR3wVn+efrFhXBWrit50kOiED+pvQe2r6MWY0iJMQU/mSVKxvNzL4ZaYvjdX+G7ZA==} - engines: {node: '>=14'} - dev: false - - /@restart/hooks@0.4.15(react@18.2.0): - resolution: {integrity: sha512-cZFXYTxbpzYcieq/mBwSyXgqnGMHoBVh3J7MU0CCoIB4NRZxV9/TuwTBAaLMqpNhC3zTPMCgkQ5Ey07L02Xmcw==} - peerDependencies: - react: '>=16.8.0' - dependencies: - dequal: 2.0.3 - react: 18.2.0 - dev: false - - /@rollup/pluginutils@5.0.2: - resolution: {integrity: sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA==} - engines: {node: '>=14.0.0'} - peerDependencies: - rollup: ^1.20.0||^2.0.0||^3.0.0 - peerDependenciesMeta: - rollup: - optional: true - dependencies: - '@types/estree': 1.0.1 - estree-walker: 2.0.2 - picomatch: 2.3.1 - dev: true - - /@sinclair/typebox@0.25.24: - resolution: {integrity: sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ==} - - /@sinclair/typebox@0.27.8: - resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} - - /@sinonjs/commons@3.0.0: - resolution: {integrity: sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==} - dependencies: - type-detect: 4.0.8 - - /@sinonjs/fake-timers@10.1.0: - resolution: {integrity: sha512-w1qd368vtrwttm1PRJWPW1QHlbmHrVDGs1eBH/jZvRPUFS4MNXV9Q33EQdjOdeAxZ7O8+3wM7zxztm2nfUSyKw==} - dependencies: - '@sinonjs/commons': 3.0.0 - - /@slate-yjs/core@1.0.2(slate@0.101.4)(yjs@13.6.1): - resolution: {integrity: sha512-X0hLFJbQu9c1ItWBaNuEn0pqcXYK76KCp8C4Gvy/VaTQVMo1VgAb2WiiJ0Je/AyuIYEPPSTNVOcyrGHwgA7e6Q==} - peerDependencies: - slate: '>=0.70.0' - yjs: ^13.5.29 - dependencies: - slate: 0.101.4 - y-protocols: 1.0.6(yjs@13.6.1) - yjs: 13.6.1 - dev: false - - /@svgr/babel-plugin-add-jsx-attribute@7.0.0(@babel/core@7.21.8): - resolution: {integrity: sha512-khWbXesWIP9v8HuKCl2NU2HNAyqpSQ/vkIl36Nbn4HIwEYSRWL0H7Gs6idJdha2DkpFDWlsqMELvoCE8lfFY6Q==} - engines: {node: '>=14'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.8 - dev: true - - /@svgr/babel-plugin-remove-jsx-attribute@7.0.0(@babel/core@7.21.8): - resolution: {integrity: sha512-iiZaIvb3H/c7d3TH2HBeK91uI2rMhZNwnsIrvd7ZwGLkFw6mmunOCoVnjdYua662MqGFxlN9xTq4fv9hgR4VXQ==} - engines: {node: '>=14'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.8 - dev: true - - /@svgr/babel-plugin-remove-jsx-empty-expression@7.0.0(@babel/core@7.21.8): - resolution: {integrity: sha512-sQQmyo+qegBx8DfFc04PFmIO1FP1MHI1/QEpzcIcclo5OAISsOJPW76ZIs0bDyO/DBSJEa/tDa1W26pVtt0FRw==} - engines: {node: '>=14'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.8 - dev: true - - /@svgr/babel-plugin-replace-jsx-attribute-value@7.0.0(@babel/core@7.21.8): - resolution: {integrity: sha512-i6MaAqIZXDOJeikJuzocByBf8zO+meLwfQ/qMHIjCcvpnfvWf82PFvredEZElErB5glQFJa2KVKk8N2xV6tRRA==} - engines: {node: '>=14'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.8 - dev: true - - /@svgr/babel-plugin-svg-dynamic-title@7.0.0(@babel/core@7.21.8): - resolution: {integrity: sha512-BoVSh6ge3SLLpKC0pmmN9DFlqgFy4NxNgdZNLPNJWBUU7TQpDWeBuyVuDW88iXydb5Cv0ReC+ffa5h3VrKfk1w==} - engines: {node: '>=14'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.8 - dev: true - - /@svgr/babel-plugin-svg-em-dimensions@7.0.0(@babel/core@7.21.8): - resolution: {integrity: sha512-tNDcBa+hYn0gO+GkP/AuNKdVtMufVhU9fdzu+vUQsR18RIJ9RWe7h/pSBY338RO08wArntwbDk5WhQBmhf2PaA==} - engines: {node: '>=14'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.8 - dev: true - - /@svgr/babel-plugin-transform-react-native-svg@7.0.0(@babel/core@7.21.8): - resolution: {integrity: sha512-qw54u8ljCJYL2KtBOjI5z7Nzg8LnSvQOP5hPKj77H4VQL4+HdKbAT5pnkkZLmHKYwzsIHSYKXxHouD8zZamCFQ==} - engines: {node: '>=14'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.8 - dev: true - - /@svgr/babel-plugin-transform-svg-component@7.0.0(@babel/core@7.21.8): - resolution: {integrity: sha512-CcFECkDj98daOg9jE3Bh3uyD9kzevCAnZ+UtzG6+BQG/jOQ2OA3jHnX6iG4G1MCJkUQFnUvEv33NvQfqrb/F3A==} - engines: {node: '>=12'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.8 - dev: true - - /@svgr/babel-preset@7.0.0(@babel/core@7.21.8): - resolution: {integrity: sha512-EX/NHeFa30j5UjldQGVQikuuQNHUdGmbh9kEpBKofGUtF0GUPJ4T4rhoYiqDAOmBOxojyot36JIFiDUHUK1ilQ==} - engines: {node: '>=14'} - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.21.8 - '@svgr/babel-plugin-add-jsx-attribute': 7.0.0(@babel/core@7.21.8) - '@svgr/babel-plugin-remove-jsx-attribute': 7.0.0(@babel/core@7.21.8) - '@svgr/babel-plugin-remove-jsx-empty-expression': 7.0.0(@babel/core@7.21.8) - '@svgr/babel-plugin-replace-jsx-attribute-value': 7.0.0(@babel/core@7.21.8) - '@svgr/babel-plugin-svg-dynamic-title': 7.0.0(@babel/core@7.21.8) - '@svgr/babel-plugin-svg-em-dimensions': 7.0.0(@babel/core@7.21.8) - '@svgr/babel-plugin-transform-react-native-svg': 7.0.0(@babel/core@7.21.8) - '@svgr/babel-plugin-transform-svg-component': 7.0.0(@babel/core@7.21.8) - dev: true - - /@svgr/core@7.0.0: - resolution: {integrity: sha512-ztAoxkaKhRVloa3XydohgQQCb0/8x9T63yXovpmHzKMkHO6pkjdsIAWKOS4bE95P/2quVh1NtjSKlMRNzSBffw==} - engines: {node: '>=14'} - dependencies: - '@babel/core': 7.21.8 - '@svgr/babel-preset': 7.0.0(@babel/core@7.21.8) - camelcase: 6.3.0 - cosmiconfig: 8.2.0 - transitivePeerDependencies: - - supports-color - dev: true - - /@svgr/hast-util-to-babel-ast@7.0.0: - resolution: {integrity: sha512-42Ej9sDDEmsJKjrfQ1PHmiDiHagh/u9AHO9QWbeNx4KmD9yS5d1XHmXUNINfUcykAU+4431Cn+k6Vn5mWBYimQ==} - engines: {node: '>=14'} - dependencies: - '@babel/types': 7.21.5 - entities: 4.5.0 - dev: true - - /@svgr/plugin-jsx@7.0.0: - resolution: {integrity: sha512-SWlTpPQmBUtLKxXWgpv8syzqIU8XgFRvyhfkam2So8b3BE0OS0HPe5UfmlJ2KIC+a7dpuuYovPR2WAQuSyMoPw==} - engines: {node: '>=14'} - dependencies: - '@babel/core': 7.21.8 - '@svgr/babel-preset': 7.0.0(@babel/core@7.21.8) - '@svgr/hast-util-to-babel-ast': 7.0.0 - svg-parser: 2.0.4 - transitivePeerDependencies: - - supports-color - dev: true - - /@svgr/plugin-svgo@8.0.1(@svgr/core@7.0.0): - resolution: {integrity: sha512-29OJ1QmJgnohQHDAgAuY2h21xWD6TZiXji+hnx+W635RiXTAlHTbjrZDktfqzkN0bOeQEtNe+xgq73/XeWFfSg==} - engines: {node: '>=14'} - peerDependencies: - '@svgr/core': '*' - dependencies: - '@svgr/core': 7.0.0 - cosmiconfig: 8.2.0 - deepmerge: 4.3.1 - svgo: 3.0.2 - dev: true - - /@tauri-apps/api@1.3.0: - resolution: {integrity: sha512-AH+3FonkKZNtfRtGrObY38PrzEj4d+1emCbwNGu0V2ENbXjlLHMZQlUh+Bhu/CRmjaIwZMGJ3yFvWaZZgTHoog==} - engines: {node: '>= 14.6.0', npm: '>= 6.6.0', yarn: '>= 1.19.1'} - dev: false - - /@tauri-apps/cli-darwin-arm64@1.5.6: - resolution: {integrity: sha512-NNvG3XLtciCMsBahbDNUEvq184VZmOveTGOuy0So2R33b/6FDkuWaSgWZsR1mISpOuP034htQYW0VITCLelfqg==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [darwin] - requiresBuild: true - dev: true - optional: true - - /@tauri-apps/cli-darwin-x64@1.5.6: - resolution: {integrity: sha512-nkiqmtUQw3N1j4WoVjv81q6zWuZFhBLya/RNGUL94oafORloOZoSY0uTZJAoeieb3Y1YK0rCHSDl02MyV2Fi4A==} - engines: {node: '>= 10'} - cpu: [x64] - os: [darwin] - requiresBuild: true - dev: true - optional: true - - /@tauri-apps/cli-linux-arm-gnueabihf@1.5.6: - resolution: {integrity: sha512-z6SPx+axZexmWXTIVPNs4Tg7FtvdJl9EKxYN6JPjOmDZcqA13iyqWBQal2DA/GMZ1Xqo3vyJf6EoEaKaliymPQ==} - engines: {node: '>= 10'} - cpu: [arm] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@tauri-apps/cli-linux-arm64-gnu@1.5.6: - resolution: {integrity: sha512-QuQjMQmpsCbzBrmtQiG4uhnfAbdFx3nzm+9LtqjuZlurc12+Mj5MTgqQ3AOwQedH3f7C+KlvbqD2AdXpwTg7VA==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@tauri-apps/cli-linux-arm64-musl@1.5.6: - resolution: {integrity: sha512-8j5dH3odweFeom7bRGlfzDApWVOT4jIq8/214Wl+JeiNVehouIBo9lZGeghZBH3XKFRwEvU23i7sRVjuh2s8mg==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@tauri-apps/cli-linux-x64-gnu@1.5.6: - resolution: {integrity: sha512-gbFHYHfdEGW0ffk8SigDsoXks6USpilF6wR0nqB/JbWzbzFR/sBuLVNQlJl1RKNakyJHu+lsFxGy0fcTdoX8xA==} - engines: {node: '>= 10'} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@tauri-apps/cli-linux-x64-musl@1.5.6: - resolution: {integrity: sha512-9v688ogoLkeFYQNgqiSErfhTreLUd8B3prIBSYUt+x4+5Kcw91zWvIh+VSxL1n3KCGGsM7cuXhkGPaxwlEh1ug==} - engines: {node: '>= 10'} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /@tauri-apps/cli-win32-arm64-msvc@1.5.6: - resolution: {integrity: sha512-DRNDXFNZb6y5IZrw+lhTTA9l4wbzO4TNRBAlHAiXUrH+pRFZ/ZJtv5WEuAj9ocVSahVw2NaK5Yaold4NPAxHog==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /@tauri-apps/cli-win32-ia32-msvc@1.5.6: - resolution: {integrity: sha512-oUYKNR/IZjF4fsOzRpw0xesl2lOjhsQEyWlgbpT25T83EU113Xgck9UjtI7xemNI/OPCv1tPiaM1e7/ABdg5iA==} - engines: {node: '>= 10'} - cpu: [ia32] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /@tauri-apps/cli-win32-x64-msvc@1.5.6: - resolution: {integrity: sha512-RmEf1os9C8//uq2hbjXi7Vgz9ne7798ZxqemAZdUwo1pv3oLVZSz1/IvZmUHPdy2e6zSeySqWu1D0Y3QRNN+dg==} - engines: {node: '>= 10'} - cpu: [x64] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /@tauri-apps/cli@1.5.6: - resolution: {integrity: sha512-k4Y19oVCnt7WZb2TnDzLqfs7o98Jq0tUoVMv+JQSzuRDJqaVu2xMBZ8dYplEn+EccdR5SOMyzaLBJWu38TVK1A==} - engines: {node: '>= 10'} - hasBin: true - optionalDependencies: - '@tauri-apps/cli-darwin-arm64': 1.5.6 - '@tauri-apps/cli-darwin-x64': 1.5.6 - '@tauri-apps/cli-linux-arm-gnueabihf': 1.5.6 - '@tauri-apps/cli-linux-arm64-gnu': 1.5.6 - '@tauri-apps/cli-linux-arm64-musl': 1.5.6 - '@tauri-apps/cli-linux-x64-gnu': 1.5.6 - '@tauri-apps/cli-linux-x64-musl': 1.5.6 - '@tauri-apps/cli-win32-arm64-msvc': 1.5.6 - '@tauri-apps/cli-win32-ia32-msvc': 1.5.6 - '@tauri-apps/cli-win32-x64-msvc': 1.5.6 - dev: true - - /@tootallnate/once@2.0.0: - resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==} - engines: {node: '>= 10'} - dev: true - - /@trysound/sax@0.2.0: - resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==} - engines: {node: '>=10.13.0'} - dev: true - - /@tsconfig/node10@1.0.9: - resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==} - dev: true - - /@tsconfig/node12@1.0.11: - resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} - dev: true - - /@tsconfig/node14@1.0.3: - resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} - dev: true - - /@tsconfig/node16@1.0.4: - resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} - dev: true - - /@types/babel__core@7.20.0: - resolution: {integrity: sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ==} - dependencies: - '@babel/parser': 7.21.8 - '@babel/types': 7.21.5 - '@types/babel__generator': 7.6.4 - '@types/babel__template': 7.4.1 - '@types/babel__traverse': 7.18.5 - - /@types/babel__generator@7.6.4: - resolution: {integrity: sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==} - dependencies: - '@babel/types': 7.21.5 - - /@types/babel__template@7.4.1: - resolution: {integrity: sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==} - dependencies: - '@babel/parser': 7.21.8 - '@babel/types': 7.21.5 - - /@types/babel__traverse@7.18.5: - resolution: {integrity: sha512-enCvTL8m/EHS/zIvJno9nE+ndYPh1/oNFzRYRmtUqJICG2VnCSBzMLW5VN2KCQU91f23tsNKR8v7VJJQMatl7Q==} - dependencies: - '@babel/types': 7.21.5 - - /@types/estree@1.0.1: - resolution: {integrity: sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==} - dev: true - - /@types/google-protobuf@3.15.12: - resolution: {integrity: sha512-40um9QqwHjRS92qnOaDpL7RmDK15NuZYo9HihiJRbYkMQZlWnuH8AdvbMy8/o6lgLmKbDUKa+OALCltHdbOTpQ==} - dev: true - - /@types/graceful-fs@4.1.6: - resolution: {integrity: sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==} - dependencies: - '@types/node': 18.16.9 - - /@types/hoist-non-react-statics@3.3.1: - resolution: {integrity: sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==} - dependencies: - '@types/react': 18.2.6 - hoist-non-react-statics: 3.3.2 - dev: false - - /@types/is-hotkey@0.1.10: - resolution: {integrity: sha512-RvC8KMw5BCac1NvRRyaHgMMEtBaZ6wh0pyPTBu7izn4Sj/AX9Y4aXU5c7rX8PnM/knsuUpC1IeoBkANtxBypsQ==} - dev: false - - /@types/is-hotkey@0.1.7: - resolution: {integrity: sha512-yB5C7zcOM7idwYZZ1wKQ3pTfjA9BbvFqRWvKB46GFddxnJtHwi/b9y84ykQtxQPg5qhdpg4Q/kWU3EGoCTmLzQ==} - dev: true - - /@types/istanbul-lib-coverage@2.0.4: - resolution: {integrity: sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==} - - /@types/istanbul-lib-report@3.0.0: - resolution: {integrity: sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==} - dependencies: - '@types/istanbul-lib-coverage': 2.0.4 - - /@types/istanbul-reports@3.0.1: - resolution: {integrity: sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==} - dependencies: - '@types/istanbul-lib-report': 3.0.0 - - /@types/jest@29.5.3: - resolution: {integrity: sha512-1Nq7YrO/vJE/FYnqYyw0FS8LdrjExSgIiHyKg7xPpn+yi8Q4huZryKnkJatN1ZRH89Kw2v33/8ZMB7DuZeSLlA==} - dependencies: - expect: 29.5.0 - pretty-format: 29.5.0 - dev: true - - /@types/jsdom@20.0.1: - resolution: {integrity: sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ==} - dependencies: - '@types/node': 18.16.9 - '@types/tough-cookie': 4.0.2 - parse5: 7.1.2 - dev: true - - /@types/json-schema@7.0.11: - resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==} - dev: true - - /@types/katex@0.16.0: - resolution: {integrity: sha512-hz+S3nV6Mym5xPbT9fnO8dDhBFQguMYpY0Ipxv06JMi1ORgnEM4M1ymWDUhUNer3ElLmT583opRo4RzxKmh9jw==} - dev: true - - /@types/lodash-es@4.17.11: - resolution: {integrity: sha512-eCw8FYAWHt2DDl77s+AMLLzPn310LKohruumpucZI4oOFJkIgnlaJcy23OKMJxx4r9PeTF13Gv6w+jqjWQaYUg==} - dependencies: - '@types/lodash': 4.14.194 - dev: true - - /@types/lodash@4.14.194: - resolution: {integrity: sha512-r22s9tAS7imvBt2lyHC9B8AGwWnXaYb1tY09oyLkXDs4vArpYJzw09nj8MLx5VfciBPGIb+ZwG0ssYnEPJxn/g==} - dev: true - - /@types/lodash@4.14.202: - resolution: {integrity: sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==} - dev: false - - /@types/node@18.16.9: - resolution: {integrity: sha512-IeB32oIV4oGArLrd7znD2rkHQ6EDCM+2Sr76dJnrHwv9OHBTTM6nuDLK9bmikXzPa0ZlWMWtRGo/Uw4mrzQedA==} - - /@types/parse-json@4.0.0: - resolution: {integrity: sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==} - dev: false - - /@types/prettier@2.7.2: - resolution: {integrity: sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg==} - - /@types/prismjs@1.26.0: - resolution: {integrity: sha512-ZTaqn/qSqUuAq1YwvOFQfVW1AR/oQJlLSZVustdjwI+GZ8kr0MSHBj0tsXPW1EqHubx50gtBEjbPGsdZwQwCjQ==} - dev: true - - /@types/prop-types@15.7.11: - resolution: {integrity: sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==} - dev: false - - /@types/prop-types@15.7.5: - resolution: {integrity: sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==} - - /@types/quill@2.0.10: - resolution: {integrity: sha512-L6OHONEj2v4NRbWQOsn7j1N0SyzhRR3M4g1M6j/uuIwIsIW2ShWHhwbqNvH8hSmVktzqu0lITfdnqVOQ4qkrhA==} - dependencies: - parchment: 1.1.4 - quill-delta: 4.2.2 - dev: true - - /@types/react-beautiful-dnd@13.1.4: - resolution: {integrity: sha512-4bIBdzOr0aavN+88q3C7Pgz+xkb7tz3whORYrmSj77wfVEMfiWiooIwVWFR7KM2e+uGTe5BVrXqSfb0aHeflJA==} - dependencies: - '@types/react': 18.2.6 - dev: true - - /@types/react-color@3.0.6: - resolution: {integrity: sha512-OzPIO5AyRmLA7PlOyISlgabpYUa3En74LP8mTMa0veCA719SvYQov4WLMsHvCgXP+L+KI9yGhYnqZafVGG0P4w==} - dependencies: - '@types/react': 18.2.6 - '@types/reactcss': 1.2.6 - dev: true - - /@types/react-custom-scrollbars@4.0.13: - resolution: {integrity: sha512-t+15reWgAE1jXlrhaZoxjuH/SQf+EG0rzAzSCzTIkSiP5CDT7KhoExNPwIa6uUxtPkjc3gdW/ry7GetLEwCfGA==} - dependencies: - '@types/react': 18.2.6 - dev: true - - /@types/react-datepicker@4.19.3(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-85F9eKWu9fGiD9r4KVVMPYAdkJJswR3Wci9PvqplmB6T+D+VbUqPeKtifg96NZ4nEhufjehW+SX4JLrEWVplWw==} - dependencies: - '@popperjs/core': 2.11.8 - '@types/react': 18.2.6 - date-fns: 2.30.0 - react-popper: 2.3.0(@popperjs/core@2.11.8)(react-dom@18.2.0)(react@18.2.0) - transitivePeerDependencies: - - react - - react-dom - dev: true - - /@types/react-dom@18.2.4: - resolution: {integrity: sha512-G2mHoTMTL4yoydITgOGwWdWMVd8sNgyEP85xVmMKAPUBwQWm9wBPQUmvbeF4V3WBY1P7mmL4BkjQ0SqUpf1snw==} - dependencies: - '@types/react': 18.2.6 - - /@types/react-is@17.0.4: - resolution: {integrity: sha512-FLzd0K9pnaEvKz4D1vYxK9JmgQPiGk1lu23o1kqGsLeT0iPbRSF7b76+S5T9fD8aRa0B8bY7I/3DebEj+1ysBA==} - dependencies: - '@types/react': 17.0.59 - dev: false - - /@types/react-is@18.2.1: - resolution: {integrity: sha512-wyUkmaaSZEzFZivD8F2ftSyAfk6L+DfFliVj/mYdOXbVjRcS87fQJLTnhk6dRZPuJjI+9g6RZJO4PNCngUrmyw==} - dependencies: - '@types/react': 18.2.6 - dev: false - - /@types/react-katex@3.0.0: - resolution: {integrity: sha512-AiHHXh71a2M7Z6z1wj6iA23SkiRF9r0neHUdu8zjU/cT3MyLxDefYHbcceKhV/gjDEZgF3YaiNHyPNtoGUjPvg==} - dependencies: - '@types/react': 18.2.6 - dev: true - - /@types/react-redux@7.1.25: - resolution: {integrity: sha512-bAGh4e+w5D8dajd6InASVIyCo4pZLJ66oLb80F9OBLO1gKESbZcRCJpTT6uLXX+HAB57zw1WTdwJdAsewuTweg==} - dependencies: - '@types/hoist-non-react-statics': 3.3.1 - '@types/react': 18.2.6 - hoist-non-react-statics: 3.3.2 - redux: 4.2.1 - dev: false - - /@types/react-swipeable-views@0.13.4: - resolution: {integrity: sha512-hQV9Oq6oa+9HKdnGd43xkckElwf5dThOiegtQxqE7qX761oHhxnZO07fz6IsKSnUy9J3tzlRQBu3sNyvC8+kYw==} - dependencies: - '@types/react': 18.2.6 - dev: false - - /@types/react-transition-group@4.4.6: - resolution: {integrity: sha512-VnCdSxfcm08KjsJVQcfBmhEQAPnLB8G08hAxn39azX1qYBQ/5RVQuoHuKIcfKOdncuaUvEpFKFzEvbtIMsfVew==} - dependencies: - '@types/react': 18.2.6 - - /@types/react-transition-group@4.4.9: - resolution: {integrity: sha512-ZVNmWumUIh5NhH8aMD9CR2hdW0fNuYInlocZHaZ+dgk/1K49j1w/HoAuK1ki+pgscQrOFRTlXeoURtuzEkV3dg==} - dependencies: - '@types/react': 18.2.6 - dev: false - - /@types/react-window@1.8.8: - resolution: {integrity: sha512-8Ls660bHR1AUA2kuRvVG9D/4XpRC6wjAaPT9dil7Ckc76eP9TKWZwwmgfq8Q1LANX3QNDnoU4Zp48A3w+zK69Q==} - dependencies: - '@types/react': 18.2.6 - - /@types/react@17.0.59: - resolution: {integrity: sha512-gSON5zWYIGyoBcycCE75E9+r6dCC2dHdsrVkOEiIYNU5+Q28HcBAuqvDuxHcCbMfHBHdeT5Tva/AFn3rnMKE4g==} - dependencies: - '@types/prop-types': 15.7.5 - '@types/scheduler': 0.16.3 - csstype: 3.1.2 - dev: false - - /@types/react@18.2.6: - resolution: {integrity: sha512-wRZClXn//zxCFW+ye/D2qY65UsYP1Fpex2YXorHc8awoNamkMZSvBxwxdYVInsHOZZd2Ppq8isnSzJL5Mpf8OA==} - dependencies: - '@types/prop-types': 15.7.5 - '@types/scheduler': 0.16.3 - csstype: 3.1.2 - - /@types/reactcss@1.2.6: - resolution: {integrity: sha512-qaIzpCuXNWomGR1Xq8SCFTtF4v8V27Y6f+b9+bzHiv087MylI/nTCqqdChNeWS7tslgROmYB7yeiruWX7WnqNg==} - dependencies: - '@types/react': 18.2.6 - dev: true - - /@types/scheduler@0.16.3: - resolution: {integrity: sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==} - - /@types/semver@7.5.0: - resolution: {integrity: sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==} - dev: true - - /@types/stack-utils@2.0.1: - resolution: {integrity: sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==} - - /@types/strip-bom@3.0.0: - resolution: {integrity: sha512-xevGOReSYGM7g/kUBZzPqCrR/KYAo+F0yiPc85WFTJa0MSLtyFTVTU6cJu/aV4mid7IffDIWqo69THF2o4JiEQ==} - dev: true - - /@types/strip-json-comments@0.0.30: - resolution: {integrity: sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==} - dev: true - - /@types/tough-cookie@4.0.2: - resolution: {integrity: sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw==} - dev: true - - /@types/use-sync-external-store@0.0.3: - resolution: {integrity: sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==} - dev: false - - /@types/utf8@3.0.1: - resolution: {integrity: sha512-1EkWuw7rT3BMz2HpmcEOr/HL61mWNA6Ulr/KdbXR9AI0A55wD4Qfv8hizd8Q1DnknSIzzDvQmvvY/guvX7jjZA==} - dev: true - - /@types/uuid@9.0.1: - resolution: {integrity: sha512-rFT3ak0/2trgvp4yYZo5iKFEPsET7vKydKF+VRCxlQ9bpheehyAJH89dAkaLEq/j/RZXJIqcgsmPJKUP1Z28HA==} - dev: true - - /@types/warning@3.0.3: - resolution: {integrity: sha512-D1XC7WK8K+zZEveUPY+cf4+kgauk8N4eHr/XIHXGlGYkHLud6hK9lYfZk1ry1TNh798cZUCgb6MqGEG8DkJt6Q==} - dev: false - - /@types/yargs-parser@21.0.0: - resolution: {integrity: sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==} - - /@types/yargs@17.0.24: - resolution: {integrity: sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==} - dependencies: - '@types/yargs-parser': 21.0.0 - - /@typescript-eslint/eslint-plugin@5.59.5(@typescript-eslint/parser@5.59.5)(eslint@8.40.0)(typescript@4.9.5): - resolution: {integrity: sha512-feA9xbVRWJZor+AnLNAr7A8JRWeZqHUf4T9tlP+TN04b05pFVhO5eN7/O93Y/1OUlLMHKbnJisgDURs/qvtqdg==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - '@typescript-eslint/parser': ^5.0.0 - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@eslint-community/regexpp': 4.5.1 - '@typescript-eslint/parser': 5.59.5(eslint@8.40.0)(typescript@4.9.5) - '@typescript-eslint/scope-manager': 5.59.5 - '@typescript-eslint/type-utils': 5.59.5(eslint@8.40.0)(typescript@4.9.5) - '@typescript-eslint/utils': 5.59.5(eslint@8.40.0)(typescript@4.9.5) - debug: 4.3.4 - eslint: 8.40.0 - grapheme-splitter: 1.0.4 - ignore: 5.2.4 - natural-compare-lite: 1.4.0 - semver: 7.5.1 - tsutils: 3.21.0(typescript@4.9.5) - typescript: 4.9.5 - transitivePeerDependencies: - - supports-color - dev: true - - /@typescript-eslint/parser@5.59.5(eslint@8.40.0)(typescript@4.9.5): - resolution: {integrity: sha512-NJXQC4MRnF9N9yWqQE2/KLRSOLvrrlZb48NGVfBa+RuPMN6B7ZcK5jZOvhuygv4D64fRKnZI4L4p8+M+rfeQuw==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@typescript-eslint/scope-manager': 5.59.5 - '@typescript-eslint/types': 5.59.5 - '@typescript-eslint/typescript-estree': 5.59.5(typescript@4.9.5) - debug: 4.3.4 - eslint: 8.40.0 - typescript: 4.9.5 - transitivePeerDependencies: - - supports-color - dev: true - - /@typescript-eslint/scope-manager@5.59.5: - resolution: {integrity: sha512-jVecWwnkX6ZgutF+DovbBJirZcAxgxC0EOHYt/niMROf8p4PwxxG32Qdhj/iIQQIuOflLjNkxoXyArkcIP7C3A==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dependencies: - '@typescript-eslint/types': 5.59.5 - '@typescript-eslint/visitor-keys': 5.59.5 - dev: true - - /@typescript-eslint/type-utils@5.59.5(eslint@8.40.0)(typescript@4.9.5): - resolution: {integrity: sha512-4eyhS7oGym67/pSxA2mmNq7X164oqDYNnZCUayBwJZIRVvKpBCMBzFnFxjeoDeShjtO6RQBHBuwybuX3POnDqg==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: '*' - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@typescript-eslint/typescript-estree': 5.59.5(typescript@4.9.5) - '@typescript-eslint/utils': 5.59.5(eslint@8.40.0)(typescript@4.9.5) - debug: 4.3.4 - eslint: 8.40.0 - tsutils: 3.21.0(typescript@4.9.5) - typescript: 4.9.5 - transitivePeerDependencies: - - supports-color - dev: true - - /@typescript-eslint/types@5.59.5: - resolution: {integrity: sha512-xkfRPHbqSH4Ggx4eHRIO/eGL8XL4Ysb4woL8c87YuAo8Md7AUjyWKa9YMwTL519SyDPrfEgKdewjkxNCVeJW7w==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dev: true - - /@typescript-eslint/typescript-estree@5.59.5(typescript@4.9.5): - resolution: {integrity: sha512-+XXdLN2CZLZcD/mO7mQtJMvCkzRfmODbeSKuMY/yXbGkzvA9rJyDY5qDYNoiz2kP/dmyAxXquL2BvLQLJFPQIg==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@typescript-eslint/types': 5.59.5 - '@typescript-eslint/visitor-keys': 5.59.5 - debug: 4.3.4 - globby: 11.1.0 - is-glob: 4.0.3 - semver: 7.5.1 - tsutils: 3.21.0(typescript@4.9.5) - typescript: 4.9.5 - transitivePeerDependencies: - - supports-color - dev: true - - /@typescript-eslint/utils@5.59.5(eslint@8.40.0)(typescript@4.9.5): - resolution: {integrity: sha512-sCEHOiw+RbyTii9c3/qN74hYDPNORb8yWCoPLmB7BIflhplJ65u2PBpdRla12e3SSTJ2erRkPjz7ngLHhUegxA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.40.0) - '@types/json-schema': 7.0.11 - '@types/semver': 7.5.0 - '@typescript-eslint/scope-manager': 5.59.5 - '@typescript-eslint/types': 5.59.5 - '@typescript-eslint/typescript-estree': 5.59.5(typescript@4.9.5) - eslint: 8.40.0 - eslint-scope: 5.1.1 - semver: 7.5.1 - transitivePeerDependencies: - - supports-color - - typescript - dev: true - - /@typescript-eslint/visitor-keys@5.59.5: - resolution: {integrity: sha512-qL+Oz+dbeBRTeyJTIy0eniD3uvqU7x+y1QceBismZ41hd4aBSRh8UAw4pZP0+XzLuPZmx4raNMq/I+59W2lXKA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dependencies: - '@typescript-eslint/types': 5.59.5 - eslint-visitor-keys: 3.4.1 - dev: true - - /@vitejs/plugin-react@3.1.0(vite@4.3.5): - resolution: {integrity: sha512-AfgcRL8ZBhAlc3BFdigClmTUMISmmzHn7sB2h9U1odvc5U/MjWXsAaz18b/WoppUTDBzxOJwo2VdClfUcItu9g==} - engines: {node: ^14.18.0 || >=16.0.0} - peerDependencies: - vite: ^4.1.0-beta.0 - dependencies: - '@babel/core': 7.21.8 - '@babel/plugin-transform-react-jsx-self': 7.21.0(@babel/core@7.21.8) - '@babel/plugin-transform-react-jsx-source': 7.19.6(@babel/core@7.21.8) - magic-string: 0.27.0 - react-refresh: 0.14.0 - vite: 4.3.5(@types/node@18.16.9)(sass@1.70.0) - transitivePeerDependencies: - - supports-color - dev: true - - /abab@2.0.6: - resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==} - dev: true - - /acorn-globals@7.0.1: - resolution: {integrity: sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==} - dependencies: - acorn: 8.8.2 - acorn-walk: 8.2.0 - dev: true - - /acorn-jsx@5.3.2(acorn@8.8.2): - resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} - peerDependencies: - acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - dependencies: - acorn: 8.8.2 - dev: true - - /acorn-walk@8.2.0: - resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==} - engines: {node: '>=0.4.0'} - dev: true - - /acorn@8.8.2: - resolution: {integrity: sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==} - engines: {node: '>=0.4.0'} - hasBin: true - dev: true - - /add-px-to-style@1.0.0: - resolution: {integrity: sha512-YMyxSlXpPjD8uWekCQGuN40lV4bnZagUwqa2m/uFv1z/tNImSk9fnXVMUI5qwME/zzI3MMQRvjZ+69zyfSSyew==} - dev: false - - /agent-base@6.0.2: - resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} - engines: {node: '>= 6.0.0'} - dependencies: - debug: 4.3.4 - transitivePeerDependencies: - - supports-color - dev: true - - /ajv@6.12.6: - resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} - dependencies: - fast-deep-equal: 3.1.3 - fast-json-stable-stringify: 2.1.0 - json-schema-traverse: 0.4.1 - uri-js: 4.4.1 - dev: true - - /ansi-escapes@4.3.2: - resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} - engines: {node: '>=8'} - dependencies: - type-fest: 0.21.3 - - /ansi-regex@5.0.1: - resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} - engines: {node: '>=8'} - - /ansi-styles@3.2.1: - resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} - engines: {node: '>=4'} - dependencies: - color-convert: 1.9.3 - - /ansi-styles@4.3.0: - resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} - engines: {node: '>=8'} - dependencies: - color-convert: 2.0.1 - - /ansi-styles@5.2.0: - resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} - engines: {node: '>=10'} - - /any-promise@1.3.0: - resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} - dev: true - - /anymatch@3.1.3: - resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} - engines: {node: '>= 8'} - dependencies: - normalize-path: 3.0.0 - picomatch: 2.3.1 - - /arg@4.1.3: - resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} - dev: true - - /arg@5.0.2: - resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} - dev: true - - /argparse@1.0.10: - resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} - dependencies: - sprintf-js: 1.0.3 - - /argparse@2.0.1: - resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - dev: true - - /array-buffer-byte-length@1.0.0: - resolution: {integrity: sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==} - dependencies: - call-bind: 1.0.2 - is-array-buffer: 3.0.2 - dev: true - - /array-includes@3.1.6: - resolution: {integrity: sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.21.2 - get-intrinsic: 1.2.1 - is-string: 1.0.7 - dev: true - - /array-union@2.1.0: - resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} - engines: {node: '>=8'} - dev: true - - /array.prototype.flatmap@1.3.1: - resolution: {integrity: sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.21.2 - es-shim-unscopables: 1.0.0 - dev: true - - /array.prototype.tosorted@1.1.1: - resolution: {integrity: sha512-pZYPXPRl2PqWcsUs6LOMn+1f1532nEoPTYowBtqLwAW+W8vSVhkIGnmOX1t/UQjD6YGI0vcD2B1U7ZFGQH9jnQ==} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.21.2 - es-shim-unscopables: 1.0.0 - get-intrinsic: 1.2.1 - dev: true - - /asynckit@0.4.0: - resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - dev: true - - /autoprefixer@10.4.14(postcss@8.4.23): - resolution: {integrity: sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==} - engines: {node: ^10 || ^12 || >=14} - hasBin: true - peerDependencies: - postcss: ^8.1.0 - dependencies: - browserslist: 4.21.5 - caniuse-lite: 1.0.30001487 - fraction.js: 4.2.0 - normalize-range: 0.1.2 - picocolors: 1.0.0 - postcss: 8.4.23 - postcss-value-parser: 4.2.0 - dev: true - - /available-typed-arrays@1.0.5: - resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==} - engines: {node: '>= 0.4'} - dev: true - - /babel-jest@29.6.2(@babel/core@7.21.8): - resolution: {integrity: sha512-BYCzImLos6J3BH/+HvUCHG1dTf2MzmAB4jaVxHV+29RZLjR29XuYTmsf2sdDwkrb+FczkGo3kOhE7ga6sI0P4A==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - '@babel/core': ^7.8.0 - dependencies: - '@babel/core': 7.21.8 - '@jest/transform': 29.6.4 - '@types/babel__core': 7.20.0 - babel-plugin-istanbul: 6.1.1 - babel-preset-jest: 29.5.0(@babel/core@7.21.8) - chalk: 4.1.2 - graceful-fs: 4.2.11 - slash: 3.0.0 - transitivePeerDependencies: - - supports-color - - /babel-plugin-istanbul@6.1.1: - resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==} - engines: {node: '>=8'} - dependencies: - '@babel/helper-plugin-utils': 7.21.5 - '@istanbuljs/load-nyc-config': 1.1.0 - '@istanbuljs/schema': 0.1.3 - istanbul-lib-instrument: 5.2.1 - test-exclude: 6.0.0 - transitivePeerDependencies: - - supports-color - - /babel-plugin-jest-hoist@29.5.0: - resolution: {integrity: sha512-zSuuuAlTMT4mzLj2nPnUm6fsE6270vdOfnpbJ+RmruU75UhLFvL0N2NgI7xpeS7NaB6hGqmd5pVpGTDYvi4Q3w==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@babel/template': 7.20.7 - '@babel/types': 7.21.5 - '@types/babel__core': 7.20.0 - '@types/babel__traverse': 7.18.5 - - /babel-plugin-macros@3.1.0: - resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==} - engines: {node: '>=10', npm: '>=6'} - dependencies: - '@babel/runtime': 7.23.4 - cosmiconfig: 7.1.0 - resolve: 1.22.2 - dev: false - - /babel-preset-current-node-syntax@1.0.1(@babel/core@7.21.8): - resolution: {integrity: sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==} - peerDependencies: - '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.21.8 - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.21.8) - '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.21.8) - '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.21.8) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.21.8) - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.21.8) - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.21.8) - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.21.8) - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.21.8) - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.21.8) - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.21.8) - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.21.8) - '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.21.8) - - /babel-preset-jest@29.5.0(@babel/core@7.21.8): - resolution: {integrity: sha512-JOMloxOqdiBSxMAzjRaH023/vvcaSaec49zvg+2LmNsktC7ei39LTJGw02J+9uUtTZUq6xbLyJ4dxe9sSmIuAg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.21.8 - babel-plugin-jest-hoist: 29.5.0 - babel-preset-current-node-syntax: 1.0.1(@babel/core@7.21.8) - - /balanced-match@1.0.2: - resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - - /binary-extensions@2.2.0: - resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} - engines: {node: '>=8'} - - /boolbase@1.0.0: - resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} - dev: true - - /brace-expansion@1.1.11: - resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} - dependencies: - balanced-match: 1.0.2 - concat-map: 0.0.1 - - /braces@3.0.2: - resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} - engines: {node: '>=8'} - dependencies: - fill-range: 7.0.1 - - /browserslist@4.21.5: - resolution: {integrity: sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==} - engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} - hasBin: true - dependencies: - caniuse-lite: 1.0.30001487 - electron-to-chromium: 1.4.394 - node-releases: 2.0.10 - update-browserslist-db: 1.0.11(browserslist@4.21.5) - - /bs-logger@0.2.6: - resolution: {integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==} - engines: {node: '>= 6'} - dependencies: - fast-json-stable-stringify: 2.1.0 - dev: true - - /bser@2.1.1: - resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} - dependencies: - node-int64: 0.4.0 - - /buffer-from@1.1.2: - resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - - /call-bind@1.0.2: - resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} - dependencies: - function-bind: 1.1.1 - get-intrinsic: 1.2.1 - - /callsites@3.1.0: - resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} - engines: {node: '>=6'} - - /camel-case@4.1.2: - resolution: {integrity: sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==} - dependencies: - pascal-case: 3.1.2 - tslib: 2.5.0 - dev: true - - /camelcase-css@2.0.1: - resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} - engines: {node: '>= 6'} - dev: true - - /camelcase@5.3.1: - resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} - engines: {node: '>=6'} - - /camelcase@6.3.0: - resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} - engines: {node: '>=10'} - - /caniuse-lite@1.0.30001487: - resolution: {integrity: sha512-83564Z3yWGqXsh2vaH/mhXfEM0wX+NlBCm1jYHOb97TrTWJEmPTccZgeLTPBUUb0PNVo+oomb7wkimZBIERClA==} - - /capital-case@1.0.4: - resolution: {integrity: sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==} - dependencies: - no-case: 3.0.4 - tslib: 2.5.0 - upper-case-first: 2.0.2 - dev: true - - /chalk@2.4.2: - resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} - engines: {node: '>=4'} - dependencies: - ansi-styles: 3.2.1 - escape-string-regexp: 1.0.5 - supports-color: 5.5.0 - - /chalk@4.1.2: - resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} - engines: {node: '>=10'} - dependencies: - ansi-styles: 4.3.0 - supports-color: 7.2.0 - - /change-case@4.1.2: - resolution: {integrity: sha512-bSxY2ws9OtviILG1EiY5K7NNxkqg/JnRnFxLtKQ96JaviiIxi7djMrSd0ECT9AC+lttClmYwKw53BWpOMblo7A==} - dependencies: - camel-case: 4.1.2 - capital-case: 1.0.4 - constant-case: 3.0.4 - dot-case: 3.0.4 - header-case: 2.0.4 - no-case: 3.0.4 - param-case: 3.0.4 - pascal-case: 3.1.2 - path-case: 3.0.4 - sentence-case: 3.0.4 - snake-case: 3.0.4 - tslib: 2.5.0 - dev: true - - /char-regex@1.0.2: - resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} - engines: {node: '>=10'} - - /chokidar@3.5.3: - resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} - engines: {node: '>= 8.10.0'} - dependencies: - anymatch: 3.1.3 - braces: 3.0.2 - glob-parent: 5.1.2 - is-binary-path: 2.1.0 - is-glob: 4.0.3 - normalize-path: 3.0.0 - readdirp: 3.6.0 - optionalDependencies: - fsevents: 2.3.2 - - /ci-info@3.8.0: - resolution: {integrity: sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==} - engines: {node: '>=8'} - - /cjs-module-lexer@1.2.2: - resolution: {integrity: sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==} - - /classnames@2.3.2: - resolution: {integrity: sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==} - dev: false - - /cliui@8.0.1: - resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} - engines: {node: '>=12'} - dependencies: - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi: 7.0.0 - - /clone@2.1.2: - resolution: {integrity: sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==} - engines: {node: '>=0.8'} - dev: false - - /clsx@1.2.1: - resolution: {integrity: sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==} - engines: {node: '>=6'} - dev: false - - /clsx@2.0.0: - resolution: {integrity: sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==} - engines: {node: '>=6'} - dev: false - - /co@4.6.0: - resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} - engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} - - /collect-v8-coverage@1.0.1: - resolution: {integrity: sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==} - - /color-convert@1.9.3: - resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} - dependencies: - color-name: 1.1.3 - - /color-convert@2.0.1: - resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} - engines: {node: '>=7.0.0'} - dependencies: - color-name: 1.1.4 - - /color-name@1.1.3: - resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} - - /color-name@1.1.4: - resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - - /combined-stream@1.0.8: - resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} - engines: {node: '>= 0.8'} - dependencies: - delayed-stream: 1.0.0 - dev: true - - /commander@4.1.1: - resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} - engines: {node: '>= 6'} - dev: true - - /commander@7.2.0: - resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} - engines: {node: '>= 10'} - dev: true - - /commander@8.3.0: - resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} - engines: {node: '>= 12'} - - /compute-scroll-into-view@3.1.0: - resolution: {integrity: sha512-rj8l8pD4bJ1nx+dAkMhV1xB5RuZEyVysfxJqB1pRchh1KVvwOv9b7CGB8ZfjTImVv2oF+sYMUkMZq6Na5Ftmbg==} - dev: false - - /concat-map@0.0.1: - resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - - /constant-case@3.0.4: - resolution: {integrity: sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ==} - dependencies: - no-case: 3.0.4 - tslib: 2.5.0 - upper-case: 2.0.2 - dev: true - - /convert-source-map@1.9.0: - resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} - - /convert-source-map@2.0.0: - resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} - - /cosmiconfig@7.1.0: - resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} - engines: {node: '>=10'} - dependencies: - '@types/parse-json': 4.0.0 - import-fresh: 3.3.0 - parse-json: 5.2.0 - path-type: 4.0.0 - yaml: 1.10.2 - dev: false - - /cosmiconfig@8.2.0: - resolution: {integrity: sha512-3rTMnFJA1tCOPwRxtgF4wd7Ab2qvDbL8jX+3smjIbS4HlZBagTlpERbdN7iAbWlrfxE3M8c27kTwTawQ7st+OQ==} - engines: {node: '>=14'} - dependencies: - import-fresh: 3.3.0 - js-yaml: 4.1.0 - parse-json: 5.2.0 - path-type: 4.0.0 - dev: true - - /create-require@1.1.1: - resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} - dev: true - - /cross-spawn@7.0.3: - resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} - engines: {node: '>= 8'} - dependencies: - path-key: 3.1.1 - shebang-command: 2.0.0 - which: 2.0.2 - - /css-box-model@1.2.1: - resolution: {integrity: sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw==} - dependencies: - tiny-invariant: 1.3.1 - dev: false - - /css-select@5.1.0: - resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==} - dependencies: - boolbase: 1.0.0 - css-what: 6.1.0 - domhandler: 5.0.3 - domutils: 3.1.0 - nth-check: 2.1.1 - dev: true - - /css-tree@2.2.1: - resolution: {integrity: sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==} - engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} - dependencies: - mdn-data: 2.0.28 - source-map-js: 1.0.2 - dev: true - - /css-tree@2.3.1: - resolution: {integrity: sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==} - engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} - dependencies: - mdn-data: 2.0.30 - source-map-js: 1.0.2 - dev: true - - /css-what@6.1.0: - resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} - engines: {node: '>= 6'} - dev: true - - /cssesc@3.0.0: - resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} - engines: {node: '>=4'} - hasBin: true - dev: true - - /csso@5.0.5: - resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==} - engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} - dependencies: - css-tree: 2.2.1 - dev: true - - /cssom@0.3.8: - resolution: {integrity: sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==} - dev: true - - /cssom@0.5.0: - resolution: {integrity: sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==} - dev: true - - /cssstyle@2.3.0: - resolution: {integrity: sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==} - engines: {node: '>=8'} - dependencies: - cssom: 0.3.8 - dev: true - - /csstype@3.1.2: - resolution: {integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==} - - /data-urls@3.0.2: - resolution: {integrity: sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==} - engines: {node: '>=12'} - dependencies: - abab: 2.0.6 - whatwg-mimetype: 3.0.0 - whatwg-url: 11.0.0 - dev: true - - /date-arithmetic@4.1.0: - resolution: {integrity: sha512-QWxYLR5P/6GStZcdem+V1xoto6DMadYWpMXU82ES3/RfR3Wdwr3D0+be7mgOJ+Ov0G9D5Dmb9T17sNLQYj9XOg==} - dev: false - - /date-fns@2.30.0: - resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==} - engines: {node: '>=0.11'} - dependencies: - '@babel/runtime': 7.23.4 - - /dayjs@1.11.9: - resolution: {integrity: sha512-QvzAURSbQ0pKdIye2txOzNaHmxtUBXerpY0FJsFXUMKbIZeFm5ht1LS/jFsrncjnmtv8HsG0W2g6c0zUjZWmpA==} - dev: false - - /debug@4.3.4: - resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - dependencies: - ms: 2.1.2 - - /decimal.js@10.4.3: - resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==} - dev: true - - /dedent@0.7.0: - resolution: {integrity: sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==} - - /deep-equal@1.1.1: - resolution: {integrity: sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==} - dependencies: - is-arguments: 1.1.1 - is-date-object: 1.0.5 - is-regex: 1.1.4 - object-is: 1.1.5 - object-keys: 1.1.1 - regexp.prototype.flags: 1.5.0 - dev: false - - /deep-is@0.1.4: - resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} - dev: true - - /deepmerge@4.3.1: - resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} - engines: {node: '>=0.10.0'} - - /define-properties@1.2.0: - resolution: {integrity: sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==} - engines: {node: '>= 0.4'} - dependencies: - has-property-descriptors: 1.0.0 - object-keys: 1.1.1 - - /delayed-stream@1.0.0: - resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} - engines: {node: '>=0.4.0'} - dev: true - - /dequal@2.0.3: - resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} - engines: {node: '>=6'} - dev: false - - /derive-valtio@0.1.0(valtio@1.12.1): - resolution: {integrity: sha512-OCg2UsLbXK7GmmpzMXhYkdO64vhJ1ROUUGaTFyHjVwEdMEcTTRj7W1TxLbSBxdY8QLBPCcp66MTyaSy0RpO17A==} - peerDependencies: - valtio: '*' - dependencies: - valtio: 1.12.1(@types/react@18.2.6)(react@18.2.0) - dev: false - - /detect-newline@3.1.0: - resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} - engines: {node: '>=8'} - - /didyoumean@1.2.2: - resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} - dev: true - - /diff-sequences@29.4.3: - resolution: {integrity: sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - /diff@4.0.2: - resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} - engines: {node: '>=0.3.1'} - dev: true - - /dir-glob@3.0.1: - resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} - engines: {node: '>=8'} - dependencies: - path-type: 4.0.0 - dev: true - - /direction@1.0.4: - resolution: {integrity: sha512-GYqKi1aH7PJXxdhTeZBFrg8vUBeKXi+cNprXsC1kpJcbcVnV9wBsrOu1cQEdG0WeQwlfHiy3XvnKfIrJ2R0NzQ==} - hasBin: true - dev: false - - /dlv@1.1.3: - resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} - dev: true - - /doctrine@2.1.0: - resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} - engines: {node: '>=0.10.0'} - dependencies: - esutils: 2.0.3 - dev: true - - /doctrine@3.0.0: - resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} - engines: {node: '>=6.0.0'} - dependencies: - esutils: 2.0.3 - dev: true - - /dom-css@2.1.0: - resolution: {integrity: sha512-w9kU7FAbaSh3QKijL6n59ofAhkkmMJ31GclJIz/vyQdjogfyxcB6Zf8CZyibOERI5o0Hxz30VmJS7+7r5fEj2Q==} - dependencies: - add-px-to-style: 1.0.0 - prefix-style: 2.0.1 - to-camel-case: 1.0.0 - dev: false - - /dom-helpers@5.2.1: - resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==} - dependencies: - '@babel/runtime': 7.23.4 - csstype: 3.1.2 - dev: false - - /dom-serializer@2.0.0: - resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} - dependencies: - domelementtype: 2.3.0 - domhandler: 5.0.3 - entities: 4.5.0 - dev: true - - /domelementtype@2.3.0: - resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} - dev: true - - /domexception@4.0.0: - resolution: {integrity: sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==} - engines: {node: '>=12'} - dependencies: - webidl-conversions: 7.0.0 - dev: true - - /domhandler@5.0.3: - resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} - engines: {node: '>= 4'} - dependencies: - domelementtype: 2.3.0 - dev: true - - /domutils@3.1.0: - resolution: {integrity: sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==} - dependencies: - dom-serializer: 2.0.0 - domelementtype: 2.3.0 - domhandler: 5.0.3 - dev: true - - /dot-case@3.0.4: - resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} - dependencies: - no-case: 3.0.4 - tslib: 2.5.0 - dev: true - - /dynamic-dedupe@0.3.0: - resolution: {integrity: sha512-ssuANeD+z97meYOqd50e04Ze5qp4bPqo8cCkI4TRjZkzAUgIDTrXV1R8QCdINpiI+hw14+rYazvTRdQrz0/rFQ==} - dependencies: - xtend: 4.0.2 - dev: true - - /electron-to-chromium@1.4.394: - resolution: {integrity: sha512-0IbC2cfr8w5LxTz+nmn2cJTGafsK9iauV2r5A5scfzyovqLrxuLoxOHE5OBobP3oVIggJT+0JfKnw9sm87c8Hw==} - - /emittery@0.13.1: - resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} - engines: {node: '>=12'} - - /emoji-mart@5.5.2: - resolution: {integrity: sha512-Sqc/nso4cjxhOwWJsp9xkVm8OF5c+mJLZJFoFfzRuKO+yWiN7K8c96xmtughYb0d/fZ8UC6cLIQ/p4BR6Pv3/A==} - dev: false - - /emoji-regex@10.2.1: - resolution: {integrity: sha512-97g6QgOk8zlDRdgq1WxwgTMgEWGVAQvB5Fdpgc1MkNy56la5SKP9GsMXKDOdqwn90/41a8yPwIGk1Y6WVbeMQA==} - dev: false - - /emoji-regex@8.0.0: - resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - - /entities@4.5.0: - resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} - engines: {node: '>=0.12'} - dev: true - - /error-ex@1.3.2: - resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} - dependencies: - is-arrayish: 0.2.1 - - /es-abstract@1.21.2: - resolution: {integrity: sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg==} - engines: {node: '>= 0.4'} - dependencies: - array-buffer-byte-length: 1.0.0 - available-typed-arrays: 1.0.5 - call-bind: 1.0.2 - es-set-tostringtag: 2.0.1 - es-to-primitive: 1.2.1 - function.prototype.name: 1.1.5 - get-intrinsic: 1.2.1 - get-symbol-description: 1.0.0 - globalthis: 1.0.3 - gopd: 1.0.1 - has: 1.0.3 - has-property-descriptors: 1.0.0 - has-proto: 1.0.1 - has-symbols: 1.0.3 - internal-slot: 1.0.5 - is-array-buffer: 3.0.2 - is-callable: 1.2.7 - is-negative-zero: 2.0.2 - is-regex: 1.1.4 - is-shared-array-buffer: 1.0.2 - is-string: 1.0.7 - is-typed-array: 1.1.10 - is-weakref: 1.0.2 - object-inspect: 1.12.3 - object-keys: 1.1.1 - object.assign: 4.1.4 - regexp.prototype.flags: 1.5.0 - safe-regex-test: 1.0.0 - string.prototype.trim: 1.2.7 - string.prototype.trimend: 1.0.6 - string.prototype.trimstart: 1.0.6 - typed-array-length: 1.0.4 - unbox-primitive: 1.0.2 - which-typed-array: 1.1.9 - dev: true - - /es-set-tostringtag@2.0.1: - resolution: {integrity: sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==} - engines: {node: '>= 0.4'} - dependencies: - get-intrinsic: 1.2.1 - has: 1.0.3 - has-tostringtag: 1.0.0 - dev: true - - /es-shim-unscopables@1.0.0: - resolution: {integrity: sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==} - dependencies: - has: 1.0.3 - dev: true - - /es-to-primitive@1.2.1: - resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} - engines: {node: '>= 0.4'} - dependencies: - is-callable: 1.2.7 - is-date-object: 1.0.5 - is-symbol: 1.0.4 - dev: true - - /esbuild@0.17.19: - resolution: {integrity: sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==} - engines: {node: '>=12'} - hasBin: true - requiresBuild: true - optionalDependencies: - '@esbuild/android-arm': 0.17.19 - '@esbuild/android-arm64': 0.17.19 - '@esbuild/android-x64': 0.17.19 - '@esbuild/darwin-arm64': 0.17.19 - '@esbuild/darwin-x64': 0.17.19 - '@esbuild/freebsd-arm64': 0.17.19 - '@esbuild/freebsd-x64': 0.17.19 - '@esbuild/linux-arm': 0.17.19 - '@esbuild/linux-arm64': 0.17.19 - '@esbuild/linux-ia32': 0.17.19 - '@esbuild/linux-loong64': 0.17.19 - '@esbuild/linux-mips64el': 0.17.19 - '@esbuild/linux-ppc64': 0.17.19 - '@esbuild/linux-riscv64': 0.17.19 - '@esbuild/linux-s390x': 0.17.19 - '@esbuild/linux-x64': 0.17.19 - '@esbuild/netbsd-x64': 0.17.19 - '@esbuild/openbsd-x64': 0.17.19 - '@esbuild/sunos-x64': 0.17.19 - '@esbuild/win32-arm64': 0.17.19 - '@esbuild/win32-ia32': 0.17.19 - '@esbuild/win32-x64': 0.17.19 - dev: true - - /escalade@3.1.1: - resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} - engines: {node: '>=6'} - - /escape-string-regexp@1.0.5: - resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} - engines: {node: '>=0.8.0'} - - /escape-string-regexp@2.0.0: - resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} - engines: {node: '>=8'} - - /escape-string-regexp@4.0.0: - resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} - engines: {node: '>=10'} - - /escodegen@2.1.0: - resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==} - engines: {node: '>=6.0'} - hasBin: true - dependencies: - esprima: 4.0.1 - estraverse: 5.3.0 - esutils: 2.0.3 - optionalDependencies: - source-map: 0.6.1 - dev: true - - /eslint-plugin-react-hooks@4.6.0(eslint@8.40.0): - resolution: {integrity: sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==} - engines: {node: '>=10'} - peerDependencies: - eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 - dependencies: - eslint: 8.40.0 - dev: true - - /eslint-plugin-react@7.32.2(eslint@8.40.0): - resolution: {integrity: sha512-t2fBMa+XzonrrNkyVirzKlvn5RXzzPwRHtMvLAtVZrt8oxgnTQaYbU6SXTOO1mwQgp1y5+toMSKInnzGr0Knqg==} - engines: {node: '>=4'} - peerDependencies: - eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 - dependencies: - array-includes: 3.1.6 - array.prototype.flatmap: 1.3.1 - array.prototype.tosorted: 1.1.1 - doctrine: 2.1.0 - eslint: 8.40.0 - estraverse: 5.3.0 - jsx-ast-utils: 3.3.3 - minimatch: 3.1.2 - object.entries: 1.1.6 - object.fromentries: 2.0.6 - object.hasown: 1.1.2 - object.values: 1.1.6 - prop-types: 15.8.1 - resolve: 2.0.0-next.4 - semver: 6.3.0 - string.prototype.matchall: 4.0.8 - dev: true - - /eslint-scope@5.1.1: - resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} - engines: {node: '>=8.0.0'} - dependencies: - esrecurse: 4.3.0 - estraverse: 4.3.0 - dev: true - - /eslint-scope@7.2.0: - resolution: {integrity: sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dependencies: - esrecurse: 4.3.0 - estraverse: 5.3.0 - dev: true - - /eslint-visitor-keys@3.4.1: - resolution: {integrity: sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dev: true - - /eslint@8.40.0: - resolution: {integrity: sha512-bvR+TsP9EHL3TqNtj9sCNJVAFK3fBN8Q7g5waghxyRsPLIMwL73XSKnZFK0hk/O2ANC+iAoq6PWMQ+IfBAJIiQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - hasBin: true - dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.40.0) - '@eslint-community/regexpp': 4.5.1 - '@eslint/eslintrc': 2.0.3 - '@eslint/js': 8.40.0 - '@humanwhocodes/config-array': 0.11.8 - '@humanwhocodes/module-importer': 1.0.1 - '@nodelib/fs.walk': 1.2.8 - ajv: 6.12.6 - chalk: 4.1.2 - cross-spawn: 7.0.3 - debug: 4.3.4 - doctrine: 3.0.0 - escape-string-regexp: 4.0.0 - eslint-scope: 7.2.0 - eslint-visitor-keys: 3.4.1 - espree: 9.5.2 - esquery: 1.5.0 - esutils: 2.0.3 - fast-deep-equal: 3.1.3 - file-entry-cache: 6.0.1 - find-up: 5.0.0 - glob-parent: 6.0.2 - globals: 13.20.0 - grapheme-splitter: 1.0.4 - ignore: 5.2.4 - import-fresh: 3.3.0 - imurmurhash: 0.1.4 - is-glob: 4.0.3 - is-path-inside: 3.0.3 - js-sdsl: 4.4.0 - js-yaml: 4.1.0 - json-stable-stringify-without-jsonify: 1.0.1 - levn: 0.4.1 - lodash.merge: 4.6.2 - minimatch: 3.1.2 - natural-compare: 1.4.0 - optionator: 0.9.1 - strip-ansi: 6.0.1 - strip-json-comments: 3.1.1 - text-table: 0.2.0 - transitivePeerDependencies: - - supports-color - dev: true - - /espree@9.5.2: - resolution: {integrity: sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dependencies: - acorn: 8.8.2 - acorn-jsx: 5.3.2(acorn@8.8.2) - eslint-visitor-keys: 3.4.1 - dev: true - - /esprima@4.0.1: - resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} - engines: {node: '>=4'} - hasBin: true - - /esquery@1.5.0: - resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} - engines: {node: '>=0.10'} - dependencies: - estraverse: 5.3.0 - dev: true - - /esrecurse@4.3.0: - resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} - engines: {node: '>=4.0'} - dependencies: - estraverse: 5.3.0 - dev: true - - /estraverse@4.3.0: - resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} - engines: {node: '>=4.0'} - dev: true - - /estraverse@5.3.0: - resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} - engines: {node: '>=4.0'} - dev: true - - /estree-walker@2.0.2: - resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} - dev: true - - /esutils@2.0.3: - resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} - engines: {node: '>=0.10.0'} - dev: true - - /eventemitter3@2.0.3: - resolution: {integrity: sha512-jLN68Dx5kyFHaePoXWPsCGW5qdyZQtLYHkxkg02/Mz6g0kYpDx4FyP6XfArhQdlOC4b8Mv+EMxPo/8La7Tzghg==} - dev: false - - /events@3.3.0: - resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} - engines: {node: '>=0.8.x'} - dev: false - - /execa@5.1.1: - resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} - engines: {node: '>=10'} - dependencies: - cross-spawn: 7.0.3 - get-stream: 6.0.1 - human-signals: 2.1.0 - is-stream: 2.0.1 - merge-stream: 2.0.0 - npm-run-path: 4.0.1 - onetime: 5.1.2 - signal-exit: 3.0.7 - strip-final-newline: 2.0.0 - - /exit@0.1.2: - resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} - engines: {node: '>= 0.8.0'} - - /expect@29.5.0: - resolution: {integrity: sha512-yM7xqUrCO2JdpFo4XpM82t+PJBFybdqoQuJLDGeDX2ij8NZzqRHyu3Hp188/JX7SWqud+7t4MUdvcgGBICMHZg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/expect-utils': 29.5.0 - jest-get-type: 29.4.3 - jest-matcher-utils: 29.5.0 - jest-message-util: 29.5.0 - jest-util: 29.5.0 - - /extend@3.0.2: - resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} - dev: false - - /fast-deep-equal@3.1.3: - resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - dev: true - - /fast-diff@1.1.2: - resolution: {integrity: sha512-KaJUt+M9t1qaIteSvjc6P3RbMdXsNhK61GRftR6SNxqmhthcd9MGIi4T+o0jD8LUSpSnSKXE20nLtJ3fOHxQig==} - dev: false - - /fast-diff@1.2.0: - resolution: {integrity: sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==} - dev: true - - /fast-diff@1.3.0: - resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} - dev: false - - /fast-glob@3.2.12: - resolution: {integrity: sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==} - engines: {node: '>=8.6.0'} - dependencies: - '@nodelib/fs.stat': 2.0.5 - '@nodelib/fs.walk': 1.2.8 - glob-parent: 5.1.2 - merge2: 1.4.1 - micromatch: 4.0.5 - dev: true - - /fast-json-stable-stringify@2.1.0: - resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} - - /fast-levenshtein@2.0.6: - resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} - dev: true - - /fastq@1.15.0: - resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} - dependencies: - reusify: 1.0.4 - dev: true - - /fb-watchman@2.0.2: - resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} - dependencies: - bser: 2.1.1 - - /file-entry-cache@6.0.1: - resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} - engines: {node: ^10.12.0 || >=12.0.0} - dependencies: - flat-cache: 3.0.4 - dev: true - - /fill-range@7.0.1: - resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} - engines: {node: '>=8'} - dependencies: - to-regex-range: 5.0.1 - - /find-root@1.1.0: - resolution: {integrity: sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==} - dev: false - - /find-up@4.1.0: - resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} - engines: {node: '>=8'} - dependencies: - locate-path: 5.0.0 - path-exists: 4.0.0 - - /find-up@5.0.0: - resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} - engines: {node: '>=10'} - dependencies: - locate-path: 6.0.0 - path-exists: 4.0.0 - dev: true - - /flat-cache@3.0.4: - resolution: {integrity: sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==} - engines: {node: ^10.12.0 || >=12.0.0} - dependencies: - flatted: 3.2.7 - rimraf: 3.0.2 - dev: true - - /flatted@3.2.7: - resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==} - dev: true - - /for-each@0.3.3: - resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} - dependencies: - is-callable: 1.2.7 - dev: true - - /form-data@4.0.0: - resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} - engines: {node: '>= 6'} - dependencies: - asynckit: 0.4.0 - combined-stream: 1.0.8 - mime-types: 2.1.35 - dev: true - - /fraction.js@4.2.0: - resolution: {integrity: sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==} - dev: true - - /fs-extra@10.1.0: - resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} - engines: {node: '>=12'} - dependencies: - graceful-fs: 4.2.11 - jsonfile: 6.1.0 - universalify: 2.0.0 - dev: true - - /fs.realpath@1.0.0: - resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} - - /fsevents@2.3.2: - resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} - os: [darwin] - requiresBuild: true - optional: true - - /function-bind@1.1.1: - resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} - - /function.prototype.name@1.1.5: - resolution: {integrity: sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.21.2 - functions-have-names: 1.2.3 - dev: true - - /functions-have-names@1.2.3: - resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} - - /gensync@1.0.0-beta.2: - resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} - engines: {node: '>=6.9.0'} - - /get-caller-file@2.0.5: - resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} - engines: {node: 6.* || 8.* || >= 10.*} - - /get-intrinsic@1.2.1: - resolution: {integrity: sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==} - dependencies: - function-bind: 1.1.1 - has: 1.0.3 - has-proto: 1.0.1 - has-symbols: 1.0.3 - - /get-package-type@0.1.0: - resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} - engines: {node: '>=8.0.0'} - - /get-stream@6.0.1: - resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} - engines: {node: '>=10'} - - /get-symbol-description@1.0.0: - resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - get-intrinsic: 1.2.1 - dev: true - - /glob-parent@5.1.2: - resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} - engines: {node: '>= 6'} - dependencies: - is-glob: 4.0.3 - - /glob-parent@6.0.2: - resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} - engines: {node: '>=10.13.0'} - dependencies: - is-glob: 4.0.3 - dev: true - - /glob@7.1.6: - resolution: {integrity: sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==} - dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 3.1.2 - once: 1.4.0 - path-is-absolute: 1.0.1 - dev: true - - /glob@7.2.3: - resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 3.1.2 - once: 1.4.0 - path-is-absolute: 1.0.1 - - /globalize@0.1.1: - resolution: {integrity: sha512-5e01v8eLGfuQSOvx2MsDMOWS0GFtCx1wPzQSmcHw4hkxFzrQDBO3Xwg/m8Hr/7qXMrHeOIE29qWVzyv06u1TZA==} - dev: false - - /globals@11.12.0: - resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} - engines: {node: '>=4'} - - /globals@13.20.0: - resolution: {integrity: sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==} - engines: {node: '>=8'} - dependencies: - type-fest: 0.20.2 - dev: true - - /globalthis@1.0.3: - resolution: {integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==} - engines: {node: '>= 0.4'} - dependencies: - define-properties: 1.2.0 - dev: true - - /globby@11.1.0: - resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} - engines: {node: '>=10'} - dependencies: - array-union: 2.1.0 - dir-glob: 3.0.1 - fast-glob: 3.2.12 - ignore: 5.2.4 - merge2: 1.4.1 - slash: 3.0.0 - dev: true - - /goober@2.1.13(csstype@3.1.2): - resolution: {integrity: sha512-jFj3BQeleOoy7t93E9rZ2de+ScC4lQICLwiAQmKMg9F6roKGaLSHoCDYKkWlSafg138jejvq/mTdvmnwDQgqoQ==} - peerDependencies: - csstype: ^3.0.10 - dependencies: - csstype: 3.1.2 - dev: false - - /google-protobuf@3.21.2: - resolution: {integrity: sha512-3MSOYFO5U9mPGikIYCzK0SaThypfGgS6bHqrUGXG3DPHCrb+txNqeEcns1W0lkGfk0rCyNXm7xB9rMxnCiZOoA==} - dev: false - - /gopd@1.0.1: - resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} - dependencies: - get-intrinsic: 1.2.1 - dev: true - - /graceful-fs@4.2.11: - resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - - /grapheme-splitter@1.0.4: - resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==} - dev: true - - /has-bigints@1.0.2: - resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} - dev: true - - /has-flag@3.0.0: - resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} - engines: {node: '>=4'} - - /has-flag@4.0.0: - resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} - engines: {node: '>=8'} - - /has-property-descriptors@1.0.0: - resolution: {integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==} - dependencies: - get-intrinsic: 1.2.1 - - /has-proto@1.0.1: - resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==} - engines: {node: '>= 0.4'} - - /has-symbols@1.0.3: - resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} - engines: {node: '>= 0.4'} - - /has-tostringtag@1.0.0: - resolution: {integrity: sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==} - engines: {node: '>= 0.4'} - dependencies: - has-symbols: 1.0.3 - - /has@1.0.3: - resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} - engines: {node: '>= 0.4.0'} - dependencies: - function-bind: 1.1.1 - - /header-case@2.0.4: - resolution: {integrity: sha512-H/vuk5TEEVZwrR0lp2zed9OCo1uAILMlx0JEMgC26rzyJJ3N1v6XkwHHXJQdR2doSjcGPM6OKPYoJgf0plJ11Q==} - dependencies: - capital-case: 1.0.4 - tslib: 2.5.0 - dev: true - - /hoist-non-react-statics@3.3.2: - resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} - dependencies: - react-is: 16.13.1 - dev: false - - /html-encoding-sniffer@3.0.0: - resolution: {integrity: sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==} - engines: {node: '>=12'} - dependencies: - whatwg-encoding: 2.0.0 - dev: true - - /html-escaper@2.0.2: - resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} - - /html-parse-stringify@3.0.1: - resolution: {integrity: sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==} - dependencies: - void-elements: 3.1.0 - dev: false - - /http-proxy-agent@5.0.0: - resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==} - engines: {node: '>= 6'} - dependencies: - '@tootallnate/once': 2.0.0 - agent-base: 6.0.2 - debug: 4.3.4 - transitivePeerDependencies: - - supports-color - dev: true - - /https-proxy-agent@5.0.1: - resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} - engines: {node: '>= 6'} - dependencies: - agent-base: 6.0.2 - debug: 4.3.4 - transitivePeerDependencies: - - supports-color - dev: true - - /human-signals@2.1.0: - resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} - engines: {node: '>=10.17.0'} - - /i18next-browser-languagedetector@7.0.1: - resolution: {integrity: sha512-Pa5kFwaczXJAeHE56CHG2aWzFBMJNUNghf0Pm4SwSrEMps/PTKqW90EYWlIvhuYStf3Sn1K0vw+gH3+TLdkH1g==} - dependencies: - '@babel/runtime': 7.21.5 - dev: false - - /i18next-resources-to-backend@1.1.4: - resolution: {integrity: sha512-hMyr9AOmIea17AOaVe1srNxK/l3mbk81P7Uf3fdcjlw3ehZy3UNTd0OP3EEi6yu4J02kf9jzhCcjokz6AFlEOg==} - dependencies: - '@babel/runtime': 7.21.5 - dev: false - - /i18next@22.4.15: - resolution: {integrity: sha512-yYudtbFrrmWKLEhl6jvKUYyYunj4bTBCe2qIUYAxbXoPusY7YmdwPvOE6fx6UIfWvmlbCWDItr7wIs8KEBZ5Zg==} - dependencies: - '@babel/runtime': 7.21.5 - dev: false - - /iconv-lite@0.6.3: - resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} - engines: {node: '>=0.10.0'} - dependencies: - safer-buffer: 2.1.2 - dev: true - - /ignore@5.2.4: - resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==} - engines: {node: '>= 4'} - dev: true - - /immer@10.0.3: - resolution: {integrity: sha512-pwupu3eWfouuaowscykeckFmVTpqbzW+rXFCX8rQLkZzM9ftBmU/++Ra+o+L27mz03zJTlyV4UUr+fdKNffo4A==} - dev: false - - /immutable@4.3.4: - resolution: {integrity: sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA==} - - /import-fresh@3.3.0: - resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} - engines: {node: '>=6'} - dependencies: - parent-module: 1.0.1 - resolve-from: 4.0.0 - - /import-local@3.1.0: - resolution: {integrity: sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==} - engines: {node: '>=8'} - hasBin: true - dependencies: - pkg-dir: 4.2.0 - resolve-cwd: 3.0.0 - - /imurmurhash@0.1.4: - resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} - engines: {node: '>=0.8.19'} - - /inflight@1.0.6: - resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} - dependencies: - once: 1.4.0 - wrappy: 1.0.2 - - /inherits@2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - - /internal-slot@1.0.5: - resolution: {integrity: sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==} - engines: {node: '>= 0.4'} - dependencies: - get-intrinsic: 1.2.1 - has: 1.0.3 - side-channel: 1.0.4 - dev: true - - /invariant@2.2.4: - resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==} - dependencies: - loose-envify: 1.4.0 - dev: false - - /is-arguments@1.1.1: - resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - has-tostringtag: 1.0.0 - dev: false - - /is-array-buffer@3.0.2: - resolution: {integrity: sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==} - dependencies: - call-bind: 1.0.2 - get-intrinsic: 1.2.1 - is-typed-array: 1.1.10 - dev: true - - /is-arrayish@0.2.1: - resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} - - /is-bigint@1.0.4: - resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} - dependencies: - has-bigints: 1.0.2 - dev: true - - /is-binary-path@2.1.0: - resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} - engines: {node: '>=8'} - dependencies: - binary-extensions: 2.2.0 - - /is-boolean-object@1.1.2: - resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - has-tostringtag: 1.0.0 - dev: true - - /is-callable@1.2.7: - resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} - engines: {node: '>= 0.4'} - dev: true - - /is-core-module@2.12.0: - resolution: {integrity: sha512-RECHCBCd/viahWmwj6enj19sKbHfJrddi/6cBDsNTKbNq0f7VeaUkBo60BqzvPqo/W54ChS62Z5qyun7cfOMqQ==} - dependencies: - has: 1.0.3 - - /is-date-object@1.0.5: - resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} - engines: {node: '>= 0.4'} - dependencies: - has-tostringtag: 1.0.0 - - /is-extglob@2.1.1: - resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} - engines: {node: '>=0.10.0'} - - /is-fullwidth-code-point@3.0.0: - resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} - engines: {node: '>=8'} - - /is-generator-fn@2.1.0: - resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==} - engines: {node: '>=6'} - - /is-glob@4.0.3: - resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} - engines: {node: '>=0.10.0'} - dependencies: - is-extglob: 2.1.1 - - /is-hotkey@0.2.0: - resolution: {integrity: sha512-UknnZK4RakDmTgz4PI1wIph5yxSs/mvChWs9ifnlXsKuXgWmOkY/hAE0H/k2MIqH0RlRye0i1oC07MCRSD28Mw==} - dev: false - - /is-negative-zero@2.0.2: - resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==} - engines: {node: '>= 0.4'} - dev: true - - /is-number-object@1.0.7: - resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} - engines: {node: '>= 0.4'} - dependencies: - has-tostringtag: 1.0.0 - dev: true - - /is-number@7.0.0: - resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} - engines: {node: '>=0.12.0'} - - /is-path-inside@3.0.3: - resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} - engines: {node: '>=8'} - dev: true - - /is-plain-object@5.0.0: - resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} - engines: {node: '>=0.10.0'} - dev: false - - /is-potential-custom-element-name@1.0.1: - resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} - dev: true - - /is-regex@1.1.4: - resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - has-tostringtag: 1.0.0 - - /is-shared-array-buffer@1.0.2: - resolution: {integrity: sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==} - dependencies: - call-bind: 1.0.2 - dev: true - - /is-stream@2.0.1: - resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} - engines: {node: '>=8'} - - /is-string@1.0.7: - resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} - engines: {node: '>= 0.4'} - dependencies: - has-tostringtag: 1.0.0 - dev: true - - /is-symbol@1.0.4: - resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} - engines: {node: '>= 0.4'} - dependencies: - has-symbols: 1.0.3 - dev: true - - /is-typed-array@1.1.10: - resolution: {integrity: sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==} - engines: {node: '>= 0.4'} - dependencies: - available-typed-arrays: 1.0.5 - call-bind: 1.0.2 - for-each: 0.3.3 - gopd: 1.0.1 - has-tostringtag: 1.0.0 - dev: true - - /is-weakref@1.0.2: - resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} - dependencies: - call-bind: 1.0.2 - dev: true - - /isexe@2.0.0: - resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - - /isomorphic.js@0.2.5: - resolution: {integrity: sha512-PIeMbHqMt4DnUP3MA/Flc0HElYjMXArsw1qwJZcm9sqR8mq3l8NYizFMty0pWwE/tzIGH3EKK5+jes5mAr85yw==} - dev: false - - /istanbul-lib-coverage@3.2.0: - resolution: {integrity: sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==} - engines: {node: '>=8'} - - /istanbul-lib-instrument@5.2.1: - resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==} - engines: {node: '>=8'} - dependencies: - '@babel/core': 7.21.8 - '@babel/parser': 7.21.8 - '@istanbuljs/schema': 0.1.3 - istanbul-lib-coverage: 3.2.0 - semver: 6.3.0 - transitivePeerDependencies: - - supports-color - - /istanbul-lib-report@3.0.0: - resolution: {integrity: sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==} - engines: {node: '>=8'} - dependencies: - istanbul-lib-coverage: 3.2.0 - make-dir: 3.1.0 - supports-color: 7.2.0 - - /istanbul-lib-source-maps@4.0.1: - resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} - engines: {node: '>=10'} - dependencies: - debug: 4.3.4 - istanbul-lib-coverage: 3.2.0 - source-map: 0.6.1 - transitivePeerDependencies: - - supports-color - - /istanbul-reports@3.1.5: - resolution: {integrity: sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==} - engines: {node: '>=8'} - dependencies: - html-escaper: 2.0.2 - istanbul-lib-report: 3.0.0 - - /jest-changed-files@29.5.0: - resolution: {integrity: sha512-IFG34IUMUaNBIxjQXF/iu7g6EcdMrGRRxaUSw92I/2g2YC6vCdTltl4nHvt7Ci5nSJwXIkCu8Ka1DKF+X7Z1Ag==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - execa: 5.1.1 - p-limit: 3.1.0 - - /jest-circus@29.5.0: - resolution: {integrity: sha512-gq/ongqeQKAplVxqJmbeUOJJKkW3dDNPY8PjhJ5G0lBRvu0e3EWGxGy5cI4LAGA7gV2UHCtWBI4EMXK8c9nQKA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/environment': 29.6.4 - '@jest/expect': 29.5.0 - '@jest/test-result': 29.5.0 - '@jest/types': 29.6.3 - '@types/node': 18.16.9 - chalk: 4.1.2 - co: 4.6.0 - dedent: 0.7.0 - is-generator-fn: 2.1.0 - jest-each: 29.5.0 - jest-matcher-utils: 29.5.0 - jest-message-util: 29.6.3 - jest-runtime: 29.5.0 - jest-snapshot: 29.5.0 - jest-util: 29.6.3 - p-limit: 3.1.0 - pretty-format: 29.5.0 - pure-rand: 6.0.2 - slash: 3.0.0 - stack-utils: 2.0.6 - transitivePeerDependencies: - - supports-color - - /jest-cli@29.5.0(@types/node@18.16.9): - resolution: {integrity: sha512-L1KcP1l4HtfwdxXNFCL5bmUbLQiKrakMUriBEcc1Vfz6gx31ORKdreuWvmQVBit+1ss9NNR3yxjwfwzZNdQXJw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - hasBin: true - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - dependencies: - '@jest/core': 29.5.0 - '@jest/test-result': 29.5.0 - '@jest/types': 29.5.0 - chalk: 4.1.2 - exit: 0.1.2 - graceful-fs: 4.2.11 - import-local: 3.1.0 - jest-config: 29.5.0(@types/node@18.16.9) - jest-util: 29.5.0 - jest-validate: 29.5.0 - prompts: 2.4.2 - yargs: 17.7.2 - transitivePeerDependencies: - - '@types/node' - - supports-color - - ts-node - - /jest-config@29.5.0(@types/node@18.16.9): - resolution: {integrity: sha512-kvDUKBnNJPNBmFFOhDbm59iu1Fii1Q6SxyhXfvylq3UTHbg6o7j/g8k2dZyXWLvfdKB1vAPxNZnMgtKJcmu3kA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - '@types/node': '*' - ts-node: '>=9.0.0' - peerDependenciesMeta: - '@types/node': - optional: true - ts-node: - optional: true - dependencies: - '@babel/core': 7.21.8 - '@jest/test-sequencer': 29.5.0 - '@jest/types': 29.5.0 - '@types/node': 18.16.9 - babel-jest: 29.6.2(@babel/core@7.21.8) - chalk: 4.1.2 - ci-info: 3.8.0 - deepmerge: 4.3.1 - glob: 7.2.3 - graceful-fs: 4.2.11 - jest-circus: 29.5.0 - jest-environment-node: 29.5.0 - jest-get-type: 29.4.3 - jest-regex-util: 29.4.3 - jest-resolve: 29.5.0 - jest-runner: 29.5.0 - jest-util: 29.5.0 - jest-validate: 29.5.0 - micromatch: 4.0.5 - parse-json: 5.2.0 - pretty-format: 29.5.0 - slash: 3.0.0 - strip-json-comments: 3.1.1 - transitivePeerDependencies: - - supports-color - - /jest-diff@29.5.0: - resolution: {integrity: sha512-LtxijLLZBduXnHSniy0WMdaHjmQnt3g5sa16W4p0HqukYTTsyTW3GD1q41TyGl5YFXj/5B2U6dlh5FM1LIMgxw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - chalk: 4.1.2 - diff-sequences: 29.4.3 - jest-get-type: 29.4.3 - pretty-format: 29.5.0 - - /jest-docblock@29.4.3: - resolution: {integrity: sha512-fzdTftThczeSD9nZ3fzA/4KkHtnmllawWrXO69vtI+L9WjEIuXWs4AmyME7lN5hU7dB0sHhuPfcKofRsUb/2Fg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - detect-newline: 3.1.0 - - /jest-each@29.5.0: - resolution: {integrity: sha512-HM5kIJ1BTnVt+DQZ2ALp3rzXEl+g726csObrW/jpEGl+CDSSQpOJJX2KE/vEg8cxcMXdyEPu6U4QX5eruQv5hA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/types': 29.6.3 - chalk: 4.1.2 - jest-get-type: 29.4.3 - jest-util: 29.6.3 - pretty-format: 29.5.0 - - /jest-environment-jsdom@29.6.2: - resolution: {integrity: sha512-7oa/+266AAEgkzae8i1awNEfTfjwawWKLpiw2XesZmaoVVj9u9t8JOYx18cG29rbPNtkUlZ8V4b5Jb36y/VxoQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - canvas: ^2.5.0 - peerDependenciesMeta: - canvas: - optional: true - dependencies: - '@jest/environment': 29.6.4 - '@jest/fake-timers': 29.6.4 - '@jest/types': 29.6.3 - '@types/jsdom': 20.0.1 - '@types/node': 18.16.9 - jest-mock: 29.6.3 - jest-util: 29.6.3 - jsdom: 20.0.3 - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate - dev: true - - /jest-environment-node@29.5.0: - resolution: {integrity: sha512-ExxuIK/+yQ+6PRGaHkKewYtg6hto2uGCgvKdb2nfJfKXgZ17DfXjvbZ+jA1Qt9A8EQSfPnt5FKIfnOO3u1h9qw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/environment': 29.6.4 - '@jest/fake-timers': 29.6.4 - '@jest/types': 29.6.3 - '@types/node': 18.16.9 - jest-mock: 29.6.3 - jest-util: 29.6.3 - - /jest-get-type@29.4.3: - resolution: {integrity: sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - /jest-haste-map@29.5.0: - resolution: {integrity: sha512-IspOPnnBro8YfVYSw6yDRKh/TiCdRngjxeacCps1cQ9cgVN6+10JUcuJ1EabrgYLOATsIAigxA0rLR9x/YlrSA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/types': 29.5.0 - '@types/graceful-fs': 4.1.6 - '@types/node': 18.16.9 - anymatch: 3.1.3 - fb-watchman: 2.0.2 - graceful-fs: 4.2.11 - jest-regex-util: 29.4.3 - jest-util: 29.5.0 - jest-worker: 29.5.0 - micromatch: 4.0.5 - walker: 1.0.8 - optionalDependencies: - fsevents: 2.3.2 - - /jest-haste-map@29.6.4: - resolution: {integrity: sha512-12Ad+VNTDHxKf7k+M65sviyynRoZYuL1/GTuhEVb8RYsNSNln71nANRb/faSyWvx0j+gHcivChXHIoMJrGYjog==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/types': 29.6.3 - '@types/graceful-fs': 4.1.6 - '@types/node': 18.16.9 - anymatch: 3.1.3 - fb-watchman: 2.0.2 - graceful-fs: 4.2.11 - jest-regex-util: 29.6.3 - jest-util: 29.6.3 - jest-worker: 29.6.4 - micromatch: 4.0.5 - walker: 1.0.8 - optionalDependencies: - fsevents: 2.3.2 - - /jest-leak-detector@29.5.0: - resolution: {integrity: sha512-u9YdeeVnghBUtpN5mVxjID7KbkKE1QU4f6uUwuxiY0vYRi9BUCLKlPEZfDGR67ofdFmDz9oPAy2G92Ujrntmow==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - jest-get-type: 29.4.3 - pretty-format: 29.5.0 - - /jest-matcher-utils@29.5.0: - resolution: {integrity: sha512-lecRtgm/rjIK0CQ7LPQwzCs2VwW6WAahA55YBuI+xqmhm7LAaxokSB8C97yJeYyT+HvQkH741StzpU41wohhWw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - chalk: 4.1.2 - jest-diff: 29.5.0 - jest-get-type: 29.4.3 - pretty-format: 29.5.0 - - /jest-message-util@29.5.0: - resolution: {integrity: sha512-Kijeg9Dag6CKtIDA7O21zNTACqD5MD/8HfIV8pdD94vFyFuer52SigdC3IQMhab3vACxXMiFk+yMHNdbqtyTGA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@babel/code-frame': 7.21.4 - '@jest/types': 29.5.0 - '@types/stack-utils': 2.0.1 - chalk: 4.1.2 - graceful-fs: 4.2.11 - micromatch: 4.0.5 - pretty-format: 29.5.0 - slash: 3.0.0 - stack-utils: 2.0.6 - - /jest-message-util@29.6.3: - resolution: {integrity: sha512-FtzaEEHzjDpQp51HX4UMkPZjy46ati4T5pEMyM6Ik48ztu4T9LQplZ6OsimHx7EuM9dfEh5HJa6D3trEftu3dA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@babel/code-frame': 7.21.4 - '@jest/types': 29.6.3 - '@types/stack-utils': 2.0.1 - chalk: 4.1.2 - graceful-fs: 4.2.11 - micromatch: 4.0.5 - pretty-format: 29.6.3 - slash: 3.0.0 - stack-utils: 2.0.6 - - /jest-mock@29.5.0: - resolution: {integrity: sha512-GqOzvdWDE4fAV2bWQLQCkujxYWL7RxjCnj71b5VhDAGOevB3qj3Ovg26A5NI84ZpODxyzaozXLOh2NCgkbvyaw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/types': 29.5.0 - '@types/node': 18.16.9 - jest-util: 29.5.0 - - /jest-mock@29.6.3: - resolution: {integrity: sha512-Z7Gs/mOyTSR4yPsaZ72a/MtuK6RnC3JYqWONe48oLaoEcYwEDxqvbXz85G4SJrm2Z5Ar9zp6MiHF4AlFlRM4Pg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/types': 29.6.3 - '@types/node': 18.16.9 - jest-util: 29.6.3 - - /jest-pnp-resolver@1.2.3(jest-resolve@29.5.0): - resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==} - engines: {node: '>=6'} - peerDependencies: - jest-resolve: '*' - peerDependenciesMeta: - jest-resolve: - optional: true - dependencies: - jest-resolve: 29.5.0 - - /jest-regex-util@29.4.3: - resolution: {integrity: sha512-O4FglZaMmWXbGHSQInfXewIsd1LMn9p3ZXB/6r4FOkyhX2/iP/soMG98jGvk/A3HAN78+5VWcBGO0BJAPRh4kg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - /jest-regex-util@29.6.3: - resolution: {integrity: sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - /jest-resolve-dependencies@29.5.0: - resolution: {integrity: sha512-sjV3GFr0hDJMBpYeUuGduP+YeCRbd7S/ck6IvL3kQ9cpySYKqcqhdLLC2rFwrcL7tz5vYibomBrsFYWkIGGjOg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - jest-regex-util: 29.4.3 - jest-snapshot: 29.5.0 - transitivePeerDependencies: - - supports-color - - /jest-resolve@29.5.0: - resolution: {integrity: sha512-1TzxJ37FQq7J10jPtQjcc+MkCkE3GBpBecsSUWJ0qZNJpmg6m0D9/7II03yJulm3H/fvVjgqLh/k2eYg+ui52w==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - chalk: 4.1.2 - graceful-fs: 4.2.11 - jest-haste-map: 29.5.0 - jest-pnp-resolver: 1.2.3(jest-resolve@29.5.0) - jest-util: 29.5.0 - jest-validate: 29.5.0 - resolve: 1.22.2 - resolve.exports: 2.0.2 - slash: 3.0.0 - - /jest-runner@29.5.0: - resolution: {integrity: sha512-m7b6ypERhFghJsslMLhydaXBiLf7+jXy8FwGRHO3BGV1mcQpPbwiqiKUR2zU2NJuNeMenJmlFZCsIqzJCTeGLQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/console': 29.5.0 - '@jest/environment': 29.5.0 - '@jest/test-result': 29.5.0 - '@jest/transform': 29.5.0 - '@jest/types': 29.5.0 - '@types/node': 18.16.9 - chalk: 4.1.2 - emittery: 0.13.1 - graceful-fs: 4.2.11 - jest-docblock: 29.4.3 - jest-environment-node: 29.5.0 - jest-haste-map: 29.5.0 - jest-leak-detector: 29.5.0 - jest-message-util: 29.5.0 - jest-resolve: 29.5.0 - jest-runtime: 29.5.0 - jest-util: 29.5.0 - jest-watcher: 29.5.0 - jest-worker: 29.5.0 - p-limit: 3.1.0 - source-map-support: 0.5.13 - transitivePeerDependencies: - - supports-color - - /jest-runtime@29.5.0: - resolution: {integrity: sha512-1Hr6Hh7bAgXQP+pln3homOiEZtCDZFqwmle7Ew2j8OlbkIu6uE3Y/etJQG8MLQs3Zy90xrp2C0BRrtPHG4zryw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/environment': 29.5.0 - '@jest/fake-timers': 29.5.0 - '@jest/globals': 29.5.0 - '@jest/source-map': 29.4.3 - '@jest/test-result': 29.5.0 - '@jest/transform': 29.5.0 - '@jest/types': 29.5.0 - '@types/node': 18.16.9 - chalk: 4.1.2 - cjs-module-lexer: 1.2.2 - collect-v8-coverage: 1.0.1 - glob: 7.2.3 - graceful-fs: 4.2.11 - jest-haste-map: 29.5.0 - jest-message-util: 29.5.0 - jest-mock: 29.5.0 - jest-regex-util: 29.4.3 - jest-resolve: 29.5.0 - jest-snapshot: 29.5.0 - jest-util: 29.5.0 - slash: 3.0.0 - strip-bom: 4.0.0 - transitivePeerDependencies: - - supports-color - - /jest-snapshot@29.5.0: - resolution: {integrity: sha512-x7Wolra5V0tt3wRs3/ts3S6ciSQVypgGQlJpz2rsdQYoUKxMxPNaoHMGJN6qAuPJqS+2iQ1ZUn5kl7HCyls84g==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@babel/core': 7.21.8 - '@babel/generator': 7.21.5 - '@babel/plugin-syntax-jsx': 7.21.4(@babel/core@7.21.8) - '@babel/plugin-syntax-typescript': 7.21.4(@babel/core@7.21.8) - '@babel/traverse': 7.23.7 - '@babel/types': 7.21.5 - '@jest/expect-utils': 29.5.0 - '@jest/transform': 29.5.0 - '@jest/types': 29.5.0 - '@types/babel__traverse': 7.18.5 - '@types/prettier': 2.7.2 - babel-preset-current-node-syntax: 1.0.1(@babel/core@7.21.8) - chalk: 4.1.2 - expect: 29.5.0 - graceful-fs: 4.2.11 - jest-diff: 29.5.0 - jest-get-type: 29.4.3 - jest-matcher-utils: 29.5.0 - jest-message-util: 29.5.0 - jest-util: 29.5.0 - natural-compare: 1.4.0 - pretty-format: 29.5.0 - semver: 7.5.1 - transitivePeerDependencies: - - supports-color - - /jest-util@29.5.0: - resolution: {integrity: sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/types': 29.5.0 - '@types/node': 18.16.9 - chalk: 4.1.2 - ci-info: 3.8.0 - graceful-fs: 4.2.11 - picomatch: 2.3.1 - - /jest-util@29.6.3: - resolution: {integrity: sha512-QUjna/xSy4B32fzcKTSz1w7YYzgiHrjjJjevdRf61HYk998R5vVMMNmrHESYZVDS5DSWs+1srPLPKxXPkeSDOA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/types': 29.6.3 - '@types/node': 18.16.9 - chalk: 4.1.2 - ci-info: 3.8.0 - graceful-fs: 4.2.11 - picomatch: 2.3.1 - - /jest-validate@29.5.0: - resolution: {integrity: sha512-pC26etNIi+y3HV8A+tUGr/lph9B18GnzSRAkPaaZJIE1eFdiYm6/CewuiJQ8/RlfHd1u/8Ioi8/sJ+CmbA+zAQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/types': 29.5.0 - camelcase: 6.3.0 - chalk: 4.1.2 - jest-get-type: 29.4.3 - leven: 3.1.0 - pretty-format: 29.5.0 - - /jest-watcher@29.5.0: - resolution: {integrity: sha512-KmTojKcapuqYrKDpRwfqcQ3zjMlwu27SYext9pt4GlF5FUgB+7XE1mcCnSm6a4uUpFyQIkb6ZhzZvHl+jiBCiA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/test-result': 29.5.0 - '@jest/types': 29.5.0 - '@types/node': 18.16.9 - ansi-escapes: 4.3.2 - chalk: 4.1.2 - emittery: 0.13.1 - jest-util: 29.5.0 - string-length: 4.0.2 - - /jest-worker@29.5.0: - resolution: {integrity: sha512-NcrQnevGoSp4b5kg+akIpthoAFHxPBcb5P6mYPY0fUNT+sSvmtu6jlkEle3anczUKIKEbMxFimk9oTP/tpIPgA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@types/node': 18.16.9 - jest-util: 29.5.0 - merge-stream: 2.0.0 - supports-color: 8.1.1 - - /jest-worker@29.6.4: - resolution: {integrity: sha512-6dpvFV4WjcWbDVGgHTWo/aupl8/LbBx2NSKfiwqf79xC/yeJjKHT1+StcKy/2KTmW16hE68ccKVOtXf+WZGz7Q==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@types/node': 18.16.9 - jest-util: 29.6.3 - merge-stream: 2.0.0 - supports-color: 8.1.1 - - /jest@29.5.0(@types/node@18.16.9): - resolution: {integrity: sha512-juMg3he2uru1QoXX078zTa7pO85QyB9xajZc6bU+d9yEGwrKX6+vGmJQ3UdVZsvTEUARIdObzH68QItim6OSSQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - hasBin: true - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - dependencies: - '@jest/core': 29.5.0 - '@jest/types': 29.5.0 - import-local: 3.1.0 - jest-cli: 29.5.0(@types/node@18.16.9) - transitivePeerDependencies: - - '@types/node' - - supports-color - - ts-node - - /jiti@1.18.2: - resolution: {integrity: sha512-QAdOptna2NYiSSpv0O/BwoHBSmz4YhpzJHyi+fnMRTXFjp7B8i/YG5Z8IfusxB1ufjcD2Sre1F3R+nX3fvy7gg==} - hasBin: true - dev: true - - /js-base64@3.7.5: - resolution: {integrity: sha512-3MEt5DTINKqfScXKfJFrRbxkrnk2AxPWGBL/ycjz4dK8iqiSJ06UxD8jh8xuh6p10TX4t2+7FsBYVxxQbMg+qA==} - dev: false - - /js-sdsl@4.4.0: - resolution: {integrity: sha512-FfVSdx6pJ41Oa+CF7RDaFmTnCaFhua+SNYQX74riGOpl96x+2jQCqEfQ2bnXu/5DPCqlRuiqyvTJM0Qjz26IVg==} - dev: true - - /js-tokens@4.0.0: - resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - - /js-yaml@3.14.1: - resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} - hasBin: true - dependencies: - argparse: 1.0.10 - esprima: 4.0.1 - - /js-yaml@4.1.0: - resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} - hasBin: true - dependencies: - argparse: 2.0.1 - dev: true - - /jsdom@20.0.3: - resolution: {integrity: sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==} - engines: {node: '>=14'} - peerDependencies: - canvas: ^2.5.0 - peerDependenciesMeta: - canvas: - optional: true - dependencies: - abab: 2.0.6 - acorn: 8.8.2 - acorn-globals: 7.0.1 - cssom: 0.5.0 - cssstyle: 2.3.0 - data-urls: 3.0.2 - decimal.js: 10.4.3 - domexception: 4.0.0 - escodegen: 2.1.0 - form-data: 4.0.0 - html-encoding-sniffer: 3.0.0 - http-proxy-agent: 5.0.0 - https-proxy-agent: 5.0.1 - is-potential-custom-element-name: 1.0.1 - nwsapi: 2.2.7 - parse5: 7.1.2 - saxes: 6.0.0 - symbol-tree: 3.2.4 - tough-cookie: 4.1.3 - w3c-xmlserializer: 4.0.0 - webidl-conversions: 7.0.0 - whatwg-encoding: 2.0.0 - whatwg-mimetype: 3.0.0 - whatwg-url: 11.0.0 - ws: 8.14.1 - xml-name-validator: 4.0.0 - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate - dev: true - - /jsesc@2.5.2: - resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} - engines: {node: '>=4'} - hasBin: true - - /json-parse-even-better-errors@2.3.1: - resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} - - /json-schema-traverse@0.4.1: - resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} - dev: true - - /json-stable-stringify-without-jsonify@1.0.1: - resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} - dev: true - - /json5@2.2.3: - resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} - engines: {node: '>=6'} - hasBin: true - - /jsonc-parser@3.2.0: - resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==} - dev: true - - /jsonfile@6.1.0: - resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} - dependencies: - universalify: 2.0.0 - optionalDependencies: - graceful-fs: 4.2.11 - dev: true - - /jsx-ast-utils@3.3.3: - resolution: {integrity: sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw==} - engines: {node: '>=4.0'} - dependencies: - array-includes: 3.1.6 - object.assign: 4.1.4 - dev: true - - /katex@0.16.7: - resolution: {integrity: sha512-Xk9C6oGKRwJTfqfIbtr0Kes9OSv6IFsuhFGc7tW4urlpMJtuh+7YhzU6YEG9n8gmWKcMAFzkp7nr+r69kV0zrA==} - hasBin: true - dependencies: - commander: 8.3.0 - dev: false - - /keycode@2.2.1: - resolution: {integrity: sha512-Rdgz9Hl9Iv4QKi8b0OlCRQEzp4AgVxyCtz5S/+VIHezDmrDhkp2N2TqBWOLz0/gbeREXOOiI9/4b8BY9uw2vFg==} - dev: false - - /kleur@3.0.3: - resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} - engines: {node: '>=6'} - - /leven@3.1.0: - resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} - engines: {node: '>=6'} - - /levn@0.4.1: - resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} - engines: {node: '>= 0.8.0'} - dependencies: - prelude-ls: 1.2.1 - type-check: 0.4.0 - dev: true - - /lib0@0.2.74: - resolution: {integrity: sha512-roj9i46/JwG5ik5KNTkxP2IytlnrssAkD/OhlAVtE+GqectrdkfR+pttszVLrOzMDeXNs1MPt6yo66MUolWSiA==} - engines: {node: '>=14'} - hasBin: true - dependencies: - isomorphic.js: 0.2.5 - dev: false - - /lib0@0.2.88: - resolution: {integrity: sha512-KyroiEvCeZcZEMx5Ys+b4u4eEBbA1ch7XUaBhYpwa/nPMrzTjUhI4RfcytmQfYoTBPcdyx+FX6WFNIoNuJzJfQ==} - engines: {node: '>=16'} - hasBin: true - dependencies: - isomorphic.js: 0.2.5 - dev: false - - /lilconfig@2.1.0: - resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} - engines: {node: '>=10'} - dev: true - - /lines-and-columns@1.2.4: - resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} - - /locate-path@5.0.0: - resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} - engines: {node: '>=8'} - dependencies: - p-locate: 4.1.0 - - /locate-path@6.0.0: - resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} - engines: {node: '>=10'} - dependencies: - p-locate: 5.0.0 - dev: true - - /lodash-es@4.17.21: - resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} - dev: false - - /lodash.clonedeep@4.5.0: - resolution: {integrity: sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==} - - /lodash.isequal@4.5.0: - resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==} - - /lodash.memoize@4.1.2: - resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} - dev: true - - /lodash.merge@4.6.2: - resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} - dev: true - - /lodash@4.17.21: - resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} - - /loose-envify@1.4.0: - resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} - hasBin: true - dependencies: - js-tokens: 4.0.0 - - /lower-case@2.0.2: - resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} - dependencies: - tslib: 2.5.0 - dev: true - - /lru-cache@5.1.1: - resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} - dependencies: - yallist: 3.1.1 - - /lru-cache@6.0.0: - resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} - engines: {node: '>=10'} - dependencies: - yallist: 4.0.0 - - /luxon@3.4.4: - resolution: {integrity: sha512-zobTr7akeGHnv7eBOXcRgMeCP6+uyYsczwmeRCauvpvaAltgNyTbLH/+VaEAPUeWBT+1GuNmz4wC/6jtQzbbVA==} - engines: {node: '>=12'} - dev: false - - /magic-string@0.27.0: - resolution: {integrity: sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==} - engines: {node: '>=12'} - dependencies: - '@jridgewell/sourcemap-codec': 1.4.15 - dev: true - - /make-dir@3.1.0: - resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} - engines: {node: '>=8'} - dependencies: - semver: 6.3.0 - - /make-error@1.3.6: - resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} - dev: true - - /makeerror@1.0.12: - resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} - dependencies: - tmpl: 1.0.5 - - /material-colors@1.2.6: - resolution: {integrity: sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg==} - dev: false - - /mdn-data@2.0.28: - resolution: {integrity: sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==} - dev: true - - /mdn-data@2.0.30: - resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==} - dev: true - - /memoize-one@5.2.1: - resolution: {integrity: sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==} - dev: false - - /memoize-one@6.0.0: - resolution: {integrity: sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==} - dev: false - - /merge-stream@2.0.0: - resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} - - /merge2@1.4.1: - resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} - engines: {node: '>= 8'} - dev: true - - /micromatch@4.0.5: - resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} - engines: {node: '>=8.6'} - dependencies: - braces: 3.0.2 - picomatch: 2.3.1 - - /mime-db@1.52.0: - resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} - engines: {node: '>= 0.6'} - dev: true - - /mime-types@2.1.35: - resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} - engines: {node: '>= 0.6'} - dependencies: - mime-db: 1.52.0 - dev: true - - /mimic-fn@2.1.0: - resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} - engines: {node: '>=6'} - - /minimatch@3.1.2: - resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} - dependencies: - brace-expansion: 1.1.11 - - /minimist@1.2.8: - resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - dev: true - - /mkdirp@1.0.4: - resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} - engines: {node: '>=10'} - hasBin: true - dev: true - - /moment-timezone@0.5.44: - resolution: {integrity: sha512-nv3YpzI/8lkQn0U6RkLd+f0W/zy/JnoR5/EyPz/dNkPTBjA2jNLCVxaiQ8QpeLymhSZvX0wCL5s27NQWdOPwAw==} - dependencies: - moment: 2.30.1 - dev: false - - /moment@2.30.1: - resolution: {integrity: sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==} - dev: false - - /ms@2.1.2: - resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} - - /mz@2.7.0: - resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} - dependencies: - any-promise: 1.3.0 - object-assign: 4.1.1 - thenify-all: 1.6.0 - dev: true - - /nanoid@3.3.6: - resolution: {integrity: sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true - dev: true - - /nanoid@4.0.2: - resolution: {integrity: sha512-7ZtY5KTCNheRGfEFxnedV5zFiORN1+Y1N6zvPTnHQd8ENUvfaDBeuJDZb2bN/oXwXxu3qkTXDzy57W5vAmDTBw==} - engines: {node: ^14 || ^16 || >=18} - hasBin: true - dev: false - - /natural-compare-lite@1.4.0: - resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==} - dev: true - - /natural-compare@1.4.0: - resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - - /no-case@3.0.4: - resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} - dependencies: - lower-case: 2.0.2 - tslib: 2.5.0 - dev: true - - /node-int64@0.4.0: - resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} - - /node-releases@2.0.10: - resolution: {integrity: sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==} - - /normalize-path@3.0.0: - resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} - engines: {node: '>=0.10.0'} - - /normalize-range@0.1.2: - resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} - engines: {node: '>=0.10.0'} - dev: true - - /npm-run-path@4.0.1: - resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} - engines: {node: '>=8'} - dependencies: - path-key: 3.1.1 - - /nth-check@2.1.1: - resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} - dependencies: - boolbase: 1.0.0 - dev: true - - /nwsapi@2.2.7: - resolution: {integrity: sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==} - dev: true - - /object-assign@4.1.1: - resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} - engines: {node: '>=0.10.0'} - - /object-hash@3.0.0: - resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} - engines: {node: '>= 6'} - dev: true - - /object-inspect@1.12.3: - resolution: {integrity: sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==} - dev: true - - /object-is@1.1.5: - resolution: {integrity: sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - dev: false - - /object-keys@1.1.1: - resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} - engines: {node: '>= 0.4'} - - /object.assign@4.1.4: - resolution: {integrity: sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - has-symbols: 1.0.3 - object-keys: 1.1.1 - dev: true - - /object.entries@1.1.6: - resolution: {integrity: sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.21.2 - dev: true - - /object.fromentries@2.0.6: - resolution: {integrity: sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.21.2 - dev: true - - /object.hasown@1.1.2: - resolution: {integrity: sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw==} - dependencies: - define-properties: 1.2.0 - es-abstract: 1.21.2 - dev: true - - /object.values@1.1.6: - resolution: {integrity: sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.21.2 - dev: true - - /once@1.4.0: - resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - dependencies: - wrappy: 1.0.2 - - /onetime@5.1.2: - resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} - engines: {node: '>=6'} - dependencies: - mimic-fn: 2.1.0 - - /optionator@0.9.1: - resolution: {integrity: sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==} - engines: {node: '>= 0.8.0'} - dependencies: - deep-is: 0.1.4 - fast-levenshtein: 2.0.6 - levn: 0.4.1 - prelude-ls: 1.2.1 - type-check: 0.4.0 - word-wrap: 1.2.3 - dev: true - - /p-limit@2.3.0: - resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} - engines: {node: '>=6'} - dependencies: - p-try: 2.2.0 - - /p-limit@3.1.0: - resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} - engines: {node: '>=10'} - dependencies: - yocto-queue: 0.1.0 - - /p-locate@4.1.0: - resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} - engines: {node: '>=8'} - dependencies: - p-limit: 2.3.0 - - /p-locate@5.0.0: - resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} - engines: {node: '>=10'} - dependencies: - p-limit: 3.1.0 - dev: true - - /p-try@2.2.0: - resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} - engines: {node: '>=6'} - - /param-case@3.0.4: - resolution: {integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==} - dependencies: - dot-case: 3.0.4 - tslib: 2.5.0 - dev: true - - /parchment@1.1.4: - resolution: {integrity: sha512-J5FBQt/pM2inLzg4hEWmzQx/8h8D0CiDxaG3vyp9rKrQRSDgBlhjdP5jQGgosEajXPSQouXGHOmVdgo7QmJuOg==} - - /parent-module@1.0.1: - resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} - engines: {node: '>=6'} - dependencies: - callsites: 3.1.0 - - /parse-json@5.2.0: - resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} - engines: {node: '>=8'} - dependencies: - '@babel/code-frame': 7.21.4 - error-ex: 1.3.2 - json-parse-even-better-errors: 2.3.1 - lines-and-columns: 1.2.4 - - /parse5@7.1.2: - resolution: {integrity: sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==} - dependencies: - entities: 4.5.0 - dev: true - - /pascal-case@3.1.2: - resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==} - dependencies: - no-case: 3.0.4 - tslib: 2.5.0 - dev: true - - /path-case@3.0.4: - resolution: {integrity: sha512-qO4qCFjXqVTrcbPt/hQfhTQ+VhFsqNKOPtytgNKkKxSoEp3XPUQ8ObFuePylOIok5gjn69ry8XiULxCwot3Wfg==} - dependencies: - dot-case: 3.0.4 - tslib: 2.5.0 - dev: true - - /path-exists@4.0.0: - resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} - engines: {node: '>=8'} - - /path-is-absolute@1.0.1: - resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} - engines: {node: '>=0.10.0'} - - /path-key@3.1.1: - resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} - engines: {node: '>=8'} - - /path-parse@1.0.7: - resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - - /path-type@4.0.0: - resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} - engines: {node: '>=8'} - - /performance-now@2.1.0: - resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==} - dev: false - - /picocolors@1.0.0: - resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} - - /picomatch@2.3.1: - resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} - engines: {node: '>=8.6'} - - /pify@2.3.0: - resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} - engines: {node: '>=0.10.0'} - dev: true - - /pirates@4.0.5: - resolution: {integrity: sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==} - engines: {node: '>= 6'} - - /pkg-dir@4.2.0: - resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} - engines: {node: '>=8'} - dependencies: - find-up: 4.1.0 - - /postcss-import@15.1.0(postcss@8.4.23): - resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} - engines: {node: '>=14.0.0'} - peerDependencies: - postcss: ^8.0.0 - dependencies: - postcss: 8.4.23 - postcss-value-parser: 4.2.0 - read-cache: 1.0.0 - resolve: 1.22.2 - dev: true - - /postcss-js@4.0.1(postcss@8.4.23): - resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==} - engines: {node: ^12 || ^14 || >= 16} - peerDependencies: - postcss: ^8.4.21 - dependencies: - camelcase-css: 2.0.1 - postcss: 8.4.23 - dev: true - - /postcss-load-config@4.0.1(postcss@8.4.23): - resolution: {integrity: sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==} - engines: {node: '>= 14'} - peerDependencies: - postcss: '>=8.0.9' - ts-node: '>=9.0.0' - peerDependenciesMeta: - postcss: - optional: true - ts-node: - optional: true - dependencies: - lilconfig: 2.1.0 - postcss: 8.4.23 - yaml: 2.2.2 - dev: true - - /postcss-nested@6.0.1(postcss@8.4.23): - resolution: {integrity: sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==} - engines: {node: '>=12.0'} - peerDependencies: - postcss: ^8.2.14 - dependencies: - postcss: 8.4.23 - postcss-selector-parser: 6.0.12 - dev: true - - /postcss-selector-parser@6.0.12: - resolution: {integrity: sha512-NdxGCAZdRrwVI1sy59+Wzrh+pMMHxapGnpfenDVlMEXoOcvt4pGE0JLK9YY2F5dLxcFYA/YbVQKhcGU+FtSYQg==} - engines: {node: '>=4'} - dependencies: - cssesc: 3.0.0 - util-deprecate: 1.0.2 - dev: true - - /postcss-value-parser@4.2.0: - resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} - dev: true - - /postcss@8.4.23: - resolution: {integrity: sha512-bQ3qMcpF6A/YjR55xtoTr0jGOlnPOKAIMdOWiv0EIT6HVPEaJiJB4NLljSbiHoC2RX7DN5Uvjtpbg1NPdwv1oA==} - engines: {node: ^10 || ^12 || >=14} - dependencies: - nanoid: 3.3.6 - picocolors: 1.0.0 - source-map-js: 1.0.2 - dev: true - - /prefix-style@2.0.1: - resolution: {integrity: sha512-gdr1MBNVT0drzTq95CbSNdsrBDoHGlb2aDJP/FoY+1e+jSDPOb1Cv554gH2MGiSr2WTcXi/zu+NaFzfcHQkfBQ==} - dev: false - - /prelude-ls@1.2.1: - resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} - engines: {node: '>= 0.8.0'} - dev: true - - /prettier-plugin-tailwindcss@0.2.8(prettier@2.8.4): - resolution: {integrity: sha512-KgPcEnJeIijlMjsA6WwYgRs5rh3/q76oInqtMXBA/EMcamrcYJpyhtRhyX1ayT9hnHlHTuO8sIifHF10WuSDKg==} - engines: {node: '>=12.17.0'} - peerDependencies: - '@ianvs/prettier-plugin-sort-imports': '*' - '@prettier/plugin-pug': '*' - '@shopify/prettier-plugin-liquid': '*' - '@shufo/prettier-plugin-blade': '*' - '@trivago/prettier-plugin-sort-imports': '*' - prettier: '>=2.2.0' - prettier-plugin-astro: '*' - prettier-plugin-css-order: '*' - prettier-plugin-import-sort: '*' - prettier-plugin-jsdoc: '*' - prettier-plugin-organize-attributes: '*' - prettier-plugin-organize-imports: '*' - prettier-plugin-style-order: '*' - prettier-plugin-svelte: '*' - prettier-plugin-twig-melody: '*' - peerDependenciesMeta: - '@ianvs/prettier-plugin-sort-imports': - optional: true - '@prettier/plugin-pug': - optional: true - '@shopify/prettier-plugin-liquid': - optional: true - '@shufo/prettier-plugin-blade': - optional: true - '@trivago/prettier-plugin-sort-imports': - optional: true - prettier-plugin-astro: - optional: true - prettier-plugin-css-order: - optional: true - prettier-plugin-import-sort: - optional: true - prettier-plugin-jsdoc: - optional: true - prettier-plugin-organize-attributes: - optional: true - prettier-plugin-organize-imports: - optional: true - prettier-plugin-style-order: - optional: true - prettier-plugin-svelte: - optional: true - prettier-plugin-twig-melody: - optional: true - dependencies: - prettier: 2.8.4 - dev: true - - /prettier@2.8.4: - resolution: {integrity: sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw==} - engines: {node: '>=10.13.0'} - hasBin: true - dev: true - - /pretty-format@29.5.0: - resolution: {integrity: sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/schemas': 29.4.3 - ansi-styles: 5.2.0 - react-is: 18.2.0 - - /pretty-format@29.6.3: - resolution: {integrity: sha512-ZsBgjVhFAj5KeK+nHfF1305/By3lechHQSMWCTl8iHSbfOm2TN5nHEtFc/+W7fAyUeCs2n5iow72gld4gW0xDw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/schemas': 29.6.3 - ansi-styles: 5.2.0 - react-is: 18.2.0 - - /prismjs@1.29.0: - resolution: {integrity: sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==} - engines: {node: '>=6'} - dev: false - - /prompts@2.4.2: - resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} - engines: {node: '>= 6'} - dependencies: - kleur: 3.0.3 - sisteransi: 1.0.5 - - /prop-types@15.8.1: - resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} - dependencies: - loose-envify: 1.4.0 - object-assign: 4.1.1 - react-is: 16.13.1 - - /protoc-gen-ts@0.8.7: - resolution: {integrity: sha512-jr4VJey2J9LVYCV7EVyVe53g1VMw28cCmYJhBe5e3YX5wiyiDwgxWxeDf9oTqAe4P1bN/YGAkW2jhlH8LohwiQ==} - hasBin: true - dev: false - - /proxy-compare@2.5.1: - resolution: {integrity: sha512-oyfc0Tx87Cpwva5ZXezSp5V9vht1c7dZBhvuV/y3ctkgMVUmiAGDVeeB0dKhGSyT0v1ZTEQYpe/RXlBVBNuCLA==} - dev: false - - /psl@1.9.0: - resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==} - dev: true - - /punycode@2.3.0: - resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==} - engines: {node: '>=6'} - dev: true - - /pure-rand@6.0.2: - resolution: {integrity: sha512-6Yg0ekpKICSjPswYOuC5sku/TSWaRYlA0qsXqJgM/d/4pLPHPuTxK7Nbf7jFKzAeedUhR8C7K9Uv63FBsSo8xQ==} - - /querystringify@2.2.0: - resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} - dev: true - - /queue-microtask@1.2.3: - resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - dev: true - - /quill-delta@3.6.3: - resolution: {integrity: sha512-wdIGBlcX13tCHOXGMVnnTVFtGRLoP0imqxM696fIPwIf5ODIYUHIvHbZcyvGlZFiFhK5XzDC2lpjbxRhnM05Tg==} - engines: {node: '>=0.10'} - dependencies: - deep-equal: 1.1.1 - extend: 3.0.2 - fast-diff: 1.1.2 - dev: false - - /quill-delta@4.2.2: - resolution: {integrity: sha512-qjbn82b/yJzOjstBgkhtBjN2TNK+ZHP/BgUQO+j6bRhWQQdmj2lH6hXG7+nwwLF41Xgn//7/83lxs9n2BkTtTg==} - dependencies: - fast-diff: 1.2.0 - lodash.clonedeep: 4.5.0 - lodash.isequal: 4.5.0 - dev: true - - /quill-delta@5.1.0: - resolution: {integrity: sha512-X74oCeRI4/p0ucjb5Ma8adTXd9Scumz367kkMK5V/IatcX6A0vlgLgKbzXWy5nZmCGeNJm2oQX0d2Eqj+ZIlCA==} - engines: {node: '>= 12.0.0'} - dependencies: - fast-diff: 1.3.0 - lodash.clonedeep: 4.5.0 - lodash.isequal: 4.5.0 - dev: false - - /quill@1.3.7: - resolution: {integrity: sha512-hG/DVzh/TiknWtE6QmWAF/pxoZKYxfe3J/d/+ShUWkDvvkZQVTPeVmUJVu1uE6DDooC4fWTiCLh84ul89oNz5g==} - dependencies: - clone: 2.1.2 - deep-equal: 1.1.1 - eventemitter3: 2.0.3 - extend: 3.0.2 - parchment: 1.1.4 - quill-delta: 3.6.3 - dev: false - - /raf-schd@4.0.3: - resolution: {integrity: sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ==} - dev: false - - /raf@3.4.1: - resolution: {integrity: sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==} - dependencies: - performance-now: 2.1.0 - dev: false - - /react-beautiful-dnd@13.1.1(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-0Lvs4tq2VcrEjEgDXHjT98r+63drkKEgqyxdA7qD3mvKwga6a5SscbdLPO2IExotU1jW8L0Ksdl0Cj2AF67nPQ==} - peerDependencies: - react: ^16.8.5 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.5 || ^17.0.0 || ^18.0.0 - dependencies: - '@babel/runtime': 7.21.5 - css-box-model: 1.2.1 - memoize-one: 5.2.1 - raf-schd: 4.0.3 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - react-redux: 7.2.9(react-dom@18.2.0)(react@18.2.0) - redux: 4.2.1 - use-memo-one: 1.1.3(react@18.2.0) - transitivePeerDependencies: - - react-native - dev: false - - /react-big-calendar@1.8.5(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-cra8WPfoTSQthFfqxi0k9xm/Shv5jWSw19LkNzpSJcnQhP6XGes/eJjd8P8g/iwaJjXIWPpg3+HB5wO5wabRyA==} - peerDependencies: - react: ^16.14.0 || ^17 || ^18 - react-dom: ^16.14.0 || ^17 || ^18 - dependencies: - '@babel/runtime': 7.23.4 - clsx: 1.2.1 - date-arithmetic: 4.1.0 - dayjs: 1.11.9 - dom-helpers: 5.2.1 - globalize: 0.1.1 - invariant: 2.2.4 - lodash: 4.17.21 - lodash-es: 4.17.21 - luxon: 3.4.4 - memoize-one: 6.0.0 - moment: 2.30.1 - moment-timezone: 0.5.44 - prop-types: 15.8.1 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - react-overlays: 5.2.1(react-dom@18.2.0)(react@18.2.0) - uncontrollable: 7.2.1(react@18.2.0) - dev: false - - /react-color@2.19.3(react@18.2.0): - resolution: {integrity: sha512-LEeGE/ZzNLIsFWa1TMe8y5VYqr7bibneWmvJwm1pCn/eNmrabWDh659JSPn9BuaMpEfU83WTOJfnCcjDZwNQTA==} - peerDependencies: - react: '*' - dependencies: - '@icons/material': 0.2.4(react@18.2.0) - lodash: 4.17.21 - lodash-es: 4.17.21 - material-colors: 1.2.6 - prop-types: 15.8.1 - react: 18.2.0 - reactcss: 1.2.3(react@18.2.0) - tinycolor2: 1.6.0 - dev: false - - /react-custom-scrollbars@4.2.1(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-VtJTUvZ7kPh/auZWIbBRceGPkE30XBYe+HktFxuMWBR2eVQQ+Ur6yFJMoaYcNpyGq22uYJ9Wx4UAEcC0K+LNPQ==} - peerDependencies: - react: ^0.14.0 || ^15.0.0 || ^16.0.0 - react-dom: ^0.14.0 || ^15.0.0 || ^16.0.0 - dependencies: - dom-css: 2.1.0 - prop-types: 15.8.1 - raf: 3.4.1 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - dev: false - - /react-datepicker@4.23.0(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-w+msqlOZ14v6H1UknTKtZw/dw9naFMgAOspf59eY130gWpvy5dvKj/bgsFICDdvxB7PtKWxDcbGlAqCloY1d2A==} - peerDependencies: - react: ^16.9.0 || ^17 || ^18 - react-dom: ^16.9.0 || ^17 || ^18 - dependencies: - '@popperjs/core': 2.11.8 - classnames: 2.3.2 - date-fns: 2.30.0 - prop-types: 15.8.1 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - react-onclickoutside: 6.13.0(react-dom@18.2.0)(react@18.2.0) - react-popper: 2.3.0(@popperjs/core@2.11.8)(react-dom@18.2.0)(react@18.2.0) - dev: false - - /react-dom@18.2.0(react@18.2.0): - resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==} - peerDependencies: - react: ^18.2.0 - dependencies: - loose-envify: 1.4.0 - react: 18.2.0 - scheduler: 0.23.0 - - /react-error-boundary@3.1.4(react@18.2.0): - resolution: {integrity: sha512-uM9uPzZJTF6wRQORmSrvOIgt4lJ9MC1sNgEOj2XGsDTRE4kmpWxg7ENK9EWNKJRMAOY9z0MuF4yIfl6gp4sotA==} - engines: {node: '>=10', npm: '>=6'} - peerDependencies: - react: '>=16.13.1' - dependencies: - '@babel/runtime': 7.21.5 - react: 18.2.0 - dev: false - - /react-event-listener@0.6.6(react@18.2.0): - resolution: {integrity: sha512-+hCNqfy7o9wvO6UgjqFmBzARJS7qrNoda0VqzvOuioEpoEXKutiKuv92dSz6kP7rYLmyHPyYNLesi5t/aH1gfw==} - peerDependencies: - react: ^16.3.0 - dependencies: - '@babel/runtime': 7.23.4 - prop-types: 15.8.1 - react: 18.2.0 - warning: 4.0.3 - dev: false - - /react-fast-compare@3.2.2: - resolution: {integrity: sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==} - - /react-hot-toast@2.4.1(csstype@3.1.2)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-j8z+cQbWIM5LY37pR6uZR6D4LfseplqnuAO4co4u8917hBUvXlEqyP1ZzqVLcqoyUesZZv/ImreoCeHVDpE5pQ==} - engines: {node: '>=10'} - peerDependencies: - react: '>=16' - react-dom: '>=16' - dependencies: - goober: 2.1.13(csstype@3.1.2) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - transitivePeerDependencies: - - csstype - dev: false - - /react-i18next@12.2.2(i18next@22.4.15)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-KBB6buBmVKXUWNxXHdnthp+38gPyBT46hJCAIQ8rX19NFL/m2ahte2KARfIDf2tMnSAL7wwck6eDOd/9zn6aFg==} - peerDependencies: - i18next: '>= 19.0.0' - react: '>= 16.8.0' - react-dom: '*' - react-native: '*' - peerDependenciesMeta: - react-dom: - optional: true - react-native: - optional: true - dependencies: - '@babel/runtime': 7.21.5 - html-parse-stringify: 3.0.1 - i18next: 22.4.15 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - dev: false - - /react-is@16.13.1: - resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} - - /react-is@17.0.2: - resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} - dev: false - - /react-is@18.2.0: - resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} - - /react-katex@3.0.1(prop-types@15.8.1)(react@18.2.0): - resolution: {integrity: sha512-wIUW1fU5dHlkKvq4POfDkHruQsYp3fM8xNb/jnc8dnQ+nNCnaj0sx5pw7E6UyuEdLRyFKK0HZjmXBo+AtXXy0A==} - peerDependencies: - prop-types: ^15.8.1 - react: '>=15.3.2 <=18' - dependencies: - katex: 0.16.7 - prop-types: 15.8.1 - react: 18.2.0 - dev: false - - /react-lifecycles-compat@3.0.4: - resolution: {integrity: sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==} - dev: false - - /react-onclickoutside@6.13.0(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-ty8So6tcUpIb+ZE+1HAhbLROvAIJYyJe/1vRrrcmW+jLsaM+/powDRqxzo6hSh9CuRZGSL1Q8mvcF5WRD93a0A==} - peerDependencies: - react: ^15.5.x || ^16.x || ^17.x || ^18.x - react-dom: ^15.5.x || ^16.x || ^17.x || ^18.x - dependencies: - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - dev: false - - /react-overlays@5.2.1(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-GLLSOLWr21CqtJn8geSwQfoJufdt3mfdsnIiQswouuQ2MMPns+ihZklxvsTDKD3cR2tF8ELbi5xUsvqVhR6WvA==} - peerDependencies: - react: '>=16.3.0' - react-dom: '>=16.3.0' - dependencies: - '@babel/runtime': 7.23.4 - '@popperjs/core': 2.11.8 - '@restart/hooks': 0.4.15(react@18.2.0) - '@types/warning': 3.0.3 - dom-helpers: 5.2.1 - prop-types: 15.8.1 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - uncontrollable: 7.2.1(react@18.2.0) - warning: 4.0.3 - dev: false - - /react-popper@2.3.0(@popperjs/core@2.11.8)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-e1hj8lL3uM+sgSR4Lxzn5h1GxBlpa4CQz0XLF8kx4MDrDRWY0Ena4c97PUeSX9i5W3UAfDP0z0FXCTQkoXUl3Q==} - peerDependencies: - '@popperjs/core': ^2.0.0 - react: ^16.8.0 || ^17 || ^18 - react-dom: ^16.8.0 || ^17 || ^18 - dependencies: - '@popperjs/core': 2.11.8 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - react-fast-compare: 3.2.2 - warning: 4.0.3 - - /react-redux@7.2.9(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-Gx4L3uM182jEEayZfRbI/G11ZpYdNAnBs70lFVMNdHJI76XYtR+7m0MN+eAs7UHBPhWXcnFPaS+9owSCJQHNpQ==} - peerDependencies: - react: ^16.8.3 || ^17 || ^18 - react-dom: '*' - react-native: '*' - peerDependenciesMeta: - react-dom: - optional: true - react-native: - optional: true - dependencies: - '@babel/runtime': 7.23.4 - '@types/react-redux': 7.1.25 - hoist-non-react-statics: 3.3.2 - loose-envify: 1.4.0 - prop-types: 15.8.1 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - react-is: 17.0.2 - dev: false - - /react-redux@8.0.5(@types/react-dom@18.2.4)(@types/react@18.2.6)(react-dom@18.2.0)(react@18.2.0)(redux@4.2.1): - resolution: {integrity: sha512-Q2f6fCKxPFpkXt1qNRZdEDLlScsDWyrgSj0mliK59qU6W5gvBiKkdMEG2lJzhd1rCctf0hb6EtePPLZ2e0m1uw==} - peerDependencies: - '@types/react': ^16.8 || ^17.0 || ^18.0 - '@types/react-dom': ^16.8 || ^17.0 || ^18.0 - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 - react-native: '>=0.59' - redux: ^4 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - react-dom: - optional: true - react-native: - optional: true - redux: - optional: true - dependencies: - '@babel/runtime': 7.21.5 - '@types/hoist-non-react-statics': 3.3.1 - '@types/react': 18.2.6 - '@types/react-dom': 18.2.4 - '@types/use-sync-external-store': 0.0.3 - hoist-non-react-statics: 3.3.2 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - react-is: 18.2.0 - redux: 4.2.1 - use-sync-external-store: 1.2.0(react@18.2.0) - dev: false - - /react-refresh@0.14.0: - resolution: {integrity: sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==} - engines: {node: '>=0.10.0'} - dev: true - - /react-router-dom@6.11.1(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-dPC2MhoPeTQ1YUOt5uIK376SMNWbwUxYRWk2ZmTT4fZfwlOvabF8uduRKKJIyfkCZvMgiF0GSCQckmkGGijIrg==} - engines: {node: '>=14'} - peerDependencies: - react: '>=16.8' - react-dom: '>=16.8' - dependencies: - '@remix-run/router': 1.6.1 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - react-router: 6.11.1(react@18.2.0) - dev: false - - /react-router@6.11.1(react@18.2.0): - resolution: {integrity: sha512-OZINSdjJ2WgvAi7hgNLazrEV8SGn6xrKA+MkJe9wVDMZ3zQ6fdJocUjpCUCI0cNrelWjcvon0S/QK/j0NzL3KA==} - engines: {node: '>=14'} - peerDependencies: - react: '>=16.8' - dependencies: - '@remix-run/router': 1.6.1 - react: 18.2.0 - dev: false - - /react-swipeable-views-core@0.14.0: - resolution: {integrity: sha512-0W/e9uPweNEOSPjmYtuKSC/SvKKg1sfo+WtPdnxeLF3t2L82h7jjszuOHz9C23fzkvLfdgkaOmcbAxE9w2GEjA==} - engines: {node: '>=6.0.0'} - dependencies: - '@babel/runtime': 7.0.0 - warning: 4.0.3 - dev: false - - /react-swipeable-views-utils@0.14.0(react@18.2.0): - resolution: {integrity: sha512-W+fXBOsDqgFK1/g7MzRMVcDurp3LqO3ksC8UgInh2P/tKgb5DusuuB1geKHFc6o1wKl+4oyER4Zh3Lxmr8xbXA==} - engines: {node: '>=6.0.0'} - dependencies: - '@babel/runtime': 7.0.0 - keycode: 2.2.1 - prop-types: 15.8.1 - react-event-listener: 0.6.6(react@18.2.0) - react-swipeable-views-core: 0.14.0 - shallow-equal: 1.2.1 - transitivePeerDependencies: - - react - dev: false - - /react-swipeable-views@0.14.0(react@18.2.0): - resolution: {integrity: sha512-wrTT6bi2nC3JbmyNAsPXffUXLn0DVT9SbbcFr36gKpbaCgEp7rX/OFxsu5hPc/NBsUhHyoSRGvwqJNNrWTwCww==} - engines: {node: '>=6.0.0'} - peerDependencies: - react: ^15.3.0 || ^16.0.0 || ^17.0.0 - dependencies: - '@babel/runtime': 7.0.0 - prop-types: 15.8.1 - react: 18.2.0 - react-swipeable-views-core: 0.14.0 - react-swipeable-views-utils: 0.14.0(react@18.2.0) - warning: 4.0.3 - dev: false - - /react-transition-group@4.4.5(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==} - peerDependencies: - react: '>=16.6.0' - react-dom: '>=16.6.0' - dependencies: - '@babel/runtime': 7.21.5 - dom-helpers: 5.2.1 - loose-envify: 1.4.0 - prop-types: 15.8.1 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - dev: false - - /react-virtualized-auto-sizer@1.0.20(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-OdIyHwj4S4wyhbKHOKM1wLSj/UDXm839Z3Cvfg2a9j+He6yDa6i5p0qQvEiCnyQlGO/HyfSnigQwuxvYalaAXA==} - peerDependencies: - react: ^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0-rc - react-dom: ^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0-rc - dependencies: - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - dev: false - - /react-vtree@2.0.4(@types/react-window@1.8.8)(react-dom@18.2.0)(react-window@1.8.10)(react@18.2.0): - resolution: {integrity: sha512-UOld0VqyAZrryF06K753X4bcEVN6/wW831exvVlMZeZAVHk9KXnlHs4rpqDAeoiBgUwJqoW/rtn0hwsokRRxPA==} - peerDependencies: - '@types/react-window': ^1.8.2 - react: ^16.13.1 - react-dom: ^16.13.1 - react-window: ^1.8.5 - dependencies: - '@babel/runtime': 7.23.4 - '@types/react-window': 1.8.8 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - react-window: 1.8.10(react-dom@18.2.0)(react@18.2.0) - dev: false - - /react-window@1.8.10(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-Y0Cx+dnU6NLa5/EvoHukUD0BklJ8qITCtVEPY1C/nL8wwoZ0b5aEw8Ff1dOVHw7fCzMt55XfJDd8S8W8LCaUCg==} - engines: {node: '>8.0.0'} - peerDependencies: - react: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 - react-dom: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 - dependencies: - '@babel/runtime': 7.23.4 - memoize-one: 5.2.1 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - dev: false - - /react18-input-otp@1.1.3(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-55dZMVX61In2ngUhA4Fv0NMY4j5RZjxrJaSOAnJGJmkAhxKB6puVHYEmipyy2+W2CPydFF7pv+0NKzPUA03EVg==} - peerDependencies: - react: 16.2.0 - 18 - react-dom: 16.2.0 - 18 - dependencies: - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - dev: false - - /react@18.2.0: - resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} - engines: {node: '>=0.10.0'} - dependencies: - loose-envify: 1.4.0 - - /reactcss@1.2.3(react@18.2.0): - resolution: {integrity: sha512-KiwVUcFu1RErkI97ywr8nvx8dNOpT03rbnma0SSalTYjkrPYaEajR4a/MRt6DZ46K6arDRbWMNHF+xH7G7n/8A==} - peerDependencies: - react: '*' - dependencies: - lodash: 4.17.21 - react: 18.2.0 - dev: false - - /read-cache@1.0.0: - resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} - dependencies: - pify: 2.3.0 - dev: true - - /readdirp@3.6.0: - resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} - engines: {node: '>=8.10.0'} - dependencies: - picomatch: 2.3.1 - - /redux-thunk@3.1.0(redux@5.0.0): - resolution: {integrity: sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==} - peerDependencies: - redux: ^5.0.0 - dependencies: - redux: 5.0.0 - dev: false - - /redux@4.2.1: - resolution: {integrity: sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==} - dependencies: - '@babel/runtime': 7.21.5 - dev: false - - /redux@5.0.0: - resolution: {integrity: sha512-blLIYmYetpZMET6Q6uCY7Jtl/Im5OBldy+vNPauA8vvsdqyt66oep4EUpAMWNHauTC6xa9JuRPhRB72rY82QGA==} - dev: false - - /regenerator-runtime@0.12.1: - resolution: {integrity: sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg==} - dev: false - - /regenerator-runtime@0.13.11: - resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==} - dev: false - - /regenerator-runtime@0.14.0: - resolution: {integrity: sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==} - - /regexp.prototype.flags@1.5.0: - resolution: {integrity: sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - functions-have-names: 1.2.3 - - /require-directory@2.1.1: - resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} - engines: {node: '>=0.10.0'} - - /requires-port@1.0.0: - resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} - dev: true - - /reselect@5.0.1: - resolution: {integrity: sha512-D72j2ubjgHpvuCiORWkOUxndHJrxDaSolheiz5CO+roz8ka97/4msh2E8F5qay4GawR5vzBt5MkbDHT+Rdy/Wg==} - dev: false - - /resolve-cwd@3.0.0: - resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} - engines: {node: '>=8'} - dependencies: - resolve-from: 5.0.0 - - /resolve-from@4.0.0: - resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} - engines: {node: '>=4'} - - /resolve-from@5.0.0: - resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} - engines: {node: '>=8'} - - /resolve.exports@2.0.2: - resolution: {integrity: sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==} - engines: {node: '>=10'} - - /resolve@1.22.2: - resolution: {integrity: sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==} - hasBin: true - dependencies: - is-core-module: 2.12.0 - path-parse: 1.0.7 - supports-preserve-symlinks-flag: 1.0.0 - - /resolve@2.0.0-next.4: - resolution: {integrity: sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==} - hasBin: true - dependencies: - is-core-module: 2.12.0 - path-parse: 1.0.7 - supports-preserve-symlinks-flag: 1.0.0 - dev: true - - /reusify@1.0.4: - resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} - engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - dev: true - - /rimraf@2.7.1: - resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} - hasBin: true - dependencies: - glob: 7.2.3 - dev: true - - /rimraf@3.0.2: - resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} - hasBin: true - dependencies: - glob: 7.2.3 - dev: true - - /rollup@3.21.7: - resolution: {integrity: sha512-KXPaEuR8FfUoK2uHwNjxTmJ18ApyvD6zJpYv9FOJSqLStmt6xOY84l1IjK2dSolQmoXknrhEFRaPRgOPdqCT5w==} - engines: {node: '>=14.18.0', npm: '>=8.0.0'} - hasBin: true - optionalDependencies: - fsevents: 2.3.2 - dev: true - - /run-parallel@1.2.0: - resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - dependencies: - queue-microtask: 1.2.3 - dev: true - - /rxjs@7.8.1: - resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} - dependencies: - tslib: 2.5.0 - dev: false - - /safe-regex-test@1.0.0: - resolution: {integrity: sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==} - dependencies: - call-bind: 1.0.2 - get-intrinsic: 1.2.1 - is-regex: 1.1.4 - dev: true - - /safer-buffer@2.1.2: - resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - dev: true - - /sass@1.70.0: - resolution: {integrity: sha512-uUxNQ3zAHeAx5nRFskBnrWzDUJrrvpCPD5FNAoRvTi0WwremlheES3tg+56PaVtCs5QDRX5CBLxxKMDJMEa1WQ==} - engines: {node: '>=14.0.0'} - hasBin: true - dependencies: - chokidar: 3.5.3 - immutable: 4.3.4 - source-map-js: 1.0.2 - - /saxes@6.0.0: - resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} - engines: {node: '>=v12.22.7'} - dependencies: - xmlchars: 2.2.0 - dev: true - - /scheduler@0.23.0: - resolution: {integrity: sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==} - dependencies: - loose-envify: 1.4.0 - - /scroll-into-view-if-needed@3.1.0: - resolution: {integrity: sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ==} - dependencies: - compute-scroll-into-view: 3.1.0 - dev: false - - /semver@6.3.0: - resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==} - hasBin: true - - /semver@7.5.1: - resolution: {integrity: sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==} - engines: {node: '>=10'} - hasBin: true - dependencies: - lru-cache: 6.0.0 - - /semver@7.5.4: - resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==} - engines: {node: '>=10'} - hasBin: true - dependencies: - lru-cache: 6.0.0 - dev: true - - /sentence-case@3.0.4: - resolution: {integrity: sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg==} - dependencies: - no-case: 3.0.4 - tslib: 2.5.0 - upper-case-first: 2.0.2 - dev: true - - /shallow-equal@1.2.1: - resolution: {integrity: sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA==} - dev: false - - /shebang-command@2.0.0: - resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} - engines: {node: '>=8'} - dependencies: - shebang-regex: 3.0.0 - - /shebang-regex@3.0.0: - resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} - engines: {node: '>=8'} - - /side-channel@1.0.4: - resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} - dependencies: - call-bind: 1.0.2 - get-intrinsic: 1.2.1 - object-inspect: 1.12.3 - dev: true - - /signal-exit@3.0.7: - resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} - - /sisteransi@1.0.5: - resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} - - /slash@3.0.0: - resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} - engines: {node: '>=8'} - - /slate-history@0.100.0(slate@0.101.4): - resolution: {integrity: sha512-x5rUuWLNtH97hs9PrFovGgt3Qc5zkTm/5mcUB+0NR/TK923eLax4HsL6xACLHMs245nI6aJElyM1y6hN0y5W/Q==} - peerDependencies: - slate: '>=0.65.3' - dependencies: - is-plain-object: 5.0.0 - slate: 0.101.4 - dev: false - - /slate-react@0.101.3(react-dom@18.2.0)(react@18.2.0)(slate@0.101.4): - resolution: {integrity: sha512-KMXK9FLeS7HYhhoVcI8SUi4Qp1I9C1lTQ2EgbPH95sVXfH/vq+hbhurEGIGCe0VQ9Opj4rSKJIv/g7De1+nJMA==} - peerDependencies: - react: '>=18.2.0' - react-dom: '>=18.2.0' - slate: '>=0.99.0' - dependencies: - '@juggle/resize-observer': 3.4.0 - '@types/is-hotkey': 0.1.10 - '@types/lodash': 4.14.202 - direction: 1.0.4 - is-hotkey: 0.2.0 - is-plain-object: 5.0.0 - lodash: 4.17.21 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - scroll-into-view-if-needed: 3.1.0 - slate: 0.101.4 - tiny-invariant: 1.3.1 - dev: false - - /slate@0.101.4: - resolution: {integrity: sha512-8LazZrNDsYFKDg1wpb0HouAfX5Pw/UmOZ/vIrtqD2GSCDZvraOkV2nVJ9Ery8kIlsU1jeybwgcaCy4KkVwfvEg==} - dependencies: - immer: 10.0.3 - is-plain-object: 5.0.0 - tiny-warning: 1.0.3 - dev: false - - /snake-case@3.0.4: - resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==} - dependencies: - dot-case: 3.0.4 - tslib: 2.5.0 - dev: true - - /source-map-js@1.0.2: - resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} - engines: {node: '>=0.10.0'} - - /source-map-support@0.5.13: - resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} - dependencies: - buffer-from: 1.1.2 - source-map: 0.6.1 - - /source-map@0.5.7: - resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==} - engines: {node: '>=0.10.0'} - dev: false - - /source-map@0.6.1: - resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} - engines: {node: '>=0.10.0'} - - /sprintf-js@1.0.3: - resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} - - /stack-utils@2.0.6: - resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} - engines: {node: '>=10'} - dependencies: - escape-string-regexp: 2.0.0 - - /string-length@4.0.2: - resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} - engines: {node: '>=10'} - dependencies: - char-regex: 1.0.2 - strip-ansi: 6.0.1 - - /string-width@4.2.3: - resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} - engines: {node: '>=8'} - dependencies: - emoji-regex: 8.0.0 - is-fullwidth-code-point: 3.0.0 - strip-ansi: 6.0.1 - - /string.prototype.matchall@4.0.8: - resolution: {integrity: sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg==} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.21.2 - get-intrinsic: 1.2.1 - has-symbols: 1.0.3 - internal-slot: 1.0.5 - regexp.prototype.flags: 1.5.0 - side-channel: 1.0.4 - dev: true - - /string.prototype.trim@1.2.7: - resolution: {integrity: sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.21.2 - dev: true - - /string.prototype.trimend@1.0.6: - resolution: {integrity: sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.21.2 - dev: true - - /string.prototype.trimstart@1.0.6: - resolution: {integrity: sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.21.2 - dev: true - - /strip-ansi@6.0.1: - resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} - engines: {node: '>=8'} - dependencies: - ansi-regex: 5.0.1 - - /strip-bom@3.0.0: - resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} - engines: {node: '>=4'} - dev: true - - /strip-bom@4.0.0: - resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} - engines: {node: '>=8'} - - /strip-final-newline@2.0.0: - resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} - engines: {node: '>=6'} - - /strip-json-comments@2.0.1: - resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} - engines: {node: '>=0.10.0'} - dev: true - - /strip-json-comments@3.1.1: - resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} - engines: {node: '>=8'} - - /style-dictionary@3.8.0: - resolution: {integrity: sha512-wHlB/f5eO3mDcYv6WtOz6gvQC477jBKrwuIXe+PtHskTCBsJdAOvL8hCquczJxDui2TnwpeNE+2msK91JJomZg==} - engines: {node: '>=12.0.0'} - hasBin: true - dependencies: - chalk: 4.1.2 - change-case: 4.1.2 - commander: 8.3.0 - fs-extra: 10.1.0 - glob: 7.2.3 - json5: 2.2.3 - jsonc-parser: 3.2.0 - lodash: 4.17.21 - tinycolor2: 1.6.0 - dev: true - - /stylis@4.2.0: - resolution: {integrity: sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==} - dev: false - - /sucrase@3.32.0: - resolution: {integrity: sha512-ydQOU34rpSyj2TGyz4D2p8rbktIOZ8QY9s+DGLvFU1i5pWJE8vkpruCjGCMHsdXwnD7JDcS+noSwM/a7zyNFDQ==} - engines: {node: '>=8'} - hasBin: true - dependencies: - '@jridgewell/gen-mapping': 0.3.3 - commander: 4.1.1 - glob: 7.1.6 - lines-and-columns: 1.2.4 - mz: 2.7.0 - pirates: 4.0.5 - ts-interface-checker: 0.1.13 - dev: true - - /supports-color@5.5.0: - resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} - engines: {node: '>=4'} - dependencies: - has-flag: 3.0.0 - - /supports-color@7.2.0: - resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} - engines: {node: '>=8'} - dependencies: - has-flag: 4.0.0 - - /supports-color@8.1.1: - resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} - engines: {node: '>=10'} - dependencies: - has-flag: 4.0.0 - - /supports-preserve-symlinks-flag@1.0.0: - resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} - engines: {node: '>= 0.4'} - - /svg-parser@2.0.4: - resolution: {integrity: sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==} - dev: true - - /svgo@3.0.2: - resolution: {integrity: sha512-Z706C1U2pb1+JGP48fbazf3KxHrWOsLme6Rv7imFBn5EnuanDW1GPaA/P1/dvObE670JDePC3mnj0k0B7P0jjQ==} - engines: {node: '>=14.0.0'} - hasBin: true - dependencies: - '@trysound/sax': 0.2.0 - commander: 7.2.0 - css-select: 5.1.0 - css-tree: 2.3.1 - csso: 5.0.5 - picocolors: 1.0.0 - dev: true - - /symbol-tree@3.2.4: - resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} - dev: true - - /tailwindcss@3.3.2: - resolution: {integrity: sha512-9jPkMiIBXvPc2KywkraqsUfbfj+dHDb+JPWtSJa9MLFdrPyazI7q6WX2sUrm7R9eVR7qqv3Pas7EvQFzxKnI6w==} - engines: {node: '>=14.0.0'} - hasBin: true - dependencies: - '@alloc/quick-lru': 5.2.0 - arg: 5.0.2 - chokidar: 3.5.3 - didyoumean: 1.2.2 - dlv: 1.1.3 - fast-glob: 3.2.12 - glob-parent: 6.0.2 - is-glob: 4.0.3 - jiti: 1.18.2 - lilconfig: 2.1.0 - micromatch: 4.0.5 - normalize-path: 3.0.0 - object-hash: 3.0.0 - picocolors: 1.0.0 - postcss: 8.4.23 - postcss-import: 15.1.0(postcss@8.4.23) - postcss-js: 4.0.1(postcss@8.4.23) - postcss-load-config: 4.0.1(postcss@8.4.23) - postcss-nested: 6.0.1(postcss@8.4.23) - postcss-selector-parser: 6.0.12 - postcss-value-parser: 4.2.0 - resolve: 1.22.2 - sucrase: 3.32.0 - transitivePeerDependencies: - - ts-node - dev: true - - /test-exclude@6.0.0: - resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} - engines: {node: '>=8'} - dependencies: - '@istanbuljs/schema': 0.1.3 - glob: 7.2.3 - minimatch: 3.1.2 - - /text-table@0.2.0: - resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} - dev: true - - /thenify-all@1.6.0: - resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} - engines: {node: '>=0.8'} - dependencies: - thenify: 3.3.1 - dev: true - - /thenify@3.3.1: - resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} - dependencies: - any-promise: 1.3.0 - dev: true - - /tiny-invariant@1.3.1: - resolution: {integrity: sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==} - dev: false - - /tiny-warning@1.0.3: - resolution: {integrity: sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==} - dev: false - - /tinycolor2@1.6.0: - resolution: {integrity: sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==} - - /tmpl@1.0.5: - resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} - - /to-camel-case@1.0.0: - resolution: {integrity: sha512-nD8pQi5H34kyu1QDMFjzEIYqk0xa9Alt6ZfrdEMuHCFOfTLhDG5pgTu/aAM9Wt9lXILwlXmWP43b8sav0GNE8Q==} - dependencies: - to-space-case: 1.0.0 - dev: false - - /to-fast-properties@2.0.0: - resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} - engines: {node: '>=4'} - - /to-no-case@1.0.2: - resolution: {integrity: sha512-Z3g735FxuZY8rodxV4gH7LxClE4H0hTIyHNIHdk+vpQxjLm0cwnKXq/OFVZ76SOQmto7txVcwSCwkU5kqp+FKg==} - dev: false - - /to-regex-range@5.0.1: - resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} - engines: {node: '>=8.0'} - dependencies: - is-number: 7.0.0 - - /to-space-case@1.0.0: - resolution: {integrity: sha512-rLdvwXZ39VOn1IxGL3V6ZstoTbwLRckQmn/U8ZDLuWwIXNpuZDhQ3AiRUlhTbOXFVE9C+dR51wM0CBDhk31VcA==} - dependencies: - to-no-case: 1.0.2 - dev: false - - /tough-cookie@4.1.3: - resolution: {integrity: sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==} - engines: {node: '>=6'} - dependencies: - psl: 1.9.0 - punycode: 2.3.0 - universalify: 0.2.0 - url-parse: 1.5.10 - dev: true - - /tr46@3.0.0: - resolution: {integrity: sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==} - engines: {node: '>=12'} - dependencies: - punycode: 2.3.0 - dev: true - - /tree-kill@1.2.2: - resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} - hasBin: true - dev: true - - /ts-interface-checker@0.1.13: - resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} - dev: true - - /ts-jest@29.1.1(@babel/core@7.21.8)(babel-jest@29.6.2)(jest@29.5.0)(typescript@4.9.5): - resolution: {integrity: sha512-D6xjnnbP17cC85nliwGiL+tpoKN0StpgE0TeOjXQTU6MVCfsB4v7aW05CgQ/1OywGb0x/oy9hHFnN+sczTiRaA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - hasBin: true - peerDependencies: - '@babel/core': '>=7.0.0-beta.0 <8' - '@jest/types': ^29.0.0 - babel-jest: ^29.0.0 - esbuild: '*' - jest: ^29.0.0 - typescript: '>=4.3 <6' - peerDependenciesMeta: - '@babel/core': - optional: true - '@jest/types': - optional: true - babel-jest: - optional: true - esbuild: - optional: true - dependencies: - '@babel/core': 7.21.8 - babel-jest: 29.6.2(@babel/core@7.21.8) - bs-logger: 0.2.6 - fast-json-stable-stringify: 2.1.0 - jest: 29.5.0(@types/node@18.16.9) - jest-util: 29.5.0 - json5: 2.2.3 - lodash.memoize: 4.1.2 - make-error: 1.3.6 - semver: 7.5.4 - typescript: 4.9.5 - yargs-parser: 21.1.1 - dev: true - - /ts-node-dev@2.0.0(@types/node@18.16.9)(typescript@4.9.5): - resolution: {integrity: sha512-ywMrhCfH6M75yftYvrvNarLEY+SUXtUvU8/0Z6llrHQVBx12GiFk5sStF8UdfE/yfzk9IAq7O5EEbTQsxlBI8w==} - engines: {node: '>=0.8.0'} - hasBin: true - peerDependencies: - node-notifier: '*' - typescript: '*' - peerDependenciesMeta: - node-notifier: - optional: true - dependencies: - chokidar: 3.5.3 - dynamic-dedupe: 0.3.0 - minimist: 1.2.8 - mkdirp: 1.0.4 - resolve: 1.22.2 - rimraf: 2.7.1 - source-map-support: 0.5.13 - tree-kill: 1.2.2 - ts-node: 10.9.1(@types/node@18.16.9)(typescript@4.9.5) - tsconfig: 7.0.0 - typescript: 4.9.5 - transitivePeerDependencies: - - '@swc/core' - - '@swc/wasm' - - '@types/node' - dev: true - - /ts-node@10.9.1(@types/node@18.16.9)(typescript@4.9.5): - resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} - hasBin: true - peerDependencies: - '@swc/core': '>=1.2.50' - '@swc/wasm': '>=1.2.50' - '@types/node': '*' - typescript: '>=2.7' - peerDependenciesMeta: - '@swc/core': - optional: true - '@swc/wasm': - optional: true - dependencies: - '@cspotcode/source-map-support': 0.8.1 - '@tsconfig/node10': 1.0.9 - '@tsconfig/node12': 1.0.11 - '@tsconfig/node14': 1.0.3 - '@tsconfig/node16': 1.0.4 - '@types/node': 18.16.9 - acorn: 8.8.2 - acorn-walk: 8.2.0 - arg: 4.1.3 - create-require: 1.1.1 - diff: 4.0.2 - make-error: 1.3.6 - typescript: 4.9.5 - v8-compile-cache-lib: 3.0.1 - yn: 3.1.1 - dev: true - - /ts-results@3.3.0: - resolution: {integrity: sha512-FWqxGX2NHp5oCyaMd96o2y2uMQmSu8Dey6kvyuFdRJ2AzfmWo3kWa4UsPlCGlfQ/qu03m09ZZtppMoY8EMHuiA==} - dev: false - - /tsconfig-paths-jest@0.0.1: - resolution: {integrity: sha512-YKhUKqbteklNppC2NqL7dv1cWF8eEobgHVD5kjF1y9Q4ocqpBiaDlYslQ9eMhtbqIPRrA68RIEXqknEjlxdwxw==} - dev: true - - /tsconfig@7.0.0: - resolution: {integrity: sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw==} - dependencies: - '@types/strip-bom': 3.0.0 - '@types/strip-json-comments': 0.0.30 - strip-bom: 3.0.0 - strip-json-comments: 2.0.1 - dev: true - - /tslib@1.14.1: - resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} - dev: true - - /tslib@2.5.0: - resolution: {integrity: sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==} - - /tsutils@3.21.0(typescript@4.9.5): - resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} - engines: {node: '>= 6'} - peerDependencies: - typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' - dependencies: - tslib: 1.14.1 - typescript: 4.9.5 - dev: true - - /type-check@0.4.0: - resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} - engines: {node: '>= 0.8.0'} - dependencies: - prelude-ls: 1.2.1 - dev: true - - /type-detect@4.0.8: - resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} - engines: {node: '>=4'} - - /type-fest@0.20.2: - resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} - engines: {node: '>=10'} - dev: true - - /type-fest@0.21.3: - resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} - engines: {node: '>=10'} - - /typed-array-length@1.0.4: - resolution: {integrity: sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==} - dependencies: - call-bind: 1.0.2 - for-each: 0.3.3 - is-typed-array: 1.1.10 - dev: true - - /typescript@4.9.5: - resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==} - engines: {node: '>=4.2.0'} - hasBin: true - dev: true - - /unbox-primitive@1.0.2: - resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} - dependencies: - call-bind: 1.0.2 - has-bigints: 1.0.2 - has-symbols: 1.0.3 - which-boxed-primitive: 1.0.2 - dev: true - - /uncontrollable@7.2.1(react@18.2.0): - resolution: {integrity: sha512-svtcfoTADIB0nT9nltgjujTi7BzVmwjZClOmskKu/E8FW9BXzg9os8OLr4f8Dlnk0rYWJIWr4wv9eKUXiQvQwQ==} - peerDependencies: - react: '>=15.0.0' - dependencies: - '@babel/runtime': 7.23.4 - '@types/react': 18.2.6 - invariant: 2.2.4 - react: 18.2.0 - react-lifecycles-compat: 3.0.4 - dev: false - - /universalify@0.2.0: - resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} - engines: {node: '>= 4.0.0'} - dev: true - - /universalify@2.0.0: - resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==} - engines: {node: '>= 10.0.0'} - dev: true - - /unsplash-js@7.0.19: - resolution: {integrity: sha512-j6qT2floy5Q2g2d939FJpwey1yw/GpQecFiSouyJtsHQPj3oqmqq3K4rI+GF8vU1zwGCT7ZwIGQd2dtCQLjYJw==} - engines: {node: '>=10'} - dev: false - - /update-browserslist-db@1.0.11(browserslist@4.21.5): - resolution: {integrity: sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==} - hasBin: true - peerDependencies: - browserslist: '>= 4.21.0' - dependencies: - browserslist: 4.21.5 - escalade: 3.1.1 - picocolors: 1.0.0 - - /upper-case-first@2.0.2: - resolution: {integrity: sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg==} - dependencies: - tslib: 2.5.0 - dev: true - - /upper-case@2.0.2: - resolution: {integrity: sha512-KgdgDGJt2TpuwBUIjgG6lzw2GWFRCW9Qkfkiv0DxqHHLYJHmtmdUIKcZd8rHgFSjopVTlw6ggzCm1b8MFQwikg==} - dependencies: - tslib: 2.5.0 - dev: true - - /uri-js@4.4.1: - resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} - dependencies: - punycode: 2.3.0 - dev: true - - /url-parse@1.5.10: - resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} - dependencies: - querystringify: 2.2.0 - requires-port: 1.0.0 - dev: true - - /use-memo-one@1.1.3(react@18.2.0): - resolution: {integrity: sha512-g66/K7ZQGYrI6dy8GLpVcMsBp4s17xNkYJVSMvTEevGy3nDxHOfE6z8BVE22+5G5x7t3+bhzrlTDB7ObrEE0cQ==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - dependencies: - react: 18.2.0 - dev: false - - /use-sync-external-store@1.2.0(react@18.2.0): - resolution: {integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - dependencies: - react: 18.2.0 - dev: false - - /utf8@3.0.0: - resolution: {integrity: sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==} - dev: false - - /util-deprecate@1.0.2: - resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - dev: true - - /uuid@9.0.0: - resolution: {integrity: sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==} - hasBin: true - dev: true - - /v8-compile-cache-lib@3.0.1: - resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} - dev: true - - /v8-to-istanbul@9.1.0: - resolution: {integrity: sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==} - engines: {node: '>=10.12.0'} - dependencies: - '@jridgewell/trace-mapping': 0.3.18 - '@types/istanbul-lib-coverage': 2.0.4 - convert-source-map: 1.9.0 - - /valtio@1.12.1(@types/react@18.2.6)(react@18.2.0): - resolution: {integrity: sha512-R0V4H86Xi2Pp7pmxN/EtV4Q6jr6PMN3t1IwxEvKUp6160r8FimvPh941oWyeK1iec/DTsh9Jb3Q+GputMS8SYg==} - engines: {node: '>=12.20.0'} - peerDependencies: - '@types/react': '>=16.8' - react: '>=16.8' - peerDependenciesMeta: - '@types/react': - optional: true - react: - optional: true - dependencies: - '@types/react': 18.2.6 - derive-valtio: 0.1.0(valtio@1.12.1) - proxy-compare: 2.5.1 - react: 18.2.0 - use-sync-external-store: 1.2.0(react@18.2.0) - dev: false - - /vite-plugin-svgr@3.2.0(vite@4.3.5): - resolution: {integrity: sha512-Uvq6niTvhqJU6ga78qLKBFJSDvxWhOnyfQSoKpDPMAGxJPo5S3+9hyjExE5YDj6Lpa4uaLkGc1cBgxXov+LjSw==} - peerDependencies: - vite: ^2.6.0 || 3 || 4 - dependencies: - '@rollup/pluginutils': 5.0.2 - '@svgr/core': 7.0.0 - '@svgr/plugin-jsx': 7.0.0 - vite: 4.3.5(@types/node@18.16.9)(sass@1.70.0) - transitivePeerDependencies: - - rollup - - supports-color - dev: true - - /vite@4.3.5(@types/node@18.16.9)(sass@1.70.0): - resolution: {integrity: sha512-0gEnL9wiRFxgz40o/i/eTBwm+NEbpUeTWhzKrZDSdKm6nplj+z4lKz8ANDgildxHm47Vg8EUia0aicKbawUVVA==} - engines: {node: ^14.18.0 || >=16.0.0} - hasBin: true - peerDependencies: - '@types/node': '>= 14' - less: '*' - sass: '*' - stylus: '*' - sugarss: '*' - terser: ^5.4.0 - peerDependenciesMeta: - '@types/node': - optional: true - less: - optional: true - sass: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true - dependencies: - '@types/node': 18.16.9 - esbuild: 0.17.19 - postcss: 8.4.23 - rollup: 3.21.7 - sass: 1.70.0 - optionalDependencies: - fsevents: 2.3.2 - dev: true - - /void-elements@3.1.0: - resolution: {integrity: sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==} - engines: {node: '>=0.10.0'} - dev: false - - /w3c-xmlserializer@4.0.0: - resolution: {integrity: sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==} - engines: {node: '>=14'} - dependencies: - xml-name-validator: 4.0.0 - dev: true - - /walker@1.0.8: - resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} - dependencies: - makeerror: 1.0.12 - - /warning@4.0.3: - resolution: {integrity: sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==} - dependencies: - loose-envify: 1.4.0 - - /webidl-conversions@7.0.0: - resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} - engines: {node: '>=12'} - dev: true - - /whatwg-encoding@2.0.0: - resolution: {integrity: sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==} - engines: {node: '>=12'} - dependencies: - iconv-lite: 0.6.3 - dev: true - - /whatwg-mimetype@3.0.0: - resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==} - engines: {node: '>=12'} - dev: true - - /whatwg-url@11.0.0: - resolution: {integrity: sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==} - engines: {node: '>=12'} - dependencies: - tr46: 3.0.0 - webidl-conversions: 7.0.0 - dev: true - - /which-boxed-primitive@1.0.2: - resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} - dependencies: - is-bigint: 1.0.4 - is-boolean-object: 1.1.2 - is-number-object: 1.0.7 - is-string: 1.0.7 - is-symbol: 1.0.4 - dev: true - - /which-typed-array@1.1.9: - resolution: {integrity: sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==} - engines: {node: '>= 0.4'} - dependencies: - available-typed-arrays: 1.0.5 - call-bind: 1.0.2 - for-each: 0.3.3 - gopd: 1.0.1 - has-tostringtag: 1.0.0 - is-typed-array: 1.1.10 - dev: true - - /which@2.0.2: - resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} - engines: {node: '>= 8'} - hasBin: true - dependencies: - isexe: 2.0.0 - - /word-wrap@1.2.3: - resolution: {integrity: sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==} - engines: {node: '>=0.10.0'} - dev: true - - /wrap-ansi@7.0.0: - resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} - engines: {node: '>=10'} - dependencies: - ansi-styles: 4.3.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - - /wrappy@1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - - /write-file-atomic@4.0.2: - resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - dependencies: - imurmurhash: 0.1.4 - signal-exit: 3.0.7 - - /ws@8.14.1: - resolution: {integrity: sha512-4OOseMUq8AzRBI/7SLMUwO+FEDnguetSk7KMb1sHwvF2w2Wv5Hoj0nlifx8vtGsftE/jWHojPy8sMMzYLJ2G/A==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: '>=5.0.2' - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - dev: true - - /xml-name-validator@4.0.0: - resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==} - engines: {node: '>=12'} - dev: true - - /xmlchars@2.2.0: - resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} - dev: true - - /xtend@4.0.2: - resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} - engines: {node: '>=0.4'} - dev: true - - /y-protocols@1.0.6(yjs@13.6.1): - resolution: {integrity: sha512-vHRF2L6iT3rwj1jub/K5tYcTT/mEYDUppgNPXwp8fmLpui9f7Yeq3OEtTLVF012j39QnV+KEQpNqoN7CWU7Y9Q==} - engines: {node: '>=16.0.0', npm: '>=8.0.0'} - peerDependencies: - yjs: ^13.0.0 - dependencies: - lib0: 0.2.88 - yjs: 13.6.1 - dev: false - - /y18n@5.0.8: - resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} - engines: {node: '>=10'} - - /yallist@3.1.1: - resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} - - /yallist@4.0.0: - resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} - - /yaml@1.10.2: - resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} - engines: {node: '>= 6'} - dev: false - - /yaml@2.2.2: - resolution: {integrity: sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA==} - engines: {node: '>= 14'} - dev: true - - /yargs-parser@21.1.1: - resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} - engines: {node: '>=12'} - - /yargs@17.7.2: - resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} - engines: {node: '>=12'} - dependencies: - cliui: 8.0.1 - escalade: 3.1.1 - get-caller-file: 2.0.5 - require-directory: 2.1.1 - string-width: 4.2.3 - y18n: 5.0.8 - yargs-parser: 21.1.1 - - /yjs@13.6.1: - resolution: {integrity: sha512-IyyHL+/v9N2S4YLSjGHMa0vMAfFxq8RDG5Nvb77raTTHJPweU3L/fRlqw6ElZvZUuHWnax3ufHR0Tx0ntfG63Q==} - engines: {node: '>=16.0.0', npm: '>=8.0.0'} - dependencies: - lib0: 0.2.74 - dev: false - - /yn@3.1.1: - resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} - engines: {node: '>=6'} - dev: true - - /yocto-queue@0.1.0: - resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} - engines: {node: '>=10'} - -settings: - autoInstallPeers: true - excludeLinksFromLockfile: false diff --git a/frontend/appflowy_tauri/postcss.config.cjs b/frontend/appflowy_tauri/postcss.config.cjs deleted file mode 100644 index 12a703d900..0000000000 --- a/frontend/appflowy_tauri/postcss.config.cjs +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = { - plugins: { - tailwindcss: {}, - autoprefixer: {}, - }, -}; diff --git a/frontend/appflowy_tauri/public/google_fonts/Poppins/OFL.txt b/frontend/appflowy_tauri/public/google_fonts/Poppins/OFL.txt deleted file mode 100644 index 246c977c9f..0000000000 --- a/frontend/appflowy_tauri/public/google_fonts/Poppins/OFL.txt +++ /dev/null @@ -1,93 +0,0 @@ -Copyright 2020 The Poppins Project Authors (https://github.com/itfoundry/Poppins) - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -http://scripts.sil.org/OFL - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-Black.ttf b/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-Black.ttf deleted file mode 100644 index 71c0f995ee64396f29a3d9ef283b5050f45d6e0f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 151396 zcmdSCcX(CB6F;?Mklv&VNJkM6q}wZCgWUHsd(J(%;#YsZzvp@Xc*(Ofvu$>EcedWy1LKUb25cHr zSYCQsm#!8MOB2R;AA}~Q=jG=6^$vNNvEU@e8gEO_@0Idohq+@I>-!30p%ZiS+eTgd zZE670&qw;fLo13$$67kL!T%d$;`H$HNyGSl*BgvYtIzm}J|jwtOPYKdG8OU2!{SCD z!08E-3+VfBAtNekCjL}&`y0kwH!{{}R(aLX;aRby+~?;i0wSc6~CT!y--!R$p=pH(t5e1iD{Ggbnpz(?`k zwQsQ!%g6__f4gR>p%D9jYLE<3xtYDOr$v!-$Gc2XzN8fAm#scQ_@YPL;RmxHJa4H{ ztKfA2SM48hf@JcaVD*?6Yl{uD7l6afww<$zW%Ii=7U%i?@3%8C2IoH91Idfw}P&@Y!7393axHxl^7eq78SX?Y_mgbfSOSGk(rHiGTCEJo`8DuH7R9Qw_s{Nnz-|7F0|L*|}0^9<; z1N;Mm0zv~K0@??p1*{5K8?Y^4XJF&Npuo_;@E{Rn4ss514RR0i4)PCb6_gP)IHZ^yIvAU;CMc3*f&*$zUs^N@{OOqTi#?yQQTi z(h_UwXi2wphirW;gDu0PY}5RA`2XzxTR{DQ#sOXdz5xMJwsyK~TmL4T3uJ3z$W|g{ z^;4||F{ks~riSJ6eJh$PWTgp1aEA79POAu~sV|I8XOc2Cyk-h*GAe{IOv zuTHXV?ALbpeuS^duip3GyxHTX&yCwRu7B~w^&{60T|apJ!1cY?w_bn$`l>58uO~Bh zJ?h%$*G^nJcJ08`pRfLK_4d_US8rUsdiCBskk7m=^Q_`;y0Ayt6z0x6m?y?g6X>H4 z#*RNmQ82VJ5M##)`svI3*t6_8He2~#7S))2P1>wl;CoAT)oISWmEUw)u!f2)jlvR? z^?JB!m&U{#l~sB;MuIY1r|Yp$WrR*!*Z`%zPCK#o;kDIE{3Yrx9z& zF}fw~!W{V!oo>w5v5guHok(IeI?YknLY)?@C2OtI3Mj^sSvgXcvTF2EF{m;&9=-q; zh1dWVEn%F5?fx-sF{?nI5|6RqQHfbGja9L6YzTOlvNi~Qq`O+BrbBYzgRU&^6#hJ)O_Gfo}l#Qy)?f)N#)G3kTAOLYbRn(|RQm5|RtBlkBhM zQooYC#n7x`(9{~5W&}XuGAReeQn|^jhfG<6HVOE@Y@vtSm0G(5<&b99Km)0k!yr9r zSqWNV5~Lpre+A1yDjEgUQr&?`djnW=XmhEQo#KYWQ7^TSV0B>Ag`w6W8j7)Tj`zZh^TKuhJa4k+WV=>zQ2Q*2$H zAuNIQL@kD+*3|yh|B^EmH5d)~sFuTJAJA+NA)VIC)G7~`oQL#PY$VE{ksE-X94C90 zT7!DO`QLhTIQWwe(mY}7fwnU2uf5aup|V=Y`8kwnf8}foyTeoYc)m+GiBaN1rG?T@ zc}@969iWa-Yt(7#OX@cDib)Ua{CgwcMtO|}G1^Z)TF4%m?j^3yLfl<9`Aj?`<_p_PqohupFe!VeTVxl_x;At z+b`Ac8NV%lwf-Ibhx)JdzuL4_(-BQyZ@RMSg#f>R%z&o?js=DVJ{@>FC4gr#D~P{BDc<7PDI%YU$kag;s4_t#9qvdU)#tZ35bqwK*G}8@{@2 zNZUDWe~K6ou|CoBKrs@3g10bLXti&vibP)Hdnu42| zu1uHA5t+NYyLRu{{q^o=vpli}X06HkIXgPLbM}+j-{lnMY|U+uJ2m%6UTogvyaPSF zdkpFEe$NIy`}bU*?~*?{|8PNM!D|I~dL{Il-s@yxXyJ&$&4t$9-FmO<6VzvBU*5N( z@9BOC{f6{g+wX4w?)_ise|JFJ0iy<792hll%D^uMg$=40biByDsI=(9;IzRTihYV7 zDZV(wcgWBo2ZoBF1BdP^v6K{+3@KS#@=a+}>B3?D!=4`Y&G6je`$zO1vAoQ$tg`Iz zNSBe5NA4dLKWb5VaQUk8KPz%7wpO;N++5Y7YRl+GqaPoAbd2YiN5|Z*&ab{WwruR) zn%J7BYc7vV8n<mT)e^yx=GdGy+2-5=XL%{;Bow8M`VKE7#sdX&`RTq-FMs;infWsp&b<9hmuGg)@}2eAvkuRWe%AV2 z-{;mm@BRFk=RcbrJA2{mA74m$;gJ`PzL@snv6s?cTJX~MFa0s6$DH?GZv67pm(RS? z?v-70J?D;|yKi39yalgXUafrf(EOzN2VV<*ZS4Y=1=ALM_Ij(==e~Y*VdsT=79M*e z}qRd4b7P~KgdGS|^?=I=PWcQo=&5AcSz2*Maz_(5;^;CUD1mvvY+ zciGM5Im_oRzq}%F#hjJqmCvrUuIj#O!KyD;7pz{qrv93-YkqsX+uJMN{$XvqwM*94 zzSH-elj{Q4y|nJuyD9H3c#pqV_TK*Y6W?FFzTx_q^`+~p)<3&``v#v4VH*Z+Sia%? z4PR}H+?cqrV&mHzH*dWALCOc!AFTf1r%e%?#&0^h>CC1po11R#vw7v_+AZ;0p4)PL zYyGX!Tl;N&Z0oYEA8q|%o9DKqZ6mkM+qQSx_uGB8XKWw0ed+d7+pRm=?C8JanH`&V z+}x?|^xB!R^QD~^b_MPV-_?Cr*{)}IE#CFct`Bw{-F0y{-|ezHZg+zn)#~t5%eBbdC$1fhgef*c> z))P)AJWr&a$UV{j#PAbiPds+w*%Ql8+&b~|iQ1EnCp}IEolHGB=j5W3Yff%Dx%cGp zlNU}!oGL#x@zjh{FP>U>YSpQYr}msWcG~f@$LXNc;iuc5?s~fC=|@j*JALr<>C=}_ ze|`Ginb0#)XF8q9JX3gP$eEYUoI7*%%y(ygKWjSca@Ox`i?bunjy}8k><4E*Jp1w4 z&(7XF`_nnUbH(Q>&P_V^V{!xtpIvebVWZ%ufnG8S+WxCzC%}_{s54E`0Lk zr;R?1|1|Z}+)pb$edg0upC0`5)_LW;-}%_{$>+1rk2(M5`FGB5J%8Z*sq>$o|LXkR z&-iEUK70DJmp@zl+1bzjxM05Edcprf%L|h)9KG=Ah3gl7xEOG;^~F9HOD>MSIOXEZ zi?3WCb<;>~}f)a<9v!m&adz^zv($-@Sb9^1Um5S0b*YU&+5xbY;|)30I!D^1_wZ zudKYX;mYnSN3VQ(<;Intu2{dQ|3#B8!oNuUqW2eLzIf$}_rBP3HS}uS)vi|yufBM7 z&(-g)-o55^t^2jf*VbJ-bnWuB->;jmH@xm~z3uf$*I&E79B0=n*YDkEcq92n_KhJo zCf#`Q#{3(bZd|`{?Wx)pLO;#S978Mo%%dh^yh zw|=_qbGz&9Nw?SXJ900<*cxCsC6`63Z?I^QY`r60TkBmV+j>_8CZ{|P_nP&t047Ii zWqJxP&}g1(y{oy}pBbxlh8qF*4BQ8BKDxu*Q>`=4vsNK3Ze?mScn|Aa%6jlQ1=k<^ zH^7~TdjNk&xDjyGaQO&(0&XE(1l)XtkxU;0w`R>nDd;a*jFJTm9t!bz7kDyjsZ;^u zUO_YhMm{kQxSGX?gTN{9X8^B(dxAwOLxFAb^aG9Zjq->mc!=e2ufw4%PVormln3&P z7I3ZLoZ%jlSM)-Fz~g8mh> zF0V*ok)k>LcJj#b!3%ABQ?_p$Yaxc-0-j6E+lv**>1S(He|nVMO@f}19u#m<;n1%H~#dNA9* z&HiT}o`Veg*`*0ettq<2B&aMxQ{=;6lWsuT2Ai7XWWW_|L!>;F@aAjCxDg4SwV?E5LW*o&p^T90L3e z@IAP#EYe{ti!^m)k?IBD!`8bFPKe(H3>h3+TJKs$!e558&`0Thgx^hbrdJR~4*Dh? zl|$|t9LATr5BPC7j0<%V+%L$t0}eWG8Vvq^dYajw(f6iVa8zG%YY~U>XX*nCdDNS5 z!~=QNE1ENTBfd504}oKJM`dF^knS1qEk=2N!jHZ%MIkN5zlt_e|A2cDVKae016~T( zMsw;RVCbC*a+w+-{&(P1II7E&=r8C1IpQZ!f6^yY2jG=(gj22e)Dys6v@jF(@f+ZC z0PZH-M7Yn9hsxOqmjwS+VAREI)$t-=6CCPKPC=Wf8?1NK0^m(>n5WE708==eIS=XL zK%;NXtAH^_sLvwI4Vdb12H_~vj5an?e?_t8>Q8WwVvKFGen(Eh907g2LzwziOwgR^ zEXtn_`lR)qAYCxA81pU`V`{@<)NOD*KyOD|pF%m1P4*eJYdG8|D3AILeMse-;(_nO zp>NbtaL_Nc6z&1SsC@_rB3&W)xx@dq=1eCMM(#<3w?vv_pwS-=oq#P!vluuV;V(jG zQHFzxFvKZe0b^{beu#$*q$_Fw=q|u$LlyO*F{D6msVuXH=BPhSGc;!wz?le#PLngO z1I~f-2mK@RK^Ai6m*5`?I#(|@AAa<)3c1y2IMj`FTOAI}k^VGj=rHvewT}sNn#oUh z2U$yXH{36XBfjVh^$<2g;P=g&`m55w+zHaHs>7WBL;5E(430Epf_;JFs7JhLXWn?{fV#&_!j}UhTEyf&jsBAdEk_L z;JsYKstG(@;eTJlB)j=d(5M?Z(h2oC!q5)tM7RcU?Ge67a}HF8kKl$Q9Q~~xM_Tl; zx*gaB;eo(6;U5M%3m9XA9E}MTWtv8#z7e2lj(822+B*?ryk!rJf?xWhkzePtvo z5Mj)X7qT`Y3Ai)uL#&tn>wZM0f1IWApO~AdMt;KH%vmjCSqlBiw-)=8pP0Y64cr>8 zGu)5x|AOE9Qb6ZGHlqJvQOagGCpb66KcnMCNHd1{iV;Y6f`yAe@!Mt=;?Z;B0^E4S zyR#ytk{t$45cjZuy3Znc8q%+VEUOS-EU_DSxbesJ@U61^Af9BsES71qs4+;3a4|*C zhy45u{He^HZ)DDh3m1!-1-fJv3sLrR%*AG;xxqT(msG-W;t+o4!QP#t?UBz-tYl3^ zf7X#?#qTF&A|Jfo#P2W)^XFF)KN9`p$l_(&TM@PozoGodJY}D(V9oiT6)BcPRT|EbC6?QybW0=qAn`Zd7mVyWM2lkc`wu|AtAA zQI6DS!gn-3+Hec%aSHe-@D1Qwz*gW|%|YL4(4YR`Pr3))m+f!W^bq^`D52`%TR>aT z-!;$~j16iRB~iyVKaCf(ksLR6SZ#v7tA$(`LsE}->e%L|aRuF$;H&pe9AFGIzjzHn8qo_$ioVI;5z z*at40x$-06&oM^!Ge=w54`cLYgzQUunMk&`Sv-CdjYk<#vYZjDm6!=xXR;(Q4s+ti zIDa35-;0$f_@%+5LLP*}Md3VoQ}JNVN;Jwo09i1fDH~9CPZlU{pdQmf3)J~1#AjkG z|AD-iTWJg{_gP~liv{uo>miymJHi?#!2Gu}=J5HX38x zMTRFyx!mMj(FF37BVD4ov=HZ28b|-d?ZBLAJ9qZP@6e60DsnhSmVsZZm9(x|1uUYZ zbbJ&G0>sFXb`}g)0Rn#}J0Ay)X$Y$+#bFv@=HemaD_FQa98x}^f`!^cq$EycwL!VS zr9nZx3rb5!K-@U&Ot|9eFa@waeoueFVWjv&+!5c4FU2)+$sx(1z4%0&6h{!gU+i*d z54=UJ7i+}|u|zD8@vn&4Vy5}9d9Qhgd54&8USnP+sVQQ-7%fJM67w`MNc1sRnn%FK zAFYvRbVf;8hY$`An@V%xKzJqTvMez6d8otcr#TW7U zus?ZD{R4mB^QZVUKAG3>O7)0(1oj_8_yAtWN2n{*6+Dk;su z;!(V{n#DtT0OR~H%r7|q+R6ccBB->p3kxLJoEgXeu&$5k@#HOi&Trw~&;qU#+bi1P(XH+Lf1G@YP&hBQ)5 zBU=1s-3Oj?t!1F^SQ9{>v%)@$zi534@Lg(WzL{E>-?eT9oMn9x@LcWhfG^hmLL9LF zBo1gLtRLVB4rotsK)D$0XivUbhTkRGz~L(zo2Wf{71kog_%JO}2SCP0X?&=eYMR^H`YvqP=SvjwqR*oqLl|8DrvQ62jtW#DgZz&6v zdCH5*EM*3$h03GKM5S6OSB5Er@k?fblA~lODN08rPKi)jDj|x$;-$DM_3>wh>6zWA z)J7zYM|~`=*G8j|BT-Bj#>f*TH1cTFX=4u7To`X6$Trr5%4@X|WgAB{iuhg{KicTA zjU8ZP+!$j9qlLx_PoNQ^kB`q_9mn~fwM)_Gsn$VgdqK3x!MYNCJ5cs;g!L?94_c8@ zO}FkONa4z-5=L9|Ku25O1>9Ck<0-+~TSBB)wo`iOEYh1=N}f#e$+f`|lERgRlIJOz zrk_k7R{I(9#!A^@WsAK*QYlAdnn0?rnk}VjMJbi`wG&a_gj$RezypAR)EY`DQ1d(*#fs@3z%gKI8ccqvvwJJ_Ec>NXp^KD)fR)!uicCA zowcJtcccE|2Z`nfYDc0hM=57tDgP!Z|4&kWoC^_~P>YsVR!|z;cTpRrQp$Rt5^Zix z>CIV`-t@AJJ!4&hG&^ODK9(HvCAZVE$4^rT_9_II5mX$lrI2KW)d{qz_5#AgsXh61 zYb4+zYa76w)*gTdNXLX??FITZYirPH)FxsGmB_Ex&I4Rji#Z12&@OOBEI5-sm`FC< zIa9geBpdici@+c9gFmzg{HYBUswwzCNqi_hdx3?p=CIZZhviQMENo&~JJ_GSzdm4_*fw^RU1PV{m+X7k2L8hCu;01h3O92v z9>QB;Oh)ncyaP{!9cFh}JLd3Q-h=n#`Meiaygs}y?+5$L0lb(G<)ir+*j7Bor}4-6 zbp8Z?661X)pT%F~FY{OUT>b`TfW>?jtP$7q4=_7y#vHMm@8=)!Bm5XY%P;X8{5HRX z+2lSfJr$t}lQ0WM*lIKouEJe-3R>a)guiGi0z{w)6=9-&D(1u>j50o)q!yY6GT2jF3^CS8U9iYw8s+)3O z&esjqhM2AIt3s`(+yit&I{aY{zmK`RA^Bx2&FxwqZKj7G>4}@>H~8x7z(ah&!=A5= ztG6rx*0w0e4HEn)*Nq?L%5ejJT0PEd>jzd3inaaSK$?Rx>@+A^S$Vl^Png<@!zKBmtv40lBSjQ2IPVS)VgTnhUmA0=+ArLi9Q9U93&N$v=Mj@D3bXY=+lTH9wY;W zt%JLPvQHyU`#XrXSqF}*B#-lGEs_wL1Go)oXdSUVvfBYuLO;3t95c|5I-9vZW4smmgm=Cxbzus|EBY}BY9W0iE-Ys4S{2e!OTsWc$KGfo<`5HR;atoib1|p3W>>k1 z^@J7sdNzS?6#aOxJU_wO8rH_-uuH@#l;f^(Aof>9bu*7?4=jkeX)<<->s6-wq1;iv z$KRLuyQW-HK2c68N0j}_E@g|dURjIS6~IfB1~@df@ah_m9jIE=r&u-)G)HVO2dSSA*U`QlqK2d(>@cuGve-(*oED#Zvf zL<|sxuw~B^nIa8V>;foUYx5OEs~rd`cO^Ym!W4og*^-zGK-ZI{n@16CmThQyNv1p_ z;bMZ~V@W?P;b|GNU&3Vs6&Hg1qNEi`%eq51DX)yh&VE0%31Xs{Bp#9TauVj||K-~ML%5cfTcxI2cgfyrDBp_&!76$(n}d05IOel5 z++mFp<#=PI-JtzyHJAF{lDpU=g8m6ZYW_ydJmU^^Oy-&l~WD+?hAxF5Hzj z=5E}bdvH(e)SGZ`yqEIjet4JEln3xYyax%!{=FGa)M0q3)PlE^d-*mzoVVo>cvlt0 zqj?OE#T%x0oW0x0J$^^*@;mX)Jc%ds6xd0p@h-e8Psh%`8&2u8`_Gp9|2+K!P$16$ zz3ol`1MwQFh!4hTU&_1|t*D=d$ zCyXWhP0adB`7*wouiz`?iDM0ao3G{XU`==zw-WE;1hN4ugmwzq!nb0@*pAc4PQD8# zl0E!G++Xa&$>admlS8=8pcBebtS%qpZsP<$$xmUeIfE0+IsOU%6l>3CxIesz^ULR0 ziLT%_@hZQ@uVY=hi93oijn`$lSe1KV&y$byRWGd4@{A?=iT==axt9@xMKNZ{p}09J6~q3b{}otk zt8k+`MpWaqT8-WKn=Br|ozqnDsCW$ffX8tI^@Nxqp2WW3X)#kggSqip>=K^GJ@E_T zMe!1L4KL%aYOa_kUd1lrHL*avE*6S6u%}pzTje*!TVg487|U_1wNk7StFhmB8#m7H zh;`y!>^|Pd{qqK~QG9^C$Y$JtZ57+ZcI-`d;y!G**dso~E@dBX#}0^t*!3O8i@qb` zs5pid@oliYM;)nke3i4|ZdIxbbVM1So;nodx6mubC36 zgki7N0=I#!l-5cc?A_YpZZJ}bQlhbci^WZ0ypo`_!@jNq?hO-_PD*F&@{)0jn5v{H zU9jg%$DLv~B~$5+9bh(Y7;}|8rH9f}$;W+TFQriFjU8fN+&=bK1}FouUo661WU(?t z8H(LwDQ+f*D>)?tp0YxzRI0Go9D`fSu}Y0H4)0AT;0|+=GFf>9JJ+eW(R@sq zraX?_*%P?qd{TKzc^bRgXK>^Btn!@lJodOR;QsR^WsdSP_RJ=%j1K>@HoE`iC7&dpGFzQ+1Hh!wHHtXO%4 zEoC#^S=h@0QN6lk603dWN$f*-Pvbb{6aT z2=)#8jy;22Kp9?{Rj_irHClq#IAgFYsK)CXdWkb0Z>_#&6S02L3(2W?5xE8TMvvjw zrfKYP_APFTwkfYF^YLt4G50p)?E%<|diqpwfoCminJFs)wrR>JZ??c>8 zU03!h`*7kpfRp7R<*@P*&Ob+SUi_H7$KF?tD<^OwI)!uN8Re{U4(Fp!*$s9RC&kZ{ z3(7^Dm_Enp@rv?=auw&N>o{ZHRBkD^ahm!HXWwtwcI8{;JDjh6z#Z6a6MiwQhO>TX2#`iQ4=t*Q2ky%-Y z?gDQzrOPFvmjxk1iz`d2i$NtDRIWxLS4A=vQPGXaZd6D`FCu$MIKHPDG|Hf$@mbQ7 zl|_lV$gDDbt|WUoQmDq3NWBE4?VgMT-7~0|bRyF;2nqpC|n(M5ahGo8tQt=t&@pKp{ovORo9( z6d-~0XJsWjK=2}j17{ZHNU6F{Btg1oOr(tI5@yPvw6uwo4kAi#o|dBLMC!cmVB9?e zL^_D{41k<;_)*wCiM)KcTFoW<+IG z<;W^lVrb4WqC{GK`(-Wm=-W1)N4KLDC(Lh-mD%8^C>&4~gYr#6wL$fF`it9cR7~(=v zw19LSXX+8q7(}2m(MTwxJMzPWCV_`q2#6X8VQ3$CsCnQ)>%c>eLkS=t5H$@|m}(3! zH4IglYD|exV`M}cN~&=|3QDMXw0JZKf~Y-^7|j6>wFW$B3_uhGh}r?7B@j&wK?n_i zFb!%!)Ch>7HlU`Un!-yh07MOdP+5OURMcIz)5Ido8 zns;JRfy`Z0P}J8(Xzh-WLOrL>PtK3si}d_?T5??vx))(Cr0$wX1XNlQnm`kaQnVto zCBKP9*_yYnhO#tNsPQX=98ylqn8XHC2huy)D8$-A>!?DFL%tqWgsN(ZiZG{>P(}Hu zCMFLeNMM7^lF=E|WK?K|-Wq6dt$8v~TP-46OG(0O0a|k*r6z9%T1!_~ohZ_4fFz*w zxO@^dy(pcS0Lhk3FOpr9UZizRdQqAdi`53zfHy;{wWMTSk(3w$Ax1Jx(XwC_l3Ad6 zdW3B2bogZYp0;4w=;@GI=9bG3292yNu&g7nhNu3Jznbf6{n_Rl#)qGMZ zGc`ra)C3}?l!JOm=FnK{wZse{YY8lqp`cU}hOkry5K>@Zt@|)bXo@8XnFf^13|vS` zMKcH-@gY)HiU_F@m{uS=YAy+xAC$}wEc4Tp14sFZl=+E}`7sY^ z^r$zQTh?E;zvh=Y(=h0$U5oTp6of1vy+YZ5bgxvUG^mnHtuJiw>(O#RQNKYb>Nd(t zMN-ECBSH(2jW0V<^GhLew8jT2k~2Y$HnxB?ozt~kb}RU!$4Lqxt&&8@z9K>nS`e~_ z2qO)Ikd$7Soc+MemPwZvA$le$pUecRrN++;UHDG?*9rXxL8PTn~*QOlW{C`u`7PhBjt$VSSeBnT%g`vipU*V5&o zH;_CJ@k4GPR}H@0daDn0oJAxN+bI}MVtD8WO2X{4?=^GLZ_fGR3$~zQ6Smkgf+oaw5p~+ zTV*b-{o&PzYl>Fe6s>$9n_uc}3W+1xOI<=HStVQN5ffb;ssI(B7n`k#m95Rb*_sg9 zT0;OqKfpCx8$v)}g0>_=Dh>!)4Z?^*gG)-PpQNNpWNWjyt}fZyERcQH6#3d7hBm#Dude_46qv7dEG>1Cha9&Uwvr_c zUxZR$NhDQ)dP@4FR5)13R>eI`cyU%$<#1>13|eG6c{i*(gohVbbVFOy5tYcs52d6q z99X+`v(F2@h8KCsOZk!4MjFv>T9~b1O`Jw`3AKPGbpb|BEe2&*bTPu9bMR4FIK1nW z5s6AwB>z<~cx{5|q>-8!=Ndj@oNF-FC?irb?qztX#>u_v7;B^vO|76`U;QOazsEAd zpbb5fiz<4d|0V8)fDAoAVv0#a!1$gt)F^0tPXrl61v)n(5+Ra^vJufXBF09<+K4zC z5pN?BY(zVqh>WliHqMbY&XG3Gkv7heHqMbY&XG3Gkv7heHqKEt&QUhbQ8vy|HqKEo z4&y4zA|jF_beyK)lvs!4isGTwRh1673nFYzrkk5myj>vVkX$vqsX7o2P zE|thJ-AL#-+#qE|BBP>o9BuAaQ&wJ5YA%z=p_?v6nU1L$BBN6E)M*ag^b*T-EaRi& zwD{lv)m8&e3vJ+U1Ig)pEtenRB#Ul@dAR z>XKF2OBmBdPu;aa?uc=f!;7oORg@Qxt7%YW?=$z*%Bi+1Csvm|Hs0J*E2mmR`C8hs z_G$G}V!LXk#p$gW7i})kGSz6Pm)54^v^MQ!lT^dzURuwNlgQMox~y`zX&k`@z3ioJ zFwWlR(97218g}YU*LveBhLx92bedpC_qC%Z)zRjDT74!<ftF`zNpl8=H9qG#o(Brq28JV6C|?qE-5RmF2#47 zEE8-L@r&%D$YE)!owDdBvtUR5c{ zhGR=B%7#|iybZ>TqXP6uXN^@!)dVeZV~qz5e^gcD?>5#v}x z_vDhSH@0k|BrKJ81GbV2T8|y%tJ7W zw?v3NjOGCdv4=6-10e<+UtBYe~?eaA&WsbwjW<4x) z=E1sWGAx4z;oV#cERsTD&E$wLbAONDT2JG5=Qa3E6mLMJ4E0dD2djq{+YNL}6KP?B z*m`Jf-w0{b`*q3onA} ze4WH-J70vTvhxLrN;@CD%&yC47Ugw5%Wnd&ZER9oz7};Y&MlV&-p?6v$MH_L&bJ3& zL#p$w7sKl8m;)h~-y$OxUqEK6?e}Vgs?s)RHQ7(wcr!D{n--`tY_Rn(f!W0ozkN5s zuiu{d#oG(Ndwb(|a37>_6lecp#gY$e6uiGNa)#hL2PWGu?(}=RTAzMrulr3MX`P{& zi(xH9c0udKFR-(5fvpQ!3y`hAmvyy)U5K;aTEEeL!~6#L_4I4WLVREGS>ZF%XRuG9 zPnJ)JH>}E$dL;B{D)Gf^Soa#I6D&nuvwMBBQ6E|LFp}tt%N+9_jWGtB6XN1&=x`8b z%XY9Yp%*-BjMq9}YA1~8FowS*fiKW#cAA3%l^X6 zfz`sMH4fIBwXkSxtf!e~q-hJP9&BFycmS+5*YO`<`Jbz&>Sv@12UQ>TNq@p}bqFj+ zUxVf6a@cbI1Z&82J#}j%bsJDl@1QB{#(o?PFD3=ra78bW9 zuvDE4%hHAX6zn7uM5>ln#k)K`Z7WbJyAG?{_1vn*;=PC-+fq_D*=^Wr{mSl3+g-BR z9StkjrP8+a5LuIoE_zN{Ycv^JfO2C$u;1AOe6QaTR=xxI)38rH4olM?b<0q)0Ck1s zXJ5R->>Fu^>;^025UkQ^ytlONUB=&see7viyxxGF>mB|pY*T|_ zb(#d5(Hz)`!a`K%NHe5X$52qMq+R(9Sf>4?zdO7O8!)T1ZVrO&GFd8t?cN zF5rV7iFJK*f7w;Mr zp}leRf(u(s`~mx#CHdWpfHys<}WA?>-_!(zLye1A!{)Q@5%APeYSutmNt zUr&0%GB^;{y?7O=b0WDl*@KAUE%COqAFQ)qkS{bB!wUKgteVgB-=uYL2Ur4UXgT?L zBWEBe7rFM3HTM`;aDTwJVEx$x>*qgVhpeqV?e%<1jC=v09C1?U2Ajt>U@5nmpVi)O z>M3b=q{-G4R3praQPS!%8Q(Bp%2u(puzGwSZ%d!Ro^g}tt>;QIa?!fui8-+q>^S3L zr$w0ndF$|+ z>q}U$-`7)~Hd6Y6a>7X_8nayrUe_*z?cRE}i9aI>^t3CCw6tGwl_wv~mNZFU)91tD za}DeR-y^Gc`Bqiit(O})@%;hpZs|>LHoe@H7V)!m{#}h!O+a}Wdk)%fWZ^BEZO^gs zf47HqjsiV43orN9#ql&MXh^D;v2zQ^%&|k0~VyCVTVfEtMcnu;Swc9 zI|+@7G^k)nshDXz(DROHZ^6>|pA;(F{zd6iVC$L)iGB4{M-ZQbug(b8ogW8Ga6a#z4)NGn(pZ%C~0vP?*9I(1YNw7v0 z=(%4h6)bTDtb%_5Y=_yAa!@VXfwGs7*7YPt-&;w9oh;?6MemJ(kM;=tWcXiX!+T}q z5PbkUL&uR1Gbr-m`=SD0qACE4LSOG^J78nF7FxH6&BqSmIoJ?SLwna?$20;~!2_^A z$b;^uVV97AFVS>IzSSZPa2e(T!RBJFL>q{nfJveUU}wy4R9X&TN0ANKUSt8b6PbVs zq8ng5W&^=uMOVNWK`k3Ck^#d-5@0K=NP;!P7jgs(kt;XqDc9t|z_ivCp)6WUi{S4B zoQJk)jIsF&x+2M9r4W^lAUw_uLl96@ReA>+KN_y;i3g#h)948Bx5x2AYj@P^alN`_ zEk#qn7QzoO1gGo}=)MzGS34Bx61Q;Pyz_vmG498cWNGE6ngkp~b&EPZwLhvOx1$O5VatAmH zy$bScnhmOq0$e#&&>VMS{e490^9+h?gFq-cN4Ci|RTmADG z;U6MCmG1#e;=2Jm^Id?6d?#Q>z5}ow-wv37?`9G2Er8MZt`0PbZvqVGTOlpx475-N z_OJ=q88+tu+zT(^>fy-q8+MUj;=brS#>Fw%+V8=dy%8(-Dy*0bvDbJJ=g1j&y)Y3k z9LmvOgE6KHux_jd?=^fS;A;LB;7Yy-a0Q*8-Eiy)AJfUk*GJ;Y)$37vGdPkuL@=hJPXOVBps!PUNov7eW44 zB~Iio0q4QL0UBFHCD_k9`~}F8#AgF0^5+3N;#+Q1r{|ChrT$9~KZSH}^J##u@>zgM z{29PRJ`=Dbe;Tj@?siC5X8^|YCjjI4bii2tIA9om3@{Ys$U6!CD11qLDqtd?0vL~V z`!Zgz9G5RCHlc469H5C1i&Od9B-857oJ^f#L z)PJLaOTZo9YnA;r5*TYBr#l4bGcN(g8pwx8oQRzsq-GJ=RqCq&AB=M7`+6kZAi$1% zAYc?902s;p14i(EfNgnSKw7u77VHCG623)A`3eCe?dGUnGOj?z(Jmm8_XMQ1Ps`gw z#^=fST)?(=d2?i3wv40QL0jG(Fx)OhCj7~~8(=G*0oanK1GX@@cZDyJcLD6k(*Waf zTQ>fG?^?7uiN_*UI!^_}%T&Nbo&<=q9AJCi2{4`~0><)=fYH1IU<7Xu7|s&_Tk~kZ zRy+zYgvUdY-rz)c6f^_Ut}OyF5APA%QWy^h?8w^y#`9KyEqP187Myklh1i|g+N=da z6M1vMjyw#IX8bvrcc%aIo=nd8p-7v`n*k>A5Wvnn2r!Wc0(Rt00XuMi!1ml1FrL#+ zAdY(h#&S=y@D5P|F%B=g#4@V z_Kap$4w#BF7fQvMAEn~912xAyj=~4G`*_^j==9Fgu~gNz~v~FR&tC? z)K43kW@vO3ja_iDV2tMDW~~?A%&AyK$KkiE30O@+@J8=t%vw9~7sfusUvuosjzEIb z_|tylqSG5!=HYw0&*4quM7)12!CT2ZysPYp+w10dujz)joeb|lzs2t)=kdODKi;Cg zhj*(B@y2x~-ow`5?f784v(3WIKmxvV6oPw#M))1&K5lKk#EJVX?n3t9Yf5WzgE1d> z4m0FAY#6@D)Dx>^C;X<;5^JRgUXKg3@b_q=3wRfJ5WC0qSRogY{$uSF=s}{c7}RBh z`rM!{85He8v>X=SM+SA+pbi<-L4!JAQ2PyPpFz=XM=Rq)gW6+IyA5iWLG3iC9R@}F9gWL2 zgW76PTMTNmL2WXq4-9IfL2WRo^#=96LA_^C?;6xPgL=oH)*95?2DQeZRvXkRgIZ}& zD-3G6K`k?=r3OViEv@En8q^YlT5M2@4C)PoT4+$O8`J`Wdd;Bb8`P@?HP4{tN=jfJ z48h1}7~@^AzsSG~-awr3b1-+%|IZhSkv;}9LJdZGIz|S=_`q1kJwqsN3b)}a0xNJo zJO_7CkK#)PBl%!_4~=d=5^!@F!o7JTZpIwnnnMdMGSX#8mujX#Z|@uyKV{xpilpGML6(b# z{Am=8KaHaCr%^QiG>XQbM$!1wC>nnnMdMGSX#7ouA{Sc5fj z9_}=zLwD$Zj_ik6nq4Ov(()KG&OVo=2fHQ1nv3~G=;4K%0$2G!r7`WaMTgX&{Yy$!0+ zpn4fpfkEXPR8NEIVNiLJ5`ZBX{S0G27i+ko3-Z@#`aM#`j79oV7xNrK>tZ_D#dxz& z{30RnWsk41I-kZDM7BYP-okGTvoKRs<5!b@_=-vj^rt0$HE_i&@dsArYdGB;f$prw zerf?`i0Rl9kHnpnHa~2`u4F21@<Dir++t z?kedN(4_ON>~ai)J`aGtWq?l%cCP`*PdRdt@=;0U7*w`FWf@d=gUU3hZU&X1Q}s|P zeRZQ1&YO|g;nTO9sfu^R4oGIkUYqQ|m`k8bAP@9Z;={O$L*p<9ey6rRUsk(z&EYj` z_={_vTeC*&c#tM$S!>T)t+j&JN&-?wGp6(v-x22S!7jnE!De%CP-tjuw3|nCR9tLK zXmD_>P6V0VgJa_2qN6G<^JJyx>lFtqF0b^!&w`i>a&`M!w zv1kvioq~hH&}uPp(b2Jiv4QTv?!nxtQPWm^#}CNyi+1^NGxu_9>=xI;H*HvHew*5> zqVKrfd|G`ckHR52L!6xkapCUf>Kr1}9=SaVTCHBaT`%`z)r{Zt7>^4LM)SKz6Jrqn zNeGfA%EJM_^SijXIX^OPcf)@XhzAXsR6g3Q1o`{>1thNC{*R)yFM4JYte@~dvYGG| zq#Lk1M5{m>NGZ@Je^VU2Irwj_L)x^PS}|_+UjHUuzD@nQg!we9{ZMpC;a9R8l}7y% zhfa8Gj8A}HlO}=h2ZnjBSrdS2Qz=RzN{Q7|@%42<3rCO7Yy3@Fb zjdqb80xgJlmx9E^#mAb1NqHRtV|l3TIBnc`%C-=*rg-|SYZ}nBd60M0)-B4POpI&R zdf@gMwSV%*>$mocO*qzmOy1xQwV`Sk(P6ZkyJzik9}h22hc_Kc`*iB%5$IHVuClTo zZ|d&ZDJp+jM#8w&tEFVMKdSUK!}NzGi-yvW%4iZAB1&!jFOmlO__s@n32fH9X-rI$ z?jgm`w2x@pzH`Js$ef|eyrf~{fO!o)JQ_OBb#lt@-L|Wf>ewzOI5Udk!c8v~Q_l!mo8hzr+gtSKH=&;toL0;iuqn~Qurm0V$ z|0@4L|Deo9j*S{mpPg3G`H?8qx8sA;%2LsxyU&ET{4ASAqL zNJ!K0u)sVVU1m)xsERF$^Xg)>HNK~M1Lq#5qSqg8)Bh-Cuw6ClI!T#Q*UC}N|3&V1 zcc79@e44gTiV14gqG`=v8oEvUhUR~)f>vEy2gy;^7+(duf%6<@UOA4#Vxcb3?qC;f z9f;Njw-Ov1DCVd48|EEa-|Xf#IndM7|Io}akIv+I_<&f4gi&Knn}mm3bnT(QKwr<1 zRxV3t@Lv`}Z3-;6GQz)wRAdRc+L`Wi!j8p)=pN;{PEe9Z+3xk4oS4 zbznjGwUWd3o%SFbRZoP|ehlHtVCJBOtE*&s+VdbBUyQ=Ojs7oC-VfU90OLK(vRG_g zQnGnzmguk=8x2HK3gunZ54RMIVJeaH1fq^??5tzi zIpz@`KBfBgUognEVSHYz+%Dh+j(uk5gCqTav%DWTdI8&b*v&?9(E%>*ydT))`?L!8 zJ^1(wyuRL2tAks2?%X=J%hRvbegV6aTIbrAQU4h17MTZ499S{Ua;#z2pskjNd&9>@ zb(@;Ikw>)(3H%iR(_4M&<_y)=IL9GRMx)HP+ zaIKpTAr>}7wjGNd$A($KLuR&*D2vSr8&sdxm*^n5zSO!o^^|LcS5eyzP0AZK4xH-pVXZ?#T8B3M zH-SAdz}trT`1&`gRh`B<)Jseajjbo@g@*;VaTNb7y;DQ4u}-dy9H|CP#TDxg%#lv; z7;{~1ZBX+FOtk&Wic-Xtm^PSEL#wACwX?Wt-HzD_;{hYgGdT2bQ%&uPeuDpB`OZtDFF+Nja%~_ZKZz9n= zD0%h&x2bLQLo*j@o1uiU0<{rrjdktTPe;)O(`U?>zOGf9Hm&fv{6`+C3~SRS3^UzZ zkYXLu2jTz6!vMuh*AMN4{{a&o`i66B{7M5kbzn`CP)BSA+cJ3chS<#A3)xEINm8=yZ3I9MczO85z9i+&dixJXXZQKnF)_IZ*BeF-EmEozh{1&sS<<+3z*u} zq)kgrN=^k~i0#&b?I5P}9>NzKMI{cB+(Ncua4Obzo?W?h>*jsTi&1fL5!@Sm6n<&+ z;MUCvVfqwZdeUo6?nj3Uux?uceF++BWj&Gw>vI1?cRjgxcHKM{KV@2jZDwpCQvK$*1##l*zM&U+xdqKK_1)2?q>cDQZR#QGd%cx`c^+nrT@k1oNG>1s+e zrYGr5=AzoZI9^xmd=952MHV9Qpo7{CgeXL}f&VJDcARZcRF1XoIe5;V$Ao!qQ|w0# znWFl21p1V}e|#5WpIuWPiumdd=QNuF*Qhv+A_yu(@2sdaJS3ytVkLkM#e%nE9AZ#7J{zCL4zszpSDSWQ+vPDad zv{&!wc9z|q#a0w*_pP44t*UC<{8f7s9T4I!FR)nbc8diU zi|j${hYx$+2g?y`W|C|^*rK3GOyC`t!@bWo^`8M0C%{$%0Ec|q1t^O?#;ONDEx`+< z@s2Lw86<;~Vn;1Y8`|>ABE!QX71yg)_bwa0;po7vOWLmJQ*-yT4;?$m9BHX7Z%Nlj zL`BYAYUa%x4A}Ut-q_T3TqIRfs&%Sx(#q)K2(4SW{=o;7`nqpo*%vi}fy$xNqKgR4qTqi6oM7O4>UMPA)rcIxk zn)b_Io12PPRn+gAAH_Y&?qT|0V!F+GoxzmKy|Twtu(qRpV_B8k{W+)wxqb+As=ff+ z3%W~%-%6sP0H}tVO85{+PaNJ`VhpUO<*e(9+6~q9IgUohveIQk?9EIpXzR+ZE^4=! z8#)-WT1T3*7gUtDL?~kxy2^TUR?kn0j|Chy+LUr{jwYe683 zXmtHZER^mv#p2Gh%cCoYE33!q8z-BX1kaO-LC<^X=>XK}lgXmG%=vXobDZm%+Se9z zyFFi|Cnf6jiAm{_Q#KM_8c4PG9bCaqUb|>M6D=f1)nWkn%$X zanpr)wVPWSMk`IdZthgf=E%AEmA6$yZwU{FkAr+HL~#f_h@Ln!KNhw!2mDg;{PLGOKoK;S4+A}i9eVC~;rX(aM5eb-r)J>Nc z54kpu)z}hXJHZ(k4yBk~AM4Ixu zYskL;hRkCj`#uzt0R(jUj0^Jv{5vQF#{l!*(7u$2i07HD>)JLH54IF>e__ov%!4+ScDpe}1wT;%w8$gH1RV#jv^cf!E9piDKNWX8EoTJ}5@1Y) zqM*EX_oBGyWWCJ@;3WYtZTjTsxJA2b5y5mSGFv?`ij1x3*KO?V*yds$WwR^>OJ2H8 zhtQ=Zi+z-FZR-H=vfmY758x%ud+#RxOU_xrsKRHX_X-lifyLh4@#}&`ut^-lO$!UT zb>V};IBo~-$I6T0it^z?hxLtT8beO zs|qIIKbDxBY^4~O=NF&w5_4c|$JK|Mm&U}##l8yLvTgjzdk(OZ9j`HAxy31(nE3Tu zFQ0hTQXH3-BVk?0xG00&c4L|Ejqk@Df{ z-;@+V(&DgZ5+9nr2IfY5jHe@>OWI;gLrlF$RVKN8%8XObi`%qo?(>RN zO@h&w5T{cOp#mHz{Hql{F)^Rcmx=-I}V} zksA0**;8L4c=`m_y=Eu&?HaOqYy+S0tSJiDw> z(+gSMwI=)toz7^~!T%*y2f!0Ba{1ZyMNbILuvT$uOj*XwzuKtcyrXNKDU4 z<{r6TqgmZuyC7ZD2?eZk<&x4|Cgy@MDLpj7J z7%?sbHo6~P8;s)jxeKJKH^MzK`98c1sw6+ZOM&&Y5q+8b{Ffmwlke9nuvRjX`%d^h z@Q&sCWBApt^a zMqD!iApHK`4>Dx&_l=lo3TTJm^gd_^WEh$R!NovY=2il7RHSOFHa66cR927FSu@O& z?Gy`99OaTfN(UeY>MrI+#5hdZrL`*y3Rc$E^%u-_RPacMyYrf~B>bUd7_*6I$zhlq zgB3MI9eUd0At=Ni@ELaTIvMIQ$X5739{5`plW0P+76>0wV1uvWaBB$YhlU9ku#J+! zAJ^_*vhOHGJ^I-S0oN$YZQb3na;X>afL=D>ajy}a!-&~Ec*H*DPt*S(E8GOVBOp4H zdIW^#kzyan!NH#NEMy;KZohuh6%xeZQ2^sP^?m)zy=Vim48O~lK!ftkR z-ClHotTb>9o`T=QSBwq^@pDX>5B)G@nuB|hF^`p5->BjA}reZ&(INqpy9 zf&v)HnTNhJ6_UdHlr;M6W6n`}Q=o8NVecs*?g+R2l8QHsEqz~7?0$;(P~5b)&;?_{PH2XCNEaZo zgfKi|fo8EZ!11X&4SEz)g*=fxyZXe+TOJ8k-ZB&YS2Y~JHb`k(S558wG^tjMB%*;m z*@@}*NKIM;6QC{t5*?C3QV3z^2d_sl8FsL@_xxaJ@2vrA(WUiukA1VFZskCbDm2>x zuRy?}7MLJ~7Uhv1gEJP;WZutlhnk%LQ;P^B`!i2f5f+{|{En|enJij+CZ&@dv}tXD zTPQ%2NZpwZ_iXSvM+j?&>_@ubB2YytLq?UAWwD;B+}^inPjv&lCa)lcX2;G3T6PjI z3L*FWDYVkVs3TU* zfQevOL+Vje*h#(xq()M_vg~WGd5cF#(zl9*gE z5uH>dzf+(!F;1xHUJj-EJ9w=Zrr5;|IQP|zHoO+N#KUNEyx%#oKoPjEgO3A!v!HVb z^&hBZ@$;j_!hi|_DXv&~hOw(^4=h`}x29(A;^q5mx#PL!jNIIe%sdjgZX2zoRULSb zF(=1l%FZ=n#F+jTdL&^@7m|6YN)Ew&_%tW04_L>ALWTF&z!e-R?)dmU0vbhO+>O(y z1c;gJTono6B^)zayMO7j15n{_7n_Pgn0A-Jz;<)k24E#$$t zG9fg^i6s+VAF80mWR|RxY|RwUav{>1DPypl7EoAC{R!(OES{KVsD()NMAkA!teVQQ z%QrTYN@~PNOQ%#5EuCoH1XKDhK$PPvO(I0_f=OclF@=hW(FHD+&U5P=e6_?B1+SC7 z|5Kq%g41^T3y=f-7}H*efhzB@;8Q5EuX{3~|IK^v_|{1n_@?ghDUmQ!fcgmQ3nQ|& zeCi|2`He7zj(p!+ACa8f2pnJf^J0BOGCd=_hNM5w*GJQrle5AIljZ2oL%joc$A3|z zPrssM@m`&!p$HdTQ&Mlwce(QI^@`Q_hs$LTVflOJ3HVvKsgnLQVTuUUO2>#zpJvO} z>1}5YA2@LM?vlJb=e^2MTXtGnc6Qp49XqyI3kzYJ;@iFoCIb39gdzG${0`$ge0sD* z5+M3BSet%;m}>vN4d6WDgztFJ4u2md z|NkG-e-A(3$$kd^9bocxh&~7J@JAPPi}*%g)xN z+idCkiI)`Ld*lE!wE5;M3yTY*8=M*OF)O9pG&#>>xW;^HOj?O&9xZycU<9ow3t2zohb8@;03cGT4q-pVcT5w1v`8@hz$e1LgDI%^??UOnHSj+HGfK%1ksnlq6i)xI@@MP|P%}8-MJ?=?&^QxD9Rnf} zKpi3ejlx1L1lPtz$aw)PaByzsQ{b3%+RJynl7r%itiy~AE0=Ne{FaN_fo4Mhc=PH+P#t{pVp z{cCwm^xloYiikX?-ZHxT^3^NtbH1LMVpXieu(xL|AgM{|Yj*CsVvi~&Zm;3S8zG1O z>vVERq9Plu8XDt>(dK`mjJf#V@Y&Os!N0joVMiukoCVAoYALalXiK!A+JTn*M#IVh z!@w&2s#UDLed>f_bu0PCfPU2g_UtR1{sv}Ar;?~-f)l3#ILm$Tns_6;621cFIm#jx zjeIm}355zqFeNSoAI<)MeJ%a{($8g;{R;s|Lo!a?Bf6I_WAcU{NEk? zpFr6qzZX}LWnT<=3SJmQVP-;X9BSfU8gS$}z+>d;0Q=%nB>m&d0C;&e{T1{F{G9A3 z=&y?zO(-!OMF>j5;3b!3W9it9s^^r2)&hXi;XFtf@2oGb z5{XiSK$6k{M(9VBvKYCqChpvyw1HltS7BB`Kv!g~i`{erWO_p-M#*7nDiZQjFV&D)rI4y-~Lupk%3wWm2|O zoQx|q{ddJ0SRo9Jh&F?nkbr{c3hWsvaHv?*@#~X+V-ERqroMMKGybQY8?T_Wsnqnu z%gOE0^qV|u3Q%KmBPXnYh^irLN`@^WY3j;%_SJTINmG?l&eYK-Z;4t^=fw~sYowg1 z-2_YI8FP>hA~3)|!3oux(1W~u7b$lm2yrUQGJdYM?(EpWrP)&j?TOzX9K6Z7E--}( zF$VBcs95FY?`$cnm!O&gkLp@R`NY++3#z=}CQzwxuT9@g zxKt^Un-@WCTuKqFoduz4bln`>?%;1XH?fpQp%Fk!wJvUN{)yRF!_BlQM2gio{>+Ac07lL{pReS)J#m<7{ z_Yx1jBoZp6S$)v)qw!Z6)60PoQN>@mzyC|5TJ2zLa{?oyQnuCfNyT9zlUE|5sMi4@ z3-BRa9q#-|`#DBE9=m^8AktOmwv=t2!;0_YP2eiV8r`>?@UG0d)J(#=YN+`ctMTg9s}CEtw#isn(e>Rau{t*EZyg-GIelwaU`Ez%a<7CveP_9~ zAqt4JESq%r5khT8*?_ag&jG1H({7L6>q zV)Y|wH8uml5LDWp4LPgt&!>SP1(r{C41L(_M;B(=e8r%*BwvB5B3IzT5HQ z72H?Mc0;zk>MUEiqjzaFm&bmBzPW$j9Hv@ZT-ddvK4$}ZWQcY88N%E`h=rUnY6yPh z^t&{$O}JZOQEy%R@#5iN=r+{xo)_ntnpY-x6Gb1mECzIoYF>zjf<7dwPZg zV&)S5)?3HIm$wIj&UpeCddXdiYe3Uf#NoX07QGY?cg%J(sw*Po#5^T<#a-#!bfO5|)_X8h(7Pr`lhbczdK{d2l}0qT4P@*u5Yip$qt2 z(|0P41K#H4DM&LkQr29O({;S-$y4t!JFncmsd=pGnvHpx(Xon`fY-Hi!Oug>r z?MYi&DItnGfMh5nMS+fB-+#isy#Nsk?gwbRB*g;xAAZ}?4G^l0Aw`UqPzWusbziof z{%=q3wohLuDKFdD(mGadu-(A@JWZd(fv`zb@dTi)R{Alx#5pdTGWqU ze<=CdRj`wpad(P#I1h+X#EMTV@K(=6ZaDcoa3-J~0|t`xSLrqKS~oH85te{6se)=S4D~xX5bqL@{ZgSSSG@dO3>o= z6>kt{2y*ZT0e`rhi0uQf6TK0=I?dNeS80Y`9Vx5>G>}lr3{N<`dg=F*Ea}Y{-BD z)SvRD%KOvr8yvhdK_8Sebr}+-5d8FJKhH` zl$MbfN*QzJ;0vXcG{y7Y$~#_}@GMBg}pV)YFw(o>??E6M}qkLYo=M0=HZ$am|OZ-?Gg`l;;$8gFN zU_UYNxbJxQOK&pR7qpGpHOmOEJ2Qgkb)P#5nLz9P%5KkNH)I$y%-nO-#jM3ZB~486 zrFhGe0`7Iu4X=Ym5ywAL-la!c7e3#t?k|mJSM7YP7`)nMe31xR3m9o>`Vjlf9 zoLh2m$uoz!<+Yc=9nj_^rX#|H=^j#TgusY3uaKO1!4DZzEh+KG=QpjL2*6d#=`;TK z;W;b1y-*Wj5Ut_aYR5@lu#b6d`YJvzI0?GZyGY1;6#VD$8pF?9#yEphR7I&6$Cb0 z48*os5Za(rBkgmTUtRYqll^)S2q%Vnld{^lx9?&K1LHT8**1NWzgI?DwFbF0(7Xsm z35MU`^)fTLE#(h4pJU?o1Oacrhut4E)Nt=T!<700H^j4U;4zyM)U()-Y0?LQyPD^? zo#Z|4;`D>OCsK~v-Te1|JAD=T{s_-;gLlQqr)1|q=875nD}?2O95>A7g#Aar0uttXY1Y{ z1gz+h1@}eWzprLi_6I5Ec^=&KT|5s?D=@wY4^G@6w4M)4g?r#VX8#*O>2Tc9-!u35 zQR2vceBYlF7pfq4D5SpTIdNrAoWE|v7B44mw2|W;j=FvwWc?&cygvggjz&rUYF%K( z@kM$7R-F78#@z0!>0j`?I1N;#;@*WUD6|AVhrfOeMvTi*-29s*ons}zsBufGnb$U| z8%JG%*m2W$vVQ>IFp(E5WKCpsu-}6#v0D*9Ab3`xoX<@?|;mNAC3BnUeu) za;S?*K~C&^C#lxxODnkH4TV8Z{4I{R9M?#wc?|ak1DqJsYwb{(AZ!hm0 zEdd@|TXu%YW;11EV-)%$P}jQ3-x^ofZ>WW8ocmW!whQEm?^yveW^c&Sv_#F5HyD&d_m{`P9;b|!2Mia<^5f(4M--> zM=t;BAVX5s2B;LccZJF_aLI5^{;A#!7Ov`p<*-td1k7hW7rY@>xh0;~$`AeYEwR)tUFv#U+rMF~7&S{9c1LUdX zSv8_(lN$mVwk|%a3jWMW33o#j1lOn~mwwVmeag3jwWDEDeLB;q7f)sMICJyLp-qkR zbKz;$kAyFRHmmV_wpE^(STsKVjnKSC_!Crz$f%R)kF28qyA9y-2+bh9RFT<0;=+2# z*Wb-H)3s=qoSx!Lu1c_pU>r|1P(+b2%044}@g2+s#wtqX{wdf=`1AKbLghJ>^v@6# z*zX(l0Qip;ytxrLu`9uPi02}p#@{jV@0LaPLFN#-K@7n!@X5615>0}n(O*5#`J0UD zFl-pK@@T7{%Zyz7w$|E}AH{vc{3%tNzo?`tlY6tL2^$I3=u)6TP?~nCQLq-?RHj2i zp~lOWu)a>i{cm!iF3Y~1`;(V9vn`r*8A?dUE<=RgBlJQ7c2X8t!3bPHxG$gFNi z7orjVx&u`it{Ejzg;YmXR>wJLp|fl}r!mtc?bW0z;y&eyZ2cVHSI5&TQ3aaoEaxd-KW>ocu{d*t{S@Na9@dn zQ^{3G)XayTSje82AU(7Kaux)WAMbO`B>}dk;+N9qGrr=hEXPjn>Eo`<>8Z`lpjt)h8IlPc#dpsY)O z<&;_|L8yl(fQsldLJcN$n@|PzzF2=GF~Vjr+iBk&CJovwOJhxFq^G(mDc1A-%-YO~ zxYiA9>O_l6T~$!maB)K7OuNd5fsss}FYeeIZqAYGd$BayX-?m0Nl(q~ZEPBiG1$6m z?iREU;rH`8(AvV^f|0vG5j_G_Ht?1p;gjYXQpHs3WSo=~pM*lSh5NhbpSG^*-0HhhSV|d=R0aJ3x!9cOp=dU>PI~PENEC*IQ5vN*-#qWI>Ow-x80bEm#9eE zZ^X~zUz)0O^5jc3r7Id4hi*l=+($xhjxev*7M=ArD=`lv07_SUTJwu#N2T1 zMKLS<_{W}Cpm)=*(O8Xq{pL!WH^HG^q_$b3HLC6ZEz@oKZKVfR)DFDKCh4gn>U4=? z1sOxQl(Ljh@wm-^&7WSjG?WZf`kgCt`d?bt6U}XggQbA8@C(Is!(K6Fq;Rk7HbP&h zP`{Jq^M|VB2Mh5>)n7pRERub+3-J&wkIOTwXec|)Wn1bm-iA&Lo0=kD8`VD`81BsJ z>9Lm8GYM1Q6Wr~Pgeb&_4WA;_iT=lYu9o{;SPP}t3yNw5@C;PkX2SaS?lL|eifbb# zl)(>5RW|mFS}>lVO7`(CRk5d{c940E^pWy32?{`J_(blqju;525d3S~8tZd33Y#LJcV4}}F%RAFO`%6BgWmlh~f(e2LJ$_tqYc8b9X5%`*7hKdI& zQo6?j2VzZdn=9W8*g~ssEH2zC_ab%wl(uFK^Bj5j?CBHNAW(a)6`k;SM%IoT&_^r5lU@G3VmU*7qrdP0ykZ*W?BPJe!tmW^)?f~Z6A{2JR!WYy|D$M(g zfOuh^g|_nUL9-=Tnk~TqRD7tH#*>T$+T=`%si<18;aTHYE$bH~`#jbjr=@m2^g#<} zIOu|ANcEf%jF3w^qIph=Ht@W-Ft2K?(Kl*-q_diO$6vrUX)*T+PLe_zrg0yNIzu%J z`yvu_wg5JXI^*Rl9CHB760!Rr9CvQM^R7VQE&HTZRq^Z@tJ-LMy zIov4wNPqv-8bQlD#24(4TJo*WqUR+0Y=aAhvBQ&-!`!r>-g5qdJH)mWMFa9aAsYS{rhrrr4s1Yn0W{NN(y+!V1D+va5XGST-ZixF8$JN-fZD-`;dj zvJq&9-o%(z=mj8q1H66e2g$`QT9@hxH$$QVp_(Qsn5a19YR%9(&Ua6$E4lfto;}&U zMb^ss&{S@U6ZM?AuUw?>wsbF!ub;H*%>8Etr{!184w~a#8p*~enu3XpB`ESJ2|7}} zG9;O_%8r?I<4_UfxA#;Po|>M53}*-Qtg|o=@yWC2sHm6c#ew4LQNU8=N6&?XjG~dr zDgShtMv|{V3oE?%st<^ZT~Zk+3rI01ui`8K6Of_^Y7D7*>aRP_)@>PSOj}wG?fzIl z9YdLmyLw72b;H%G(i5UQClt3$)U?j6$gOMS;&T{QWlP0AeKMVQR}`5nfz(d5Wt*cN z8T!e|=JwGzU1oR9k8EaTL}EH}SiMC9NItYI3)7>vvIK|QdgDxpE zW-1&MSBVV(eH~S?JHs}{Hv;r*t+lSmbM?l>&z-~FDU=P;_JBFQt2RkVbRN zE!-a!i!avl{Sy?Y1_ro~putM22AXw3@L_VX^U1KwoIXPby7g|M16}s|2a>djN%|C+ z9tIkQ?gtI=egJFTHPR?5ITrr|MODa-aGH>$FI0#Uy+ikRf8@t-IVC0{0rz* z24@cRyx{A5zJ8pITDWBq*O%xeMKzRvX@9wJLDO&%%l$gAUWVt&mu592ao2J834Igq zv)O$5hejj@)vYwQFePb3RE~RE2+RaC{6_T_#AA#^Ud%8MeA;&U#v;`Rq=l#FWQ}!cUSb^gh7f|t{**WMY)F!wGh?T*!KX3F6_IBs-1ArQ zsH9qjYCtO&OcKFLCJ-L}U33&7@)E$p_qNHzZ@(7E>;2Ll^P_NMM4Lp@HzMTOb#*v=+Xa%y^}dqvTG{d*AQf9 z2p!;K=Ul=HV1ne7I6o8M4dwfxPOyLzD*gtTj|IJ2M0Dmm`2)~yLZQBcFpz*DJV+!6 zb&?lt3oP7|GihGynHRQ~86&9ER8=6=0nkb9yR_0r2pZMsr#QWvojVDdFN1a86tXAe zFh+Inv$>giOIrx$^IiNxL;S)?Y78_`2@)H$!Q$ndw}TZN1DB`TN_)5Hzw1%FN7-)9&wvI^^45_?vNtf@FSEY;*N2>$zEQz61?UeZ)^D=Q!9XU#utKvyVg-6BBCk=0 zJI{`jOT(Pju{4{*WHL{-(LTAV35X+|LMPq5Yl{}?Ttk!GutYh2ltfW+)SNfu)+a7~- zF{NLtE+X2#b9w1NdSSD4Rn^?5Q$O`;xS^_I70gyR$7{&A^!g;|W=XSsz}r`?wOMuE zg>=)!gVkI$UXk;SpeRhe;YTmWHffeXU_tp<5Wgswd$4DR2u}1t%pv zA;`|1dH0e;kUB1MZ37`jC$KJ`tSDJ3QCM|)N<}tl(L!B#DAz_6PTO=;U0DDHxmDu0 zyDM6jJp6q>Eh)C!A2QD-IC^V*gmzwlt$v5|$LZOg3QMBdP~0b~ z3t*ot$N?n`E&o{rfea$q;l8de+vBX{rbBh1;~e)&6hFeCYdqOjC^3ra-xr@mQ`8RX z%sMWNdj3Y3Zs2*BcZqML5U`{&s+PuX-Wa*Ic@C>X*awgUJ|nmY_d5{^CL9*k9+c!myt_x-F^EV8B}}Z1LB^PK1@@Qi3~oi(|!7=ofjruT&t_ zEhKlBjzW{mKsfHenL7oaoZ-7F_#kNpo4~$Pof1Y+^~*l?u|VUfrJy63Z;@uIyd-)DD|&FcI?RKm7Y&{|1%XxWDPj8 zNBwKOeW@QYS;iY^SMf`zw$xRtn$zjn%svP6eQMi9jBHoA&1$uYt;c8773OA%PgB-L zkWoP7)mw;q73j_1&IrqfF%)zA|77Vv?-E)y0f;6=&eW^$=Aw30itFgMYw;D?rL=Z5 z&tt;3tbp5$pD}6VnTt4qr^MbfOSSE}ss-!Gs2=(n21fNzy;Y;IYiK7sk{@E|N4059 zgN(mGkyNUXYShaqp+my%l)9*D#|#6*X&0xS zWn0tbYPcKg*2J6hx@%t6(~MjnLb-9|8QB7WmbJ|mSf|5tW3`gl8kCznGqaE`pM{6;lIP6~X>2NL;LLy(0M zM+~|>01Q?Q`P}+IFCy-OR65o8Oq1K>B9bv~WXCF@9W~%ElD8Sc8S9%^LHGdyY4XQE zg&;YEoX+_h@wnCNmpj%@l4cjm-vMa0oX%|0CmIT()CpHDV-mQp0#G*#Qc|R2n}m5z zsg|hDZthbribPnO%71|LI(Xqw3d{#;U4A$FOCLg09;8i4wLl0-5=@oS5)Gr#UYoGsdD!o~}!b$64M)l4#}M zK$0Aidx^1%iUONmE(-_&%Lr*0tUO++$lgNnH4Pn5TB3B3>E5F!Z8iPXYb(W;Dd~A( zakZH_T&(h|nJ0c1ISR7-DukXYx|GWFndX0k4{$#wniSJJRiQJNv@-j@Yo&l?5*{z1 zhP5?Mj8FV1RBNzVNas&moGT)hF3 zQ|ec$C|FJLMD$Mn4dh!`w-6$WG;FJ)__$NL#!$U-^eSQH(p%%Bu360_T%1-V2g>ww ztj<+S>2f8dG%Z~&8xqXF87TqTvH&?K^^Gtz*TlB7f_+Qy`W7OLUoS#c2?;B8Ytw61 zuRkD2m)53X;x1p7Gxep)6*#lW$#Se-!poQ1-qkhiJB3zx7NnEqHGyr$#}ArAie<-H z3>t@9UAs0N6l6;8pTj62Xn1xj|KOY4^t1_^GAVUWgjqhcmbX?7$dd?ocZ(1sL{!_` z6WY6Sbl-_Q2g%+TDGEMPMXHTkI`fHH#TmJ zux24Y1#+~zRj7x~%iqB2&LZ{>z!*bf3M~GAs!YL(wb(+BZ+iV*G^$EJB5#2`E4BEY zhWIQ+<26`a@TYy?*Vhn8eK0!szaa0%AZxU&oDBNTy1_eDaBV^3lANhV)xSM=h-Am{ zqH6IGUb2J~7eCKCd{AFB4l}fF_i|Avl8AVoRoIb4N{g?s{Nquw9Z&Hf;?9E#msh6W z<15jNYgLblAmZNj-cP;*o`f!=6HMy~eu%g~%+x{rUs(%(vWx@pZ;)sDL)9^mhd&p^ z_$8kVWT4oEw_g{#b3!!~=5z0>~dXJDeLPG{C~uT)I5L;EUwo+9t!4%G=FTy<_E zw9d3{WHOVDh9q5D<7m~vvAb`tufKgcyi_C=awwuDcG!E8iW8=|kVBgO;r(m^-@%zk z@CkWT2puK#&Z7wl<&l>UZQLK#9(CCu5r_GGk~WXgb{2hmK6D57*+f$#KGJJM`F2-K z3^7aAuUWik?ZL6#llAqJlLlk`q%#jI#fA-WhFsWLR7&i-CB8`|s0lf;XIw+U@4TBW z!4aw!-m5j@iu$kYdT0B>!f)Bo82_|AdJZhJ?^*-HRyHjf*7cO?UXT`fT-2sPJB0Z; zAAb%=`o62~?RC5xW z;Ra#whbLpVQ87^coKGSiv%c zz2zHA_~k^_nj*`ehgCBfU+oPmwnB(>FDm={&oDj=t9)R1mLD9u2SZD4uABCHhuaAi zNR&l=OvYdE?hNnNC1FF=adv{f(rnt+9J`RI9B(5&#NIhIM-@#X2IVsqu4!>t8a7rO z9CP1WUw<#mFu-{Aw(@rPM85kS0hA>75doq;$!W-CM7Q)ZLQ%(7j<-_hy`ri+YU(QG z1OAs0n%IW*g3`Jww;OIENF;zuElKVoMosdH(AWN`v~=^UuIfCyBBpYhzl zBV>~r>{nt@Ym^`H;3rch%EJuT_Vtxb8I@lI0Y4)`y)tE+dfh(AC#e!u4}oMEaN5CX zLvPL~)DlQI;vDjlR`n1tA$CnRk5g$gUy4`~h5JGYiziy=<51ocBMj8H`@p@^1gGE)^an7}ANVO< zhbK~1&wV-$he>6j5KPEn!XG3>`Q!{)A7_L!1aF_G>s2U$k%6d8ZlCc?CLX1HldK_j z{ud+*WF&C~g?n`X1e9;eP7^8}IYFk1mG1{RLHM2Qb8wIh@|0>5$YS^24|x5XUJ_vG zjPV#g8*Bw_+u*SU#j>${;A;wKOo`==%=0(#rB27bq$Z!^q+#uvzFh9lsy%?WTuzP< zCRBUXlE6?)SCh}tqU@R9vmhISQg~QGd#40!K`zoMz~3=ySNXqHn}t3Mt&3=(L1&5=B|nxn z(Hv+T%+Qr{_k>1`Gus8&W**e+6ss|3lva@0Fjjq97MTsRd95b?#0dmzTI%^}K_Stq zE|9Z`z5o`=4zM=8&l3+{A5muw+&n;lo9my7tqwBtyLVUO&Tvg;j^`oO(wQ-wg`ur?S^mFKD1AY#U0+Ew!5BgS0q2V7yjUEnf3NCcZ;pU|= zv2n4lvI@^}#heV0spMrMZ5;4dq6x`INt%>|@VHpF^o0~=Fx=1CyTIPE3a2C-S)_vE#%*t$wDc5eZf?HWoR9zT zr!>|Ax!%YCA&tLi0L;@M0)i4l0ZvM@EV!be>l~WkfNJ2^&Q(?G>l))U39;O|Y{&xBbCwR;wFPqC+O|6xg%6|l-wE7~q2$8H&RaV>+6^3`Sb1v>qz^2YI6g#(t6 zdpIjC&1%uNDe+u@^?U^6N#pTIiY`kRlS;$So5Fa-Q(>YdUo$^lw9%T`3G3N&sH-#6 zx?$nJ*{3R5AVOOp8AGc+$36yf>!8YUVjRK~z<-JThj_t8K{p^aW}aTS(RwDY zr@p_ysJCQqSmVP$#$E^Pr= z^eSpci=BJbn%1F$^U6YE$-^w#)CK#OEG3C>l;A|al%1p)3BkBRwFrwYW^Gsew$>;~1Z*o>s4luX0uWI>wYS4vZ2VyapH;hNqe?sc|j_!;Kf`0%;S zi+dKuM}{^n=~=wAPos&8Nep+jRT$!&hFDEpbVB$Kn%Z;R?uS9zuenE9oKXPys#jWX z+=IQ1#u9KBEry5PMe zE(Li}C7PK!v>2h3?{vKE9H?6Fs*X?9#d0@C`j_YPFJBAEoe)Rht}5?Y6Cg4#5d6L-Wgvq zZ)UVp#J+BW{x)wDqA1vF{RXvbqfGNwpJs8pM-PvE}SxU``S_Ch%5a=mJG@3P?=jt<4PtOS%P2oBE0~iB!)-h-4wpbkjMKzv6IyD^ThjFrQIYfJsFPw{Ei1c*M_rdK zBlGmYp-LH5{=JS`2#`b{?mqU1WIw?D^$cs|A1WnY#d^;mCs`JhJDe`d!hxdG_WAXz zv9nkKzY32(yn~Ikm1h}^S%FrgJ)f&M>?mGQX75Qhtab6$=f0O=P0O)efE;YJ&CJnECZWtqOM`{6v%Hko^`G&0 zsI`1h#^yEcy}6={Pd`oVg|b~xeoDNpc1CZ51D9-i;$y)jyf*k2xs{jkU&ju5ZlW?C znPFpyvOfN}cWpydh~}is#M-7SOY3o1Tf8$QE3A4`U~P9&K~(Qw+#e5eu#4!z!j2`a zDXT|Z=Y@PTRe_OuxB!KaoaJ z7YNRP&H)?8h6Uj$nf&iE1Oss64v26g1UBZ~O#+~ESa)@0dH1dR{ zdnvaHT~YnScWyuwp2#=y>>YO|fnx^kMTD#O3snrDP5Anq=V=G(=V9`0B(IlZb5c@U zdx54b|bc=DAlj?s+Ly z3%P!UnR^S~uQ>~u+SKHvM4c`Xlg@o<&{6!Vy>(e88aFnTx>6bo+@9y?)9Lh;UGVBS zr>sv0&B(EjIMrl7(Im@{>Ajd_8Q|1Gpd0_I0Qxq4&dv9L{)gvZ z3Yh8$4pDC7TRxZD9)zn=I5ED2H*{y)w2(V<+QOJr2_VyepG-su ze~pFXN_tu;2ZYP51rm|iD$>f=tsL>c-h$@6UduqcUMVpGD@%~*SFq3VN?+2Q*JmF< z>MJ@sedfSJ$OudD513<=zv_f`ylrE*YPXp!nHgy%<2M}N@mk;F=*Y-8<&z4QbFwQ8 znb?(g-`tAg`S98w$8TGmn}!U!6yu4BtrHVj1u^lOxbWEcsCn^j?84i>KQzTcMsU*{ zK>3E$dfuBjUlWo{h4_rjdT(@`TOM6GR9ORe%}XUAmEAH`rF;)>o1t41eSX8#(8o7x zo1u_5l5Gi5v(N4MoA}&@-07;X2Z?;U&dFX6!Fhnmq&GeW;_66up!#~BheN8B3oMqT zf=$OD`m;N_92cKd-v-WisaHcuDOIV)6Pveptg!<@6WAMm4~8Jl!}{aS!^~KciTJEP zU!#h-r!o~=<1XG5-y<<%57)1>aG#O(B)B1-$X(qdwVa!D9l>>+exH1%USLYVXlghw zEVKAfXmRUoj_RG^Q=tV)Y)@I>&kbSJ%R$!$l68qS=FHs@2@5lbXxPS;Fu4o)&Lyg+ zgT0X;ElO7HP4E$2y%zf-N*a>P_+SFskNB9>+h~N(k?TOeQo?cynj>rY?!1A`?X9jA z&j#!`yJ#$SzTEz*4$tGH)hvuYb@)y`eLUMoWYTq)uQIUPVJ3J1+_?DhAtV9OyPLrS zrT5LsuL&cfWvo&mH7v8+<5V$(6Y;T`a3V;v5~h$oiqghqr0K0ECU?!>P&njv4^d7; zGQR(ZLjQ4)$wrv`66Py7r%?02X%tX2hSq8bP6#1U@*F|^I36b0nr`=?H1aC0B# zXQemRuZuB3vpJZwPMXcpU5!&yabA{WR|A90SMrC4?Tn zt3N&WB+dML$BRh!ex!==#~zjD2Oy0exnd$#YzY-x_DlNO2n;q2;IU{>8OpmslG=7gq?_!laM(|XqajTuoy+KUx3;c{WR!+%fwjh{(m@vN*D=co z5b$gv#Uyzx1rb1bAlSBtr%}pflHKx0XC-tU%50sgR@r9U>aJ}aI*m7qCHA}G>v56= zwI=c%WL$AXdHnrStaxRIV2Y4JNbB2|NCt6134@G%OLMXPKb(I)2?WOSZ*q`z&h5hW3pm_7X|rdAv2-;Mt-4tM_44czv`cJ_-P8nAbjB z9d9V~W}6*zi)?VITV&6~C?LErGccXUxvohu8|`$d5@rw86klzYkqHx$ zeMV2H@1P>~EJNH$8p_l+V@MdkCXRdU66&3tdLrylS0PghtYY!;{Lb>R)w9v5xWL%b zur3OtSqQ&+*r%nO?*uC%Q8P4Qlcic-FQco5;FWYUs2#W&>Ia<)p(|m~WvljR!`!;1 z74Smd#ylw7Bo*uGE~VW2_DoAo(Fk{g2x_sIaRJZw0?*I@TR%yluU!Y<_t3)z}g0q9Tn?ef9L5q{A0-Y_-itRt21U ztIy8LTH^D@hUlZ50)4~|UQXO(!UMibsY0)Q5a7o&Q#eAEQyBkSCrmjzUAH_END&zD zF&y6IIKrMMr-A5lyDJLwb8?Sg8&H81b6Zmy|LkRLBsS6xq4FOE`LUCgPz}vq0h$6r znJV=!XoV1b>#uX-@PxazIwYkc6=dt)g4Tk;8j=|4HP@_490-+@0*ii-qgNK zUd%Dm7r686i?CbZ^@8^<2fRuhN^=)%um?kYHSvsSI>EC4QE3a$|Q$@E`clB%I(0reBPn0>T zR;R|#jfel-l@PB<04JROa{3sH_oUEBgWf@TJGMxO-l7hK-c1%XHKovG$x2F1PjkPN z)sfxnEVk#BjW0^zK4pgv-p%Zbh)KI%7a5-#&;4_gMc-HiH{;5%`uw)~Qn&jIe)3n- z)OxasX_U(oz;;@VSmg2x&qGY2L_rLaa#~e*&wI=?0TvM7`4u~*R^*pox`7!OGOIe_J>6;Rk!xNuOWE`F zyXHr6kE%|K4->g_*Stm=J$dKYw687b{@kqB8BD3%D|<{Zr>A`*NbPM#!u{ckFUXn4 zy~rK}4NSmj_1#A_X+a(p_^sDhStPK-1``PEOmU7W6?XGu`70WR9EJI{-`^R{9cRC{ z?3IStJ#1R(>LQyy@dmZHsh`|kS20*#wj#A`c-mi0OLby0anuz^u+u14 zWZGA6NEsB#^dissTtwS5o#lNqtAa? z=u~{K@748_+eY_YH>s_tSy#Dpt(JRDaVz)dgD+_k6Ez<)8@Tr~Q_Q-=0=8nRib;yz zerVhJt*i6%me-98gC+kK9}^Sz=KWfoCZ!G{($|o{zY8*HaX*WdkT(Vq+LS~TDicO* z_TA2xD|hwpF3iny)MY)${)PK|$QhfUi7P6K(^QsrU?sN9wYbAx9ig6AY%MHgfhV;i zIx-^qcub_J7PbQ9>To__-%(N(`o_b+`V z`kVPCv%!#+mM^Z|s~ZYR`%4`QQ1noOS#L}Xn{!3Nb(hlssS>Ast_@?=@b$>9U|s7eXo zQ$3{bIz;$7*(n{jB`fwLYq=TlzSgdaCk8 zqLHNcrI^I2g4Kdj z5HbPjVJ`*Ym7T$O0g66JKd)UmrEnQLs(M4@1wcedPlCh`{4Ln4XsyPaFE;i{kTV3l zs#AG(YKx>h+f`}${yw|OT3iP&64y0Bc6TCC zqfKmXcotGR*?Yuja!Z>n^ph26D`pE^MOx&UDYj3Lw>U|dp~s0#`k#|$Gy5Laho{)h z5uyUQZ?BGygSRrrp{dXvf6yewMWv+Gb+3n(3o)^C9|)@`@)U{QTBcp!vg~kMb7Nvj zU1Ua<=%Cvo=PJXi9C>cHF*WU8eUc%Q_FmALZT8x}c z4iXj;CQgYaCTPJoN}LJey)zLmC_)YLZ7LofvK{z}I7Zl7Hjn!@x^iu0 z&1fBvS(xhP%}cANYL#zKjYy=V(}T_M%%@^mfn%tlZGBO5&IY&Vj3^YvbUy=`F#h97 zhZte-*zZL?{fv&01rc2#_DdaD{h2q&`=FoUlGc@Mo)TgBi(~5lAu;~qu+(RQ;qkc{ z{53j;NG~$~M{H7|Oblk*!~h?NoRDfpa)RdtX~duQgoc7PK9e2ze9fkj8HE|y8l$-GKsRQ7c7a1h>BG{sboDrA<7N`{Q`#~`(dUR8wKT| zl%(M4=o-o5*$UvsB|bbhc`nb*7yxdDAB_6CxUlHG9`!M>1f7`bhp2vMQv}XFC5BKR z^P;V@Fhmga_2RT(2rmqK2}=Y5WHUrdK>UV24Le{T=L73Zaw^W4tdYY8J2go6sv<{< zJ1M2-;t7Wr9IK}BDu`muHv;AVQA^Y%0%IyKAKvG^FP{_Ua-IX}hHlvh?M4eaSi+r553iV}h9Amc9iriAbI20Ma7zf)e z*-p1P8l$HkRPI^&8_y?#i9enR`Us2nvG^xf2o}FziiVJyW)AF?Cg=|BMTRbsA_glt z6A&?qkzI7D=3sO0zCE!q1?F(^^w0f6e?p?8$@5e8thd8=OdYhZZntJObTIK#*}R&F zFCA%asV%VsvoEe_{F^vW6*Zs4UG$urS^yn zmD?je8tviV4*Y(>3+VaxgbLT5PSwUcPRfUxxjqPHl^~0M>pOdDSHwgYSZAfa#q7WO zZylRk)<3YmLDqkRWc}sn!++@K0su%#dFjcYAw3i}i15Ukt74+=uOS$l89M&fIpHED!KsK9jet3(=hj};`2Od?dliGeDiy~L}Arx?{$TG zkvDoN^cR1u8ol_Ypo2;H0=BmIoVes2=3SDv!=+MX$x%3E83UCpK(Y zAqN#NcqNicwFuSo(58@rs+UMqF#`$G9GVRU08fIvm_xJy?q;U&1xaJPJ=D`uB-M-n zU&CB4M}VMDx6WRkwip^-xBE0+`sIFuzVk&C_73@#OtDHl~9+Z0l)j|$0A97QWw>|BEyI&!BRqD~ zkLDNStMFjWJYQe$y!k8a^#WpL_?@adiZL%A<&qc%7eCGsa$7YuAKn~({Q44Z_ zyU8|qMH4`rMxw@$xZomyPv46yzBfe^_Ky91$+F%=e{g`2QOupPEtw#eVxq4l0{zL! z$bD1am5!gmt#C4FVLfi%1sDYQQD1zz;+je>ppNL^+_;fO}hm@G2D-8iXd$DKV7aEv%;=f1XD&xcU&hl{!4O{AOwq z#&YmFzMoM87}r5ggI4;sh@)hYyibw;of31hf*2=iv^ZY&SjAnvVoHb<`;3|(rND6S z!)88VGj+I&pV4+?~A_6FDN*#c!b3^g3JNOnK0;J1e0y`7h#}7#!WIT zxa{o>j?S=FohpHsqLfD}*KV8f8>wEcu~;-p1N$5K;o+8zRW7G8W@A9~QE5hd?)c~I z+^r`#;j+=1K1eKL7+|*@o+ebVh@hIp&w)!Rt)96&>qJ>ZIJU+qwVjUBZo~rzcxyh=g=* zb8`K*mV2QrII~yofj4Oy718lZHXc4(1AS!pm5Tpu2;}V{tmQu#FTxN$Sj-X96f%5d z!PVxxI{fLZ&bP#-P%f(G9!pP0SA-59X>Bl25V#+bG*w-RZh&G-(CiVgICv|WPI#3U zRPPf(L^rv>EyHEHSy(v6&l2uAnCvWox6pMgtQYhB$KlYJW*Cpo--gq8e0^_bTRa7V zke~rto@pKe?~$>9^{5idke{K*0}c@YEUhc?H-Wb%*hNw!gDo<_4~cpXq-5k`z+~_G zD)4)vEA93qivk(1>}B**JS%GEAsr*-yT|M~$*Q{gIsOMmyoG)m;MNiw&HHwOfD>rR zP3$+ zc!)9pBBewRSsjr!3UCv+2za!IXxa&zfl~&>J%-{vEh`J_*g{{23!YP=7esF=UCs`2 zZQN{+3+D(>1pHP2Bd6)@wZms-LT(&;xYI?hBv%uF1-HS303&@PZ0POnG!$Qkp_9!Y z&m_=2G|k(;bo(rU(*eeGJ3BJDmCPgIrm@tO8|t^V+;@uGUtp=O9=z|qkVz~o?vPaS zTEL&x3Q&sd!XcKFs2?Qac6KA?_V&$|l8%n`pagc0|7cY(3|`#x7rUK_4xYnO>P_b_ z(w>?U3vUsCk5EKl4hzwTtPh}ZLxd~RNSE+ll~-4x&r{h2Q@v9o&r>nbvWWlkK`Omo z(K}S?FG~m&u~#3S_wkKlPZ{&SSd+36>>t+;rsS3r8bUENPrq! zSXk-R@)XRO*;}v@=(ocEx(1+tmuWbIhhH5+=Ue#NxtPyD0)p=qJjGv%z1~IJ;xo%S zHk8(}zaD?Vf%kSj23Sh@c^no+x<>2Uxu$M!f#C5jj=D4d%m zH(P3Nq0_kuoEG#yV2xo@26{^JCZ5vGJ`Gd4skXuM3thmUFts>Phx7aBc@!Hy>ld)y zaNktd*su-9d?5YIZ^2=JE3~fLcwA)d(C4!dW5d+-a@%0rQI5!z!z@z9l%AI@U~=lb zi=y%d>sgiO|}4${0!7vO;N=ox|>@)5pc8o~o>Sh2bX^#HE8tP2~)IU`ym(`o$>%NC96 zFvO^$VHABRvXR|0l}VG~dyZ+|uz?y&O55!K7q*nO7tQlx^$#_wNqiD8o5Aq1KZdoP z?JG4hoyHHp{NRP}{P4dvjTP-`Qt1NtoZ^0Rbx5-aZoI4hxxz8j$Qd*ajE zJuD63^o?JXsj{KMoUe7uo|?)Q!kg{XR6kzw!7(ini~eTcI!$f5H@&n2o^W66@wVI` z)=R>TomSPZ*CeGn2S~(vL7ooT+6?f0s-Q;(Fr{zv+*-N|nKuSO_X6N!@wbv$EeR45 z(qpV)Wo{w1qg1a-AIMQ@UP8M^wp^hP2)Oh%TjuD2J9aZ0Ti@W6dFsaA#6$(lU6Iku z9Va&zl~t%yC#Y6z*t;e<*_NCA7v4aKM|Mw0a_}^7W?#l>%7mm#d9Q2?ZVbzJ1?VZ5 zQX1ac`VCwVPJtW3iWYF-dCe^$sFL1vw`@#oiAg7NK#t^Nn+O8+U43qC0(X|#12%bF zUsIEtQ&W=@YMg1uaX_8~Ui?i{OqR*LaU)QnUX4wNuuc#9SHi-Zl)F*#+&OXJo@ z#!x%WV(2XB4Qf54Yr#HK4R;HC9p_c6@RZ?zypw71Qve?(&uR<7MScsv{-Zs5(~@uq z!7h-od*vtaBNMs{WbD>ZIU5ZP8z_MLnj1#qd|A>7Xj|ht#2|`$*OHTqD2#RZCE1srV9vmil7w9c zy0lr+A?9;`<_pAA-~qhZ(cfc_#Wvi_GGGbPK*h5LyAC%`<#rm)i%Xh!%&ghcmZn!H zr>8z3Wl%(=!(Gp{F&?8W_dg`HIVDly z8ir^C4JR#*TOU7z_qE>!{BUM?1-2C8z%(qYUlN*tC3$H@N*17osr>|(%f=%vBfE6S z)1DnQ{|)lJVcTaG@wA||J?NgvyEJlSOzxi1Q_@g9$Kvh-3$N*jUI=+hh_j#guECE7 z?i92$%$y2pt`x9lKjb~+tS(FACd#J`D=jq(kY?P?el}jOKfxfTJR5E1bGclBNNdyK z2?l)$<=8Y^dOMLSIUB3UEPhl-cqSA5jjbo}ZUw-v=k1Gs0}Qq=L83*j$!}R$UMmaw z%m42RyuE5^ZSz(N`S!-zrLei`EZfX)uHxnpF+-_Xah;TM8v?+0WtUe_u**tBJFMhX z-j0>VuaA!QP{4VQ68EiTfftQunNq&wQ#fMSG2Pvys{mT+are9iw-bbFFi`cpeb()l9Iv)~0^DFjJRI<{I zl`a;M8aj#zwx3#l={@nsZiRjBe+O@@7$SleLCWyx0-WO^=00#8ew4yt6nH-@Tna3n zf8yjvXzTvMl`R8RO}i*oO{=XHbF*#H5x#zA(2})vc)w3Y@8+7U6tiAa$RYl2u*5W+ zLVXSD`c>swH54(I!|ctFW4scOOu(xtS*zBT<TmWi-X^)K!LqWRzmrjmGS>GzdmSv0tZp=PR@eI;7GDXrh}%m&_H^MI67t9n z!P?7I#qE`otvf8wiv~!suYx_uOY{51Yz9bxp7#%B3h`Rlk-Xxn?emmz$tIU2)tq|L zU^ZG@rj&T)*4o;Y6r~+a`g+?gH*0Gi?~rN7>1Fs`vVqi9t*x7C&}k;!X?A8C4F+R2 z{2`q=joNc4lI2={QqwPkOceFT&H;9Ke(_w%e8A7I!a)ns{DWWpdetG^CSXQXSkO&@ zqMm$J@^d&5x#4J>%#b`iqIug~ZTi^Av0?ca;H_1*u;uRRy0QMyYur|{by$@=CB_3L zQ$m)(bm~+?^H7WxhN%Q!F6BZyuj7apAz&YTeQo)*%DrhrbHxWIk~S|As?*(yl=JUA*7 z0}MO3fcW^<{7N7YnuTy5vja9PokhDxYX(#6d2VZ6Ytc9KB_>9&zIJA<6}BneNrE#U z7??mP5ZJAhla))6$Z?SvENGXKfD?@ih(4q5nOwwgtD+n?xf385A(0w@>}LoLEW9@7 zUKYkbRW8ug4N#BnF<$0hP`n{YpnxWzc@L zjca$T{6k>{j79oNn&qDgz%hJ-5Z5$~-vMSpipD2Ba-MDt^@Lr20W)JfgD5)!% zio)tTUu9N`#h}rXSN+>d(_cg0>{9j0{RIP+gUc&3H1Sb!nz-(@p>1;|$y!yCS%>FO zxWL}L1=w^;mH;l7W^HhsBkH3Ie_LWBgaX}ivOJn_nrwd{;ly-I=EBnz?Zg!M{vq8y zw%yNv1BO*?Q&=Gw8W@)n_D0j`{6OwI!QUa=$i7N$vzq2V1%q^nI8NIc_-b%BU=UAE z-Tg;=H`?sPH~obOR`@n{FMi{&sa&Oq#f$eB;g??`hI{YU5 zLG;>Vig{#I=aH3}Fxw}R`<&B%=dRi28xVD}_@ByJbpqM(?1+RX1et*<)H^KB!lZ*1 zhHB5G=y@`+0WcgsJ8+mERPB^@_yg#|MWu`gI`#xa&l?BgxSzhj?dQQ0htDumndQ zkG{J;VfCfJ1U>o*t%XxVMI%fMm4VMN@i*)NCo_yfBamSo21Mh%8zG4)x?%P+VMns) zdNJWnRxg}e%w?kUOnHu3pU=mbWmm0hijs>V_E_g|zl)ubQ4qp>_Twult0)<%`u$UrtlbyzI3^p~6mdX19LLrSI4-0&&&?aMqf_v(D;R@XN z4?)QQSi++hsX*LM54nJEDqS+cCOUbZqn+2CfJFiEKKrJASrB>`JT8K}h6qajmlQ<* zC^QpBceZt#F=)dVVDTC}c*a(juWZEAoZJH~Y%0Cr*BdwjS64K3^HzFG5*q z&e%G9z}Jj++M^@5=g`h99VA5zDZZBkOog;a1@`uRVP_= zI`Iw5ag3Lh>TjpHmHH)5)rZ#|!@phpVBtEY72f@D%+~{#~$lH2Izd7YX)u z`Y&4g1pioK>_Gv2w{X#u9!rE?HgTN9m(5)h@>s%u--Q2Vr$7gpz?;Im>chR32xWbs zf*JIz*qIVxEITEv&60`qs_e1xUbK$8nmIZ!F#foJRF3cmTmr*+JP=(mI$(7L=fw{* z^>0`e#;3~GL+?qBtk&meFh6?pxyawPnd2jZd1WUm8+ zI8!?J|0m)AO4G#yWW2YS;+f zSjUSL)&1;mdCP^hzdt^l-p80iT-e|8?<9h3)(1Y%oyE8rtO*_E(>%WS{m|o{Fwuh{ zSh{wK`zBjFbeVq`M0DB(*&W1E<$zZ~-uZ=8-*M8H&-c=b8-2{~@R^fPPx6cOsZ~tI=cSIf>|EuT0xKD30w=i9(vcGUY^+E z&E?(|T!<-q*rkTGd+4s_0E&;6u`Ws`H8a1Y2K;HNA#wAe*AFodZBWPS>sVL*q}jfrvgc?!0g`=x z{Lf`_{KxMB(uQ27Qb;RPP1%{2Rkx>`tROtavE}&;`6GtYnSti|+h1u-R}C;Gffe#Y z=)jW#+$xO*C?+mgXfVL68bh=43eBclWWTn!ld3bgJk}J*Yf}JL^fMc`8Jr0WTU~^+ zT!ccrpw)){ z0U#xH1%Iucm=8FFaI09mWfHJ*WiwDjOfh}5P;V0o&AS5ZBZ!5}_>CKk2Og79CVdePm9 z5g>fx-HDficSORaFH)SI0#ol)Bt6aYTA)V>(4#j%4Jx;R2E}PE9719uHy7DwC$HuA z*==Y(e7js~(m@$1+_h(Q7?Q?+BcH`@w=1)g?RJHEbh}+@YGAwF^*>2AhP`&1RbQCv zUv+01XoFRES)x^UWJj3{Pr>AU3L%`lM0{z$MT(m+&9Z;vhTaiqx3R zK1Llo-Y5GU>kFpzrPce|W@>_qUjoZ^cgMvUliz^7EEGyStIh8%$HF>i&{x>AoL&e) ziiMVL0_GRERjh2JdBg4qd7;G^2F}7gMZmYBq+V{?8r_geFTUraVipOu1Ik~q&pL7+Ev{-^m!UB(E*y5nd#qABe@4}Ym3-+e~ zJycOJt*4qm>9_>uA*~ex*;h;+MOm>^0@eL z%rsx)#M3DW$4~rSmIPQp&y}BJc-2E@M_>cRJDZnyE5wK6S?IaROQ{EL=$(p};BJZ? zLnZR@T$%OQJ`{FGb@i?uYxq0LOf{vSI`t?o#m!U-sP`6JL%5V1p{*fsC9-BwH32b3 zeb`%YZ^@XIT0gjL>_u58k>nD7?NfY&(Hias28<>rthl5a=T3-W{Jkoq-|dn#4m1=5 z6|V6;@u{f<*_~vM~!3^aw^c- zjEs0S3=D(ij%0u!S?)-@J;idsrCYKzVAq71iI*S-B-M+VPbkHUFbX?BQL!ASdz;tT zGPc(4yH|ZNR3jDRR|TUQJuCkZ^8xg}RFSTUdVF_uY~tqCwkhr{6#2js8tkUy{&%b! z-XR&_8j)1_msDJ$9MOOkmyx8${Y4|sKp8WIW^-?u?QK{dH(tj63o1$C(oxQm<|*8@ zyd2z$Ng=+U{RCtt6k<9Z=hE~sg_zh50b&>)n12Zn|5FSwOc2x#G+qQ^!fi6TgIk=Z z$YCyl(TKM!^H-9haQUchI0Z3@&amR0IT|15TO-p1EyirEk+Qkrc%)xi#ZP|X{e8Us zs)f`%ru6suX8KEVhp4abFNCp=gng&dI;@S*B~5`&V!Us)ykgpokF#o1Ql`X?K3Jje z$6SCNt?C3KJRIU(g3m--qOije+D9mJ;nGy?>S`&Qni3X?+>NHn6jNvJ+~TnhWgfmQ z6570QJ0uq4uI$p`+EjB`6x3>+LDU*~oYbqRMAat0@fg@h_Qg(0-rpjQ&K zGcAv_3E4e*Ti?NLmGKpTpt86mHJ0!PIohozq39`!;(W*Ure z0r?&t6)O8UQ-*690j|4Lrt>EqghCD^$>1r}m2onoHY~4scBReKX~*A|?S@2z_(9ax zVRceC4g>RUD962Y{F5l+nai=C)erFcJ>84EhPos}fsLl9V^X$ts`PQ!_9`@m>B!q+ ztJN82yU1G0v3j|6+Luw)8(Ts;Cj~b-3I5@iTvBh~uYhq#?QKfM@ZVvJ(>j6d5r2`2 zzlOiaOGBOB@`VKoSnfFeEnkEJp|?-@73aacf1Axr-_P^4byp9`CzzZ8#&_`4)q0ImpZta>~oS3kwg=5EaI(c4nSZ{KhW(J|6 zjTLZCAcIO5$luw9KUtCa?Od*%Hu2OE=Q#L&|DJ~9QsNal85swH#E`63)vWkKdj ztkc-#Q<<*1Ro1djip#PZb7vN@Q{(ewiQ^0Sdit~mE5n&hQ7hN$`5sDtZ5MSM^ai7gd$^cjfz}Y`kwF4)wp!#>5$YETRpP}=;%Y-?FlQoLqXjXQHr zuI~});jJg$HYuZ27OgEK-D1&(DRA_)M^%m10S0;(7P+n3*jQx(u!$=fZ&91LAo~(L z=``4Ru^9V2A(dHN>k)LMz}h&^H1)t6|3D4A=99)iV(viPC{>u5eqk}z}+`ZE4{Pm7NqyFACui*y$WVKEc zU9rTweRS6@lsn?C7_MwsUO40Dj4>s}h6ivA7MHKATe&x+>@hI`6fh79SQrD`9^`1H zU&HPv2Keol(sj}(UMu-KR-i%!?)l7*bY1H+ z*+Y4CXzmBdy$>l3+VmV2Wc*Lhl$1f)sc~hIIpap|Nn@%}lVSwt6JiH;Rz-Gv+tr#QdW)r5|$HEVRP1Z8@%)=coYKqk*xBqM+a^c*ud z;|aU};FZNo-42y~aidx{}|*#*4;kfjN!OG5+yBxV8NAX4*~ zDbJ9%>`t-B$c9>fOLk0GuQC3x>GLz*lHyfO%_Ajd*L=;vUMNhbG5;QWJ=K(qvUFPX zqX|ZSl9gxx9c}R~D%d)Zbfa^iq+($~{`^vRTY5(8toBp488pexXk>F}jEFlbJeCCG zM(BQu&|TOpc&U6t(d9GIepfRj3Z3Q_*xSj{L5RDLtjys4EbS(11^<_SaCq_S;G39m zKT04F<@KDGU{pq5PhP5?6M_JLY2N>EPlI*ibf{$GtS9snC}z2@V$KJ$mlwrid#R|Y zfM1zSC0X_|!C}#VBMp}t#`@iSD(2H+X5>A9WHWi*cgjGj)X8xwh~S_~z0=KFTYUeN zZo3GklUtxnS^OhE-c77g)t;hKHSuerjjd!3KIj7BUa;QvhLb9yf{8F$p|gTI#o%>e zWt_jHXkJc*yKw!Vo-eJS8rYg~95irQo-qlt=acgz9TL=HQ8W<8AC*RZijmkG;uX!1x-x*<_f@1z*~Jw$*Uwa z_7r(vH*8-4b;vw0no7+QYpT}Dy-r-ggaIV}^p^CQussBo^OLywEXocRNhmD=8R7dv3@Z%FZonv+H$Mn_h1v{akO$E)5R_ zI0HB{&HFtB4;ddQC-Qg@F`}qU#zU9NoLTqB72edcHj2pDtM7UVD%TTC)Hb7mATtRS zr-l2^np-bR@i-|iXisCoDE9?H#$~105T8=UBDj-O=+}p^(O+BR&7K%=2$vmXAf<2x z%pHDHxAa<5Q3DyG7JcmY-z}@+djB+&0Jy+!+GaOeDJ182xL6Om{8|DE&lmEC0fubI zlhp~)D)wx_sUYUyPG zlgKU40GORwzpmLicZmSA{F$-Y1Z3&n3g27Y#*JRLvT%Gch9Acqoke-0UCP+l_*jff zhOKQwrGN|fEkR{MQc{dm74us&>tgI{c)V~<;x0m4E7-!kVA?8R)wea2Go~bPdC~IU zR-vlbhg!_jQn_V=<%-3N52ZCtNonpeGI`VqwD#{LBn;?QVO|yDpJ0v^!$pbeXA}4d z=8h6~R-X6Q-e+>Vsu$$it#&PyI#ze{$=|K4<2uf@s?iA$yVCSDiz!W?h8AQvZJD{w z!5Xe!f#C{w%=b0LdwX*oS{T8p65$w@o5hk`V|g!XJ4mGU6*K{?=lfB;Vw#rxKHf0uB8 z-(e!o;FR#-xpV&f2!&04KkfHHe%SivvQp!MV!~ld$L@I;Eg+-!xI>gi;kL%q zJF|J1SFR%U%cYcLgxlDE5Pmn1iJqPyju)#y>|;Q@haX9>Fm{JHOrj?e>_12qtZkjX zu$B<;$MN{TS1-!s8kqM627JG;MM_U*!|+R?ng>RZ6!XfxCIZ^MS7#0mKBiSFupGql z)h_ye?hb%Y$G`5Rb6NyPRcNLbt3e8;jCKHH5%o&~7$pq1s6||U2bK!DJ)&b{N^Tj3 zT>ij?{vS)`W^%KbwZY@y$2Mi>WNYOo(IX-(_{S4JJ(Q|J(_ z9O4k{yPJ3|Qs4Hnd!VG>Wa=*>@U)LV^$^m@$hP&kd zEf@vt>om54l^e$eiHh=HImkNj^gBQa7sSf=-JoCdP&+j3{o?B-cWn;syvBSl#$5JJ z+pD&UNR?xK^WobRK`SN*C;w^e)95|Q#!~Kk^cS5rXP&p*&b>XSzR-;-4Z37)s)6)e zUuI|eeR7JPuTriD zJkx|qQUaY%Jw1YO^$NGgjgxZFBq3YRH!-i?9m~BFDvi6iFMJE5HvcvkI+#fb5_c~T z^@Qig>|1JUZ{e$_ES}|@k0&|%!ySJz7=&mV6@mjM9FhW%(p-Ox4C&yYoiCWe>~Ueo zi>N6XlEA^MU8r8*SGfq#{Q;oM!S6@HqW}uN-rynAi4ozcm!c!O>U%O9xG*Fl3KV$AUI%cGrvuWz>j7&jv5zd@j!kOt#&$Fb zbooKL@!^`N_ggnkx$I%f&iF8x(A5tho`s^l52T(Bbp-tCO{l#=`zOL02?pX$%?y5O z=WE%Q+Rp6fzC~Ls8LkpJrwJwZTijQiBI;htlFIHX%&B;5dwG(}8J^fSXC_Z=+?(zV z*^LQlK`&O80NACFTgIzM${6D0yIfCvhCw&#a^_`Z#zS!+47r`Yf9KV~Y^;X~x1bw> z{fNv=W3a8BRx=RN(=r!r#-s0h`*;w?(v%dO8jY!70#T91b_J6D|KYZZm3LW6j)J zv*$z@WM2kkLw2-Zc$r6;>~WVqfIE&WU``t?zUSv;X02V`B}V^EZ$Va|!&+!E&PsEL zcevL#`?L69+1_EX#?Qyy4gt;T&pu5@3NoyQgf>LCI{p2|g$v6j=>T>Y^cVkXxH^v8 z5cQ0>&p3CeHH=7odDu}7TuASVY&FXaSHXgmqlA!Vvs$_5gs@>qSp3Ddg-!KwEq0{QGG^1`!brtYII*vHi>yDcn;b zfU-~fBBOS$L+3@~?;9&7lk%fn`(Zl!9{c9!CP74E{QnXni1$04+!Ggt2%>)d-048# z@ApqYB|JYIwz^O0O$&P(rqwvanHH08AZ!|-c_^7X9Rj`VZ)1OqI`t`T4q*o3dHA}oce%g{vnI-i2VYQ4c>d# z%O^o-bd9sDcF2G5TTN(ue-ZR(v?;bnGB(^UI}&Kb(cN_yn0#S*9p8rU zp;tDU?tfH$Yn0@Wc8*sg%6~yK_SuWRrkqAMk0T?ltt5 zKv?TS_XT-yd&1-?fMuK+;(-^uX2m-K3t;jH?pOh-2-|f!9qaHTB2)_O^+UI5Q!7k{ z5bcDVq>^NF$$W^nqL_UeAUg}UKbfTBT9)5#KcC%vk{LRTeYg8S#zkYrM2|1 zs({MQ#hubsUjx0c0(e-?2bh% zkj#;4HF;~Z>#UvE_Z8&Y?451baC@Y;au=3Qpt!_%on_rlyFuv!5wI@IUNF z)RpH~8?In$FYm7(u_`MZn3sI+73kR~Vq#$&aPOR~(pVktvIZHzyFFlDbU*^MM43X5 zsYeqCqli=p*Fxt!;OijcupJ~tHwAjoac>6Uh>o`9t~sc(y4sr~XUG%@>V%ls_*L<- zG4Q7d`HaZs_F2WXCKQzzUzOLjuxPHU`}jbSCq2Eh^+PV7`O6CK86q`ttYTq%Y-Mgu z2O2_ZRlHIi9|JW^Wt>_KU+Ac*t1VDP>rD$g%jY=u5Lwy^vXn+;iDXd#<6i*A#d!8I z#+eYENIi+lC{h88wBZFm5#D!zmY{43=&6k6CHpI`jm^?mHn)`L86!$_o9DS(ol;N% zB?alA0-of$nEU5&AKnKcqEf3RNlC+tIxXAHe%5LVq*ltj=4b_{swM9Fzzwm)Gp%!C*hxa_{x*W2dy9NgD^=H+)kI!l|D zN>4?Uw6&EK)E>V1*2}eU+uS(wrsQkZ3e^)>V2`{N4E!*p{}ucKTXi8(^7Kq@93j}e z(YU&lIWA4JIx;U$RjUjJl{%Gqb3x16Qg@F>k(g}DPg~G|ziKe3aoFhBH5!TA=bit(o?*&a^tdCjkc@@ zbO`($Dixp=esU^skCv~luU=XRusQ+O0|uo^uUDxcE!5nyrp!IhGlRg|T&CA6)jFLT z^ZvIJC!|SmKIsOO08Et4)sQTric~Zw04OhBcY{05sM#Y3$;mBH6Up4PTY85y-R8*H zvFU}_cw_Vk)iAZ5|5r*fF5U*+LZWK%tyM6Z2e1nul`w$YXuv-Y@ssJ zY%*!*;CWc3r>vQkm6@~a58c!YKem}$z7D|E~GniHlXda*B$xf5>9 zo+1A5W3@>=)k%aj1{G_`n^zc_m}JXKYYG9Q$K`&3QbcW-n84BkG)aYN8(4??X+plP zfv>}o!b_lu2^SDUh{6k(WMU|xt#T4s{F1gd1K6G+pZ9-D7yk2o%T)_zPE8j-9BbTF zBD!!!=1-Et((iy*QYl>|FHC$3yn?@e621=lAW_6CDlmFlYc^vAZb@Y@HD>oR_cLJA zG>M2o&s)-RU8O~}-OY~3wnpi9L$hb7G$y?^)rO?ff~IT%{9{R2HTa9m=HR16q}eBCEdlwD_!;(m#Js><-@cA zhJ5y`8d0&K%$f>34Jo zTyBljzHS>8)x3;^JHZOz^M8Qz@pfjakW|{xb1AFkV@56c4Gn` zL0JxSBG4Is8j}Ki=YSk*ki-LLhyPSDjnN~121HU30Mjrbl8ZnIW z(0&pGnEQ#D#`jQ5*`5s#2G9A;?TB89&Kxlr7Q+2lG&Pp`^mXGE_=?E_G##68o8J8XI?haJ;$06<^%C3ayKw@;S>1@`!rDFTN7{@ zkmq0rG%Ca2s4GpH^aFkq_OXy12hdx#zhF$#Sc)>Y?Krfdack3K*-o3&YP-^vnra6 zuFq*|%06}KY5b~To2NNgfC!YKFy` zUs7GMy1v-MeG&95=^CVJMPAOuKS}BXo2ZSwH8E{|Q(2#O)!~r0NDb&g5Xe48{2Yng z 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 diff --git a/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-BlackItalic.ttf b/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-BlackItalic.ttf deleted file mode 100644 index 7aeb58bd1b9436a814a50ee3539d38f7668908c8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 171604 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�xCqdr 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 diff --git a/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-Bold.ttf b/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-Bold.ttf deleted file mode 100644 index 00559eeb290fb8036f10633ff0640447d827b27c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 153944 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%0AC7VmlS@LZ-TEZV}oirP&oT11@@uCp8_l( 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
iaL9w>qKFf3RqGIc*<&o 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 diff --git a/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-ExtraBoldItalic.ttf b/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-ExtraBoldItalic.ttf deleted file mode 100644 index 14d2b375dc0c2c4aec35b3d9d4bd89d7291c52dd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 173916 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 aY8oUeaqiTv3)-Z=xq_K?cQ8w#`2PT*u76+v diff --git a/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-ExtraLight.ttf b/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-ExtraLight.ttf deleted file mode 100644 index e76ec69a650f1323fe9af6dafc55628ca1afc7e1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 161456 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 e4&?DO=qzU-SNlDimmCj>Yo9*= diff --git a/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-ExtraLightItalic.ttf b/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-ExtraLightItalic.ttf deleted file mode 100644 index 89513d94693ae8100315edbb982d7d243f5469f6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 186168 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
-@2oR%hBy+92c3cE^s1 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 diff --git a/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-Medium.ttf b/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-Medium.ttf deleted file mode 100644 index 6bcdcc27f22e001e46defdfd9e23f224ff65dd67..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 156520 zcmdSCcYIYv*ET#ed!Lh>1PCn&oiqXj2+R)LJ~qK5&{VjNP!SS5fBj@A}RtR zDk35RVgV76CQ<}xg3_DxCLp3>CFi@=?0t3)+_yfy&-Z)(cyoTU)-`=ipFMlc4&#im z`fM6gSawQMk7SFRr8#4~0IqQ<*}Zaqo>uG6n0G2;O;)Ak_DKjocdip-{bn)dThlAI zYtYH;3BMuy0)!ttw7h6^sHIy|*x@F=8D2JV*tmn4@r+GV8NWDWL`hL`i{(DU;qQU) zVI$yBFVA!b_%9fr5#`krp1l5(A7hO-G1hQqS>@28&v%DRXUwUZF~`*Mq6wqbcepRY zo`&(LC@L@6(zH!2W9mM}Ov^@Bj;)URe8g)=`e(+x<}s8_^=2=!dTbms!zNf8aK?&Z z6xhhWMePz+Y&m-W>hFClH_65RPxX>6DmOpX?9tx~U7~+sigJxY9ACO(8}UZGcUOp+sfFVLMxkEDaLv*_A>d2_Tn=z z52lg@MuBMxO42rEs@MX)5WT_7S^(8Y$f?|cx8&YDkcabBK7g0-sr&^#hu;@1ML)4g z>=IvzGs;$rVsW!4L&omO(%ifM ztIMwq82i;(mW}-yc@LxF*XF;rxcAoO-j`cmx_as2xhF2}xwz}%mltog# z|L*d|c*ZUUT{v^$>kIoX>^y(>{14}^p1*Sb()sh}&z}G0{JQgRo`3yp#L1l&+1ItS z|0OL$xC%B}4_yI1noVapxeX0FVQ zxnt}!M}KUIvC{^l$Q!-V6Jy62{j(Ko&7Nbk*lgvt%&G~yLA_bEz_vtfp<~WkDYZHl ztbuYz#|n#5*6CQa3u9tV$|~L64EGmx?8tnTkvg`pfl31%JF_TpSjY9b7hj{}1}uzE z)^S7DfLG|a5v#|C=(q{{fUVUq`a~S7)-gv}^K~qkAM31R1&FbDR)&xztO{*Z1XRju zVDn%>@bzH9l7>kd`L|(jKwm*sFjiBF!R}%f`UB zNV<3+bS3=9!lx8*J&@8k@S$*{FeFi>bT5}NMzOJ$2ul}B9P_aFV}^*ggkx^#iZLkQ z;%Z3;b+=?-<_HC9Y6$=}S=w8=!!H)Dws0wM_lJwWCCH-T6q!ORq?rlVR`BydmDu<+(=8RrI=-++{GxxIMh-$$}$>1D(P4@0;Q{l zZ7hpoUGW=>QdhxmG+f6rf0$C#pueoU;qdRpl351JL%dS>dB8?BQw@|0Gz@6GQL9>r zGK3t8{3$n*!b8?=F>n=dHBzFs&SgE3t_RYmR-`tlOF83#6sS++%h)8F)-HjNkc@|& zWDn*`Elctip>Gudmw{@1!vhkRN;$}v@{MP`WyosOiO2u4h90h0s%>fk>YLT*gH+04 zke>QkF=}EWq#p`EP6VJy-|y=MpJ9`3;AmR_Z86MVhRI3fZ%>W$Y1f zq0vO~G7y$}a0Np5l(}hAP+e(qXmwpD$%8$l%LBb91ab!87lO3%P*=TJH{|kQ437t5 zkbhmQAuNLBpd`ala%zLBe~g=ma*T$QRMz3LC1_R%kVb23s+)&%&qnx4HWIne2=+jG zj+5<6bwYih!+*5taHLN?k>(3qJ9L$954KU)hw|zu=V@Q2{l>DLT=3rfY5t99EvAYS zN}BShvO#UBKC8}C-%{UKx2RW5^-Wz(`KB7vd#1n4Mdk_SH_V&O-#aJ{E)K&SHaT2& z^l%*M_?qK2>^=%CFIl!&?mBgH8s#+4X_GT^4sagt{Gs#FdaddWthcz{k@^nxgX%wC ze?$HI4g4AmZt!k{T9+J`r(EW^EOJ@tvd(3j%K?{@E>{|MZ8)XjTMdskywIp&qk=|L z8*ORyQzL6*=f>`hy&I=AE^WND@kfn+XyV-@vB{(+>zn-2)Te1q(?LxaH$CF&;u_{U z%(cpOms=CJ9&Wwe9&sym8|U`8+beFLyIpo~;_l%d<{s;w?4ISG?>@+Vn0tkLjr&yh zrZ8q!>u~B>f7p_Ru@~l zx9;8g<<_U#IJF6FGqug?Huu|hYg^Lx!?t%lx_Q)ie1M(QFP?FpV?00b{LL%hYlnAN z?{e?=eH?rWeBSdp)~->z*6n(=Th#7=ubXeKZ=vrf-|@ar_+FKxfO zgVG_T!*dTE@o70$Jou?J-c`5z9g=BT)VghaZBS{$9If>Cw_H8SVB$0?nKALo{29e z{**L6$=V~U$7jinl1C(8O9@GtlCnQFICVzqp`QLdXZO6B7LxX6x?lQZ>4!2rGiGP1 znWHm5$lQ`;${L)tvzKSDM|*9_cFFFU{X+JM-hsU*_1@ZhUry_sr*rP+4$NJh=aW~J z_gx>)KBM|<%deL|D1UR`dVPoY-Br+`U~0kDe%bv#==W3q*#0l{zdE4ffbj#i4Qw)S z%D~!3Dj)e}(C|S=3wst;7k)mt@!*kz*A%%G^)7m0h!`?($lF65hxQu!>d-%ndloM! zsbBJF$(><+hpiv(IlOrIsu7JwJv~g*1>H5+gBO8p&8@XtdWz-9!{wgajJ6N7x zexV|$Vr9i&l>;l^8SOIqkM>kI{ntXmr#*}BL9GvPrb@G{)FKGFJ#E>Fyw;V@(DjB8KEJlXTf4Nv(!HT9{JPv<>- z_nG`>wm%#E?24HVGp9e-;JNDO&d!RP_1^RKp09rXkJ%$;uX(}rLg5Q5UikIJfiHgd zQu0g7=LF1||FYl9kH36mZtC1+^BT%BVs)v>Q0 zS{S}?@oP<9t9b3y>%(6E@{NvfOnc*(MZ*>yelzmTg^QanUip^mTPv0{T=LYC3vWlg z{r*zR(osu)d56C<=$(=8>{;fs?3rahzMKE6>ddf4F($=AXAjZzAZad|Uf%8QaEeo40MlwsYGZw+C$>xc!;!Yqy`+S}-BX-y9erfmi-L+pe{%YV?bH7^r)vB*HeRc4wb9-F(OxiPd&)z-1?`^d=Xm9e~ zNqb-3yLj)Wy+`+6-pBX3?@Qe`YTw)Y_U^B@zs>&c`}6i!?Vq)O$^K9Gf3^Sk{_Fey zJm7f1^+5W8#}B-B;QZH3z7F`h=Id9#UibCsuYWqo4u&1Cn+bH@{)u^!TRtH}k*Qir<-U>wla0?c?7r|90cyW`|=AzkGPx;Zuii9KL%b z`bf_sRY#sX^2w1qNA4f>I2v^{>1ff>(MMlD`pMCA$66j6b8OkMHOICb+jH#bu?xp; zA8&lT&+($;<;N!;fAaWC$KN=<;`k@W&mF&c{I?V8iH0Xyo#=2P;l#`n^G_@}@xh4= zC%!y!^JLh`gp*k(`=1sduK?8J{ybXI?w=?wOCze12x{nPX=z zp84Ty@Y!x>C!Kxj?3}ZU&aOPW?(DX+x4sMcF7CUG?+U&v{%-VllfT<~uF1K!=Q^DW zKbLr}*SP`brkwlm+{Sae&)q)X_T^@Y7?DB-mGcLb)`Sr`oFR#74_42;U$FDTFl5!>YO5v4JSH@p?;>rtGUc2({ zm5;A{er4|!>(x$I!>=Y@opbfVHGa+iTG6%AYZI@{yf)|B``5N#yMEnrJ@|U^^+&Fk zULSY;@$1iDfA#u1*H>TPeEqBIN3Ng0e(U-lHymy>z2SMI%Z<1jxi?B~Ja%Krjn8g; z^?l&?@!$9UzUce6zCZB&{hOwnem4i-oOAQQ&8s(SZ#BQ=d&~b;*saW43vO+^wd>Zk zTh`mIxBYJSyIpd7;_X?t7u;TXd;9HMKd>L1ehB#?^M}DdjQwHm4{Lwe@k8w$r#o(U zyzcnliMo?~XWpH+?!15J_a8lfO#gA}kE?M$BL6r}Gsr02S+Y8eC5t5MIccJ;*OW)B z*Hkn3C77pRUa(#hvoxcYAv_G(m3R&FF4k+h!Ff(?GR&JWAHnQ^$<$3(@b=u_`Ut}I z2A&Q4g>n+g&m5AJ|^<-nf- zQ(i9tPXlkma`|*{M;KjR(Sjw37O>mNBlAaEj9GoDe%U=1A(F9sX4VMU%f;g?8fRf$ zL_7-;IV?at&HTkR2Cy1WtnO_$aalo^3H6bXv7w(kK!b)0y|b_y$&-? zJcpezvqp2273sR#*V%tC`-QP~ zvi&9?tX>~#V}zxCq_>+&ZD3b7|6^O($tBxr8S4IDwiU@m?Zkg)?H+C?#RYP_1wZr` zY99q{X5OruC#=^@Il!I4(GSdi74Gt!g>W5V<@ za6=di@HX&raHQ>kacp)5j%7&>4lGH%3-dlpGS`P+B}@R!n=s?7*DUBaW=gw{ZVrK; z*3I+aWJoqLdvtRg@*aRezc!)2n*4#gz>qwnV90(4d;|Don7hC$!T*5ySTm-4aDz zNd<>I>N_x0FGVnG;ZFG{08_cY0v-r?DPQ6yxb4yWDU7%R`%LR86>_Mka}{I3JQ)23 z^CKB^p`O-q;41i!guy&Pa;fcLP=98W%lr&XH@G)vsp?snc^G3ekv@zZM}F3G#7(2% zR;(E+w-N`u)%u%2-%+o!4D))Hq5clD5+;jfn2uVnIijDbs2dZ~m+jgC{1fCi4E#m# zec&W3!kT9RqkdHME7=}9;YRIC9R0)O1%3P_|`xT%W}uf1kWH3;*LX5{!91b4J0 z8Pj&~p)lwhG-hO9f(+(W@aw3jN$yD7EWp2p833~%<|52=Eu2Fu;B*-DKlL!&{IPCc z){JQ`_`5Lg!4Lh#)EfL1-C&HE(YERuxcdN~29E-N6TCCrZ-S#e)urH=i^<5kI1j!W zZHd08kn9uDW=Pu{gD@z&F|N^Wl%Bc~ya^2I%7prLD2Ll$Fn?<9<^u36m>n=EgJ~+# zYKAl+t0@-YQST*!utet5j%SCx})x&UehCLqG3cOhN z?+N?};=rg5$Y-*~RTZ523XLyH8{9#0lL$FtvD6-gS?0-6_9Q&ou zn3t$w>52(^f}Pl>)PqrU`xh*oJHj5qI*WvdS|)S&HtW+B|2Ik7{mDe8>k2Z@!a=glxzm?!E! zSk^n${X<51mDQ7NLG55?&<+hWWBmp7aaSB<7F#<|Ti6@1kv$4~3)vRb9yUX5fi{DG zm|pKx_YW8~26k!-w1d6*kNhXY-_{P)7WU>{)bUikEvP+g25lkRj5w8>@_vZRvLpY8 zIrUAYKEhMEDIV1g$@o8TnGV7~%u$Eub+aFw%x&G=1BYR$FzM9qW#2>Jmvx}=bpAE$ z@4=8gN^%SQWt+%)!5FdUa=f699_DH|Yp43692i5gA3w}#T%m8v@nz33)?|MtJ`r_I zZA|t2FFB1*8Y6OiQhV6ODUB5xuXY?V$MOv8Zt9zA9Avf0WRn5y!0s{+9)xj0hQ>!c z`u{AL+t5$A0edk_Ir7=Y+S>Adm>X?Lve;xIzJv8uUdOz57w=?|4?D(8d>LyF=4A!|*BCu;`Jj-g@@>hs^2Wyt>#^o1dalf%OBb`}Q^ zFitELGpUl4oRP-@ic4yOm=|af-e}`=0xPB!Iu`;u9d$MiTxTPF40F+tnsVlEPluF^ zFK52?6p2wvq`g5}97v^pj^km><4!U)4t*1@m>p_BNekr#hhlMCToV_>X>n8>bck{Y z5qrcAu?6ny#TthY@Ks`&SS%Kbxnj2Te@09flg-=Ao6PIX>qL!tsdz}NAQ zOl|l|_$@WnKh^)nWU+FQtcKpm`Gh@zdj;$+=L zl-$)lGR%7tn=l@@*lqm-bdPmDeAA`x*M!Zzt#=qV<&uk6uRC6xufl=3rF7CuXsA6yOexur;6@nj$p9}*Ky_4w@|SW?xue`rE-7b~4%InH}Ff{jv;A1q#vzSDIRaPN#-NbnPt@2(n;1l+jYgU_&Y(?%F(z8s#+S(9wfr}29N9(> zjUB#;Mh(WyQrma|H^z!FPB21feDDYw75bQ1RC@w-`2|tM%{mhJEyC&-*0Z27RHt}) z`5wNDtzQCnwVo$R?#e7l8(0g08(8;&&acJDP#RfBNQ&^v3JR~Rkm($vG?g_noqE;{ zXy@Hlw4NGiT|kt=h;OV>z_~KyF~WG3dkL*MK+5x$OlOUiBwRyMsae){kmhd`1}8a? zK@E`U@06*9%hWc?xb8CiPKo=;^tX|VX|8m+VLgtxH)Q&c$rL)sFwiqZ`VO*mPUNl> zQ<|nFG7NOP;PRu@6LITFsp`qPDj|7r5>4UHP)VUWXWf%*!yU9W>qD0a`lKy6$3wdg+ zZQ;Jj>J8eA>Xv_GjR&1z^#`48EdiZFJ&hl>_640p?!uSqM|e`c{G-~Npc87*vT#RU z;y3HBaH*jdM9Pp*c*-1`k!(mGeF^EK6i6Snf%H)pq>mOv`Y5~brSR+p%tIY8Bl$z~ zCjc5Yp_s3_vn1%VrLqiYd1SG9>{a#}dy_3@Z?Pq88C!{c$XfOZTgNuwsqzB5!meSb zc9;Exeb4XEj!?LnH{(9M6GmnbkK)~U3^Xy*q3@W*dqH<1hv)J>SnCRSKi(f2nge+e zAIeAbF}#L9hE?%#KAk_opTwwthR@_L@|XEsK99eFdEiaH9QufB_$QbnKE+J&Ip4v* z;(Pc$ti7lCC4QCvj5*~m=z1zb6((WEO71M`i^jrLxQiB;eOik)qOI@{p28Qpr5%Nz z=p?#eZt5xmMVN>dF(O{1V%F*{@6boexOEZSNK>&H_GVDcg^{zi zT84Fus|C=S`$}!2Hd7m`^;9!VJ=#3sXV!koAMkCg{0#TTY8&u2@YBM=4ot=jLq9DY z85beVy1g#lW-_cy8R;TLnJ(hlrK_h3e@eBUlnh+Cjo&qx3(9Hb0_;bXgD`uP9k6ea zz7*m%+_vDi1BOCuf$u?Oy|M=Ap#Hlc!|gzr9SCz2Ax=xY1EE$)+cNNL$Z5T@7>FpP zum*IYj#dG!l78ev`L5B^ItZC=Bb9|RETu>Ol*byF+lWs;#Gv|B*4I(kR@rQbyG-Ul z(oqh}AUhdK<1|Wd%m11(7r!kqYap>E(=y=2QhtiNO8FT-)TZ(WawUA5>J{8pE{a7d zuSX6P_ZsYbVA~^8q;gPtYh=yufuHsx|HY7oYL`lR0rq8TGvu-iwYds(E-0lpXl!tt;AqOh$H5ihG z+JQ<;tw8liGExt{E#s4{)Svbk@>4Ea?Mb;Y21q81HnhwR%=$Gr5#Y)tft{Q=1}7R^ zc^}V-Ty04%YHvJea`hSU1!nYxICtTuA$XGG=5YdZr};gcI&f2GfmKfZTx_wUuK`Yodf_92u2+YBvM0^GMy4Vfh?$Wm_&gKN>7$0caG0e0ZpzU{_ zn^+EafL88!zLwwT-rOqs|}MVd&$FIGf|Aki7KJD#GE zXNYJj8VE)Qe@jxCj+!lTJ4tJZidQAxNL2Yz;{Fo9 zLDV!?y6lv+y>x$#C|ZNU_mcEWq9$kqK&pn)<&>mXB;73OUZU#bl6n#4KM)l%B>#oR z7$~`aNK|o`zO9L>;nHO$QPWEjzboCFNqkSz)sp^1l%JPW=FY#B_@bm|B|Sq_oiAxi zqRMxY-j^=2L~4x0PfPbLL@f?P&9WZMn+cm15mj$UdR@{ZM8y<|*Aq2aq)UmUgCzZ( zC_gUoMv1pd>?!eGNo8vMpu|r}dW@);D6y7{FJU~HVdo|;6XjEhs?U+T`lJjgTU^Nc z6q(ZHlBBYIx$H}94dxS^CgNr%8~NGHgJHOJA+?!R_BQJqgpnHE6!C%{^dvS1bJ}psZKXK38YRkbyNb@t@Mrym zbU*&{4|m4UWpK=*aFv^|`*z@t+=5#>&b%J4&l_+T-jFxqjd>H^l)G{_?vDL=bKU~? zq+0RTxR2D9dvH(O1M$YLy&ayW+vA2(NA4$g@Ljk+@5%#kpDKt4^AH}2yG!AC?v9i@ z`)KU#V|jNT$K!bdPsB5M51!0Zu;1^ACv@8LXUbiFw!ZJrlRN*uc6T1amVUK{u0jKv?q!A{1u$Uy~-Ez*D$+l&lQXLTbS?P#;LMaNK4+}t@9|aqeXIu`;zVLKo;yCqdaONrtmmI$#n^~vkj;Dxo zv5M@%Nd`Ta?8Vx$pC8~~^Mm{lR+?||oN|O8<;Sq@oWQBzDLl2D!7B6}KgZAW3;ZIU zVJ`D4c#gTouVa<^9;b}A_-+0J)~p|KR`L^`Z+_wT_^)`%`JMm4|DrY1kcs>de!6HP2;yEcCt864rPrJz{rdZJ(XFTz^HI|4KxCc&rQt%Ab6Dx8$o}@By ztE?B+=iXQubMaKw2kW(bvJ(Bp0Q7XZdl7?05!UjdIHxTU!yf4UYk6}mfIL?2c5HrM+*daWP)2L^0|L{5N6`mKf#S7v^@e+0qFXOa$ zo|rFQ!EWMJoE^W0z1SPrTfB)=thdAx@iulE@8DedU9nuOz`o->oN>J`KEQnYkywor zuaCuA@dhYJT1ls63(!QVMYnS)>e6hGOqof>X)i z$_S+t`>Rnnqbye{luGPN$Kb?rtWvFv!>)8ZR>_IVB;351tW3da=3~k<<#Fs_pTODX zlgd-d)7ZT}i?hz>lv&F2*ipWKlh2owIm*k}C7ZB1I{d@>==#77zE*g$r`$1L}=CGr3rK`{3qNUsk~S8Efz$R>%gkB4sXno6TTPvdwHh zPF$X1v)Oa(4Yq3D|vQl}E{l^t@vo@D>TDgAw=R#GgeJGhJeI(rvq zcyqBw`;2X6U#Ko>L$wi3k(#JYRaezbb;q-GbF~F-D78{saMisGswpm+oI(hy>m$5R&NJqh-tOv?KZ>?3JSju{wvkW)>Dv}9yZpdK=+ z4Btz_K2GGSB}-7x0b$eQ5giv$YU86R7dhk#Kq{Kng%gDo9oe zY>5Dgeu1lp8Q92@pvFW@RB@9(hxYB=RIcuGz3;lO+Klfj}lqKN#vl z)GPAgkxQxM=E6pXEE&|IR6sB)ADq%BAVZW6Foj4O*-;iWX?(@Fu{A|iMfD-H>`_BX zD~iXVTjiIIEvgz`L_unbhF2AhC~_pns-p5DR7lpiQPoArpnpkKCDQ6uQ39Gbz7k$} zBdSVD;FVlCu8LS`4f*AkPJmx-Nli%wc#qQIBdXzgtl>p`}$r%Sf!8lF=h*{7ftHs!mUjg25_7!B-EI zt+{6Fff6)pf^N-(6=5lBundjmYhiNr>~eE8XPxP;859`VrB48dxacUFLyFGRbPx0x zc%U&+NysA|@nJ!gz(O?yMim4%)DJ9FJ+Pp5V4=#P01yz2s)h5%3jcQI*3Gksh zpsJvf!b&v&Mil^8S$+ysm@eySLLuf23SEeaP`W2-EKPI8Tqk|AvNUJSI-xL6#xBe& z>}La7y~8D6kEy38=g0IyJ$|+pT<-_zg_sMexh4=mmzF>iXhLCvmSv_)Z$e?FX6>i3 z42|V$>E%NXDJN!3N(NI0!aLg#d~L3EC|^qD^Tag?b4P1W5PGB~eofQz#KI zS+gmHvWZd(wWdiaOwxR@+MpP)rfQ{@NR|~rDM2v!$OIF#C|HGL6!bjZL)LW)Y%+X~ z%~@7@3S^eCI%Lp#hNGwdG#KC0Z#I<;dT31Na+A|TMDVQ!S4L2=*8agO) zNkb_i19~qOGYAPgt#{-EWB}Rc;3D&)5L&Wn)Tl^4jha-ZCYNqlHJj{|X-??WEdbB89bjl70n9ie9S{y{u0PQKw6R#*qqRfP;NNM0)lG= z%tA7#hdgb*0Mq3I&=v-Oe3Fm6v>geMYzN{9BxB3@0##v)txpqhk)1hD>zZH)AS1|$ zB2SJxsuuvc>jBUs$f1>2m}!fkRhzB*DLRXTZ^@R;~-CGVQYBvZ)%|>x4OKMnfcxW!N z@?|4xb}2-bR{3Csawf>q#uk{?=k#7Kn-%G!#YqY|h7C^QTafE>o66u-A z*$;`?BI)wNMUN!qlaYXOwCF&3bXmh$wwBB)lr0H{kSG&?o{HQANEdDX$wFn>gaOi1 zlg*4y2_IQBo$0=E^3I}(TF%sjD5NYsHL;8$D=C|Tz@4~k69C<=h08{3Ab2+9g-~F6 zDA{t^wlK0+WoyD^Yn6#|q){Ev(uzrn@}Ti7Q_+_O^0GO|ZiW!DT8O9896%vp^hmNd zz)vnT;M!C|VPyL!Xj4C!%nlslL1+LebOJI%Q4+L13MOluxF&dlR@4OaRvAmHe^~Y5 znxK_7LCYV^W|#dofy9x?%f5t2vPibZqeOIZr~s6Ko^7TkR;D)lW@2>)j<&n*}maY{-$R3l6U3B#Y5clY>@_ zdd8mmL5mERp$$SXcp^0b-93d;%h1{cOg3VM-rb1H{+B^>F3Lk(k0*Ns0?1+!N5V2C zTR^QwGPE8ErpJ@5mO<4aL(2}TH&KSxWMH2_6KmeG=ZZhQ69O- z0}V(XRNCrk2x056EiJ5VS^~CZ4PmqO9I4-FIih|w zVKQ|u>P4E1jcan}(n5y{Be5x}AvJ*PX>gJCo2zvSSr{2!PC2k5VHr`Mn8`(tsPzUo z=)ECV8!fbO$rR+Yq3uUWG`&;gYI_*k^vZPg{;yAgxmv^0QYX`p;}*kKCP~8=uGChN z$*w>xC2dkFJXpwD#W{?BQATCOa2M?vw9xkC-JtFv+`p*2C+eCWQ3*DAC?vVzfwgB( z`?yHgup%y5DL&%bpyAzAbF*cviPNwypytrL&cTSO`5^D|9)=tG9Bh;q9^Q3ic%o3{ z@ec|Ht4%N+8lfrSUWSbl?qwvbA;VK9+{ds|iR1g!C9FZin`%KnzIwn-KgTlM&>M2% z3(NbU{Uz@Mhg97`a`H)nLro41HFB!Sfs+Bs(YOJDaFHO$27+xM#0ElbAj}5BZ6Lx1 zB6ScLU<0<418pe>+ENa*r5tEWInb7Jpe^Mk26X75XIG+eGuoRHE|JVB z#R%v$+<>wmfk8n!4>tF#E-foAF_%i_&{LP9ROeI;fkBCS=p=`pdXA+!m;S+FntyP( zIiqOkxatyfnPj?ag3c4o8Jc`$lBpRGN-YC1XKAr2>|%w4YOzAX%voBj3dtOL>5^62 zOBm8a51m}U*NAZy!;7lMm6sKbtFB*ZZ!_m;`Bd5E6ROJ|8g9HzeONu0 z&}1#QFufMTg3Wnaq-u@z(du-ZR;PVzl4{)CM{C(}l9~Ecl~xQljU!sWkG-_@$JyH) z`q*k*CBv$v z)kRvX=)_ps5gFdaL&}U0jcqPL34x38Xf>)IY~TkFEfduPyas)sm@@y*p{ z_||G`yrcETyII`b!(Ftoe9i-O)QAc`v#5^F!0ohQe5xIt06dmg+0zk4WyAPLJ6a4} z!3WvX3cP0E1@?3_h6~SrfR3#m%hN{N(W+5Z!+4TTM~@iBW2@{a^>H5Z5DnsfaIvSs z+ygH5G=#gs#h^7s)#JFl*MYh+qjufVWVo>h9~_FneGj@CK5YHA9|NgK8++bOKOso5+m(+(q2+@Ewm8tZQd_>5Aja*bn>|4akR~z zRyUfrao%CNf_roYrYPKTa>BdcDCk1Wg?>mf^ii4$Gqhc9KvU)bv~1QuLuWp;dnQ3M zXb|qnB|sy|7urlt_#*dhyledi@10lTohWYqNEsZFyBl*916X}s(?n{Rz}FGA5sSCg zq-`a2PmCDc>x%V&o$a_4cS?-ZoR|-?sRadx(9QEg`%20gL5kOiX3&Xy*|*x91RXgiy~sI;?r z;9Em=sWcYlb~aU%)!8if#3(!4HBndN+;S4PtBq7F`vh*b==H}fTf}fX+ec!U9M|&R zgeUTD{kq|cFCR12_P!dfs?^O{Ayye7&~N#ZutDS1a1)qaobc|uIo^J|@G3KS9r? z5p-QhTYz*0uGN(WdLb^YSGKNbUEI2V>+IGYm`|%YEtj?&(Q;7Byq0M#y;|UQAVQBs zKbk`6Vm93rGmfC&jh|#?m(tp(Mz+~8!A`3VPdWRj*8VY9b1{h6` zLgp6ewSqNAZxxt-sdXliD@;9RMz1BmafaVS=|_@=V$8%N#k%qcq)jEl`~#|$h`u?G zk%?FwI$kfsRUr2VwB$v9Xv}DHlq2Hp!yLB_x07Z0F! zCmQ7$1KS*o3byA=@@qeueOt)8NmSb^G>c-f zLQ*@Trg6E5dXcThIl@Wz);A6JARv5;Tz5ZgxCcu2mbyFcJ?QD+))Rg$q^!+MvrL0c z{_1b)9(9&lj9aUXp_5a=zg;nScc+5)ro7PbJIL}6NLrX>%g))-nvf3jM6 zt4xJ%YZ$adi=oT;0Y3u`$0pEbj1|4~m^901<>?A!VKyAdh7@2XZ^~4XMf>4`=g;f zs%ecL)kD8%g!Th!1YNA(xTf2y>GXca_e$+g((-H!?auysyaFR$N1z7K@A@9PV1G!B zVAAA$gmhJ*IqE37>oI(d7#)CGNL}tr&;Htlt+w8v7LniIxP-qXwN{!(ptm35aI~nWx`+PO@XTO0a>?P>G z-iFTWUFf@#w(EWD0(_vC+5uXnSQEPFM+z;Aht)R>7$$hXVAid>iT^BhM zJB7Z`GM)mB;OC(Qyb$+`Ka^UzC#BZyPyBcNt{~~ldO#1h9rhmCq^qjuM59d0sU46n zx_MLX1%2uE(3I|qS+zTKp9e#CxeA)ev!Q7`pD%_!@iA!mo|f9Zq}BU9bb0@Pj;;yX zw=HlNItUuGNg`9r0pEDB<={hQfu44AXl8emI@mEh54R*sr9L$2KTpN{y}-~@{s@}L zo1ugJ1@wtYYxo!3eRPL@FU{%UQbSkM#Ldxjs_XyW$jJ>F-+oe;yC3dij)aEw3%XA9 z7U(M>h(v=&+_&tcO}`gz^BwcdDQ(IUkyipW@c%pU_yAH#YUKv;)%G1vhLl z=7XRk`7v&gKEXegcRKs(;WCVH+DfBoZg#^;l!%+Iq>-79UE?CQ9N$h_jk~R%@n>*P zw7nj?ff2hkP(93{A<*%RhF)hHdj(o}??Yp9J^xD!ja{fsrdB`=@$5pnqG8YkT>#y^ z*Vs~M?0q0t=4;S%e^!rG*Z#CqX@IpZ7+R!J&>T&`YCaEop6@|>aSb%63-lPYYt+h4 zbCokz)<~@SxSuQa;pzVDOx$19_VRV*Z;r4n?e-e9(;zL>UUvJ9wg2_D?sUYYCkJEA zt>27ws^{Z;!#UeI%vm`7<}}G^uv4s4D<@$&XZg}H!*P-03`Z}Ad-xVdoavrvkSSVy zU7d;j{Zyrun1I=K5@x#$?BN?iS9KqDD>I-!IvQH21Cfu)FJgU*k%;yY8VWR!w?ryd zNjKE76KY?uB>pG4%9eOh+7xKI#z5j$dMLVWm&Lb(rt<@!iTq2@U>NLLc!+dQ0FA*K z)*0>WfmslH9^M3QiM%mrJfsyYjyIrCydG#Yw}1ve7y~-D$O*cr0;qa8wj>HdIhXk)}cuMqgsS6}kStc7@@ z4`{f^0}aAXXb0N_4bxTVeT&c!=ivJ~PqAsJ?`rIqMnETcAa)1Y=+8;mBSgq=-w3us zBq8=v5d%68vo7jDq&pKRlSGn)7G2t zJz4Y!(E&6b^9#w~3)&su1){q20gV>ax5I@OXfST|Q|=z1exfaCN6{M82PY~%==;qu z1Jo0(;F2U-g2oBjfp-_pL1RQS&~S|QlXx!PgWd5a%sL-ooc*glAe<3FVs>#G$Nt}o%3`PG%lh0hjpZ?E%gx^8vRs0s{d;A9I3Vbh*<`EjPiToaDJg0FRhi^Bdy!a{{ z<#87@n%@TP#=i%R;xwKk@ZDbI&98!n@++XhayK~~v44Q1!x8f)_%KLZHzQp}xHx_Z zv^&258pF?nhNE3c{(l&u)T8)Ggk8lCf~NCxpo#oD(0G0pG>)GJ?aohu#_$uM-S~0P zD14ubcfJ=ihJOVbiSOT0t?U8~=Q}}z`3_Kjz74d~-;WXg1^g5FR?s;9IcRsj1vG|l z294&MKqL7^&49v2<%!ra1Y)L_uw4y!1EjS zwAZjsAIG@Z2Tl8}xOcr4Yw~j3l6W0^jTiA0IRkeWCg6TU8QNNYUP2?Ye#_`-5w z0UFMq01e~QK|}fDpzZl%puWgQo=EViu*LBypfP+hXgJpGv$(%VOB(DIC;ANoU zd=zL19|0Q7hk>@^MW8Q!N@0$7lOv{L7>t65zrt$5Hyev01e>%LA&yP zptNpjHCO;!9PbMngKttItlb>dNBZSSKiUNZ@*GfF`?R>drGK{c?*-b`E^e0e%ane! zJLt;OLH+GQq`@A~dxCc2si1y51+=4)dNOP=ya#ABPXZ0cSy|10-?eCS5)VbF6rKo* zd#Ru?JPtIPcL$B)v7q5R1~imMg9h_%paDDz)SpLycILsLop=za4-bbVeUTEKP|yrW zyS4!MJiJHjN^aaAG@5q-4d>UDIoGX&|=|Dzj%ayn=`Dx9dveKe<4Kc2gQ#_{@~G29t6nmd7p zb4So%?f}{j;cs9MY}SGV95fgA*ZygJ{t@w4;KYJvRt}no6Mp21TVKc( zXC){(=5geWvlHZv6AFw$%y5EeFnE-t8s69 zFmBmq;Cvwh-!}5WX+uN2MfnToDcA5ce;7BDx8hlP6;3D?;Iv|fd=49i6NVhDma#Y) z^21u`hP&eeHGCU&bP~4$zr^lw4OYlS)c>(|3bY`h?+kR-KxYhe+Ca1m(PEr55ba4c zx8nvnW}u@6I%1&12BIB{7WW$i9Wu~C1AT3v0|we}AlkENsq8h-9s_-4pxp-AWuPw& zw9`O447A-qwA<10_`*P24fMHzwisx$fi@Y4_B&cC8w~WBfz})7Qvs|@s>fmRx5g@KkE=v@OXGtfH*T56!T4MaOFt>kYRXt9Cb zG|(agyYP11&JnD+Zcxpm`Dr%!57{`3z&c2lf}KI5YCZGkzB44*H*b zz8L9aFgI0Wq^DqHFpLk3Wt=nk;#_kBz9Fy-C&zQ}_Hrst*+%lg_y!uCenjA;(ucR; z4Y?U}++EC7XL0X#4|b*NFk3Fclgw={J4QSiC_Wc=a{0)8%-*woBbN=;sH?b5a%rD~nZ8FY-N8p4z58uMT{sG^^ z^TIPo1FS87V5Pc(`@!Gf2KE*_d#uEoI3H&k)6sY6zeV=aHc+L3DhyO^pfUrEGSJHgDmBmu0}VIOFawnssMtV54K&0+MFtvdph5!; zGSDLi8fc&a2I_C1eg-NqP+tS(8>o+g@(h$~pd17BHc+-i0@MeipJD9x!WwS$1^I3o zV;(MZWym_8S#YiMiM5N(q(8o)l7Rl> zhc^O^F-!b`Rrvy*ZuX$>tigV2A!dl_*b|S$nUpp^Y{0H$3QqD!5_%T4;q`X-Q#-uQ z4u4{Y={eID_hUOu&z&~+)pqzJJN%&?rsq>z`1kEFJ+Io_>6z7r>3g|0yuuDIx5Mw+ z;bnH1o_TF?m)c=^2DZ5`vBUICY;#|1hv^yF=Dx@dzhQ^z`PmkRo}q1cp&h1YYMc84 zJ50~mHuw23(0wA0qF=Expr*4G^cT*&ildb3V%dFgF9$Jqu^n$tM? zM_oEz2<;~IT2!oFBpJQ;RB@t8I02aYd?&jY!_c1xqQ9jgoe=C^JrJK_^g_s~5@i`E z(?A&pN;goNfqEJ!RY#7*x|>bBqkWfe-@h{n;E-p(gS0SJR5m(Pj@A} zJ#XaDq`d?Gxwal(TDxlH?v*R~iz{cXTq!o)Ps0D;sXc79)(T!L0pv_(Ovx9w@Z7+- zt9K*sM(y2Po0`47e0@WMo4N%Dg@uN+_YU>-_70WU*W1hN>Kzgm792$399-SpgBvON zlSa&R8a*&!c;t}8u+Ug%;nBNe$MBdK|A6?S(7`eNE2AS@HQO&P72L1J2D`OK-;p7Y zLtnMIX+DqD}zYlL5l z>KfBCw75rdWo&V;Zc)iei9tS*F}0_g$rrjqKad%h@e3IyI`WRcLY2!=fm&YBgDk ziu(^%vw28(L});4%;2#9DwTDc+6GdE(YOi@3Jnczq_<2^xGPl!YCk;G?2TR>>fjy9 zeeFh;yQ~zExgu+TYj8wNV9Pc>-uabjael!Ii+-EIC)dl4${1K0UD0zw^!>loFfr?! z9wD`FhWZ6`am;h<+dD45L0QwZnZq@i-)QUO)wY*M zJ8ussb1z5d9?4x|@a3lPF5S8*m491;tqOfLE|E$={mQ{6cU`q&&XTq1>a7Ee2g8?U zF8AOtR6dPG@nMhN4IR2g#|C<|@bc+bnbA#+4v&s0h>D7e%SdWf-*wz$;YG2@eHtkZ z;zIAgseB@4jcZ&pAfj_<1n1-a3Eotrd|$?BKx>YCO+t9#EJF_1q!x@T#4R=0qZ z`qf6gD{n*UJ`YR%kJbKvS`Kf!Mu=!=G(txBKbL8vtxONKMoMR`H6pvIO`83WCB%O% zT8a{SVieL!5$f4UTRwv2Y7(k=%bAZX?L+yf1_7OGe@;z|O&&g_Y~o}-L2N3`9?-NI zUmM8wd0_+MhlsdUmLQM$T9x!rwoej}HJy!N?8vakgO zcI+tRR`z~oNLmG?k8Q#waP_zxR0vJ9VI)spBK)vH%je01rQ$s>Ib zy*9b_E9?qANw3|F=J4=fv#|$)o#N#{M%v|UYDA?vDSpU_6f)WlQFPNwj@jo2=3hzfM8@qLW&; zr&XzI$I$RjEhAiN2)^=g9;r|CuZT#LD~ZPT*Kxq6Z675i;f-qI`#1D=+`AIz{PB7h?~egLSpNS`UznP z`OfCfUAlVJR++l%woH!ySI-Uh~;yLa8RqoLX*y$5FXkBNzTU|vyM zcs;hj-^7?P`-_Ve-4z0d!i+UFD;iz5}k?6QiqQuG079+T)zI>ttcJZiIdzeBE-p z#S97w8x-x+%Ec|BXL@8pVu^b}Z0z9Bup!-}^WAIPtCss!-P@)ncFWF+5wo^;3hp*M zA!T^DL#~>h5SvvHQ@i`qP9agH$vs9y2KjwaP*CgRm*5@QCo?V~3xZm&S^rXYV#aNT z_og%*QmaV0+H0Fo`PhlA8Vd`Tqh4;`Ffr4!CnWT-llK3k?LFY*s*d(y-MdY$a#6D- z%T`@=t+ubUO?}nNO4?QL-CeG-4H$zdHa!qJ1PGAu0xuYF*|-n)03Z1U#&C16=L+L<|X=FB|jIWw~7=O*K7ir1{!xT|UJ@zjc1?sMkm z>e$R2U6;AICXPEi(A-|fwAyW@`kHD33862gX31>z7w*5Rta-E7H=CcH9SHE z-9IrZcUN^Bs4v#mm77a3-6WQc16d(5^4_zWCO3MM4uyfVXLYJU7B@F}yQj<3$Bd_| zb5gly_+ZT~xF?6&kZkA0`hUPR-|Ki9N=hCSmn|s5rUinjvy823U#Fr>$I>w z>~Hr^Z=9(ZKHxHFt-INOe8|04QdVFnb2i$wZVeuzuNE5zyw+ymCDxQzdc6*#sl!yD zH{~@!3uXA!g_F?3^&_}>!?Pz`%_yl{NysIYYZQ7BeU=^5+RWn`=a(bNLtxxA|3;kR_0vttR2NLbcF-$`%=ne)>W#{LnHVoNH2h8fsJDDw+_QqCgwa53m zEEaWtxmz)F$U4gqxz8apmE*xJIOmyk|` zVfQ;5dw;m&nulB^#nxHb2=^*?kJ%QtwyJ9P%sJOtva?L9JgaN1?D4HJ)9nM7H0+ou zYhNK-;wm42Y86(&Q4FmLr>&I+eYU31;BqhLb(f8UWJy%_)iia@f|`-{DlAnDsj~68 zsk^uK56(>M3yX}*O5d35ERr)_)6h4&v;WRJj83QVjvI-t3lGS)!B5PB8((Tq0oDOr z!XQ~)2`(Xn&*f_=oow6O#NC#VMJ>dKamEZH;Ew|C`;?_AjsQ9QP5b5Wa4t?zKyI}BOPV-xMnMq1|rZ|-~W zi4dow2pj@i6ABUZUR*{A62rt!_t!P_kCxB1G@n)NI@c4!?PVW(c{}rSkGrO4puTZ$ znRB+Tb+>KO>xCN>@SfxqU8-HLyqB@Hsi5$dcb| z{ALtkrGG`*k>lJUcIL=^fGUOEiUd(Y%myp#Lf&RXem>=+-cQn;a5hXksO@)FdW*AC z3!&w4cXf0B`LmwmEe+>&q;Z4nj~*Oj-kTh@bZg5^YHe=Xvgp;ToOL~QJu?+&wR;cP zN4#FRQzg;$0DS%`k_kgBg#Xca17R^$0ly&&;u%AJfew9vJ$`nVU-3 z)zN;iLO$@4F0a5KACPOiOqNc~v3;5BU{A?_nVb{9(YY!PwQo#1Ki6z3idmn$s-@ApgL*m~m*wDp3%rVV3WFHCj;CUF#4;B(-NNMf;YCV|xu`c(4pV=9a%HC#NjN~l{xifX>ZooS~~KJyRDAyqHDaqckRU*hoiXIMn4bUH+}?_ zB)%9%FXS^3@i2y9!rnVKxvtGLy={_x{sZo>mDNrc`MJuB>oKHYWKP{usi~Nloxrt^In#fHgBEHqPv-E2*rWN^I1P%}he(_2L5KbXqG& z-c>SQ=9)4^w<#LS>`i?Y+%Fk+H94G>Tu5`%PPMsWG>pn25m4R*i2PcY6lA3|4L4 z+SQ4xT8H;c)G$fhW%`1Ay#qMxB+`_C9b&uC4 zx$9+ZAKrBr_mx{?h0oQJg%>{dgx7nblm8^L`s@PvBs?wPbADsf8$yRi>bDzr+K78znVUMAchAbTdCLea2?wZ#R7v8mpXsma^9%h~F!+xIXvhfL)*UHsbqZF`4a zyV33Sx_6xgqJoVqvfJRN!Zb(!DW^6hfY@~36Xb(85G|K!-RljBp;Hl2)cKC*KyQ_B6 z)L5c*E!_ivDzz(Su<<0P2=B*O$q+xLF`s>OZhU=@qjJIwaJ{{EJ-3Rvs46Y3N?X?1 zQBGXvNBKJI=IV;6Qf>azUf&D(m6=RdRbE>~72bur7yd141K-WX=pW9`f?Z_;cA!B z;V`a9PFZQ|)9o1FGRxeDXwOId4t@l_!d?=H7oq1$qmx2uG-aX_u#e15taF!ao}FZ0 zd!PG&N$Ia%$$jFgA->9HPFrLcV`lnBW;|`J-1lVTA3mx1jC@S8Pq?$XPpMHWX*%dh2#^yV(5~JSN zmDvW8*I09H)+0u^to)ajCpQR>&y!zRNowSoQG)m1v_sGW|N z$4%EdCQC~v95oZkvV?YvuS34ncd((sW@~J+vTUQ()=&>$v&Z+v?rE=>u+&@==!TbHKK%quA?;@-bGDX*_iS6jS3##3o>7i2dY^U9K#?2jF6f-y7StcMrK zB*wakRe32P69O?XDg|AQViYvUTyqmos0%gi24j2T_^`6UV0N}U*E73(hv~;Zn9yV} zx(jmCu4dxRjtafKs{N=Jk>7=j3EGi{*#Y3TXR#eo4)XnCf-9tvJ4pQgWeXpZ?=Q%( zhEIc2i2i+j)WhWa_mUbc4er|M_eY|J$@kMq9-fB#p8Wiy3vZL}n@F~thG!Z1{(TFd zk?;SVmj@yz`F?%Wo#gwiBub}|JA9A_-hn9j{NOr}CzDiaLVl1=W2~^8Vq`^I#f61v zro%upvIl%W3xsASD^3T=Kx_p>2CKC^;YO9@-F|PPs~7~&K;;}4FH~9}JTr=5i@tho zW{!EpIlSL8;z-%BaaS{s%gBbfq+e83SxTy^N|^s4NK0WxvfE(qE8g1g=qpy*8+cI0 zyT7*5VyVP`h*!MW9$n9zNE6t4g_E?=};I1125>E9GyOxCrNh+4@ohSc84x zU7mH(5+2fkBAKjPGt5jvT}b^8=`!3V$MFY6D5AmM?5kt0*}L+F>%+qtsN<6p?Oj1Y z2JxJQ6$HabBNaJ%&L>gvB$BKp2t*p3SJb2a5|zw*8X^Wlq&md>Y~cg){m%j-)gk5| z3;!aT{12JexE%3gaW_H8f#aR8wb`~AQfaf(Z}Nq9NRf@X7z^+D(IW73WDuW36iKYI z6WNld{RQ@Oc$@a6SX;w~LuK6$lc_#Ow1c6C#OaG_1A9&Lh3<=P2wzY4?q-IY_c*RR zH*6{0*G%{3uV5AyS`2my?oD>z!tY4^+XIuhJ|{QL;;uB1Y-F+8*Pvs^YL0kjj)tv! z+ic#^UPiXr+S3`K=G7IuD@i^w0k0X*p0NZCh5fPyinG+XbYZV%8RTNN38~(Vz6Ljz zxfguN*Sw5ED0OiMUDRFV6{_8>Vx5ao;&HNWa5E?5vqIz}A@8xOYKO~XoOJD+n}jw7 z$4NSqQC!~&&lPj6#;G}*8Y8Xj}0@Bi;YIb<@bfr@)Vr36-8zn{TS(9b;VQN=7 zNZZOfiB;A@#6%>?L)3VK2+LrYhu?{4OvS##;mmWEwU$F2rPF~hFI!nLTWP$*oz z4_0omRctG>ZLO}}Vyn&hO-?aDbib|7hOhLbZta38YM;YuKyDz$E&?A>b@`m1maLMh zk-hB2(0cVn?k&DxjZmu!;&v8j`7#1%>VxOw_=IP-p-v6 z>(djRd{GS5DfcJ7I;E%JLHOweCB@8)Y&u$(!g=nmOLgqQ>cbsJucKut`yK92PfT>^ z+ckqs%I=Eqo+l|vJBRZcG<0wMJ|N#Y#N4s)C!&!B@>le3;BjEIQ1Fn5ikfnfvNBON z%pF?OS#Rw(PDiLFF|vG9+F+V;u@Op0_)zerPUxJ2GOWcYj#8{5`&`Jg;$e0aR3W&@ zh&v~;ot9a5M)Nx#_Tpt)$Tbs$yVH7y^Q;_s}>TaKNfn;SQ&EJx~$(EZ-(w*kPUtXQ$Go> zhk=?8-Gay%rYZ!m@qW#}Ia^Rg$X@mmYwjxP46O#KpPWr%Zv!5S~~S73N9uY;M4=;b7Kq#6{Er2EYUrK98`fj!~N zWR@{+aDNFc!Z1hqeS1j#MRSykWmvtW!5bi4J!$oYHAEUr_M_hyt1ptjrvY=CeqXG< zNQRgOZ%pa;h3f0?{LdfbzYoQDu@R0p&DLJpJZ>v6*BR0 zL0!l~E-qg9CZs@duS~E0Btxw+6qV+7R=@dJT6VFaur#Nw*3z|c-3DWxx}+iB>F7#G zNipSTmDJ~zz(obKVCKGx+O+UItbdeDtxIS)ICVDn)oZT-(_qU&J@>`J>u5c>L}u=b ztPND$?rdf-~WL89;zgm-3qU-peGb~Sd?wy zuWUK_{Cmjnfl~y@2>$&40ZC+VSFs!>8UBT=?l^o0qXRAg*i7lCP{8lx04HO?$a5fs zLP)71e;31wz;_p@$uyD#k=+sh1ln{qah0Ul$*t{`cJP#K6O4J_?DN!CP3llnp;1@p z8I9T5S3Y4LsW2EE4rfk|+2$zJIEH}^-?PJJNnBcLHug-)E(VWFPDZbraHE5{Cya;Y z&b#EYrQYb_kxOgp?1l+1lbPGSS=VRNmX#Tbs=DlX+EPN7A9GvVV`NH2v3(fdBsE4o z0x#_5;XObi*g@0^q!nWlXv8>s`)Z3D^9JgGWm}MMGUqY#J+(DGxphUZ9;Z1k&uq%e zH-rB~T@U|a**~KHFp$qNFqHX^z9jetd=~!M$K=m|DTjZWO8*Aa!2c!k7iCfP3t!38 z*dLR-lnRhasal&ZL(GZ2NMfU=(r+^N!#U0*9kKI?j^JL35 z6LB?k>v8XPmw!T87d1!IfYTfFDqL1aPPyG!f zSU3m%z=0)KvZd~|^X%)WuoAAX!;S1)ygoL$g z;h!}A*Fyf+a{kvE{?}Ii*CzhgmY_d(@W0pbzozoP;woC%H>0kDHyf~uG8(3Yha1mz z^tkzl80?!}UE9bzi*DFy3x9^*eOHlFd=<@s))KSPO=3!9Zj4E&%^E(&jxt?Nbq+~f zK3%zHc&gkQ1Cc&%;TAxf+5l;cQmNs}R+n&WZf^6f$GdR{3Mq38+6ZDEzc5QM-ekC`cA)eSS$OUkh-xsAy@@df zL_Kr<*xEI#*Sxmnl|@P^Z4whTOJ6#Pf_!2Z#j zrWw`WvLbNOUiMwaT(Q6qUK(CL_Wg`0OJj1Ji&E22^ZR62-6HOYnxkY9v0S_XBv0C9`61)ix z^IyVGUoU#H_@*$69O{JL&Hi$3?8^(8eR1u^n!YK|wIg~44(+KRhrAwJ-cHZFK!t>v*kka2CUa{oG8jv$U34cqBSbW09Ymb#ksx&c^b?+be<1Ewm>jHhZh3CXg_%_RS`aTve3Sjx~ zn`efO>1&N0MPX=h#D7l`UYwtwNfN?%wx;m|y9n@Eqt z?DHhaK69yuMan+mt-@;QhXvgzgz)2*#Jo5AT>t3l8FH!>F{fTvoI5R&E{t)Dr9wOdOskw{qDt+pntJdOD`ueX+}4ysUJlDl%6NR@w_YF9%UYDg!y3 zggg;X20g+4{jm(_a*Y2vK;}I#a0*)OJT%&cl)29K%zW86Fy{I_4{?9(D6$&!3Mpj{ z7QSc3IJ|SPxxKezM{!X+tqf$drQNL0uasb$j(~$5s~9pB(qIG-iX8}_&o=?0cNnFl(XeY5AQra zv^x^#?6LTDk6b7}dn^L$EOX&&U}b#{(GG(=iNKIS8#3`P*x)&WLw^_}}(t9BLlSna)9ed#ZkuuHht*nEveQ&_4gBK(@0vwpth)8@k~ zZpj^X>N|=dVJYp_Y-d>o^B3-MHnluI*UH#(^UC0O!Mf`ti!3ypQzX1ukZw;^s)FF} zKt)3yT1@;OXJ47vLYQdpGaI=-xN3A&F&yPs%M(_8{(_v`DgLhBTb%Rji|MLAFS{H* zAw3{7TjcHmD;ia*?DDzZC&!s}zJ-DUbwLl)*VZ#aS!s8_$8j2lINJ%18PB=CsHC)L zMf6hd#I~+?MQR$!%I3&@1nfEqH;v?D;?`P}owhM4ZbRG7n8|^uRhArQLqoZ)Vp*@M zWY_`xG?O+nKaii687*%r-Lf@tleeP0Lzh<6P+;F&SvIj@wOz_kTX>!@)bNJEiGC26 z!~I7zHJAM|lFQ^;SAILLTK(2_w;Vfm(<*!R84%XcSKgH8!b&2((1fx_it%TPFaVB= zFfPG)*?#H!v;<^FBigV$;nAI!n!SiN*g6t?f0LK{ZUkDrwYkX_12b;8cQP{IV+MO0F4^=!?#8tq`L91 z+0Gn|;MNFgAr%{6ww*C7@h&QrwjN(%VZ;^i3Gwuch)}GNEJj$g;u@(;;ZO_TPep^0 z&lW1tJh0|zYEda(0B@th)}_8GK>)~b!WF2#!~V}Fkxi}#Z^e9sAWD(EHF8EGT|;&( zvBPTXL-u}JTEbdOO`WB(dRn#S_n4jBBX1D+``j*3%4c%5p+tVwvNhjG)0B7oGE)Q( z@8Hz*T^lbkC1RZErV?N~U=fCc>wrl3&k(py;1Zq&rX#C5OBjU&6~cZn5g3IbTjmT{ zg&Q}lij|;srzIAqB>15^DYuaLJ&TOhc>aB-5N5=;op8XfW#Smt^{A9#_zrWF`v;t% zfnc03nmMqA`vRO8_R}kbYX}%aFdL6NN&~G;d_eO9LIHfkg1n#Yyg{`xJm;`|u|)T<;^U*XbCOj&8>!lQx#h*$&)Vh9Kpk%t%#03*4}J0cembCs_?MlRXVxLFkt6DAT|7BxQCy$@3R);eGIvh3AQHKF0GG zZ{@%L=Y?l^pLK(zX;I(fzyI9AG4lPZdH&*U{P(|JfR1#quij;^hrf^fMY!3A?3mxN z0Hyem6edo_UjGnbF*4T@6vfNz{04*%Kv1|8=A1p;m$*Y<5j%*zkUaz50sKxTus;*q z;k$}?;j%>T2Y{r+x{a>>sq%fzXpdh_dbs;}5XH3}*dd6Z{4ZfP;=MW0)A*d=wBQNs zzH4*^+-K*$A&<;(>|rv-(mzw)9g*ZntS4|B;XYoqGmCJ90=1TLfiSVUq&hT;@xx0hJTh)e)xA|lwtTS-Xej}(fAMP^0js2|;Y zT($Bv%t-TK>WPb)@Z?B{E7h=m$d23ySrf25{QD5RC=5m+_UnQNeHdgfdpQK37~s(f zFLMs}Pv+H*aM(l#1tz#X%(fvsXE7c;C~_qskv$z-5VAU^DafG#Ca9@1$z-#I6aTocNn z%$dmet@0-3yGJB&goH=Ai^n7Gr~6m7eBlt$_GO;KnBb4=p=-|~>MimZS-bCt$cD|z zczXNgsu)UW{BCDOab;R+RdKloH_cAvlo$7 z#M2Ql1Y9?ARS=fpC|et+0+u7Qlv4*m4D9#bN%;FQ7MPB$q2Pkg!W8d%ik^xiJm2wQ zx}MC5h0P=i(i&d|cGcn=A>7U2SK^gv=vy>$Q!izL8zom9vM)}3DSI$tl}{;yHYQ`#-8c0?H=7te*~F&ddJd`&Z<0 zUMNe>w2UWv$zPw;klp+rS18h&G|U^QMXD7phOYs`zK4oXws84a3DU|PSK>3Vf}tp7 zq#7nmQn!bzXPy%3(Fk?TqhbMy=a4j`Mr0T~S7gac?vF924*1{*`SnTvhlkIzkBOg- zI-it5E=tZ`a!WlWAyN*z;Y^#b0&%b%8dIf;0fR+65x4sQn5q| zi3PT7sy3H7g1R>;KbvncZD`d(D<|K%ajh2aX5qo0Y?bg;cyf~tj*n>Mfja8^rZ-$|xTqGWy>HSeM+K zfLqabn~>eod&{uSh7W~-GesTEf6ah~729qR;wF7rFYUplydHXRRcqI*-Zb0PyxS4Z z;~t#f-nX`s>x^%)FyF-oqC$JF{^s^tUy0y#@=CiE8g*4cdt2YG7dK&_F6*|2rZ5io zY<*e6?B)|W8qScPp6m&1;MFBMUYgj-3!(w9ypje(1X;-doO?G93*q)*8?JyMm^uAT+HS?KQX)+ict$ zf&J3n$SG{Ev)ZemkJUq>E+pTD7z1x4A@jGwj99Y50-Ip*<)vyM%}>a5;I>$@p2|wn zXUfr26mI!?Kb(Ia0)@3l16`;LOw2qh)q}xaR637gnY}(VFhE2w_FGnxHdWkD{mRhw zI2Ux$0_qCgXZ$z&^f4gj)$}D!Sbs&@NEV^W(A*;-Kpok?q#d4Ew#pO|1r^u7y#48& zH8H+lfoA&F^}Yo`r@%H)E932Tp=v#&$(aPz^0T?$`E{aaKpE(yhIg8xm%&9gxxG*? zqK-TP*jM0anZh&7054OPk^V=0lkBpx{18o)UGqQ6{&#$%g*lIa9li~MCg^3fNzyiN zPH~79FC?7*Tz27B+B!+HHiTIs?*%=Oy|tP|Sd5t%N(}ydQwgs=g$CD1gdn956wvs; zIQCFtm(xCMG>KyU5#bz|@DzCeV`NH>x9OPSwf+ieO6>wJu z4FsF3oGrN(nG4LZ=7!ofw{gO?#p1mSJ1S$=ak1hSkP7=CAfK7{e;Sd91mH8+6@AgY z45tP7O@O;7r6>ctLJ2l_@Q!oa)9(1p{XpghC85(MxwST25dR1Lrh$%j*jjpY>|0FBVsINcX(le%5 z*4z(0Zcg}j6^#^+gFW)dx>IXF5i0ag2ZrkzEICON0#;q|cmOfVWpS1Tuk@Sm`x@g~ zEIRkP)cz8nP0e2{)lVc(soY(mO)ueAM^}`QrBidK{K}A5Zc(Zgpp6Bh9?cVlbDC}- zsXH5r7hPWZj*?G)aDi!-2vjiC#i>3el!YY{ZeIrVj1 zt=`zYDr;8_bFJ7p=hW*`E6^4?1)2Qg?$t}zwzY*U^FNa}LWiBFv+|wxY&G||q|c7h zZydVPUjou#fw|1^DhzNX(XAJYp18JT3yZDS@P$6@&!&nC&7Kus?w?B7-O+Zil37ww z7!purKTArrcr?ecNn5XF(CjXCk+)i^6{#ED&*QCW>aG~XZ zV%G&X{b!hvJNzHrOdl-7i-q8F6Y78Y@ji`uOcAJo&`tQZA7GvURmkzgUHCn@F@^H}k` z7m%lbSgaT&o#wh`VwRO;+pWP->7eiYl=tHrP0R_}g>L?RQ89?R4D_QL?#vB6?w?(; z_ovx~)8tM6KSXWFxq6c55SX>_?4JLd7K9bpIk{rFMg6VypxnXX_UwX?MTwvaT@NL8 z33EV(&=S>$F(gv{LhkdGkhd(RA{60#W)^m(Ne5EZX)Ft1Tw`Bc2uBC)| zoOVE%|H!{~&`3Fy2LKKICc3loLrmbEUN!ejcF{Cx^!Hc)a^n3D)Y@o+G?=Z2_dn!? zE?AdDqTp>`)#_JmMwLu@8FDBi7RLH`gtOhjvs8lg8P%G@Cm+s2ET^_^~4xM&bI%i zrqad#FK9jgv1HNzQ}ww&itA0zcf~toAMo|Nh&I7fuhT5GP?BIZ622HwyhEB|w(WJW zs#2}9Jop6s!dK89tJibQaqbef=&>h8I*lEg`OU&=6Zj}!U%s~~+Z4LO4&8E>qxSjS z;_048m&hB20?Q4|$iSD+1Knz1Dkb7*V#nvx_QG5gv5;f4XdbPgaX=Ta^A$HO(m9g} zhhZph8yC^F^`6z_5oUX@m+zaRaGPpzlVwIQNk6s6M6F40VZbCcfFiTrO55 zzB0egzFkm}0L?y3RC~52D;LTT(2lz>dQdO6nI;(zN>bSFQ>pGil{zqU4*=zg`@SUhk zW!UH@Te+vR!sn3Mfsx6u>i#04$0K zEyWhn9Vd)a$lgxL6k4kKUhQ9FZksCA#l~`t0fkyXv(@o1p0PyIbM@Kg%4(OvsSGOB zS9;T#XsrrSn)HD%q^1S{Qj#`Ws*SJ{W@`5zS6qX(NaKDJ(H51= zv)mu#Z+(4}Xo{6jxf;0p*_{AO4SS%KvM;}lv~4cNgW>Uf!=-bT2~Q9A|8@F$qGcMZO4R{L$l*8_`XMrUlIPr^<4Fb~9W zCA#7w;~2K$4ri;uBlr0p5pAVl-W+v}bLwKLtwP3ws0+YecJ1=j*up!kxgIGM%vEcf zY45g#deV=>4!NB_sX7ie!F#JFK()P4ncpPL!#~il?G#@8=CJ^q5Lk|F zC_i(HEM4^$Y^py61CPWmUoH6z?1EYLHkjxRor%u0+ENqbPS6@DNBE6JISz3F(I}G| zE`wBM^mR(Os%(iOQdKsvf32~lbvmAFUThC8m4E%dXjcXE(A~wdpFz#`4K)6;E|1yS ziY43EZGHu zk$6M9v{ktB;HeD0jd;uE)&0JoV`Fjl7x1lYU6)A!t*P~0~wbQ*80h~2lBpYtY6ep9DC&VOgW&~}`a@cClXrsnl2{$A;d^FN$_2bCTy$jsS2 zTs4|lbMd8Iil8o{{4_MV+tdo1zi&DnVAq!9Hui5d8!EX!2nrEYxPa)P20Zvy>_^T- z!&!#s+q~k>pkFHZf#g=5kIO`}MbzV0o#;m{Yy1krZlfs?EWP5-bCdVwZ|NxMTc6af z4_ND{59a&D#__v{%LdmcyLB+X=Qdtj<_KQaI34^DA6;GNs3w0?x_@=O{OXZ4?%%2_ z+PH~(M^Kzx*4VW$<%f8@;-8@DCdk%pz@KRsR+Gk#u&N6F&M%g0`i@M--PmdDmUOYXxnum6R$3sVmM~pX67T zcjf-Ac8mH1uQ+Qs@wK4u?G&mc>s?H=#r1|xoUq=@#Pz-dtgr!?VwC|X&Y}h&z%_+5 zc-JO;PZh$|@NKCn6nE^$kv3O3l6p#|>MNc+^%1|VYbFxRdA`?h-DO*!Z|-LYI)Fal z`&i=Pz#iC~ofo1xj0

mo(Pbipuv4jw_ozP^jE47=ib8)Y9+mD@5&k;QJ|X7S{?j zJgwLkRiAfo*v5VrKAdAm!G9=!h=z%(>*XA5dN^aE#*kE?PfVzOmo0QyZg(7DD zFC@G@BbG1R^Aa;a8DrF@YznbM|JJPZJPp(?ydB(8~%|w-quuq+~dI;xQk~$^3BrdS6hZ7b(Fku#K6vb2I`&0 ztOh%%_-KgHp+IpG?34qq)?NO49cV~Jk^}!toN#ub8ER zPN!>fnK|GXP)7TH(CV8*BhZ7OZaOZOdEp7or@k9_n{b>I*~bzPQg7!XWhe$-oXpWFc z9ppWp9Oo@$lj*R4vC;kMe=8dH!|}|G5A4VzzgaRg_3{8=9y!eb)nKs#=cy7KDR2T8 zzJ8LTfD-PzAvLU=io&3go>fx86xC=YLaFqR_Y4+>zFCC7irq5!u73i{&>(){?<by!g*5@RZb@f5uM~}Y$ zp3#`YtkhtEmn=5-Pc9GZ7+O5M{! z-*%o>(KcJxAd8RMe#qVzG_KBWu~&)DKToT=u6SFoOn3tKuq;U9ma7Ur!|Ay(KNh5RytOONaS`Vmmc7r zPI0tcwe75K58vVI3~bTV>>M?P4x;0Cdo=7dGM|aY8_dtq8fXqkydwk~X-hCgw4X0U zM`X@O9a^zzk#U*9E-aazN$*Rs4;?bt8m0SBE#-Tml%$9Z>%8cb(3d+jnN6Hk z@8PT=qc!0WkWSVN7CCs(ChsFlVb2(7zaZ(ML|^4Xo~jCeB#}56ICxXBR5ExoH)aj@ zmQ-Z+{tfA53MYT9g577NlTB2?@-7K8k5hSq#A{E~u++N#n85Qp29+d!!04IbcI0 z878c6!8QrFD_kQPQ93PELXjJJTxqbFLDNg+KPPfw4>~dM3a3XzsU$>>pOX#C4>WyQ zDwO=gpwogMZW{5&%a3=ga@Sbyp{tAitw8qqwCDq4b772gYqvv zoPWKfEL&6NbeSuf;^Zs#xETlcSty~|teqL~`nz@uBMY6)E9%|c&w@D2azp?(zzJ>^ zIm~z z>P1Ae{A9F;NervsmXK>Y#)J8egExy{)K^Cb7)xdCRcjh6a5s~cNZEUnNKTYWDp z)ZDAwJt3ngYpv|@tufQ>Fle&2x~{O>?r*$qEhw@rlPz(T4|u(y<0)P4L?@wsfTYI$m*@v^fz1l0RR;R1RoLjPi z8ld|^HCK0GCOqad>D$(&_k2rRaM3H6YO|#>!?Kb0J)BfdJNJ`>QK%~D&LP`AzynQu zu@Ts8SPtD+Nu)COCnxt?9pmEKHd!onOG`#mhHuc+g@`B#J#W)` zd^R;@OU?KpLBOWc+AfBLlG_(6KN*xo*~|{d#DFLoGqpt-yCP_2Jk2=ALB1sV&d7hd z`z?s*0v=9Ln1bSs6}{k-L`f*%c&sELJplh#jPIY`Fj7&wEj8=mv#xoFj4M!_HD55* zsBKGhI(!4cdzyXkvFU-8!ybJnVP!p^r!#6j13PBK(FI(;!P8NYE4Mnzg7-DEucc>1 z-ZhNmB=Umlm;yCRh5IZ1PDjK|#AYf1C)oc*zYTlZ0){etYc0#XtFI3l({QG`Qe-3v zbfx^;#oo416X0pbpX87i2~c(_Szhu`H-Kv?Z;9MVV)>2!IlUIE7djxE@+oz`o&Kw9 zcy$rdS(QF)FH}oM`kJ7%0koXS@Dq?Io{E^Sh&l5aJ(3$pYA+F9)B+Vy+|0wN-}8&N z1D>fHnU$vs6pu*I)CQ>xcaRt9H+ty}$-N5vY`h_X8Fcuo{08`yWf0q>vh~BC!|!P1 zA*TmPjsTkSPt+5t5Q(Tn!v495C$kHS+6|`m#EB7QgV9vh;p83_<&{OBno;Z_lHQQm zY$PMkzr)0u990H;Rcqhu&i?P;j^f^a6F-AhH3rh)?H!Sa_w{tqQsNi>^%hOWU*l7_ zg3NM zzb;64>INqIl=bTeR7GJ6kAh6%7h$}Eyjk@ImLOPxJfOG|G(pBE)g*GK$8g?DGG{}o z49b0h6s5RwZsP9H8P(qrJ%mXq9-f}}Dt`F>lVsdUKCZx*p);$ar7o_W_Hx5a^&^{r z%1=*wg$3kB9;0Lh@UaFwn}uB#>dOqOiV$z0~DK(FbZTWpLCf|SLK4|xuot4(s)zmlT z6(*f1qynx>oblp>a(eqQ4-#VcW|GHAACZSW05CAg=jSFb>A;2z>ANp2|CJ~N`~6d| zDPl=HqAZhxJGPWaKN!L$d?wug+zy`^>Ha7Dv4caO5SU4q=>zV6R^1|gJX2G%!#;16 zKN05s2cDu~u(^|lZ_}99`!uauqR1O+GLMyy(Y!}T@Dmh6T5KzN=cexG0p^t3MOkF> zQ`;1~xM&2FolXKMGuMe|a#5gBe7{Ec*scZmn8%MlA#0+8s3+NR5hU_QxG(_nCjx-H z;%ZSgMxW}{!kfQS?<&~E5airkshAtn;8~wH1V5(vA@;|~1jL)i{``n!xW(jGQd;yY zpdAjpUGU6o@|%#2o}YnEIOKH#z{T`+!Y=6VF*H5WV$Z9<_5AMDcz9fWkJo!ory)Pj zP@)x9wSY(jzm?>x{*%tX@57yIiUPR)opQmsR!GjZX_6K!>ujI@iK6PmM~AxMFr)nV z7xH!dVa6Y0fd?324cx0hS<)E5b_i<#GX-+4$=@TZ9Dq)qc8!A}v-bi|F)Av5be;N( z{Hw)|E`B$JJFZA}`~cY-8n_ogj54s!i+pxb6dE?G2teqNbsMs4Na0tvb18lN+ znmeG_^`Wneo?1+5^nP!6w5p*Z^fYU5O+$Rc*L8A@ykdE5dN>rT!xdgD?t7Idh+<5&6vqOs z(m|_ySgF8!w9{)O#lnTOA^_4-q>~fdJC|}@q=hQ=Rm%x3{t(ql78M9?)m25%W_Vp1 z!MJ#p`~Vltot2q8wRP zrj?9fs~j`SzY}A*li9)}V5go}q~I9_ZMNF$ng@#7bZUKv!`^}O*v!L@iY&JqK`-dB z#ZT@7 zf5QKoFbGz&zcN|0X7Z45Z27*h=HC-Dae3QRiB?;}PRf&f-Lj;OYTM%b?tHEk=Hu6hfKR-RSq-AJbc|Yz#?s3DP5Z7Y zHJB{NK#HyGJMSNB^>`W@;2$bYV;OKC*LOK8hO|4z99`>@daYCv-}ftR_DUR}&DSJx zAX5S6*7StOJenY5fHS4iG#1Wwf6@;@g4hON4f{ioR*m_QWF3QWWL!lmBd*^XJ{luQ*1)Xl z_B6M7Mym!BT8*XM#ri^%?bz-@ZZrGrie(q)Vm1o=Q>e|Ua^xt*t@&4tT`|RDx>pN|*xZ0r#Tj%=ZZhP&Ylf-~D5qjD1^GEi87aYjvN?)zjlDYRDxPX4nTn`VEj5<4rxWu(;&n zmI*bl3bwR(7rrO31*KI@6~^3Bfcd+Z zZ|NH-ftAn7-P&E!nUK`u=-s@$+w1)TcTIJ*?UfZyo4FzrAbgB0+>h8{kPfhjHDpWE z%9MUC{aQMDm$IiI(D(~i@-4r=K37^YwSD`f(`qZ-ynSYL=0e#hTVH81RXc}gax(LD z_&cN7n`YYUznQSxU%@*OXC%#3tZwB}}| z<*?PV*2;#?{aahla=DzEQX;vj1|%P99OI6#qvs2fpTAP>X>oNL3et1aQ&dZ1R#}`a zICg8_zHMukZp>^5#SH4IXS~$Ew_FPD zZ3UxaEc+$96*M4y$8inf6EJ23`*lQP!?|D({QH?yZSl}_W>`x=Z~FZ+vM&N6@Zwl#L6$7F0+5}m@7I9kdZd%N6?U50|R zyo?RYRPa4#Ygu!TX{Nqzay`6~qrJ5Y&TJFBlgW7&)rMEaMwNTb5>xNhC=g)eMx6o;}3OxqP`@eqG zUBjP;H}@RaxpoPgnpL=Ia@f+XEjK|U;)Nx@y$SVNXw7FXwpGJg zXt-hak*BuiiAXQrffEyy+ON+ zWFZ;yr$BZ!-ocBq2d5l7odnqEP!PwZi=A3!8Y#?3GL3FBdq)2M;HC0BX~`N0Gh%wd z{S~MAGHU>0e1PXOC}*IWk^9E1ZCSd8jlDJ^%V#Xdj4n`#~zggQWIZJhVkL zRZ`@b)=5RZ;>dlRnc6H2{0X7VFX$tW@P-2kqh#_|KtdB#&U6LOWbZ^2vvrX@qoHzs z^x&B9=Ty%37TYL(aqqoXy3SFG+d$rR@cNRUdMDEMC0|Hb`;Z)y+Zu!7HlDtJEfGZV z_^IF1wN=w!Ag84*f6=@ApU$spvLFNP;MB$YS}tT}{NhpYO zRHhyCyf9ye14YK&og842>KaRGP3 zvbz_#8{RDRlfCiqoz`x+8=n8Z;@BD94QIAAL5JW&`wtwjgj@{Mu2U-PkBXBjge07JS~l!{(g06+YwL;O32`rOu{i6uT#NA0QgOC>#^lwG*s>SPuh?b2I>3k zY5I|2{C!h_t6D)i%tXBNCnqb;ohoGU(tojK^{cBjnG(XRtaKt|IMrcsZw#3Z89Y^0 zoDvzVvX?Wo@Z(ik1gE>0I;Zw=oUIML$dJ#TWxJ1yD+fT zm{irf3CCgn`ziyp9oyLRpR^q-RP#-d4<00(hcoCuhO7Oe`~&Eh!K@6g)r2K=qG4Aca-a2(nHEp!zw|z@h&*r!|bB?_^ z*7tk%%8fY&j~@E%aIIpW9YXfs7SvW}lWWr5NVak7HuG?Ed`4`Hr&5#YU7Fo!gf2Tw zw!f7Usj24`c=qIjPd7_eo2McHpc?GH{(ETQMK&L%uz144H{kVc*ZwJCBE`OAORJqN zUaH-9_PMvMFE;qP6gzy+3he}{@F|6igBM;0$r|c@)zNy5VWi32?@{`@50^XEzNag*uYMUmo%gY9zxHklJ=q} z6+)C8?*^~~Jzwwhu_+)H32M`=NKvf7qkmF3`j>C*9Ogv>ld$V3No9Ueeolm&KB=X` z-t>_bk>3IMIowbZ#G5x=@Ky@0Dy6XkkvRBsz*gmV%uOZiYTtKN^xn3f_U0t@u1l^y z^pST;n^$0v56EME->|bO=}?Ou-IvQioAd!QOp^2Mf^O)?_LY~Fx!gOqY@W8%Wa~{u zG3%38>9f5{irS6C6Kx~K15T!&&sOC3f>m+)8J~S(yb5Po%twhgN$>3C_fp!asdT`c zA~~Pg&GRYp=ecMn^o`(Ofic(VYxyT&MPf|8(Mxaa$%8MN-zoQjJVkI@#drD;Ot`3A zP^IHqQ%+U5Cv5wOLrR{orEKjS>}Xk+vFFko53`#+?o+#zm!Hx5wW6Au>PGk8*_}J# z9UC}l=)*PMTjN0w%u}gdDYqxY5s1{n`w4kGX*xce0C`G!?FHjmGJl@(tqk-5#ZF;tIT>XYwo7@STadQo ze9iPzkU1S9y12_>lT)}WSU|GHVIN63CGwvVPKkFWVZ!g1%x+(t{Ou4@$yt@-+sEC8 z?An@|CfFsrc0KC#K1vuR(9)9TnaYbnnjAoS?7{>kVUI5^6+_wDsd!;3z7vtWcTrwU zHu>B!Xjs|jole;1?r(2d4?Fz2vzY_FSmkZ=|MQfqp8H>R$791?^i5%bq$f4-;x0Kr zq&H?)S5?%yXSeUz{(_hL!0EELg!G+6kB^cc22C;qtA;x#F=|FSBoBvm7Jx z8OxIKF%SeWCXibv>ydfa(AZShGq{AjA9pc!R8xQ{vpK%22yq4J0%U_rYA>NkK^LGqi5{5H2ooNPado6O9kww{DcAe6 z^_I=a>s#A=N=4L%(DZ~HK$ZLb-3dF_>C?sR+)&V73vYl1xFRKjL1UN}1 zfc1py{H=wB|A6!`4In@%-}AqzXSkWgwNQTbp|45vanh(gJO>IMsK=;IuU@`JLWZK6 z-U^zQ!09KgHo~`}%nA;KazAt#%^;nCj*R-LNwmp6q_)M>w6yqlPna zHb&SVu#7=~oYF095N`G~`px*ho3Nu1JCMJnVZI21(=Dzd_eez(GnnKVi5Kg)lMYlXgs0LULqlr;;*u*Ig7=30RJ?)zDYOiG4+;8I9N&j8s_A5X@;YmR@;o zSY6uc93v*|XdL)#0nfvQJQgeKfIBV#mz0>!2ue{?BR;|30Kh&&5|XB^vZT17XDGHT z+uh#O!E~i(WvALIFW6bh>DkB6zk_+*UTSby4ZgRTyOYx8lg%Stb+Jp6(u(!!J!?m+ zMmGaf`zhGKaPRz2B1Hg$2CzMN%?}yOnM>09>LjB%y)-xfKc?t^L7=?mVXq*N#V-(^ zVmHZ<#je3@(0}*Gqm9DWqZ~iXrVvQ;lO=d8%9JIEs^k)y)eJN`wGBfP@lD#~q*X`y zd-l{6>ubh_^E_D@LuR|LME1n-o47x=t;{W2ZzwjDKu^19Q)!t#VCI&xd$OFp**jb}WrxwLuOuG2ZU_Y@~`*RsP`UB&z@D=$62p&%cN`$Z{~nU2(2IJD?qR+&9$)JbOT~xdFxX;>%d>r)O_IND>`!ESaOb=$faS zs+|qaQtreb0#S_|%aT^a29 z=dOThL0x83XMAFN$yutKR&>zq29LvO3K_3l8QykU1ogVmvusVoMk+3ocnWR@(EXJ)9?E!lbbn(=9@ zSQb)QoSu>y@m){|!xcS{$V4fUOlzeDyl*JJL1XSHDpD5`?;B<>IeJHBMZTuAT%To1 z7SEg4E=bSRbQtt)3Gtm)-t#_AmCLOfojuK%d@q;=PjE#vtYtarX%J8egbDDFaD;CN z7e2odnrb;l)}`!j8$S;_hYdFcb%t|0T85U%=X#;7Ehbp-oCuIfC*RSQBv__mvsLVy zR^J!cAx>9ZRpA{Gk}O9tbhDMAR?62wE1Z-WI3rflb%dVl{*VvpVtDO{a;F6{UndC| z0GYQG=zB~RlZhL*HbzwW+SKTMU5vxi?kmR%K5}<10?j&11x1S(V6(eFBH~GGp;!&J zz}U^tB@&nn-)e=?3c_ZQZdk?Yx#_z`dM%#Bv|UY;=Y(D3Hd{;o5=OP%-NBt?eIHTg zL#ZK`Ok<(lvGUdGT(Kcs=px&7^2T;)QvvF`@@I)=*pEZ3f4+@Y5nKPspC!vFZT%PG zxo6tyFI1bbo&C^j<>!GuPIB)j&8q`L7P3CWd#W*F3qSdJ!7cnk)cIB|`$Bb!)S(Z~ zHHa~vL)^ocJ6*6+i=nd=szVS$+^KwqK@v=ol?6~Qq<5S0w8%7!m^)!xno9;vm~8B{ zv?C8APpRw)8J8@>1A}h)slvI^*BP;&QW%na=v= z(pt~_a`{{H=Q#Zh5aIpPsJEatLEk9$Ky+6b&5q%EZ2t4UL&HB=MITl&zA5g`0nzKa>%!5~Fr&Si) zd`A^C`1Z+Ui;u0W+O_$dYt1>CrWntvx>DaJ(S5hYOkp>m>5=MzmHJ9RD5$$OH|tl) zmQ&tOsydK|Hs;Uz_(Rh zkK=w%PjVb5cDxhsEpJQKvMkAxyyU%yy!VVV>^Q>-2^o+k5Jp&Kwyd(g1mh_w3=NB6F<>{KyE@kIsJP z0zb(6*8zUHb9#oTPQZ%@MC>F6mU&&Y*G2Oi=L}H#?FE+kco8aCg;}56QiQ$pBPO9o zyDQ%%&5I?qY%f5h0&Bsw%Tnycdy~$-QYn084Vcxky`E{6{1=^crLzt~ie+K5?0O-^ zvhXv3soiod!={27%-IHT0X@<#_W_xUq2b)b&h^J^MTt(yWg)89gdXV|P z`LX{Vp>{>IYV+&Ezsghh6dL-l9x){K7g_t`7ZaK17Hnn?(dPPM3JSj;j zECk9HP)cFJf{9N?#Zo?U3&R$C4~i}$+=DaC@HbR9K)U_0Y?nrWT|?rxBD=<&{H`%v zzV^Zy#RY*cW`Ah}T2+tAFm7?OK6M3-;Yv{%#u56X_>_*x%jZ*}3ZtmM(6N@kBcU9F zdkct*4#=S%BP=p(1FFXuYaW{5$}t>25w489kgot2wAXRO)9Un`>dT8lm&Qvhq(}MNhqv-OT zL45B~2n745Ghc1w`(MaGZZ`?vD3YB)ksS&QL(T) zMdl!k3oZSc*#wZ{ZYNhr{Z(Ah$rr31 z+LMQM^=j($>@D_9-k`nIE3l8R4S4BR+4kXO!G-s?l*9}Hv|6yM#XV0hAx9i2J71U? zmkTqR~>MFN|uTS>+$$=cSk z^d!6YS^mz@ERGq%<9Gt_MO)71Jmsk`yt$~&iirF6??u#*J>lZ(d(b2#FA5Rm7Wx&B zwFcfo01KN%B!cW38T;=+rttMJP{JFXybk_z*wFRPC+ABq zZ;TQYLj8H$?ZIA{Wf{VF;g;V-g?w^*cgy(`La9=A>Z#2lh9UZQC-6%b?BU$*rgQ!u z;O|obiT!FBY)OMOCASXYLfFM;h-J~r^9<5MF!dR)M>u~{sg&NsFOdSgHsl3nA070Yyf18_X@ZKy_q` zRIIEr9S<;%aTThLABislN>%qvx?BM%Rz2&aKhm{dW4IEbCXmnP2z9F$0vRr09hf># z*hn$!u90QIBi#HxP_2;jRhNq@LL%RtX;91pk&_!t<4(_YKFU7_U9re=+%Nr}um5!1 zB#;;)h+Ydp-HDD%LH{NY9n)NjutF6}sQY7itVmMOi)FZ3j>Rg^5VY4eS+ee+Q;RD{O{AzG0`B4zRDp+vbkzL8h6|FPsE;xTg==gg zx7Wl;AWk{M?(~>&C$sn>Ql8plVbLpO$%&Ixq}R9^UVJk2dWiJ0Z~7n|;8q4M(gf1x zd6bT`Pp=m#Wx8?kkaP;o?*1>gLeudc&-zF&d*S?3cKa!)4$o1W3!SVESq&KQho&=R zK#hV4D%e`jm)8O6mCD4$jO`zb)iPf)zTxS7As9|KEgtk&`z{&hu1i>P#MO=$W~poR z=&Dep%2)OH4Q_f_N&~J*BnmFdFuK3wl?)Ck?QUe?k{4Li^qDl;XMc;qzaj`EyR=`Wig<{5iZA*M{<%|-QuC&6sP0@0Vty~GigqlC7>TSa zDaa7%0gcBSaJ5NcXGtT#u66bmi6Wp}FdxGApb6ZFzI@D6+UT15qKZp0oTxlpHys_^ z+_d;p=>*zkviJuP4z9VanM}0^nLMbYZndMOCLg=#?C)e~3Q`sHAoq)-J2v!eQZ zQ9_}WbQ}_bWUtFMU1_Q~r6OU6oEbhJSjt{`W$VY6b_KHv#n+2(aJahWTGmI3cb0p_ z`gByugbU^}l;aFQ6W7b3dZZ1@`Q1L`ffoWSf2B}xO>#<7m%3}nBe<{B6)Av#7#FFr znw^0Cq1q9Y{hapB00F9VKUPv9usNBnqa)n9P=4S;JWPkzc9irO=T6aGgWibt^DceT zi!ZwLsR*9@!)h#Dv9?_L&VLoq(Fl~pJS37eUMRQ1YjCes_d+v&D9wRB&|R1LbA|ah zLF|Kl{1X@N=PK9daDxt14ll%rp3JXuGzYe>#6LerCF~uOs9&e98%o$85MB_fjJkno z0Q_;*3CAF#u$)Q}nw_DtO%+6g5sy-=I#>S>nY{2zDBBZ9y6FE^$4AFTkIV#)1X$Ik zN?UP3$?6gRqCrd5#zKvf#*aSE=JC(u-Q=|B)Bx#Vb&9&qV5})f zQPk%Zjap6pvFX(!(GazM+-`r2{Zxz<cDz|$p|q|MJ>Kaq#gT1)g5;?Gzyx%}DKfF0oy8Qu zCr4o21-2L8^+y)DkdH;nav&~*`71S@SW)QnHS{7LlQVKnxV9aAcD2J|2@2WP$flK7 z(0QI?awa;J7y|Z3Icc>+Yc~NDJ;I0_E(50nXrhIlOTO|CQu!i?c+XY`9Yrb?qZ5*w zTP_bMDBrYgf@wJG7%eNyDPBv(T|RUimx7Wh+?zKhVG!V>vr?*H@EGwA{}(4! zT2ap7itGGDACKlEpyCSrTrdo%zCzT{27B7!&FnrVT|ZLOgqr53E?-kn1#rpy9A%KY zTzspvOlep{MPJlO_xb1&n;1z&fPREimf#?%vMX)J8oPylF`~`y0Jx)^dykw^8YkBX z#6sLJ=6L1~E-+I*6{&8Ro}FM$on2fgDaq8Y4cXSm83-TZURTzQ`cLLtLT!VR)c!;=EAM+CO z^Lh5>E|cOxlHB6Mske3kkmUfR^w8pl3Irwf&n3!YF_!vn=`2*KbsX0dkg0jn*NH89 z=Enec1ZUF|njv0tVR!ugkkmpP=QcTr`7xdxR5KcsQ!_X|!92Y9EA85dkiEl9(Rl$q&i}Mx%Aa3st2a%|~WGB`a z-515hG^wZIC0@0-Xp!N8$Q4#A-RU|qsIe+J_2?|zXnut*71~GOLaBM@xFD1f*5jQ; z1-PDEkWS~(lT7Z`OA9iS?yLC_w;%(|(zFY2I!0L7U3-lQEzyezgZqW;3u0RG+(+&f z`>TkSZcSD2HpZg6%?&e=h3$x#W=FYsb?~kZ)H^k6wNgJ5d{89fz^HDjP!W3Sa+`<1 zpaz0YikqYWCg?bP5@1S%*MeFeP+*1Udl+cRd6OF9d4R+a-mHc%Bzw};W>z;%&rLAD zMMYt&tyaCYgL=v9;=c{~7`PK)8v|f9I@!O#OeF7DhU#Oh+_a&W}ctCub`*c(w$eKx0)x{*QrJ~PRKeIw58~+GO3HtM>ZIYT?$Pu zEVv0K%AwZOr%|n8x3E*+1}T*-xJ&czv8R0f^;i za}cBXeHr4|g1E{^qvRbwUWj_X2{6c1(q{#gyyHr$!srC|?S+#VSjim@8>+$ViK;y! z*3WzA*9C?*w~C>4Uc#@ll*RfIvTf*JQUq->^#V{A=#J@6;%f=;b=19qtGx&>ajF0@ zlk&XS36OBnm9hk95~IAZ%*r#BZlRqpm1Obbh(^7(W4)ozyXJ&&0==%n)>G!Z1}9>_ z%v7k9Dv>rN(H_(TfTp+MP85s@eiBefi0mTo*WeeWNkLCS(67?%Sig2n2L+n)tz|sl zT(RhfA*Y_q{4SzFNBzlF*N)gT^SoZd+Q@^y&d8f4s+Wsso&q?L=B*?%$5+tFcywD_iSqK-g@;Okghf{-?$eaQ;!6M7XzqFWf?K z{7rkfTFq9&BccmVJvG$Mb!oyVp3`p76?+Yc9l&D7VHoMPzLYO?{!?aSv4wwG*PL6xcuKZI z^Z&Pz4Z2}PY_k$g16XMc08`Z3dlUM4ofu#>#LN zBd7SgdaEc7cyYZ=H8vgu7 z$LWD^3yjKFg@v+Z5%q=aDRQ;ip(2}A6Xd^YaLb;FUUO}C8T%Jrc8iDNzgBHQ1M_9C zWaP2u2;=&pdh&nC(%?3;LBCtM31~jFJc4f4A7yE-MgZ)@ zEE8_ew9trAp|5LVC6UcVr2}C!HVPKrPEU`EO;3--YS*Dibmm z*f-&))6!yN<$gqGF^hmH*9Rxq4Pf+hwgZ`$5eZ|N(Ji4g9&Bv#m!be6Hp zu)}HczKPypMbhS~+Bu7PuC{s3KvS^kMnj=|RrsP*NzohRwVDU=>+{NUSjPs=YUrD_ zD)pw#6&0IIB^!-uGgt?=+7~k4%~Zmy^!I8q;7@99J06BKnR-y4eg! zbrTyO`AaT5D6*TdBaUKGSOCKBIQry;2f=YVMkb$h?vE~?A<)}*#H4R6JHFToe$-{& zE{3btuBt2{;MNwWrh0N9zK72lBo%TX_766;(<0^dlne2l9onel(UY&}C!Mv1MSZbJ zQxyc>noWr*+bW9}A7@NZYRFRVAUpetp$9iFJ6BDh)+_Q9sNgWxoqupmFtZ<|bUiChU|=7rGC1)aF6Ww6k)la${`bsQH4Z z;On3V?0C;bQ;>S5jMO}i;Y+k} z2{#Ceg8dU846f%?2F{-{vw#QkiPexnh0YbHP3egR*Nq^^73UuWB!oZA;yrklslau-hYW-jNAM}$z@AaS3O zmXS-)YqVxSZG%5eqw)?L72~GlRU}_e?8--(1;9@Qa@K~Y3E}sPhI0s^J?}WWVW`u& znkix&7k2kA&QVj3j&>F_MJG*^6FfV2#Aw>uGqb{j94+(}F{yp_M;AW^G()Adi}5nk zo^Q}*mD<`<|FvT6t==REZaa}R9MovNzXl$0=2PjG6PjVpn;7xT*xPYv}*`#ZGl z3Wv$#>OhA>MT#;{YbGd^6|SnX;Z3Mpq)liOs=H}|q^urWCdlqW)2+_a>+$;boTJS= zOfaU)T35KaepPgA87K>*$zG&4SZd2+qgUBRW0S^836?d}i7`zD+_1BS|4Qu(r#*4p z8$Q=wxz$4V(5)5bhR&MM(5miwOGPVu!Jx^^Czuy1g=9S@5|EW~t`xp^9L zwc5NKC90C(WezhyF{Vvg|c;GU?erYS=~c&MX{z9T+mX!}*OboKnk#Rc~p6Rm%1t*IbLbywPK z8jH+j+Z&G{??GXlzGJ?C`^o0?2EHFr;u3~F#&{N5cA!Fw$(SQFAc)mD|5O0a1y2PTU+)E+E8&FlF zi)G?c?1tO~&JH1C%23cD0>R-`k&OY@N83S4?Ld02` z=#GK6YDAa7_r18|lFnrXszDqafrW#8SS1i23v4KW&iK)(f1Ti}Ww6F=0hl zWH8s11%$`S>No5Ck9soX!?AXatsT$zbi6CszVMWAiJqFSx{K3vFz7Qq)+>L?Vd|&K zhNgb9pSo}-sxQ?6Rhp0_6xj3p(S@jyn}io%uv`78E1ND`FN)1CQVPeFiMlha@5)d= zF81&|$ix)!93@fg0aZ$inHPiy1Svs^h{v)p&kKQv9aqNu1kK%@>1=0jX8v;4@y0i0 zlgAyKL^Vx9cX+v$y8|?9MZTA(H^h5BXXXK>Opph@I~v`ga@MekL6fX@Mj48S_0sE| zz#WJ1!V7b@Vm9Mx_ACdVOK<@Oe8uo=0wDe>OED^Bxv{pFssYL*-0f{%Q5Fl7bIA&c zxIn6QCWr#B72uTCo^|xUVG!?YW=qB7^pv@Xp1ICc5HDwmh4K)<9F6NM_&LxdBjkR~ zluDD5#HV$oqDT4=P6l;O2j3&W$h_#F@3@1GB?%zMK{0sH{xRGxTr~Uf8Jzb~aJLOG zLFWg8DHig{&W-0n?}ytpF9#KF{b(CMPq;|yM}M$lL=A)4-^S#z*!nW zkO}6wFN{dJ5-CzbrYZb=Sqx?Oxq4wDJY~-jJ{c{;HiMtO~w@qH!$r=cxsLPP{Lx8Qm| zU-&~BgA4g5`+JQTz>Nk4p5A(G*=CCfS4MoNnd|YT>b>6RL*aohU1iurq_}FmJ~EOS(sNVvIj+2%D#1JXW4MC6W90SQuz*+b58@p zTyM)`%n)ZQE*N>F*9rCpHnq5ZmcmRY{+yMCDZuylG@cBk@q}AK2!S3*8a^s*26|-U z*bnx*&?6N^e!NH>@x7uXqR6BYAuMg))4#ZlntE)svtwhF2;EPjQ~Ej{bC{?{TEm0b z7pO#vGUF}1QRQH5#Xl9^6mF|Ka6$7}<__O~2>rrVbEaEN~OHbJez@?Du z%t2GBeArID?w6!Izq@^I6uz84I$3I^W>Hsy8fW^gzjtFaMDYpu6!|ei3 zOS>00QTMW)^e^tcckwI!UIX|)fTMtem18X;WI$K*_6`p$$u~+FfHoQ|pL~eFiZr70 zy>dCKmAY`G46ab%J8!VjD2?^qZo@e=T$fG9ps&^%KWy3RrKq;m2=i{*5a&6oIio;q zc)Bn?JQyG~w}u3j3Zq1})G0NYrMuK zn-0|moaM28_U}#zTXe?4BwH%+$8W1^Oobm_~BwyHTV|w zg77@pKmiY2Jm~CJ>lip=XFtYo;SBqMC;O8ale7WkLl)?-Qk=OKVoZFI03Yra61Z5@ z38fRxC!qoi|7d^=6OoN zZsw4oY}-WD6Pr0{_oY2x5tf_%gPl+7R}n&_#kucm+jn@APN4Rv>t9m zj;kYizkoNSMfH*H1~)1D;L0RKXh`dY2or8DxlOnf>6qjGCU)Xm>cyv(Uo7%I3=YJ! zU#=@Psn+FEE=&i=Bss_wJTT06BpxFZC{6Qle(>4?yi*v0wvMDVhS zd+Dg|kZU0lzmFAKBJ#uT6Ocij5U1KKb8P)j*(LrWd^bcyAr(8?Vjp-JS|Oqf#$VL(xeaEA|}0RmfjTL(V$t zp6+Y0bOrj)LqLrELe!wb4IWVE=V`cgrqD_ULF3kuDr0M((<>t~%8z*kAdP`cqpKw4 zi%$eFp~{zsnS&R~xOu?A7=Hyz(x&Z+iCb!0E-E8{G9N5{(naGx|K#bRX8JPM+G&f? zP*T3XJ$eHgyYF~!x?=wL{0iCBxtkmdZ>#O$rv<{P!;YygfICG25IMI ztA>6=5BHkdn%P9jM|d3oxAP4Q`or6$BcsJ8imGZvsTNEpR8fZJ%zWPsEng)62Hytja0T_M52TT5V`>puKv|@tCCO=;M@c&XaD)=`JxC zBpc#PC8!UFP)Bw(z}kVcA$&P8F4T4F9dIq^6GzYl^}}fy@_*!aRCDCI1(tci$M{iG zpO&UgI+P?TDk4cDsEgAL79+J2+Fp}WwIQyn5GIl?9Fg7UoJfkB4-D>`Lr-L@bt9$5 z8k8FmN6)Gzbah3B+Vm8ds+)Bhf@P^$B`Ny0I!j4Cu@F|F_zjxCI)k+~=}C_5x_L>i7lklsz9xt*~Sk$>K#>8_>=SGx~-^FO4|p{KCrf;TxPQ z1*w6%*znr{9`drJQ`WY7BEqH&4NaJ<-+4@CQ2AsX>p*ypVzF1Y$#J?OJS`Ij%`0sY z>;bZ~v)E)%+bT5JLQ6`Qm}-v9w@&4cd#<~0YRZrQu3&{{lHMNn~#RJVp*IewFB%%lC|Wmeo$1)nEc|afP6G`D#Bx- z(}^t!Oauxx88<4{?5U(p27~ttX|buBW8&LYi+R$m&w2JPnzmHcHv2#zCp%5CY7FcuZeEBzA+|1hFmC zXR3KEp2&p zl=}O`kiM&+L?(g1Z6a`EC2 z&L`oPuj!WcK3!RnT$psDD8WKe#TtWG^$sFT-yKzZ?D)cf^bipT-14~4fYqVpRbACy z)p((<0(SlqY5NfPV{SRF^Om;UDkgt;JLneiHS)X5LZlDD9^37$V46|^+8h(qZ+My~ zA?;PB&WQCdtFqPyFHTAJ$0fpETeKLfzehhH?6ob~-JF)=3l$Aa7KN%KZlow#af>TJ z{VxT~dSH^9*m@F_Q82gG5v>J~bAP?t~cTE%$w3|zyjA)Lp@$B( zj+>{$HEy356Jabw)6s7C(XG`w9vs`?6E9tsV}tAnKOxO`J9U3clMhT|ud>@oXEm;% zmW~3ZBwHqu8oUp)lDfq&!@a0h$Q?;XaSE_xlT|_+wLWlat<0^9CS|n?gVMc1S?vRS z3JD1V$OY=h%X~O#MRnXOcIm>%y~yJq_wK3BSLF$Z3+rq?2#PAIi>}V656pA`hKrBW~ePT=R)p* zE2w3!;-YZm_CAB#GeY%R4UzdmV(fyD17!xqMdFy2^fNBJzC2PL?F*mDXfy}wJ5h|3 zi?T0=56a&2;Y9A1&~^m9Gg}Fd?LwGPsG1crV$$oeb9@ohQfjkNEv2U}4z<_n+aihz z7u%%|^7WI5+n`$Vs@FvcQLm`VCxc9##dP`&QPuY>%LMtBq98dZ9t49L4CxyMtGuI*|dtO@t4*$~K=? z$AzPgvJ&2kXjef+36w&6Uh{d=g6D!CPOPe3i3?VyiJ{gDe-I7W4iqYO-2(x=p+0Hk z>O3R!pA)ohX$@&RpUaS%d;~VYXwIvRjvp{WZew9qx>+=D4Dmaut)ew0J_JyDO18pd z>==&iE`5!wq%&c2-4@%3!p$KumI{wB>4~N+ur=Un#~h+ zV?W&HUjyqz^0yW0x3$!5D_)i~_0dV2%GEgGQ)=%h^K|>lo2o02ch^=rZ8A=k8aLW} z6HA_!Q^4-asYsKTXE&FX!xv+XIc_+S95i7(g1vJP@7s2h{_Z2HYjc1pv4$6%+dG!z1)mlq)!>W~Yi=oVW%f`5qN+P4X>%4f25J8HI$5C&rV{vpM zmCW-eIcuJG<=Bc9qFnGoL6f4DGLXg)nABwu;2*eLi>5O+e$WDZSv*@^JrM`-6zGup zp80(7kvbVg*HxBuw3kx1cxGHO({fb6ePt5{vnC-C3XNZ^*9Q9=+nY_j=2Vl*vd`Gv7q!!!|l*^ z;5bv3MA572O$c&MhbnRlj0D&!V?cFzz1=?WbTCzIhup_2+2wLscwoL20ghh?)Z*#^ zUNo=myfvsN{l#h$iAkrdV48g?&D!a>gdP*Yt9&FrwoA+RmpG(Ni+Z#XSxK;3CXH>< zX5 zE96e)$a3S}gv*sEilJGiZk(OH-cXnAB$Fz zNE|G87b+&WC>x@fYZd_rH4{cD#j*3uHt?%#L`_FYtn-5rO@*2Y z=CTMs&y z!O@v1IbEgeuh<`SV|rh)q9HH0L7{2RpQavV|4yeGvoeYd8QGQ%7^ClDw=&wL3zjI{ zdKQhg6VvGS8C2h%g(4{zaQCS^qAET$fKnPl#DAH~rL#BoSJ(87mh5h9+-s{m)DgtK zMvuO-Wra(;zN5v~-Ct9;(+UHLnzoxr#2>@|1%@Butrq}4QCKd;?i~2bfG+uH&&09z z;m+o;lpR;zdf+%)PhY^5rWznUmKRX+D^5E!#s768Vg>evV zu?mI!C&&&Eb#egRSpgHNl19%@J(#10=AFKn|K7jrO~>pbg0G5t-nQk1acHPdk(F=J#)ySZc{3n3yM1PZN*)AH`qV0ZU{&! z$;mQOrY!b2YBeq{fZ^K=qCI3=5bu~t4bKJBpA#{B#@@}@Ugjp4Mi{v<1T9K4oo(y~ zk~L@VyH6N7eLgorN>x#vlE~l2vQQeuVFaB#7l;oLd3CgaW$=@{Qc|biTrZ1- z9#&Cn5A?9sZQ;ukf-CfnFC;@NqP%=-4=zsN+;8;Kmi~t2HL_BXUE!42ND=G2JvBEO zjS8~_?TiF=RFh8KoZDHk{hqnkD!qznzooM5v~+EGF-%5KY1jSv6@$-J`LATplh`CAr^6bxHM9cx;1W^NtDWvBi-MV`WfB z;+Xw4%!pux0do#NFj%2-gx#-5p{0MiyrHAVSwMm- z1AmY4Lp>Lz#On2hpFa-xzRhn+W$A2Qw#f;D+54D>fSmUP76j zRHoJ8i=(d<66+J!gc;l6a+pGx!F{+h*zqEjHCDQs{R-~pH9>byb*eE?XvX%ZH>uO! z0rC@`l3fQd>9hPH!n-Z)lzBvQ^qgEZgfwuM9J_0nD&`^frw^BQ2CYQtoU$IDbqUvV(|EwIHejTN=F!c0RP^+&6z$W~{a zGPA`;)3Q{ZdVS~mSbbs|=9MR-N_yY>nLHt2-S2^0t}fFTmZTOX93iqOizx!WiGV6b z)LkUTyPsV3uuwWddJldAP^DuZzYOQJv8j%3nG{k2Lqm3&3$-~p8CtjiP+yG9IHe?P znf=M`G0m<~;A*Yjod0riTKS&35e^I4lMAI73+BmYM+Mc(b8{>A9{gt)RkM!9D3|&jGq5!J5{`;yb9RbP&rRYwBC0qx)8X0yk<>xfwZHe4}0<0bi;cAjt*k zgKF4;`+Zpp{KQiciJ)b`lSY!uePp1wX`_3MKsD*Mg>InQ1Q`#?Ht|&RwT!DHi)ev2 z-^BUlYj`?hNyVH3=#l(PfY+X>^fveJ}|sLDQ}ICJLN?1Z3q;E{}TSya+BdJtfX z!ClT;XD*d9DY+H~cNl%F(a>=(MFh3%AP%3B2B7MZU|lW1F1vYQOxMxw_>A7=wj^WW z4bxhh!I)ew!UUcVZV9S36J#FGbK8S4o}D0Dg-{z7?ZE)SiK>*t0lN&Ww}eoRh&x0b zOhWclcu1tgxa*?0*cJ_<`Ef^$e|sxyrQhe?xZ|%pU*PV*`pV=COzk(hGJD(u$UkXL zLc&BPfGG|l1l(+G5A$7UGUm0Z`^ax44t+MxM8?+TBe<#@KZ%~MM7=ws&k}&B``tsH z&75VvF!4hPbmb!^LB zxbT$VzEe>|9;cDX5MGybLaNw3_P-lHu+njTMWNL^8f7o`#s&V*oI5FSf#iSG3EhS0 z32uyR&{J0n&(C3XvIovKLl3yQ4o#4U<_oWk!Jg(BW3Mee3*IVBBxa9@-lVNgzG5XFS4I5VkIHTm7!f*$KB-A$@Q0Xkuv_fxOiRaoir>ku0?0J@ z-DB7|-H2a2I1GG&^)f_cQ0yFWfj?@zaNW8iyUKm*&W3wa3Q;RfFts9#hZ9A$1rCHtg!SaAdda!8!6SDsnV-GphQdOR63QMqj?R$rGxs6 zcES(5B8BNE=gk;my^8q5txQK&!wdt(~eu;cZFrhJsZEhD@`-6+UWgb}IL z$xY*m-o5bBe_|6rU;=t+>FiS?k;&De#6hgJ5YO*&Mv3LiFcZ=jD1v|j31CA&@JkAe zl~lm%P|}qwjf2wnxwf6x2C$plE`|OgK1JV-YupEV^!~rs5AVydqZ>+Q9f>J>8~5Mr&rbVB7b@GgQo$XqC+VlpJw=et)i8jxRKPMy(sG5{KGQ6nk7+0U6v zfQQvWFDbJ3R89+FS*Z&NDZaBAN!GPB0+;?}X_<4R6zyTFNqf#Z>J?wc^%r?> zVTOGpfi9~WiK+l#CM`LnI=7IXLsN z$`dbiw|^S=7esebJddM8-!Z=fywENaw3k4nt(SMf#1^^dgem5C{NOAPZf5@BI2N*ZFgH84F3FgWfqKa0dxzk?|iNHZ5WCu|6{)m2Hj$AL1 zoUs(TA4gc$gv5U*YjI?TWuek6KWL-?GK; zFg`PUwFe90@ig#)3XNzt4!#fOB;bi80H@7(%W*j;%;o(fQ(aWjcapaf*==qd#=Pmc zWy9ub|PN16-#1hBtb?`BAj?Zc;&|!5|mK=ZKrAuB&vVWH9iq!Gk|?f9~w**6J!GZ(G|z z-6HQHhkhO))}ZKjC>n!?EK}0HBj*8@p{#ObR0OF&Bz0**wN_Fj7v6-D^OS!p^(yN% zN9xk^i}i)Ynk;pfzU4rbUYC*AR{1ULu=vYv_DywELUeROR5JB$Lb5VE__mAp@3#$X zxL0qhEPMdAa9Rg7bY=D7wwk$=5L1@kJP9R-&oIv=q^Bo5$6)TbK`H@=^MS(Lyj)TJ zlaSAJS;PyEfUC~}YlnOb;ECKgSEEP589g;y*S%wbExanLXbW#@7R>ly=cF-b9VZ{p z!gZE!ui4S=-2!SXuQeKLRH@2_JpGuZxIZ@3m_>$ukVU=qcCj6VVl6k|j>KHBG)shf zc0NkJ5>|c~7l`rm$U&aTMQwy(wb3-O>MgS`w6YidqyG{*VQXg}!PrR(yy*~ojd#4* zX+!au$1rY`RcJy&{FNj*4!4y9o;RmQT=J;c5A@1fD31yTH2Ma@lVo(iq3@lkHc89$^s&Xi{iu6$+v@Oi zd6s-@SII~rU0rvyqh?2Hs3{XM*y6ddChEJ#5|Z;HgKKJx#Zo0&1PQQb(Qg6lJ))NZ z%bf75a4D1#cNY|pX)K80xUy;NilYalijd(FUiSwCk49NKyeBk6QBktSU%EOaBPk|6 z$&eTqmnctJE%RSfQeoA|m8)r4LR3h5$)?RkW0ua#>-B{h`E8Y~<|&$jD6(2uRD5C!}V<7ph@qPi8`7azM6bd#`mwS58;|sJ(hFRd{8* zJh%aHE<(M%f;_+ifh6V#UII>0QQn0@D3-sd&Jcnt|5j>q)h*kKYJ+uY#nttuB-Ogr z9i@F!Fb9MJ@nFy(@$fR6MnANRU3@+#Co@%%pN0@pL)zurRC+g~qrrH{)c)}T;oUDSvDxx)fD{5d{_-^yX5*9Q21(ut|z$G0QCB;uSZ-#xi| z@77yynH_!T`IjHS;Q{)XJ)miLq%kRD*A>U+*P+|j({KAdE?Mvkfh>}rM2CqI8R5^3 zPKwYX$BaAjC5oEv)Hdf(O_VA}QFv`^ctmV$M0hOy_F(TsW<^d=T0yaPU>JWbHXJI_ z3=)&ybCT0gS2s^ZKm-%OT{;1G^s(fuUodZddIhp-v_;z}mo(7@0OA!9LF>Z9*9Aq;A2s(*Mwr8gd9jM8l-{WHBpHO4;iL9M0bE( z^synTjnd>4Xs*BJF|_?F$G)Sl)!eXdxKCPPQ0ppmg|?xIhADO%ba`yCDP?EH)-G|KLnJ+r8xp~z~S+}o9s2^2)1^fG)BvfSXaz>wjV{6#GDX<}kg zyBevs?DUL0@dsa|Tvx8Dc2BbZD>4Z6;RU?m10p5tG5kF*AQ(bggQfg+zu(H^o z&ds*eSLaY=Hp!nG%x+6=t@YAk<6aQ;$nNfIM$HI+uT&ZpU(}6iXCS ztE7rjX6I_xb01M$SDD2$R@2;tNkKb*mX=sK{efRTgYwD= zmLsGS6N2)RSC_6zNUg01DT{3GDFk^;<4-(}KM^Vh#kl+Y%y42-U^*1H zaC8Wjd3?ULs=7c{Um;MW(*_hNNJ$M8ff3Qzsjn3XgJgOyT(cc+I1EvS1}ufANDyau zi4Ni_r?cC75qk0plR=f6P0h;Xv1nhWf~bQpRoS+r%DM!OT9wCF}1}e zl^6+E2hbjHVCL>D=kR|TpgRw4vk!p4nG< z0Eby#xFe7S*GUw97Dfa8NxNle|LJL<*e3XUGE_=|!Qi)>LfQ%0_uolIzyd*)rw{<) zRJ+r%_w?L_YL6u9xq`>WriNSBCq}K$Q|p!N#sX!oW^}S=cygkpk-b`S5!*wX2TXk( z^*ypSS$w|VE_9~$cG>?p8?VL(w_NWE|{5NBeA&u-s1GgM|_|Lpdm zY3yU@LpKf%^c9)Ce;#`yJqVu%{S3&u2bl+WM@DdJ8dXkmY@4oA$>49q-u33Tq?}X7 zMaOTb8_6HG7Mm)&>!&x*jh1h*(7lq@GDDfg(!R&6QF2Z*Jx`j3H>3qfi~4n{sv5ni zrn9xNJFmB_*qoo0lcyUgG4{rW@Ln>e4SOes6+B0$CnBh*$S436Qk|q|13@l$Uc|w2 zbF%aOAW$#|9-CnCQYceAOqu107CB+wLG>X(r?B??jD9*RevZ&Y|1N2Ne!A3W_M575_ARj`J^8<2_ctJo*74Jil zJ)vAo0-2XjnbqL3nktMHt)0~2ZR7hZ6^hE?`hDyqedx$3>TFGFMtx>hjN z-s&$|RjRHi?YG$TMAY!!O^D?)*HTFQv?%(Z&Qg$>lA&&y zvsu_b-m~ks6_unBOl>&(!R{pX2u&Km6!}@YEM;zBb5MJyxik3iPY$g8X%^r5W$04O zQ~$6#s;QT0zVj>F8cn!8nMP9?Iqt*E7X@SJ+#rUP-GYO;tkXQZ&%eZ;nWak4Oh_p( zcQw|e<*aM4O>Z*KR*l@)OdRCz9pBQwon&9<9ORj9c|uZhpk+;orKp0=%$Vyb9V@J) zYcJ~*JCx{Uf_(o9Y*_%@4_*j0$1y9?93((&vElIO^)(jNf$ezH7LXUU*dU_0^Y#1da5y43mQnALgOD zM)I#^PkQE$<9A(i^-bJKzda-Q*YU$4L8JYx+(}0+ke`jP^E?dLK{B&2_S#>MKh#VRluZ8pamjzKxgwZaGdYyTAJ@0jz@K-QcXzR{NYvw@g+a;X&XUTt8IP+C% z-T1&;58eNK2Y2S?|GX~w?{!zfnU&qS=}qMX?f#u88G_z{G(_?p@(}#Mroo820jkxe zsw7uZYj=!~y#MqQZ*+0T&QN~uO1>*U(2ox8$&(8Q?=o`dR!Y7X&P9^k+qq9runu_q z%a>38xtBY4j#~YR)gLk=@(5*{qXkdANO)=Km!HuroQsCYVmdq_IVYTp7$DX4!Vn!8hEndI*vu|`_|CtE$id7! zmBdQU*$xe_p#rM=6`B0WgS&*2zkqlK53%=?lbCF(_|W(a`_(@`Ss3L`-b3Y3fs%#F zONQ1`0X6-~%oYylAvhYtEK89r3MV640L+Yt$pGdgrkScaJUPdHxcD{u!#MY*_Cd%n zO0rma>F}D>HT?xy&XbF|lRG62*OQYxPoAebj!x`i-)6seu>YLmjy^z@P$?2e)!~uA zfSN&7ws7=3iMxV>dX<8@Y+qlz@QX`a&@vAv9wwjXFjc9U((|7Zy`>R(z zT^dOpxnbf`_D=SBp!~0QaHk)mwot8%UoAepepoA~RqaOU}#MSIx_9yJ4?Ctxw)32kB zQ}c|Z{<2AQ`gmbJfBNWdIGy&hFa6;65Bdc74iKpflcY#OMNadd*Y>YX%F0TJ&&rCY zPp~o6$BvS$gar6XLKggNGTlfeO5)&W{o!Z*Q{*c6b#zVrS{8mu;$DaU;JXsQG06eT z2z7G!QAlY*_Z9x7QRyHMqf`0gUlBv@`{_i!Pg=27m7Rpc?6xP?6+F`=5|YRGD}+xbA49(M*rQCtd!E$BKB%! zZno+*@I|h(8)Z#R0VBTgL<2E4N_rKW^hn-}z|RGq^P>oYQP zgI@EK`eiMBLw`-b;1>wAT~E4LLy=R$B_GpZ4JH#2jy;hSKhZyE(HCKYU{+gcX&d@Q z53w^_sjJv=b9|E7Z>g=cq^+%_w9Rfdo9Wq>ma3}O)~c%IQ`AOTJt_}CI2P;qRs5Q11DAj|O%9*4TQ(9hSR&Ko_EmIyFqscn4 z{pQU3TI){b_p8c*1rXo(9mRZuxdw3z;uiRqiApB$u{Nz^hlTjLNs76i8fdRlxq5bp6 zr{}iq*gP{i^i<#!75(GesEnz=A@=tV+{6B@55Z%mCa5Rq_aQgnM@9KZ`OBkHbpCRk zTo(nGO&!TSdNgk~q9<%tapXwe_VC_F`rbyBs=4d<@t!u7vbpP;Yv7DAzbSfzo`Add zgA8Sg21TD$5h{A*)u~tEf0X`>HwNB#1AUrwX}jOcen)|G{dAI)u$SknZAaNJsBqNV z-a!>n|E6yCqrrMW->ZPvdD`!CbZVsNU3PJA(qDLt@V%y`ChA$qFVMLZa2E9}`!y63 zN`A3$2h)#u!01QQW%MU-7Dz!cAEpxH1n2QkR>-*hs;jo6U(}5e7PHwB5pFe`t>JXa znKKVQ{K5+lH*K}qwl=lSR#eQk!sq4!qz?LB;CH_;R6rP#`{p)!X2Kz-XW(B8{8rN& z=>J7_o30qH6#E$|ID7-o0IJTQGg`A}U`w^$WU$hkC5J{g?Wr2A&!wQ(^_G^6b4A9o z8uRS@u93=1C(ASXQNjzTvPehLpTT=4c%TR6FU)6f83V3?^cVl3mP2C|W9pJTLy2~( zdirwdW~SO~D(UQrj%jK`Iepm{`rt%CySCUK%!Kw=8+tTbCN!m`nhHZ7r7l$H^+@Ky z`=LkaH~m)otp$uUlqv)S8r*l7M3qN1Ue?>YRrF&E5B0L~WjK zc(AIxys6$=h608tx{*P zphMAP5I$J!psNEPlm`(+65j@mKorB;pq;L-+oaX!>q|;@)YNP*DJi4^D@?|UD!sn? zg_g$hvWBJ->XEJ5E{Ti6yA(QmPiXtg6C+#`N#H0le?mV%e9CRB}KgyI_-Y7r>9 z2>1w7Op9qE)t zB!yq}6^<|HaQGECFmQHY0G+cH&iRC#6Q+s+?Ys4AbPipMj)8MBmVTfj&Q&(w>MQUT8 z+EFM7DH=9HYa~z8har|@L!ZRoJJ)oBSpt-ggyT@|<#HjED8j@T7XDEa1@+5&I&|X| zsv`e{K2=?ZdDhmE5-?mKiHJ$p#$0#Bj;E(?yrSP0+D?ygI}xW2YLL!-#HrsU<* zhg*L<(s)fzZe~VaZ)5Ex4Tb{t!-#d!(KVaZ7a!jG{I30nQ)=bSKOVX2i=&R$ludaG zlcqHfrjP*5lHs=->61YD)o?p74mV5{mLiu(sIS<+7VP}tk{@<8&&*zVC4JIS`8id} zK8xM}%-p5R!PZ)bd%X#zWhqG^@GKD}{L?s+r(nw@1qxlL@9K3EpVP6y0z$)jd?KX;acC6io%j#;`nZ?w*;UPqh7XYU9l-PccB=|m8bU`#l1kqR!64?_)mk22L&aGt^ zsEY%8ukQx52|=iTsx`&4H8tCcCpx=3n~pRA?qi4^9PIQC_(Xu60-cuxzf|x$yo0671;60Wf2R?lF+UuJn}pj*O2ME|Au9UtEPFpd(lJZ>JAQzdaE_J% z@1!mLKz|N56pZ`<2GkO{4I&G$iQwc?N`4o*jG?>w`tBMc%t?Ot@4gG&1-g;Dx`)l% zfjx^6lWs@T=n!|pvj8l@fjfcAQ8@szaU*0PQqa4il?9vI_sxFYxJCDb<-opu)MI@H z_7nPquCHjupq<51F>UD}{a=7H3`s?)+$nvy*BS-#3+@OBDFQG(x;s}p$OGI`b+6-Y zcuWG+`z~2406lO^{v*_t+sy*t9MndDItWDvVGAt}GYEz-+;Rx|I)!@t_6`hoL-Wts z1N$sii*+9r$$mzkDC{dL+Y-*cPt91gT8NPN6#<!F^?q%CVR8mDce2mJR*O0>#ixlvu-VbX7^_J<~j+#-E$O3wPA z(6oB1u}d*(PuCXYCuXh>3QKP&(ey#Eo`UKCKY9&dgtZgGCq+ZAIW_wfyjRCPbXPC> zo@#({621rINEkQX0{ECW2ST1{sjg|6t-h!R?U*^imNzsuHc(HqwzeHbMLXK4Q*Z+O zBz-5Gz*!)`t@NGj!X@mv-M@WFY0&rZol8rSx8OSwcLQrn{3Yn^6c*`F=KMp&wcWzpxsd#3B*XI7!l0~-;299{p(?57vQUT{DLzV~VNf2cO} zy>LFY2;U1Y2$~7;Zr7xUQ=mDtR0;b_>eb=lPHJJe6Tb60@N`DN)8VL!LhRJY=;)i0 zd-@i8`USSxdQKvm>ab>$+`Qw<>92PF{62b}<7?_L`iy-`o9Uk~y@TW#wmW|A_;2jjMF ziyN`nc9c_R@?oP)ZGL`THvCRa?zgGZ8y)X9%hgqfT6C>B*)4i~du~n}a34qk_^ka9 zci=t?zkkA4xWXv}M2|=tYBhfzEw0*8R<^U!G8_j|t0BKG2iAt8kE+Y39IA=~EiH$t z3gnHj=JH>MTe5Rn^)MSSw;gjj!b=HwftTZ=Yq6#6RJ7&08%7RA?FlX^sAy@a7*5=? z{*s}F-R0Eefr@^6X5MgT=SWBEujcd=^8Jzs?ts-Y}*z)Y^mJo zg;x1;YPBP_CBuVQ~E{wQ2ShXxz=+RFDf z)$cCbN!hK2)>Ra}p`@{{b`X$gJ5W)!r!m!BTVp|70l6WnK*C@zn+O9a=F_SoAVmg- zPQlnVt95i*oizsfOG~}lEAQ{XH4=~oR#LPr15G%b|yR&SvD_hgr*dx~fPqpl~ofWec z(R64pG14rcP(Y}GSOyf3v__5{b?>l#rrKhunVpU6)X)m;R1NzyrLJyhsAgeTq$4FA zVFclPzt<%+NE46|;)q)moHNFm(uK41R^E25a=c3;S}c9Up5uNJw1oI4!@z2Xhm4E{ zm2g5+ok5-wCpyQG+Eg_VlR#gKZUpJc6u<@S2PD%_-CqcZv^4hS-^|Xw{1YmS{qjqw zHD#Os#eFx}FCU5QmyaB1Ru?`;@(OSd=t*~)xDp4~p{gYEaRVk1N2l0!WywVBhAER9 zwFIik&B^J7ngRn4b18h5Px{|EUc{f<0Z-DE{^%q`|8#h-b~q&%@jKFvJdYy*;bMcR zzSvh;y}zMhe|7DlhMYP@9tfbCobK`fVDQ$16%_|tTMycXvs#VDmaME6W9J~8Uc;UP z$s|b+(_?`nDP`FgQd7$|Nk}iCGb%d+1|J6 zhHM~9LemL^Y@MXDb+%5YlXPbf2_Xw)-$?-3_pmAmMl*`2gUX=b^SPih>MZ)yao_!v z2n_1*xsA_pX52?V=Lv7b$B>)vf2!{7z6m~u@JqU>KDU-rr%s*aU)8ybN)~Pk7*O*v z99gj$4sU%v%k3)-$!yHAWhTy!i4L0x#Qj3wF`Oa3?GdCyTw~ zvOny3`|@S%WwD?IjhYD>!zKZdv`I35wSE2i?Z4i9E&_byoOabe|H*ds_V%*1y~2juHCo9YQXjzC zASImaVY>(wNC81OS@^ahJQpP6IdUd)amyy8VON$jIHb9Q7D$hx*W%J@tA(}w zbPua8wu5cxNrLhXpTPE0u3f}nB@|!ZYxN!0=4>jCN{fn|9TsU1Xr9ts9F-b1BQ|WN z9ofwC#;~v{ljEXW^jFem9ix{ahvU|F6Jj?>I`-Be#3Wus?uIfp&b~`7<8EU=`9Kp9 zrt;ATAfbKai6!JwC-l^bIb}k-8^oUNE}gV0cwJlB`fc0Rm$j`6UNxySueznBx+l$E z)gRK{9(vQNRX2yWw}-3~~i}AiVGP2m|ad=l3W;wEQXO)#LURdHy%N$=;RHTj8cs*4$9#6HW zDBV%#Xm3c)uqRpL;;go|ioBK?kr$S_nj*IpdDGLqUZ@lf|6mQvfaNfeJYBO5lOv>7 zCdeE%TVy1D_v^2Gjr>2A$zQDU4D91y=&=Nt2mXknl(5P0X-!VL@4s$&MuwIMOcE#9 z!oIt+qvOtH%kJvvxNBMWwKX-@c6Z_LHQl9`78EWis#_Pn;*zXnS7|A}=OT1P_?ia( zQupEI%kS;(zIXZZ!`&HmH}>}4(9m#0Z|{wD3kp_L;-E`od2D<=4l~5ZR@Kv}W8fIx ziELmP*&ILO`-qOpT!kgZQJ;}k8s6DCtu!^O!O`PQN%eSAQ@p&YF(W!6OFNulot@c| z>nif*=6XH3sNJD#XHT+s5!b#8!(eLl#f}3h<-?PVrhXe-)V+5gm2iuNZ475E(zu9)}7XDUiE4UE_Uhm^CAV*{9 zS>O6Frx&7DG`U)`vfEruOIbvIvfb@UPA=fRi_#ihuEzAm3tS$LE3cr?g|=2GyOH-< zODmmWb;rSqFf0jn#~Ye@3|YQ6*yaQM{RjFF9qPvuvsgb%Ma~sHpx)_Q3*~r3G*&FY zAeO3~SrZo*86Gx+ck$}tl%lvaXAOH`Zu$I~=`*dfriWz}#^y#RCIXE(=s%V+TbRC4eCpNBz{!;aw0`m{bJ59iHA;UYk4D zJ+w9oKknKrcTSFH?o^9q>JB$OIi2+@_d<_gEyx_^^nIOtXQg&Gi_%x_zhmW+?CUGn z5GOLQ^$imIj>&1y%WKbZcI4%?WwUrsN=iX~3a#2EM|yLftJ&db%ENli%PsUcQEv{` z>~qGN4YyNjCQQ~|`ihF2Z$3YKnDP?w!1dm&*NVA-nTI_G)*%c3 zU*)zV4-LGo-MEHr`uN8rShm$6n%ehc!W=*mdVWI4vIF-qKzj>&=%OV0!ttj3D;Zy+ zty|4*dDzd4pA!1^u`J?>b95y;&9k9f!5^6+lM>3sj^%b*{X!tF0cV?ZC;rFQUwYnCO@J%1p z<@_A*1^)WqFv`^v%IC_>kWu(0YC9rOI!MamEDV<-%=`P_*(KVbHd4Qn z?Pe#m!9Vc6Qym|6Qtqo$+rzHXcF_tWKb5jkP-BLrDffAwrm{(_j@XjTnJRV^>8ON* zmlpchZ^uo|GDYLhS@q_*dsYXh`PTJ@kcI!&C$cq0_KLIyA|C9x;OfFCPI{i1yIALviGO7#nR_2}r=L_c~O zOU)FOD7)_cDcw+n^p(&w>3~LAK5)0PIy@@GuvImcJcOO~*X?^{tmtA$$Wg8!T z)R%)}i7Q#k$lZIlUv}N^SDw_l!mm+DR$iuFt0$G`M6X4rMvUeaIT{xHW94e#m~tA8 zpa-}9Zlb69W%)_vLsL18Qr3S?`39q8$_`~OFX6Rh$t02i591w%yOn)^@L=ts12qQ@ z)E>BPFWw)N?+=m=n~6LEWozgb?KI+^p`qnn-+F)t5qrhTNMi?D66}o3-#i!JpPGU6 zQqg)xVfUq}nUJia4UU5Dk^3@Ob4d&GH|#$h+HbsVc*pBAcT^$cln`ICCJXNl<6W^f zw~}qlMD}T6nRZhaQaZ#_k=GEs8`dS^12N`t<&Xq(Iq^==gLf~P=!r+No^#6cqFi_! zqW%+N&H|N(g@izo5s7|EW|2hhi^6&q5rd-a7tAnU`fKBEk^e#pX&19E^kBi>hy>V{ zVz;~GYp_ts8~vXDOr1a{Uj4dRxbV_{@R=Os$4h5J`p+^RnzwUj%?ej`s@0NKTwA$j zVfV(M%{xKVym zIbtfuH?HaRpHrUnm#g_kIrQUyQv1|yKm?*(sfd1*(M-feb4Zop(8B~4ZkhB)auB*U z&_BAbA1F8`C6auVfQSICCNQTu(Oo@xXXNf4@2a@P@k_lFvA(=^``o_N{-WBgI zWpQule<2lOBt%$XWM-u;)mFF2nbO>x;-u*HU{!lVLq-a2;EyU>u%PTSWJU^%aJH_K zDQf|zEI~T||57E3NWiVJJ7#QbHRp%6MIH?PU!}2N+2XeZDHi zJif2b>pAx&%Atu#-rD&WKvxj{O?STaS)O4{vcpcMn5Wfk3F70X zn77XxY?{Ao?%W-<*>&+z&K>jPlQ&zYU(vXsK9j=B^U~95(%IQ(R_>oCgUnI0%e=`& zGh^ezrl%Cm8W~PUUbZzY4S(CxXiT)`D}*fn1$|MWzYmMCiYT|52;Te^A~Y8etP~VU-?Pp7E?LCiAS&hoN_OAC>6I(*CM)3s6}BhE5b&SqGg3e zY4-S;SGSW!H2zopE&CtCKa~Fq`=gkIX3)Iko-j-?YDM zH6;}=M$re+H_-?5{#wzucy#2Tx(M?vY*UqC4U+9BbT9mHLzgnh2k}bMU+bekrte*n z{(B$&G37Ba#`sYO|0VV=x?Gjz32ahLmesw}pTHgHH3>|5e9fQM~w{0A_ zZgR~Om%BXcYJs|noEj5uiDa7v_=?u6gRg?*#8d3TU-ua8EHaCFGfCqYZIx)WMJA&4 zw6tjXS(6f$U6|u66B%h_4@G`jcI6YZ_7z-%MXde~TzCSb4-!_?A)TAqLIU2^B|u7y zz;mfr_~?(ba>Yk4W=ql^hviP(m-PF6^uw^m{PeUVO8Q}Czn~{Aa+k10vE!jX!^$yO z6k>-{=~k*Rsq6H}l{pg)^=EV8fGNySWnA_Z34C)>n*V8Iwrja|#KYIo+F|*v9z8`a`_|O7 z$)mkznnd0(@~F&`V*)eno6B2TMvj>$PuIEdBxo%>CAh_KpQEuBD#co${fMh%+faN-SPFV= zIle{eOX|(A=$4HMClKCI7D0YFaw1h(R|w+BgDJ%& zIZ?AM{j@@9JgN)%##C{4fh!lN3VG1V^V9nY(F4`wRM1J zlIZRcQaQ)T?;oj&5MqkeE$Wasj#&6eCOe1ILa_vjIHudE)PQB=7%M?m?nily#6=p- zSVlgeW+0Vw(c72&u>kcr?$IMJ85#w64BSVKh#gD-;7Ji)#sA4xIIZJCEj~OZK|{k~ zF6oMz_Z{$|M9U^`$a0MVif+rzydtLnvL)-17!lNELW!vUx_p3~_jRv9_c={sGq3$9j1HtOU_6lQ`9BGW(1Ghv>arKLWLS*+yC; zgk3%V1v)HNgLqPT6&g|n&RTE=&-oYL^8?%E(gGF-=#}%2`_>Vj#0X`Zcv7Nc{iBpX z*}s5QqD2#j@D$<9RPrroCUB<9*bbi`U@m$>HkO+uHZ~fauO*BQ89pQw%+gak08s9E zBk}d*$o)OcVtk9axqvD4tBKsMo` zSlDCQz4i5*dV1R1Swdu6B=EvtMYl(v)}K+(+C)5yuTL=^VoJq8dHFyETV~b%gC$!# zX0WB&D^`84Lp+IMm@=LesC*^n8l&bF!m^QmN}%#4=3LAem*`=vMH7D5&qqI$_+!CN zAD;O9=VK&QB7>049*;v3ZnsJP*%+A6bXoyFh7}q)tk1KQV2p}(A33UF|F2hNQiS`!KzN}@l7Qqn`&zZiUFdhT?&yB z23x|0cv2y8#Ai)V9BFJn^bDE&wjqCTBvI_KK>-X1#DJF4ULX*M7R^~_OWZmK{&ek? z^D8$c+BQ@JjX1K@yPp0is+W%7w@BItoaK|4Mii0_i2<4DdWe`CdpI5a`+&fj}?PPvu?^ zT|&&z1Xu8xkmF$KJT|kd*c5GAxb>$I>Id3A!8yLz)7Ics%|8iN$6ysW_yVhjEK~w8 zFhA1Ki@>k3&-<_+MlrT63p%zLflOa_Se2Qp-&?21pJO=(-;xe;gf`xJt|~r zSQ!*?ZTL0p=jv4Cqhe7mw`FMxh-+8?<)K!Mp0GWE!ZvtBHY#9#{J#<{D{`hxo3iMu zUK8FAZOWKD-fpfD|K^v73|1L9q@bJ^6l;o}t`NPjL`Q&y*n(J!y7H ze@uDWL{EN*q(7$IZ=$DMoTNXY`zb>A8((pC`EC(199oVZ|$Jyh+mHM0q;O9}@e;|3f+Ui#2#byON&jk@Uxv zxBc|6x8iZ@74`GDo|%yJq^%|Wu(HcUPyLkj4>#j6?DxgUh`opXK7!DFMy^m?Y3z=Z z`zQfX@O6o_53|1=Y;8WcWbv)d0X!mbQ)lJkh@r#>wzO{!(z?|xJ^ZDvdseJC+|_Vk zc|z>MO@~;XlU8Hi`Y-}&iQ?pivmZxrF(M`80kKr>x2H;!996aD-80AZGpB2Q(#XV++x{q z@Rl3Lx6Xeqy*oS|h0#f@HDoQE6o*+GBNCyTCPHJIkk_jx6Enx*ueD&ZQFL!k#&(R8 zDZZ>OTx}`E0!?KI@s-A5lmK?W?}SQeOR)AP3_zacJChP4&!}kj+R~CblC@a&hVlV+ zO0JSD3B(gi#>$?6(ZETvS`O{ow~yb~hKn^i$9>L%>@KUDC4*oezXzYUwNZ@*HVqL# zy~bANMic_tMiF5Fd|f=**@#dwcVku6g0iH{%%pODPhEY9B>_J(WIJmV`mR~{US=Q9 zz()Epk(h%Z-m83xH6c-zGW3u~_&c%K#Yv<**Oz5knPp{}S!Jch{geEV*PET=_2xuG zMOkOeo-NxQbtr#^l^(7r&heWAzGwGs({|vkNHy&M$<;C8f`1EfukJfO^d0iWrlhE@ zeZsts@TloiESF5Ps-?mBPjpyV_$2xdzuhxwQfR2fQs}v4(!`J%2t5#Lv=g_zs<5M|Y9j-6+}^3>u#@!L ziCL0+1$nB+FfYiE0f0+8XDAd)rqJ1cLpf!6&Ip|?=^r)dg--JAEq;2GcbVw78T9-d z>L(p0>K8g*)_;ycN>EOg_i+&;dy4vlOkStxk6Z<0iM&t!LHQm*FZV9$zub#X5ek6n zADcx&|B*#PxuhpcN6@35tNipLgZK^V=jzF=cmX$Y9|>Q_ZU@i7O&~{>RneYja ztG04{a_zU6bJWGI5p|j7oNC-AYyB2abE=o!=%d2(R0q}m9e&Y*q(U1A##0-piztki z&VcG#B4SE5EM}`ZMM#f5+=jJJSHQTr_4P>ulU^5p*vZh5c>br*&d}4RF^xF6sXv3Y z!VpUlm&drQ2$P6MEv!*I_Zohv4)(s*g=f#4!5?-+)bqIGMUWm zS3I`5y!^Vl{NpxHg1fHH?XJZ<;wA~+f_q_XxG~}l%oJX6rU1NbkgNUVNWQIajicQH|kyqIV& zE+&eLe%iR0h&8vB)6GTYZGS{DeZ(w+f^TSQs;+Kst`@fyd0JXi)6!D3F1@f?e{D%Y zDgCyHci;}t&B|GUl+>}!64fm5%wi7k`FZ)d#8K)9N`g+(=T5N3iR=jEi*SwHscqZ{ zfG{p+p!5m@dgz=Q1Y5~rpwqx1JHqs|&EQ3ZBXqBn+zmQHf)^+?9?*xo5`MjY%M%Gb z^EjW^lkmirb##|PUrEjS+3$R@Ms&;@4@R@ErY;26gfGAAY4}{ec6A4sE&bzPs*< zbL`p7e*MY&kzd3&#<$uUZ0|pxv>>rHVL`%!K@T0FwE^FccVcZs5Z+SWvLMeTU|8}@ zJee83=)`A7j}CO=hfc=R5&2zZrF6o4W>fdtwcSmftP%hr*FsNcu{*z5>ML<3kc;Ro zS0Y$H{}y}i8PzZNVUzZhc*8X1GTeOx=-1pXM z-)=50U3hpgt`1<6_N3vKpP`-xI&k06Gc>cOGi^_!ygq<bfSCPH8%@WY-JJFW-9Cyy8`5bLW<> zDWj{s z?>f$Q{OrHa3{e}#Jy*0IsY|D|rL=oMqKsRtI6o|QECc7wRT+5}H zlJQL_xR=PualC0>diMOJSuHtLU3lxPPngx})IKVh9-B2QHaR(#b>UBpXL@u_G&|co zH>)ZsJ1xa2-Z<0Kaz9IqPENMlY)~fvwZid&$Mh~dregKq$-08x#ZH7n0Vbd6+7{s= zMFez(wANcfrbR@k-D+EP->lg4&kH9>t)RU;L3xx&53^c)?AyG0Jj2nYYP37?!g;$m+K9$!CA~;R^h)z8`)#dv@2ZD?k|mDU5_y zDem?U7e<^g-up2t1A*-HKSv0NHVQ{w&4)yYh;* zWIAqems!`6l)F&d6NmwkRfBq-7#zaGXr&6}M@8n2tQvemzefiL2f2M{Nb3a=Y#Oxi z08hrVlO>JJeu?DqJ%f9wC7ulGAiX)<;)EvUElvU(Oq)5XrA-5SHxQZP7AI;++}^at zbazwmAiv<&rdLHT7T{*8w|R!n52wXu#ficNgM-uPFk!|}gBD4W=wefqC)h8Ppa6^q z?`e$A(N#dqr{PKThQZ5`NO`=WzUZzGDLpo6@MB=)&HZ7&92y#VbsRPxHFYUXz?EGo zdLy-yxbbQNjp_D5d|Mf3rGxy+p`lNgE`9B1Kf?^n=B3IlYAnqYZX7eR;^#U*Jn>nv;V;>KYCsTpPdrZqUN;=46vPS01lmu; z8lm%TrejC&>j~WC(&vF)v$Dw5mbHHU9lRpnSrM{f($!1LSC?$Qvfgpv@$`;DLk3g9mB!&9)at9C6}rJUev%b_`E65rS-v z{882j=l5luJv!nmr!?sTK1sLu6N6bkuE0mPsS-atmRs+jT;Py>qe;Ly^xI)Ii0|2n zle?BMPTUf2t=kR=g6NAJ$E{g{|36M1)@@l;Uf7?X)9MV2k4tYYENpdzYzR(`i%pq2 zgRRxBFDuK)EGf;{Q7|uWk++~XPYrg|Bm$P@s7Z;khRuitDAST0rA4>R$sT4%C_}$ZO}XNQb!ihQdwABSSl`cT(`6F(#u(yHj`PKR^{dv)mLoY zv8iv)b&I#-N=Il)+~}Bs`e3aZr)&HHGNc*=oF<4pIdJWkRo6`z2&gJ9shL}{{Bi9= z_VDl6OIt5pvy1L(oL5#_)A&qZ`!lo}@_>}GqfW7djER3DF6mS$^yy=olMSsbEZJCH zGfT!``u>A~x*U z6&rT!U5bj>yP|M@-&y;dL-4uxzW06JzkWHNnKi4=nl-EL**juHq?Vi{o{Sq-IAmy~ zaip1unMUZKVdKV5T#z+miAa~vL>gW>Y~tj>eSiL>smR=hBJEdLs-{=}I-MC^|F3s=v(KmD9DM9x|#HfQXDvXau&FFXE9d>ZL{FCd^+e&k~4 zzk&1xi>g+gnV0{lNb)w3y6YBJ&MgVediWiY=Hu{Ru&89!V*h4SK-!Ohw2G2NWzV14 z?kf@hWs%Ttiz}B^^}Bz;Rj_`XNSjN9w)t)42B}H=!uYJT!WAh6JbW6TdhiA*jqEt^ z`JauFyLfx$ZsHPu*@mAdt7Q%Ncz&L&=jYj(wirC$E(9;L%Lwk@;*p zE&8*JQdjcKEXgt>wNPzH@OM&DoKknhq@iQwWRbsY&^Eu)E=v=+NaJh=`!G(L`04>X zpdnbfZ79Bd9QR5mtyHL5q&&eSndYXg$uv1;xH;04nG?+UX1zIJo7?I3QTx1o(SGbb z8Sx@1ky?>@k%p0uksgukNZ-hi$cV`3$hgR?NLi#ZvN*D|)oHCBYxQ%hKhtWZHB3uQ zYn9d}t$kXLw0>!YX}6|rPTQLHSnCF@+q7=qI-`wk6K+$dP5m~F+N8E=)uv0E;caHO zDQmaA-OGR3XcW!@-df8BlWbafdI zMLI_^BfTO6BEur1;B8uDc4VI8?W9(Zw)&~nA89qy8l*K%Ymt`bcn%@rm zUg!7PBEP4&w#n~(fBOMnv)@yHyZ-a>pEuw0<(}Q2ow|Gb?&o(uxBJ=MPw#$s_dUCB z{q*zQ`69csc743-wOudodUoeeJHOxg<<2j5?%BC>=O;UN?0jJ7bvv*Aq|XP>Mx3q( zW&f9WA?Ye)aV&KO?qWGbPIvwQ$D;nNqb2-ze`^>K$1+;#OKqux9*&iPGDJ?0z8n0bW3-VL{+JkT zrKUePMtd^c+aIHS>Fm88qeGJHJr|?H($Tv%M%R$`-l`ZKk(pkL7@Z>h?5-GH%d|01 z#^~D8+ngPv>q>2NT#T+Ksb)@$ZXkEceF2S543erCZD{M17;UAqbdAv-lt{iTBxRW_ zrH@LW%4G$VVzxk9y7yem$z?Zc%)QD)9zAQu^cI-$p~6hf?UdJ z;W%2hn4h+LnJgft3g0s6C*AoiBgInU78ANmGJtaOWjIOBCw{C9l_Hr$zH;Ky@Tr`t zpe91igIbx8agZXYwanF~Jf%5Fm%5%yBmDv>%{_v0(4Y2fv>G(5bY; zavZLGBdB#Kz1$z(4zK4w<MYa4L7*)v-G6d2 zj`WpsG!oYtAq|aK?leTDs#?_Xe`v*g%2!?1`6u3!-Cfw>z1;mDb#-E^^-HkK7-mKV zFHgEL_jj|yrGYI321o;MrZn)wlIquxRPS_Y?42*E_8e)ZplMzDb%*krZp_gRqU?VU z{3LR4C@>Kij{kGu3+Qu!Dw7f2YPv<)=N^oj49foixP$T!0?z=~OAEV)uuN&{jlrEI zO`ucl>C(hrCyi|`@ux{MZ!Te5i4RG>EtTf>4axP6AnX#t-p9QMw~J(YZQ=V%{7s}e zZE0jT5Z+I+?X|eQ37;sfy|-v{FX`ZAv6HOEkJg{4Ps?l){nWmPdp!`M%{St1kmlxP z%7R~;0Uz1$*@p0c@QZ9y&FR1=GQ^gU=N`$k$k;3=?K`+v;ntE&TSJ9{DTYa$ufXF@f4dc z#!iqldj|42QF81lGSc>t0(*jVw#Uj?znwHTdu1g0G2Paa>He{_sX{nQL^(f1PeKo} za7UP&C})v_q3SXV`Y`(QVSRWLeOOF;RQ@;M=0?AZ^vU zM}JQARcG`lhjz92pM2SV(!y*c&-?The3{LhC8GF`ri^za+4WVD^t9|d%)LT5>8QeWH~ z2>TWH6JQ8*5^hldVRSGKp^KsG1`v80cM)mOm!z4{DbRzZLsE)#@P7mDmJZ?C#4QH0 z0LDYo3Q3LZAgz`?B?fQcejJ1Ia21p{h3zq5O!%(?=xC@h&>cDhxQMc}Jb^2rIAeZo_GQ|HFxENhNJNh2*zIQJCcb59zd9-^W`d=!wfSREt=;t75 zW1l4KC~53}FL~j6CC~p8xD8k+d7*bCwMGi{+(Oy?SP#}fcLDCFzInJ8;J$);0QYTR z9rP4{w)*3NZG>Hk`yBm6yXdRb@VEHa0;l1J_mIknGQ)cSt*1Y73KOq-a07nEM))dp z7TJZ-DSrU6VSM^cfi&WgfzKH7>p;_wK4a6TF4aZ9HgFyJIs_0}L7JNbaN`R;6XWU` zX!=mcP*}@`ABFJkr0*mxU758ALs!v9CwqSe?yEsM|2)!lg!d-+M*(jE$Rx!6k#ZDz zGOjX_WhVOC%%3C8LM?GGi^09pEPNsK7Q)*=e~63B!`I?=CHxy))UL$E6UM7ud%#=pxL!;0YzP9EDU;P~F-z05A-%7TE*I8YWD_33{TP2GbOtUuq7eEGcPnn6*jW7@8Xb3f*qiZs ztF-oik+hI6Y2F&vsmEAX&%yt?G%y(l+p$hgVaL@E_j=%3-D9!;S#k&twlm!VU@e_p z4RjCXfV8@2io+Su4v2qA_g$=a&Fyc%NamlBa)d<|>}?vd$7y|NZ^k;-%|U9lu>W^D zU%HzDDfJdeO?$Sqw>zYlmn02jvy_@|A4=i2S;Pj^Niwj<<7`B9Rrjqdwz z_apMFB$*!3x0s_Uys#{weUsSx6!}($+K1UotYCi;l1}zn=CzuD7xO=yQA56BGfOPOIWjvizloMJPg2hHc~GaJe? zW)JDlliFS@&VUDHg5AjczmfdYoZG^dvv(+xc6MX*FMC!{zo`*Dh56R;!kpAZ!`W-K zB+XZvpS?p9_Ir&?mGmX9mmMTIUSG*!&+rcWxBBMY=pWXXGfkni#;svHvDeW3dpW#p zVgGhIb9TC9z-t4SX0X&VePlFaKhwM*L(Nz0@4t~Ol|B2`$?Q>vn%iZteT6;VB6!y z#}Oaz2kndMaElyePl)byeWCpk2l~SGnY!9;t@|IYYdiJ-(^cK{xPx=;)_f`(PyW0muN>FW5@G2+Ij_D6i2 z>R8e7THW0x>82=Jp}Of0g4dZbo&p})(GO|zlZ-c6G7uQ!#z($PlXcuJd?yoRU-W5H zDuc};8DyVkY{u&Tr<>?Y=wTA_>}5JhH=uzX6^(jrm~STV5aQVAe(gifwUfMh(ZB7l(uFbI$UYSP%zn;% z@fmZfj&bHz9m8HOcTbAk%C+&c5wve>djKNz7A#Y0cg68_^HtYcZyY z)R(uSpXmHbcnuA6LI~vZiHUBHPOzP4{HSHYcqLZbF=Qd+l z7xk~`a4_6Qn;o=v0XGcYfqQtUw2)_3JZlS+G`veB=`WEQ+_y(~CKKVATJrZIDLflX z*$iw0IH%Se25bO+=K%<7a4pu~+D8DNiqv@ppp3dhfU^P0sMj9I6RDpByeZOvGj0Q( z@ieRgz7T0d*^TQ1TSc1m0wx1_9=Pl~iU z8i4;JrUK83q@5ztnl`n*1c0A5%SGBY2jD-QcDG9h1^|-)WYBIca5Zor@G`K6XN&7a zI*bQy66r|#j^yoxzcV~^p3n10WYmRnyU@n2t3=Wre7x-4B2W}7Q z&7{uE=S8xP0$vo!hX3rlM0$<_wu|(ly}hpEv1AHBSvm04r!R0L@CbnX`cPkA>h4Q> z`jWoyEx^xgg=l-fB>-~jhwS=22D}1%48TLb10wxv0WE=UK(5GuGl9z9XNy2R|iJfc_LbBT{&d$PjoL!t>FgBY>#LF#2V<0nX() zApJDreUXtPfsG=g(2G$Ip_-Ii^oz*o=^|s0=a?%*#?qFtJ4D7!0FDNzb3D&(&;BTHQp8xFSs@lhFN>k>ANbikw2bPaP$)mNB=sLgch^k<%X(IfHa( zH5WO1u*f;HMb@E5=YA-1-d`dYghVc!D6*b0a&bgV3UHgqjqrZsK9QSth-^egxAX>n5V`dW zk=x*T(`h2NKPs}Bv2{l?;4P6mUlh6PM3K8QMec!zdv=Lzf%khCiQKnLm;5?BB z(UAuah&=3xY;7v?NK1gS9{EV*(bGj9gYU;@i9Ast@?-=+o==@1@^k~>F_CBJlWm2- z&mzxW1Kcn2+!B%J;pK%=kr(Oj?JY!JqTgQHE%Nf4BCk}5yh=T<;(v{Pd;KVpH}XVw zz{3v0-b9{n!T;MEMBYJH-@S;Z@8o$u6+mtu+{{z><1qSc=WieQocrR9 zoNX3yURo=LyGA3gvobAbwK+zNcRkN6&l3|mUQBqQn547C)c8tFWRsX=(xsd)rshg9 zwc3lRoesPyrp`_=b>9+GzqXhLW5hJ92Ye`|(feW=?-SE>sF-H+#iTwbruk)JTHG$C zc8j3lBv}ujSv~DJ*O@)}YuZc;&Mohbh#I#=_ro$*P9lML^G*wJz>gd9A?XCyJ zbc4r?{=oBMx?d-z$1vbsF`4&^$(ja`CVRJ-o@a{bl_REiO`xlooDcxdIjp>W-V@XJ zQZfB#bHAs=^uJonfM>zrc>qOE|0wyN`@IK{oF;nw^?=f|G zMa=Xc#LU2F8q|ze`UvsnU^nSei&FUX2Bc| zE*r!gQ&-Hwkzy7_fL*|TF%=()seD_^Vr0C8K3ht=mOUh9xdFZuvw}9Syh_ZfyTq)f z{57OIcAJ>vZWeR=`(jSa6mt^&ell%8nuo-bx?iI~&6h&de@pTV>JGrkaWRvVz7 zn6n!J-Nc*&59fR!X5Hgr&ZX@0koo!O!Ufj@@Ngk@t$#|)MU-`MshCU9hf8SprT2)r z>}gDTXy+BJ#av07uDnUiRmlJ9bb$W82A;27Bj&mc;8igjsQ(7!eB)vbp1uCNm^Wzu8xOJtYyrT>4*LDgvA|X_ zZ>0l_tG6igZFqhgU3q6PKz;9${@t?x`t99s#JoqI_fr6L_Jf(g3g7|Y7cn390g&T| z$m&CQ`|wc!o<2gZAC&^z#C&`taH*J2EN~8Rub5BEf$zn9mINFDS927q4fTLn=5*G0e+V!nZ=Z>aN|pT&HO9)62n zeR~JMJn|iBzS{sG)9>lW@4prELmF@k@S~U?k;#wqfg6E$#QY=xb^i1e@PU}0>Cd0( z2Zdki1MvJSX@0#3_(IHYwE@ccy*~hNf57J-R|DwNAIR%Zbo$S;f$zlZ?+Ba*knXSM zz(U|wfd2XG4>5l?1K|DdyTlwoRtL@%bCCW&h;AI*0qn!3i2jPE0$Bj_OY~B)(jLeI zrT`Vd>Ay?VdhQ@L` zY5l&yc%U3O8Ms1hs1o>HY?v~*b++Mwz*KC1ds!a1?B@M0+#`IiLG4^xDR+4*aQ46woXH!D^Li0B)0A~!2Q50z~{gLvGp1O z-2lp}_mkNA$v}JHB;Xz3JFyMIKoJ114c->puo*A|xEP>c8u>sApeJxT5Ea|F3D6xF z1{4D;f%Ab|fG5N@p$$!`yD4=y9Rko_%~}GzfYCr1aJ<;mEC89MBD3bmtob|unKb`R zYzz9Y1^w58{%dg$@FK7i_)~04 z9WrW%jM^cicF3|_DR3Ns4BKr6wgL1}yI;h%uL-0BgMgX9a^O5*BY>{8{}Hpt#sKwo zKvz0E55P-DWYP(lcA}0>=xHb9)OjF)E_HrfY!}MyLOJYxY}a#vCjsi~Mjvz=0no;7 zRt;N04xA#PxrlIdo%zB14jbrXpg7FX0`{$0kki3yVxw`nuScW zkY^UM%SMLT9f6yH$AGtiZ^iZu0rY>*UcesUZ?U}^0`SuW3Pq4XVpUG8%49A+z zD;!!hNqUr)t;mu#;1a>&lUTuOk$NRm&1aww#c^RF;gUHk7D+~RJZIs`Mbf@HrZ6pu zojaKMQv6!yMSO2C9B65VyBFhiPTB}gmzr{Z(#2kVucpU?CcEGMl5})Zsr}CGwY!oo zwjbGdl1g!3x7+PD`?!6`ZgKIO?MAyH{8RXw@E7(hrXWCQjap9Zn z3cEPGKD^EzZA-&z>?}LYP6*GjMPY8R?4WR=?HBH2dt#5-5i`?N+rl;u*RXX%f7|5H z521a)9)9nIc7$HGzBy?A2;FCX3f&sIA#^p1&KKr0^FinY^Okuvv@o>LybzjUo;Ht$ zhMNb>-DXp$hq=jIYc4nIL-qO9H0#Xi<|MPmRQY@Ty`};S)j4LSnc_e0KW@gEk^V+g zX!87v{qy|uOdtO^e}&01UHwwi-lTCpD2TG7F!Q6VlV+G={}kzq^kxgY2JUIu+u4_~ z4m9?O==0#$T=?nH{iIy)QZCjI?*%LN=x>>(GtuaP3hHlc~`oWCpp|macGjG^IToKmF7-Sxq8lp)YM#|IgWlM`ZeXW zaOLN_kSmlo^K!Hc;eWc)iWGbERAao$96ys?`sI$Fa?QnasF%Ps4{k@R{y6v~befjH zQAKg*sB+HpM{u6YyUf)!NkjbMT7Hce6^GL`Hk_lep<)g3vy}EZ5ySsY8cWH?LJx?Z z2t75r0o*FeDs6J3&p3?a%?VNVVWeCK9?(Lp$GGul`cwRI{z$*j&-45ES$DnYd)^MetM{_^ocE-+)w|ET)4SEX!MobK)VqM++1^_3 z1aFnM)LZDy^JaT9yh+{|Z@4$u8{qZ!dU&0^bgz}ywE8#=#;A_a>Z6kp>c*yy%J^8+ zk*K3E7=yM*$Di#NAA36TY+aiij5706d~}%)baa^)bX*0aDn6zTjiy5*Nk@^6A=5`k zPHf!tKlna!ou$~m7Cjm|Pige|AXU% z`x@#kbaaW6?R;17xlRtFTwTXFITUM+=9+_Vg3}MOmo`<7wo1#l2TKUK!`0jPAUkn$ zpZ1+;?dY_F+MCxqZvS?z$aW={YnfP4XnSuwxE?%0)A;$0pX)Sbji;0jr)v6eH%%X! zsv-V{D4h7*1%V&Xlvo=o-O-i#p!PoYF@)H<4mWi8>gdIA)g*cZ;XR{mp?9ck&FE-8 zxM4H{oE8pi&lRF4jrU_DS^7|ol(wJrg^j<^kU`Ee5N&; zx1Ho?sXR?PZG{=6@??ckd9Ky=nhsIg&QoF1%lXVn9hsdnuvzZGbKzdhT?3^MyZqri z-5AZ2j!WbUxk|2+4RXEQARA?q+=V6k{hU9x%1+tE`C~6nntqa>d2aKkvBooD)6}G! zE{xJF)6eubxx7&_iuX3gn6YNOnP4WG$!3a~W~Q4NylFDil$g0@u~}kPm=jq;Pd2BR zQ_X3tr)Qe8%?0KnbFsO^T*GR6ow=3wDz=#WnJpe<&UnIXGcTI$=4IxQkIWwPrTNAD zVgBZw70>!MWW&rkDYllaZyVVrR(Aj`Z7VjlX|}a(&pR@mY-iiWc4LRo-DcX}c7V;b z`F1$-*LXY0PGvtaUBXC-8_0hFCdv&a`yV4sr6p8i=i{&Pd!P-|q^5b%Z{;`j>-#nR zFi=xBTl~Vo&)ZLIeeV~->-(*6TM-we#Sg6z2J{z{l~bfnDB5-Y&nU_m1~Eu-)5+|8Wvh+bjntv#Y^Dtl0a~K|v^)*}h%jxn z{*>MTR_Us57ygatkx<4qLN>xvz@(3X2Wn?d3VQ@uz5rzm7ev@t?L&G55cX-G~WY!;@$e zz7v~m6W+s{btW|5vda1U?URJ4ZHWozTkd&6nHs{r=OxM<%C6XWb*%0h53qYT_6GYR z_&xgq;m2vLU5IUwWsXUgvCJTsFr#*rohBp`cpGnvtTgwT1Eww4LNj=Nucs}YiF9SD z&E3$Kb`N-;!=Eb_&mP4NteELSXLinD;dz`6Oz3a+lK1*2`D^?tzrtVO&-uUESF+>O zou=+H-}4)?$5fy0GWW#yqTZ9Su&uG3=zabI?@rR*nh4*+PBgw3J)6Dgf4dv)?DnI3 z;wQ&{u_N8)y{uMi^a`_c*ja%Z|Z*qi=`-Kp+RtM5?VxsP3J;>>5N*r!%_ zosI4suh+g{O;PMSywu^14qvA@}p3}CTa`;-s zaxb%85_408yP_24rCQ8ob)>G;!*-_ubKC#ijpqGhKRQ+>$qcM{W@6P-B1g;O|F}ba zLY|f9)hjZ@~RL5C0c`*gK;_#uJZgsSu}!BvZpgczZg<)HJnBZBxh8HT6t=)4((| zjZ9%K2KIgE)oNLbGUH1#jg}jR&oP;hlmvQfSg}KsPWv-5$ zi#C|+%?;*8?oVzu8_g~5oV3Z@ZZ?}c%$?>gZY=NN+;lJdiQw$?ka^f_HIJ~WJ!T%~ z9QCAmid&dxI8Qxmo-@yLC!^=8mpECy!VS%9=5_Oi*}8F$iBnsWtzjeX>{gT0TWwp16}ld$xCYGCjkrr{!h8SCIL|dd>_peb zwzcWZJ9@I~7(3f_jh*g#u!>~aY<6b7m^*W9AKRDvss8T7muClZTa|AI+X7o?hj4c_ zj5FW}-svC3eql6k_mAZr|M7N$oye(hvYir~4DAeiB)aZ)Ty{2d^c*{v8?Z7v?=bye zWGieXw_{7}Qoa^YRc(x}vB%ou?D6&ld!jwbp3I&0sdlYB&7N-0;D-Awz5{WNU1!hb zj_rJVfxXbKw-<5ac8R^zUS==1SFq2y%3f`+vDez`>;`rnH`p8PP4;H)&2O={+S}|V zdpmogJM5kIE_=7V$8NFr+WYMN_5u4KJEn*2R{IFwb$E;w=n4Czeab#v^8%@&_7q6?=&CBq*bHADCWqH|NPp=oZojG0~udmn7>(AY1 zu9xQx^agqP+=Lc*h29Wvs5gw2Z-h6}8|4*wqq!v=>y7iqdlS5g+?h`Hrg&4mY2I{h zP>=LxdPjM)ykhQCOT0PWT(8tC<92nvx4u62=D;Z=Hzy(QedF7v9q<=zT! zCHJtay*1vk-f`aX+{&Klo#dVDo#LI!9qnn}>E0RMnci9KkM>O9{wc-{ceKJ&1slww&9Bk>C%qp$sM@y>BOB+7fu`95;s3tk}W;C|LH9` z(uZ51e$rnCNG{Kw2eMk^%U~&R_d-Ku7WTcFW-4KoCUET568c$?pp3HOVsjN8D z6KnJ=DVEt%;$18^%33*19+OMCF*;q&lXK)6xlL~JE|IfYnNQ{0C3=f=u6L=NDbL6! z*!5q_ZPTyZEB(gJ(;f06>-e2Ahj)!HU~PLz%2?I6%WJIcuka-NE14&6vc|u`{nUK< zK`xYcz96^;&?eu z9^$s^M4qOdBqz%^+;465F7qz;uHcM$752~v0QSe`FdkG-`wckJx62>$CnwN5xQoBbyW6{mbLqX@%-`=l;5{h&V4+zJMJpzR6iraF zNXeoiqKDwk*K}i*m>eNwZb?Py(h{ir1T{9G$h9b6t5`Hb$q`z~qRC25c6h~vFf?^& z(264GDJs%LLtIvueyk%WCu?X>tdg-BNINQ@1fzy)HN%t~HeB(TVH!}NWPyttspQBY zZo1NwCgoG%q)9HQuuwgPg+crn%{$2@a(N4tB#uxQHBSBGoPV&AgB>|qc6g+A;#4w0q!$D();VXbB(BP2}BVF)xs7aG3ns!4L7D7@ax1JeOjA=Qd0L`>9DCQihspq?V_QEecOwvV7utEiCHK@L+iQvI}r zF3qo4zHCLw(vn&*?KEmmc}3}R)M`rkvXZ6qOEk%flKD$Z7L?S`z@;UNN)X7H<;PT& zP{E9{rInO5wxSF?X=No*lNKy3D`WyR5zl(w`^A}H|JEUm1nDl45^zI5(FWots&;srW>R+lX-S+;;?Pr#{K zTB21FJ*s$uQ;^}fc!vevVd~XptDoS4z*`VYH8SvyjHj9&_$N)mMQZeV5Kvec1km?E zz?d<(l&1+*bg}X@8D5u)*jS=*LFl+xqQQZ8aLhXzFKM-EoT9*)5~P_Jt8U`NAUNj4 zLW?vp)s0$(3%e*v5HKv}j*La1F+|Xr2#GpIksl8t!J`twQ2`N#eDJ7v@E{#LDjZD! zgE%T0ZLqc&uL?#RtS#0=w3v*f(WF5sq|k(cCx}NN1gShoj5zS9H1Hq{FjawR9T-Uv zt%6Vo0T348AV`HkjLJYop)JL$62MUb5bD~miHb)#Ijt&Y-q6&=OoT4HAaF(oq0DtI zcFdR{IPk72p5$^DPb!`sCjz+>G9{KXR-T(5M-|8Nj|-B=^k7smbD?(EDkV^9M+6M5 zDjpnEIog%Cs(5tZogO$vfiop2Zwh=kKAADK7^V)=r^G2@;dG7=SC^&;iXEw)O7SDLOI>Mh z>BbxQoKlVq6m4X{kQS;rw1-@dpy*gDnE_lYaa}ShDo|$Bb;6jdsnK>#gBN!KVPoJKIMz&kB>gv$@*^5eSvI^_VGUrCo=374OFD5&2NE)i5v zq$A=%s1fZZ$Tbi*Xy6!%K@O9G`2r`#A4IS)Kul5oG%DDUK)HTUmqaeNn=cSWJa=rG zAjB#2q(C)sNZ>NKiDHr)cPbYMx9fq3WpG1lQt{|`hCpm_^_fI+S5A!kiB7rwSodYxl5XA_S+Qw9e*isK}%Zov59 zps9n=R+lS~Ki=4I9UQcFa8N%^-0$>uu(IQdce+F-*Q9voX(2Iov;kT{tlH55Tcd;7 zcXYtS=s*xS=m({a4u%j8g&<2MoZ>*ZW~fUP0(X>CKSw#07#+;wF?AUo%mSlnHhheZ zF^(J5Vq|L{b_=EPR-jRuuFJj_bstn7XO!^sh+gT-u{< zET7W|61ZlmOTn(Bc)~y;MS(`*#PYdbD^hW|)J{QTT~riw8BV+`H#Zj*PtodKa@Y2v zVwZVJ(01KD5jQ1})|5a=rjUeYvfXq$MRi@vAl#Ctu3I}G0&Q0!mc?lVem9-rI*H*r zeZX}~6E2#hbxfoVI#gZSjFg~)5$y=FGeiQ?ZNmc6HO-O3b*mRpq&-B(%VKQ{3WzUj zq#YNlQS~mU5%~tpjE+UA76l=3H{g4sE_4V?+19KHt|6SJ5#r=KF;EKE7?<8nIe019 zWsFVC8WPJGXafN;ZI~F07G1bp32xd5_M^&LOerP?dl=pHx^iRsADaRv1|6$Qoh!qQ zTZXMGQim_0+E>nTs-QjPe2yy)7EY?%!(@~cRaVTe6P!Vd<0tRhhYsNxC5uKNYdxYW z+29{3HH-u6h!NHEQf|UaUiE5z^2W(Tbi*JlUU9%q-9rh3fM$mR5;=ny>RvP?5r)p; z)4Djk$EZXkjarm{IAeI@jK#=AYAtwd!lwm~O%xnZiAY!Q5#fv7>|IVFE!0=drb2X-rfE5#TsA&;CW?`EizlwUc& zvZCymq>{i*F5m{HWbWLuimK$f2{K$bw?wm+22LU6lvE`Ri4|NHD|kpyaG7(Gha_?* zmnF!gA+hSp0yj*5Yr$pCNgkF+m^?p0x`t$CWyRd=@QAANg{5WTa_1zCh;dOKb5#tP zSp~7wg-Ih~HI~O*7oXibh|kUm7nRIiUR4%e=$u&S;FwzwE(-Wt=p4TYru>DV@R%T1 zMYUW#dj+|A_70B;a#c7dX>5$M%IX~U91=@CwAR=K%PZ!WEM2~6Vaf8UT9ws(;R!)K zORLq>E5>`TobZI8o~3~^F-W_tdfHemy@m$0^^QrgcXoJEkf|zgCI>QI9>{cZoYTM! zPY!x^xpP93mzGz|4=q<*YjSmNYb~$tOPU;)c;Kc?)oZ=wi{>pXTa~i18a=%lz4{Ox zo)NTXjdPM`BorcfO@a(s)hE`vp|M^c8Yq8OW*--ymDxAu=EvNif3teU!UqTWvI_c! zr*e79;8+EvO60>fyROD=X8f7jEB_vQ={zmMqFx zwyZ~H^4!Wri%J}svbbz%d1YxlcwuFQBWo`!TU0)`GVZOlWVu!li>wnARa&_+NZcSO zLx(?24a%w)&yh%#Qd+*EytFJRe#!F6se|~q)Tm< z1nGZM=Erglc#d^fI%wI$lt+aGp838ZL#dFte)T3LZ0y!;2TpGkHs^VbyWd^B*|NbS9)a&NgX;RL4C{V?q*mMM>3i|(V;L24Pspqg- z`x|;&u@(#mX|=t@zGkgbwuQb3tqM&G_2XTw zWS;%znPmsIl&u#2ntGHhivUz+9JxkQ=u?V@Tv z-!44li~MGfspi{j4@ulaKCpbNAYMx3Ws6mB!uPnHU(I*7ofp$HtgTy9cT2tw5KC*( z89)BKno!@_=GI zHQ0sJX}PIoMa$BbGg^*o*-_G4tZ#l(^99XkHJ{XcWb-zuyy-~lqtT<|wOnSyjKn)c zd02{EQSF_f`xv1*rua%9xD}w|QYDoQ_W5pKT!-5*TlU4iMBh8wlz7KzZ}5H*HhlW7 z&=Tz7^i7~got4@FY{c}v9<`4Q-r*^#)`~IEh1eZ-q&GZf@7j!}W8pa!t+jk}lsE91 zf3WWp_bYY zv+}nyu$T$vD0BxKmI#&;8?l6`AYA{aN1GU5T8fT0pYal>mN_cko+bF!Gb)H>-01%r zTAgTXg^OF{;wt|U#}^yOr#-luv2l351ukyBi_*OnOadTsF%HM1afzt@9 zV3Wh_j@DO`7qVzIoc5_q_{1IQYXO0FxG_Ur!B1!3RQGO`O54Y>D329V`;pYV9Hm+m z*C9hX7vA!lM0ggIVGRz#-%5mMy71<)aNgU9mBU+G{8AmSO+)KKrJ=6=AO3Ux*?uW+ z-!{g&=5ntqc5$WHF5ZpZ=1`N0ozdfRIhI7dgVI7b99CL=tcw1UgYK<3AKTX6Sc#Tm z%Xz2y7z@V+STp9?v9X*w%LMJ|4i%Aauv5JP8_%1uRw4N9cTfU1E7Y#P?2 z>#zXbjFsk(u~eyvRNbIz%Wmvt7h!?A2J6wQu@wErcExf$8%xN7AcwyIJU!66uTJavGcB)v7*1#%sU@S++ zL=L4=ou&64`sc^kyNP^%602gh1nz^q?+|CuV*be78d*MRXaJBY5j#YgVmbUj`Kf41<*ge?4euvHLPuRPvwd(5k7pwHVwb;O>m1=h^1O**>*wOk$$vqm1xUSTSB#^+!Ud=Zwv zcR8Ef51h^I&*snAn~rKNn}!u^J9Zx9)KWE8OYKB0?V#GD>`u-CJJ(F&9nW%S4XxJCCop@j$AWod%ue}kERi3>2Kg!Mi9dIC!@u(0 zOcN}8bynARG6PH839-5k>3>`5YK+BqXJ^Yjop(u(#=`pim`(KK*ekz{rSd1{OWybF zi-qs-pagR;A&)jt4cLFpaO=uS_onIn<{>PjpEQ49U#x42+6-eW9LxK3B5!M`uu<#o zC9Fhm+1LzVHY8G{L8V}EIRcBvYve)RTKx-)W%tHuth7V@&l|$5j#=19-pd=Xr@A*% zr^Zr^NtCGTOoN!+W`EY90^ZYAd(2Vn8LyRF`EvC=yqWtjJIn`Td9_!9o@ohHoB6XR z_MHQ;=^QDSVS#rC){+mIzj@oGLy&`AXgo&?s5+co)K;`NmY|noyLT1#dUwiREZon+ z8hTnRkM3!LT6CXMkCiSP8`6H*jSgllzeH}r&hvJx7q{q&ZcgOATBrY5UdHq3d=+7J z?aRu~JI3ZDX9=(WRZEB|N~CKB)x6qXLw6cQyr(?2+J57{fBClYDdg0XLt@RX)l|CF zOi$UDGA^ZeicS6_c}?={*zTh*7@t*pg!#LDceURvvW^%ET)qDqO#R7b~Lzv;h591A8AP1UcWuTp^@yuJ~vy;I& zb`m&?ozOOU6bsYMXwtRl;d)tz1@TG9yNdnN0&Ie3vO5@uP8YIA=)?EIN0IL~TL`|% z=7KL_&P4`x0(g)e4<2awA`i8V0S{m%qjk0j+}Dl-_pyBYhx&(ud)cAjo>nEBZS%ny ztQD3o7!L%u<4XWm(%m{vJKZ`x8&_A$V(QZMwHSXM?l@%AfU$Zp{IA8zcm?`$G`7sM zu$Z2J9uFnHA0sqFI@(^8dYe^^*}xZ%tz2h2gD+twvV8xXFDp^DlfKP?9>DygysHlM z;YI_gS-v#ISJu0LGx&zG^4JF+l*eq`S-3rLkHpQuok4l}-{)q~S2h1B&$u>OzB(ks zZAWlEGlufe9z2lmGOH}p!2_)7b`BqeQfajYXV^4wXL|&=ljVCx%nu=;75!RYYT6cr z6x!zCK~{I*18p;Ku5Aj=VYGk1xp+If<42i2?q;0*S2|!*$i2~8@F?2^Txc7E3v5I1 zAgg0xpsfea<%mER*;?SfR!2u4n+)z{^(5ZYCV{hU7@W!1=&kgyKDawy z?blZ7nYxSB5zyY~2xwNo5t^?h)E^B%aLc^8~x-UjzJ zZ-IN6H^JFv2e_+w4cx_i45!H2=st7~Wxatr70x5^86vLH($Ks@j(qbnc%XR+oNHbL z_vQZ&s8pT@=a^@~*=8F!!#oY{^6$rpd5ZV~^CWnXc>+ApJPyt^kAVl6N5Or~Bj7$} zD>%nI1kPr~Mw83~;0*IH+%jh%q2cUR`><>6Xwpnm-h->bf#(l)wR>5o-(y_7jHUgP zyf=OytMaY9C2=)-jSDzMuI1f@RlMJ@kp7y@n4ZMCaT{fCGPi(lGdF;5G1r1On#;jA znft+o<{of?xf?vl+y%}xcYp_&P2m3KR&Zal5!}bz1kPc%qrGqqxR<#S+|yhN&Ni2T zdzg#C8Tv&+>1@`6JDCf>=|=w(W+i2Bac+jW6PJ0(Y{pf6yWP3D=4RZvgx`p(y?DKI zbIoZkKzIPb|dTFzwKGp=)tVCy-g)~Z!`13 zm+JQ*47jIR0M0h^!0k*4 zIGyiHYQGIK`hShJ|MZ@rlJxqYjJ5w33p=nPvi5*S;X{co#p;#g7GduaY}fEftxXY#-~ zCKuex3;<`F{@@;_A2`GG0e3ao;4UT$oNjX9WGW@;`9xOksBtmsux5b4^F^0Mh}iGyZz!om2k(p3Keo?MYi;+JOg|bnrma z2ApeJg9n%+!2L}ta6i)moMUt+(AzWx_cBeuS*9_V6DGKWX$Wp_(%?#0C%pkut!s`u zhw@XMo6BvK+fi|&=JwG>SN(ib2Rz8s0_U0(@Botx&M`H>*(M3xj`aK31IPCtTRFdM zW+ZOl9CR`7ul=X>`A72K#+`-EtOi`b4L`N=))%#MS3=8~$ElmU6YA!Uf-%Uv&KTrA zMdkS&xEJ>&j6`O8c#gb{(TPa!h2t;X}iJgxbM*34$&GFg1Wn07zLm$4QkK1fiyCkevdOHl77sCN?7+X?Ee1f@HcAn%R@^+tkv zJwd&epk7T-uOuklvjnBQl%TdJs23B|3kmA^1od2kdNx6AOHj`wDBbM@bv%`zo=i|r zB&f#|)ME+i(FCRYouHJh3F_el^-zL(FhM<#pzcpl_a&%%6V#Rjbx(r2J3-x*pzcgi zcOG%sM9e)9(<1e6e`~{Sbzkt&57f?F> z0!qhUK?(RrQRBj=zA?@fT1!{sKzJUqI>j3n(3b0j1+Fpmh8Nl#ajS z0mE(FR{Hi(|Ng=nzt4QecO72lp8p}9P29u{^96i=?>KIR7jVZtiEm-Bf8cw5Z8(F} zW^LKeO7#Wr2k+nw?8iBKY+_Bklsk=6&>j6h-7|>Q*>zQ-{*?)8MS@zMpsEtovIMm> zK`lv8ixX63f~rVRixSkr1a(Y;x+p=FC#VGpYJP&6m!QfLRB3{mo1o?-sFDOVJ3$pE zs96c>s01}LK^>W(W+bTT32It`nwp@dB&f*=YEpuln4l&ksPPGEoTDr_ozX9h{jsd! z30-ja_>6f%E_Nvo#jGRrP)uI6JgL&2=NpzM9s5|Fckum?t?1AVJViL0nPMqVNM`U2 zmBHvwXPybvXO`H{s=SNS&31HW3;U@nnITSLPkc0YQo;PNm0ij4+~g@IdKQk;4^^Y} zhC3eqKsEaQYP6m+<7w`#M(epV9)3?X`tEAMqjhAy%HCoTW@$eg}(RwD1hi|Ax>lry7er+}SnrgJ3pW|ut3>~Mh ztVZjZIv##`HCoTu@$gHl(U-(%OW*yw^(LWL+;+zGYQ59AaBSW2aIL$))+g)wlvkh9 z8}2p~>zroLk3;3OA$4PC*W$B!DQEiJ)8|H2>A}#d^IfXtn1?>kMBj!}PEU5PY2?=& zV@Y{}qsAnt(Fv+3L5)gKBNNn!1T{QH)u2}W=0+FJo0;tJ^Jt0M7xB*~?4$4&X1A?& zU{bGjz1F666ECNOsh8BCLz4OBU`=z=!Ofdq*tE%9uxZ_|Ph5QfXgKtIo-=5~6 zBjA(&J>*?#zom_0lG=K08+2&gs9{n#+_p{oUOjWN!;Km?&d%zc)4NyC4sCn2Z{N0U z4qkli+qMZOwe8(IJ1a<^l+?IMb_4Iy+H0y7^gi43>Yh4tdbvN>=5+5hvWE3dB)dze zI-NS_wa)I;V^mT}oimOaca)brs=sO8_!Yi}Htj$aTkl$R>eic9KBFk5Mcr`c?7ZyS zk-9a#=4q4L_G#InTc2)I7K|yVTffhE_Vcwlz&qysXJCFP%q#Ez&-}Oc?_G^m;xqr7 zHq^1}4pb@pFB;MydJkXQ*~2M~UPw~WI>E<Z{o;D5f!WG}c`I!+RqP^TU==Y?g z?4-8(`@dvU`L!R749)afw;JED<|TF3H#L!e$9BJh+^jg;4Pe`xSF7&)ncmMsbDKA+ zU9(l&%Hh-2X8o&1bnBF!XvDDTYTm`gIUhdS!yrOSP;FH8-|2WvzjIWo_O!~nDIyFGFAF(yZ_ifN{_Nvl}O&dn) z_82g{U!;D$a(r(kOFivdzn$UCU1yQc?JC`1ID*Ik>p4`2|jk#x!X-W41re^vtPIw@c@= z^x7?QXZQXWj88if;iX2O_KRV?pK4}y)<8akM8CUPAtxy|OZ4uYlM`;s;L8cM%`r?8 zIyL0k?5rk%cAnVowo$di#yc{vQNO~TT~pKQq_-VC%d0u9ph35uR}L)ca`5A|#yso| z=~uLW;h)UcH0Y6=b$+TVqm(7SSan+fP4W&cs%5)Z=;9&g!BknuE+p5mLeTO95vUIiO zZEJW>NtR@J$$Rf9Z#(gb6K6UJNeFus2&0tQL1{}{N@;1!C{Sn_fdVc6mbUcIEM1f? zpb);&LaeLrJ?GvlT@5=9?e}~SPq3soy63#-J@5Fvi@!-5?7+Zp;e6NvBO$3tZ|<0a zD9*7d%L=NbM0swSN3Ae4X0}W=l@sOyGI6p}S6cB|yr;`xPbAut67Q*Y(2(u-Gqf9R z%A1v_W^ZevY#_W$?-DUUDTmqv5ikdW<_IkB?cMUm3ERCg@I#%3$RpsqT2Ze*A z-h>@4qJs?WaEnJ7x$mH$vIki<#A2l7Hk;)x) zl?xqpu|=lxRMT+xfijaFdVQ+lU^HQD?Q)JVDKHop5hjP1Wfn6pLfj^bJ6RVfZT?AO&CAcn$-7+^$~Pu}sYn?&>A>BE7M) zroD0X$c~BhEaEs4U-+TaYhZzJ0`!azEhUkikVzUHTX;P2H;dg}QOp!|4|SC6-E1jc zjk-}HYYC@>Rhm&xX({gAH(O!Zimn5M9A1d z8HO;J5iYU}l94)Sc8npWf%nL(eP9oBzXUDb-o;6*SPF{dIM`sw;&nSfl#!3$ zK0#AENb;7Q=RB-Et~z>f<=`>qnCARP_E7IScXSwaY9>)T*gHH2-;2KeoN8#ox*sh8 zzCA6-TW$wZ6BaX=pvYLmroD`~oH+(I@iFGSc)EXkdeV|ydt~Q$zf8uTu&+RuI(cP7 z&CcVaU1^1o!RJ54J{PzL&_S4MAc!KdBf;4-$l94ZZ*KgEWQJ~XyW3?%o;96GZ|^5x zogNxWGpcpAa(jtXo{=}$<4pi3j%{Us74YN4;J7%>N(!zqsNQ%T$bP76n4}cv_?Gv} z5=IjwZ4K_uGHbj+$^NRfj#2mdj?AYP=9eXBHuYB9vT`7QhhT@;Cj+Zcn*((s$Oz*N zY`amUT7T{+>a$VGBQqvhlBu$(svs|)VV|@#+Zau^k7%#4Rf?(TOcVIVs6Z+@=Et2@O)ZQ$sBtAVoQPKzG{}WphR|tbVTF}$Oi~>;df*Byblj zGMfsWi4bz)_g7NDNEPwwu3nni0sE7X-2NomboQTy6iTYq*4g6o3>vcu$w%(xQcUx=JnIS=#y@vc<;0@4Za&W|hD(EsRs6b|_P+++ zk)T=Jj^Oi!JFhx!wpT{zGhb&Ds9~(00-u6^ybG3-8TBRM`UPg*9y_pd;F#(n2du3m zsR%2J3b(CHUeePYR2Du#_P46B><5^FDv!H|`WwjpI1O*r1VxGC_Mv~jXHjkkb82?RFbyfAWoT_&=j>yTYpD4614j9sR9hMEzU5$xadu@L;`=bYn>rl3e##y-LD>WB>t?dWdMYqPQv)D6yjm{Y}_Ib_|w36?By0)mQy2Mg!NNoF@v z!aye^FtJ?5k`w-#N;-OAr#1gz)5t0%A9LrI(@BRI@`Df9pC^$HcM0R{=xTD?$LkW1 zsrNrNSAUyIRRiRRcb4h2%|4$uQJr5n((EiOF6^5& zVlxLLJ{XS!W<`h(LSuL_=pa94{-`4q2uC4Gt;M~Il=ArU;t_HOQD0rx2`Z_68(=>@ zJGJaFhV0HL&M;(@04aRCy0Y^n37P3Ft&FZ`a=c5MC`~GM)R*U3tLha@XX`E>`&zmo zy(nFuf$B8i)=;&uYD+Zzu=OJB00;)hWTHyIgf?4OYx@@HcxS=a8Dck+Q#URro1Lx8 zN~izl45dT#daT4T(jiYT?@k^X>Zk(b#6`?^5Jl+BlH*dStKf;5NRjbgpfaSH!;TG~ zoA}s*dJfLhXyG)ko4iZwsFJ7;J-C15vMxGlq&B~dQXgQb?JD+X?A@XP}CSLQTS@ftM@2+z52}AYr0EDs`P4azk-svY%2Y%&o`-3_Koy;mS-a}cGVz}#*J$86jcMKrqwXx9D>rRx0Yff+hu zC4(x=E2~e;Ae8C;MTR;byAn5dPp?gMr|9nZs!Ek#TJQSm7Z9zCfYdz*Pbmb9nB(sd zj0?yixP%)ZhZZr~Xrk^sc3`>aE(4XcG+5ihq%4%oke=!)ug6*GMF?U)Ac)6R)aCC# z0(fI_6UG~fUbLr+D=#6>qq2NrOVN!-fp~detLn}?(kpCQ1h5P zy7ASrK}TN2h*zVsd!4y`i_61A7II>ZkV=b&512p+@Kyy--XY>B9!v#blQ11)sQ$*D zU5W+8@WlgdyK8B~{g*Ks_K1pnNcEg~FY(i{Ar({IKGWTMxWPH=P0=5}WQWg(LaYWz zRYoH;k|8t}i)}fWB@B#lxruvqgA24`;?ka`UA464GuyT7PgUeERsNoajy(#hr@mva z0+>*_I+SAU+E-HnPc$xHr~v88-leMPm}pa}YdR;}V4Fy~pt&D_Bn#WzqEG=B1a=<3 zxfog0vF9`7F6I)`J;eW>Nr3pi4+}~G^c4+yaE2BHbPwrJ3&yeeC<1SHgKpx0ZL}iy zr`7cIaP#&=GT&sbkdob$e6hW;pHkhpzxQafL^XfiuvyK{smKRZ_hv4_r1JUnq_)vU zcak(aufUuXuVETGx@sBq_)gD`7DMLPk=pG(A4=jtBJELFnFcv3+~uSMrGs2Di6scB zP~S!_(D!VrgVTd^v~3?l-QfT52|HDkTT&N~7>~S~eRt}>c&AZEXy8!ZR48(at5HQD zdI&%^p+^XU5=ni*BS1VsQD7mpT|Kg2Nq4sClxa&B()xQZzCO+WF$2x;;St2mgT3kU zs`{a&`PAg&N8#QRNP|!_5CDP67+7tF)EcOMfyhRT#Ug(Tc4xr~4njC?Ej5E2yS$8x z2lrekr4whLNhS+&E4{=lS-ITY?V~lf9ko&;1D>TeIY@r+9AB|_tSw|_C7Ul(#+wTV zYa4df4YnkuFYHvQ>$>bS^^kH2eKm=ycR75CbMEEr zv=Mv%vP5^+;W`aFqav?YU2eXO_@C}p72~L>uVzo)XR>Y^)@SZNxajl22rZP3LKVXH ztGM$E?Ossopf53J@sW#Y<*wJl($bEstyuGqcMYvlNyGLc52aY`ZS70!ljd5B>uOx> zjI+6?$z3_$kZ4!_9v#K=&g$M4Mpf6IK*^h3YE9QfYi+h!uQuhjlob|_)Y|ilZ9We{ zq@cJv;(#MSkW|QYi{sb~odp&=oJR5Ed2CV=TV53&xY&z}%E30wNHMmHruiQq| zr}=MWs6M~HqR7#mKsrmUbymz7U(B<~Q$52a(_I?v^y1Av_DY-Arj!+wTWhV{3Sf@H zn-C|2wcr)-rnK1RfkPtSIJ&l@#JgjBn>23+Lq2_qeK(h`uBsVS;-^p@OJfP~|7;*GR|34-3)1jpkz>9u)!4q`jvtitG^mxfnh`Ji z3_+N4=7+P)ulszjr|T;!+fe2zI)enDKr*l;)DDa83c7+p<30|HWtq|Cra8Yk4;G%l z96PX>m9<=7JzYU3^&f8aj_9-f0fu;@v$>_>HB&r$_n^1A;x{H3)dZL1dqowt@#+*~ z+fI+8=wTnbS5?(p@N$7or>beqf2Gg?R0AB9x)j*97;9?|#|fGRKtmpd0WD>;a2&!R zF%B&u;t@A>DRb=LO2aKlnnP2)6O`)WiD4DpV0Fe*#0tsm8|mwiCf#CZpB}Ka#FHh~ zlA1)kKw_zq$>_-WDxO?yj@QnZ9(?yQMOBp6@cE>=S&)5HbIjuD`buac~ z$@MQ7wZfaYSXKtV9ocHao#A~d6FIkAssGgB90OfkP*RgXEt0B3lcTc|>yDurrINj# zfjZi&hYp^9UUFi7S*^pMo!v9lrBDA^Z|@Ki-7pfHLiPxD0)&8M#HHB+NQ2xv2mt>@ zZ}&*FH>?$?RY}d0NmO}RO|OC)Bc)|!)m=$cU5!kqPL?jNO0-wZw0FtKKR4LwqV5`5Q)Czom6R}OTksnRk>lYB{eY-w^ylKts{!wS+?P}{zPYS;tK zS75O_FDBoa>Pyk@-oFiOdwXl$($Sd#L+1T$Z3*f^+hkMM{@MhDsJ_XG7#L0SCV;!z zpt2A04E8OJQkOfATP6ADqif40S-#5g1uSZR_8$BDe;?~Hyq7|IJ@{r2IR!f>u(W;S zE4IRyuGSenjfn7pe^J-LQ?el`gcIcAQnxWS&e0vTV&*r-(s%PDDMY2lPm`8b)pak9#uMPMiZ%Or@c$H zpln&LpW96*wm7Ru`S#8=EeoFZo4)04b50_iucGaJg=S}Om5M3zILu|eWy^!fx|-=U zb6vcurg7N^r=X1+qS08uTqq1dSs`5T5bN4Ji*OvAybs1aIjJA=39J#uIb|_aH`7Vo z`@9{qNp#lXj*(q-QqMkb+mwdNo~zdRM^tp8%71O0vvx9(s;{UWmlMa~&4ipnAAzJh zs+w#{)i*7BI%;Y2(wN?^lpUSB_GWQGlb zuq%&*Ij8P;YgC-qg4YQb1^%P1LMTtqqji*GZJ=sEle$#Cjc#-nH0HE_;Y9A zE&O?aLI9K*T`crD)L`C^KmRs`{C6_~^YQOz$8Cep(*qXlXPF^0i~GDN@G^X!5&$y_ z|2}AW{QJX!xAFJ9&#wnA5dK^n_w_&^@Y}!uRyt;!+l8LH23cL427C>^r+~}65G*Uf zkOg}xC~<@43*?4`{3?L4;P8Xk1ze4Gbx95MNRmXKe*pRrVzgV@yq_Ro84AU8Ln5tKQV~oa24Fk4sXsn?^Ap zNzE~ADncX)kH=osFDEMCaLXSoi${K@JOkmq@vlA zpeb=SBiWW3_W~4*AoCE()X*Z)P2+(_6IpX^EYQ&co?kTLh`w?ODpGLLkrsW62;@WV zP{{h{Gf+6A_-$%usj8v@GUl2p2M#I56=RnVEFGZaBXFt8X*pzXtuPP+=wX!oBkmHV zq^M-BtNl=Of4fRk(-VRmK@g^~uLMy>jGuvxCdh06I79w62x$RP-xOygjFvp|W7QvY zg!0}qV2vv}_Na>N$|wS?u{fQ5a$JC=48H9KJiKL4+_b#|T8Gz1BT7;!9UD=v4z z`3q1ZFmLFvcxys*qXZz4qaHp&9$6Ga$bZ}bM`Vulw2j6@AN@y3+eRqj*vMeFAtvw$ z^ht|524nCLeQ*rd@zsq9Cz8Kq{GAMO zc!BsF^SO<{L_~^5mxsrrKty<=F`(e_L!>xD)j-1&#ICWepp5=s;N_*TZ}UhaQn8^E3fCp z=J3U*RF4UWBhETG%z=v&Jk$tMpao>`_26uw>{&r;oe^6kuUE`n({ubv^~`eyvLL4t zYO+bsj^@rWN^{$h9CEtHwXHdU*w%ZF_xO&o>dOIdDD3w(?yMbZ(drg<>f)O_i+T}K zlE%KsgNfMP32kEoND&<%`7J;VyEcOrPa%bL^l8GhL4#Yx-j~Zs{stlk zj1eHiD^&kXrI_L#jqg~`rlskQ4_2YadakV zaB{ZFQveOt4JVDNFInBbSDFKWVU>!#g?Ipgg9A0h(nY(cGP3?hfD5Qs;oHn8O)3Nv zij3Jaf`gJ>(TE`RJ%-JSKm@@(-vkMyVPgX@EfWslqFS$S z-Cg@T#?x2uYH^ubRohYbr?#RU4WB>%Pm z+Ew)|jHE&aU^K3WrOPOJ0e>AJUWrymaz+$L73^4|o5A~MAqNd~$SBj+etf8T!9^$a z9qaB`ET`FJ$bjo9l~N7frsX8G2zLJtugzDLs&8KMR8BaPwed`4Lvd>k!vLDs3}P0L zJm`{#knoV#7!G6c${WLXg41I8@@B~05TJW^?C*Lp60uVi+A(6s0eZj`sd4u~`34G$ zAblBBZdkfWf|u;iM6<%X+Oh3`1jR&}81{GyUFUIkLHYvwr}8oP?y2k)h>7$9LRZyL z0U2Egqk{q2Tm%g6&lvzZ;$-zlDz`yQ-hY`6%;xGryVUP|pswxD_)wXUQ=E1sN49YPWpi^Q{E)fHCKzM-W zU}E~akWU1Jhcwq9zlbB9F+e$)DK{LIBa~Y}~Ai}5h@#R@5S!G$NS=6r5tmG{C6Gw8ng|stHfzSmZyO1;P3_Ldq z$cWpaFdNkdg38uu44*l^hv_Fu>vW>*7U><59LBdXFI zWiVCQ4=`D?Q?jlo$uee_W~IQ%*MJu?Lqc-}@(w)MU=%p5jf8duLU+B<5 z6N03HAR}y=56ua1I)W{OH%I)BxVs|`J%yFY>Y>;QoEKm-a=+1R&|se0zd#L>)s=PK z@gs_(%fwu~G3C}v^Q)M0)BLPDRgzQW@FZxL7bKMGvgz?S*kv6uY}>J8b#3mXcgzy}eqc zZlCC=W>oEy_04u`G5Q0{J`OC?ME(geA^`zsqUqut7n6VTr@==g`b)S8=r3j5Uz>zq zA-aRVhkqAL{J_5;8uTDeeCP`az^O2s#?)Qv9^ zhl#R+@;WI|UF)hZ^&aRLo+HTzr@IuXt$Q`>Z;4d)E%L#~m4#)0uXC>ylon*uvaAec zQnI5^p)Ttz=d+v1@1ugzaAgsd33v5jrQC+MzaiJbdDr^w)O-j2U^aVh7HZk;U_6aO zPfsK?EznD?8mf~6O)7(0uR*Kbm%y|niA_EAYvdst?Fd%c_{ zs;W_Pzt<_(NF*C&_dk-Nd-OW#bhkdeWrv4L@ULhu)=QJh8zIMET~Q6)(t&uXfi)bi z%zy-|Hnce*)dDh;U?1E@1V{Okr1@*~=D!^vKKqF?{j)NDE&UX+IF)`@+5UmIaj+F5 z3}{CZ3n9uY3JYD}l>Yk=L%R+}_(FPk6Z!fOP!}pWCj&ilDC@X7>U z6)p|dSb~am0Pk*I3Lx)ofI)LN9JsT(;j9V*st47#o;dM&O`q#bWdVU-;<5mUiJ-(n z(d`12BM#5OTRm48fbz5qnJ*o{*;(vLAaJfE3p?I2Ga^M0hFZ>*yAe%^j|+8CimU2`o)FApQK9Q z@NKD|J#pd&iPHCfT)1rMwEFp^&H`~ZW8ko6%nKb z?x7xnRU<4J@f=z+ANfOuc&t(aXibAicGt1B<>a?_6RQcIPPu%(cG5;{C#oIw!--_2 zyP`w0V7zQ@Vuk)pxqpPAu0^Z5{AHra?OQGCER}<6&{vb7Evu=>>sy@aBIG!UFYpB% z6ht{AA_xF{1Gu{o@DjR9h!UPdr3LE!W4o4TrPm#}qp@ql`huBDl5f1?idzj!ZK4{3 zw7};`bZ+TfGHYz^j~N&w9z=x(%#qQ3NG#md)OUtrgYn~rFPu1Wn_)-W1_cL!cR`Y& zpr9v20wE+QzK$CTFdP^Zl^w_j8h4|$eZ%986j>V-AJhz~ZaHz{3))_1f-bU#i7P<} z9KwOYs?ZXV3x;GVX5g@}pm@;$Mj;u?Epel`pkj4+7h3S$?ZaCyM(9{j-FV`}?J4_u z^rDJ{^uX)5B7wu+5pTns6|rIjiHgeIc{B`FC2TKX|MwVizw3Ku^}*S}Y1+I`MSa@; z?P1z0cDq~eV1}aQvcD8hEYSV|3ilSJGv;RPDzN#0>@}~=iVAe-b?6i*a8pX+5{I8 z(4207EMX^qCBSA8PT&mn3A%iO{n^ET+_ryfH42u@-$}n5zaPpE&s3-IBn~a2^f1xY zjnHAD^AD?3&@!*A-Pbm@S*?OT;YYW|&-RMT6#_rPyag_#pE6A)J5Hf`(G1g8g9auRQ~2Q_~=2xh-HR6P_nR6rx5j|yHXw5b_S?<2Ud0Y@||fM;%na1KvD zw3=`$1~?{E+@Kf9UC34?LP169R_5Hb*-ZA;L&P_<_a~?iPxMSu%*DPjhHlAm#*<5w zV*f}_r!47y8+*@aMSDq38DHE0oHVu5wXmJ^ zddm3XhD=!Pg;@Ay;y~~Q%jM)hE^r947_|?x_)CX~yA1avsSi(fqxFsZve?h6sKIx? zlX(yO=2%%XlwIVM)+QnWdCxjAdAxD^zCP%LG&=hopYJ=v6~#)4rJ%kNd$1=dRPT@? zR7Qd!4cr})mAH%>=^=1kg5uwHE|}7(R`%@+i9bRJ9+f?CAyLZk!9z;R!%+6n)qM~G zRsMrdX6c@nepz;KHWLD)or5(#U(XL{7bBRVyDx&-Z=?zW58!pZ zi1YlDaqSW>#YJY#rkVcP`qW>oeQjV)qW$!ucSyTIuK$B++6S(X%nxQIFCDWNKNch1 zUmExbtg&|>`vKyooGL+nQ4ac_kX;Tr=qTwK>@EPAv;>GILAxMw8L4ZTqX(DM>rWpi ziL%Roc!YR+CzMG~IQt5z#HPlR6d@aBZ=~$i@nl(1Nt2Z9!Y<{Pa-L6kyZ0i+m%0}; z+P?YxmO>>{JyCvuASmL`><>s&Bb=bMygFYaYsIq!Kt`bw05p|XsP~PO5>Z6}x^(tm z2mjtbu~8jBvi7Z~q&r4pl>$6XJpoU%2z6!H5Ei762>nC&CbnYW?_?(G-kobxmcK3$ z<1#;+Oy%VbY9)Q_+vFbnV?6&RRuun6yV^XlK{)$AW1%HlKX)*Ecq?fm0HCE1s)kuqR(&-l~G6M z&yzdz8=#cMTj}UlP3te6sh_b+=#J94reO~uzaNzbND>NEwAGutdymoNYtpDI z>K*3xIr~_pKD~Z1!(1;576$~L#>D|efGT#O1qYdiceMY*5&^|j;{jv|elyn={~y!| zbni*|;)xSqRMqC6MX>-Ccm+_fdEf_xf}x;`OfV(>QydI6Clf$d^74&)T|{AG{(q?G zdd44xiLpO})L#kUJXOZh6E{rH~t^tWTKsmT4Q`d%*_8c zVcQ6=LTxh69z1(JxN9=-aS%ld>i*4eG>vF8sEx+vm|B^5ts7u#{>MYB0i;rZFUo-a z(YiDWzz`!>TLUxJe z)g3j~Or=3vIg*5v)0CAfrBIsi;eo#(>+Wub?q*QMY93UqLPHRCnPYkb$M$pmq1shO z>8Kg1W1k{qD#gdYkW2}7p0=WDpq70Rvw6A!>*HVIuzC@MSw%Noa29Y?2nb=$ZU~xx zG4SmmwjHbs!MFFDojx(5jVeQ~99D)9ZG2(Hl_40$mQ44EU~FrjQv_qKLE!$HSjzx9f4LgS}{_#BsG7Z-%Q_O*lWYyI_Fs?l@gQ8pSCv4I0+jv<(H}5O!l){Su0@q4iA|0d z58RQoVR1>rs^y8RqsvQpuo^^A=s+Vfnvj*s_o;~mpGBbAo7b3ZgkPVz&SWF#8X{5Y zfmg|OV186;@3%0<7bTlUx534NbSPO5$}b3;nJkL+}TYz_kC*#FtiC zG#Ue;(`neC0c`P;RiMC1xaQbG70PE6@16mjCjX{l4-wT`Xo@Oe&YRVu_@U-*&$Eas zfqP&m5u#5H=PaR`6eB=4$O^&kcnoA391jWkHsh~@{zxnIUCh@uf~53>#T%;oLijatg@9h)Lp-QwUk0v^!mim&;=&{BzmatT`9SDEaiRf| zN^t84i&NBwoV-~DeN%`ppBK;Wxsv$4qQlj`8zAh_OZw1#dU0ix{f>l~F0hSux9>wk zLs|mg#T758X`l#(IU`{0Ag}P2u@7HDymQXQn-;!I5V_kd?@PZypS!q~;ukB!Fb!AG-IXRd{L z#ryj0&-cM}({vVX&N+IzT01~?F@bpyg#9GGD^k1Sei(;%^0cUKOfk!r*4G z=c5W{2GgL8JKs7dCd`az{w!ZJlL{al>Yx~`nF*Rr2+RS8!+?Wo2yW(5wB3L-i|S?) zs!Cqob%$mPr8AY=%#UBWVet$V2T(J4JAi^YP=8+$E?+hRnHhlbg>%9TaXa&qO|a$N z035X8$v?F|Itqfy(S*V(&Jv;i)*lmdO`DDj5Kt%R|cpLjb3dia;-u3_;#u zf{^%QCaR_h2b9ULFzn4CH2G`98yfa5^6Kvli{WI}FTly{KH~Ci?CY38Pm-v-20~mS z1{H}k;8!s=D3sX9TixvMFC^aBf2jyljx4ijT($g4dK-1lVpypSNMZIM-`A!cMv}wM zd63N#5kUcg5>jF4@&tSHW0nt%BtY&0{IU_%D17oZH|*UPA!zP#A1>HA+m<)}bMJ#( zv!}6UOAS27m&~Mb;di8Rc=VZf&^#7a zI|F*@VO%idn9P3iT0*Fy`8ce6hKvJboXmhrkcyjA0Qil>bPQY^)+4~MMk5m7q_(N~ z8ufZ48eTxo^7yv`f#C!@Q9d{whJhEf7R77mAm|;BFmUYRzlTd{bhs0+i9k^b6u<}x z!Z=<)=E=xu_9~)TTS1dF`D(A zn}HZOxBnWVeC>zm(d+_`bwiK@88m`MK3lY#E4TS1i1&+Nje&aFI_kaN3p@)YH$3u9 zfhkc5Gpl$B&WB7kM!y|&h;y)hi|ma%7|K$Vk+vBg78B#%7#s6{q@sQsfsL{M45E|J(D|%D zGPek`1S~sFiOSDtKuY+(JYKXl#Hrw?X7-8RmAv zVG66>Ao|2R6C$%VSY9r=rzR8kJx0zZkc3$bpvQ6|Kaf;!9G~KQu@h9Kt*$3#Z+7WB zjQ<7yg{JgTPiecsJZ^`c?S3k(HPWBL-fi5vefzL3Q_a|_`)Z;(x&H|Ia+*w5d(nxo zj_%U1caZH4Ev#fl5m7IN@rD222rCvL*`m8Bso4U-J*D10p5O!9#m(@g?;?U3C&NhmmiXOzBd(Pz%O+Xj#hH&lXr>RHV})k=j}_R?d<5*Tp)8LtY~y~y3>WBh zC4C(Zr5x@i;`PIDBsl8^7&;F_ec%WMEnDQJ^PC>yYSfkupfAMPIS`UZwi0g~iL!R0 zSe*{<)GvKLqEUa+CTxA*de>n5lvcxzm@`H_RKf?gY#k`JstSx~k!X3PJZ!uWex-2WdpXqDUzWG#Sl+FlV!Id=){Na+S0&kz|=f~Md| z!MiBZNj@KHMxWp;yuM{b>54I#L2~ga{}-*9v-N}rdeuYszGi3JveI86CJIW3fS&cE zo$_>hZ%R)!`&`7Bh8OZniQ%pqg|gfYo$SqFdj`J{1f}#c*k>tBY#8D88J6EE$g`M? z1GcUpRf8-838BKj!CVCUmmcE!$g3}$9-5#?f^yj!`sD5yJC;tJT7Sx%G2!VOqe*D~ zT-JcP&965^?wCMb=SQ10+C+`2%rjcYJ`=I)#|v#!t;t$tqPDOMdeVO{#lQzFKU>#VjsB{`7j37Eh?VFZHb#14lgr}^wH>R+cd>(ajXCLX*l^$zn zH2zTe=qPO+GMB(yo$*-q?pdzJ7xx`6F}sn1{GUXeQ)8 zAl@(HZlRNTqOQa-RvUTJKTf{<>HM-iLW6T#_+C**k2v}^*lQK=HYG4C+Gw5|`ws%I zLaBN1%A)jEe9sYJh7*;@dx|sX>IS#dx@)!6 z-&5(k6M!~^YF#Dy11uHI;TVM3(O8bhT9vTom?tFMN$>%qIg%7B%3ntHcb6neDvD~l zl44y=$-^J`cbL;g-Jb5m(TlEMe^Y!;uLPA}L|gc*6n(10IUIFtzLWW-E=N`Py$tC$ zL=CXuP`^SiOfa5?DeNJwhK@#*#T7cOP3c0zP34swBncu87N;&oF|q98VEbk=-tK8z zRNyZ7=EZX*vIN>*Tpe40$yWS2TVt<6zWy@swaeA1>{%s`iCR9N?iAAv@i%S&otT`S z!?(CvoH1tSq-eFp$?8H~q-Cl-Li;i_FhQoKDhe6~ViHD{^WN!? zK-X2ddgLSL-r2Sd+D~65?mWHT8c8R<4Rx--DyTPFQ4XUlagWBhVFl%tWzK3_0sBf= zzv^(ahc^M-0NT|-Ym9jyV6db!bkSl;8blrp*jPj}9Ke-@jyO=O-;CJS?nFkr)+IZ@5N5Jn@E z$M%Y&{orDHa@Cq3w_1oP{b~|1LqA zBZi3}B1e!nM4yE$>oeO3ls~#L96?r=)%3@<5e}je6zp^D`oguQI*tEfbB52&z9c3> zjQJE&jm%a(;$feNxK@u%GS$hb!SvIxM~APMtKXDr*&A*Y^UrigEnlDNUFP{f+i zCw)P+o@+InGN=38#4my!1|wa;O(u{mjpvmNdDzDyB*0Gfxri(H!6XA=#NxUpVPcu% zPT?|)P}B0O@aK748*_*#o=?9H{f#DkSp0u8r%!mu&%o@I^@XsDd1MIu(_TNpiN&oIYMfHj6C%^(Vrp!&&<0EEx>>PNLt#{xJL|K3Q2V@2Ias1lioK zm;M;GJ_Lj45zzMuV%eK>kBk-wgqZNM|LW&g>;fej4-=Xo7N=*KGg--TR(@6^y#7;s&jCzB1A$@IM?*9{)k?6Qml| zOnFrd`3}O}YRb(&lM{gDj_d)Tg$-XRj=KaHId`i#GB2&eE}2xO=fw>)+yL%!IbCA! zJ=&Z48i0kCoc*mG%V|0LJGUR839_QBZZM`fEW7&jwYmAby?xu6`VNJ1{Q z)~NQ)P{ZBMnYsl3{nH8p?8#e^%++5NbvZlo ziaM&}J6e0u{hXa$q$!Q6rloYSf_QeKmZXxCO1wjjXc$n0bA>>ie@hp@xvB)O`+F6Ybh<^<`;vP0&YU~hzYLY5 z@>)FA4B6pJ{7|!+(k;!XdBGPpqh{|5s0FeVPal(@-k!1rpiUQ<1M_kFYk*N0A@Hd1>?(d zh$nN|-!k+|9~~EwkX(PXV@xS$zf10aSgGAViRPm@r@aaOf}wIWD=r7kiUX=XWve>8 zXf!m#khUgyYQt7YTilrTVmNIVn}+K8SLP+S+SoPeG@wK^D>q*b@nJ8|hqDn>J`2iX zA_90EL0pVE3gXZ$@d6pqgt*=3N!bs#M7*au<`nWrROAm&e?~+->i9!)^W*f0FjDTR zBKxkn%&aZ&9T2B+pdp+*dmElplRABU8_b}i5bH@2fFP}4b1VQs!oVR2bZve569(Un zz;c)AeJ4K_}kT@l`(y-tBFX=MqO&2(n9YJe7 zq*eHswWA5@lFA_$`)If|_2af0sI-2zz_te_uq{Q3aOg5Edk!ek15{Xu zI}}zMAP%aXHEnnhEkqxhb8uqPNFEh(lStTw@MhMVkWBU^6oXrTE}U#P-7p2SmTfSU z?w$2rV%kN=9ovxvyEN`~cl^MbwY&`&9sQCFQ> zUk)R(s6r)tFnmGLmWciR_O_a#PQw{m1_!rY2IOVUwFn^0q?6 zu)E(C9=F3;DDTOB1{ACV8)YyQuM^V3&YmyB35Vn%IPM|xN5Md{$3hTe`)C5&vvqF# z+UbcfP9%N}JoqY=OB`bb2=cTvii8{l+10p3;&J@92e?o5(o6bS&?HLb`B!2Q5OD`c z&*P9=^<|`Q&n{a%57~idPi>^3E0mhW$p>g!h5TapfTJ{qVS*+&0h}K) z$GP9*qD)ctlQkkO>n1KO%dS=zUDP|bCaW8a8eAwn_v7#NrErLms6agF(7#qH%t7pG zGF-44%*rC2N0%4qAv`HD$JmsgqOTe&&o!~@4V4KpWhu-=WMDob8IhqCSeJYU=$sFk zOT#fmc5FIBR5FZ~A(A^BKuy6+!``{%b}+fxuYr&*)zPMBQfJoUYlc(Otl@(nC6}FA ze_0@yvp2;_tFD}Hn_z}6zuy0MVGblB5-)nSn(Bt3;ek9cZG1sulv+!A(5T4DiYE4V z!c0iCe+-G1;60m(cpegM3;Y3Fq3hB@phYgG1+vo-Qh+Bi;bWlehNvWQZ;__mXc_Ui!b2S}hS=sEXVruP`=G*G*%J}?pZ#kbP=>iIw zKt(lzisGF+A=fQ7t3+H$oahzg9Z1P`U_2{W%3^?|62WEwktxmP&~^2G9~9O$%taF? z>2$V0lJ@B&&%lV;KP3>0HhshV4gW4Nfxgc_u0R%!*@OWHu{kU9=7oe%?4%QGq+q%h+T~z1J)V8b`({j!Y8$~Nw zdvE~_?PX%&)9CD!7e zWROj{mg2JV0`}!FZ#Y-XV7#zGL}xRqXIGMObkl+4~dA#J}Zi{iouCq ztC)}?Y1fFLi6AjaC-~IA788;EeK5$1lR_nVKsFer$SKNT>MK|0gGiw@#rzP`o0GxQ zIB-YQ?B!dcK6!$nS#+afOk|ERq8WD%pnnU?Dn&D>(87Yb`T%8fCGPG}macfIsMih` zPe5sI!FUV(^P>lLTJsM!h1$elA8VE0|A75@66tW4FwTyyCbxaOj%yrmbA`8!k6yLU zo2bq&9BFnI78mx|P>CdqpI>l@KyftBh0<``?2 zv|>{M<%3Ujr|9nZs!Ek#3K`#Dh+Zoy3x(Bv3Raf~!YA_RMG4U1q?L$rosWQ$TKk?b z2KB~R3!3qNC?qrUhyU)>f$>hGj?hG}$tNT(Xr%)ew*G0@na?%{QPN zg2~9YqO-|#cO98I?vKaYd`wuS0Sn6Zk@zQUD?2e4O2N@JHE;m>opDrkb zRZ;LOj9F11Vfr^m)>f?f$Gd_}gr%CRVyv9JKGWO^LrkT)*5bMv7aC&P)8ww4Z%DK& ze~+mCytBHuMbue1H#2%e39Yr+X1yBbo0b(8kJQ@pi)}s+L8Odvj3Rvs_Bj`~S9}$N zCiH*2UmOR;A{Tdd0)QhJ7tl-Z2gBNuv!|tZPfGqp!_(*TlUp~S#iujP;R9Wpy4##% zM@kUmZMx`wc_RvDf2m8E9vi+E#wv>xLu>j)1MT7ZmN8 zCPwl1I*t*zH@D#foD8}V#hr$H^HIng;F~|;0D6+?i6zSzc%%FKzTX5b$ zu)l{?b7d^jB(jjXTdEF>XtQ0sPi!dilynPO;Y?wxb{ zxi+%J%{h$9(K6VC0>!*A>V*4_lC*hY6qa6zn^|vgnc{Lqo`%aQsRp^!aZj*`h#H!T z)TjAZVr*Vw^7}VSk(^$eaTSWNyUpOs7UOy;$8;7rk3w|L|k^RA4PvQ)+-4!?ccb$PCa{I_)##`B9JT7{3OcF(a4Te=W&2-?u`T*; z_V;{R8hbaNh=z8=<)5V(p#!#~=;7#%uzUH$Gaa957Ua5jz^B8riNbX>Rxp5o!2G^Q zHDln6A~)dIIPIHnkR3cbNhfpc;7S9;gAdJgPf)6hCx%sYgG$Z*IN;g;TU{_hCyN`QlTS!|3H|M9g>6I{ zdtGo&FvpY>2%teo9|#lLa5~8KOp_1+A2_P; zcMH_v1K>sW{o9kX`P>4aP`bR+UZl|^ChCgvq8T7zpd-uLR^5rD_0%?rl+YO(+Cg52 zlT9)NTwMfms7jDS;pB>B1%VU5;=oCrk_Xv8 z$4nO-n>!i34d2O8enc~8!X@GLq9%M{MluhjZg2%*wIT?WUPs3T!S~hka8DV~lkBrG zj)(puoCnNuhT{n7&cjO?COpnL#!^Gsd+4$S-j*m}v1M@GM2pRXwz#=&q1jE4*bx?T z#De|&g8TNRu)jH%xR#Wm{_1Gm&%|o%8FS%{H6IZj`BuF4sD__D(bAD2?~!~LmX13& zpIs0l2xd4yQEx2z>4&!zI#^tlXs?(-7=JRr{{Fo&4oUJUD78B?;x7!KwgZ44 z@JRGJ{XY&vT`-sZ3twR;ZqI-udX)bkj5BO@IUJIv7!Eyc;o7iY8e{d6!c*9JN6JX- zruU^7e+>S3^x}QsmvgFAk_mLq!O&u)@gp`FxmZVNpoZ9I0Fr}^B#khIP;rr93VEm1 zmfcM=`zZOCi^wmhlMXTTKToayR8H;Ueck%0G+EjOqeT6O8yQ7a_6kp5he}h^lSsxp z%XHdipU=xE21c4Iii&&N6&<}|)6_&dHul!yuwRE%Y1sACsF_JHx6Dodi5G0$t7*mOb%pf?rk`Iw zcfXD#1WBitQ#(sl6%C#7WK$*i+i1yL!?!2+g!wC z8{zP=hue$=?ZsuT<_avMQXoYQWgJbzO1b!OD9FQy5IJ)tw6dVTil7&n3Co8tUK54A zgtotKdbie5B~c%GaR1)V>2szXkSoV+h}g~Kpv5~JzBEmrBfpGN`=4hdIV$#N?A@XP}CSLQTS@ftM@2+z52}AYq~p|%(=N8j?gf@GFMe;d99p+ zA)6}wtj{;8QudAXdOS5pc52kcHfM!apPZboPR?u}Nl-vI<}sjYgQiSmtW2nq0Wq7ww&5 z)+hy#k2kwQVZNXn5X}n+TSJ{Y!iBCkD0Hg^)t0%+p*`WtuW$_*bQcVbSk|9`8?b?8 zTUMA~JkrfyZG4rzj-KI@zDUD11xO!hD~3Fmuq7kYN)WJk)gqB9Nf2zsJt=vIx`z*h zExF0rIcFewk?Pkm^m{Q(-1;hS^GHwVdX~G0<%t9=M8TK%zMR$?n!0g@Xfj}`Xs~WJ z&isp#ojkt?@HL3cP^P*dX~3g2VpB1sDR`ZSR0V9zfW1%2t&p>Awyrd`FQ>VFe8I?N zU7=~1k_*?_kgQ>j^i*`I8`_hS{8M4_h}1r;R2`cqsj^+LyG)Q zjqXF!L2p|WO$YtY=utnW{v1pURm^qi)ZTuDAe7IJDA1q~fb1z?jZTR3fvN&)O{f@; zY`-Cv+9Gc~%p>A0aA0;qq^%6d$)GPfW78*@_Vdv}h05k}=%{5r%`#Fub#%OgJF! zfC7wJ0HDaApmPj=H_n-@^)*(e#6Q2hd6})r}J2t$9PeaVC{qZad zr8qd~>DQz#*|yP*&cX(3OBMStqdI?jV1~|E5w=0e(ogSZB!}=eKCi7SP-_Mj3oSPh z`pUYzrhI$lKxDKB4UYvv%f1dPZ3wo!5Y~f=ZG@$XUWx-!BAWArNA7nBnv~swWKYhX z)|DfqVhsXjQ&&>C6G$cY^RJAAVq_*s{_&?6+3`~kF^U5dlA+Gt zLs;gVEVQ4uBTZR^cA-zVd$`?v`g$&Qbv^&{qPix8+YHRbr)ir9r#ks9j=>g#y_!N1 zLNPO)d5^)n6y>K%7xX5022v@C?%H4qgFt`~a}t>u!(}@)>!Xb1 z+wVUj8XMe;*!9EMBZJAvJD8fh)8`ZBIl#%%ASI5$CY@2S-`rdaPUONI9C779hX|@O z$_Cge09?muiKAelj#8`*R1Lt@D@RwapfRV-lgbbo4TFFU1Dt+8Bk7M}2S7XJl=O^( z0Y)>hP-wZCFjRW;n)2-K;rv{AM&4#*KebUOQUz7%;+y!)UEGj=wm1$UOb5*JkPzVUcpErcaxI5%ZH~4BL(Ay z?F2*GLJnG}<;@0E$D+9m_@wP>an~AE%@ayau4U}ZictTV z*&2RfU%yiby`y2fX@S4fZ^EJgQWj{8IALpgUNFQhRC9DZJ3;$}Xi_!}9;(x?O+@((fq}z zwNiBY>3xi34LdqriKMf%oX>Z>HODT8e8-aMPOY}KD001D@Uf%PJX@_@CB-?7Fw_JN z9%X%&1C^V3(TJr4wz)T2H@3ozq1DTv`C}+3B}o$nrQ+3}iCR8Unmg-6k*d?bWh8N% z$EAubk(VP9hbrg6K=S;K^$VE-JpF$x@@SsVhb+qgejshF9)72g-+q|lId2fHCN>f|hG`%*}ovi!fmpHb*>29Cz?lA~M zbz{BQ}Bnx&>D)4=92Y_jX>?QFQHntihOf0KGq%?UG;q==8z@FJe2&VDG zGzln}#PmH0lHe{V+3`i)+Pux}gP_bd1skSADvAj{j>3{}62TW;+v6|Uz(`OW?BA(j zkESvF2|EVGromeDRs~eZ}d^iK4V~Iw9iM6Wl*_e{AS^v~A;Uv)jDQCk3;J zXcZyQXy3bF-{k@w8#jLA)e<-(uiok~f-04(e|^)26FVzep62w17?D7uxt@ zG%(%WwArR}^%z+RZ9L4XN(EWd23rqHI&fMzQSXj1#S44c4!FihHVW1NxXB2ymbgJW znyss~eG8w>x^%pLyqMZfSK8}GRArMF*6Cm02N`UkY zn{OfPN7+rmmgaxn_ZHnN$z_uLKY1RQ@eFzHIq&J^cOHQoU}E9Ab6w}&e2#^t4PUDs z?;c#J9&y=f$D08BrpaA0xVkc$lF?Q&I@;|^=Ac@%*iBTRtSoC^CN7Kw`1N6&J>qgT za#EfhiBJOxLfr^i`Mp-HpBJmvs6eDz$i8hMA(3+{#GSTUT)RHWGS^%}?HbpLGN%s5 zFkpQwe~;yBeoXRP@T5YUQ7j4w_kK$fKPv3N$*nPt-kwAO2OgIkn4>p#pmjN2O0Q9s zpq*v@nUCebMUMs(5OYJcOn{#8;$N9h!F0tSQofxqEOvZRGf)72P$=J+t*}6j21u64Ik=hAKf5+-V)%{HNO6q=Kb0Bc}>ph@!3&L zPSaFY<$^Y634dSYMBP}>;Ti3M4^0Q_H31*$Lf2OL&YFl-0kVLK!4}a(p$tK?4Kg2UYb z`kNNH$iK1VciBI8w{$OR$nIwNr@yPEYe_@FPnH0UI;WZOe#1jBdOFtWA9~nqV~h{Gh=B5YCWB6Wm4*$ea7sb%NlAI znzGF6*QFi~g)Z~@+j^N~L$!BtgyR~zX1YM;dS`<`VERRZd>aU~f{Gv)FT7P^xk~(0 zYN&2Tg^YNb9h#>NU4u&*xs&eR5G?4`*eYoKEz4Zwd6xV(`&L&||4Itk(-vIP%++|? zXHzIowRc!c-Z8D2?8-1Ncq5Is6qS#6rJKCS?o^wNvj?@)@KC=>b$Hqull9K}j=*

@ZQd?iv5?<%R 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 diff --git a/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-Regular.ttf b/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-Regular.ttf deleted file mode 100644 index 9f0c71b70a49664ced448c63edc9c4ff2bf8cf4a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 158240 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

MnKQ==?C8ul= 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 VdRBvc`AGHFy4I1}Z8a*@{{tQ)3{U_7 diff --git a/frontend/appflowy_tauri/public/google_fonts/Roboto_Mono/LICENSE.txt b/frontend/appflowy_tauri/public/google_fonts/Roboto_Mono/LICENSE.txt deleted file mode 100644 index 75b52484ea..0000000000 --- a/frontend/appflowy_tauri/public/google_fonts/Roboto_Mono/LICENSE.txt +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/frontend/appflowy_tauri/public/google_fonts/Roboto_Mono/RobotoMono-Italic.ttf b/frontend/appflowy_tauri/public/google_fonts/Roboto_Mono/RobotoMono-Italic.ttf deleted file mode 100644 index 61e5303325a1b4d196d3ba631ac4681b1fdfb7c9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 94372 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 WiD&}1U9w};%2Kl9!gJKh^8W$jCY;;= diff --git a/frontend/appflowy_tauri/public/launch_splash.jpg b/frontend/appflowy_tauri/public/launch_splash.jpg deleted file mode 100644 index 7e3bb9cee6c59127a02a5186a0990c0cac34628a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1104537 zcmb@tcU%+A_b<8tN(%}x^xi_+U3@-|pL@T5-PiB_!fSUAv$JRCoS8Xu&il;Lztg7xPD5P-T>t_B z0DbTep#KE4F5UIK2>`~%fD`}#%mCz}C%^zwU=`pvcmM#j0QzqUQ$YdjUm8}hf0Moh zD4Tis1o{Mc`1lGdoRtNXFBljz>~jEre`v8kB{^9@kwEgW*+kxRT`@H@Z|KAaZT-^d(K;iAd>Ni{iT)aTK6+D(*eEkCf0QL?n z7YPaU-KW2TG`Bz4K#-o@r``XdDf{$|KQsd<30TL{LI(gCL_zhkIXnBi0|1*WSS}pw z;tuj*GXrT^Z&y!mkd6XrO&1SmSCDQ6>C;|bKKt|{NK1hAeF5MgH%Ker_}_TC|8KOj z^Nqj!ot@qP(*MW>J_$DBqGy2b9p}(L|NQ@X^12fQ>h90)6nJLy@YlBhuSY@seZTEv z^q2O&VPg7kxhE*yzqCh?zSY09OMnij*MFXMb-rlyFYSHDWWVqK@Vx1%Yw$1a8E9bn zFYOj^>B_&fzmLV=cir^Y(f+sG*&n3-!zbvr)!%XtH-o?Y!#pf){;d=2X>IZ^9dP@~ z-}Z!g==?4B53=~%em8Hu3;))?>96}&cCP@i^gr+Q@HF`QY@mlFI3NF!%f

?RWFm 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

X!9FJ7lGcsjy{9{KXL>RqRO+dZWzu^P{l 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

KfsX(BOgRngWUL%(~R@mdh7%(=1 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 xB7C|c?}?)Hb1XX`i`IW`7KVbA{|B{O)E58% diff --git a/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-ThinItalic.ttf b/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-ThinItalic.ttf deleted file mode 100644 index e26db5dd3dbab6fcdb06f7fd9ab9347ead808f02..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 187044 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

Ud!5A(C;j;`r{z7cP0US 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�sgq;mtEQG=CAEgxZId5{IKidg> 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 diff --git a/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-Italic.ttf b/frontend/appflowy_tauri/public/google_fonts/Poppins/Poppins-Italic.ttf deleted file mode 100644 index 12b7b3c40b5c8dd7d90968d43bac7bc673a6c221..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 182012 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
    ?;_d7QR+e3!@*M~gh!9Dwgr*NHqmUgQ}MAnciU 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 YAF!D*rK-^QCO?2lK6UusLzJKnj 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
-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 vRqoP8w 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%0A@MY@y5oMrBJnN_8~t^}voaM>RZ~5Zx`%DG-iJpW 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

DjfsidV>QH{SF z6<@+%l^v#Q1GZp9K z#fhE?r?M+QOLw1y?_Dx$89gnK7ArcttPKEQ_0&F7Wpmq;ZA@N+&~wrxA-iK$Um&-= zHh<1+9~vYF-#=@a$~QogXnIZa`nt5#beIRDuF!%_=bVwgRe1_)4lyB8Gh_V5`WP*q zj#o`8GEW{8-m@-E$WoWu3yQfrmneEccWcN#J}!06D~7PR+TSe8_B&UoY16Nz1*U>Q ziC(_kL&8C!(P}P^sM;${h3G5QOhnD!aXN`}^JbBHP@^)jl*%@L8+neO;;2kJ_0IPT zS?%bd-EJ^T{uU2a!>o(9rkLZTrbK+tj8>01YQC{ovY_*-tA8y%zM~XM zLqo^$=?Q)Wgy-M$`oM)i)KL+^;Sf%az<9u+p~Of?Q+9E74&rbV>=c&1+d6fC>-`~} z=Bq0D8IfnIZ^+o(@wIaIy65aq#h-&E8#L@R(jK8931z_T?{#8#lwBn)F)HqLbY1PrI99nE^cRclrinJ zy5;4pQ}I8Oon-5KoA*FN+$cA9w)pVmdK$Q(;#pi=7`dSx?l`%zqS0a!zO2eKHj2`0 zYEoZP@%9f5bbpeREol_Z`nhXxL9?ax?Iu|#Omm!Cy4OWzRMlY7e|qx7zp?wcHrro& zp#(h~+mIOfl1HO$N@w+sgyO($1>Qq=p8${n~sOFk)X?r`hpfc`qdTyJs z$FqV9+iQGR@LLO`>wI&u1~xDvt~6%ge!uAZJSDVJtoM?Sd*L|SWq_t37}>!I`}(J9 zueHf#Gw@Cx7G7hd^Si=`q;-WN+XVlfU>8{P$gs%br$s z^g|B3?@c8}JZG_}>|MHZm!lfDuFFPk$0@H8C~w_EKQmxZ@gPKJv39xG2r)h9@JGQ> zeXA{w+5kJB{w^ymSa7^RK>crAsSR7XXu6Xn;`VNS`#N_*sx^QA_8iygYNGa*L8Q?w zmg-Bszx&=lSw@y)f(yFwx8a)CmRG?1=|2!!aN=H$_620#*Cj|H?Thb&!Pe=m&bPcB z-^_CX$m2=+ zj5`3~tO?_%l-}kx=!lrsgI@HmyY-^5x&YP@N?)^%&6el4nxyJ44^S}s+pfY>jv6yB zwlwE?8$?g=E=hpNL&sRdAl5%jTkyPbuM4X!WE^XAD2trybn0AIsWS>7Kd?NtF}caZ zaNc-Z&9b|6A`#y7YlxVlQffD3S^UW<_qZr;aR@o zPeKkz(#U?$@zBxz-Kwg~mK&7sLezQbAj3TVDa@Yn>Olg)Y5FVw32xN*=Kv5v^@l+` zhXt#>HRqb3RexJEX0*l@{roO5LK71IN>xa;IDSvWt*7L5H!Rn={o`w!T5~XvPPLgh zHMi=xoJIWyf}vnikVpDJs?rUA#V}WjV>)@n3qCFISWc_>Z)*$cakez#FY5Q|1Af%; z(&bt76VbU73%J}}VLDxamuhCE*FSk}o7c>%5!lWL=~L!CR}d1+b!C^XlW4jISXgu6 z?61@UW8(<9(is;JEAl6HQzeMvh6R{lPInz+eauSlWa^*WTMFF@{WNBVkor~aesKEZ%rv?3m&r`hzYudjy8KUDM;E+`c}dY#KTfH16?s;o8kZ*7n2-O= zGjsg?SIQZ@K!(i$%Y969GcW-A&@)vm`F-Y7>!|sW?(O`gf-2135z5gUvqNmOWoSyR z)%rjKZo_)^@458DANb1KI0A^u$5iLDwu+KB{zkL>L7m8hiwjvkD~?o0gYd1bt&~4O z>@~HuGeG2aO~TyrL$ijNG}>szgbE(UgsjBt3qb8AekoMJPG^Z@YIT9+FCLa}|5)pd z*{D1AH$vzf*W3zb?WNxrZDmK73KOSutiFmN3GBPH5}umK7moRN?sCkchBe2L^%#l2 zk888J&ZFdmeRVs{wS63HBPSGC_#?RF=nVtonHtM%w+!RewgML|L{JZ5D@!yf?d9<# ztRgqa=kD|zL~-B1^^6SEx01V^;C@4~Eq(QdWWAqq=Y|)^&Rp5R$%oJ65-3vCE}f9i z-!NJOp0*M+lFxilw^Ev?iGGqIis1EId zg_$KhfqRJ_kYB=1v5NmswY@|5QBM`vjNbo&iiB7)wv%~`_W$Zkm}_;{-8hxu{sXnW z@y-ttILhl4#)hSu`a=@_13g0{X7|U(4;(g_+bV+IOU`{AyJ8-Pz}yo0^p}Y zRptLD^E+m2x5yl;@?RR93|{dBBHDJA=JLlZ*MUpk{;T8pO33Zm@yzXU1KE}zd(V|e zFuqq%n8J2UWcA{~VMQY37T3yQDts0`vJ#$4dD%By_xftP#<9G*WfxEjZvs=$9~$KM z>P29k`^$t0yEoo3(K4++V#!5BBi{|2XfcMd<@)T$vV=LS_`ql8bn5xtDgx{nt`oRmd{BXUiKfZtlzJL6@qrP!;hn0iL8sOng2HtDH z%@N5D*P$;9N$xX9Z#YO{O2cFw&aT(RNF0+7dVw>h041mA&jtE4%-9-Cqfq!CNV*V~ zafyXqQ*S0|7{Bq8Sdr>O2W=nQ6u4oV&OVrY`GhwqwF9fu)dke&qKc29ze*K&UWP7P z*y%!!@kXW?lfolFMk;hX6(BM#hpj0qq*|+*MK;LejpCxzvD!Uit%uP0FH#! z?hIqDX-FR2;U2x3cXAv3`ttmq)P%i;m}_|C6maR^tJA-H_G1Z;FOyYb({x_m#3q90 z#m0L0$N2l98k2PYlZ^FB7$@s*lD5gHDR8ddUha7VvC$tGFgNf>#B(@fL7kj@Pw&e_ zNBn2c-Os3)y+ji-SiSJJrM3&#E5wKZI2yTkWCP*}-U zW<_o)zH*j-Kl-jg5R1QtkeSr)737pm# zOHXaspj+(El)_;d=QiFnlk)mx!p-NM=j$H${hI@QzXEhK?@bVdX3EKTvO& zs^TB8rnCC9ef*fOLn24C9s`Qxly>{xzDONOUr@R7ABc>=0qh;0Q;D}d#?P3vsRjOA zh-ug9oKg%0M9b*FeagJyl%ds2@vN+;-y=~2GGn4`c||r%r&Dad!*XLlX-I2+=2sD8=y+-J&Io(i zC-u1(lX$W=o1pTDc!!Tx(#UlFNyHjA*Pa~ln_x1vN7xk-aI|687gNR&8u7TVL~aLR zM|5N!q6s1IKd_q=1E{CLnN9RdiIqnGRNF+|u<(MPAD#(`s4Ds8J&YgaVu<|hP2=Gu z1e_bXoo5b&F`#goJ=D8MV*y%iof?djk12vC|exYC-f?sKDYY8I z^}C06xw+mGAPF0vae8xhtw_T_@6S%p=ze@n$6J3Csr@4*cjXfmKBwQg=IAv^ve1UL zcGU4Vy+k2ZBI_4VfZb&GN*HJ|8K_hUL4|i&ZZc$LFiOQIe3&wmd~ude8&+gK^;gaY zzTsr)VYxWaW3FVPMAeTANJd-Amb}tOkwn`@oa3?EvBE=2g`_Bp3XgvpMv2j29{T>E z6`l;alNXn4U?KeB#HZkgldG1qs@o3|vc6cvRmD;;RJ_jyg*4&`Ey)hr#jV;iyh|Cg z*`yxw8vGTr09gSYjfjDYB~ti3DyQv`0c)(v={nMV&-`-Vt<$d@f~sFOKg2on)&8mu zsivGKke*~*PbSjwdIXY1lCVBjWiu@zwxsut7TOL9hYKh6$_T8t71B?Dj2%)3^p#^s zf0>pLO5DBjqI#Ga@7b1gPLM`l2#N}b{g4LZkub5b9B*ORO8nR+zdAJ6N9e#=15&q7 zq)s+{E)w|ar|l?D&|v1g+cMvWgTe@&GEYDTUS^m-u;yM$|3c_p@Lx+{mM_=W~=_qgroF+7?iEvkkT7+BzxP{q*oA^AKVtP z07zjVp7+LeDjP*wkx7{;1kMpCd4DiEC+0_~(D2Ci=J*@iy&{>XxroXupHstHOu6priH^SF%1;XqT9$|iX5BXi` zFL2h0F{#jqPQYz{S|mqBrdStrHQ1$ogRLe$tZ?-yBn((hU88*b02Bw(uW*U|a9?8~ zd>p!7F9j^JeYEWt(fSzvFASVjzU#R>A4g>ZHnze4BdPxfBA+ogrP;7e7kiv?MT7@e z?G)vv=NY(Uz2xkj{=n*Q68ZaMh|AZy}E`%ISVb_KQj@zkxqSoF30|^-h?i z4n5WYzfZDM=a49Ngz*Y&Atte-<_yilP##O`LrK}>w=`-0Ogz)~NDZddd_ z`~Uxijnh)SeyZ0L3_eCqd8s2S?GG|dC_kjn0Hg~4$$(PW`Mu}EpR%%)3o%&diW>3M5bw1`)3d~>Cqq0+jyo^ zjb&Pz&6m471bhDju^YQdY?bi~p2K%xy*Xp!`UZ*p?WvIo8xKQz^R#@37-OHb0hz(0 zyC&j*(-#JjOKPEYj{aZ4QCFdtp>}gP60o3TlWVX0{+u#==LOjfd!U1kvAf;5dqv6} zE%Mu*a&g%IN7H+Tv;F?>|JqWjXsH@CTYHb1rLSomr3^kN=w!;X`dPTubNQe{|EUZ_rbw zp#Y$kciKbsR=B^tgl5Fw|F!=o4Dm4*k1RQ}B0LHI^?m=jqA+o+#cbUQ+Hg_r>;Jdd z+g51&waxg?iS-znfYm-idRoNTYAomPu_ktb`K3t;g<8rKf9R3WIQ(~BQ7;dj&mMOH zE6q_A6`dKl<81RSuCd)|#!cQl(zC3DHjc>~Li;CLhv<4=bg4wqpkWgAY*CUwh`~#% z>b?A@ZqpJ&Rzqhz8Cg2nvaZ{&$Uo;QwV?a=?8u}cZEnkA?{2KUleW3B^j{*7>kH+3 z-V|+71O14hn75fu$_($nIt2ZE5GXz>+_UU`8O{>HMQ6ZLTyFaFxZe7p)m21kgyC`Y z694GVJc{GRecilYQ*WR6@$K@7dh6@5w2~R6>Q4z;AUCCVw;a#!CEeN`_2}a1r4EoK zFUo$L?jcn!B=%Q>jn+fd{VL&8zGEO%|G9j5UN;uttr9MQ1UL^i ztt#F%9$SmE?w?3DCvCgzMWnVQl6JW=V{Md5p*bNEa*d|}&O=SH#ac|s66?<_?a#j| z9@>BB^2vK&rZntlobxG*Z#_|2F0Vn~7jZ6+UUL|kb60KKowHKGZnqV#LeG_!THoGH ztLqzwXVtx!V8<>FTm=%i6|djg3DPM#~CQL zRfiP>H7QIF<8uB{45h7s%%BtcC&=BPKwOflJfTc-wKgDbSBObd){8&wWkg_KP`mi8 zxofsXain{n0@K;k)|JE6?^v0LEp8Yxey*ghc~Grf{wHUBJ>JVJxtG1ib<4=ho$35P zj8wVOJCTql-X&l1N)#{#uQKupi0OSj#M5SA^ZFlakEp?HN1`L5`JDxe)TzbBk4(Oy z`K|)bXF_Cpn3w9!!*r@;!&`4b=FmI4LxS88y6I1EmdoB1?E!iha!o3QD<*B$u_E1i zxOJmTA+-T+pjC-*FR9?3>!%Y$%oQOXQeALSGnWul!=g`MdZb%>(?7LGpq=h_X!bo$ zI2)Z7jaQ1;1uaWtVsUXAa{08SEg7=2P&2UaSgH1!QNuvBc5U2-2eCi~O&;Syu=wLS z+Zwe=p~TCXE`w^$KN+)Y{ViIScW{13jb!(R4ddW}`OM%pUcRy}eZqFnuntzxhIpEM z@7|}H`R3*zuC!AW;);5jfP@|E5E5C9WZY7D++2kH0}XDAxWTh)PuuoS907ApSCV2o z(th(ln;ns4|4kO~WI`Xd6WXIrzk^w~f{l;1C2L~fIEN5_NEqc%^+rA7TI}OGnct${ zwWiLmVNShuo+?WmPh!%{VGG0ZPObTv%O{(lne%dD17}Hyu8*F>F>Eg<6XTq=b^7#% zPLsudt_~24XLbgiT-E&yUNM-Z#xDnfa5HLu@9zj+QqEdtHF03nj|}nznh0yf_#B7*3T;{V`@@*`=DC?z)dZ#*FG0 zb(=BeVcQ1u>m^gR<&Oiv$2qXC`{fzu8dWjFYSsH_(mbZ&9T^ehKa&_#O)!hq%cICtFBLWOF7OxWn8 zcPnDH#amYt01M#H999q7glA>u@Z_R6ulPtmRfd(6^A$#UGXZA8IydN8T`W`HNYC<8 z7UXw_y1(DG-ssCHo=RI!teKvh6(iHL@s)xR8fO6zdE?B7Wmj9pLPKj*A^y|m{(MAov` z6s_E|8oDNo6Ql$VE1_Pl$Z(<f+v0|+z0aWJDQ7%RoFZQ;@@zn;`D5swg%>TKi3Kcs@hHW%jjSXzu$_-i@do06A*cCm5ZM!wQJkYg5F>`Z1~y6B~JWb zVPtEbB>SdMS(MM~?FMWM) zoTKW^H&F$J>g_HY_Vr(iGs+cz)M3nO((DRHYkIc>K0DsSY&*`R%w1J@CF~i=Pz2N6 z!2P|xB3}Z~GO2j3(rk;B3d7)zwo7(K)gYFVy1)E0j6LyTDnS2CRKV={;PUS@&>wL#)j&#O1jDu&x5|OLNX0ZI+h8B~!TaC?0 zK^7v;Lykriva(LEp1r4h(hM?_Yc5k!DP8=xveTLJA(U)1zYpgeI?dWVM0YTk~*(E z?R!9eca-D6WpTkxpP0PD$JKg_w2!@w)~#Hpb8O^?nJ=NIKo%2H21i@Ndpc5F|F$r^L1ly+NeX~o~U+1gqgG)tYXK3K_c+aa4S>sno;B{5EF9wBubmCGz;vd|mYaDV z$FC<1ZY=dNC8Q%( zQFn{@HX$~?;+wWD1)aC|%=ux|_R%$)e%$L7Q4QltxrP9bwTS#e2>R#e?^XjgugFRo zOUi^|Suy%ywuRd@>oVm0wzE)*=;H2!%Bu1KF};)c%k+m6pv>1+LSc&WE|r_0eelI` zEq!2E879!Dm8*tA@!Ll5g+m)45?&H<{-j6H5~l7xd;%3apO zege&6KI=Q@$muL6!cdEfjr!2dwT;TJzyc-Kk`VY<&fB^eeSUmf%L}>b-Pp! zIs}$4k41|HJpIhte^cZ&-mF%wBncLc~L?!QYW$cjl%(TZ1p$J@r8%U*$T zfx5nq`pOzrj>S1qE%CYa>vMAwHhZZD|0v>A#g;j$wN1$`S9*Ypm@RBBQq?NAe+fYZ)8$!a5oNxWY+?V$ra>aZ6wm`rYzMwFqQV+*-K?> zlU`e7NIIW(dd`nlYk{Zt&+Wto5PPte_mO8&9R-qyZPgwM&$BEcZT7N#KcqX? z>{(>JHHblyf=QOy8VImy`Ilm}xWfcery>8xxOp%fek4_Ycxj$xK78hL|6|GhlkC@( z4(l1ltC_9CWt&K{c=FF`+XL4*_{W_FDMrh0a3q=jsOnjcFEDa9U6AG5pvxaMTMU5C zGuwY{Ugjm0_!l-y@a7sU9ECQbOC}mI2VY@XN?kY*;D6%0Dz+Sslu{|qBL5!Jdw z8_hjdo8p3cO!J@Yc_lhw2!QTh;A|<&^3KWBPIbxXrY&#G>n>Hs%=P0XQJ4hlF#lvG z+Ca7*{T`W)ap9@RX-tq8o?a%bF9;@0eG7C-muXFVAN=jViiC2Guh%AoaxRomE}z`t zvW6x*b4X&12|`^$u08{Gt309d^U!@!(Md(ZX~W7`jU}M}3TdU=yBg4INVDC>edtFh zDz?>$+%1WnUY9-4saQ*WWAPR8naxfHo)@m5kQbe$lp@d{Libn zNL{p%6~3{sXcdj#*taRW?6y48^)|$=XGxsa2OxHK!F$v2T~03hmx#=*_2s7u7LP=0 z9t15#Gx`5l^|Gx;;uqs5`_BXJ;$FbFp1UUAb@icW8xQbQx<0{mGvLp;+idA@cw!sz z0%Ke0O8{7HR9y1q+3E$osU*a{i*i8VGY{q^XCKAT_cH0TOiS=71VF46{)ioO^Hlws z`b*SW8Fi#WPMx46%T@f~D?#B#U4bsk2e7|?3`~tPsz_4vM5+9rZ?QnqzlCVp}Qq^Tv6u=oZFI|zS@En+B|*A z(z`V2xy9&N*!=)<4_1XdCl6JYvZ`ysW6ywHfU>ze4S*Ig%g~g-966;bj)6)V;2`RpG*B+>A z` zC8W1Q>dS-BmYQ9#-soARHmP}s7xp{z4 z0U}|D_6_uF7qy?$GnC-S%^jwej$%jOTZl7lG>%i6;U+_#jvL)Dp<%R5=4 z^=B(23GwhlUvtABVGp;Gv6rIX`W6B~=%G9yR;!Kt-v0vC^jH%e8VD zOn#scT`Op?gr-n<;&vO7DXA_?=#ZB5upxWSEn~*?rNK*an*g97^}c1g8c_1peU&fs zFnlx8LQ)TPPLyns3JBvqwGHz#GbXWqIG8Gb^~v#Oj&JUiijC>EUkR>M8*Ck+d?mMK zJpVn_#C`yBS)L!T^UNUuJTF78l`mEt(2UBT7l44N?Y-HOE2m0p;B z^~1pBNl69W7)8@2po`Pjn<^0Y*`h#c#XBPJY=(N|NuT+vbxj^1TEo!JyHWwr$Ec1A zvAvTz^-Qt|x>C5iL&nu~$^oM{Z?Y{&4o(fzKlRlIaAz%*JF&JQ(M&z4dWJ2JlZKJC`7TKW(9%pvpjL)Kfja8+otttP;Usp8twqRAd zj#P#c0@UmGu=}8Xpnm>79OVDLzcWqg%m!JhB_aVe_;Cr_gb&z_seh4g;ynW&WZn~| zS5FS5@}N;=@p!VO$XCXg^%3HzWRSoaNgvEC#BMw>I9h!=QEHG2BDB5W&`ZB>G%E%l zXw5-EL-xV$7(gMo*NZHgC-|9rC;ff!pBO`$;!w3i~dpYwO5?G z%AKZIg^sFtszR*4epK%wQf&Dz!?HR^Tqqv&#bn8W zO2Gg4jdWxlv8KR!YydrK-!X7e`ggv`3vsRYo4=oJ1yOsB+-k=}{{K-Q_WYaK4>aj$ zN(umvsNzM8i$xEQz$;Pb4l5tc`;Je7FB{1<4X!Yj*7?Ak$K}FG0?zyXS+-fI6O0d# z!n^B^f#Q2Sbz}rdTP|~Z3OEXk0EX+n1REG{n4Q|?Bc49xIUY?UgGFX@MhP#@NIsc5 z%V%~g%5ySDL224Qu)FWHym)?ahv$pBwjYDc!%##IwXtLKQ@_p;`G?&4ZYS7K^Tz$r zFjn84Q1YLl{Bs`h7WFycRb9FI9n)z?Ut^~%-GCH`x@?yzdQfZLy!5FUr<85~;88k> z9AaZGI&K~%PO1UJt?b62nVF{%uP-U1Rg zo!4?LY0+CN_;hzPdZL)zk8-M{qo{3&eS*VX zuL6?x!73IL9)VuqjzFKBhP&F0sJAPN%d4MBNbNpItxEcHsd%tT7C!;s&*cgTECD`# zNG+mNx;<9k*gBBo^wk-;cDwg*8)`m{h5fm(GQ7I$S`u=!2@lx7*|e<|D(+9s1FO~< zdxol-UggTZqtM=XC=d~B@5NU;7o*rdNGDaK)Ko6i5)zCkg*ArluT7rM^oP7WeFae( z^mAHb$uc_*lM7L?sXse8`^8U=_O2bOj7U&;+}}>qQxO`vhLpJ?CKo(@#HvwTmh|lA z4cbehe0)$f$ZbWhu@Di7o0_!*wUzER>w<(kY^)B;&>&?6#4!k(*(f+s0`m!;2r#O?rOq61Ld=JuOF>C_)_X;u8V@nhM&%f0V>Q|A zK(){`r|6?k%jK7Cp z(lT7WNi9_P)qLit^0^{Zg1A5m@!!ADUOAMt#`?Bpq9fF|UpIetv*8D?EAc#i>5hT3 z{+8sRo^amN)_tFA<_ZRuo{&JN9CbR{dt-{S(HLGH)wthaap`BTp3itcPSd~1nBf?1 z9!}i!MLG>wuctI{h?7X@E04cEHgP)U7l z`Ecu648xP7i|_(cJcR+DO5O9Gu%{8+I4JR{NHx0R{uJ5Wbo=rj#ptnMN0K*y3rS@7 z%-&mm>Ubq#y_}zmZfrTXvm4z1r2uK|^FKnhsqL5Yq#kdnGgN<(Y=d4=fBR;yb_lr) zQ@=Q-v!c+rF5>n!teJGv@O-!bn>j2iVtdeIi%9FhaKH6CJKLQ05V;p@p$St!_BFJv-$Uk&qALmE%4ffwXQ_=ZSCo<@e7Itulgl{_=-As5qwRIYDamAB0 zZ-u$Q9d2)&MqPjAXRf>%txDYBS=r57|L_2TjcanmU_ahq+p#E9e-&|U>H|-`P^1jz zhPvGf7YX&`202zi2FY25lWo!D05~Z$Vg;-Ifc#Tb>3LHZ0#DmC*$E3$m?wSNDw?wO`pm$U=AERYce)E#=Vb*Lq)t$;{u`Ob(}~kZ&3# zTJ$b$qmNc5sH@4ZvKzn5%YTqw7}0roWqY~)f%~};f-~y(sCCrI?hZM3^*?4CIdx2o z+wP2}$Vaq9QuLqf?rxvG;{Fl74OE^e^>~LJ z^*$ekT8EP@!>j58t!d9Vz0$nUY};EA43G7)g+SYzr0~MDv=)?<9o~EwGaSx+z?hMU z3s6#0T1`?zm6jr`C(Cq}CZ%MrsQm;IHR^o#zf>!cCQ+pN z+>JwJvERMWwZgC0&E^)&PUbg?d^tWhbCRQyg^H#Wu^9(W*{lJE5;Ex*D_7^cm&zR< zlz%=yjlF!X-p8|c?(m0F&GyfY>lI1kVtH$D25MgJu-=Vf3bCLj`-I5&hN#`X-Q;bI zDlLWnOq^Yt*Y%##hn@poa1RY>C2{3_I5Wk8L0>lD;fnE&XO$hh5RYvWVb%8y%~$vI z@Zaz8mrXNyOFn&@IGnms9L2{-=zw<|R;T}A)98O25(R-o>>a@pp&^_WYH)NAj`ax6 zwJ!HFyu55BqI$9Rv4!HyU=&Q z{#EWekfV`#rWYxS(j2dEC=cY6vQ&qorceJDWp|hGAcgxAuZZ{Nqwd`f@LY0C%%EJP zlUe&bU}!*b?uC*2^z65qbhs(My;A>ZGx=cU)lFgWP-iUvOP_T)eV+8{Mr0s6=iUWF z#xOH+dbHn8#Ukb4jN=vF*=Q~b$!Uc<~b zt8Wr0KA$vZPXh~wlg$ZbzMsz55oroFcdsw#rX=8C0~=6FEJ5=@i_tcxZp#;^D;F_A~ldl_J6j%U@&P&Ld@B zvDYb`BNavVcfK*|-ILAoWuovjeUbT(qU{-V+Y@^@o8SiyaoMUbDf}q0I$0om7_uGn z?vYaOs|Ypc>t=AtjT_?EDUYTUt|Prae%(0e{po}MpmUHX=Y}bEpy#6!;7v3zExN(a zVdwa`xYt8PVFPyzZT;B#dFBJ<8#Ahrb)k>v27dqKQu>|Gw| z9BQ}UfH|5sfAb%hT+i?(KXr_bWKyifzNuL7&dGdu#&$aoZreOML~)j*G3-6cJXE&x z)E2ZF7r{gmi{vQ!5^-ukw&@wS z<~J3Q-Z;r_-d{9LSma@$=C|8`wO{FxC6n@~uaWv|GRW3ResNHmbtCgPWla0Gn`{r3 zK`uPE)?TJvr!i$3X?0)ok79S4iQ$g~AGJ+>%>*$D%2HaBW4>lSCaS(iakM4oWwY)MAn=ZXBP?P-K@cRlS zM_CmE$fKE*{j7rKQ32a3_3^C>Dbe2cy!LHuG9f(8%hAN?{et9dmt0l9JYS?1NR$^mod^GG9x5XTAC=XjZoZtkvhof zH>Z4GbsM!5fxkk`EduB~+wW@m9|^f|`Pb3#ney*CsCLaP={?%pqIUiEfB0xeW5Ozx zHWqeyV0}dFd+mQo>R@N4D+u7c$>V#yR)1qiHu9Yvw#-_P!EnTD(7%1&Ywy`N^`8bC z3T;|5R{ovmALpKaXykhKwzJC7O4949UTI*@0N@hwn1FWdzf=1*bC`0|$~UBdl*Jt-_*DY*nUqHSi1`#7?& zGht*FLcSomkyPQ+{W_1ZG6T(tU!zT4YMC>x29<4f@(z5?RAo|DeV1-vur(ZEZU+VW zVw)lX%?dA82bq3x+)^?F7x_A4esybenAY87``F}O_7hSVfj_x|jT7X^j`c7i`Q+Od zoAB|@g-V9jD;xNAesynCUSa1Iq4iflp%1r=U4>eXNgB2%)qk<5C6u~^J#(7r<>D%zvU-lTH9Z7me%OZ-n zEw5$R@6`nLs(y^rcVts@+AV5?+m*DDV}&%0u>xri+2=lp8*qphdt+X$rUA%;FWZ@c z#yr0x^4#UX7d1L%w?Hp6mv{}3Hh1l|)`ADOy{u!CkV-;fhxWRxYXo%rif3O^7Pe0c zzq5bIt|dEEW1m_Nz6Qx2)Jo=eyE!wW_Rju2Z*mqzR8xwX5PXzuPbaO%0sf)}FK{TF z_iPq}e%tOKlYGxBHM&a3Z6R&yB70sx_fJrPK=O|6ip<`JZsVO&^!tIMgQb@|TPu`5 ztrO$~S!P8g@$3c)f+w%jdeu97H9C3Gbmb$WyJAv?WO^v6*MyZHD#2>Ly%~-;#UtbE zw0~83RgkH_766-O&xGe$yfHTBMchwnXPF}Cc4Mh^>Ze+ z3FF+r2=-eIoJSK$Bx>av(A*wD&rUBCx{6j0rDj%eEI%?e#z{(xyvml~ocv9NZ zCFY9Hj&5=Dh2+rtnBRxCw{IEbIX>7ViYo*u8jLi|`TlBZToDmh^*rJHu6NtW@ZxdD zh_v6qK0nmp#Pfvj!9AaN14TeLK#{{zS@`dHbNagb_&LEjyNR+VbSTA~s~2wV+rE!E zLk$Y+yOcih`a4a!*xeY!0Ozm1l$c3wi3}`k!$X9?rqvZw>Utr4WD_!*&pjpw-ZgYo z&$U519{0A2KE1EU%ni82i93R8l`4WA{!Y&{jN(%uj zTA2LOj|DR$Ub~wZrDOvA?-iJL$p4xDsrQx49?FW8g|)VzG?!3-%Yn0$=X*k9aBxdA2LF z**PU@a_h$8o6C+g)%*l*^VdMZHG~Np$-Ji8yMB*Wr0SYYGjSLCCulvyW_7g`{UO6= zwZZ^Wkv)Gk6U))^7v3X2c1h01p+mB|DC-01Tdr4W2P)l{y`2nVzc)-wb(@q2AappX zL~}O#Uj|m)8iuo~nm4i5VucdLrP_02rM6Wihn(8!VIH@YT3keaZt5#36i+82t&HKQ zF^~5HKYU~<^E)x34c_-5{!N2geTrJQOjC3@`1jafd42eSnrGl#o==X&b0WUT=7Cks zZpd^Rca7(Z_4m$s-0FGK@hT@n;Fj=!gymLphm6IOnI|w$*mnWrggG#h3n$wEeZPmC zfLvBJLuY<9HtF=eU{;9_otHHv&fdhYDjR!Eq}{Qm$Ju{MC&bNlm+1ciR9#FIAXtkLT|Tb&W39`xm;6@Xf))SSWAFQtSdu2pV+5;TKt&6mJP2>VFd4mU%QcI-u^|qNbwIsVn{JXU$YeQDbuH61cEmVbo>< z_J*%9`(=#$bLMY{f)n?Ks?$$zJd#OQ690Z3@40X(H0*TYf-5~osdtPWa-2#`se~(D zwrpQ$|EY4X=afv^V0yyO(UkVa(@GU$#AeLQnYSy1FgwgL-3)Qr1c&((PqZH;q~|tf z?fbc#l8azjdRsdY|Ik;nU`Ve1!H?$Ni~9|D9wc6%sHfZW);rFRFugWq3Ssf)SzX9( z$?A00)@98x{@is8KVwxeYzEmWcr@;zr-`$fxWuJT^$xc2pY3ry8Ud<^ykqL9)AQqZ zNBk=N!b*8FKoS87kWGkWrBqB!9%6X_vFI@JNh5gK9^1)fo`#lNVcWKC?W)g0I%P+E zXqB4(WE}X4j4~Q~qz5WVeT1o4!VZuDvC)h4OkTxpl`>Aqsl0`Gt&w7_&+D-_SZ8X# zb%N)s5u2847CK7@_bNrS{6GL*pO#i&7(4(qYk>7|7(v~wk4_veU-TS%)v?6amtijM zL5~vKb~vqDu%_;|5wpl0Y>L|1r}N6pQSht-udvZt_Vur&T0ZN1Ohq}Z1REY6tjRwa zx5Akbw#JbSO%7~vCdjCa@rc`}z-gm3*tguaMm>FK{ePEZXZ0(fU0NHRzEg!*r>fM> z+Ek`+N<>M9XpBX6lEsdhC>PobS_rk3)(6ZI0$luOCCHS5iT6%x#AJHMZ#r-_vt>*JqCH@;Z!qe%Q9{eyX^ zP~;{`+3#)avZ(K-vI)VW!n=~#68k#2A#ratEx>9{7VLiRo{SoDef>RU<)M{!F;LEoy21$4_aew}1Z%0q$0P{ncF+ zI5RE|EZ`@`7)cF%{{!E*bdqG2o_tf{yr39P$ZD{S)!ZEOU%(7r9xS)971?nmbxr&% z*_FxB!^V5NAC4PfW?fshK_R#?)*bj^+)6Ju7rZHFZw~M#iOumVfamL88GA-+P-4W4KJcBfU}&+t%947qPM~OQH+D8OmX+$lgXo`2d+jPxDY`$7(jj&>SMt7@dVLyrQvjd~`Js zOemG1SF1fNT*j_6@ol_6tSM))zW$#+@#nz64H33C&E4|o5Zpn?X=Akncy8D-_wW&_ zu5nk>V_}-O^N-?Zc+=U_0zEFkMa=Yx`3fbNsP{8aNL}{V$^@S>{R`a1CMlv|Bb7GA zWNOW+s2+iy67yKS@|U?1KC@KSU)ofKNaHND>M;ftKnO*x__wvgLJ9u4FG2i~`d&^m zO3R+X#j8bo5?sf`blciGbjfJt5(6K!vw4IYDVm(lDBS+1HN*8eLK%Kgx+axbbr8nJ z5u&q}&zL}4h6w4ea+EoeRp!Zdgsw%ShkQw_DG9KXVZ9U!)Hrv>`>x<1*S${3sFs4{2cgXK% zA0fdPYszoLMCZ;}A7t+=d;i%`@jdu>QG_FxIQDp9FlP&6Oa8QJf(j+#_1}q#h$%JX zwD`M<$otOWM)#EIgV8=5BI~!k&vQM~hu&1(5LF;pP#BHe*y~ru6y|2J)s(RNPrw;et z70t4l_ZUXQa_&{cT9l|4VBqLzg=u!Hs= ze$6s=O>Qj(2+tWsdZYbB1ar-%YHZ|ON(Ybbba~MHeUP@$NwBz@8)J2@VUm)(?{IE> zUv#r|L;1!q7i--4Re&bkfO`i^kzp`*Mqk~ z?fvS=aG@1wH;MwVh`Wb101Losk&q5U29|EF8A`h3b?eXRLhD&Hz)!y^%*R*RyP7ws zY=(Va>q;K3oi%`qTLNZ5A>0A6oFdT$z?hyT&pqqO%7KQ>UR%%vY`I#o9)u06%C0;h zTpYbasvb5 zuO4e;$GU)Uuz1&l45w2o) zg!1;37}^qqxJ{sIB=w_!kZqr%9d`!LYIz!)pZo5n4ye%(AG-;&u=gc)&A&eHcOq^z zmCifqceQ?X7I1f*VVou099XOcPsg?;%C&6&4Z8ns=etL+q2^DN3g!@xdVN?=*gJ!j zUCNUh)Ez*9>#0kJG0kOX=C5AL>_{&F#ruZhMYC4zc@%{pi|*&#q8-a#N^HG%3=8yF zwLEZQ3c zKRQnTTCW!KDWvR;1+!>B)3@fl%aNBckFK%zOlJePBSDLY%tG822K2s}QN%XwQH zWc`cr=Zfu*_&7qGENOmnb;2WEljAZVxU9y0Eh6rMrKCdpHU=2s)ae^56Qwt{*XFfO z!D}bO;(b^b%MHv9%}>poXT8?ywoU(<5kO>9c1B1wEOZ!>F^X57EgM4L~%bJC^P(D^&_A(e6ud9Yz zikf}asjzUfBnH&9NROxMk)1ok6I*nEKRLP#@C6HoM_<);RJryPS95)rv{Z)_4TDvx z5tUl1hRF>!dVu-@5?K6g9V4>EIrH#ocm2St==>V%jsd=8S)I_KpLZaw(Pph~j@kvX zW@Z5}qu6x=Zh#Qi%Lei2w=9ffd2SLB8-X!(PMWC;ADgc3bA?X*{;U zkYAcyrQ~+`>Ac)XRRl@+-lQU6rb*fud_d0S+>#%d+vF%gUP7ip7 zo9W~Qvb@}*P@-CD*nnM=LYSXujYqO)q@W(}ikqJ`uI`b@yqZeWA$)Qbf2pyI?S%6z!FoJC z1gHm}avNR2v51WI9m2kwXD0Em3JC**6cq`tqa}lW;w($nA;l5a|0rrD(|JC^q-XW7jn4x8t1fNHXXMH-Eku||N&2vk5k2-Pv+ zy6+ee5XQ8omenyLlo`|+IFwUj=ORwKMldUEMr)C4kJS&dLa_Yd{m;Je>f^}67TrXL zh{o0Pkc5&DDVyI8{;G@DfxU529W4O{{;*~%^P@2dDKfIKd3nA3b+?T+9E)4tZy{C_ z!dXc_T9t5a&CP+l7P`yR0+H{!?%xD_SMCIs@uy$fbs|zwRJlt(E6baRv518^;D(jmJ}s%`uW=G;HZOn2jG z?%<(CR0equ4NN)m9VSFhsjU57ZxIl9r31){7`OanA|09VA_AG^6>yU#BU>y z>;hAk6N|3Jb#rU#e$k^8nrv zN^lHdISJ5LAzwd8S?5+}wW$Y#`eA%V@jIRvt8TUMF(&Niq<+RcQQ{) z4+l$)wFB9<1s3F!z*h0xwNDa5yMkFZnJrjXXxj)nxobswN?FvPUt~Pv9VV$}WMshD z#_;jW z7T1QC)9H&IBweZ5Q`_<_Iu{6dnBwrM0NxzeN4yPwX^2UXBMcv_y6oX2r2W4o{-iYR>ctJJ& z*m}^ebMRvSY)*BAeCU>}T+h!5vs=0t+}vjfERiyL@F%<4zoY)q-t(B{mt^rotj3LW zHoxbmUv7wvv5S@48ES9V8DF-Mf&yij=ig13H6kxB>|Ua}OZ}ldc7HBi_-&2*y`BIV zHjA3>xz)vJxr9PIcq?{%eG}o1IOp4^UN*j4{P68{g#T3=A?SYD>#j}eVPg@AYFt${ z_im{K!fN6I+ODQ_M1{)1_g{a`GiPjm#Ae8(`A9bn@X$~~AiyD!rf*JR=_^{3y?4$= zB_!7p>DSgcD>S9O?~%27#7X%E0#JW{*k0M##zBJcpV}Coy6qBzv3%Ws0EaBQd{YJU zc3~efuyir1-U^D7jtN!VNazSjtv@krl;U%36fXtFg^eZ8HCSfZ%R7 zxj^qAx77YMdxErFDd_^3RTQiyJBuB|?wvGW$S)3uE6F)qVOR6N<4+Ocdkb=}{tFWA zb^up5=V$A!Udyk6DRG91F0Kq3Z_$p%p=mw_E8p4F5MC|aGMwlCD$tazSUjTY8l zp{kK)8--VM!IP)tvhq%V+aNOr<;+FvtUuId?fu-1CX;LfDUltxU!wB}u2Ynu5oyl! zVPRCG zTzGuQo7xT1O648}iaPa5qxD}KEbL@h z+eP?~9uoJ%ONZ7Q(2d?nmDw1ZEPXkg`N2tJPSJ7CJVQk~aF6e~=pF#-0YxZ5vz!Tu!AwxgF$S`6f;w$v^$gQR)0c zol)`-05zbptAqUNXTv#RhSHumx++}%F=1V;2*Tm_q2xs3b9Zj+7c6BsNY3p!-ChS< zLd6Gn(x1RSB_gha;dvD~p~y&1uAtXlI&_Aj;lFT8PKj2d0 zZ2LTH(OK)W2OackyVAb!9c}~tV-=oUaru9=#^|D9GbV4a*46@0Di;+d(bn6oeqa@K;y=cNjr$@-nz&*?&kVpvLjLovUX*dP$`M46W;3Sbxs#U6Dh$x8n} zj?Vg_srPT=LzGfM5Rj4@NH@~0w19MXi=(?k6lq2`O2-&IdLl@NfPgSYhr}3+9QAqj zeg1&$hqJT$+~J_J4@NE&+)l;_nKMv6n0wN86E5Y zynk}Me8RxvN-Y$N_xbN9{8XV;u*5AH)!caNf`bm??^`uC> z1trffcZ(;hNMg-HCp_l}|k? zR?MumKt7iYkX4SKIKZk}HN1MUY)Q+sqW8sWjq(ApT`b7d)=NeYsLAtkx<+DX0-uvv z;LT;0g@f?!>;AaQd{;|fY}mr=nAO-}xAgm+9e?WFAP zf&~J3>(lG_3J<8<`N$&M(Vit&sX5cL`^*l}siPTIyI%&w#m%hUcucq^MR)b|{k2?h zQ=5nB9$KJ^1l3x38+j*)#M0sbMz*Cwc*O^ZV77C3pZc_1nB@(9OC7CA@54{g31?Ve z4tDBN-*Wea!(Zp&VcDDL&@4BJL7#N+_73i^c4UlzPicyUe|6z$T4G;f&!Fgtd^|ldo?$=*LSc_3vf0ksD2jSz>THg>;X&b)D?3VH4RHS!}9R@aCLD-yF z>GsJPN}+OE)%;wui~Ztl3<}#}sV&?jLTS@wk*XrOmv~NCNr+*Mu^o>qCFT*A$@l^M z8;z9vbPwm6;JlTF>&$6lZqBx~@2FPyE{DdT$crL5_s7E_>kX>^o)-^wtZijl%m%CT z1Xn^wg-#DHO;2@Rw-$cy+CN8?mCuf$o4501@KZiU6rbMiC5t2$mh$vmxWOV0&~KJ0 zB+i+#BBrpEGahfD3ou-X3g^cdR|DNFNr}B5fP#W&f`nBU6Vhb`^evrymh(5LRoDT$ zUx*)$<;DF@QEe=G!3>Eu=ghdqh*_w1Fu)5lJjj2nh93-8eJ^x2DRr`kPK?Jtz3;`@ z`Sv9QcyhaYBjzI2-LIa0ey{B*+|P&|jl#irIH4)CBeS5Z+E!v(N_M@jq%`Bl{74?XSkZIx%nKSV79o$5# z9mJCSccmBtP`UYn4}Ey{Z#Cd_cW)(-$%@Sa>C51;YHo_ zNDjFN+b@ iU`%zNoQ$UfsUCyL8#`U{~``fDONK{dK%em1)}Zgmcm&Q;C?~?Lue&(&4*d;a5!at93c3&)h>h7Wjj~iRX+1!T$l2 z0se<7o`;YwY1Mc3P>*nty+m&pysp0f2a`X1ZG&)0el#VFsx59^?$JspCtl%YYRIrNQn00o-0EKG(FmhvO8!anY`B zs|(kN>qbg&;f)GqEWbumXH68?J8@KPP|0)P7@vZw>U&_lLD{EL4TW7mB}WB5cX2g+ zBs!TSQki9P@qM#fX3I65A7CBPHYt&a^#j3fifl2yX32Et{i z2ozz#*qEbY1#?6L`W_8q$O7cz3oX?Xj_Ls5W6ALDC6TK{e;oH^OvPzr;fT(9H@Yn(RIr zFi2j4m)1r7#9!$^WBVx9}CFSJr$#WAFY1Fi8a3Pck%fSC9OVrEGlir((9X5EXGSb#iO=Rg%Na7ruArcMGCM z2Eho3N|t8Sn+)Hua!9#(%qt1w7FFa4NHCbaUY z0#UL$CheKfSPNKHPT$!8$);AubfM$^CA#FHcCwL$iaG_igps!KwhjIxV38yuZ5Egb zNZz-L@-zU)fo**IV{k(3e`KR_u{cX9d)bUCux*4yZ&TgpG8rOmqf{9 zA$11Zq|UOCsF1o^)g3jdpdu$RoIQE{WhW=o0G`PMq9xWKc3%c>1GO7`{G{&ZgTg+4 zecJKL%kvSjI-Mj13l*K2}M2aLbhmH0afWk_MS2S@-FPB>ra4mWaFRM0&`d9=U9BtoK+)>(?d?l1uc#%^(|EF9j+~JVBb~ zXODCxv}U;rUPzBN5Ee01@4}BGO#;#<{7xTpD%HG77E{nscmUQ&ZhuJ5tmIu205G&W z;nn2*X-cahP?Y;s&q#iMhI!lh-HWup+}+9k&${0~(7>d=0mKo#X%xo3mpdqUc8E*4 zSunUGMlge@&{TK!b5Z(5D- zxhn2o^rWV?Jt>D7uS17T-~^!g%rJJFS@U^W_2VOl_|?ffM59);O6;2#o;5y+WCgJ$ zL~Vp|W8CCE!o@tJqz1rk|5VkZoj4)qj}&P{UC$^V5RWHT6`8+ne8m4Q9~MOctjgdP zocyVPXoY|J?W$fiidr;{;kto<0rAx8rk~f(m-c+<-r|!W~0TJ{!&4kbP#)OnEd(~!U_&f z3{bXD`VK|}CB3Zzvk#9sS@ZfNJL!lO3$ijw$e%nWD#mM!Jv1li=X297a#ehKyzZMe{n z_XkWYp1+56FL@n}4}Var@_YszXR8}_NCwU_{UW#RQDBRE0_iR(U&!21l~&mZT*jom z9PoN0fzI@sium7Y;?HB0#TNmBH=nb`q`GnU(1QO?iWXHHD#D{8E zcJj1_fh3A2LYV90a|35BeNmNLZvca=ejS&X9haJTl-(gm2`boNeC1^*!wN{+Qs250Q(bp5bEj zaB_uiJKNeA4Ulk~{4-G?OCTlTmcj&MXe+n51g^ughLLiV;CAGG-cy3@6c@D5z7@EO zu#603Nhhf{C3+&R$d!7UILa{CrSR~J&2U1eLWz5o7cOpA)b&5xH}4~!Oi9x+3W{G! zsc&i@*prmKH75~UPm3Ct0fK~p^<=8(=!~D_L zn8LmELJykpE*T9P2P)op0(0yc9e9Yj6YD6u9@$TiU}CBsh(3HaF~YaH9&zBY-}k@x zhyc2Ss*gGD&%5z00t_xTwJ#DkJ*p;WK~1kPC26Bx1>OlTE3d$LC%^1fm$88Za5wcX zG!?>`s)G5fYV?z$p7eZBtc@fWAVE7R#b8hN^!3c@!a4SwPxRszg1ca2oJvQJKb}&u zs;t#7GB8lE8Lo^AZyTI$pQLPLAGqT-X^NS*nNB41K0oNi85I+zNCQ-(Iu<{HI?R-+ z5|EQVN^obW7781DbeolkXBSRiHeD>_u-ML%csf?%>xDsU9TZ9I>9db#_M)xxV9WOk zZdXN8h)>T0M;1scAA&$k_hvE5&r6URBCW+Ci1d176 z3|?Ft;!ZzkY5ME{fb*M)#7(xMtN_@-UD%M8SSb(8(RmsNW!|cT>M3h2Kty#^rW)zF z0U%N#@Mz`EG6B&RdEOR;y1MN)>pmoGb$$?2K-Uxix(LT5ZlFI*%9V!Q>A$QTQN;+4 z(5c@19yNJV=KV;|n{+v*0Qzh%f0 zklx81N^vG#GE-pgua3JTD=Wv-STWm%6pgPqNZAZhc)QnLENS~hHNf0eR90w11<*Ky zI?TgbEcxGv)n$`;mpP{QCL;4lm75?c*EXq~(SDGLRR>vE9$mo5L^DMgEi75@dO*wG zrE?<1r6F7pWO?^*RizWR*)B%yZTCj*r{*(e_IibVQgFzw>u zet|I8a<+Ue>9X^t4+ol;^Ft+Q(97cRl`yf@?eYuG{flX9L{H&N?T~P18jRPQXkE^J zKO9ZlH_Qg@yZZJ6s}t8vUVQBC7+r4sDBng%LY!_oR+)1{@SIa8q->*NsWWM1xzi^^ zMkFOmGvzavog+cbY~f55a+B>$H?bQxS`zrO#YFLMs&YSox@>Jo9(?c5O)7~@CH@iY58`fefY`f;qWfYT+D-&*lgzKF(Wd*TPgXSS#8>48374K{zvJ} zA!qOj^{Q*pJb!I2ZSE(yhc?zH-Znb_zvoDvG2lz=82)+@ya~6^F#6 zh^1F8Ar9H>(lq7>G2I)ZzqFVT(0uu!&I$(eov*blzLmE$0HK{V#TUzr(|}oqTGQ2u zBWxHv+-eml4D?!Xk+}cI9D3)wj0WrTj|I{S<`36IF_R<@m2DLRr_p4*A5=}wVW8OXdF zEAE_Xzd%}Eg$_bEbh5O3w5Ci4^}Sa3)nTI0E_HLw)i-%qV z_mtMQQ{b0)84kY^or_vkc?V?2^tz{w(}-KrH&W`i55z2q|&OYUA9%kn%4qkN0m}>8E5W$8mcr-nKkwMrsi_)I$V{S$P(;G*m=h^ro1#LFZiuG!V$*=PUYHlX@UMD|cg#erWQ|>_>RFXSk8- zP0#yI5Z~UN-DwMZh}rxc2&UR|kr8sxxHexZQGo@~^PwQOMK$-jvznUK``4t60Un|P z6l}u7C@;QoyN@~D-^eo!x3v{;5_HcaQ=g09sJ)9XQF+F-I%Z~x4j`#BLaCzT5Klc;An8##jw!AH!EM2B?hO8T$C$K&R(5=8EKs&* z?7q+c?EXUd@>wK=fyJ^hmjCFRaG;Z;(Z|?+0xG1o=XQX9z^p35V}G@*``%>npn0`z z$qenW5Mck`@B5kCH+4^+_g^!Xv5Lg;0$UZ--kQE@;OVtMu}UEOLn8}NLwY><_B_VRC2X5axyKebtki~y3i2Fg?wE|=C0PE)}20%j4TVucCYZ+Cp?%?^qdp&6Yi&+UYN#A`mMJYekeiO<7a-Z)mGP6}m$wGfXqnaK z10xWtond?QkvRFlruPqBN4*))80C~UJzm4z6w~Kg9`3WUWiyK=?V}}AF1Ot>(5jLk z!}EN{{Xz>Pd@Ai{89~c?NRC(E%T~I?{g4M01@PR}RwsDHN}$=PZp$B=)n2D|mjw~d zuc4JTGb)D@3>}2^C+$K@;>Ke;tt|yyBT}>57oMHH$$BA+q2j?K!Lkf+&*PiNR-avJ z#-8e~v%e8HsA*)9uL@AB8N$yhBER{#nsnL_Zw^tu%X22t2N8=y7kq~o%e8HFRc44RP{I`DTdN%L>Cz-YXrCL z>(Z^rzf6z!Qir!fApg*pB3BDiwc6!oe@gz8>r>V!THgGQ=|-NLab6gq8}GmoeKImS z0o8$W^R!TYI{((jESJr%OG*$AisACHsHm36_Oq=RYpqD6c_-?057b@QJvGoi56;+i zVwIsABMlw{^az z8`MKM1j&E?n#a=lt)??Lka6XD1%+hELlPd9;`y914n@k65k%2-s^r(mmHN~j1c=Qn zk9fRn5cPwppg$3mb7!}8GoRmEKSNkygokk7{di?_E2 zRy&uhvqH}m1#QZ=Xkq@|QeQ;{0aYHsKEHOo9qAw%bL2i^2@ubTArQv5uP4yb^k3D< zt~|H@`{Om;nMZks`}wT)*pyZ)!d&E3^`6LJ@$O5vcvx=2%GJR}>~Wq0cAMe~Vdhe9 zQ%WKF4d%{9jcl*#tJJ)#?hL<9XvzC`$Haaor@hdzFyQ5G5?EfB!QiI}x#w$Z#=vr~ zht_OarYnOKk^InFk+$C*#_+<-w$IG&51-)?E>w%+jv3aMtc%aDnB?{hwCat0Q6BP^ z##!^{jekSSX-$N*>gf2zCW(M<54;J;Ze*p3*S}1O4AF&G$g-8{{jy#V1ze*&(rhrBb=R>{^D+J9pda zb4@$Jiw5e_uA`{1APdXhU(f55^F-|*RBYjGd$9;h6%-yKF2z=lP>5eazUUFE)@<-? z#MHTJrBT6MSy6L0bQ}sno?rRRPibc@puCs%(&{&g)-x+;$*b_k{eK{t=s$U-V{#?s z2YS@%-&wATESTU#^73Tqvda~f4bi*h-wU9!=(Z$254bThA|PYlLh-|l_ISJ@Abw8w zQ;>bHxc>uFW{aa!Vp?afsKkXRPAzubUsc-fOMD)4ZlKVXo$Etc>v~0vPZp(G zOQpsd{5(cznlqU4_FrtsP%mrp(gj)*^YCzwEsY4dp*CxmmP_?nO_E(xxbQ!KXV&YT z9bMmlm_HUxAUR{FrM=b}f6Lp>k+HK$`X9VH&9yI%%D&@V{5Aevp-npGd}98W;hI;4 zbt|qHwKb1iiiiw8v5&ng%`#wlkk+u(PV&PO4@h;IleMlcmX`$U=@GY-eNmV)FX6@; z`_BA3qQGJ{EW69itqu7){L4={`4j(`4Ia&wIhoYcTxzz$Pc=1%`Tp2Xf)PTBgtYI= z9^t=U8=^AH_+u;JNC(#Xqji_WdlsOj*%EwoCT{`l$3Q<{=zV(mT3h#ho}L~kR5L7Z zI^bXZ^lA1Nv|alg^y7M`Vu;-+wL}^6b$=Q4@1HllpkZ~y3uPm9iLK1Lp;(b6$z(=a z-w!Gkz^62ei`9mH{N>w~&1SF*DB5iI#3O-`(H>)AH{QW^++Mwrx4oU{dc&c3Kyy%A zTaje}X=O_>JobnT)rSNk`g^%gbdoCI)1fb4FJG&E;XC95g0+;6B+|^#26)@x6)KCk zETQiI0JQkqH#MTW+7@%H8y(<<&ful_0BZquKGWN&96JjRebUV_iIqJMP&&vmoUt0Q za`*L$>*T?v21sx@mre+vPNRm4uK&gRI~@G7jCt6wkCtk`I;7DnedPuO)q=so+%LN1 z#A#y^e5%LX-R{uFw+qcW#$i&6X4d75cqa}ojDBN#Z9Kv6j&{Tu&6BDKtNDikBko&I zj4h?&KP+^aV-cEX2M7Cg)0n_`@+4&(ez(wU496^8FHKptH1FKf;RHz~SM{1`M9Kwl zW_oCeenUKu6%-$u1&fy`TcPKq!KqYso+j3uyUYCYvBgy!UMKZ=t=V7!?XtO(w#6c$ZL2sB{w+z zGW+y!=F`T}+T4<>tMhhv7FHatId=Z8_=OHRw_zorRY69c?rf0}l4IIx|43Y8#X0l9 zEVOy3=10?Dtrxvg6lyaK9xO~n+g9ZyV?)ktG>nfQFuqW3^gV-XRIuuBHe1s?R}PxT zRp|g-@CZ2@@9ZV#2HAf2IU$YYqlSbur>hShfT%CKwvRz?`Yx&8^G%jQmZf;Jy@wtp zW#~`QX|j*1QfRSvrG_@3*c|`VX<+eLyRqvhuHJtJ^b0_B6aiT)J$}Yun3gE+ku^{M z+?uG`*7-{zPx-5R0c1O@e73W*$!h@{zL4<2%yQtlWmq~LDCeE*#E9TBuW-SL4E68! zICmK}T0@vP(miv{Zcl?EBhOR)Kpc{{4+#@DH-{jxj+Y(oteF~I)hdQ`m#)a0nZx?R zB~44C1oMBP$P=XjD^soVCFOt+UUku#rg}u+H?HyePebJXiQArCjdK|r-A_OolbP-s z?k$pRJ>R{IruWkWnylScZu!I73`sv+IjuH*DC2u1kYZSBc7d)8hzL1BZ_a)4zYZI2 z#yE6jZEP$ZA?akZ){40$`Q`NJHi+3QyEG1AtLbSSp@$4x8I+l=mU;^&ZKzALg9wg^ z!yR(BrMAM-fO0U5cQ(xbR;GR$o3c^G`ofsgWXj#$)SXgIv7PYFxAG{RHm4=+k@UrS z;96TNJP1X}B&})g*ED~_pA_s?Yb?VU*dyRSmk|PU<17Bx+HlU{F<)sDKys{J>8=rC|KiGfxTVTqync2Rlyt1~)5><`nMfB~+~#s=j9B zqPDr?7J8O2Ttrv&T-?XoqI;&&af~3_(Jv5PdUKevH`@PJj_hRWXPBx@%QIIihFK1< zq?jsawn}R<5!tBl8x`^w4A^Dzlx%eS5rrvxsB+IO_ubOL0wmjT=NeXxO^K3>Rd+te zY#{;>_1aAy4p5iFV(ZSKeUC5vw!O_ptgnbY9@NU%8S*Nuzifg`(W;TnFWR-nZ2UpBRBx2iCXoAmjz}Iq6j1p>%V_Fd|xw&5l^=>9m*5?Od<2> z6G!w~Dc%C}EnK>wPof980y_wScdYF3$@$A|Mm7e<`%(DPnVGN8OqhD`tWJgS9c4r? z_VQkRbZK55@#0ZB9#t!R+VTNLq2#FlTm`sN%QX5|I=B7(xFR$fVpRWfY_YPeylrpQ z;cRJgsp~N9j_XVQFa2`AN;W-BbAA(?1V7Aw)@|tY&4zaV>-kFgT$aG^@w8LIG~KPC zqqor}G`Z|K_5>D;fyJIhPdprXC>#uJXZmdH(Ev0=dKlXUno3uf`FS@kc8&=#L zbbVOH)eH@U(kn*cZ#gmAp@{ z=32hq>N@ZU*%Gmkt|;qeG@66KadUfo9c3Oy4d0gLjyP%)5^eTE=FHvHE<((===E?T zrTs*U%)W}EA_m~UlpFY-^ppg$69A+IDXpE7pm(cm0L?^D^=%KP}Y_KSLGKT zpDJ6sj(vVMhR&}TG=Cusc|C|VdLyIBT2@7@R5cM@34FwJcAMAYrdO#=mY*e%%g39M z--Ql2L+nhAw|C)jOMa$vf2!o!?Qou0mpALtQh1ad_HlXwIr6e~s7OyebK8J?gbSIO z^u}gGaF1yv?peKQ`Do(p%4t#sq_s!Nf8iR{cnle($nd``uej;G_J!TPoLv1G&SvkQ z+R}8Svn*Pqn?y0Mp#96y(e%|jXIV9AZIM$C>I}aKNUt@nGO{0-|D02UPeU_zNO_^H z%j)LR{MDIrOQ_5V)xOn%&we;t!#o{NYkR{i?`)~) zYx^`;)MKrAFBM^VDHEh$t&t8Pf}vmgE&4BFSB?(C>cXC`M?9BpcJ$y#lR+JT*B>lz z=y1#A%CwF+EhnzS?6>t#eRaXR=Qt~xCBpsk6lN*l6hzxY$Sqpe>N=_GKYxF8V_J4& z?mmh0MoJbT25}J)#M>#4XF_UO7*S5Z63hExMVXfTPnGr4gR3Kfo3tuLj-SewojJKA zdEBTbZ?k&L=cf(_7vvkepO5XoiQ1{v&tT_pv8bw7)3wUE5UO>4v#<$1M@+yVQ{rBUn|S>KkJAaE$-LXw%7UOT z4wWMm4Z6&COGFAPWUnYeOTDC^xUTpBI2`2XvFyGi>%XEotI1UEX-v8S{G#W(tCwUl z?GEDMtPhd);iHS#?J31DR-e%8%<&yrzyGrv(4Cp1`lu?|WK&*L9r&G7i837+I%&H?n-Hm8Gy$xw*cBAZ9RwIs>RuB5_EC8!q?@+Jeba2C?r37 zU}x{=YlD`ip3aX5|E{J5IMQcWluui;>x6ILtolYYH?aKXErl|h$J0&(F0GiNnQiv~ z7hKrJeqw5$yLK`u=AT-(%2SuSJeZwXvbYc1$V<)ORub0MC@U7|fi;7*QS4VRs3vgb zR{UvC_fNqlP150q5Z>}%_4+v*+=e-DEyS4%#Ke48b1ogGx*>3A>cLMZZJpE^ zHlBKT+85&0<@9@CSNe~{)}v&lA0>!?2A2)bo8Y|vD(h=6TkaU4ZmvCOw_^Gno*e^e zZ^J|Q_Q3r?TWd>?xV*27^Cr(H86O=1`1+qTCuQc-5d?pC>)b3*r&I*S zpl@w-0q#no>g%u^|c@RXjV( zV4L@;zGc7j>*38J?t@mv$2xHlYvmd#H&pl2tnO5DR98)^(sK{0W{Y{fRn@$alZ0A> zt+f}us!sUp))%{5ovyew9&(g>x_bM(_4z$b#1A2Na_(E9*>zUIU*9>D$z{7al1jL6 z5wF58f9*ONte@Y*nXWwMt&)8=&&%t_j^?BhJ#B4yi^WyFq*0Jio^?21-<#6EgVSl3 zM6oI~-XcZqGXBFgA#kHW51dGo>@FM*tmiGQW-LybY1>KhyNuG_*?H_z-d?1fOw-_i zlQN0(6n^{4=Kr|Zex%Ny|G+;K}VCuSNYgh_v%$QEZRNaa_{*y^&e~ZA_SYuwmy#* z=cmvFHldm|fSfnbN#Y(aStNF)7A}2{8&=Gu5D?d-TCZ3|gf9ggw$9JXC;tcdN{tyg zsobzoG0&wCc(K1bx%~Bc?FW5oLl)T65gy^EC7*1F9>(Ji1jQ0eA+EA7`1nAT$F8W| z2#(Vm`R1)A51#(Ml@Wv0$+EMXPHSygN2RSZZ6dvB(pv`=E7!*jUYGA_#TtMecz=A^ zBN8&(MSD>Gi_qeJH!NngGIy3!<%MSH5&~-QAAsLlHeI%M{k$c8gh;XTjg+P*tJ9+m zqou9n0+3{Crv8wOQ3iAQ_kc^VnJYA)95-fK;*iBns`lZ8xTdosdHV?Gr&4pGaqNQQ zp@Uy_*>IbdAiCQDyaoCn!q4YO@Y15;I$3>HBOex8<|;vyK&Q(He{|c>(C$-i{iI9W z-ZuXvKfoXIIup@vM`NNqGMHmxQ5E#X&(~5v!kbP(#e^g4JqRN-DI) z_)_357_kLg-aqVb1RG=DMk)9$F?1$UeABdv>04HmRZ)}-5CwEqEqe}O7+0+Q@iZPfJ>Z7#OF1=S>R zy0t-*2QI_B3X(Xv5w7z0`@%i=e}}X^w1(Qc>aSK`<68gz0|0P$jQ0F~@7e##{f->; zK9%ycpqyIo;Zx@ci-RleG9f3t7(amUERlRgJSp$zekV&E=+P3W!Q z3}l!Vu;o0vd^&V5i#pI=ox`b3(@@NZ8Lc4Su#1KEtJ$^y@&!auk4glz>K9OPoycTR zD2iEbk%o46cJ*Xa6p!^I&|T4eGYx&V85wasSpW1xv3Z1b%E51fLl2$#65_}y*aALo z{V2Bj{{Km9u4amXs3KOHDaV7oVe}B32J1jfvRhAdopPdoeQ!O0XYoTd`_#Q(2-q+p zcXcR9dr|Jel}ZrU;Gb_Ws?wQ)=vYQw-Jz86%w7@HotR- z5K?~lOtV4q{wUmHEousPbHDtbP4XVsZciepI=;wE8oUX8FR``xJ8U!61&>bsJ(JKt zNmR7ds}QE_V1uaW-~gO+@?bgK;Q!@ispWA_Y^a|$FTXW--OVmAeo6iV zn0L&GV)$sN;yL=YQ}@yWBr%9yKaxTX(mW7-A>FSwM%RBUfBIMO0{`I?KaS&H0Cmhq zC)FntGxAUiuSDEb-*lgo>`Fdh6W74}1Scv=4&D;e8I_LcmJUEGP-L0>9%34nz0I?|C*jBj1 zDFf~S%8U#4eP6vb87T?up0hOQ$k^Z?1(guW4r-=l+91Tvw(x!&`V6U{2c>>Lv?ts- z3ckF)4!GD|ihRk%8`_icon8<*y@YLlmKEIqbI{mckjL~*P`WPpOyX7pwcj-ry zWyTgfVva_oWJccni27|WAqkW$Ayk>EdBG}M^PO{YdDtM>nI1s8D&T=`^HCt_T7Asu zfXzAdGi31XcV2p)v<|3sALE5SIr;XKZjeMsZE9kezjS*O zY>@S`pTKO=#Y^^?HpfUWt&M-iWx0`hA#z{M2!`IY1HC$HwaK%8;Y(#QIKAQ&GIzHLg%{BO>bFRu36@w^}V)*^Qq9*Ly zcswvQ>PzBnymXFNa(GwJR@!tVPapGFYVkJ`k9ytl1--+)Wxt+Fj`Z!d;2q$T>fWuoVPN!^~Fau#~dDw!yzFutV``Qo8s>ixcfXa zNEGkM^t*fr^;^%5CjnH>;-nbm4r3xZWl8A(P8kLpI>1+%&wrn|JAC3s=?y(hCi{Jk zThlboZHQ_6N86R6Ox73!+9a(+&S_;Xrf|MPq=RExI_J#rhJNI85-R=Tob^9vFRv4K zBDr!_G~*19r^L{r9uk_K+6(WyqSE5HCG7`sjlO2ei{0iUt(I+&?y}JpGp0Oa6|ADf z&w^UKnMk4;0m;<2NBQ$_)4ZpDjME3Vc`IP?!!Gd~a1Hkp?E(}>o@&CPWl-)DYCJV= zwXKuH^dX6d?;m}(rByZ*fCmK;h`3KNjTWHRb_+;z2c;Swdb&GO+e9CJ>KHnjejAT4 z1|Buy%P5LDPaTQhx>oA5(wq8oWtXq-Ld9nb&|D+jZmzK~Jp4_Dq0 zN%e}B@|!C81<+UA6-}>mv~6D89$=GGJe8mKco&`XQUgD2FCJ$+#=Xfvy&>DnGA;&n z-&t9K)q4wQ@jKC>m;Q_{4+6*d$NVzqMRisjZvS!;k0o30NML*QvA-XR)p~wb5%y4e zeB?)tbyuOvd0_t06_WHkAtc!k1SyZuCkN8QWe0Y$ zh0)u05msRe_7ClZsosnxQ?UJ^)$g~MA&9=IF#7Q%^NFGVjDC6DiD{U@e;Ypj*?9TkeGdOf)gim1iE+Un@ ztnSUw=>%CpuJHj-#q%2(Gs{|}((X%hJ|$Pwz4iQmfVqC$Wvj$WjL2w$gd^3#puhOi zF_E;$z`K><6;bv~80yF4#1;*{u6pGEn7|#qgpdg9?DP7zMjW9&9>d-E>bJkd%1u5S zPuLn>AKtO#$J5pi#O2TkYaAE&W7ZsUAJzrB^lO80e0CKCcnE^2dVdS+=6};B^}3ai zpcynz*OzFUzrf&zUg+ygg3O!$0N{Igg@$eClVjpbzA-K`V5Z$jd)yd6EG2&%wCwxP zAwF5PB0mTs&l3uNnV=;Lhh{6%cK;Bs?U!LgWBxKp2}Q194DeeX)aW3W#VP(swDwMU zT!nPq&R`Qkn{OvS%N|1L03D9xr{BEf1#c{&{i0@~FMY^o5IUa-7;)0~`M5 zGgMw)f_DCy>*Z@bowsNV3>$9kH;U=~YpWKCu?>14!6d+~xGXe6WEubhjR)%9T4a`T z>3`rp+CzYfak=b9FMro8l-?7nw}4?<&C&38hrEBMETWR77^N2;RvW~3m&};=Na3Uu z*B5Y{@8o;N=G03NT!&-tF5qwMe*m8+T5go1rv0B4NOEE#EgWJ8y(O79(~87xb;XI< z>X#56nESTgA!8ZK3^QcV$J_gefC6>)WMc%cd4O6Ir?WKXSD^kd$q)m;XRpW&Y_RkF z%uTiauG19@*CHLB#Fcf`&zoCY_wT4UDsuCF;So~6h>5@byR7f7VlND+!$bc`HqX!O zEeKXR1>SQ{n&b9u|Aky7GK!TUc^%c23Tkw2 zq+f9MRb%ujRjJ}3T=klomk}fqnbXyGxn@uqz6r&yR&kFYK3{j@-plwIt()GbVJ3$A6X3C{4~%_xIX{SfzV~_ zx3^CfO04|OkSzXLkK$r$F-QA*&zF-Nq|NT``lqM-x~)h5ulN^;d&0(rKTcO@zj@0) zQK=a~SNe&?hW>@!XRM;0+LM9Ir(kV7v;P2Z?h}eN7#2R)Iu<+~Sp399WZ+XuyJ4<3 zQf}2_c2rZ%=o+SN65q zXz?nOG+wkE=ofIj&{l6BJ@Ibcn5)0Pxq+wHxoGQVSb#V^w5=&Y%>f!0>Oo!mnQ4Dr zE?3@hkK%Kyv#Nd-W=)E-XhNFp;CXB8P)}%V>L}Qlfk@r+J9g;2Fb=sQ^(=ql4_}ikT`>Vj& zD|0-`FjvJ$SdE!w{4GDS=7pQ^BPU$V((2DY8S6n-xAVr~njU#rS?b-<$+2AJifl`L zEM#`&@;3Ls>HGF>-Hi?zRr^Xav-$&?IzC_&9uW1Es*~1PJ8JGh>OiskuE*f!Nz;H) z0o5{)(P5!~ye_MUKYwrjB7*oD`uG?zc3khbHWzd=Tm8BwcGj!wRDBg`?pi+e1@7|r ztIhOQJh4U4_)nvFZ9DuFt|^IS(qN1v?Vvg4p10b3|8-a)7OBQDb_+4+sqYNEM!w1x zKVOJBYxq56`(e=FLkPEfD5P3dU4K=(_{OgXv0|NUP2(r4uMWoPON{Q3x|iHb%fn5* z>V#wcoD$deRh#$$n}X29a*{t`5EB){=iV`1U{_RmQT>e>(xOH6qRlv_&$%gmXuE|& zd!#14^aZVGi#npz^86pnd7u-D;yCg73D*97klnnOiZmZ^%upA_d}X{DPKxTI*Q(On z0wJDlH{n~+i;N#+Nx&7){1Bc*LiH6v({O(Yb&s& z8sZvMw$fqyQ=R8BF+=jL@c2+;7m$0sA1B`{@IKG*%DY^CC(NX-ge@&tUE*;xZ$88no$+9N2(k&o6`BKeH(@n20NFPM*DgZ z&gQKF;Wxj+u8ZXperhDiX%t~l6&$Qj&;L$7O*01?yM3GVYg8h+SZTQiM z?ETqM%6PZ(go`5s@M|$FR{Cc*mybGsZ~^%Jr+}_>e{<`42yhz;7-PGpPNPjW+3r}7 zkwHXzsGIFvb7t^3U!|2v(g(iT%e%Q1rL-&po7dNBwSiW(mNb^Eb}{yKCzK6=6?ve! zjI9fc6Yvpa{Ngenq1HBi*BH?!b<~|CJZG38yf;tYAm{MPi99J*m8nOfPfrfbRzPyi z`@{;S#V2L$ZhlX9c8U@hZyzm_En5|y>*7Hl_28b%9xGD@;pjlu(1B=UW%3rUkN1M( z2E>&1(zHY@1)J9kt<=y`OQXD=uAr5nEt+cfiTaZ!c=q<4$ZtLql;a?^4C)d+WW8O$ zcsu7UxB9iXweoiC8Vj8Wy`L}Bb0o%(S}5)>l9-g0Yabf*9&28>#)g;s@E@RI`)JVX z*)!e;q>>Cb$)#XvT(G$!sPb@o5wUQ+cORxrFE@3Bs~)-S-P}o8x-o24cZ;`f&-QG%Lt6&a9xxjQ zjrL}4I*Ty$kVQ<6dIcRxPtvFpWeIWS?TJY09dtMC4at5o*}#q(4rd2 z@$L^6P2_-&|N5sj+Flo?h`Sj{d|6jYZJN{CoMOKoZmNc6tz(^(*>Sh5A& zJQhSTEwi(80MGDmnIFDS2=&=NuTLnRPW%V0OSjISIcw~`wL}$igc%z&Al)qO{;j8LqFxy@z%B zhPR7hGNB5m&8HAC?emZ`{Nz>oJ{6a<4P&*@scB%h$S+7)YW;=@Sfp=ie zw^C!S&5S79|A>U#Z?$Jtc$9^fcXJpL$~-7z6QmP)Z8ur%Q+*wIEPPs2zhFnmyzVY- zG+ankHLX6*Uk~6v(`}yaXgS|Mt-msP`U9q`O}F)FQcr#m0O*D{cVk5ydIKN~b{G16 zIWlpQ$f;=ATJ;k7{U>Q6qq!QxOP613rt5$IM`ZqoIR967X7q{R0!YNTv`jD+%Clel zukA9|a)EmWbpxzCvJU8#_2L*3S`~x^8`WUOw%6s<#Zo|iK23ogNiFafSJ{U+Baa&f z-Pu|(tf;4ykh$2iSO(GLz=zM(+I+QMJzu){LnAu|QFpqSN$mYq<6NG{xiQ^t1bG~o z+J4r)_rLjX#sE)>}5c=VdAjSLCi{XkqT&f*+|gS-xZ z6!eRnQPnH)mtbiM@BR}VN8#6WNwUYkhsfrABCV!VqtbHUZ6ynKpjvWtLOc9a2p8_C z{h`|!CfveLvPH{vWnt2GxvNQIod&yGgS?2jr3l5dnZM+zom#j^tc$v<`fK5-5NB9V zAv6_|;`b+&Xd~xEYGl+0-nO>2zqVM5iUl|D0mg$Z$zmTLyi<8&TWWo#6XbezlX1Rx zT7RQC#*pI1+D5S)P^vjulKABbJx6q)%e~@%1@kJR2k8^fieVUFp)R8Opg}q9WBr+j z@EgI(SCa0>lj)~hx>&Z_D?8m^MMz`zT|Q^tGr^Yw4;usS z$o#Sw=PW0dk;R-;XhY)s<_)<=_m4eQAe^q znE&KiQzG$#(CD3{`-YChm!7bzu~vnTK*28U$*Gz@5Y_{tpz76m&~NLH9C zHpb^!=X|~_D-8*qv$WOM%WTRG>n5(oytBe&%HUp)<+`MEx89sKkSLo-k7{H{Oh#N! z(2vC>@VzRo-0!w2sMCkrTw^!=w#AnMv|AS+KgaBvW$1}4K6O9ULs|u#bSTSTq*d;K za(J7cj?v|28}d9k+RNZylxBOIR5edU63G_gtvSAPv1fgg1LZj!7SXC3#~~|<&5rRZ z4IAc=bT&!;^5T!XEybg!unaM$imQ!{EG%{_AQ3YBHNc{34OgtC_HliWtdpc22+q97uGGJ9Y)7*`qof<04yy$=m z<3GPR_CALmUQb2Qdg&>gi0tLpf{j8v93dZ6n(bncsuwO57Zv)S?c4rnL`rFu8%2`E zQJOnupkgeFY~PXe)aR`s$}S4;)LSbh7$!@6Z!2o@@>$6iS)9`~yG4qv1Ig>tejc5Z zSzjJ{nY7oA83BAX^KzO)-8OJl=fLxRSTFpRWZ@-#k#N%S9BlwB*};yJ1Li;Cp4fX=;rb5QBPkp&)lN zH`^}Vjx>57x*bb43|Cn!K1d`#$>(-^b1W@i`6ds2Cv39dd9|%^SMH4LE&?6;A5qWA zoY8@kd(*0^;L?r+`~r&Im|KS#{nKZ+!(6$eUc2}@CNZDCSZw(H{QhOkSiO;ds1|jZ z411^@6L@l)UHql-bFZ`+%Zi;1enp$^X1_8nfl%(0v!Z@ zn3O#bfV*j5ZFlc?nc`dqkNj<{$=(__Wv~r1qsr^9YDJgN8xM+Q4h(sMmrZ^}N;Z(Z z9UM(nPq{Ap%`rL-s+0W^IsU@Vb8Q2e5Ddk2U`p&_&c0pV+1=UX^H{VjFQ__4&ULI9 zzwg@Zd~klxBhb~+=sBp5;g0kI=lCB>>1XRK3ExRCk_a;>vCX+T8|B*pQR5wD7Zq&CrFVGyB%+KJ|HhM;kqP@_rDfCm;OpMq=F zPww^~FK{PzesOpeHgtEa^v5O5v#$ef$I51366KnQPXi}~td5$o)h&xFsB_tMvST2ViiXndK z^eHm}y4EteeO(r+LDS3bqAO;gS^Q}g9i>n4uzn24H#+qoG2_zs`%hWM-{WdQZQuZk zMnAKJlKlh6Iq{aUmi^gD%YYenoxDnIbJ-7QH@O+KHqImC3BCsJbJ5=4!o)U+;ns?8bCNd7ss{|-eTTT5OuPR@skm#&u8RLQR)D++hbYWX4 zzUsFZVdsy3;7ndT4B#onz=Or4I{=d)Owh%7Ozle--;8PNve~Rj;UCs@f8(5zM_(Xy zIf2|i&4qQZ4b)m)KpqF;*Vd~;OepDe%fS6^tZ2)b&fYC+pZQzKbTm`{upwM<#YBld zrz;1`iroU|u2#}Fjc5f5zXf0&^S>={BC?_en=*Qz8It}Om@=!=v_tex*_kHV$mO@! zZX(L(LavZTmuPt}#>>f64-hoJ39=eTpnjWc4?^kN2daVGQ+02=J1XDMM$|~tMH>>I z53=H6VBTCeiw+rt6ArUm7jIRll(6}kT(V|qt$pWU#RiE%WT3|oO&Jwbg-;O8(H^@dY_$PkSBqjT3XDj74^-a%&xAOSAfb=bU11!|a449w4pWSctz_u)4MB7&n zuYpgsIY63u06loi@c4g-$BRoCQ}G4s_$+60F|HIo{S+RP7hdyvn<;vZL}vp&HU{aA z#cR`sNW!N)t}{`78%J60=~bptm4A((m&V0^%fS9f-6lB7KeRTw!cCBzDMMgVrsi`> zL|PR(-RL*w6Dmlq-zwe=Sbl}Q2`(_|owb_!j=;`>o?Lw{k@Oi;e9%Toe%~(hz2QVc z!roG8s?TqhDP0?PxDBLkJ4p*7+l$5eT6cg)7Vk%zn+f@8|gRuc3+Yq*hdZXA1nQ-u=PNYhBs$W=mxp^6K(* zUN2ug|C^E#yo2imaZ3STL(%jiehgwCoMY8zmu<{eGrFAI9aET=b#i4|j@Ox%#`Q-& z76c9bw#=D%w$yBcyX^?JLk9=^Tjv)G5tZI-cdJdkRB-oEIn)P>pHH02UIaRDUdmt( zxBhg})-kc;8I@;W@g@CAlvQ%rA{M0c?*9|^0(&N=074b)0?(1Bm0qnvh+WayB-&iV1VzDHkNZxJhkqf zBb@6aPP6}mNkx3Gmd(KPRNuvk2{S^F1YmM`@Q*>6iQ|bEZL(rs5%!3uY-X`tUpJ%L z$_{lK7honmJI(kufjbW5dY)QStdLk1n(~9@`%pP4`%;=_{H z3)k+7DvMuh{4NZ=Xtd#&dMo%VKtSeG5b0NMar5=)wQf2Y)4^G+yJeTk=3gZRC1{8F zO&T}+Jb(;Y2ss+rKi&4ifZo+sS3*%QaK&i~a=$HX8t9E3TMar2{Q85zgl+ZaZkvM^ z!V*<}=Q`9*=YOur(=*?>%xy@F=~3_z`-@4|3^5coVS>^4{^!OnhhWN@tlNjR1KM-8 zPka*EtjxCbZO#b=K);y_Rzj(1wc`50#3JnD6VRUvKMj5xAV&c89lfFGIFav^4j`h{ z7A)6v8Q9sjYsl?jBwriWE# zzU6kQepDbU$2uOg{>-f64zjChYZ-^Qv_xGuwH-^jt6NE8DZ|dD_dO8d8Oa7D#2EpP z^7uJ4)kyK{TzN_V&NuvWw%rIACt(lDZq6aA-A1}@N^cK)i6c#FT~BzzbRF$&`IYWh zxaX5zG8HkrIVLIZy0sq$q>ug!ENZf|EoQ}mVtUUY+kEU zRCtfyXWs~QC_H4^eVPAe>8T~Qco%lojL_0Fi5R*=dJCaB=9gP#^3y;;XagJ1PUqi7 z34Kzwmq5a-&u-u2eh!YZM%BDDBqsW*Du+o?eF$^f)-c^x2!&UCzb5BV?MeePUB@wY%_NcMXp8livj z8e|Xa5mVTaepHY*8W2zLO>- z1@F#Zg$I`sPR|8IQ(fp&HI%QLTK+a1@lXDTkdjn)w@slFyJ#V(b3E3ZL*1sMUG5Hv z9H#K?Rp3F+U9SGGcl*AU`;PiVgaH1b6+ek9ILH+d8?!_X78l%G-JNFD@M>dzsgiS| zO{kGvf=~b+VS71Sl7y5YzIxW=-Ae~anynSomAc9tAUL0E>-n&EHWMk=1Ek8w8-C(? z@GV$w!t?5EAiUuE&z)47tUH%6cT%u(N`xr;S?pI=lJDmRenosf4#_j~D6ehT^=Z#e7@zIFI^HbD|0 z6jsa8GyIsz^JuzKspCJQ6jg#jXJrmN1bs%tdQ5#!Rr6Yolr8T@LiM5NXsO@kUYG)v zuNGGulkC~mTEs&EKozr_!8Cpay|Wx~=t|PZ;iZ{4M8Pt6d^J$u%skD#5wL#z?!NPT z3?s!;2Wd^)Z?8UfupSZus;8;`Hx#|#zDEc1;YL07`acx&z(EJtog&J|A4iG+^Q(h~ z759iX0Na-$g>Ci5&E5Dpe@I zD)x+vHRK&>j#Qev<}v@cszDDIY)wh_DyfM`Hj#6lVboI5 zK2KKCbE4}GbQ#gxqf5Vs80lc=g3 zRI-R*Ia6HU|3g%pF6d>z%pU5sg(6~SZ7a3XZvt-2s(TBMHm;d&v-3em7p zMKWey@itjm>>z7f5vA@kyv_*6i&eH&+41Li(^v9Z&+MSasU}*_`~`_Ecb*M~kj1@u z#;cJPMfb@pNn?E_W!#m-9H4m_FPxxm4jZNkXYe8zOV0i|P!XG2@C2G)H@{GyH@(N+ zp9CJ_rVb7KT}1BeloYJ2E(-IskUo?y8nEluX;G2~6;X_Fr!*x#(sU4M`Vct`I-D%L za_oB|Of&2&+fVd@=baN(%;-=isfzTAA262nytDCp&;F3!PSshW!ZMk=0Tma+mCyVY zX^DGOq}BH&2+lr}Kq%SM7x|dfqrx!AJ+7nm*$ClZsq6xqh98HLEdGX#4;IsJ@T>Gk z_Uy|_65)+gI!6M8sAt;VYmBS^%2j0-J=`&eTa#@|#1R#DJe7F_O(DU$=nn9hs0e)r z6wxBTMugA~G84bQ1I-dY=5yQNVQlgk(a8v*}iA!1#RGl0Df3irMqWB&zFp>XVC!YxG$Hdq5R58 zv?NK&^iEw!y&NN>(yeERKh^(_!;y1|rBl6iO3CVBZG14Gv?7$jbhA#a%|hjKvy%1V zvz&lh(_W5KK-w7Dloq25wB?nY9K|b6HhN7tDh;LyMoCsDCucgXM@dTcKirtKXwPnC zWRnZegZCwV7bUyVY^m)=Zt+LGZtksqGO_#sXM|LW)&u__BZDq;GDd~HA-5pf-t=n= z`l+BUF<7*oUqk@G2(UljlPK4BT8by;(&?Ero%5V?KIorT za{BuO$LM$RCts_J7r;NIxxc*rTE=$QIBDq<_L+SR=)oW(H(Kd=FjGIZRsiyfK)%mY zTA1ZP8jF^^o~j3Wt6JoL%MO%Xln(wdFA73`FDFw?+_XVytixdK%CLsLwOEL83vvIE$Avyu&~EKAq~i|EPy$z3}_GlmX5pKMesUv`Xk_rjeA$ z9Q-DOqGRU--_C0orwULWKiV`eBlcC+ga-}9+J3)6*ys9LKO!P z`QL9`EipyUP<_;vLvfK9^e}Q04-pvE-u(hSKWHKxQgMd6nY%N+fV~MLpr4ysW)8$( z;(p4=LGv2atu<@EL_0YP60LlCST8%^A=fn3xJ%IONi%Y|E;_LA>I5LSBu82scINq!D(Y5*uT z?=3ztN{GpIy~pxxkRBQkVraKY=wHgLK;!6XVhMkOSJU3Yz@A0Xr_`rZ?^ygX6{8I5 zEqr<7eEYdf@VMCR-Gl$tf)Bb=G0|!9U_wg3_xi3S1L60h^#?y+F$~+wcgVQivwqKZ z?O(IQyUybRLcn&flYSc~E82f2;>D@o0h>WVnq&!&iKvXGiI- z+;*oJ3KZI1co=%=b}0}zP|A|JW!64@1w6J>;FQH;zdo289$@XC(TdO5d`Sut4`R8I znDnj6?Jjhwn3%<%LBL75b-M?*FwCuk=%z)fS%Rc{)uY~hpn@#11pHGQsbc8*N9$zW zD-QtSxZ0Tvae7I|i{^FMW3`&9ujwZv_x_PTKKs3XiGxKR(MA0TgY*RBD7UiS^2(6q zIJGVb{|Fj>S4~mXCD$_zm8*QEU7V&TVQ9Du2<~p=8Y_X?|1ggWa9}cmOBgoBn}#$y z=g%fj=rsFnYIoA5!mCM^8sgtJCW{)cn0X(Q}w?pBzr8sj> zcd&$=i|dw?Ta2F0yL*hH8l+rYD;FVb>o6sWW_Y6WVt0zJO;@4+-jx2vfmen+hfPac zT~Pr(tbG@d(RG83tFEYtsMD@r+{F9L6-0VtMyS|g@{wStL0c0i|B}GkbMTXV7z~=Y zEc&h~)zr)yrh7hx108!zV)#p5!}qI$owChHXwgyEOS`jX}9 z6r6Sf-Ps`f6_s|apqo~=8bZ8K1Un96Kcb)0!~K*0 zuxwkQp|#=nd6H0ZgXPLkIX8f&g$mJ2Y1|MoD*SU$MCS@JXvcVGYQ)_*s;Xbkem!VN z>~mE}t!KK^k?qn*=ty7x7WC+`Q%n!Paa3DZncGV#<&5%0w^qyXspj?24#}Xoen`wi z_b0yYIjnJ3%ddEXuvi^#r-%6nf0h>*C>5QopttmYL9ry?E#LZ2jRxL~@NRLd*bMumdxv}Z zDF5FCAi27Y*E+M3R9DVIb+yKHn2PVGWi3M|wVYRxBWbS8st$-Evj8*`W`a}-53>3K zcx5{)MVHo;HZ^OzKRnS53y&FgHdixbuszu@ofMHP)Ul`y?7~}NL)Bvfnd11D^u1)N zd6k{)8SZLu)F+1r^{iKiM}ADpe}dN@J2-c^@F>T1opxw!UF@*^f{WxCXNnU}YYt)@ zX?4){-_&h!Cl3s1=Vs~G`HA1Nsg()F%cF-E@7bT>8SP+vr}v@fV3#Nx6*;izij z?QExn|7@mwsLi4lmn0AZq}adbp_u)Z=`l&~h(e$Y3znoiP@#fiUVHp#8DG7fW2?X4 z5V8YCM4P;@&=ZZTDZ3}LXU$u7w?sh0w9LgZW9Mq21Iyk6OO)|rXlrS0*O8nS<&GoI zQuGyXN^UMWCjA~;7|A_c*%V>y5)e*l3vh6GsSFI)8g{RkYj%BV4hYj^a5;c z+5vZjIcDktmkAOdk17?VL1%N@V7+{8Y_&y4tH<^D@d3bYXz}RqKcdCn{$!tupw~Sa zo?}u2wJrg{4xX2tI_9({GIUmrna`t5jYLVjObrJRCaX^aznS`U78hUU*zUMN#Jf5c zM}R7Rmn8#xJ7o=dsp;t`Pa7uV3g$L-DL&?5pJW9C?J+n#{LQ}2)FObP=RBlOdJZo0 z)OgXh=-j797maiaG}QNwi%sj;8y?lJk6rQl9z)sYz)+SQ#;dLpf`8n zbO{XUYEQzwoCXXhP`B8OOuQB!`%@` ztIDd#pF@$x{pxhla@>#=qs@*XbaZL%kX+t)*H0Q^My@kVTkUOTGjRKwLYSTgR6RY z5~SsAbIZV0dzZ)KiX)Yrc%_zE@#lsQT!h~&PKkqnB{N`T*C{GsR(SWPlbz9dR)QXH z?uOT&e9ZwJXa{$h=!lM$-tgeu%Aqze<|+DD@_fucv)%(b^O3TPNZrFGI`=1jT!T{3 z*$RmrIxA}p@qtI*QvZE=PDkqfi@vl%mqIs@eQU1C;|+CVYbKM%ubsqK?a$^5SXRkW*0e=C|70#g#@x z%sK?CAIruvx&N@8K$o;+93~>bsyVCP|5(i;EV)GBlQ+)`f9{ev^% z5gMn=y_5vBAkDf{r{63rh<1#lxQdLI|f9z!^RWj z^yG>FP-@9t=&(IE42g*#=4%S zV@`aJOPe!>bwb-T&LPytOKcX<;V?k_x2T?3bdaLt&U8t9pGGP*y?{YAzi};j(_g3i z+7do)HkIt8*Dim~SY6(B&vrlCnbWYS?@XdpqSOP$X#PknZF9C@wI2GO+}QFry~d|Q zFoKR*XhR~bsuN=d%b5*8tHE_>VltaDg-tV~EWNCk=4{V%%RJay4}lNgduJz7A!2^~ zJ1MX#Cob#D0gokbtmoUDhuJwH{g^0%#I0T zfdP5(^>=C}8#wC&MUDAH8}2f~@`hbM2u}Bz=uTITjc(6{K^+(e)rLeZ__v!kbt*t1 z*N_`{bpMCv3%-88W^ET+T-3CKz=GV{8g!2_-A+|;P}F{UOXknQrvHc{w`_vc%I3Ag zkF<9K6@3PIv$&P*zc{pIXpVM&FeFbzUw(V^H8(Rb()7{!MXVZ}UJPVa2h$R&AaKTw zUQ2^3hfCYNA|(?h12rZ31@xOkd_1gwokqCxmn*HB)0E#FiS?pht?N!V4V{)-CF=R! zI*@J*7}6ixe0@{s@+{LiN&USk=`+FaTP;8BS2Z3k?WnV%I|Q^}ba*)Cn&Fjv) zHn}pLIqn~my?TQ)3p((shQy7JcBecL;*eze5?zKH*57FS=>9!=wfpW zem6Q<=b!I zV>wJ+1C%{ckC-t_hRxxnuiso`;yuHcZSPzxfLB%MUAsvR)yp_Bi(4I~ zOKxWJ=idVS=o;PH14gw%GC;H25yP0rfH|UnW)ktAJ`1Lz(<;ZF#v5mJW6yO75F4y) zVRulh!tis*=|9YrxqHf{M`n)RIK~r|kth)24)K-`SjhPGYfV2#x=AOf{9gw0;fQz} zK4yO%wCPaL2~R8Ry(z%()ks2@FVAqATguKaNhUTR$JBj3nZ&Aa{!2IzrMJo;1Q?wNYd7l}nSuSMH9A@nF<8<(xGof+_jRZ*Hzq(&c~*Ogup-l)hRCNJ|JK#&^VBwY5C+rFHyw7FB*+LL=vk z|A=f9Ue-Ggy(DdjN{nw8^0hB75%jagaYC0sX1ZQA$T^J)9XXrU+xT?CLroCM!J~jr z^;rJC)aJZakl_Qcu=H?F!#SLwlgbloM@2gO8no!!e|8%#(R#Craw{7g|8=&@#d-FV zn=-))`q!C(R%#V>BZRPDQ=3yTYm>{0W0s&`b@fv(sj`FC1vqopW;1*z$7~thxANFa zZ0cAOaE<&TEHbr2`-`BnHAHep40dX@@Hf=A5o(Deyo3d{Z1%}tH+N)XBcj?yrhB&G zvDW2ac8}{^;-h=UW$u5hy9w2Q_?dY44UinzM8Gjgg#a$Sa`%G=u{^A^Z z1YP2Ky?c@9n6hL&!dj%kAYQD?L8tpEpF#ibMgCg@bIaFZI-uQx?}7r}D1%|g%q>`*_x#WsA$pw4SXhXS1*Jni@sfG(>4<=)8H9GA4&eHZGuf{SQP zNJk@Hb=`*<(c_of{ZE!BtgOP9L7@7hw#POs{?V8K*UNCH^JSQwgSE^bU;DWTW9o!o1zyC|;JByX(w=ItH1*!IDRA0DV1Gfwx+FSo- zIeq!jHiI@_ZHP}#OW7>&Nt#xe)|uXwS4)W1@-^;@{Y^IqGUUM4ra5H$KO$vdmvmTe zFj?LF2e>$8n=?@HmF)sZleC`p?UZox^v0X7DNVto+-5VY?mQzHkBav044g@GBWeYp z%ykmx)K%KKaRz4Xw((y;hHi(#KA#~INnighogChgPf97VVH93ZsQVV^^He77@jC#m z{H3#0>gI?0L&rolOdbm(N8%+(=3iP+a?_DMvqSv~9j?&)W+Ws8GuS(Zc@^wG9a)Fg z^YdS8EA7fFO%Jxu?P$r|Z`Q3~PdPxit1R+bprPSZp2GJBxb)nLd+G+!_+ zD)_M3xRA@ID_?R-H+WgeBvsB0;l{q`elc=x7ZrF|6#FF9k%28Jd%!X157}azw{_m= zy{qD#mo~~38pcQIHVDj~q&}b&2D7{2J}wB(|C_csTrDCt#G*K@gAhsE?Sjs>=+4)F zIKrL7Pg0jxR+lS3oQHx;oB;Kj$>YwS!dj^`$x0Ur$lqx`2J2;{Tx~KR0Np5?R=faG z2#CT$eI4g`rCLSCkd@h(wpKN6%e2A6#w>gZm3lkH-qJ_mWw$!k_wo^`&?0i{H5;d) zxh{JqN%#Sl%AngU!DWj%*eC10-@B+?W49rNsg6jq9YSQgUWfZ$&6^7tGo$VVsqWvV zU^AXgkM6w6{gsv4Q)T!C<+}ctaQ>-{*=`PuuNxa*cEYrI=j88K_txKA*g1M??EhKc z9K7?O`BIEw^YnRJ+PPoe<@y+(^p%2S-JHI9?Ri1Pd2%Gv7N6wm6H8ABTvjnGRk$@y@U>uSkoFZv_`lbNM$%0H1!o9>*# z`1MsrFIU*Pfm6b9{4Cz{6rmH+y?^Bw9^dcZRZdD zC%DC^maBljzBZE5G8m`L4y*V}cyL*RL-fAZr{IR6`x(m$U#3tFCo=oJ%Cjcez=`1! zr<$5}3tL>s;`u^pwU)PoZ>gZ{>Jot-0g`RI)xM^s2di zI>1d-@&;sv1V4)HyO{5wdNh#xMA{%*y0gsUylwcqU5a7)WBT0AoJoc4gTM;ASep)H zfR`J1T)vBk-}JL3;3b?%-N3Cw9V3Yi#dNf<5WJ+oHQ>2)#oCyLNjEJWe}Kle~VXb4{FToAz=EW`Pd{93!aobfD}I-^h5XmJM&s zWx>iw(f{!AluIe05#8MwBYgWZ4f1&Xd#uJE!uNNPv+t>C;2~HrrWs* z*06Fy<|VQdE*ut&2cZ@@;FH&+Jng^h?zAUtsjK?c_l0iommGv;v3TtW&0Hnfm=4Ph zO}kb4$!8>_OZm*{jCfeAOh5&~bl)OXDj9TFk6TY5!g5a$RznsRXIpr9^7Ma1jp&|< z*l>}H+@fp8o1!rN55aX8oRj;VG^;?GUUAIRHzzLz+Gj;G}I$kxg9G#3%K0$kc4kHy%UD>5z)y9DOKa z3(Pjudrmq~Na3lh943ai@Pmyhz^fLExZ$`uTWLmF z3XfvOSlvZPXLM!lbo&Yxz*|_g>yg(QYxYe$z;%({!OP5ODbCAXEXN3BDTBG$T~wxA zRar>BrAYjTdNTd$0)SV)(6O4T$*U`9KV9rIi+5rozXSB40z5Q#pm0nXAN2fK@CwD! z5(7`v&KiBQDPWr?pzw=&uBpu{bw$5>RugT}nD}IO)qAE|7T;)q*4F8;lLWD3BC^WJ2hl7CAGE_8eO%4 zUWrmCrn{j9Cf4|c7IaiEW@<8ouh9%>1D-voZR${hY20KW{xynQEb0d1HN%)%>>K(p z=lV~qS!>Q!L}ywOup9%wS01z;2tJH7xydHbXN5DT|I-9Ug5|BcV1CgRbOyOv@1O6M zEeS)qlthu|gl*VQ-82HCDjqbSVp$9}IsPMxGV5;;f5GCcw*Kr)Sap4C z(9&+9HIP8idw|Ezuz=8;LJ^xUVK15Oc3N%Pk^6JE_o^oI$6$Mel@6JQIbgJ~Ik{&e z)6w2h|6WkBIf{JA!Y7nA^S8~?SB3Rj_l7Ep6$H#{HuXA$<>1S7`&A2_?fw&6JGxNZ z+3a(xs)oDHFobr1L} zIa->XDvY7xLVeJ?dAjzt|0!cWs2~3M-K(`nx8ENfUxvL8@QO2t@*b1Z$iZ!dXl>)H zQJ{suUAJJ_1iyYaIm?~J(`|Ue)Na*owa)Uu5&ReM%kiMfwGblKOqqtRTEW6yUVK;j zP&*uuGDQ8&O;~#{|C=u{;~;U#!sRXdO?%6KM4RAYi#i^)mj=HqxYIg~8cc22>K30fC)zyjCxX;9mNh$flO{<~l;r{;nK-9x2q=(BdihIG7o_ty@=)X9=B@eRqs z%4*KDI|sV*1W@qM<1d?o31lQsLLcBJf~b|b2C~x3BhM)gHTGhi>hw!JGHr#AT;c=B zo9*Z?22EC;e1k2aMq&!grK>Et9a8|FC7$<-Bv>SywR1p$bJLqBJ2bGM&t$Y}CZ@q% zK*;TZeV3erhtO&B^?CBrRk2qzM8WTKbH{yJeJ=MbuSbe#gIF1F`m7JFLV1ak1{PXC zIA)8`KG=HlzSdIV+T#jy1wnSO57H!vWT4mC`@~LbzlD9MmGAW;?f?AOdE6uC`9GTzf1ab^1KbtOK3IWywYjRyiF(E4%0_Vd+)`$&De}z zB#W!lYH=*aMSTdn=r;X2bj>R}H^d?@KxQ@gxwd^~8+K#2KQ%Y^y1mJ1l(5NGha=Ry zo15_kdsiB(`RTTxoLEkgU*l2gh%RGljR%6!x}{VDjFEXCBlFu0bl2$XPOa1s{UA%^ z#NS_4w!aJkqE@wb<%`W_*LBU{M|ht@Bb`UvAz{lP`bog`!3*x3H;yk^JRZvZ(&JS) z`l-^#;CnM}UAnEr-rw9ZsWOLH3irz!$qDRQ;neABtGESCL$c5M6TipJt8rYKOqRue z<|ZT7hJ#mvBEXRW?q|<(^ltE01T=$SL*<9&rQNz*L2L-sCiF`RE#~6iH6KjuA6JJ} zT?jJ$SXd1jVvp8CmCuy8Krm1B=1j4ZC<6QPYNt4g&rNNDU{fv}5p;>&rWdd0K5U|bZdf9Zbv86+Gi35V)=6s#${k^7a z>%I5H6PL1FXV(viG&)iuf>olGLLf&fb^kLf-Xb`T`3D$;_1{ZeIvgEfKTnKOZdROy zcjwEW;a_}z_96ZA1os9JSRP`H4=i%*t3y>s46;I~Lkd*Hd{iHjeD~g)(p(7yjuzq9 zKc-wdupArTBN&=2K{q_2@{XYi1eQbPw(gY3@g4EYyb3mp(8&us^K%O7=S6i|5bkaa zwb?s--~+_+QYj=lBoKX*N3l$RZckccQ%dc%^IVk3|M?HTB> zEPIR?q;l3(WZ`JIBmrgwT!MSE6XF@)y`&KL!S1Ptx3T(691*?I5n`OD)w>{sDPH!G=I`^0k_>JG4y2sv|Jg1U*MC<|4Kf6T`Pj1Rc z@T#xRC4`JVFSM-P`yeCCPXZsxKB=jg`f>%yMqvAa?nHzvVxv51Miy`H zP+wwh7X&(v==p02>Ogwzyud#Bgjes;7?IFJlL&8ewl2s!h&9-i_-p06RM}?9xvhX<9NQm&HZySA=%rhW1aA+K-~keqD!A@Bg8lsa4uvUN(>s ziMy_gNxVoeH7HU4{hGsf=~MLy!RARm5q{T|m=!dz)4*;2{{Tfny1qprBvFOrj?LA* zdU81K7y=uJz*bP~_!jwfQW=QO?ml0Xamde8lg4NQNSK5Q%px0zq*S|n)M3bKIQ{{YKCB#nUw7|09GFvIT91bY%a z@y8>$F~#!_D|X=AuM;+L%M}UE(Ve&(fGi{dSWV}tJD;kM^YN80fL1NN6dZ5lfyUYZ zr6%&Yb!GXy+!Z4voumwbl2r7`!1SOAT>kA;1BD_<8*4aZVYO2O?-X*ZK;=U2!hyMf zC6VAnj38M`FcNujNy$I*&vM52I61}%;~eCa0BgEnh@uj-nJB@eDwzAxGaF@9Z|??W z#yQ6#fCMS?OpKZlb*6|x=e#tSYPUv6`?qJ(jdjBvmV zXCnaTJ3tlUiNl#x28oA|o@m}wbl$y2a0U;5#()@A8D&5uP|T9t$Wex4Ua+a!2E{xV z$x-zL&;z$Cv2O*A0Rw0y7i0lOuv7S9t~(lO_qvV_*tcj z@S|ugvdJU&$)=%Q7U9nNlYNg4^0O#EM)73Qh_8v;kRUZ?umpH7LPdt?kjBQF&i1 zxcOdFwTV>(uY6=~0GZ>De4G<`a3e9keQf4miDo=U{5fccU;l?)#oDxcl-EI3*KiKKxRIXsv)nSSaPX#_)zhXk)5d#-y7=M(`H zi8=c>m31=PqqMJU<(#xdzHd3(h~s4^4(tKofHOeSyRuEUwT8RN=SZ=F;u%>?hC~8U zknG_xChwOD>K8GPxdU>V2g@a^i?^D3dH(>z{{WcXziPT;0XxRlXpVP8J$C$?H+5an zwnt3jfz$Zo>-~RU@JB}MvKZbqNfLPv&bZuFNhgpCa1R4Kd+jtGqi>5t0g~b)Zvr3? z@wKIpNJ8U)B#@-43#lkpHc&FVs1&{z=>GsAj?u`m+$!QT5H6~}yltZ+BLIPs-#)u~ ziVhXJ^fA3ItkCGTsBSR;Z1Y$OfgSq_MfEcX9 zaVq#KLa+qx&N&8v7-d->Yp0lV{<7R{2Osj)nDDLt08GjJ`Jf7pphvz{lHvxLwy})H zh;l|kaU!t(`NEUR;2OoJcI@oi>HS;R{D7TSKQ0_iAc&9@n9mF&BVo@)Q^%M|2d++Q zRkHLzXTuidSC~jG(`qrc^8&JnRZo{9g9(;N24#{~9%E#Kl1(rrndF)@iPB~n5y$1O z>y3~8u;x9;U{D5PP-JEsmvAIdAJ_H$eoU4-SA;Is+#;4RM43#WKf+E)A#fYfvJME% zWhC2nPuG9DMYh|%{Q$0B2YKO-FtIbT0>k&Eq<3Q;KM0kcL{PD7E!ke+C~LSnA8xR0}+~_{=cv3er`|RO|{j`Q)kJ2plsUa zUXQgH5_7=-4u>Ndtf#--{eQ!0-&7yh^|$rkVjx6lV8gqaUk*q{5XTy!P#EOJh7z-b z)nX%RhTTxx<^(d^$GX%<46+oFkJ?$m2NAEyl}Q6TkKQ&4^5liV`KD>NrR{Fl>i+=X zb^EMq4ZeS(EKO@=AyK@Uq)_s}pu2=XPtJ)Nl^eOssL$qWKYHyoZqC;HF8=^e%@=gH z{O$dJUCNg6JHvH6OA$7pN`+K_Lr4bJC-{+6oO85ta51^-rIUV&_Wr!>_*zBN+w;Hm z{dOXnyEoQ$#w*>##u4Q9Mnxa zFI)crU)Sb~hW`LB>-y92YD%|=(nQ1nMbFM;%&UmU?uPS#g}bgk=sCu5S#ry>`;AGr zG?Pp&ZcvDV%@>$aSToHUh|>jrOg2Uw;fQ&PKWX{Sz_UtcdXa%YesH&RP1hb5WhjiZ1_Gq}uHD3USH%)c)g!06bx z#cw9J{50z zt0+pXMTDTa;l z=LJaU2Lyt-{5b2+Aaw1zzsp*4(?@;p($8N#I(fTosz>fLX8Wh3Z^`!B%UyK8qiV%T zArF>}hW-vYILAE)PQ5)j-GEnE&z8zpO8naC`Suvh_ zm4tgU&IjBFu^?k;Ju~aZeKEmZoVk*+^Y5qJn*045+}c$Wwe5E9uJ`;R-ThbQReY8R zFkMN&0a=R@ayGUZNdEw2F!vZ0s(LwWn!WW(%g)x6EiZacmMMCz z9q)a9_kL3SkB8=-U$oC~ki=8vw=I@(IR|TDhDRSZI`OwTtT7psDbw~ars_|d-l&$5 zPe!kHx?e}9mWLiIIfW=HG^om~laqC$8^vDNw4-&ReRT4F3DY!?+1sbt?fjPjhnEG* zw^GODC0Kn11`T;CGObbjTE5XXnuYmPR@!KjO?uy=`zMB@QxRHT+NV7ytZCldtFpEH z)|}nl_K!cQYSKjmNI;fg%1mlQNWkES2r|TN+H@vN=nwQ_g}`d7IDLS|w5yVP39`OsKLF zI;i=Vrqj;fVwMk*q&aD?XLZlbtfuYmw3Hm8ue7gcix z&8_A0*N*%RrE8AbwwUJARA1h`#h@E!1D%%!e%)( zKDG|N7HLy{(pGS~=A%|NjXKn4WzSB!lp9f;c`8N|kDr>YlO0^YLREthBi_uS-)@i8xSBiZe%=}w2 z%DHDZCC=cQZrt3qZMeQ(ZdSKXbe@raXaCdvy&gVYJ$$`5*qCFnvo<#Ol1DOM%!?7b zw}R8nj#!Zx43Iu~8DrduDP{h6hBDP06r(9Crj)NH?yq#YntitP_>umQVYpJId^R82 z#$MK=r$27u`6o+}`--EyZrhBl{oC1c#T?&?J|@mQ4dNda>lPN)Hgdrvw)XSQ_DEJj z0ch6Q2Ih5XHzWa36?2(q$SPwo>Xsg)tHDAJEjd*8Rod?)mF<43Yp0`fSS*cJ;w%nT zfTt?eU1aK1p%+n8gnlPc(``W})!ejF((7aS&HbgnX?kPZx};u#x7Il>Y#GPv45Rt}bx8yX}45wbPsW?A<|(*4$`{N(YMkFUH)^UnpzO)^c(B)84FM`s%r2$dKxj&kNV zlLcZWWF&3ex>3So79tLO&YI?OQudm@R+W=c(LGzfsdTc8xbc~^V&>&i8#ddVetxa& zwR$(Zc07|@)@EC+ptO#WK^zMTG-BZuFe6yb8N@0N&YSmRabgB*>8R4QRp9RVQe3uo z-(I?1N#9Gi-5-?anUb$kbkyT6Yp2T7*{LSh&E0o*(?xTv&@OJStsR!lQbhrb()5dO zEM(4cg$LMm;{eyMg`rW-HiVkLNV~hO5=!mw(RuW=zFtyC3n`2?8$35npk+CZ{k?lfD8H@=dmohZ5BcUqHo83_EKv*bUaMbEY6)Af;rOg(f zERt`1G_&=8`8DowV=LjOf>2fHyTPe1YfUt{9_spAw<`0t$Jn0*zh#fvr^mYY+B{jM z_;zzM$8xB;gb>?@y8ZJV@n1xjJJ=9>(x)AE;EMV#jOJgq zTg$&Al21kFJm$J>{eMfY-=8hhm(I^HojG#P8Ht3i@X6FtO?OU7t$KT_wd{8*{{RHr z{e^9zjYsUY@LpAqC4SZ5Z3b{aI4Ae^GjK73j-H1&XJ_~RzJJ$lf&HO2?6G);-jDdd zq`G(05^#$xdjwCXs;Xu0%mH@2!)yR+8s zU9_?EOdr{HkwXkGX>Ak*nmC~HV^FR4RQY&Zg}}i1dw>B8PSWiD@9X;YIu_dM)st&x z`>vM0x@f-grS-3ewP?gQwmMbjK4ljvYb>G9-gu;V5%J zdRK&eKWlpgk*tx2wVJ}p9kNDC#)dWwe7~FLELRLw7}udv61T&q&hPJ9uG;Lp(NrW?>Rz6nvi*$5dYJU$}+e)nj;UdgZ zH`%A%%VR6)E>p@YxsyTC7aB_^)q_3)!WVL#2r^0#1i*>DcL${X3=IZLlOS`dXE+;_Q z+bU*Z42Cw2)#eI9%a$N@#zlGc+rs-ey0XJUQHz}Uxf@TSZrepA6qa;ctgpWJ~?Gtz64( z6JK6G`egPtGwgzGM&|Eej(dsBq8D`t+-to3gc>qb|zx4LB69nIz4(?Zu1#<3F=R>9eTgy5!2d6=b` z<`DkORQ;5BF0nIQj-(rL=I3!&PS0D(X{$%xWmt@lh8-O9HN{h}ii8waUqY=qwBtLu zN7_OU-JZOyH5o2cnrTOyqy3(~1nE8tU3*W~bqRb);``y{>N80d+geNIxp$5me?4Qc z(;@?Whj4jVC@6o2&&%>`HeTyOoTrJZ=gVEB>TC&zr~N4-oUtAapA~ zWtZS7FZL>Naii^Nsp#zaRHgP+l%1BBcRY{d--Pb)I^)zu*~h9_4Apw7KIsAbktQkZN^jDH46n#41I6W+7U#Z9Usp(KVvFwXf0k*IsL0U!>(3oGMq3ByASdT=lclZfhmC zeYAVYsQz_)WbnuKCa5oNZI&1_W>BPO3{DGU2EMn4_=b5U zSBY1R{giCF>h{-HuD8=$TdDG#eZq7&j|YsyR--DAuS%qoP;*JB>t?RE(?!>3r!`$D zcr~nG)FtqCoqn$jU_)WyNUy|uTQadWw$k}d#V|oh4o)^4?oa7FqcE+Q<@7ezV$KMZpH*;@sZ>DQ{ zYg>p}q=Uk@{(jjCJh3G004lCgRgpnrRA8waYgtNiaZU|;$==e^*Is&Rx_$St=1x@{ zD^jOgF{bHDmh+5N^j5XwcKRze)|%_h`#X-5DRwBM&3N>bK$I`cX;03^{s7mmrs_PrMbt#xHpBzOBGhE8)p>Gi5FsNcL=&Gs2k{cGlrW21&tOb>yfO`Gz_xnochE-rCz< zw$XpU*K~g2l~$D8V-l^6N!hfMYR`N1vu!J*#WgAJ;C)J2gokvW=s`BH8x+VS zQ*p2==l6}Yu*8NkPRjCXzun1R*R{S+r^Ro&+VofD{Wk|n73txl6rU@PJGyqWQBGX8 zS7{}yOWV z*)8|mt+iJ_P{{Dqp;A?E?GlEr(tOQt7~W3yvWn$;TUOoWFNnNl_KiMmPAKGSyWLV@ z62!1b^1!Gi9#w)6r{^R@$O^m4!PAVg_!E+h(@k%qzOT!7e)n6a*4LNm7!>7ARfG04 zUXoIFP4-Q#+TDAvqq1qoIVO>erp#*VfuuozwPi617OX#z|_mZD_8ib@G34`Tqd% zYr_&jqRBXbLeQg0ES77urzNfCP2(uf9>$&I+P-5yQ^M^?CoBbl|?VG|Enb{f5&?K*yu}UeA%m$KKVYw$E0&>GSEMkDBeXYR^aW`?_?$+<#&}@JkQcFHiW}`xR(D57o5) z01#{bB=~isYF`ihO>L&xytWE#t*#={5<8Zb-1+UMUhRotcXddkE((RO>D~gzEXRl8 z8OknoBAg?5wx<8DO^la487ZXB2_b~VXCsV6wEiLI{{e|tuf zjqj<8`vrKGZ8`jT;B7|ET^jGg-Z0iQl+!NYv$wjoPqj1Kr1uu?V;)={R75~sgIf@G zmaofk_&Ql{5#wonUZSPeqo&jyo~xOwek;FEd-}64#=a!rT!%D_m0455sNR}Wv$oHX zw6?XSuT-JA`hlzonh2wfyDF}z_G;s}Z`Sma?EHbFA) z`J;_6>_!Pz;hv{y}cy6dyj+gaZH$L2WbUUPeU*Sp(oZL0lzwz24* z8J^jZtnIDYn(8!$;%P{RX%PPa%Xu(zmQH?Xp;B8VT;&*7w+iy3My%r*b4qmEOX7M( zT`g>qU88N??BsJ{s6w>)Celir)4NK|-K|?n?JntUyqCSFH;29=Sv;TE{{RZE?zG%F zwY*JA#yD>pMZ-?;+y%I8k2M>siM6V849%S0&ydRov#p!02 z+F2=oJ#ignmS)(@BB54MjYT-kQ?AfL=)V-psD64y%?WGwz zEuHM!R@(Y>S0~c^Nq^)#E8u&3i%Bem@yV%Q+t^7nT?CNan;7DeEn}TLi)i3 z;vBiZcP>Pvo~f-gYolM?-{gO6_+6Iaa@>~#3Kd+{Wg3&`(MCyGa_^^GUst1Q$)BS? z0(5^lbhzNQTgH~|CG)19Y2VFb7nN@wND&4`$dm4=$QaJ*`TVsgSE;U?%d%Iq`6b(K z&uh!{9QILodW)HBO0R8hlD3Ur?djF@vrb33d|A@tn_be}E5f=`TU*azYajee;sK;{ zaU6~$^KTVM%#FDl%~Knw4qGhaT9n$=d-|<^ljPI7x1Ni3eE$FkOP2{+jQ!O*iOTv} zMpbOx(n&QZX5+T0tq`=QIJ6&4@NXTg&b0f^*w+0Rq7}|rQ zeB>y(xhHg^>(={er_0Y?htgxJ#t>9vD7723`IEbQq|&qKouj8-TP|$N-S|_(UK_j9Z5Qn8sd$4|n!+oImAB3Idsrft?Y24c#E#0I zO2@k-hOgB!8d1b$vBXZv%1I>UYsXzVdARVGH*oSy(D+tu$+Ohe(g_ z41c3-zTc?Dg=9S?a>&)e*iu(JpzPfI=IO=0DHClyARO1;d%2BqRQ(p6K z?IiZm-KTbsrThuvKibCLJ4pWkuznEvyQ5okHSW5xBD{dIh@e)5OT@Cna_6t_HkimP zy=s&xt42|i>B}g}{{SVg%TFzvmz{`&XA4d)QM2dKujRL7wYL8NJMMmod=~L9?NfX9 zTYXDU_%&_1;aSlyJYg$bEJ@|9x=bdD8(|qZRY4#iV9GK@c42>uO8P!-*6;m(TOJN3 zSahXLQ<84!JzbjX)2+02@^5qWqrqSB(y&=YAKCs6+Oka+)wheplE~_WJ|rgi51YJ^ zpEPG66Slpj*R}Mw_0!W$uPZ)ZlEy_wcYh39)6)08{(30)z4S-7*joI1mABqc@Tw)Z zmKXCQvhggTWKXksfmyXgCFA=#WdTy843Uvc;dN+ozjIA`bX^j0Tlw_udmUAr9~ka! zCTX=_0^Cm1e(UVoPPX4J>|^)3?*kz7vAS|TQ%!EYSM}HO`^>$z7SH!zbYgBLr^Yt8 zjwaUp3lc0tF{g+1V=w>=gFp7Km!8}bP7h20T^8HV`hEL(dOgfOvxWZvpHi~^=jWpQ zTl@`A?0<>Z5x(yb_%TUQG%TJQ)v<51e5`S=;rx!LIrBSY8s}-N@8{OuuWSAOT~t@r zFuH!~TUmRJ-|$_$-{v|m4SZ1_Iq~m@bCN%I;ftQ!5KQ;;@;wen@6#0dUY>qe`Mr|# z=KHq~tl_`s)6-vr<@}%5Z4)g=;m?U>;C}97{`7;i(vZ3 z8UFywseGXN`bIz3ZF!<^3w%U^L-ubQXlyqek>Ml`?~t@OLs z`n|sIynk83{{YXaZ~UD<@E!cW@?uZ;L_Q-=vUc(2p998QG1_=e(L|fiSwxp4yAz$o zO9DoD>botL+b+9)UyrV-rZ25f=}Gx%FaB5kZP1~8;g5=zHaC|VN001e)L^!h+gacE zPSJGvnK!qVTGXZTM<89LqPYyEfy;rK%29mI*J}IQ{vZ17v6GUVuCAiqiQh#1QvAOC z4=??nemuePlFP+9Z-_MLbUz#T6JNaW--z^>4dxzpl1m(b!>DhR=K!`fe$~Ivx0C#|^0CcAQ>pCbIJnKpT{ULC>n+mVB-Wj8 ztTh~;@?lS?)qz1`WFU}Qze2&&_H+b%wM?nN~v_e7wL-Tn|8QB{-Y%t`hAYG$}x#&l4 zVmo^ikbStz0Kc#D{{V;k-qDs;0uzJIIs!6&oqPLzb3hO)Hq${hock>Hc&9 zWe(O-eUChK_rd;Ol>jMaC`L#6!ryz)f1Zc(;(#L~l|7GtUO%VP-;B@$Jg^9FPI`Y` z-9G{A+YAY-roXS#^ZSJlx<(w~Q|0t09S396+uzlJw{2dF%y(RI@6WgS{C^q7^lUHr6m9@L_7K4+LU&$}JK~B%J`-RIc&+ZCGc>}J@fHRJu4o730XZn9y0J@}L zaykz50bW23azNzu{ePtZOo3HLBkqCJdQeY1f0{phNSFq4*%?#q*!~Cn^PZFsM!cHc zYp3A99eP^9y_!M`;CXl-CUNb;_2-UB`f_L;wq3uk>u)~NXaHj^lY!R@+tZW&bH)Im z1Z5}Y86((adUek|yM8^W0y3wD!N}vLI6u&Tp0o?A?V#5<4uEF?03)9Lk0AB*{P>`M z*WM;EBXSNvBOgWMsrU5n#Q;w7GK?q;4^f=@cI*27STqH%^4I76f0-We!H&>`M%nG0 zvJS~UfGHloT>Wv!Y6JTI zzo+>qu*ZPR2_%dK^yecO>(d=`^`Jki{{Ua)ysxxoSrlyyH(_uGaKqaNxAi0*XaS6^ zoQ!}+9esbp9lLX$D6@NipY{EA0;bp84uwEV;KC*C5FJ;Be@1a#bZ z_s`K!S^x_2NF$T#KV0YO{uu^m3zA624i8?rz@Q2I zqZ}bs3AalVxf%NAp0HGm`+k43-c?g6f5B8fOeo>rYb!;3A62J{p7WD=G z@!A(-83FC{5rdBWoaZB_$^hRfR%Q~rH_NyuR@Mq;F`Q*cCn?vb&+m-T0s>))jBEoW$&QV;dHck3kiGcFZgW5kkok2@l5QH(V zPXO=~W2YqJzDFg1BvlA}-@CUcW5<|eIL1I3E%jd6=b&H;X@?GuU&)n(TMzn>BC5}8tyt3pH4?ON2MmrJQXO7``+yNYKz2p+N+2zJJNWqW+{7o8k z1Rp5^s;*8^0tU$jfEvb2dv>^lm6l@~h4PTD%wr4Y9m6BPexq*zfErn3iOZ~zBD$#z zM)#ECdjPo`PDdTF+dF6h#vv`sF6I$O3i)1J5M^b_IKkz)893?J9cTfq_J>vS;*}Q% zlFB03rv^qXxA&lo93Q*4p~m0{A`-9KOL@{jh<_2OUa~F|>sP`u?~4{_;6w5xh~2s3wPV;ThX>cq0rmrZ%K~yZAf7XUkCXzu?m`IUk(?65DBhb&WTcte z6s|Cw9=HcM6afi)g@Gebu2u$}L$6;klXwh%^8tf_&jGzC0kcT<>XO6fG)=ITDplKg z`LY->2s>nbGtSTiCA`@#pLkg0M2$&P6lmk-kmEZ@B<)}`yBN+upc1}|SL8`7jM6cY ze6?-Kv3}_R3pfX(d0+%de*CE`@KIc-Ye_XU-rDb{NBw`pkjrsw<)v8OJ;ah>(E!5%o0Ykt->n?L|>T@1CR_Z-)3J-EJ^Hm z`9qB-b)(&MATqRM50WG@85%WV6C)3sY-$C;B@QIW9E`8Bhj3mrQ>l=%NU-@*G7pf* z<_lozgClVzNSqAr!Q0S^0JSW?x{S2bv@L+@h^Z>}#@ue>ryz63au@+FLb%?c)JyxN zAxTnk!pJ%D*p8)#sTuwt3T3>l#o<#L#tDo{p{AM<<%U8?+cJUjHg>dQCoE_JqZd>& zdD1~Dvquz>#ut|On4|ASl_kTM5;r?x z`Q4Ng{pKVb4CBu=6eBTrj~`Cn>w4rV6%mP1{HE*Zv#z`3h;fTV3|o`u@8TTcRk71>@%L6o>w>hUr+-i{!>LDL22fP zXb8&h+QnpfBg-_P{K5Aw>=nV?wUxEMUB9jW0Iu7B*xlsv*Jk!jzywxFS95M3XgS*< zn1$NC1VPdEt1+pb-FIDmdVgPo0U~gvqY_A`es=93wll|=a8#Gd?jJg!0A)bkpY{0v z0FhAmzpv~1@-5rjHUgFf2$oT?&=8~_FqsY-;xO1o+;XVIjF78K`tNsueh9M2r~ZT=J!c01O7){KK#( zkz6>0`QxK&-EFJ%zpv?T&vUk)@gVwpy6JC=zTNzn$hA7!Z27E~Gqo-p7n6SW`=jUi zo%W2Nlqnzb%p3-6-p|qg3h(gs{G5FSINf{k-F1mdNYl!8hk7q86_;}m-Ec_j+am)# zhA~|K0N3^0=;n`f{%cR``q+S>XjJ(v9BRPEpqB*@4dPBn+}*MPJ$bAu!Zv=7Px5@* zJAcF5-m5pY{_Fn$U)N(ZSkor_4 z{3Xl0Y4Dqfcq17Z(#7KNaK~ksm?s*zTz*+7W3Y0j-~7B-ig0R-l$BdLRUtS|P?z6u zmVGRbF~DNt(_EIRds;DR#!5?Nq^+#utGc$Fzbs$rX39zJ?!(1Byv8R+;kx4?SSi{; z3=R%ck^ScX0GDk0C(Za@JymN-=HUt9n>`ti@V z=fFR9xT6V9OPXD^)mzuKy&LmZJ*d;ER&Z`sOX9xH>#nO!t@qtntsSl9+^HmTeU=mV zozv!goN#2Pd2ilbi)4XQ03>=GDeln{@Sxd&PUY zCiG2S_Sai2w>&Di{6;P+F>OVw!A0{sTKXksz1OatFQ$q)x~{ijrzEo7PYt|E7`4Qt z2u{(+Q=Aiw@tpVCG4pj{GWLjo1>WiCZAO&TX~v^*pDIt8 zINjS@E8X;3?R#5P&jayy#_cOkjzeORUE4;l`{@;;jkX?IGR1cYoQ5Zm#N&*CU!!Ea z1B=B@3lQZ-thrw#TAY_QwvtayWUQA(ckbD#dXy;2qd;_vS- zpUvG3P@^>6EL9sO(@MQ2)7jlAO5Zi5uJ7*h;o5%FJ|VGmi(l~kldMcZXvERGq30vz z`G^@LWMhJG39mx8g&AfhGM-(TMw^mNDY|luW!dWOds_N=Z)47@>>*c@Rr&0ae$J#N z-$c}!a$T;xt-QS+)8jwGj~LtipIQ>xUbJQHVv;2d1d`!UyKW4~SU5#1l0$vn#8;DF zfHO>)Q>m6>@pUR%oG~$}g`xF1RE+-sd&`NAJ=BF5LP}i!08^Z=6rGdij|MV|oLp$R zPMo8swVL*^seMw9DtxxJozmTE*S*+jk6Coyx8>_!(cTmNCp=d0w-HkTkEL6QczXj|lM^cVirKv!B?(&I!#RZO(4$R3I*_4? zlY|qDd9cYees7-eo)bJypRtUlbf_pfPR zX7%kK?>5wb*UXCviFsZMPMWRk@KKwDuYa))WS!c6^|smRd!G(|)ISog$AfS5J3kmp zI$PS@eVDXsfg*yA@x+7{NZEX?#fcjSW?J)eoYt--!wXVsbot{cr6qQfwwC7pYd^o< zyHWd0k0Qfm%6Wt^^yuQTG~~I`jAW%Yot>`L+*7^Mx>{(@=i9{3soQIk-s>#dlFJ~u z7t)V01d=%kfcaR^wpRKUZM6IYtIE9bYSn%!BZt){j|E;BZ*fvn}2 z-m=y(w6ANuFQd1k=(M^`OM>wXjd233cG0U6W|lT{8iy{vQ;oO=He#Rw)2jo(uTu*J zTD&RSN6mi9&~HuM?6lTaZ^-%l+b~sdb?ecKjAH5CS!>Nw3q`8?TUlA_{tg`K7gK9d zH=QHH6!zB9f~r~@lBHcDB=a8DWd!6AoDuw9^e_=oz3*#T`8^leZr1X?`n!AIm)Ff4 zu~A#Q$y)r^ZEW=Xb-#3b9jooIyS9*s=G0-99!Uik?(b4Ig2$fSc^T(-tAwFv7Y37z zm8Iuv==SZdns44-ZfA_2)iE)IlBp_FQP-=}zP)X#?9$xp{vLb;_Q$_U9B$3SzE7wqs@j71u4H+b^huP)y;zLN7x?S3JN$g;^oX;c3I zXm$RvQM+1FQ|4=3?(Vgd(`3)NejR?yp9UUVMAUo>XvRf}(8nV=ojk=WZtJ^-a!7X= zC4OE{3)@Q>%~A`N&QprJ?~L)5`Mu zR(jvdt<{~6%rne9z4=gn&yq5htNB+=FXgJcz3%i<{rJ(pXTOCSAAo1lJSE|KPYJ8o zJI8aOOFfOvl)@${aR-v?mR7@Mx_8DyH>E```rGgPJL%E+H}L*ri^Ssj;@q}zPVUL; zrT+jIe|Gmzmq*Lbf5A8YK5KsybZ?8_3%qgSA+7k&;9tZ$?*~t)+-0w?yg2c}K7)Oz z2l=ip9w}wEmf?Q!wPwQxA$CqLwwkAXyZQNc{{Rm1=FjjEQ2OUBo7<(+>bAdqK3BcZ z&}*$(Ce}4Q86thwHkL9=b(6ARwy2r5F9dbxfJeVH`}EWNzv2G?9*2uX``5Z^&EHEU ze*1ZO?bMPek@#>=*V{PmPfzl28j;uAwZF){95EptTBc85F*p1A59R*=eRQg6-RSpy zKjHrXKfK_76ME?0o%i!=@r&sqasqB~*m$4Bt3bnL&p z-{JQJP)3ZhZ5fJ2;6}O0I3vIL=Z-}`pY``Wzg-is`O>}G)6e?r*Jbiz&1q>Wm>w8P z$jjzpFkVf>_4!yQUf*7M+mya1^8WxYKK;J?9P9Y^UFGSw%jEB`TU{TY_kR^X;Y|M8 zx@UneHF=`&cg8OY$#13TmogTz)BIVXvfK-+4E)H~)}XD_afMP7cpHmUUd>rIw{1H2 zPwunx=`V6Qa>d}`Q%A_BFH25)>E+VeUhdBF?Z0R(G93@ZF!-m*Cr{NPglB5vE4U8z zBRMLvkC=i63lL8Rzcu50G^0^slw|Mh)#9Y>Xr<9KyQ|qdYR~BZ0Hf}4OBILBaw?dq z)AkCdEhVCU)=r~p`@eea+WOnxwU3cJUb?rAt)N$y+8rJlU(dI+V7iUmP4|LIp;Zzy z!!O@pP73_nzdOw3EM+)GOISuO&ieW*z0%WV=Pgg_P7lRW;i{OdO&9I)UjG2q-CY$X z{?g`>*8NqJ-EAIhtA7dF&1t25pv1a1iI)D%k;=ldBNtF%llNY8C=#@;q%#Brj!PL` zc+zpFjE(TomXMzO{aTAk>8mL9zRlXlnVjP>IO*2JVA83Y;-uYdV)vap-t@V><A8rH8=7*kNHFSBgo-an|md>enwUx@#u2+~wiwIbR9Xj$@zVp`Bx~c&T#b z@~H~6<4#bgX~|wnGE-B9n%h*jjD1Jpw}7nltqW1GSGt4kbIbj&;#-@9d;L`xFD=B= zoIDYi$XHPBKv`D+3hT=9$}}*z$}^jfD}K`xKX#<2%XiAgl-plaz|&mRsB=55BXtFLcXg*r*{OMN#J`5K>z-A`m9N;mH)9}iEJz}_))+V5 zwvC8BNIQ?2Da@Rl0kNy&Gij`ry+Whc64XrYx?zF6~fs-d)0H z4Vc*Q2x|Q2Il`!7`JmFFC`Z}FeV$wMU0Zi}?Q8F4cgy_)!P&)3P8x1%ag1jN9%R*& zobL7VioUYjUWetk?J@AaTdN6Wx3{^D&@|UD%BAyk{G6tpmF7| z@0q0_I6R1eJY=`*bg(-u{f*s){J zqO}QPs~KWMjP!rMzyGs5$b*}EPtN_E^Ld@u8*+p2T^cTkblsl(d5mi1e4RS&-nnP_ z_@pJaFhknZ+swlmt*vrrC#V?mb`nv)nX(flUTrbYlAeM7_34*Y^>XH2(0N!2JGEEf zHFwhbSTvISa=JA{cCR_eeSfiUN5Rm;Cr`Y{q*q6=guiWkY-+*zWIV?bF6qBxfdm&TVKjy|R{{p)dO!cN-TZbbk$NB*ACB~abh6-xk`>-?}X8G<&s_S2y z-b~fH2Du!%)sE{TaTacMTRx+?eMX!5dUFHA0rKSt7wavT@^c@snD1mP``#n1*V-w< z&yt@s++eec623YVW{+J%T=20K#RG!tlxjC^aSqe^wo>e$R_0hD&{>}-xp$6j+c!G_ z4v*PnjNzmAHZO01SNGtNQ2DdIrHnd{9fw0ctwfvO-}2W}7H#R@ZCQ%Ep~7$#U1henAv z2aP4f3&_5ms2gvxusO~AD`pYLO$A%}YRjf9=x?ZGz?dh~d*`*Q&&_+~96NUtILMQ- zdLcqYU|46L%46}e6H_zg{8Q7Z*3iJei0ZaKC&AbHbfuOQ8-tqtnXtEk9%Nwocq_85 z36i;ElK*Ww`z0ek)Va4hlYEe$L5REA^rAwOMxN|TFT`x{Mc$sH3qRVRdLgJ}tF^9e zxNIKpy!5*2(*oY|&#Z+i#CgDJQfK-?yEeG*FlEvIy~J(eNR*kV`{~{|NA+dQ`^Z=< zxzN-1j`kp0Puttwt^yDCATL_2Yu%v>ftW)#aJ7f;GnvA)*Bjr`EMhGN-W5%5xj1c) z)g|_0;}J0T0*uthr(U@7oc^5GjmhHHYa+i2Nx@^ROl&4RKUhiIXt}#eu`Ssr`es+R z;rPVKgX3xbz@TI&zZTP!*~LZOy1Z8G06gYBl9`1CA~$uQtMcoc5q@7#?Sd&I(_5`u z;T4l^^hKYF8G3NCvRY5}qSbe5$`T99tDCkq$Q`L1XlmX@TQj1ddG6CAdNYJiM!Mv= zGexB(mgaXXv03@D`Y)cuc|3~`?E)uI09w)UKSD@oeTdCa#Xk_l`aRSs>xH(SbEAW6 zo5j}mtIUD#76&yt<0KD9c`d~}DO|&S47Vf zFql^qpAIdTeHXLbIV|GN(+gh{8D7N7wx|ADNI$x3h>}#eInCX3TKNy;(<=@a2?@Nq z!j8X9--SNRo<1J|h(T?gxRBD=l4}9_+FNXv#t7f?l(x)8Yr$Ue*RV6QHrCqg^Y=SM zBjh9M;!e+Ny~hFdt(dp^v;uYznt*3Y)C-i6TJ^45vJJ>>9C zLk`B3fJ2$L=FRy{TNZQwIH}H<3tH$B?K=%}h`j6D)65iUa=R+ZbcRL9yY{jm^xCT! z#O~I#{(!n-$pG&Ao-6ww6vX{Vg?MMEwcLuI<4tSr@(r)r6J+`VdTYqlJ%lMZu?z1t zRBCA%Jz9&1mEOspj{{>94!_^18*fE&)X$ z1E-%#pe9dK2N!oF@Z1Vd)1HdGS*79{Zg6;iDC4!(;MKsLxnjtT zR;7u(ZTa$;E@;q7+o?_5F7q&V!V+0e;rH0WjCO@0{=Y zt)@DE(r(sq-SRys9|e%In0&efSN)aqvGv#1{UB|!J&*OgOXSYR61H9bJx5sV@%<@Z z3G|#W!Tj>{_-N`BtTHk!8{ju}Wl{4FgbY+Y53oM63zk3lRsORdZ^<<`wfsE*qDX)` zr_&R$Yj4G4!k+7V4qakkpGPnsNQjz!Pu6Q~7DcJl)t#tX-nhththL$s;2?13612a+ zI2r_tK4DpYI>7>^ua=V=vwZx%mnrAUUXR~Bss(gdH?qtdP6ZBspTMf_1{*v3uwrk%kFlqo(L65lu(f<*@ur0 z5SLPoFOIHHeii~rBaIewQel&O?kShRQfI!UX}j*?5?*H0%3PA)vz!%K_8Fg!vrGP# zGc?kd{yNpky3bp0)AiFul(0AcqpOgUDa$wj2vq|+{qUT$$=GxSjdRtucz2XFaqpq( z{F!!z9DI$fB0}dcU8?6r<5$LOFqxr?B6SrO_^;_Ib%d+l0yqfe`tXm-x}+q;e(lf) z<-ahdzh`M{xidBAvR>4R83Vu=E-|8$_!ASDzXI3@Cd`n_l(jEaI&-NW;=>=68 z_E4bM9nf44)#Xh~4!>nON!4{gyIeM5)45To=|T?M^?{@ z)EnQxRs?zKsY;a<1c$`AMu!T<5$pK8>IcH)RGghL>!x;pgxcAgS6P*ixk30fBTIy_ z$1KLJOs~zxj94$Bc7)FBnR|8s@jf{xQtsr7!ZJ7USfb021E+%hLR+gOfha-Do>Q9I zRsInMm@nYxOW^3sKzoFOsMW^PZcQ?PO&D@YkQ(4Z_3Vv!?kiM;!7@)UN5>7L5_0?j zA2`+$J`-(+a6NW-=uenv$Q)4;_qaZ}myW~d8*r9!iD}RT`c^oBVzI?TzJr+m;pD$e z;wz|@Zgh0N;BXS1$RofJuM}v{zlu^%4E$@Usabk)1uj0D6Fj+cCtRI^;ebZMXr-+Z ze((Y|=iPq8C+54_>BAEDIKF`g)df}$L6m^%E%i^;h-fOyp%Kz0z*aXPC`|idSMn)Uq8FJxmQv1;BYI`O!VRBsd}Urofs*Xz{eanc{1-?1GT|pZ@lh1 zjV=cPW_p$SEN99nX+&D=71`zAxk1yNKJby6~EI6pG7 zGmWqencBhEuAUlna7v--#Bw&I6pjSg6@OL9;Ru9e&q|L4SahzL8|B$h@y=B+N+{2?yYL%yCu>XeEwXlY`8ZX!xgeO~hINqgt|cAyuEFQ+AXm z!ZWv`#QqmxNr>HPy69T>o6k$o+QCk*A0LCG1+wo!JTl_n{R5SApeoQQw5(*)dR-j@2DsiD@IJ%PKJ?DE{_IT+{`UiLjFH+=h? zAC@#Q&6QuApm@HClIINtI>2Y5SY{ct0;l>3{t}AHf5+mg3bZVvrS}0XO)2m|ht9(O zYkpmVF`nwrLH(-<4O@F7VDLyp6pRVg{SPD+cU^(=2o219Ar~}3<3X31>*t(4#Ovn_ zKa@Bbr1QM?i(qt$Wc3QR2<=7@Op6IT>1eG5m0eZ&M8&alvjkK@@mBbZk`Ax*~ z@Bi$G_yD4K4>z#S%Hd~}=l=uU=j_K$v*O9*%Wu#(kW0Xtq%ee2E)<*zROCU$`bd#M zksDaW8AXImaKwTFpT-w`rx^)!?e(6?hYbw$s(m2`LKWj=LLb2m^f$qq6<+PPISXl| zlBn3u9S?gcD@V&g<_5sQ04xk%4w4BHn%AI2@IVy|!N3jrO}gshI*O%JSS>~XTgG4M zHr1#Xjp5s~pYek7H_vHX=-!|73Xv4ilcY$KeI>b0BxHqWD0b@>E6=@jI!EYw+vu6qBf6N^zTp) zC6V9Rl|kq3f1&6tuT;p^TYq$)jcve^OHoC|5!XZ$D>r z#;=yc>79gzzjW)_pjWGjPmfsYy+^ri`oPg(p=(~D0WZ?!pXo@S;eK60k1BAK!26H2 z2c*MmjTO^fCq%GugOQIE8Hoz3OrW1!vat##7Qu2;t2}=Fo0Q~lJkQD0NFp~}yv3>t zc2jV%?z~nj*mkWSV7OpU#Q_l+6?0>wYCY@Ao_x@tG!eFv56J_U#OU3Cg;5T zngSjPT(cUbT2F7TC{ApV}bSCMj+AB7z`9PRM?Qi;%Sq4Y&Z+@j=h9uGC%%(C${ zJW|YGvse8ddN)qAAN2SJ)*(GpAl&sDi2MHBa05dzj663C7b=#@T3?$|rNq(aJ|*9G zP}!?(jW&;<$^XFh5Tr=KWx;}}%d&df+{gi6UQQHhAgHf$M1ee|g&27QXx_d_aJu!3 z-r#(xe_rAh!)<kGO4*tUn$nc2w87r=1tBj)D}Q*INWbk@!yK`-ITw$T)IYK^hPhaE0Q7LYmIX8 zSp#MK%)-oOu4gka_H+M%erN{h3y?W4ea6$>&mJFXO^2ug#Et z>R8s=?LNv~O4Rue94MRr*Fl7j%C4*265eNUS1=F5s$2hMU;28?OHEem2xpHt?jK1Ab-d2fRPrDG zV+_O%HGSM(lAalH8-ODA~kGB4JZyy2vuVlk@n0uUKZ1L<79XN&8*sdEoIusYJl)AEy7ge#f^Of65W!>XTp6^S_Yk5%d0;EE5n*<#}CAQ_SH8 z|CjytxUjgSzW-0~uEO85thu+I1xOb@3qGpq9x2TtR@vmkQx{8AEw^Q5S*z5CX*>r2X` z8Cq3f2XI_vM-seqjz+CaJTgw=WnuSbziS8Yy8$a_h+-oti0$!@SFGJi(D2I~xGwN2<9WLX1<7lwg!t3YUvwt(>|bRW|0BNS1Qt_!c&KWNGua$g z-{+)TaW#6aaiXI+NA&DYp>YjL`r>~P6DBd_{lgbUAFIpuZ>ICzNbWo2?1mf*#%`8e zDmWDw6t&7u5>@r_N|MY|rB~wOL~B?@*Xgfkm9pL9>`&N-mq=2HjYi@0Y8%RqD7L%7 zTbB&Tw@%!VW!NBz=D-u@DI^Bwn<|x}2FOU2?0N5dl$IZryzLMb6@4H`vMTgEvcqCI z#YtDQ{vSwi`n)cZE`fW+7B~~YEBEs8(S~#lDacJ)pUfVfMti+aFkkh=yXJQ+#B#ss zhL|wJQa~j44;p`!AV==UiCt`N&d5#6I)}RRfD;%vyJ`KzNl8OzE3gh-vlZkzQGRby z3x5mL>*YC(unIdlmGAYmlHC#+GAYO8XP*N?IAq)QR3?=EqRdjs61U(qZs~J0f9M{g z%H80!cOw6-)28gNARjB7xf>@JFh3iWsO*(|A8D$zAwn0=r9S0c&7_Z64MVZKHh)c8KPft&qTzq zMn_){r(~_E9V~4&ZX3|-KD2u1v?6@NAlgp%`Y0PVNde%~rdN|~H{+@#JazI`@)LBe zGi^{la2KU}&^qM-)5yw;hAlz=V~hG#uI#x*eT?%&byJHDZ)aFDD~}`_neq}XN&hD1 zUiAmw35?3Zg!^RM&MQW?2Sfw5hmiLAEr&ID^&yw6GL%>Y61x zBs0kG{akt*lr@WVO^|3-kckpa5FH>vLD?cHq}B$Qa_UZz8%B(34gU=@~6znB=y9y%H478EU|6JBGw+)Nfv z8;@&EsC{02?CBL<&=>L3bN{oAK6ZClS$*+-Le;q7L@ri1jXA+GIkMh5v1|UhOjAj``6jVv?s-^`5NZ-K`h5lvKB6=f+YD06r>s`_wdE}{^4x5I%O`f*ckW@;< z`G~Y}?mCAo*!c&-MW~oYMc6pasWiUAX^=iHx3IC%S@Q;q8JywZAlH6CNJ-z@d5^z! zOX*0-d~a$>`5(wMT4!oZTcSm(u>Vm%z3PB#7l&~6(+Qi&=@hx#SY_MJAvzl z!&Q~|Z)lV4vZB%tmK%EFA)D;YC)zqPI>_qpts7&_hIwg)^@g5)scsAGO;c!Y@nFk? z#Bd*WAxX&xmM(HRM#xw`d-~X*vYoJ;AQz8NDJMAvm2cNAsc+o5f<(ytytt1rjN}Nh zdQ==)qVV%c+OkIEL+5m7(vBg4WxE_;;P2Zl7(2xdATDGu7V+i!YDy%9)t35}!l(u0O&tQN$^zq32KGb3{Htspj^ zp>XSW_l2qhweR0v=X~A>qtsL10zk^z0VPFSib2dp2#@%c4U;9Gd7AS--p%D>T}cSQav&@Ly1V~&&@ulNYN491v(j`a!`v)mx;I2%0(#z z@h21~PYRSA%sFHn=rSAfz61ol`iW79*72V-v$p4N0o;>+PMq<7^YMux#stpP zntFsmpWcIuypx$r;$h@MWExAdHs3RTav3HDw^XpO|4cHIgvVWO33h%mc;@cvi^tS? znR-nNy>l)>Cs1TvnnCnP`R)eV)X?Dj#c|t0P=Lkeu;PAska(B5dH*tJ0qN71-{Ty# z{H-czf{Zys>00D2+c_6ANVG>7wR?hwXKl`9D~8{#0AV?r8(9gCao;vsBu^dp>kxA( zFQCvP>-uA_;^jTX^AHV%dkHL&oAHygr%Y0VO!Zw0n!gkV(D5y9h}y2gEn{o;#4;47 zt$gv+V$1F2Wp2rpEhCK%3dkwtJTK=QyO7%&4$2?@J~s>#yUs6<%nFm%0DS5V6i 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 z%0D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

S-XuC~$-y>2mxzxaiDd4phmU1=ca2 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 z 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 z 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

    1`D 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

uRC*H;`5M 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

uJ@2sJu26~vk>#^L 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

5+ilcPY20;$dT)1`nW-te{V18Mv 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

kd!A|VXc%A3u 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%0DpO%$u8@IcZWZAub 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 -zB;oP_U$m!Lv?oR!DF6L8`@mf@~?DMYpQN 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 YU=$3WoDOosra8_txp9hc+4_P1*$}tQYybcN diff --git a/frontend/appflowy_tauri/public/tauri.svg b/frontend/appflowy_tauri/public/tauri.svg deleted file mode 100644 index 31b62c9280..0000000000 --- a/frontend/appflowy_tauri/public/tauri.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/frontend/appflowy_tauri/public/vite.svg b/frontend/appflowy_tauri/public/vite.svg deleted file mode 100644 index e7b8dfb1b2..0000000000 --- a/frontend/appflowy_tauri/public/vite.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/frontend/appflowy_tauri/scripts/i18n/index.cjs b/frontend/appflowy_tauri/scripts/i18n/index.cjs deleted file mode 100644 index c3789e0c56..0000000000 --- a/frontend/appflowy_tauri/scripts/i18n/index.cjs +++ /dev/null @@ -1,63 +0,0 @@ -const languages = [ - 'ar-SA', - 'ca-ES', - 'de-DE', - 'en', - 'es-VE', - 'eu-ES', - 'fr-FR', - 'hu-HU', - 'id-ID', - 'it-IT', - 'ja-JP', - 'ko-KR', - 'pl-PL', - 'pt-BR', - 'pt-PT', - 'ru-RU', - 'sv-SE', - 'th-TH', - 'tr-TR', - 'zh-CN', - 'zh-TW', -]; - -const fs = require('fs'); -languages.forEach(language => { - const json = require(`../../../resources/translations/${language}.json`); - const outputJSON = flattenJSON(json); - const output = JSON.stringify(outputJSON); - const isExistDir = fs.existsSync('./src/appflowy_app/i18n/translations'); - if (!isExistDir) { - fs.mkdirSync('./src/appflowy_app/i18n/translations'); - } - fs.writeFile(`./src/appflowy_app/i18n/translations/${language}.json`, new Uint8Array(Buffer.from(output)), (res) => { - if (res) { - console.error(res); - } - }) -}); - - -function flattenJSON(obj, prefix = '') { - let result = {}; - const pluralsKey = ["one", "other", "few", "many", "two", "zero"]; - - for (let key in obj) { - if (typeof obj[key] === 'object' && obj[key] !== null) { - - const nestedKeys = flattenJSON(obj[key], `${prefix}${key}.`); - result = { ...result, ...nestedKeys }; - } else { - let newKey = `${prefix}${key}`; - let replaceChar = '{' - if (pluralsKey.includes(key)) { - newKey = `${prefix.slice(0, -1)}_${key}`; - } - result[newKey] = obj[key].replaceAll('{', '{{').replaceAll('}', '}}'); - } - } - - return result; -} - diff --git a/frontend/appflowy_tauri/scripts/update_version.cjs b/frontend/appflowy_tauri/scripts/update_version.cjs deleted file mode 100644 index 498b8c3e4f..0000000000 --- a/frontend/appflowy_tauri/scripts/update_version.cjs +++ /dev/null @@ -1,31 +0,0 @@ -const fs = require('fs'); -const path = require('path'); - -if (process.argv.length < 3) { - console.error('Usage: node update-tauri-version.js '); - process.exit(1); -} - -const newVersion = process.argv[2]; - -const tauriConfigPath = path.join(__dirname, '../src-tauri', 'tauri.conf.json'); - -fs.readFile(tauriConfigPath, 'utf8', (err, data) => { - if (err) { - console.error('Error reading tauri.conf.json:', err); - return; - } - - const config = JSON.parse(data); - - config.package.version = newVersion; - - fs.writeFile(tauriConfigPath, JSON.stringify(config, null, 2), 'utf8', (err) => { - if (err) { - console.error('Error writing tauri.conf.json:', err); - return; - } - - console.log(`Tauri version updated to ${newVersion} successfully.`); - }); -}); diff --git a/frontend/appflowy_tauri/src-tauri/.cargo/config.toml b/frontend/appflowy_tauri/src-tauri/.cargo/config.toml deleted file mode 100644 index bff29e6e17..0000000000 --- a/frontend/appflowy_tauri/src-tauri/.cargo/config.toml +++ /dev/null @@ -1,2 +0,0 @@ -[build] -rustflags = ["--cfg", "tokio_unstable"] diff --git a/frontend/appflowy_tauri/src-tauri/.gitignore b/frontend/appflowy_tauri/src-tauri/.gitignore deleted file mode 100644 index 61e1bdd46a..0000000000 --- a/frontend/appflowy_tauri/src-tauri/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -# Generated by Cargo -# will have compiled files and executables -/target/ -.env diff --git a/frontend/appflowy_tauri/src-tauri/Cargo.lock b/frontend/appflowy_tauri/src-tauri/Cargo.lock deleted file mode 100644 index 4b4d22625e..0000000000 --- a/frontend/appflowy_tauri/src-tauri/Cargo.lock +++ /dev/null @@ -1,9227 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "accessory" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "850bb534b9dc04744fbbb71d30ad6d25a7e4cf6dc33e223c81ef3a92ebab4e0b" -dependencies = [ - "macroific", - "proc-macro2", - "quote", - "syn 2.0.47", -] - -[[package]] -name = "addr2line" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "aead" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" -dependencies = [ - "crypto-common", - "generic-array", -] - -[[package]] -name = "aes" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" -dependencies = [ - "cfg-if", - "cipher", - "cpufeatures", -] - -[[package]] -name = "aes-gcm" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "209b47e8954a928e1d72e86eca7000ebb6655fe1436d33eefc2201cad027e237" -dependencies = [ - "aead", - "aes", - "cipher", - "ctr", - "ghash", - "subtle", -] - -[[package]] -name = "again" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05802a5ad4d172eaf796f7047b42d0af9db513585d16d4169660a21613d34b93" -dependencies = [ - "log", - "rand 0.7.3", - "wasm-timer", -] - -[[package]] -name = "ahash" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" -dependencies = [ - "getrandom 0.2.10", - "once_cell", - "version_check", -] - -[[package]] -name = "ahash" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" -dependencies = [ - "cfg-if", - "getrandom 0.2.10", - "once_cell", - "version_check", - "zerocopy", -] - -[[package]] -name = "aho-corasick" -version = "0.7.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" -dependencies = [ - "memchr", -] - -[[package]] -name = "aho-corasick" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" -dependencies = [ - "memchr", -] - -[[package]] -name = "allo-isolate" -version = "0.1.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b6d794345b06592d0ebeed8e477e41b71e5a0a49df4fc0e4184d5938b99509" -dependencies = [ - "atomic", - "pin-project", -] - -[[package]] -name = "alloc-no-stdlib" -version = "2.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" - -[[package]] -name = "alloc-stdlib" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" -dependencies = [ - "alloc-no-stdlib", -] - -[[package]] -name = "allocator-api2" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" - -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] -name = "anyhow" -version = "1.0.94" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7" - -[[package]] -name = "app-error" -version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ea131f0baab67defe7591067357eced490072372#ea131f0baab67defe7591067357eced490072372" -dependencies = [ - "anyhow", - "bincode", - "getrandom 0.2.10", - "reqwest", - "serde", - "serde_json", - "serde_repr", - "thiserror", - "tokio", - "tsify", - "url", - "uuid", - "wasm-bindgen", -] - -[[package]] -name = "appflowy-ai-client" -version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ea131f0baab67defe7591067357eced490072372#ea131f0baab67defe7591067357eced490072372" -dependencies = [ - "anyhow", - "bytes", - "futures", - "pin-project", - "serde", - "serde_json", - "serde_repr", - "thiserror", -] - -[[package]] -name = "appflowy-local-ai" -version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-LocalAI?rev=6f064efe232268f8d396edbb4b84d57fbb640f13#6f064efe232268f8d396edbb4b84d57fbb640f13" -dependencies = [ - "anyhow", - "appflowy-plugin", - "bytes", - "reqwest", - "serde", - "serde_json", - "tokio", - "tokio-stream", - "tokio-util", - "tracing", - "zip 2.2.0", - "zip-extensions", -] - -[[package]] -name = "appflowy-plugin" -version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-LocalAI?rev=6f064efe232268f8d396edbb4b84d57fbb640f13#6f064efe232268f8d396edbb4b84d57fbb640f13" -dependencies = [ - "anyhow", - "cfg-if", - "crossbeam-utils", - "log", - "once_cell", - "parking_lot 0.12.1", - "serde", - "serde_json", - "thiserror", - "tokio", - "tokio-stream", - "tracing", - "xattr 1.3.1", -] - -[[package]] -name = "appflowy_tauri" -version = "0.0.0" -dependencies = [ - "bytes", - "dotenv", - "flowy-ai", - "flowy-config", - "flowy-core", - "flowy-date", - "flowy-document", - "flowy-error", - "flowy-notification", - "flowy-search", - "flowy-user", - "lib-dispatch", - "semver", - "serde", - "serde_json", - "tauri", - "tauri-build", - "tauri-plugin-deep-link", - "tauri-utils", - "tracing", - "uuid", -] - -[[package]] -name = "arbitrary" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" -dependencies = [ - "derive_arbitrary", -] - -[[package]] -name = "arc-swap" -version = "1.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" - -[[package]] -name = "arrayvec" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" - -[[package]] -name = "async-compression" -version = "0.4.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "103db485efc3e41214fe4fda9f3dbeae2eb9082f48fd236e6095627a9422066e" -dependencies = [ - "bzip2", - "deflate64", - "flate2", - "futures-core", - "futures-io", - "memchr", - "pin-project-lite", - "xz2", - "zstd 0.13.2", - "zstd-safe 7.2.0", -] - -[[package]] -name = "async-lock" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" -dependencies = [ - "event-listener", - "event-listener-strategy", - "pin-project-lite", -] - -[[package]] -name = "async-recursion" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.47", -] - -[[package]] -name = "async-stream" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" -dependencies = [ - "async-stream-impl", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-stream-impl" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.47", -] - -[[package]] -name = "async-trait" -version = "0.1.81" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.47", -] - -[[package]] -name = "async_zip" -version = "0.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b9f7252833d5ed4b00aa9604b563529dd5e11de9c23615de2dcdf91eb87b52" -dependencies = [ - "async-compression", - "chrono", - "crc32fast", - "futures-lite", - "pin-project", - "thiserror", - "tokio", - "tokio-util", -] - -[[package]] -name = "atk" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c3d816ce6f0e2909a96830d6911c2aff044370b1ef92d7f267b43bae5addedd" -dependencies = [ - "atk-sys", - "bitflags 1.3.2", - "glib", - "libc", -] - -[[package]] -name = "atk-sys" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58aeb089fb698e06db8089971c7ee317ab9644bade33383f63631437b03aafb6" -dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps 6.1.1", -] - -[[package]] -name = "atomic" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59bdb34bc650a32731b31bd8f0829cc15d24a708ee31559e0bb34f2bc320cba" - -[[package]] -name = "atomic_refcell" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79d6dc922a2792b006573f60b2648076355daeae5ce9cb59507e5908c9625d31" - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "backtrace" -version = "0.3.67" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca" -dependencies = [ - "addr2line", - "cc", - "cfg-if", - "libc", - "miniz_oxide 0.6.2", - "object", - "rustc-demangle", -] - -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - -[[package]] -name = "base64" -version = "0.21.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" - -[[package]] -name = "base64" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" - -[[package]] -name = "base64ct" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" - -[[package]] -name = "bincode" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" -dependencies = [ - "serde", -] - -[[package]] -name = "bindgen" -version = "0.69.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" -dependencies = [ - "bitflags 2.4.0", - "cexpr", - "clang-sys", - "itertools 0.12.1", - "lazy_static", - "lazycell", - "proc-macro2", - "quote", - "regex", - "rustc-hash", - "shlex", - "syn 2.0.47", -] - -[[package]] -name = "bit-set" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" -dependencies = [ - "bit-vec", -] - -[[package]] -name = "bit-vec" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" - -[[package]] -name = "bitpacking" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c1d3e2bfd8d06048a179f7b17afc3188effa10385e7b00dc65af6aae732ea92" -dependencies = [ - "crunchy", -] - -[[package]] -name = "bitvec" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" -dependencies = [ - "funty", - "radium", - "tap", - "wyz", -] - -[[package]] -name = "block" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - -[[package]] -name = "borsh" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d4d6dafc1a3bb54687538972158f07b2c948bc57d5890df22c0739098b3028" -dependencies = [ - "borsh-derive", - "cfg_aliases", -] - -[[package]] -name = "borsh-derive" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4918709cc4dd777ad2b6303ed03cb37f3ca0ccede8c1b0d28ac6db8f4710e0" -dependencies = [ - "once_cell", - "proc-macro-crate 2.0.2", - "proc-macro2", - "quote", - "syn 2.0.47", - "syn_derive", -] - -[[package]] -name = "brotli" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "516074a47ef4bce09577a3b379392300159ce5b1ba2e501ff1c819950066100f" -dependencies = [ - "alloc-no-stdlib", - "alloc-stdlib", - "brotli-decompressor", -] - -[[package]] -name = "brotli-decompressor" -version = "2.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e2e4afe60d7dd600fdd3de8d0f08c2b7ec039712e3b6137ff98b7004e82de4f" -dependencies = [ - "alloc-no-stdlib", - "alloc-stdlib", -] - -[[package]] -name = "bstr" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a246e68bb43f6cd9db24bea052a53e40405417c5fb372e3d1a8a7f770a564ef5" -dependencies = [ - "memchr", - "serde", -] - -[[package]] -name = "bumpalo" -version = "3.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" - -[[package]] -name = "bytecheck" -version = "0.6.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b6372023ac861f6e6dc89c8344a8f398fb42aaba2b5dbc649ca0c0e9dbcb627" -dependencies = [ - "bytecheck_derive", - "ptr_meta", - "simdutf8", -] - -[[package]] -name = "bytecheck_derive" -version = "0.6.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7ec4c6f261935ad534c0c22dbef2201b45918860eb1c574b972bd213a76af61" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "bytemuck" -version = "1.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" - -[[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - -[[package]] -name = "bytes" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" -dependencies = [ - "serde", -] - -[[package]] -name = "bzip2" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" -dependencies = [ - "bzip2-sys", - "libc", -] - -[[package]] -name = "bzip2-sys" -version = "0.1.11+1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" -dependencies = [ - "cc", - "libc", - "pkg-config", -] - -[[package]] -name = "cairo-rs" -version = "0.15.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c76ee391b03d35510d9fa917357c7f1855bd9a6659c95a1b392e33f49b3369bc" -dependencies = [ - "bitflags 1.3.2", - "cairo-sys-rs", - "glib", - "libc", - "thiserror", -] - -[[package]] -name = "cairo-sys-rs" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c55d429bef56ac9172d25fecb85dc8068307d17acd74b377866b7a1ef25d3c8" -dependencies = [ - "glib-sys", - "libc", - "system-deps 6.1.1", -] - -[[package]] -name = "cargo_toml" -version = "0.15.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "599aa35200ffff8f04c1925aa1acc92fa2e08874379ef42e210a80e527e60838" -dependencies = [ - "serde", - "toml 0.7.5", -] - -[[package]] -name = "cc" -version = "1.0.79" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" -dependencies = [ - "jobserver", -] - -[[package]] -name = "census" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f4c707c6a209cbe82d10abd08e1ea8995e9ea937d2550646e02798948992be0" - -[[package]] -name = "cesu8" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" - -[[package]] -name = "cexpr" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" -dependencies = [ - "nom", -] - -[[package]] -name = "cfb" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f" -dependencies = [ - "byteorder", - "fnv", - "uuid", -] - -[[package]] -name = "cfg-expr" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3431df59f28accaf4cb4eed4a9acc66bea3f3c3753aa6cdc2f024174ef232af7" -dependencies = [ - "smallvec", -] - -[[package]] -name = "cfg-expr" -version = "0.15.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "215c0072ecc28f92eeb0eea38ba63ddfcb65c2828c46311d646f1a3ff5f9841c" -dependencies = [ - "smallvec", - "target-lexicon", -] - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "cfg_aliases" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" - -[[package]] -name = "chrono" -version = "0.4.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" -dependencies = [ - "android-tzdata", - "iana-time-zone", - "js-sys", - "num-traits", - "serde", - "wasm-bindgen", - "windows-targets 0.52.0", -] - -[[package]] -name = "chrono-tz" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58549f1842da3080ce63002102d5bc954c7bc843d4f47818e642abdc36253552" -dependencies = [ - "chrono", - "chrono-tz-build 0.0.2", - "phf 0.10.1", -] - -[[package]] -name = "chrono-tz" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9cc2b23599e6d7479755f3594285efb3f74a1bdca7a7374948bc831e23a552" -dependencies = [ - "chrono", - "chrono-tz-build 0.1.0", - "phf 0.11.2", -] - -[[package]] -name = "chrono-tz" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd6dd8046d00723a59a2f8c5f295c515b9bb9a331ee4f8f3d4dd49e428acd3b6" -dependencies = [ - "chrono", - "chrono-tz-build 0.4.0", - "phf 0.11.2", -] - -[[package]] -name = "chrono-tz-build" -version = "0.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db058d493fb2f65f41861bfed7e3fe6335264a9f0f92710cab5bdf01fef09069" -dependencies = [ - "parse-zoneinfo", - "phf 0.10.1", - "phf_codegen 0.10.0", -] - -[[package]] -name = "chrono-tz-build" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9998fb9f7e9b2111641485bf8beb32f92945f97f92a3d061f744cfef335f751" -dependencies = [ - "parse-zoneinfo", - "phf 0.11.2", - "phf_codegen 0.11.2", -] - -[[package]] -name = "chrono-tz-build" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94fea34d77a245229e7746bd2beb786cd2a896f306ff491fb8cecb3074b10a7" -dependencies = [ - "parse-zoneinfo", - "phf_codegen 0.11.2", -] - -[[package]] -name = "cipher" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" -dependencies = [ - "crypto-common", - "inout", -] - -[[package]] -name = "clang-sys" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f" -dependencies = [ - "glob", - "libc", - "libloading", -] - -[[package]] -name = "client-api" -version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ea131f0baab67defe7591067357eced490072372#ea131f0baab67defe7591067357eced490072372" -dependencies = [ - "again", - "anyhow", - "app-error", - "arc-swap", - "async-trait", - "base64 0.22.1", - "bincode", - "brotli", - "bytes", - "chrono", - "client-api-entity", - "client-websocket", - "collab", - "collab-rt-entity", - "collab-rt-protocol", - "futures", - "futures-core", - "futures-util", - "getrandom 0.2.10", - "gotrue", - "infra", - "lazy_static", - "md5", - "mime", - "mime_guess", - "parking_lot 0.12.1", - "percent-encoding", - "pin-project", - "prost 0.13.3", - "rayon", - "reqwest", - "scraper 0.17.1", - "semver", - "serde", - "serde_json", - "serde_repr", - "serde_urlencoded", - "shared-entity", - "thiserror", - "tokio", - "tokio-retry", - "tokio-stream", - "tokio-util", - "tracing", - "url", - "uuid", - "wasm-bindgen-futures", - "yrs", - "zstd 0.13.2", -] - -[[package]] -name = "client-api-entity" -version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ea131f0baab67defe7591067357eced490072372#ea131f0baab67defe7591067357eced490072372" -dependencies = [ - "collab-entity", - "collab-rt-entity", - "database-entity", - "gotrue-entity", - "shared-entity", - "uuid", -] - -[[package]] -name = "client-websocket" -version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ea131f0baab67defe7591067357eced490072372#ea131f0baab67defe7591067357eced490072372" -dependencies = [ - "futures-channel", - "futures-util", - "http", - "httparse", - "js-sys", - "percent-encoding", - "thiserror", - "tokio", - "tokio-tungstenite", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "cmd_lib" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ba0f413777386d37f85afa5242f277a7b461905254c1af3c339d4af06800f62" -dependencies = [ - "cmd_lib_macros", - "faccess", - "lazy_static", - "log", - "os_pipe", -] - -[[package]] -name = "cmd_lib_macros" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e66605092ff6c6e37e0246601ae6c3f62dc1880e0599359b5f303497c112dc0" -dependencies = [ - "proc-macro-error", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "cocoa" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f425db7937052c684daec3bd6375c8abe2d146dca4b8b143d6db777c39138f3a" -dependencies = [ - "bitflags 1.3.2", - "block", - "cocoa-foundation", - "core-foundation", - "core-graphics", - "foreign-types", - "libc", - "objc", -] - -[[package]] -name = "cocoa-foundation" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "931d3837c286f56e3c58423ce4eba12d08db2374461a785c86f672b08b5650d6" -dependencies = [ - "bitflags 1.3.2", - "block", - "core-foundation", - "core-graphics-types", - "foreign-types", - "libc", - "objc", -] - -[[package]] -name = "collab" -version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=67b31e4bcc64d81332c37efecb0a8618681f1db9#67b31e4bcc64d81332c37efecb0a8618681f1db9" -dependencies = [ - "anyhow", - "arc-swap", - "async-trait", - "bincode", - "bytes", - "chrono", - "js-sys", - "lazy_static", - "serde", - "serde_json", - "serde_repr", - "thiserror", - "tokio", - "tokio-stream", - "tracing", - "unicode-segmentation", - "web-sys", - "yrs", -] - -[[package]] -name = "collab-database" -version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=67b31e4bcc64d81332c37efecb0a8618681f1db9#67b31e4bcc64d81332c37efecb0a8618681f1db9" -dependencies = [ - "anyhow", - "async-trait", - "base64 0.22.1", - "chrono", - "chrono-tz 0.10.0", - "collab", - "collab-entity", - "csv", - "dashmap 5.5.3", - "fancy-regex 0.13.0", - "futures", - "getrandom 0.2.10", - "iana-time-zone", - "js-sys", - "lazy_static", - "nanoid", - "percent-encoding", - "rayon", - "rust_decimal", - "rusty-money", - "serde", - "serde_json", - "serde_repr", - "sha2", - "strum", - "strum_macros 0.25.2", - "thiserror", - "tokio", - "tokio-stream", - "tokio-util", - "tracing", - "uuid", - "yrs", -] - -[[package]] -name = "collab-document" -version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=67b31e4bcc64d81332c37efecb0a8618681f1db9#67b31e4bcc64d81332c37efecb0a8618681f1db9" -dependencies = [ - "anyhow", - "arc-swap", - "collab", - "collab-entity", - "getrandom 0.2.10", - "markdown", - "nanoid", - "serde", - "serde_json", - "thiserror", - "tokio", - "tokio-stream", - "tracing", - "uuid", -] - -[[package]] -name = "collab-entity" -version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=67b31e4bcc64d81332c37efecb0a8618681f1db9#67b31e4bcc64d81332c37efecb0a8618681f1db9" -dependencies = [ - "anyhow", - "bytes", - "collab", - "getrandom 0.2.10", - "prost 0.13.3", - "prost-build", - "protoc-bin-vendored", - "serde", - "serde_json", - "serde_repr", - "thiserror", - "uuid", - "walkdir", -] - -[[package]] -name = "collab-folder" -version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=67b31e4bcc64d81332c37efecb0a8618681f1db9#67b31e4bcc64d81332c37efecb0a8618681f1db9" -dependencies = [ - "anyhow", - "arc-swap", - "chrono", - "collab", - "collab-entity", - "dashmap 5.5.3", - "getrandom 0.2.10", - "serde", - "serde_json", - "serde_repr", - "thiserror", - "tokio", - "tokio-stream", - "tracing", - "uuid", -] - -[[package]] -name = "collab-importer" -version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=67b31e4bcc64d81332c37efecb0a8618681f1db9#67b31e4bcc64d81332c37efecb0a8618681f1db9" -dependencies = [ - "anyhow", - "async-recursion", - "async-trait", - "async_zip", - "base64 0.22.1", - "chrono", - "collab", - "collab-database", - "collab-document", - "collab-entity", - "collab-folder", - "csv", - "fancy-regex 0.13.0", - "futures", - "futures-lite", - "futures-util", - "fxhash", - "hex", - "markdown", - "percent-encoding", - "rayon", - "sanitize-filename", - "serde", - "serde_json", - "sha2", - "thiserror", - "tokio", - "tokio-util", - "tracing", - "uuid", - "walkdir", - "zip 0.6.6", -] - -[[package]] -name = "collab-integrate" -version = "0.1.0" -dependencies = [ - "anyhow", - "arc-swap", - "async-trait", - "collab", - "collab-database", - "collab-document", - "collab-entity", - "collab-folder", - "collab-plugins", - "collab-user", - "diesel", - "flowy-error", - "flowy-sqlite", - "futures", - "lib-infra", - "serde", - "serde_json", - "tokio", - "tracing", -] - -[[package]] -name = "collab-plugins" -version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=67b31e4bcc64d81332c37efecb0a8618681f1db9#67b31e4bcc64d81332c37efecb0a8618681f1db9" -dependencies = [ - "anyhow", - "async-stream", - "async-trait", - "bincode", - "bytes", - "chrono", - "collab", - "collab-entity", - "futures", - "futures-util", - "getrandom 0.2.10", - "indexed_db_futures", - "js-sys", - "lazy_static", - "rand 0.8.5", - "rocksdb", - "serde", - "serde_json", - "similar 2.2.1", - "smallvec", - "thiserror", - "tokio", - "tokio-retry", - "tokio-stream", - "tracing", - "tracing-wasm", - "uuid", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "yrs", -] - -[[package]] -name = "collab-rt-entity" -version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ea131f0baab67defe7591067357eced490072372#ea131f0baab67defe7591067357eced490072372" -dependencies = [ - "anyhow", - "bincode", - "bytes", - "chrono", - "client-websocket", - "collab", - "collab-entity", - "collab-rt-protocol", - "database-entity", - "prost 0.13.3", - "prost-build", - "protoc-bin-vendored", - "serde", - "serde_json", - "serde_repr", - "thiserror", - "tokio-tungstenite", - "yrs", -] - -[[package]] -name = "collab-rt-protocol" -version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ea131f0baab67defe7591067357eced490072372#ea131f0baab67defe7591067357eced490072372" -dependencies = [ - "anyhow", - "async-trait", - "bincode", - "collab", - "collab-entity", - "serde", - "thiserror", - "tokio", - "tracing", - "yrs", -] - -[[package]] -name = "collab-user" -version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=67b31e4bcc64d81332c37efecb0a8618681f1db9#67b31e4bcc64d81332c37efecb0a8618681f1db9" -dependencies = [ - "anyhow", - "collab", - "collab-entity", - "getrandom 0.2.10", - "serde", - "serde_json", - "tokio", - "tokio-stream", - "tracing", -] - -[[package]] -name = "color_quant" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" - -[[package]] -name = "combine" -version = "4.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" -dependencies = [ - "bytes", - "memchr", -] - -[[package]] -name = "concurrent-queue" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "console" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3993e6445baa160675931ec041a5e03ca84b9c6e32a056150d3aa2bdda0a1f45" -dependencies = [ - "encode_unicode", - "lazy_static", - "libc", - "regex", - "terminal_size", - "unicode-width", - "winapi", -] - -[[package]] -name = "constant_time_eq" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" - -[[package]] -name = "constant_time_eq" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" - -[[package]] -name = "convert_case" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" - -[[package]] -name = "cookie" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7efb37c3e1ccb1ff97164ad95ac1606e8ccd35b3fa0a7d99a304c7f4a428cc24" -dependencies = [ - "percent-encoding", - "time", - "version_check", -] - -[[package]] -name = "cookie_store" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "387461abbc748185c3a6e1673d826918b450b87ff22639429c694619a83b6cf6" -dependencies = [ - "cookie", - "idna 0.3.0", - "log", - "publicsuffix", - "serde", - "serde_derive", - "serde_json", - "time", - "url", -] - -[[package]] -name = "core-foundation" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" - -[[package]] -name = "core-graphics" -version = "0.22.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "core-graphics-types", - "foreign-types", - "libc", -] - -[[package]] -name = "core-graphics-types" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bb142d41022986c1d8ff29103a1411c8a3dfad3552f87a4f8dc50d61d4f4e33" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "libc", -] - -[[package]] -name = "cpufeatures" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03e69e28e9f7f77debdedbaafa2866e1de9ba56df55a8bd7cfc724c25a09987c" -dependencies = [ - "libc", -] - -[[package]] -name = "crc" -version = "3.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" -dependencies = [ - "crc-catalog", -] - -[[package]] -name = "crc-catalog" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" - -[[package]] -name = "crc32fast" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crossbeam-channel" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" -dependencies = [ - "cfg-if", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" -dependencies = [ - "cfg-if", - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" -dependencies = [ - "autocfg", - "cfg-if", - "crossbeam-utils", - "memoffset", - "scopeguard", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" - -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "rand_core 0.6.4", - "typenum", -] - -[[package]] -name = "cssparser" -version = "0.27.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "754b69d351cdc2d8ee09ae203db831e005560fc6030da058f86ad60c92a9cb0a" -dependencies = [ - "cssparser-macros", - "dtoa-short", - "itoa 0.4.8", - "matches", - "phf 0.8.0", - "proc-macro2", - "quote", - "smallvec", - "syn 1.0.109", -] - -[[package]] -name = "cssparser" -version = "0.31.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b3df4f93e5fbbe73ec01ec8d3f68bba73107993a5b1e7519273c32db9b0d5be" -dependencies = [ - "cssparser-macros", - "dtoa-short", - "itoa 1.0.6", - "phf 0.8.0", - "smallvec", -] - -[[package]] -name = "cssparser-macros" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" -dependencies = [ - "quote", - "syn 2.0.47", -] - -[[package]] -name = "csv" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe" -dependencies = [ - "csv-core", - "itoa 1.0.6", - "ryu", - "serde", -] - -[[package]] -name = "csv-core" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70" -dependencies = [ - "memchr", -] - -[[package]] -name = "ctor" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30d2b3721e861707777e3195b0158f950ae6dc4a27e4d02ff9f67e3eb3de199e" -dependencies = [ - "quote", - "syn 2.0.47", -] - -[[package]] -name = "ctr" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" -dependencies = [ - "cipher", -] - -[[package]] -name = "darling" -version = "0.20.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0558d22a7b463ed0241e993f76f09f30b126687447751a8638587b864e4b3944" -dependencies = [ - "darling_core", - "darling_macro", -] - -[[package]] -name = "darling_core" -version = "0.20.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab8bfa2e259f8ee1ce5e97824a3c55ec4404a0d772ca7fa96bf19f0752a046eb" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim 0.10.0", - "syn 2.0.47", -] - -[[package]] -name = "darling_macro" -version = "0.20.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29a358ff9f12ec09c3e61fef9b5a9902623a695a46a917b07f269bff1445611a" -dependencies = [ - "darling_core", - "quote", - "syn 2.0.47", -] - -[[package]] -name = "dashmap" -version = "5.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" -dependencies = [ - "cfg-if", - "hashbrown 0.14.3", - "lock_api", - "once_cell", - "parking_lot_core 0.9.8", -] - -[[package]] -name = "dashmap" -version = "6.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "804c8821570c3f8b70230c2ba75ffa5c0f9a4189b9a432b6656c536712acae28" -dependencies = [ - "cfg-if", - "crossbeam-utils", - "hashbrown 0.14.3", - "lock_api", - "once_cell", - "parking_lot_core 0.9.8", -] - -[[package]] -name = "data-encoding" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" - -[[package]] -name = "database-entity" -version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ea131f0baab67defe7591067357eced490072372#ea131f0baab67defe7591067357eced490072372" -dependencies = [ - "anyhow", - "app-error", - "appflowy-ai-client", - "bincode", - "bytes", - "chrono", - "collab-entity", - "infra", - "prost 0.13.3", - "serde", - "serde_json", - "serde_repr", - "thiserror", - "tracing", - "uuid", - "validator 0.19.0", -] - -[[package]] -name = "date_time_parser" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0521d96e513670773ac503e5f5239178c3aef16cffda1e77a3cdbdbe993fb5a" -dependencies = [ - "chrono", - "regex", -] - -[[package]] -name = "deflate64" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da692b8d1080ea3045efaab14434d40468c3d8657e42abddfffca87b428f4c1b" - -[[package]] -name = "delegate-display" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98a85201f233142ac819bbf6226e36d0b5e129a47bd325084674261c82d4cd66" -dependencies = [ - "macroific", - "proc-macro2", - "quote", - "syn 2.0.47", -] - -[[package]] -name = "deranged" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" -dependencies = [ - "powerfmt", - "serde", -] - -[[package]] -name = "derivative" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "derive_arbitrary" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.47", -] - -[[package]] -name = "derive_more" -version = "0.99.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" -dependencies = [ - "convert_case", - "proc-macro2", - "quote", - "rustc_version", - "syn 1.0.109", -] - -[[package]] -name = "deunicode" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "850878694b7933ca4c9569d30a34b55031b9b139ee1fc7b94a527c4ef960d690" - -[[package]] -name = "diesel" -version = "2.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62c6fcf842f17f8c78ecf7c81d75c5ce84436b41ee07e03f490fbb5f5a8731d8" -dependencies = [ - "chrono", - "diesel_derives", - "libsqlite3-sys", - "r2d2", - "serde_json", - "time", -] - -[[package]] -name = "diesel_derives" -version = "2.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef8337737574f55a468005a83499da720f20c65586241ffea339db9ecdfd2b44" -dependencies = [ - "diesel_table_macro_syntax", - "proc-macro2", - "quote", - "syn 2.0.47", -] - -[[package]] -name = "diesel_migrations" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6036b3f0120c5961381b570ee20a02432d7e2d27ea60de9578799cf9156914ac" -dependencies = [ - "diesel", - "migrations_internals", - "migrations_macros", -] - -[[package]] -name = "diesel_table_macro_syntax" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc5557efc453706fed5e4fa85006fe9817c224c3f480a34c7e5959fd700921c5" -dependencies = [ - "syn 2.0.47", -] - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "crypto-common", - "subtle", -] - -[[package]] -name = "dirs" -version = "5.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" -dependencies = [ - "dirs-sys", -] - -[[package]] -name = "dirs-next" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" -dependencies = [ - "cfg-if", - "dirs-sys-next", -] - -[[package]] -name = "dirs-sys" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" -dependencies = [ - "libc", - "option-ext", - "redox_users", - "windows-sys 0.48.0", -] - -[[package]] -name = "dirs-sys-next" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" -dependencies = [ - "libc", - "redox_users", - "winapi", -] - -[[package]] -name = "dispatch" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" - -[[package]] -name = "displaydoc" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.47", -] - -[[package]] -name = "dotenv" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" - -[[package]] -name = "downcast-rs" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" - -[[package]] -name = "dtoa" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65d09067bfacaa79114679b279d7f5885b53295b1e2cfb4e79c8e4bd3d633169" - -[[package]] -name = "dtoa-short" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbaceec3c6e4211c79e7b1800fb9680527106beb2f9c51904a3210c03a448c74" -dependencies = [ - "dtoa", -] - -[[package]] -name = "dunce" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" - -[[package]] -name = "dyn-clone" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b0cf012f1230e43cd00ebb729c6bb58707ecfa8ad08b52ef3a4ccd2697fc30" - -[[package]] -name = "ego-tree" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a68a4904193147e0a8dec3314640e6db742afd5f6e634f428a6af230d9b3591" - -[[package]] -name = "either" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" - -[[package]] -name = "embed-resource" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80663502655af01a2902dff3f06869330782267924bf1788410b74edcd93770a" -dependencies = [ - "cc", - "rustc_version", - "toml 0.7.5", - "vswhom", - "winreg 0.11.0", -] - -[[package]] -name = "embed_plist" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7" - -[[package]] -name = "encode_unicode" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" - -[[package]] -name = "encoding_rs" -version = "0.8.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "equivalent" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88bffebc5d80432c9b140ee17875ff173a8ab62faad5b257da912bd2f6c1c0a1" - -[[package]] -name = "errno" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "event-listener" -version = "5.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" -dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite", -] - -[[package]] -name = "event-listener-strategy" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" -dependencies = [ - "event-listener", - "pin-project-lite", -] - -[[package]] -name = "faccess" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59ae66425802d6a903e268ae1a08b8c38ba143520f227a205edf4e9c7e3e26d5" -dependencies = [ - "bitflags 1.3.2", - "libc", - "winapi", -] - -[[package]] -name = "fancy-regex" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0678ab2d46fa5195aaf59ad034c083d351377d4af57f3e073c074d0da3e3c766" -dependencies = [ - "bit-set", - "regex", -] - -[[package]] -name = "fancy-regex" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b95f7c0680e4142284cf8b22c14a476e87d61b004a3a0861872b32ef7ead40a2" -dependencies = [ - "bit-set", - "regex", -] - -[[package]] -name = "fancy-regex" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "531e46835a22af56d1e3b66f04844bed63158bc094a628bec1d321d9b4c44bf2" -dependencies = [ - "bit-set", - "regex-automata 0.4.7", - "regex-syntax 0.8.4", -] - -[[package]] -name = "fancy_constructor" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f71f317e4af73b2f8f608fac190c52eac4b1879d2145df1db2fe48881ca69435" -dependencies = [ - "macroific", - "proc-macro2", - "quote", - "syn 2.0.47", -] - -[[package]] -name = "fastdivide" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25c7df09945d65ea8d70b3321547ed414bbc540aad5bac6883d021b970f35b04" - -[[package]] -name = "fastrand" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" -dependencies = [ - "getrandom 0.2.10", -] - -[[package]] -name = "fdeflate" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d329bdeac514ee06249dabc27877490f17f5d371ec693360768b838e19f3ae10" -dependencies = [ - "simd-adler32", -] - -[[package]] -name = "field-offset" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" -dependencies = [ - "memoffset", - "rustc_version", -] - -[[package]] -name = "filetime" -version = "0.2.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall 0.4.1", - "windows-sys 0.52.0", -] - -[[package]] -name = "fixedbitset" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" - -[[package]] -name = "flate2" -version = "1.0.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" -dependencies = [ - "crc32fast", - "miniz_oxide 0.7.1", -] - -[[package]] -name = "flowy-ai" -version = "0.1.0" -dependencies = [ - "allo-isolate", - "anyhow", - "appflowy-local-ai", - "appflowy-plugin", - "arc-swap", - "base64 0.21.5", - "bytes", - "collab-integrate", - "dashmap 6.0.1", - "flowy-ai-pub", - "flowy-codegen", - "flowy-derive", - "flowy-error", - "flowy-notification", - "flowy-sqlite", - "flowy-storage-pub", - "futures", - "futures-util", - "lib-dispatch", - "lib-infra", - "log", - "md5", - "notify", - "pin-project", - "protobuf", - "reqwest", - "serde", - "serde_json", - "sha2", - "strum_macros 0.21.1", - "tokio", - "tokio-stream", - "tokio-util", - "tracing", - "uuid", - "validator 0.18.1", - "zip 2.2.0", - "zip-extensions", -] - -[[package]] -name = "flowy-ai-pub" -version = "0.1.0" -dependencies = [ - "bytes", - "client-api", - "flowy-error", - "futures", - "lib-infra", - "serde_json", -] - -[[package]] -name = "flowy-ast" -version = "0.1.0" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "flowy-codegen" -version = "0.1.0" -dependencies = [ - "cmd_lib", - "console", - "fancy-regex 0.10.0", - "flowy-ast", - "itertools 0.10.5", - "lazy_static", - "log", - "phf 0.8.0", - "protoc-bin-vendored", - "protoc-rust", - "quote", - "serde", - "serde_json", - "similar 1.3.0", - "syn 1.0.109", - "tera", - "toml 0.5.11", - "walkdir", -] - -[[package]] -name = "flowy-config" -version = "0.1.0" -dependencies = [ - "bytes", - "flowy-codegen", - "flowy-derive", - "flowy-error", - "flowy-sqlite", - "lib-dispatch", - "protobuf", - "strum_macros 0.21.1", -] - -[[package]] -name = "flowy-core" -version = "0.1.0" -dependencies = [ - "anyhow", - "appflowy-local-ai", - "arc-swap", - "base64 0.21.5", - "bytes", - "client-api", - "collab", - "collab-entity", - "collab-folder", - "collab-integrate", - "collab-plugins", - "dashmap 6.0.1", - "diesel", - "flowy-ai", - "flowy-ai-pub", - "flowy-config", - "flowy-database-pub", - "flowy-database2", - "flowy-date", - "flowy-document", - "flowy-document-pub", - "flowy-error", - "flowy-folder", - "flowy-folder-pub", - "flowy-search", - "flowy-search-pub", - "flowy-server", - "flowy-server-pub", - "flowy-sqlite", - "flowy-storage", - "flowy-storage-pub", - "flowy-user", - "flowy-user-pub", - "futures", - "futures-core", - "lib-dispatch", - "lib-infra", - "lib-log", - "semver", - "serde", - "serde_json", - "serde_repr", - "sysinfo", - "tokio", - "tokio-stream", - "tracing", - "uuid", - "walkdir", -] - -[[package]] -name = "flowy-database-pub" -version = "0.1.0" -dependencies = [ - "anyhow", - "client-api", - "collab", - "collab-entity", - "flowy-error", - "lib-infra", -] - -[[package]] -name = "flowy-database2" -version = "0.1.0" -dependencies = [ - "anyhow", - "arc-swap", - "async-stream", - "async-trait", - "bytes", - "chrono", - "chrono-tz 0.8.2", - "collab", - "collab-database", - "collab-entity", - "collab-integrate", - "collab-plugins", - "csv", - "dashmap 6.0.1", - "fancy-regex 0.11.0", - "flowy-codegen", - "flowy-database-pub", - "flowy-derive", - "flowy-error", - "flowy-notification", - "futures", - "indexmap 2.1.0", - "lazy_static", - "lib-dispatch", - "lib-infra", - "moka", - "nanoid", - "protobuf", - "rayon", - "rust_decimal", - "rusty-money", - "serde", - "serde_json", - "serde_repr", - "strum", - "strum_macros 0.25.2", - "tokio", - "tokio-util", - "tracing", - "url", - "validator 0.18.1", -] - -[[package]] -name = "flowy-date" -version = "0.1.0" -dependencies = [ - "bytes", - "chrono", - "date_time_parser", - "fancy-regex 0.11.0", - "flowy-codegen", - "flowy-derive", - "flowy-error", - "lib-dispatch", - "protobuf", - "strum_macros 0.21.1", - "tracing", -] - -[[package]] -name = "flowy-derive" -version = "0.1.0" -dependencies = [ - "dashmap 6.0.1", - "flowy-ast", - "flowy-codegen", - "lazy_static", - "proc-macro2", - "quote", - "serde_json", - "syn 1.0.109", - "walkdir", -] - -[[package]] -name = "flowy-document" -version = "0.1.0" -dependencies = [ - "anyhow", - "bytes", - "collab", - "collab-document", - "collab-entity", - "collab-integrate", - "collab-plugins", - "dashmap 6.0.1", - "flowy-codegen", - "flowy-derive", - "flowy-document-pub", - "flowy-error", - "flowy-notification", - "flowy-storage-pub", - "futures", - "getrandom 0.2.10", - "indexmap 2.1.0", - "lib-dispatch", - "lib-infra", - "nanoid", - "protobuf", - "scraper 0.18.1", - "serde", - "serde_json", - "strum_macros 0.21.1", - "tokio", - "tokio-stream", - "tracing", - "uuid", - "validator 0.18.1", -] - -[[package]] -name = "flowy-document-pub" -version = "0.1.0" -dependencies = [ - "anyhow", - "collab", - "collab-document", - "flowy-error", - "lib-infra", -] - -[[package]] -name = "flowy-encrypt" -version = "0.1.0" -dependencies = [ - "aes-gcm", - "anyhow", - "base64 0.21.5", - "getrandom 0.2.10", - "hmac", - "pbkdf2 0.12.2", - "rand 0.8.5", - "sha2", -] - -[[package]] -name = "flowy-error" -version = "0.1.0" -dependencies = [ - "anyhow", - "bytes", - "client-api", - "collab", - "collab-database", - "collab-document", - "collab-folder", - "collab-plugins", - "fancy-regex 0.11.0", - "flowy-codegen", - "flowy-derive", - "flowy-sqlite", - "lib-dispatch", - "protobuf", - "r2d2", - "reqwest", - "serde", - "serde_json", - "serde_repr", - "tantivy", - "thiserror", - "tokio", - "url", - "validator 0.18.1", -] - -[[package]] -name = "flowy-folder" -version = "0.1.0" -dependencies = [ - "arc-swap", - "async-trait", - "bytes", - "chrono", - "client-api", - "collab", - "collab-document", - "collab-entity", - "collab-folder", - "collab-integrate", - "collab-plugins", - "dashmap 6.0.1", - "flowy-codegen", - "flowy-derive", - "flowy-error", - "flowy-folder-pub", - "flowy-notification", - "flowy-search-pub", - "flowy-sqlite", - "futures", - "lazy_static", - "lib-dispatch", - "lib-infra", - "nanoid", - "protobuf", - "regex", - "serde", - "serde_json", - "strum_macros 0.21.1", - "tokio", - "tokio-stream", - "tracing", - "unicode-segmentation", - "uuid", - "validator 0.18.1", -] - -[[package]] -name = "flowy-folder-pub" -version = "0.1.0" -dependencies = [ - "anyhow", - "client-api", - "collab", - "collab-entity", - "collab-folder", - "flowy-error", - "lib-infra", - "serde", - "serde_json", - "uuid", -] - -[[package]] -name = "flowy-notification" -version = "0.1.0" -dependencies = [ - "bytes", - "dashmap 6.0.1", - "flowy-codegen", - "flowy-derive", - "lazy_static", - "lib-dispatch", - "protobuf", - "serde", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "flowy-search" -version = "0.1.0" -dependencies = [ - "async-stream", - "bytes", - "collab", - "collab-folder", - "diesel", - "diesel_derives", - "diesel_migrations", - "flowy-codegen", - "flowy-derive", - "flowy-error", - "flowy-folder", - "flowy-notification", - "flowy-search-pub", - "flowy-sqlite", - "flowy-user", - "futures", - "lib-dispatch", - "lib-infra", - "protobuf", - "serde", - "serde_json", - "strsim 0.11.0", - "strum_macros 0.26.1", - "tantivy", - "tempfile", - "tokio", - "tracing", - "validator 0.18.1", -] - -[[package]] -name = "flowy-search-pub" -version = "0.1.0" -dependencies = [ - "client-api", - "collab", - "collab-folder", - "flowy-error", - "futures", - "lib-infra", -] - -[[package]] -name = "flowy-server" -version = "0.1.0" -dependencies = [ - "anyhow", - "arc-swap", - "bytes", - "chrono", - "client-api", - "collab", - "collab-database", - "collab-document", - "collab-entity", - "collab-folder", - "collab-plugins", - "collab-user", - "dashmap 6.0.1", - "flowy-ai-pub", - "flowy-database-pub", - "flowy-document-pub", - "flowy-encrypt", - "flowy-error", - "flowy-folder-pub", - "flowy-search-pub", - "flowy-server-pub", - "flowy-storage", - "flowy-storage-pub", - "flowy-user-pub", - "futures", - "futures-util", - "hex", - "hyper", - "lazy_static", - "lib-dispatch", - "lib-infra", - "mime_guess", - "postgrest", - "rand 0.8.5", - "reqwest", - "semver", - "serde", - "serde_json", - "thiserror", - "tokio", - "tokio-retry", - "tokio-stream", - "tokio-util", - "tracing", - "url", - "uuid", - "yrs", -] - -[[package]] -name = "flowy-server-pub" -version = "0.1.0" -dependencies = [ - "flowy-error", - "serde", - "serde_repr", -] - -[[package]] -name = "flowy-sqlite" -version = "0.1.0" -dependencies = [ - "anyhow", - "diesel", - "diesel_derives", - "diesel_migrations", - "libsqlite3-sys", - "r2d2", - "scheduled-thread-pool", - "serde", - "serde_json", - "thiserror", - "tracing", -] - -[[package]] -name = "flowy-storage" -version = "0.1.0" -dependencies = [ - "allo-isolate", - "anyhow", - "async-trait", - "bytes", - "chrono", - "collab-importer", - "dashmap 6.0.1", - "flowy-codegen", - "flowy-derive", - "flowy-error", - "flowy-notification", - "flowy-sqlite", - "flowy-storage-pub", - "futures-util", - "fxhash", - "lib-dispatch", - "lib-infra", - "mime_guess", - "protobuf", - "serde", - "serde_json", - "strum_macros 0.25.2", - "tokio", - "tracing", - "url", -] - -[[package]] -name = "flowy-storage-pub" -version = "0.1.0" -dependencies = [ - "anyhow", - "async-trait", - "bytes", - "client-api-entity", - "flowy-error", - "lib-infra", - "mime", - "mime_guess", - "serde", - "serde_json", - "tokio", - "tracing", -] - -[[package]] -name = "flowy-user" -version = "0.1.0" -dependencies = [ - "anyhow", - "arc-swap", - "base64 0.21.5", - "bytes", - "chrono", - "client-api", - "collab", - "collab-database", - "collab-document", - "collab-entity", - "collab-folder", - "collab-integrate", - "collab-plugins", - "collab-user", - "dashmap 6.0.1", - "diesel", - "diesel_derives", - "fancy-regex 0.11.0", - "flowy-codegen", - "flowy-derive", - "flowy-encrypt", - "flowy-error", - "flowy-folder-pub", - "flowy-notification", - "flowy-server-pub", - "flowy-sqlite", - "flowy-user-pub", - "lazy_static", - "lib-dispatch", - "lib-infra", - "once_cell", - "protobuf", - "rayon", - "semver", - "serde", - "serde_json", - "serde_repr", - "strum", - "strum_macros 0.25.2", - "tokio", - "tokio-stream", - "tracing", - "unicode-segmentation", - "uuid", - "validator 0.18.1", -] - -[[package]] -name = "flowy-user-pub" -version = "0.1.0" -dependencies = [ - "anyhow", - "base64 0.21.5", - "chrono", - "client-api", - "collab", - "collab-entity", - "collab-folder", - "flowy-error", - "flowy-folder-pub", - "lib-infra", - "serde", - "serde_json", - "serde_repr", - "tokio", - "tokio-stream", - "tracing", - "uuid", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - -[[package]] -name = "form_urlencoded" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "fs4" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7e180ac76c23b45e767bd7ae9579bc0bb458618c4bc71835926e098e61d15f8" -dependencies = [ - "rustix", - "windows-sys 0.52.0", -] - -[[package]] -name = "fsevent-sys" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2" -dependencies = [ - "libc", -] - -[[package]] -name = "funty" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" - -[[package]] -name = "futf" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" -dependencies = [ - "mac", - "new_debug_unreachable", -] - -[[package]] -name = "futures" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" - -[[package]] -name = "futures-executor" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" - -[[package]] -name = "futures-lite" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" -dependencies = [ - "fastrand", - "futures-core", - "futures-io", - "parking", - "pin-project-lite", -] - -[[package]] -name = "futures-macro" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.47", -] - -[[package]] -name = "futures-sink" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" - -[[package]] -name = "futures-task" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" - -[[package]] -name = "futures-util" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] - -[[package]] -name = "fxhash" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" -dependencies = [ - "byteorder", -] - -[[package]] -name = "gdk" -version = "0.15.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6e05c1f572ab0e1f15be94217f0dc29088c248b14f792a5ff0af0d84bcda9e8" -dependencies = [ - "bitflags 1.3.2", - "cairo-rs", - "gdk-pixbuf", - "gdk-sys", - "gio", - "glib", - "libc", - "pango", -] - -[[package]] -name = "gdk-pixbuf" -version = "0.15.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad38dd9cc8b099cceecdf41375bb6d481b1b5a7cd5cd603e10a69a9383f8619a" -dependencies = [ - "bitflags 1.3.2", - "gdk-pixbuf-sys", - "gio", - "glib", - "libc", -] - -[[package]] -name = "gdk-pixbuf-sys" -version = "0.15.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "140b2f5378256527150350a8346dbdb08fadc13453a7a2d73aecd5fab3c402a7" -dependencies = [ - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "system-deps 6.1.1", -] - -[[package]] -name = "gdk-sys" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32e7a08c1e8f06f4177fb7e51a777b8c1689f743a7bc11ea91d44d2226073a88" -dependencies = [ - "cairo-sys-rs", - "gdk-pixbuf-sys", - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "pango-sys", - "pkg-config", - "system-deps 6.1.1", -] - -[[package]] -name = "gdkwayland-sys" -version = "0.15.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cca49a59ad8cfdf36ef7330fe7bdfbe1d34323220cc16a0de2679ee773aee2c2" -dependencies = [ - "gdk-sys", - "glib-sys", - "gobject-sys", - "libc", - "pkg-config", - "system-deps 6.1.1", -] - -[[package]] -name = "gdkx11-sys" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4b7f8c7a84b407aa9b143877e267e848ff34106578b64d1e0a24bf550716178" -dependencies = [ - "gdk-sys", - "glib-sys", - "libc", - "system-deps 6.1.1", - "x11", -] - -[[package]] -name = "generator" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc16584ff22b460a382b7feec54b23d2908d858152e5739a120b949293bd74e" -dependencies = [ - "cc", - "libc", - "log", - "rustversion", - "windows 0.48.0", -] - -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "gethostname" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1ebd34e35c46e00bb73e81363248d627782724609fe1b6396f553f68fe3862e" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "getopts" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" -dependencies = [ - "unicode-width", -] - -[[package]] -name = "getrandom" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.9.0+wasi-snapshot-preview1", -] - -[[package]] -name = "getrandom" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "wasi 0.11.0+wasi-snapshot-preview1", - "wasm-bindgen", -] - -[[package]] -name = "ghash" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40" -dependencies = [ - "opaque-debug", - "polyval", -] - -[[package]] -name = "gimli" -version = "0.27.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" - -[[package]] -name = "gio" -version = "0.15.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68fdbc90312d462781a395f7a16d96a2b379bb6ef8cd6310a2df272771c4283b" -dependencies = [ - "bitflags 1.3.2", - "futures-channel", - "futures-core", - "futures-io", - "gio-sys", - "glib", - "libc", - "once_cell", - "thiserror", -] - -[[package]] -name = "gio-sys" -version = "0.15.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32157a475271e2c4a023382e9cab31c4584ee30a97da41d3c4e9fdd605abcf8d" -dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps 6.1.1", - "winapi", -] - -[[package]] -name = "glib" -version = "0.15.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edb0306fbad0ab5428b0ca674a23893db909a98582969c9b537be4ced78c505d" -dependencies = [ - "bitflags 1.3.2", - "futures-channel", - "futures-core", - "futures-executor", - "futures-task", - "glib-macros", - "glib-sys", - "gobject-sys", - "libc", - "once_cell", - "smallvec", - "thiserror", -] - -[[package]] -name = "glib-macros" -version = "0.15.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10c6ae9f6fa26f4fb2ac16b528d138d971ead56141de489f8111e259b9df3c4a" -dependencies = [ - "anyhow", - "heck 0.4.1", - "proc-macro-crate 1.3.1", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "glib-sys" -version = "0.15.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef4b192f8e65e9cf76cbf4ea71fa8e3be4a0e18ffe3d68b8da6836974cc5bad4" -dependencies = [ - "libc", - "system-deps 6.1.1", -] - -[[package]] -name = "glob" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" - -[[package]] -name = "globset" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "029d74589adefde59de1a0c4f4732695c32805624aec7b68d91503d4dba79afc" -dependencies = [ - "aho-corasick 0.7.20", - "bstr", - "fnv", - "log", - "regex", -] - -[[package]] -name = "globwalk" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93e3af942408868f6934a7b85134a3230832b9977cf66125df2f9edcfce4ddcc" -dependencies = [ - "bitflags 1.3.2", - "ignore", - "walkdir", -] - -[[package]] -name = "gloo-utils" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037fcb07216cb3a30f7292bd0176b050b7b9a052ba830ef7d5d65f6dc64ba58e" -dependencies = [ - "js-sys", - "serde", - "serde_json", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "gobject-sys" -version = "0.15.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d57ce44246becd17153bd035ab4d32cfee096a657fc01f2231c9278378d1e0a" -dependencies = [ - "glib-sys", - "libc", - "system-deps 6.1.1", -] - -[[package]] -name = "gotrue" -version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ea131f0baab67defe7591067357eced490072372#ea131f0baab67defe7591067357eced490072372" -dependencies = [ - "anyhow", - "futures-util", - "getrandom 0.2.10", - "gotrue-entity", - "infra", - "reqwest", - "serde", - "serde_json", - "tokio", - "tracing", -] - -[[package]] -name = "gotrue-entity" -version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ea131f0baab67defe7591067357eced490072372#ea131f0baab67defe7591067357eced490072372" -dependencies = [ - "anyhow", - "app-error", - "chrono", - "jsonwebtoken", - "lazy_static", - "serde", - "serde_json", -] - -[[package]] -name = "gtk" -version = "0.15.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92e3004a2d5d6d8b5057d2b57b3712c9529b62e82c77f25c1fecde1fd5c23bd0" -dependencies = [ - "atk", - "bitflags 1.3.2", - "cairo-rs", - "field-offset", - "futures-channel", - "gdk", - "gdk-pixbuf", - "gio", - "glib", - "gtk-sys", - "gtk3-macros", - "libc", - "once_cell", - "pango", - "pkg-config", -] - -[[package]] -name = "gtk-sys" -version = "0.15.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5bc2f0587cba247f60246a0ca11fe25fb733eabc3de12d1965fc07efab87c84" -dependencies = [ - "atk-sys", - "cairo-sys-rs", - "gdk-pixbuf-sys", - "gdk-sys", - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "pango-sys", - "system-deps 6.1.1", -] - -[[package]] -name = "gtk3-macros" -version = "0.15.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "684c0456c086e8e7e9af73ec5b84e35938df394712054550e81558d21c44ab0d" -dependencies = [ - "anyhow", - "proc-macro-crate 1.3.1", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "h2" -version = "0.3.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97ec8491ebaf99c8eaa73058b045fe58073cd6be7f596ac993ced0b0a0c01049" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http", - "indexmap 1.9.3", - "slab", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -dependencies = [ - "ahash 0.7.6", -] - -[[package]] -name = "hashbrown" -version = "0.14.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" -dependencies = [ - "ahash 0.8.6", - "allocator-api2", -] - -[[package]] -name = "heck" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - -[[package]] -name = "hermit-abi" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" -dependencies = [ - "libc", -] - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest", -] - -[[package]] -name = "html5ever" -version = "0.25.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5c13fb08e5d4dfc151ee5e88bae63f7773d61852f3bdc73c9f4b9e1bde03148" -dependencies = [ - "log", - "mac", - "markup5ever 0.10.1", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "html5ever" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7" -dependencies = [ - "log", - "mac", - "markup5ever 0.11.0", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "htmlescape" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9025058dae765dee5070ec375f591e2ba14638c63feff74f13805a72e523163" - -[[package]] -name = "http" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" -dependencies = [ - "bytes", - "fnv", - "itoa 1.0.6", -] - -[[package]] -name = "http-body" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" -dependencies = [ - "bytes", - "http", - "pin-project-lite", -] - -[[package]] -name = "http-range" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21dec9db110f5f872ed9699c3ecf50cf16f423502706ba5c72462e28d3157573" - -[[package]] -name = "httparse" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" - -[[package]] -name = "httpdate" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" - -[[package]] -name = "humansize" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cb51c9a029ddc91b07a787f1d86b53ccfa49b0e86688c946ebe8d3555685dd7" -dependencies = [ - "libm", -] - -[[package]] -name = "hyper" -version = "0.14.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "httparse", - "httpdate", - "itoa 1.0.6", - "pin-project-lite", - "socket2 0.4.9", - "tokio", - "tower-service", - "tracing", - "want", -] - -[[package]] -name = "hyper-rustls" -version = "0.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0646026eb1b3eea4cd9ba47912ea5ce9cc07713d105b1a14698f4e6433d348b7" -dependencies = [ - "http", - "hyper", - "rustls", - "tokio", - "tokio-rustls", -] - -[[package]] -name = "hyper-tls" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" -dependencies = [ - "bytes", - "hyper", - "native-tls", - "tokio", - "tokio-native-tls", -] - -[[package]] -name = "iana-time-zone" -version = "0.1.61" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - -[[package]] -name = "ico" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3804960be0bb5e4edb1e1ad67afd321a9ecfd875c3e65c099468fd2717d7cae" -dependencies = [ - "byteorder", - "png", -] - -[[package]] -name = "icu_collections" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" -dependencies = [ - "displaydoc", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_locid" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" -dependencies = [ - "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", -] - -[[package]] -name = "icu_locid_transform" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_locid_transform_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_locid_transform_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" - -[[package]] -name = "icu_normalizer" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" -dependencies = [ - "displaydoc", - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec", - "utf16_iter", - "utf8_iter", - "write16", - "zerovec", -] - -[[package]] -name = "icu_normalizer_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" - -[[package]] -name = "icu_properties" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" -dependencies = [ - "displaydoc", - "icu_collections", - "icu_locid_transform", - "icu_properties_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_properties_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" - -[[package]] -name = "icu_provider" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_provider_macros", - "stable_deref_trait", - "tinystr", - "writeable", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_provider_macros" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.47", -] - -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - -[[package]] -name = "idna" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "idna" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "idna" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" -dependencies = [ - "idna_adapter", - "smallvec", - "utf8_iter", -] - -[[package]] -name = "idna_adapter" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" -dependencies = [ - "icu_normalizer", - "icu_properties", -] - -[[package]] -name = "ignore" -version = "0.4.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbe7873dab538a9a44ad79ede1faf5f30d49f9a5c883ddbab48bce81b64b7492" -dependencies = [ - "globset", - "lazy_static", - "log", - "memchr", - "regex", - "same-file", - "thread_local", - "walkdir", - "winapi-util", -] - -[[package]] -name = "image" -version = "0.24.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "527909aa81e20ac3a44803521443a765550f09b5130c2c2fa1ea59c2f8f50a3a" -dependencies = [ - "bytemuck", - "byteorder", - "color_quant", - "num-rational", - "num-traits", -] - -[[package]] -name = "indexed_db_futures" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0704b71f13f81b5933d791abf2de26b33c40935143985220299a357721166706" -dependencies = [ - "accessory", - "cfg-if", - "delegate-display", - "fancy_constructor", - "js-sys", - "uuid", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", - "serde", -] - -[[package]] -name = "indexmap" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" -dependencies = [ - "equivalent", - "hashbrown 0.14.3", - "serde", -] - -[[package]] -name = "infer" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f551f8c3a39f68f986517db0d1759de85881894fdc7db798bd2a9df9cb04b7fc" -dependencies = [ - "cfb", -] - -[[package]] -name = "infra" -version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ea131f0baab67defe7591067357eced490072372#ea131f0baab67defe7591067357eced490072372" -dependencies = [ - "anyhow", - "bytes", - "futures", - "pin-project", - "reqwest", - "serde", - "serde_json", - "tokio", - "tracing", - "validator 0.19.0", -] - -[[package]] -name = "inotify" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff" -dependencies = [ - "bitflags 1.3.2", - "inotify-sys", - "libc", -] - -[[package]] -name = "inotify-sys" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" -dependencies = [ - "libc", -] - -[[package]] -name = "inout" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" -dependencies = [ - "generic-array", -] - -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "interprocess" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81f2533f3be42fffe3b5e63b71aeca416c1c3bc33e4e27be018521e76b1f38fb" -dependencies = [ - "cfg-if", - "libc", - "rustc_version", - "to_method", - "winapi", -] - -[[package]] -name = "ipnet" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" - -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] - -[[package]] -name = "itertools" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" - -[[package]] -name = "itoa" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" - -[[package]] -name = "javascriptcore-rs" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf053e7843f2812ff03ef5afe34bb9c06ffee120385caad4f6b9967fcd37d41c" -dependencies = [ - "bitflags 1.3.2", - "glib", - "javascriptcore-rs-sys", -] - -[[package]] -name = "javascriptcore-rs-sys" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "905fbb87419c5cde6e3269537e4ea7d46431f3008c5d057e915ef3f115e7793c" -dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps 5.0.0", -] - -[[package]] -name = "jni" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "039022cdf4d7b1cf548d31f60ae783138e5fd42013f6271049d7df7afadef96c" -dependencies = [ - "cesu8", - "combine", - "jni-sys", - "log", - "thiserror", - "walkdir", -] - -[[package]] -name = "jni-sys" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" - -[[package]] -name = "jobserver" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" -dependencies = [ - "libc", -] - -[[package]] -name = "js-sys" -version = "0.3.67" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "json-patch" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55ff1e1486799e3f64129f8ccad108b38290df9cd7015cd31bed17239f0789d6" -dependencies = [ - "serde", - "serde_json", - "thiserror", - "treediff", -] - -[[package]] -name = "jsonwebtoken" -version = "8.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6971da4d9c3aa03c3d8f3ff0f4155b534aad021292003895a469716b2a230378" -dependencies = [ - "base64 0.21.5", - "pem", - "ring", - "serde", - "serde_json", - "simple_asn1", -] - -[[package]] -name = "kqueue" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7447f1ca1b7b563588a205fe93dea8df60fd981423a768bc1c0ded35ed147d0c" -dependencies = [ - "kqueue-sys", - "libc", -] - -[[package]] -name = "kqueue-sys" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" -dependencies = [ - "bitflags 1.3.2", - "libc", -] - -[[package]] -name = "kuchiki" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ea8e9c6e031377cff82ee3001dc8026cdf431ed4e2e6b51f98ab8c73484a358" -dependencies = [ - "cssparser 0.27.2", - "html5ever 0.25.2", - "matches", - "selectors 0.22.0", -] - -[[package]] -name = "kuchikiki" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e4755b7b995046f510a7520c42b2fed58b77bd94d5a87a8eb43d2fd126da8" -dependencies = [ - "cssparser 0.27.2", - "html5ever 0.26.0", - "indexmap 1.9.3", - "matches", - "selectors 0.22.0", -] - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "lazycell" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" - -[[package]] -name = "levenshtein_automata" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c2cdeb66e45e9f36bfad5bbdb4d2384e70936afbee843c6f6543f0c551ebb25" - -[[package]] -name = "lib-dispatch" -version = "0.1.0" -dependencies = [ - "bincode", - "bytes", - "derivative", - "dyn-clone", - "futures", - "futures-channel", - "futures-core", - "futures-util", - "getrandom 0.2.10", - "nanoid", - "pin-project", - "protobuf", - "serde", - "serde_json", - "serde_repr", - "thread-id", - "tokio", - "tracing", - "validator 0.18.1", - "wasm-bindgen", - "wasm-bindgen-futures", -] - -[[package]] -name = "lib-infra" -version = "0.1.0" -dependencies = [ - "allo-isolate", - "anyhow", - "async-trait", - "atomic_refcell", - "bytes", - "cfg-if", - "chrono", - "futures", - "futures-core", - "futures-util", - "md5", - "pin-project", - "tempfile", - "tokio", - "tracing", - "validator 0.18.1", - "walkdir", - "zip 2.2.0", -] - -[[package]] -name = "lib-log" -version = "0.1.0" -dependencies = [ - "chrono", - "lazy_static", - "lib-infra", - "serde", - "serde_json", - "tracing", - "tracing-appender", - "tracing-bunyan-formatter", - "tracing-core", - "tracing-subscriber", -] - -[[package]] -name = "libc" -version = "0.2.152" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" - -[[package]] -name = "libloading" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" -dependencies = [ - "cfg-if", - "winapi", -] - -[[package]] -name = "libm" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" - -[[package]] -name = "librocksdb-sys" -version = "0.16.0+8.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce3d60bc059831dc1c83903fb45c103f75db65c5a7bf22272764d9cc683e348c" -dependencies = [ - "bindgen", - "bzip2-sys", - "cc", - "glob", - "libc", - "libz-sys", - "zstd-sys", -] - -[[package]] -name = "libsqlite3-sys" -version = "0.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4e226dcd58b4be396f7bd3c20da8fdee2911400705297ba7d2d7cc2c30f716" -dependencies = [ - "cc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "libz-sys" -version = "1.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56ee889ecc9568871456d42f603d6a0ce59ff328d291063a45cbdf0036baf6db" -dependencies = [ - "cc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "line-wrap" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30344350a2a51da54c1d53be93fade8a237e545dbcc4bdbe635413f2117cab9" -dependencies = [ - "safemem", -] - -[[package]] -name = "linux-raw-sys" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" - -[[package]] -name = "litemap" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" - -[[package]] -name = "lock_api" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" -dependencies = [ - "autocfg", - "scopeguard", -] - -[[package]] -name = "lockfree-object-pool" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9374ef4228402d4b7e403e5838cb880d9ee663314b0a900d5a6aabf0c213552e" - -[[package]] -name = "log" -version = "0.4.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" - -[[package]] -name = "loom" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff50ecb28bb86013e935fb6683ab1f6d3a20016f123c76fd4c27470076ac30f5" -dependencies = [ - "cfg-if", - "generator", - "pin-utils", - "scoped-tls", - "serde", - "serde_json", - "tracing", - "tracing-subscriber", -] - -[[package]] -name = "lru" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc" -dependencies = [ - "hashbrown 0.14.3", -] - -[[package]] -name = "lz4_flex" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "912b45c753ff5f7f5208307e8ace7d2a2e30d024e26d3509f3dce546c044ce15" - -[[package]] -name = "lzma-rs" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "297e814c836ae64db86b36cf2a557ba54368d03f6afcd7d947c266692f71115e" -dependencies = [ - "byteorder", - "crc", -] - -[[package]] -name = "lzma-sys" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fda04ab3764e6cde78b9974eec4f779acaba7c4e84b36eca3cf77c581b85d27" -dependencies = [ - "cc", - "libc", - "pkg-config", -] - -[[package]] -name = "mac" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" - -[[package]] -name = "macroific" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f05c00ac596022625d01047c421a0d97d7f09a18e429187b341c201cb631b9dd" -dependencies = [ - "macroific_attr_parse", - "macroific_core", - "macroific_macro", -] - -[[package]] -name = "macroific_attr_parse" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd94d5da95b30ae6e10621ad02340909346ad91661f3f8c0f2b62345e46a2f67" -dependencies = [ - "cfg-if", - "proc-macro2", - "quote", - "syn 2.0.47", -] - -[[package]] -name = "macroific_core" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13198c120864097a565ccb3ff947672d969932b7975ebd4085732c9f09435e55" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.47", -] - -[[package]] -name = "macroific_macro" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0c9853143cbed7f1e41dc39fee95f9b361bec65c8dc2a01bf609be01b61f5ae" -dependencies = [ - "macroific_attr_parse", - "macroific_core", - "proc-macro2", - "quote", - "syn 2.0.47", -] - -[[package]] -name = "malloc_buf" -version = "0.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" -dependencies = [ - "libc", -] - -[[package]] -name = "markdown" -version = "1.0.0-alpha.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6491e6c702bf7e3b24e769d800746d5f2c06a6c6a2db7992612e0f429029e81" -dependencies = [ - "unicode-id", -] - -[[package]] -name = "markup5ever" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a24f40fb03852d1cdd84330cddcaf98e9ec08a7b7768e952fad3b4cf048ec8fd" -dependencies = [ - "log", - "phf 0.8.0", - "phf_codegen 0.8.0", - "string_cache", - "string_cache_codegen", - "tendril", -] - -[[package]] -name = "markup5ever" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016" -dependencies = [ - "log", - "phf 0.10.1", - "phf_codegen 0.10.0", - "string_cache", - "string_cache_codegen", - "tendril", -] - -[[package]] -name = "matchers" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" -dependencies = [ - "regex-automata 0.1.10", -] - -[[package]] -name = "matches" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" - -[[package]] -name = "md5" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" - -[[package]] -name = "measure_time" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56220900f1a0923789ecd6bf25fbae8af3b2f1ff3e9e297fc9b6b8674dd4d852" -dependencies = [ - "instant", - "log", -] - -[[package]] -name = "memchr" -version = "2.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" - -[[package]] -name = "memmap2" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe751422e4a8caa417e13c3ea66452215d7d63e19e604f4980461212f3ae1322" -dependencies = [ - "libc", -] - -[[package]] -name = "memoffset" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" -dependencies = [ - "autocfg", -] - -[[package]] -name = "migrations_internals" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f23f71580015254b020e856feac3df5878c2c7a8812297edd6c0a485ac9dada" -dependencies = [ - "serde", - "toml 0.7.5", -] - -[[package]] -name = "migrations_macros" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cce3325ac70e67bbab5bd837a31cae01f1a6db64e0e744a33cb03a543469ef08" -dependencies = [ - "migrations_internals", - "proc-macro2", - "quote", -] - -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - -[[package]] -name = "mime_guess" -version = "2.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" -dependencies = [ - "mime", - "unicase", -] - -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - -[[package]] -name = "miniz_oxide" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" -dependencies = [ - "adler", -] - -[[package]] -name = "miniz_oxide" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" -dependencies = [ - "adler", - "simd-adler32", -] - -[[package]] -name = "mio" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" -dependencies = [ - "libc", - "log", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.48.0", -] - -[[package]] -name = "moka" -version = "0.12.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32cf62eb4dd975d2dde76432fb1075c49e3ee2331cf36f1f8fd4b66550d32b6f" -dependencies = [ - "async-lock", - "async-trait", - "crossbeam-channel", - "crossbeam-epoch", - "crossbeam-utils", - "event-listener", - "futures-util", - "once_cell", - "parking_lot 0.12.1", - "quanta", - "rustc_version", - "smallvec", - "tagptr", - "thiserror", - "triomphe", - "uuid", -] - -[[package]] -name = "multimap" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" - -[[package]] -name = "murmurhash32" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9380db4c04d219ac5c51d14996bbf2c2e9a15229771b53f8671eb6c83cf44df" - -[[package]] -name = "nanoid" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ffa00dec017b5b1a8b7cf5e2c008bfda1aa7e0697ac1508b491fdf2622fb4d8" -dependencies = [ - "rand 0.8.5", -] - -[[package]] -name = "native-tls" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" -dependencies = [ - "lazy_static", - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - -[[package]] -name = "ndk" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2032c77e030ddee34a6787a64166008da93f6a352b629261d0fee232b8742dd4" -dependencies = [ - "bitflags 1.3.2", - "jni-sys", - "ndk-sys", - "num_enum", - "thiserror", -] - -[[package]] -name = "ndk-context" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" - -[[package]] -name = "ndk-sys" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e5a6ae77c8ee183dcbbba6150e2e6b9f3f4196a7666c02a715a95692ec1fa97" -dependencies = [ - "jni-sys", -] - -[[package]] -name = "new_debug_unreachable" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" - -[[package]] -name = "nodrop" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" - -[[package]] -name = "nom" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" -dependencies = [ - "memchr", - "minimal-lexical", -] - -[[package]] -name = "notify" -version = "6.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" -dependencies = [ - "bitflags 2.4.0", - "crossbeam-channel", - "filetime", - "fsevent-sys", - "inotify", - "kqueue", - "libc", - "log", - "mio", - "walkdir", - "windows-sys 0.48.0", -] - -[[package]] -name = "ntapi" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" -dependencies = [ - "winapi", -] - -[[package]] -name = "nu-ansi-term" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" -dependencies = [ - "overload", - "winapi", -] - -[[package]] -name = "num-bigint" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-conv" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" - -[[package]] -name = "num-integer" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" -dependencies = [ - "autocfg", - "num-traits", -] - -[[package]] -name = "num-rational" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" -dependencies = [ - "autocfg", - "libm", -] - -[[package]] -name = "num_cpus" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" -dependencies = [ - "hermit-abi", - "libc", -] - -[[package]] -name = "num_enum" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" -dependencies = [ - "num_enum_derive", -] - -[[package]] -name = "num_enum_derive" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" -dependencies = [ - "proc-macro-crate 1.3.1", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "objc" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" -dependencies = [ - "malloc_buf", - "objc_exception", -] - -[[package]] -name = "objc-foundation" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" -dependencies = [ - "block", - "objc", - "objc_id", -] - -[[package]] -name = "objc-sys" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7c71324e4180d0899963fc83d9d241ac39e699609fc1025a850aadac8257459" - -[[package]] -name = "objc2" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "559c5a40fdd30eb5e344fbceacf7595a81e242529fb4e21cf5f43fb4f11ff98d" -dependencies = [ - "objc-sys", - "objc2-encode", -] - -[[package]] -name = "objc2-encode" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d079845b37af429bfe5dfa76e6d087d788031045b25cfc6fd898486fd9847666" - -[[package]] -name = "objc_exception" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4" -dependencies = [ - "cc", -] - -[[package]] -name = "objc_id" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" -dependencies = [ - "objc", -] - -[[package]] -name = "object" -version = "0.30.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03b4680b86d9cfafba8fc491dc9b6df26b68cf40e9e6cd73909194759a63c385" -dependencies = [ - "memchr", -] - -[[package]] -name = "once_cell" -version = "1.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" - -[[package]] -name = "oneshot" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f6640c6bda7731b1fdbab747981a0f896dd1fedaf9f4a53fa237a04a84431f4" -dependencies = [ - "loom", -] - -[[package]] -name = "opaque-debug" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" - -[[package]] -name = "open" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2078c0039e6a54a0c42c28faa984e115fb4c2d5bf2208f77d1961002df8576f8" -dependencies = [ - "pathdiff", - "windows-sys 0.42.0", -] - -[[package]] -name = "openssl" -version = "0.10.55" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "345df152bc43501c5eb9e4654ff05f794effb78d4efe3d53abc158baddc0703d" -dependencies = [ - "bitflags 1.3.2", - "cfg-if", - "foreign-types", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.47", -] - -[[package]] -name = "openssl-probe" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" - -[[package]] -name = "openssl-src" -version = "111.27.0+1.1.1v" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06e8f197c82d7511c5b014030c9b1efeda40d7d5f99d23b4ceed3524a5e63f02" -dependencies = [ - "cc", -] - -[[package]] -name = "openssl-sys" -version = "0.9.90" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "374533b0e45f3a7ced10fcaeccca020e66656bc03dac384f852e4e5a7a8104a6" -dependencies = [ - "cc", - "libc", - "openssl-src", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "option-ext" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" - -[[package]] -name = "os_pipe" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb233f06c2307e1f5ce2ecad9f8121cffbbee2c95428f44ea85222e460d0d213" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - -[[package]] -name = "ownedbytes" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3a059efb063b8f425b948e042e6b9bd85edfe60e913630ed727b23e2dfcc558" -dependencies = [ - "stable_deref_trait", -] - -[[package]] -name = "pango" -version = "0.15.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e4045548659aee5313bde6c582b0d83a627b7904dd20dc2d9ef0895d414e4f" -dependencies = [ - "bitflags 1.3.2", - "glib", - "libc", - "once_cell", - "pango-sys", -] - -[[package]] -name = "pango-sys" -version = "0.15.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2a00081cde4661982ed91d80ef437c20eacaf6aa1a5962c0279ae194662c3aa" -dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps 6.1.1", -] - -[[package]] -name = "parking" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" - -[[package]] -name = "parking_lot" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" -dependencies = [ - "instant", - "lock_api", - "parking_lot_core 0.8.6", -] - -[[package]] -name = "parking_lot" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" -dependencies = [ - "lock_api", - "parking_lot_core 0.9.8", -] - -[[package]] -name = "parking_lot_core" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" -dependencies = [ - "cfg-if", - "instant", - "libc", - "redox_syscall 0.2.16", - "smallvec", - "winapi", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall 0.3.5", - "smallvec", - "windows-targets 0.48.0", -] - -[[package]] -name = "parse-zoneinfo" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c705f256449c60da65e11ff6626e0c16a0a0b96aaa348de61376b249bc340f41" -dependencies = [ - "regex", -] - -[[package]] -name = "password-hash" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700" -dependencies = [ - "base64ct", - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "pathdiff" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" - -[[package]] -name = "pbkdf2" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" -dependencies = [ - "digest", - "hmac", - "password-hash", - "sha2", -] - -[[package]] -name = "pbkdf2" -version = "0.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" -dependencies = [ - "digest", - "hmac", -] - -[[package]] -name = "pem" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8835c273a76a90455d7344889b0964598e3316e2a79ede8e36f16bdcf2228b8" -dependencies = [ - "base64 0.13.1", -] - -[[package]] -name = "percent-encoding" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" - -[[package]] -name = "pest" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f73935e4d55e2abf7f130186537b19e7a4abc886a0252380b59248af473a3fc9" -dependencies = [ - "thiserror", - "ucd-trie", -] - -[[package]] -name = "pest_derive" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aef623c9bbfa0eedf5a0efba11a5ee83209c326653ca31ff019bec3a95bfff2b" -dependencies = [ - "pest", - "pest_generator", -] - -[[package]] -name = "pest_generator" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3e8cba4ec22bada7fc55ffe51e2deb6a0e0db2d0b7ab0b103acc80d2510c190" -dependencies = [ - "pest", - "pest_meta", - "proc-macro2", - "quote", - "syn 2.0.47", -] - -[[package]] -name = "pest_meta" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a01f71cb40bd8bb94232df14b946909e14660e33fc05db3e50ae2a82d7ea0ca0" -dependencies = [ - "once_cell", - "pest", - "sha2", -] - -[[package]] -name = "petgraph" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" -dependencies = [ - "fixedbitset", - "indexmap 2.1.0", -] - -[[package]] -name = "phf" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" -dependencies = [ - "phf_macros 0.8.0", - "phf_shared 0.8.0", - "proc-macro-hack", -] - -[[package]] -name = "phf" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" -dependencies = [ - "phf_shared 0.10.0", -] - -[[package]] -name = "phf" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" -dependencies = [ - "phf_macros 0.11.2", - "phf_shared 0.11.2", -] - -[[package]] -name = "phf_codegen" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815" -dependencies = [ - "phf_generator 0.8.0", - "phf_shared 0.8.0", -] - -[[package]] -name = "phf_codegen" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd" -dependencies = [ - "phf_generator 0.10.0", - "phf_shared 0.10.0", -] - -[[package]] -name = "phf_codegen" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a" -dependencies = [ - "phf_generator 0.11.2", - "phf_shared 0.11.2", -] - -[[package]] -name = "phf_generator" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526" -dependencies = [ - "phf_shared 0.8.0", - "rand 0.7.3", -] - -[[package]] -name = "phf_generator" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" -dependencies = [ - "phf_shared 0.10.0", - "rand 0.8.5", -] - -[[package]] -name = "phf_generator" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" -dependencies = [ - "phf_shared 0.11.2", - "rand 0.8.5", -] - -[[package]] -name = "phf_macros" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6fde18ff429ffc8fe78e2bf7f8b7a5a5a6e2a8b58bc5a9ac69198bbda9189c" -dependencies = [ - "phf_generator 0.8.0", - "phf_shared 0.8.0", - "proc-macro-hack", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "phf_macros" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" -dependencies = [ - "phf_generator 0.11.2", - "phf_shared 0.11.2", - "proc-macro2", - "quote", - "syn 2.0.47", -] - -[[package]] -name = "phf_shared" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" -dependencies = [ - "siphasher", -] - -[[package]] -name = "phf_shared" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" -dependencies = [ - "siphasher", - "uncased", -] - -[[package]] -name = "phf_shared" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" -dependencies = [ - "siphasher", -] - -[[package]] -name = "pin-project" -version = "1.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be57f64e946e500c8ee36ef6331845d40a93055567ec57e8fae13efd33759b95" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.47", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "pkg-config" -version = "0.3.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" - -[[package]] -name = "plist" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bd9647b268a3d3e14ff09c23201133a62589c658db02bb7388c7246aafe0590" -dependencies = [ - "base64 0.21.5", - "indexmap 1.9.3", - "line-wrap", - "quick-xml", - "serde", - "time", -] - -[[package]] -name = "png" -version = "0.17.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59871cc5b6cce7eaccca5a802b4173377a1c2ba90654246789a8fa2334426d11" -dependencies = [ - "bitflags 1.3.2", - "crc32fast", - "fdeflate", - "flate2", - "miniz_oxide 0.7.1", -] - -[[package]] -name = "polyval" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52cff9d1d4dee5fe6d03729099f4a310a41179e0a10dbf542039873f2e826fb" -dependencies = [ - "cfg-if", - "cpufeatures", - "opaque-debug", - "universal-hash", -] - -[[package]] -name = "postgrest" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e66400cb23a379592bc8c8bdc9adda652eef4a969b74ab78454a8e8c11330c2b" -dependencies = [ - "reqwest", -] - -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - -[[package]] -name = "ppv-lite86" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - -[[package]] -name = "precomputed-hash" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" - -[[package]] -name = "prettyplease" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9825a04601d60621feed79c4e6b56d65db77cdca55cef43b46b0de1096d1c282" -dependencies = [ - "proc-macro2", - "syn 2.0.47", -] - -[[package]] -name = "proc-macro-crate" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" -dependencies = [ - "once_cell", - "toml_edit 0.19.11", -] - -[[package]] -name = "proc-macro-crate" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b00f26d3400549137f92511a46ac1cd8ce37cb5598a96d382381458b992a5d24" -dependencies = [ - "toml_datetime", - "toml_edit 0.20.2", -] - -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.109", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr2" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" -dependencies = [ - "proc-macro2", - "quote", -] - -[[package]] -name = "proc-macro-error2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" -dependencies = [ - "proc-macro-error-attr2", - "proc-macro2", - "quote", - "syn 2.0.47", -] - -[[package]] -name = "proc-macro-hack" -version = "0.5.20+deprecated" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" - -[[package]] -name = "proc-macro2" -version = "1.0.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "907a61bd0f64c2f29cd1cf1dc34d05176426a3f504a78010f08416ddb7b13708" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "prost" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "146c289cda302b98a28d40c8b3b90498d6e526dd24ac2ecea73e4e491685b94a" -dependencies = [ - "bytes", - "prost-derive 0.12.3", -] - -[[package]] -name = "prost" -version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b0487d90e047de87f984913713b85c601c05609aad5b0df4b4573fbf69aa13f" -dependencies = [ - "bytes", - "prost-derive 0.13.3", -] - -[[package]] -name = "prost-build" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c55e02e35260070b6f716a2423c2ff1c3bb1642ddca6f99e1f26d06268a0e2d2" -dependencies = [ - "bytes", - "heck 0.4.1", - "itertools 0.10.5", - "log", - "multimap", - "once_cell", - "petgraph", - "prettyplease", - "prost 0.12.3", - "prost-types", - "regex", - "syn 2.0.47", - "tempfile", - "which", -] - -[[package]] -name = "prost-derive" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efb6c9a1dd1def8e2124d17e83a20af56f1570d6c2d2bd9e266ccb768df3840e" -dependencies = [ - "anyhow", - "itertools 0.10.5", - "proc-macro2", - "quote", - "syn 2.0.47", -] - -[[package]] -name = "prost-derive" -version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9552f850d5f0964a4e4d0bf306459ac29323ddfbae05e35a7c0d35cb0803cc5" -dependencies = [ - "anyhow", - "itertools 0.12.1", - "proc-macro2", - "quote", - "syn 2.0.47", -] - -[[package]] -name = "prost-types" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "193898f59edcf43c26227dcd4c8427f00d99d61e95dcde58dabd49fa291d470e" -dependencies = [ - "prost 0.12.3", -] - -[[package]] -name = "protobuf" -version = "2.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94" - -[[package]] -name = "protobuf-codegen" -version = "2.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "033460afb75cf755fcfc16dfaed20b86468082a2ea24e05ac35ab4a099a017d6" -dependencies = [ - "protobuf", -] - -[[package]] -name = "protoc" -version = "2.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0218039c514f9e14a5060742ecd50427f8ac4f85a6dc58f2ddb806e318c55ee" -dependencies = [ - "log", - "which", -] - -[[package]] -name = "protoc-bin-vendored" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "005ca8623e5633e298ad1f917d8be0a44bcf406bf3cde3b80e63003e49a3f27d" -dependencies = [ - "protoc-bin-vendored-linux-aarch_64", - "protoc-bin-vendored-linux-ppcle_64", - "protoc-bin-vendored-linux-x86_32", - "protoc-bin-vendored-linux-x86_64", - "protoc-bin-vendored-macos-x86_64", - "protoc-bin-vendored-win32", -] - -[[package]] -name = "protoc-bin-vendored-linux-aarch_64" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fb9fc9cce84c8694b6ea01cc6296617b288b703719b725b8c9c65f7c5874435" - -[[package]] -name = "protoc-bin-vendored-linux-ppcle_64" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02d2a07dcf7173a04d49974930ccbfb7fd4d74df30ecfc8762cf2f895a094516" - -[[package]] -name = "protoc-bin-vendored-linux-x86_32" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54fef0b04fcacba64d1d80eed74a20356d96847da8497a59b0a0a436c9165b0" - -[[package]] -name = "protoc-bin-vendored-linux-x86_64" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8782f2ce7d43a9a5c74ea4936f001e9e8442205c244f7a3d4286bd4c37bc924" - -[[package]] -name = "protoc-bin-vendored-macos-x86_64" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5de656c7ee83f08e0ae5b81792ccfdc1d04e7876b1d9a38e6876a9e09e02537" - -[[package]] -name = "protoc-bin-vendored-win32" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9653c3ed92974e34c5a6e0a510864dab979760481714c172e0a34e437cb98804" - -[[package]] -name = "protoc-rust" -version = "2.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22f8a182bb17c485f20bdc4274a8c39000a61024cfe461c799b50fec77267838" -dependencies = [ - "protobuf", - "protobuf-codegen", - "protoc", - "tempfile", -] - -[[package]] -name = "psl-types" -version = "2.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac" - -[[package]] -name = "ptr_meta" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" -dependencies = [ - "ptr_meta_derive", -] - -[[package]] -name = "ptr_meta_derive" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "publicsuffix" -version = "2.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96a8c1bda5ae1af7f99a2962e49df150414a43d62404644d98dd5c3a93d07457" -dependencies = [ - "idna 0.3.0", - "psl-types", -] - -[[package]] -name = "quanta" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5167a477619228a0b284fac2674e3c388cba90631d7b7de620e6f1fcd08da5" -dependencies = [ - "crossbeam-utils", - "libc", - "once_cell", - "raw-cpuid", - "wasi 0.11.0+wasi-snapshot-preview1", - "web-sys", - "winapi", -] - -[[package]] -name = "quick-xml" -version = "0.28.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce5e73202a820a31f8a0ee32ada5e21029c81fd9e3ebf668a40832e4219d9d1" -dependencies = [ - "memchr", -] - -[[package]] -name = "quote" -version = "1.0.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "r2d2" -version = "0.8.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51de85fb3fb6524929c8a2eb85e6b6d363de4e8c48f9e2c2eac4944abc181c93" -dependencies = [ - "log", - "parking_lot 0.12.1", - "scheduled-thread-pool", -] - -[[package]] -name = "radium" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" - -[[package]] -name = "rand" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom 0.1.16", - "libc", - "rand_chacha 0.2.2", - "rand_core 0.5.1", - "rand_hc", - "rand_pcg", -] - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core 0.5.1", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom 0.1.16", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.10", -] - -[[package]] -name = "rand_distr" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32cb0b9bc82b0a0876c2dd994a7e7a2683d3e7390ca40e6886785ef0c7e3ee31" -dependencies = [ - "num-traits", - "rand 0.8.5", -] - -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core 0.5.1", -] - -[[package]] -name = "rand_pcg" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" -dependencies = [ - "rand_core 0.5.1", -] - -[[package]] -name = "raw-cpuid" -version = "11.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb9ee317cfe3fbd54b36a511efc1edd42e216903c9cd575e686dd68a2ba90d8d" -dependencies = [ - "bitflags 2.4.0", -] - -[[package]] -name = "raw-window-handle" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" - -[[package]] -name = "rayon" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" -dependencies = [ - "crossbeam-deque", - "crossbeam-utils", -] - -[[package]] -name = "redox_syscall" -version = "0.1.57" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" - -[[package]] -name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags 1.3.2", -] - -[[package]] -name = "redox_syscall" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" -dependencies = [ - "bitflags 1.3.2", -] - -[[package]] -name = "redox_syscall" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" -dependencies = [ - "bitflags 1.3.2", -] - -[[package]] -name = "redox_users" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" -dependencies = [ - "getrandom 0.2.10", - "redox_syscall 0.2.16", - "thiserror", -] - -[[package]] -name = "regex" -version = "1.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" -dependencies = [ - "aho-corasick 1.0.2", - "memchr", - "regex-automata 0.4.7", - "regex-syntax 0.8.4", -] - -[[package]] -name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" -dependencies = [ - "regex-syntax 0.6.29", -] - -[[package]] -name = "regex-automata" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" -dependencies = [ - "aho-corasick 1.0.2", - "memchr", - "regex-syntax 0.8.4", -] - -[[package]] -name = "regex-syntax" -version = "0.6.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" - -[[package]] -name = "regex-syntax" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" - -[[package]] -name = "rend" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "581008d2099240d37fb08d77ad713bcaec2c4d89d50b5b21a8bb1996bbab68ab" -dependencies = [ - "bytecheck", -] - -[[package]] -name = "reqwest" -version = "0.11.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" -dependencies = [ - "base64 0.21.5", - "bytes", - "cookie", - "cookie_store", - "encoding_rs", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "hyper", - "hyper-rustls", - "hyper-tls", - "ipnet", - "js-sys", - "log", - "mime", - "mime_guess", - "native-tls", - "once_cell", - "percent-encoding", - "pin-project-lite", - "rustls", - "rustls-pemfile", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper", - "system-configuration", - "tokio", - "tokio-native-tls", - "tokio-rustls", - "tokio-util", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-streams", - "web-sys", - "webpki-roots", - "winreg 0.50.0", -] - -[[package]] -name = "rfd" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0149778bd99b6959285b0933288206090c50e2327f47a9c463bfdbf45c8823ea" -dependencies = [ - "block", - "dispatch", - "glib-sys", - "gobject-sys", - "gtk-sys", - "js-sys", - "lazy_static", - "log", - "objc", - "objc-foundation", - "objc_id", - "raw-window-handle", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "windows 0.37.0", -] - -[[package]] -name = "ring" -version = "0.16.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" -dependencies = [ - "cc", - "libc", - "once_cell", - "spin", - "untrusted", - "web-sys", - "winapi", -] - -[[package]] -name = "rkyv" -version = "0.7.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0200c8230b013893c0b2d6213d6ec64ed2b9be2e0e016682b7224ff82cff5c58" -dependencies = [ - "bitvec", - "bytecheck", - "hashbrown 0.12.3", - "ptr_meta", - "rend", - "rkyv_derive", - "seahash", - "tinyvec", - "uuid", -] - -[[package]] -name = "rkyv_derive" -version = "0.7.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2e06b915b5c230a17d7a736d1e2e63ee753c256a8614ef3f5147b13a4f5541d" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "rocksdb" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bd13e55d6d7b8cd0ea569161127567cd587676c99f4472f779a0279aa60a7a7" -dependencies = [ - "libc", - "librocksdb-sys", -] - -[[package]] -name = "rust-stemmers" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e46a2036019fdb888131db7a4c847a1063a7493f971ed94ea82c67eada63ca54" -dependencies = [ - "serde", - "serde_derive", -] - -[[package]] -name = "rust_decimal" -version = "1.36.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b082d80e3e3cc52b2ed634388d436fe1f4de6af5786cc2de9ba9737527bdf555" -dependencies = [ - "arrayvec", - "borsh", - "bytes", - "num-traits", - "rand 0.8.5", - "rkyv", - "serde", - "serde_json", -] - -[[package]] -name = "rust_decimal_macros" -version = "1.30.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ca5c398d85f83b9a44de754a2048625a8c5eafcf070da7b8f116b685e2f6608" -dependencies = [ - "quote", - "rust_decimal", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" - -[[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - -[[package]] -name = "rustc_version" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" -dependencies = [ - "semver", -] - -[[package]] -name = "rustix" -version = "0.38.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca" -dependencies = [ - "bitflags 2.4.0", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.52.0", -] - -[[package]] -name = "rustls" -version = "0.21.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd8d6c9f025a446bc4d18ad9632e69aec8f287aa84499ee335599fabd20c3fd8" -dependencies = [ - "log", - "ring", - "rustls-webpki", - "sct", -] - -[[package]] -name = "rustls-pemfile" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" -dependencies = [ - "base64 0.21.5", -] - -[[package]] -name = "rustls-webpki" -version = "0.101.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d93931baf2d282fff8d3a532bbfd7653f734643161b87e3e01e59a04439bf0d" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "rustversion" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06" - -[[package]] -name = "rusty-money" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b28f881005eac7ad8d46b6f075da5f322bd7f4f83a38720fc069694ddadd683" -dependencies = [ - "rust_decimal", - "rust_decimal_macros", -] - -[[package]] -name = "ryu" -version = "1.0.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" - -[[package]] -name = "safemem" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "sanitize-filename" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ed72fbaf78e6f2d41744923916966c4fbe3d7c74e3037a8ee482f1115572603" -dependencies = [ - "lazy_static", - "regex", -] - -[[package]] -name = "schannel" -version = "0.1.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" -dependencies = [ - "windows-sys 0.42.0", -] - -[[package]] -name = "scheduled-thread-pool" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cbc66816425a074528352f5789333ecff06ca41b36b0b0efdfbb29edc391a19" -dependencies = [ - "parking_lot 0.12.1", -] - -[[package]] -name = "scoped-tls" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" - -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - -[[package]] -name = "scraper" -version = "0.17.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c95a930e03325234c18c7071fd2b60118307e025d6fff3e12745ffbf63a3d29c" -dependencies = [ - "ahash 0.8.6", - "cssparser 0.31.2", - "ego-tree", - "getopts", - "html5ever 0.26.0", - "once_cell", - "selectors 0.25.0", - "smallvec", - "tendril", -] - -[[package]] -name = "scraper" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "585480e3719b311b78a573db1c9d9c4c1f8010c2dee4cc59c2efe58ea4dbc3e1" -dependencies = [ - "ahash 0.8.6", - "cssparser 0.31.2", - "ego-tree", - "getopts", - "html5ever 0.26.0", - "once_cell", - "selectors 0.25.0", - "tendril", -] - -[[package]] -name = "sct" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "seahash" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" - -[[package]] -name = "security-framework" -version = "2.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fc758eb7bffce5b308734e9b0c1468893cae9ff70ebf13e7090be8dcbcc83a8" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f51d0c0d83bec45f16480d0ce0058397a69e48fcdc52d1dc8855fb68acbd31a7" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "selectors" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df320f1889ac4ba6bc0cdc9c9af7af4bd64bb927bccdf32d81140dc1f9be12fe" -dependencies = [ - "bitflags 1.3.2", - "cssparser 0.27.2", - "derive_more", - "fxhash", - "log", - "matches", - "phf 0.8.0", - "phf_codegen 0.8.0", - "precomputed-hash", - "servo_arc 0.1.1", - "smallvec", - "thin-slice", -] - -[[package]] -name = "selectors" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eb30575f3638fc8f6815f448d50cb1a2e255b0897985c8c59f4d37b72a07b06" -dependencies = [ - "bitflags 2.4.0", - "cssparser 0.31.2", - "derive_more", - "fxhash", - "log", - "new_debug_unreachable", - "phf 0.10.1", - "phf_codegen 0.10.0", - "precomputed-hash", - "servo_arc 0.3.0", - "smallvec", -] - -[[package]] -name = "semver" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" -dependencies = [ - "serde", -] - -[[package]] -name = "serde" -version = "1.0.210" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.210" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.47", -] - -[[package]] -name = "serde_derive_internals" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e578a843d40b4189a4d66bba51d7684f57da5bd7c304c64e14bd63efbef49509" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.47", -] - -[[package]] -name = "serde_json" -version = "1.0.128" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" -dependencies = [ - "itoa 1.0.6", - "memchr", - "ryu", - "serde", -] - -[[package]] -name = "serde_repr" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.47", -] - -[[package]] -name = "serde_spanned" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa 1.0.6", - "ryu", - "serde", -] - -[[package]] -name = "serde_with" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f02d8aa6e3c385bf084924f660ce2a3a6bd333ba55b35e8590b321f35d88513" -dependencies = [ - "base64 0.21.5", - "chrono", - "hex", - "indexmap 1.9.3", - "serde", - "serde_json", - "serde_with_macros", - "time", -] - -[[package]] -name = "serde_with_macros" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edc7d5d3932fb12ce722ee5e64dd38c504efba37567f0c402f6ca728c3b8b070" -dependencies = [ - "darling", - "proc-macro2", - "quote", - "syn 2.0.47", -] - -[[package]] -name = "serialize-to-javascript" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9823f2d3b6a81d98228151fdeaf848206a7855a7a042bbf9bf870449a66cafb" -dependencies = [ - "serde", - "serde_json", - "serialize-to-javascript-impl", -] - -[[package]] -name = "serialize-to-javascript-impl" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74064874e9f6a15f04c1f3cb627902d0e6b410abbf36668afa873c61889f1763" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "servo_arc" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d98238b800e0d1576d8b6e3de32827c2d74bee68bb97748dcf5071fb53965432" -dependencies = [ - "nodrop", - "stable_deref_trait", -] - -[[package]] -name = "servo_arc" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d036d71a959e00c77a63538b90a6c2390969f9772b096ea837205c6bd0491a44" -dependencies = [ - "stable_deref_trait", -] - -[[package]] -name = "sha1" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sha1_smol" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" - -[[package]] -name = "sha2" -version = "0.10.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sharded-slab" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "shared-entity" -version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ea131f0baab67defe7591067357eced490072372#ea131f0baab67defe7591067357eced490072372" -dependencies = [ - "anyhow", - "app-error", - "appflowy-ai-client", - "bytes", - "chrono", - "collab-entity", - "database-entity", - "futures", - "gotrue-entity", - "infra", - "log", - "pin-project", - "reqwest", - "serde", - "serde_json", - "serde_repr", - "thiserror", - "tracing", - "uuid", - "validator 0.19.0", -] - -[[package]] -name = "shlex" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" - -[[package]] -name = "signal-hook-registry" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" -dependencies = [ - "libc", -] - -[[package]] -name = "simd-adler32" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" - -[[package]] -name = "simdutf8" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a" - -[[package]] -name = "similar" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad1d488a557b235fc46dae55512ffbfc429d2482b08b4d9435ab07384ca8aec" - -[[package]] -name = "similar" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "420acb44afdae038210c99e69aae24109f32f15500aa708e81d46c9f29d55fcf" - -[[package]] -name = "simple_asn1" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc4e5204eb1910f40f9cfa375f6f05b68c3abac4b6fd879c8ff5e7ae8a0a085" -dependencies = [ - "num-bigint", - "num-traits", - "thiserror", - "time", -] - -[[package]] -name = "siphasher" -version = "0.3.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" - -[[package]] -name = "sketches-ddsketch" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85636c14b73d81f541e525f585c0a2109e6744e1565b5c1668e31c70c10ed65c" -dependencies = [ - "serde", -] - -[[package]] -name = "slab" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" -dependencies = [ - "autocfg", -] - -[[package]] -name = "slug" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3bc762e6a4b6c6fcaade73e77f9ebc6991b676f88bb2358bddb56560f073373" -dependencies = [ - "deunicode", -] - -[[package]] -name = "smallstr" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63b1aefdf380735ff8ded0b15f31aab05daf1f70216c01c02a12926badd1df9d" -dependencies = [ - "smallvec", -] - -[[package]] -name = "smallvec" -version = "1.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" - -[[package]] -name = "socket2" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "socket2" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" -dependencies = [ - "libc", - "windows-sys 0.48.0", -] - -[[package]] -name = "soup2" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2b4d76501d8ba387cf0fefbe055c3e0a59891d09f0f995ae4e4b16f6b60f3c0" -dependencies = [ - "bitflags 1.3.2", - "gio", - "glib", - "libc", - "once_cell", - "soup2-sys", -] - -[[package]] -name = "soup2-sys" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "009ef427103fcb17f802871647a7fa6c60cbb654b4c4e4c0ac60a31c5f6dc9cf" -dependencies = [ - "bitflags 1.3.2", - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "system-deps 5.0.0", -] - -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - -[[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - -[[package]] -name = "state" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbe866e1e51e8260c9eed836a042a5e7f6726bb2b411dffeaa712e19c388f23b" -dependencies = [ - "loom", -] - -[[package]] -name = "string_cache" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" -dependencies = [ - "new_debug_unreachable", - "once_cell", - "parking_lot 0.12.1", - "phf_shared 0.10.0", - "precomputed-hash", - "serde", -] - -[[package]] -name = "string_cache_codegen" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988" -dependencies = [ - "phf_generator 0.10.0", - "phf_shared 0.10.0", - "proc-macro2", - "quote", -] - -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - -[[package]] -name = "strsim" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" - -[[package]] -name = "strum" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" - -[[package]] -name = "strum_macros" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d06aaeeee809dbc59eb4556183dd927df67db1540de5be8d3ec0b6636358a5ec" -dependencies = [ - "heck 0.3.3", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "strum_macros" -version = "0.25.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad8d03b598d3d0fff69bf533ee3ef19b8eeb342729596df84bcc7e1f96ec4059" -dependencies = [ - "heck 0.4.1", - "proc-macro2", - "quote", - "rustversion", - "syn 2.0.47", -] - -[[package]] -name = "strum_macros" -version = "0.26.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a3417fc93d76740d974a01654a09777cb500428cc874ca9f45edfe0c4d4cd18" -dependencies = [ - "heck 0.4.1", - "proc-macro2", - "quote", - "rustversion", - "syn 2.0.47", -] - -[[package]] -name = "subtle" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.47" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1726efe18f42ae774cc644f330953a5e7b3c3003d3edcecf18850fe9d4dd9afb" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn_derive" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1329189c02ff984e9736652b1631330da25eaa6bc639089ed4915d25446cbe7b" -dependencies = [ - "proc-macro-error", - "proc-macro2", - "quote", - "syn 2.0.47", -] - -[[package]] -name = "sync_wrapper" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" - -[[package]] -name = "synstructure" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.47", -] - -[[package]] -name = "sysinfo" -version = "0.30.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fb4f3438c8f6389c864e61221cbc97e9bca98b4daf39a5beb7bea660f528bb2" -dependencies = [ - "cfg-if", - "core-foundation-sys", - "libc", - "ntapi", - "once_cell", - "rayon", - "windows 0.52.0", -] - -[[package]] -name = "system-configuration" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "system-configuration-sys", -] - -[[package]] -name = "system-configuration-sys" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "system-deps" -version = "5.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18db855554db7bd0e73e06cf7ba3df39f97812cb11d3f75e71c39bf45171797e" -dependencies = [ - "cfg-expr 0.9.1", - "heck 0.3.3", - "pkg-config", - "toml 0.5.11", - "version-compare 0.0.11", -] - -[[package]] -name = "system-deps" -version = "6.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30c2de8a4d8f4b823d634affc9cd2a74ec98c53a756f317e529a48046cbf71f3" -dependencies = [ - "cfg-expr 0.15.3", - "heck 0.4.1", - "pkg-config", - "toml 0.7.5", - "version-compare 0.1.1", -] - -[[package]] -name = "tagptr" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417" - -[[package]] -name = "tantivy" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8d0582f186c0a6d55655d24543f15e43607299425c5ad8352c242b914b31856" -dependencies = [ - "aho-corasick 1.0.2", - "arc-swap", - "base64 0.22.1", - "bitpacking", - "byteorder", - "census", - "crc32fast", - "crossbeam-channel", - "downcast-rs", - "fastdivide", - "fnv", - "fs4", - "htmlescape", - "itertools 0.12.1", - "levenshtein_automata", - "log", - "lru", - "lz4_flex", - "measure_time", - "memmap2", - "num_cpus", - "once_cell", - "oneshot", - "rayon", - "regex", - "rust-stemmers", - "rustc-hash", - "serde", - "serde_json", - "sketches-ddsketch", - "smallvec", - "tantivy-bitpacker", - "tantivy-columnar", - "tantivy-common", - "tantivy-fst", - "tantivy-query-grammar", - "tantivy-stacker", - "tantivy-tokenizer-api", - "tempfile", - "thiserror", - "time", - "uuid", - "winapi", -] - -[[package]] -name = "tantivy-bitpacker" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "284899c2325d6832203ac6ff5891b297fc5239c3dc754c5bc1977855b23c10df" -dependencies = [ - "bitpacking", -] - -[[package]] -name = "tantivy-columnar" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12722224ffbe346c7fec3275c699e508fd0d4710e629e933d5736ec524a1f44e" -dependencies = [ - "downcast-rs", - "fastdivide", - "itertools 0.12.1", - "serde", - "tantivy-bitpacker", - "tantivy-common", - "tantivy-sstable", - "tantivy-stacker", -] - -[[package]] -name = "tantivy-common" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8019e3cabcfd20a1380b491e13ff42f57bb38bf97c3d5fa5c07e50816e0621f4" -dependencies = [ - "async-trait", - "byteorder", - "ownedbytes", - "serde", - "time", -] - -[[package]] -name = "tantivy-fst" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d60769b80ad7953d8a7b2c70cdfe722bbcdcac6bccc8ac934c40c034d866fc18" -dependencies = [ - "byteorder", - "regex-syntax 0.8.4", - "utf8-ranges", -] - -[[package]] -name = "tantivy-query-grammar" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "847434d4af57b32e309f4ab1b4f1707a6c566656264caa427ff4285c4d9d0b82" -dependencies = [ - "nom", -] - -[[package]] -name = "tantivy-sstable" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c69578242e8e9fc989119f522ba5b49a38ac20f576fc778035b96cc94f41f98e" -dependencies = [ - "tantivy-bitpacker", - "tantivy-common", - "tantivy-fst", - "zstd 0.13.2", -] - -[[package]] -name = "tantivy-stacker" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c56d6ff5591fc332739b3ce7035b57995a3ce29a93ffd6012660e0949c956ea8" -dependencies = [ - "murmurhash32", - "rand_distr", - "tantivy-common", -] - -[[package]] -name = "tantivy-tokenizer-api" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0dcade25819a89cfe6f17d932c9cedff11989936bf6dd4f336d50392053b04" -dependencies = [ - "serde", -] - -[[package]] -name = "tao" -version = "0.16.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6d198e01085564cea63e976ad1566c1ba2c2e4cc79578e35d9f05521505e31" -dependencies = [ - "bitflags 1.3.2", - "cairo-rs", - "cc", - "cocoa", - "core-foundation", - "core-graphics", - "crossbeam-channel", - "dispatch", - "gdk", - "gdk-pixbuf", - "gdk-sys", - "gdkwayland-sys", - "gdkx11-sys", - "gio", - "glib", - "glib-sys", - "gtk", - "image", - "instant", - "jni", - "lazy_static", - "libc", - "log", - "ndk", - "ndk-context", - "ndk-sys", - "objc", - "once_cell", - "parking_lot 0.12.1", - "png", - "raw-window-handle", - "scopeguard", - "serde", - "tao-macros", - "unicode-segmentation", - "uuid", - "windows 0.39.0", - "windows-implement", - "x11-dl", -] - -[[package]] -name = "tao-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b27a4bcc5eb524658234589bdffc7e7bfb996dbae6ce9393bfd39cb4159b445" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "tap" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" - -[[package]] -name = "tar" -version = "0.4.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b55807c0344e1e6c04d7c965f5289c39a8d94ae23ed5c0b57aabac549f871c6" -dependencies = [ - "filetime", - "libc", - "xattr 0.2.3", -] - -[[package]] -name = "target-lexicon" -version = "0.12.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1c7f239eb94671427157bd93b3694320f3668d4e1eff08c7285366fd777fac" - -[[package]] -name = "tauri" -version = "1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bfe673cf125ef364d6f56b15e8ce7537d9ca7e4dae1cf6fbbdeed2e024db3d9" -dependencies = [ - "anyhow", - "cocoa", - "dirs-next", - "embed_plist", - "encoding_rs", - "flate2", - "futures-util", - "glib", - "glob", - "gtk", - "heck 0.4.1", - "http", - "ignore", - "objc", - "once_cell", - "open", - "percent-encoding", - "rand 0.8.5", - "raw-window-handle", - "regex", - "rfd", - "semver", - "serde", - "serde_json", - "serde_repr", - "serialize-to-javascript", - "state", - "tar", - "tauri-macros", - "tauri-runtime", - "tauri-runtime-wry", - "tauri-utils", - "tempfile", - "thiserror", - "tokio", - "url", - "uuid", - "webkit2gtk", - "webview2-com", - "windows 0.39.0", -] - -[[package]] -name = "tauri-build" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "defbfc551bd38ab997e5f8e458f87396d2559d05ce32095076ad6c30f7fc5f9c" -dependencies = [ - "anyhow", - "cargo_toml", - "dirs-next", - "heck 0.4.1", - "json-patch", - "semver", - "serde", - "serde_json", - "tauri-utils", - "tauri-winres", - "walkdir", -] - -[[package]] -name = "tauri-codegen" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b3475e55acec0b4a50fb96435f19631fb58cbcd31923e1a213de5c382536bbb" -dependencies = [ - "base64 0.21.5", - "brotli", - "ico", - "json-patch", - "plist", - "png", - "proc-macro2", - "quote", - "regex", - "semver", - "serde", - "serde_json", - "sha2", - "tauri-utils", - "thiserror", - "time", - "uuid", - "walkdir", -] - -[[package]] -name = "tauri-macros" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613740228de92d9196b795ac455091d3a5fbdac2654abb8bb07d010b62ab43af" -dependencies = [ - "heck 0.4.1", - "proc-macro2", - "quote", - "syn 1.0.109", - "tauri-codegen", - "tauri-utils", -] - -[[package]] -name = "tauri-plugin-deep-link" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4536f5f6602e8fdfaa7b3b185076c2a0704f8eb7015f4e58461eb483ec3ed1f8" -dependencies = [ - "dirs", - "interprocess", - "log", - "objc2", - "once_cell", - "tauri-utils", - "windows-sys 0.48.0", - "winreg 0.50.0", -] - -[[package]] -name = "tauri-runtime" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07f8e9e53e00e9f41212c115749e87d5cd2a9eebccafca77a19722eeecd56d43" -dependencies = [ - "gtk", - "http", - "http-range", - "rand 0.8.5", - "raw-window-handle", - "serde", - "serde_json", - "tauri-utils", - "thiserror", - "url", - "uuid", - "webview2-com", - "windows 0.39.0", -] - -[[package]] -name = "tauri-runtime-wry" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8141d72b6b65f2008911e9ef5b98a68d1e3413b7a1464e8f85eb3673bb19a895" -dependencies = [ - "cocoa", - "gtk", - "percent-encoding", - "rand 0.8.5", - "raw-window-handle", - "tauri-runtime", - "tauri-utils", - "uuid", - "webkit2gtk", - "webview2-com", - "windows 0.39.0", - "wry", -] - -[[package]] -name = "tauri-utils" -version = "1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ece74810b1d3d44f29f732a7ae09a63183d63949bbdd59c61f8ed2a1b70150db" -dependencies = [ - "brotli", - "ctor", - "dunce", - "glob", - "heck 0.4.1", - "html5ever 0.26.0", - "infer", - "json-patch", - "kuchikiki", - "log", - "memchr", - "phf 0.11.2", - "proc-macro2", - "quote", - "semver", - "serde", - "serde_json", - "serde_with", - "thiserror", - "url", - "walkdir", - "windows-version", -] - -[[package]] -name = "tauri-winres" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5993dc129e544393574288923d1ec447c857f3f644187f4fbf7d9a875fbfc4fb" -dependencies = [ - "embed-resource", - "toml 0.7.5", -] - -[[package]] -name = "tempfile" -version = "3.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa" -dependencies = [ - "cfg-if", - "fastrand", - "redox_syscall 0.4.1", - "rustix", - "windows-sys 0.52.0", -] - -[[package]] -name = "tendril" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" -dependencies = [ - "futf", - "mac", - "utf-8", -] - -[[package]] -name = "tera" -version = "1.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5ab29bb4f3e256ae6ad5c3e2775aa1f8829f2c0c101fc407bfd3a6df15c60c5" -dependencies = [ - "chrono", - "chrono-tz 0.6.1", - "globwalk", - "humansize", - "lazy_static", - "percent-encoding", - "pest", - "pest_derive", - "rand 0.8.5", - "regex", - "serde", - "serde_json", - "slug", - "thread_local", - "unic-segment", -] - -[[package]] -name = "terminal_size" -version = "0.1.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "thin-slice" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c" - -[[package]] -name = "thiserror" -version = "1.0.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.47", -] - -[[package]] -name = "thread-id" -version = "3.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7fbf4c9d56b320106cd64fd024dadfa0be7cb4706725fc44a7d7ce952d820c1" -dependencies = [ - "libc", - "redox_syscall 0.1.57", - "winapi", -] - -[[package]] -name = "thread_local" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" -dependencies = [ - "once_cell", -] - -[[package]] -name = "time" -version = "0.3.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" -dependencies = [ - "deranged", - "itoa 1.0.6", - "num-conv", - "powerfmt", - "serde", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" - -[[package]] -name = "time-macros" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" -dependencies = [ - "num-conv", - "time-core", -] - -[[package]] -name = "tinystr" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" -dependencies = [ - "displaydoc", - "zerovec", -] - -[[package]] -name = "tinyvec" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "to_method" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7c4ceeeca15c8384bbc3e011dbd8fccb7f068a440b752b7d9b32ceb0ca0e2e8" - -[[package]] -name = "tokio" -version = "1.38.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" -dependencies = [ - "backtrace", - "bytes", - "libc", - "mio", - "num_cpus", - "parking_lot 0.12.1", - "pin-project-lite", - "signal-hook-registry", - "socket2 0.5.5", - "tokio-macros", - "tracing", - "windows-sys 0.48.0", -] - -[[package]] -name = "tokio-macros" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.47", -] - -[[package]] -name = "tokio-native-tls" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" -dependencies = [ - "native-tls", - "tokio", -] - -[[package]] -name = "tokio-retry" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f57eb36ecbe0fc510036adff84824dd3c24bb781e21bfa67b69d556aa85214f" -dependencies = [ - "pin-project", - "rand 0.8.5", - "tokio", -] - -[[package]] -name = "tokio-rustls" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" -dependencies = [ - "rustls", - "tokio", -] - -[[package]] -name = "tokio-stream" -version = "0.1.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", - "tokio-util", -] - -[[package]] -name = "tokio-tungstenite" -version = "0.20.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c" -dependencies = [ - "futures-util", - "log", - "native-tls", - "tokio", - "tokio-native-tls", - "tungstenite", -] - -[[package]] -name = "tokio-util" -version = "0.7.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" -dependencies = [ - "bytes", - "futures-core", - "futures-io", - "futures-sink", - "futures-util", - "hashbrown 0.14.3", - "pin-project-lite", - "slab", - "tokio", -] - -[[package]] -name = "toml" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" -dependencies = [ - "serde", -] - -[[package]] -name = "toml" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ebafdf5ad1220cb59e7d17cf4d2c72015297b75b19a10472f99b89225089240" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit 0.19.11", -] - -[[package]] -name = "toml_datetime" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" -dependencies = [ - "serde", -] - -[[package]] -name = "toml_edit" -version = "0.19.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "266f016b7f039eec8a1a80dfe6156b633d208b9fccca5e4db1d6775b0c4e34a7" -dependencies = [ - "indexmap 2.1.0", - "serde", - "serde_spanned", - "toml_datetime", - "winnow 0.4.7", -] - -[[package]] -name = "toml_edit" -version = "0.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" -dependencies = [ - "indexmap 2.1.0", - "toml_datetime", - "winnow 0.5.40", -] - -[[package]] -name = "tower-service" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" - -[[package]] -name = "tracing" -version = "0.1.41" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" -dependencies = [ - "log", - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-appender" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3566e8ce28cc0a3fe42519fc80e6b4c943cc4c8cef275620eb8dac2d3d4e06cf" -dependencies = [ - "crossbeam-channel", - "thiserror", - "time", - "tracing-subscriber", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.47", -] - -[[package]] -name = "tracing-bunyan-formatter" -version = "0.3.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d637245a0d8774bd48df6482e086c59a8b5348a910c3b0579354045a9d82411" -dependencies = [ - "ahash 0.8.6", - "gethostname", - "log", - "serde", - "serde_json", - "time", - "tracing", - "tracing-core", - "tracing-log 0.1.3", - "tracing-subscriber", -] - -[[package]] -name = "tracing-core" -version = "0.1.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" -dependencies = [ - "once_cell", - "valuable", -] - -[[package]] -name = "tracing-log" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" -dependencies = [ - "lazy_static", - "log", - "tracing-core", -] - -[[package]] -name = "tracing-log" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" -dependencies = [ - "log", - "once_cell", - "tracing-core", -] - -[[package]] -name = "tracing-serde" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "704b1aeb7be0d0a84fc9828cae51dab5970fee5088f83d1dd7ee6f6246fc6ff1" -dependencies = [ - "serde", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" -dependencies = [ - "matchers", - "nu-ansi-term", - "once_cell", - "regex", - "serde", - "serde_json", - "sharded-slab", - "smallvec", - "thread_local", - "tracing", - "tracing-core", - "tracing-log 0.2.0", - "tracing-serde", -] - -[[package]] -name = "tracing-wasm" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4575c663a174420fa2d78f4108ff68f65bf2fbb7dd89f33749b6e826b3626e07" -dependencies = [ - "tracing", - "tracing-subscriber", - "wasm-bindgen", -] - -[[package]] -name = "treediff" -version = "4.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52984d277bdf2a751072b5df30ec0377febdb02f7696d64c2d7d54630bac4303" -dependencies = [ - "serde_json", -] - -[[package]] -name = "triomphe" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "859eb650cfee7434994602c3a68b25d77ad9e68c8a6cd491616ef86661382eb3" - -[[package]] -name = "try-lock" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" - -[[package]] -name = "tsify" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6b26cf145f2f3b9ff84e182c448eaf05468e247f148cf3d2a7d67d78ff023a0" -dependencies = [ - "gloo-utils", - "serde", - "serde_json", - "tsify-macros", - "wasm-bindgen", -] - -[[package]] -name = "tsify-macros" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a94b0f0954b3e59bfc2c246b4c8574390d94a4ad4ad246aaf2fb07d7dfd3b47" -dependencies = [ - "proc-macro2", - "quote", - "serde_derive_internals", - "syn 2.0.47", -] - -[[package]] -name = "tungstenite" -version = "0.20.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9" -dependencies = [ - "byteorder", - "bytes", - "data-encoding", - "http", - "httparse", - "log", - "native-tls", - "rand 0.8.5", - "sha1", - "thiserror", - "url", - "utf-8", -] - -[[package]] -name = "typenum" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" - -[[package]] -name = "ucd-trie" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" - -[[package]] -name = "uncased" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b9bc53168a4be7402ab86c3aad243a84dd7381d09be0eddc81280c1da95ca68" -dependencies = [ - "version_check", -] - -[[package]] -name = "unic-char-property" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" -dependencies = [ - "unic-char-range", -] - -[[package]] -name = "unic-char-range" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" - -[[package]] -name = "unic-common" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" - -[[package]] -name = "unic-segment" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4ed5d26be57f84f176157270c112ef57b86debac9cd21daaabbe56db0f88f23" -dependencies = [ - "unic-ucd-segment", -] - -[[package]] -name = "unic-ucd-segment" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2079c122a62205b421f499da10f3ee0f7697f012f55b675e002483c73ea34700" -dependencies = [ - "unic-char-property", - "unic-char-range", - "unic-ucd-version", -] - -[[package]] -name = "unic-ucd-version" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" -dependencies = [ - "unic-common", -] - -[[package]] -name = "unicase" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" -dependencies = [ - "version_check", -] - -[[package]] -name = "unicode-bidi" -version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" - -[[package]] -name = "unicode-id" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1b6def86329695390197b82c1e244a54a131ceb66c996f2088a3876e2ae083f" - -[[package]] -name = "unicode-ident" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" - -[[package]] -name = "unicode-normalization" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "unicode-segmentation" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" - -[[package]] -name = "unicode-width" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" - -[[package]] -name = "universal-hash" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" -dependencies = [ - "crypto-common", - "subtle", -] - -[[package]] -name = "untrusted" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" - -[[package]] -name = "url" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" -dependencies = [ - "form_urlencoded", - "idna 0.5.0", - "percent-encoding", - "serde", -] - -[[package]] -name = "utf-8" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" - -[[package]] -name = "utf16_iter" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" - -[[package]] -name = "utf8-ranges" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcfc827f90e53a02eaef5e535ee14266c1d569214c6aa70133a624d8a3164ba" - -[[package]] -name = "utf8_iter" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" - -[[package]] -name = "uuid" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" -dependencies = [ - "getrandom 0.2.10", - "serde", - "sha1_smol", - "wasm-bindgen", -] - -[[package]] -name = "validator" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db79c75af171630a3148bd3e6d7c4f42b6a9a014c2945bc5ed0020cbb8d9478e" -dependencies = [ - "idna 0.5.0", - "once_cell", - "regex", - "serde", - "serde_derive", - "serde_json", - "url", - "validator_derive 0.18.2", -] - -[[package]] -name = "validator" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0b4a29d8709210980a09379f27ee31549b73292c87ab9899beee1c0d3be6303" -dependencies = [ - "idna 1.0.3", - "once_cell", - "regex", - "serde", - "serde_derive", - "serde_json", - "url", - "validator_derive 0.19.0", -] - -[[package]] -name = "validator_derive" -version = "0.18.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df0bcf92720c40105ac4b2dda2a4ea3aa717d4d6a862cc217da653a4bd5c6b10" -dependencies = [ - "darling", - "once_cell", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 2.0.47", -] - -[[package]] -name = "validator_derive" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bac855a2ce6f843beb229757e6e570a42e837bcb15e5f449dd48d5747d41bf77" -dependencies = [ - "darling", - "once_cell", - "proc-macro-error2", - "proc-macro2", - "quote", - "syn 2.0.47", -] - -[[package]] -name = "valuable" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" - -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - -[[package]] -name = "version-compare" -version = "0.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c18c859eead79d8b95d09e4678566e8d70105c4e7b251f707a03df32442661b" - -[[package]] -name = "version-compare" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29" - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "vswhom" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be979b7f07507105799e854203b470ff7c78a1639e330a58f183b5fea574608b" -dependencies = [ - "libc", - "vswhom-sys", -] - -[[package]] -name = "vswhom-sys" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3b17ae1f6c8a2b28506cd96d412eebf83b4a0ff2cbefeeb952f2f9dfa44ba18" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "walkdir" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" -dependencies = [ - "same-file", - "winapi-util", -] - -[[package]] -name = "want" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" -dependencies = [ - "try-lock", -] - -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "wasm-bindgen" -version = "0.2.90" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.90" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn 2.0.47", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bde2032aeb86bdfaecc8b261eef3cba735cc426c1f3a3416d1e0791be95fc461" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.90" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.90" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.47", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.90" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b" - -[[package]] -name = "wasm-streams" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129" -dependencies = [ - "futures-util", - "js-sys", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "wasm-timer" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be0ecb0db480561e9a7642b5d3e4187c128914e58aa84330b9493e3eb68c5e7f" -dependencies = [ - "futures", - "js-sys", - "parking_lot 0.11.2", - "pin-utils", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "web-sys" -version = "0.3.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "webkit2gtk" -version = "0.18.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8f859735e4a452aeb28c6c56a852967a8a76c8eb1cc32dbf931ad28a13d6370" -dependencies = [ - "bitflags 1.3.2", - "cairo-rs", - "gdk", - "gdk-sys", - "gio", - "gio-sys", - "glib", - "glib-sys", - "gobject-sys", - "gtk", - "gtk-sys", - "javascriptcore-rs", - "libc", - "once_cell", - "soup2", - "webkit2gtk-sys", -] - -[[package]] -name = "webkit2gtk-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d76ca6ecc47aeba01ec61e480139dda143796abcae6f83bcddf50d6b5b1dcf3" -dependencies = [ - "atk-sys", - "bitflags 1.3.2", - "cairo-sys-rs", - "gdk-pixbuf-sys", - "gdk-sys", - "gio-sys", - "glib-sys", - "gobject-sys", - "gtk-sys", - "javascriptcore-rs-sys", - "libc", - "pango-sys", - "pkg-config", - "soup2-sys", - "system-deps 6.1.1", -] - -[[package]] -name = "webpki-roots" -version = "0.25.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc" - -[[package]] -name = "webview2-com" -version = "0.19.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4a769c9f1a64a8734bde70caafac2b96cada12cd4aefa49196b3a386b8b4178" -dependencies = [ - "webview2-com-macros", - "webview2-com-sys", - "windows 0.39.0", - "windows-implement", -] - -[[package]] -name = "webview2-com-macros" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaebe196c01691db62e9e4ca52c5ef1e4fd837dcae27dae3ada599b5a8fd05ac" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "webview2-com-sys" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aac48ef20ddf657755fdcda8dfed2a7b4fc7e4581acce6fe9b88c3d64f29dee7" -dependencies = [ - "regex", - "serde", - "serde_json", - "thiserror", - "windows 0.39.0", - "windows-bindgen", - "windows-metadata", -] - -[[package]] -name = "which" -version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" -dependencies = [ - "either", - "libc", - "once_cell", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57b543186b344cc61c85b5aab0d2e3adf4e0f99bc076eff9aa5927bcc0b8a647" -dependencies = [ - "windows_aarch64_msvc 0.37.0", - "windows_i686_gnu 0.37.0", - "windows_i686_msvc 0.37.0", - "windows_x86_64_gnu 0.37.0", - "windows_x86_64_msvc 0.37.0", -] - -[[package]] -name = "windows" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1c4bd0a50ac6020f65184721f758dba47bb9fbc2133df715ec74a237b26794a" -dependencies = [ - "windows-implement", - "windows_aarch64_msvc 0.39.0", - "windows_i686_gnu 0.39.0", - "windows_i686_msvc 0.39.0", - "windows_x86_64_gnu 0.39.0", - "windows_x86_64_msvc 0.39.0", -] - -[[package]] -name = "windows" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" -dependencies = [ - "windows-targets 0.48.0", -] - -[[package]] -name = "windows" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" -dependencies = [ - "windows-core", - "windows-targets 0.52.0", -] - -[[package]] -name = "windows-bindgen" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68003dbd0e38abc0fb85b939240f4bce37c43a5981d3df37ccbaaa981b47cb41" -dependencies = [ - "windows-metadata", - "windows-tokens", -] - -[[package]] -name = "windows-core" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" -dependencies = [ - "windows-targets 0.52.0", -] - -[[package]] -name = "windows-implement" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba01f98f509cb5dc05f4e5fc95e535f78260f15fea8fe1a8abdd08f774f1cee7" -dependencies = [ - "syn 1.0.109", - "windows-tokens", -] - -[[package]] -name = "windows-metadata" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ee5e275231f07c6e240d14f34e1b635bf1faa1c76c57cfd59a5cdb9848e4278" - -[[package]] -name = "windows-sys" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.0", -] - -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets 0.52.0", -] - -[[package]] -name = "windows-targets" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" -dependencies = [ - "windows_aarch64_gnullvm 0.48.0", - "windows_aarch64_msvc 0.48.0", - "windows_i686_gnu 0.48.0", - "windows_i686_msvc 0.48.0", - "windows_x86_64_gnu 0.48.0", - "windows_x86_64_gnullvm 0.48.0", - "windows_x86_64_msvc 0.48.0", -] - -[[package]] -name = "windows-targets" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" -dependencies = [ - "windows_aarch64_gnullvm 0.52.0", - "windows_aarch64_msvc 0.52.0", - "windows_i686_gnu 0.52.0", - "windows_i686_msvc 0.52.0", - "windows_x86_64_gnu 0.52.0", - "windows_x86_64_gnullvm 0.52.0", - "windows_x86_64_msvc 0.52.0", -] - -[[package]] -name = "windows-tokens" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f838de2fe15fe6bac988e74b798f26499a8b21a9d97edec321e79b28d1d7f597" - -[[package]] -name = "windows-version" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75aa004c988e080ad34aff5739c39d0312f4684699d6d71fc8a198d057b8b9b4" -dependencies = [ - "windows-targets 0.52.0", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2623277cb2d1c216ba3b578c0f3cf9cdebeddb6e66b1b218bb33596ea7769c3a" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec7711666096bd4096ffa835238905bb33fb87267910e154b18b44eaabb340f2" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" - -[[package]] -name = "windows_i686_gnu" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3925fd0b0b804730d44d4b6278c50f9699703ec49bcd628020f46f4ba07d9e1" - -[[package]] -name = "windows_i686_gnu" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "763fc57100a5f7042e3057e7e8d9bdd7860d330070251a73d003563a3bb49e1b" - -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" - -[[package]] -name = "windows_i686_msvc" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce907ac74fe331b524c1298683efbf598bb031bc84d5e274db2083696d07c57c" - -[[package]] -name = "windows_i686_msvc" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bc7cbfe58828921e10a9f446fcaaf649204dcfe6c1ddd712c5eebae6bda1106" - -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2babfba0828f2e6b32457d5341427dcbb577ceef556273229959ac23a10af33d" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6868c165637d653ae1e8dc4d82c25d4f97dd6605eaa8d784b5c6e0ab2a252b65" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4dd6dc7df2d84cf7b33822ed5b86318fb1781948e9663bacd047fc9dd52259d" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e4d40883ae9cae962787ca76ba76390ffa29214667a111db9e0a1ad8377e809" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" - -[[package]] -name = "winnow" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca0ace3845f0d96209f0375e6d367e3eb87eb65d27d445bdc9f1843a26f39448" -dependencies = [ - "memchr", -] - -[[package]] -name = "winnow" -version = "0.5.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" -dependencies = [ - "memchr", -] - -[[package]] -name = "winreg" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76a1a57ff50e9b408431e8f97d5456f2807f8eb2a2cd79b06068fc87f8ecf189" -dependencies = [ - "cfg-if", - "winapi", -] - -[[package]] -name = "winreg" -version = "0.50.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" -dependencies = [ - "cfg-if", - "windows-sys 0.48.0", -] - -[[package]] -name = "write16" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" - -[[package]] -name = "writeable" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" - -[[package]] -name = "wry" -version = "0.24.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64a70547e8f9d85da0f5af609143f7bde3ac7457a6e1073104d9b73d6c5ac744" -dependencies = [ - "base64 0.13.1", - "block", - "cocoa", - "core-graphics", - "crossbeam-channel", - "dunce", - "gdk", - "gio", - "glib", - "gtk", - "html5ever 0.25.2", - "http", - "kuchiki", - "libc", - "log", - "objc", - "objc_id", - "once_cell", - "serde", - "serde_json", - "sha2", - "soup2", - "tao", - "thiserror", - "url", - "webkit2gtk", - "webkit2gtk-sys", - "webview2-com", - "windows 0.39.0", - "windows-implement", -] - -[[package]] -name = "wyz" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" -dependencies = [ - "tap", -] - -[[package]] -name = "x11" -version = "2.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "502da5464ccd04011667b11c435cb992822c2c0dbde1770c988480d312a0db2e" -dependencies = [ - "libc", - "pkg-config", -] - -[[package]] -name = "x11-dl" -version = "2.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" -dependencies = [ - "libc", - "once_cell", - "pkg-config", -] - -[[package]] -name = "xattr" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d1526bbe5aaeb5eb06885f4d987bcdfa5e23187055de9b83fe00156a821fabc" -dependencies = [ - "libc", -] - -[[package]] -name = "xattr" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da84f1a25939b27f6820d92aed108f83ff920fdf11a7b19366c27c4cda81d4f" -dependencies = [ - "libc", - "linux-raw-sys", - "rustix", -] - -[[package]] -name = "xz2" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388c44dc09d76f1536602ead6d325eb532f5c122f17782bd57fb47baeeb767e2" -dependencies = [ - "lzma-sys", -] - -[[package]] -name = "yoke" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" -dependencies = [ - "serde", - "stable_deref_trait", - "yoke-derive", - "zerofrom", -] - -[[package]] -name = "yoke-derive" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.47", - "synstructure", -] - -[[package]] -name = "yrs" -version = "0.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81de5913bca29f43a1d12ca92a7b39a2945e9420e01602a7563917c7bfc60f70" -dependencies = [ - "arc-swap", - "async-lock", - "async-trait", - "dashmap 6.0.1", - "fastrand", - "serde", - "serde_json", - "smallstr", - "smallvec", - "thiserror", -] - -[[package]] -name = "zerocopy" -version = "0.7.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e97e415490559a91254a2979b4829267a57d2fcd741a98eee8b722fb57289aa0" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd7e48ccf166952882ca8bd778a43502c64f33bf94c12ebe2a7f08e5a0f6689f" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.47", -] - -[[package]] -name = "zerofrom" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" -dependencies = [ - "zerofrom-derive", -] - -[[package]] -name = "zerofrom-derive" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.47", - "synstructure", -] - -[[package]] -name = "zeroize" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" -dependencies = [ - "zeroize_derive", -] - -[[package]] -name = "zeroize_derive" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.47", -] - -[[package]] -name = "zerovec" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" -dependencies = [ - "yoke", - "zerofrom", - "zerovec-derive", -] - -[[package]] -name = "zerovec-derive" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.47", -] - -[[package]] -name = "zip" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" -dependencies = [ - "aes", - "byteorder", - "bzip2", - "constant_time_eq 0.1.5", - "crc32fast", - "crossbeam-utils", - "flate2", - "hmac", - "pbkdf2 0.11.0", - "sha1", - "time", - "zstd 0.11.2+zstd.1.5.2", -] - -[[package]] -name = "zip" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc5e4288ea4057ae23afc69a4472434a87a2495cafce6632fd1c4ec9f5cf3494" -dependencies = [ - "aes", - "arbitrary", - "bzip2", - "constant_time_eq 0.3.0", - "crc32fast", - "crossbeam-utils", - "deflate64", - "displaydoc", - "flate2", - "hmac", - "indexmap 2.1.0", - "lzma-rs", - "memchr", - "pbkdf2 0.12.2", - "rand 0.8.5", - "sha1", - "thiserror", - "time", - "zeroize", - "zopfli", - "zstd 0.13.2", -] - -[[package]] -name = "zip-extensions" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb0a99499b3497d765525c5d05e3ade9ca4a731c184365c19472c3fd6ba86341" -dependencies = [ - "zip 2.2.0", -] - -[[package]] -name = "zopfli" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5019f391bac5cf252e93bbcc53d039ffd62c7bfb7c150414d61369afe57e946" -dependencies = [ - "bumpalo", - "crc32fast", - "lockfree-object-pool", - "log", - "once_cell", - "simd-adler32", -] - -[[package]] -name = "zstd" -version = "0.11.2+zstd.1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" -dependencies = [ - "zstd-safe 5.0.2+zstd.1.5.2", -] - -[[package]] -name = "zstd" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcf2b778a664581e31e389454a7072dab1647606d44f7feea22cd5abb9c9f3f9" -dependencies = [ - "zstd-safe 7.2.0", -] - -[[package]] -name = "zstd-safe" -version = "5.0.2+zstd.1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db" -dependencies = [ - "libc", - "zstd-sys", -] - -[[package]] -name = "zstd-safe" -version = "7.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa556e971e7b568dc775c136fc9de8c779b1c2fc3a63defaafadffdbd3181afa" -dependencies = [ - "zstd-sys", -] - -[[package]] -name = "zstd-sys" -version = "2.0.12+zstd.1.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a4e40c320c3cb459d9a9ff6de98cff88f4751ee9275d140e2be94a2b74e4c13" -dependencies = [ - "cc", - "pkg-config", -] diff --git a/frontend/appflowy_tauri/src-tauri/Cargo.toml b/frontend/appflowy_tauri/src-tauri/Cargo.toml deleted file mode 100644 index f85e7c9151..0000000000 --- a/frontend/appflowy_tauri/src-tauri/Cargo.toml +++ /dev/null @@ -1,137 +0,0 @@ -[package] -name = "appflowy_tauri" -version = "0.0.0" -description = "A Tauri App" -authors = ["you"] -license = "" -repository = "" -edition = "2021" -rust-version = "1.57" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[build-dependencies] -tauri-build = { version = "1.5", features = [] } - -[workspace.dependencies] -anyhow = "1.0" -tracing = "0.1.40" -bytes = "1.5.0" -serde = "1.0" -serde_json = "1.0.108" -protobuf.workspace = true - -diesel = { version = "2.1.0", features = [ - "sqlite", - "chrono", - "r2d2", - "serde_json", -] } -uuid = { version = "1.5.0", features = ["serde", "v4"] } -serde_repr = "0.1" -parking_lot = "0.12" -futures = "0.3.29" -tokio = "1.34.0" -tokio-stream = "0.1.14" -async-trait = "0.1.74" -chrono = { version = "0.4.31", default-features = false, features = ["clock"] } -zip = "2.2.0" -yrs = "0.19.1" -# Please use the following script to update collab. -# Working directory: frontend -# -# To update the commit ID, run: -# scripts/tool/update_collab_rev.sh new_rev_id -# -# To switch to the local path, run: -# scripts/tool/update_collab_source.sh -# ⚠️⚠️⚠️️ -collab = { version = "0.2" } -collab-entity = { version = "0.2" } -collab-folder = { version = "0.2" } -collab-document = { version = "0.2" } -collab-database = { version = "0.2" } -collab-plugins = { version = "0.2" } -collab-user = { version = "0.2" } -collab-importer = { version = "0.1" } - -# Please using the following command to update the revision id -# Current directory: frontend -# Run the script: -# scripts/tool/update_client_api_rev.sh new_rev_id -# ⚠️⚠️⚠️️ -client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "ea131f0baab67defe7591067357eced490072372" } - -[dependencies] -serde_json.workspace = true -serde.workspace = true -tauri = { version = "1.5", features = [ - "dialog-all", - "clipboard-all", - "fs-all", - "shell-open", -] } -tauri-utils = "1.5.2" -bytes.workspace = true -tracing.workspace = true -lib-dispatch = { path = "../../rust-lib/lib-dispatch", features = [ - "use_serde", -] } -flowy-core = { path = "../../rust-lib/flowy-core", features = ["ts"] } -flowy-user = { path = "../../rust-lib/flowy-user", features = ["tauri_ts"] } -flowy-date = { path = "../../rust-lib/flowy-date", features = ["tauri_ts"] } -flowy-ai = { path = "../../rust-lib/flowy-ai", features = ["tauri_ts"] } -flowy-error = { path = "../../rust-lib/flowy-error", features = [ - "impl_from_sqlite", - "impl_from_dispatch_error", - "impl_from_appflowy_cloud", - "impl_from_reqwest", - "impl_from_serde", - "tauri_ts", -] } -flowy-search = { path = "../../rust-lib/flowy-search", features = ["tauri_ts"] } -flowy-document = { path = "../../rust-lib/flowy-document", features = [ - "tauri_ts", -] } -flowy-notification = { path = "../../rust-lib/flowy-notification", features = [ - "tauri_ts", -] } - -uuid = "1.5.0" -tauri-plugin-deep-link = "0.1.2" -dotenv = "0.15.0" -semver = "1.0.23" - -[features] -# by default Tauri runs in production mode -# when `tauri dev` runs it is executed with `cargo run --no-default-features` if `devPath` is an URL -default = ["custom-protocol"] -# this feature is used used for production builds where `devPath` points to the filesystem -# DO NOT remove this -custom-protocol = ["tauri/custom-protocol"] - -[patch.crates-io] -# Please use the following script to update collab. -# Working directory: frontend -# -# To update the commit ID, run: -# scripts/tool/update_collab_rev.sh new_rev_id -# -# To switch to the local path, run: -# scripts/tool/update_collab_source.sh -# ⚠️⚠️⚠️️ -collab = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "67b31e4bcc64d81332c37efecb0a8618681f1db9" } -collab-entity = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "67b31e4bcc64d81332c37efecb0a8618681f1db9" } -collab-folder = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "67b31e4bcc64d81332c37efecb0a8618681f1db9" } -collab-document = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "67b31e4bcc64d81332c37efecb0a8618681f1db9" } -collab-database = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "67b31e4bcc64d81332c37efecb0a8618681f1db9" } -collab-plugins = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "67b31e4bcc64d81332c37efecb0a8618681f1db9" } -collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "67b31e4bcc64d81332c37efecb0a8618681f1db9" } -collab-importer = { version = "0.1", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "67b31e4bcc64d81332c37efecb0a8618681f1db9" } - -# Working directory: frontend -# To update the commit ID, run: -# scripts/tool/update_local_ai_rev.sh new_rev_id -# ⚠️⚠️⚠️️ -appflowy-local-ai = { version = "0.1", git = "https://github.com/AppFlowy-IO/AppFlowy-LocalAI", rev = "6f064efe232268f8d396edbb4b84d57fbb640f13" } -appflowy-plugin = { version = "0.1", git = "https://github.com/AppFlowy-IO/AppFlowy-LocalAI", rev = "6f064efe232268f8d396edbb4b84d57fbb640f13" } diff --git a/frontend/appflowy_tauri/src-tauri/Info.plist b/frontend/appflowy_tauri/src-tauri/Info.plist deleted file mode 100644 index 25b430c049..0000000000 --- a/frontend/appflowy_tauri/src-tauri/Info.plist +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - CFBundleURLTypes - - - CFBundleURLName - - appflowy-flutter - CFBundleURLSchemes - - appflowy-flutter - - - - - \ No newline at end of file diff --git a/frontend/appflowy_tauri/src-tauri/build.rs b/frontend/appflowy_tauri/src-tauri/build.rs deleted file mode 100644 index d860e1e6a7..0000000000 --- a/frontend/appflowy_tauri/src-tauri/build.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - tauri_build::build() -} diff --git a/frontend/appflowy_tauri/src-tauri/env.development b/frontend/appflowy_tauri/src-tauri/env.development deleted file mode 100644 index 188835e3d0..0000000000 --- a/frontend/appflowy_tauri/src-tauri/env.development +++ /dev/null @@ -1,4 +0,0 @@ -APPFLOWY_CLOUD_ENV_APPFLOWY_CLOUD_BASE_URL=https://test.appflowy.cloud -APPFLOWY_CLOUD_ENV_APPFLOWY_CLOUD_WS_BASE_URL=wss://test.appflowy.cloud/ws/v1 -APPFLOWY_CLOUD_ENV_APPFLOWY_CLOUD_GOTRUE_URL=https://test.appflowy.cloud/gotrue -APPFLOWY_CLOUD_ENV_CLOUD_TYPE=2 diff --git a/frontend/appflowy_tauri/src-tauri/env.production b/frontend/appflowy_tauri/src-tauri/env.production deleted file mode 100644 index b03c328b84..0000000000 --- a/frontend/appflowy_tauri/src-tauri/env.production +++ /dev/null @@ -1,4 +0,0 @@ -APPFLOWY_CLOUD_ENV_APPFLOWY_CLOUD_BASE_URL=https://beta.appflowy.cloud -APPFLOWY_CLOUD_ENV_APPFLOWY_CLOUD_WS_BASE_URL=wss://beta.appflowy.cloud/ws/v1 -APPFLOWY_CLOUD_ENV_APPFLOWY_CLOUD_GOTRUE_URL=https://beta.appflowy.cloud/gotrue -APPFLOWY_CLOUD_ENV_CLOUD_TYPE=2 diff --git a/frontend/appflowy_tauri/src-tauri/icons/128x128.png b/frontend/appflowy_tauri/src-tauri/icons/128x128.png deleted file mode 100644 index 3a51041313f50b6a508d22530b8ab0ebf852d0b9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9256 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 KMNUMnLSTXwZGAET diff --git a/frontend/appflowy_tauri/src-tauri/icons/128x128@2x.png b/frontend/appflowy_tauri/src-tauri/icons/128x128@2x.png deleted file mode 100644 index 9076de3a4b004c3ff7a4e4ce79cafb11049f8a05..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19737 zcmb@N zey{8P6YlfmoG0he-e!4QFkYZ&*P;&x$zqAsRezRk2V99pOW9~HQ*xN&onJ*=MudMX;YNTkAPhffG zP3LOs>Rme>BIN)3C8LmALa%g+c$e{fb@)g8llfw^B)M#no&fp-F0QS$+MK7_eWgfW zY@`}tCdaCa!IwkfqGFzI4^?-$^DRPj#|3JcU*zt6ruH3~ojV7pE&eTN9ue+nq#}2% zNap?MV+zrHci>0}i+ToGrg~Vu~HYjEszM6*Q1E z+JPiA1jC<;@_qcc-50cAfG6W^#@o(VQ!OoNxi{kO+r!xY8Z$qmNZ44^GG5cf^b95m zzuF#7*D-zb=8Yw#0+DGgWz`#fU0q#n&p*@LkJP;?A5Wmsj7+_vOq_ItJ6Y)Dq&x1v z-XnP!FcSWX+QMa6fBy8_6uFfMcYnTEYons!;NXBBbo4`H9~UDuD=se1X@9oDp{Lmq zD@;ahvC+k4soC9@WYl2e?znR`*8SJY($W%L#aaPamGyU~VIV0~gdR|8*~s0vCz_%f z?Tn-3VL-%6IAgDGrf6gfI?R^86c7*~VbRDI&v=YgH3_eRG;^zqggcXxS6usAFE%(~ z?(gq=JI_~J>@)wItuWG3cU<+`>?bOMfk`o>NwKYI^d|#OME%)-{!GTgJ~~g9skzzN z?Xj;yE|<+a*?yNIJDKP7n|B5;UcA^i)%e{}$vE=Cnaog-2+B&jo1>*&*^c+zj3NMA8%DSTMud-)TJISaL~K^q(54@{i*SVJbg1O zPMZme#B>Z3^!`ZV(cb6^3$A`Uk};PZ^mxNU!DV_6HiXiwYlyZyw>HRlg_C3hU6q8r zC8}&Y8#K_6_&XiIihH?;zjjQ1d&X0X>w0Ft&Tb@451PdMIfXHSHKD?FD-!A9DK$Cg z7=eT$VNh1iVX%@BPD-rB?;l}w3vD0HQ%03H&h37pceAnLzF|m{laf;$Jy)_FgSBTJ zYRX6l0IM?eVX$-R0_V)riJv)PB(>9B7Y&xaVf;`u6=-Oa4dOJ!*17Gpw>gV*W(&*(sSZBu_C< z<!IV1}GarAdMiC5a9q$)3$3r2&4^3YN9{) z4adYKIq^yEruFRXz0cNXJ=z>T+tI$4EeS;>c8XgjWX5>B&|+4+bW^j=z=uf)4*613 z&{JUOy8b8a;me0+L!e(;MyhmkM$aq&6owKXY>$K?&x%Of!0+G0xe8N9%Q5V4kL3hm zVTURRK|yo8Nz|D-8WNtvgP+;VhQ`KnOy|N2?yDxmzUdJR@y$$$*5*MF{`oc5w(6yAw6%UtH+3+ox=0t$Ydtq@J18Cr-vV*j7hJJ z9G1gRTe_i(2w%X<19b3X!(0eX5?(JljBXW?RHwr2q7zpq!b@PTDv8n;SNJ^^7Qpmg zjfcGaY-nc^j)5@&_Q9@0)63^Znvy1##LHGvnp%2TUPsUcUQ^{fUK9l>btKlUtZJAK zJ7a-uU%B|qRdeKW@V(YkP<=*t_21Q_AtOr16Q~jbIqu7ar)J&8D>lePhG|hwe*`g; zgNNBc!WE8g@Q&@aX80Zb2cC_n2@{1?8hre?{se|G6T^7t8V*9eMLh1Ui zDe?fHbn=VH?<5`R&X&5ChL*D{3#~Vj3NfZK~R zJSss)nE@ee6VMa|Szri{pTi#hbVkHnZb=))TK^ru;fFrut+j8!$&Qj@A6VE$d9Jv)=mhwSab7m63 z(jU7*?C6K(%hvu70eW$pUqa41DDiIOh&6kVyKm-WrB6zBiKlHDXcNExCJB^ED!Vyq zJ(di%@1%NIyW(?lal=T0F|R&g-}d`V)021#xW1h`RYtRTTxs%ziEW74DsfYq%*p61 z%H@1r$PAw_rj?*D?^OeIs@CUfAkE)?2qb0%@fb4G8Fe3rQuD1ON_`If;-gzs;_|cg z3C*E6klYYMD52L++QH5va6Lb;2D`WssPI~Da@(kL-x+NOo1pzM&zLRDH<+sbbX@dC zCx)D(1zBqJR zbkmQ5(Qgp_CjS^C+|7TCK7Z?t$l}WN)Wp*i2*V++#YiRse=#prqXBlRNZrZTRMSaC zM5Wm0yfJ>3ed*^Om&R4QB80j=a|QY?L`W4n6}la9A7{#z-CiYquF3P9Al>?=E@{-@_$#_dK~xoX%+ry@-M)B<*7n50 z|M@9=xH**1u}Ra;_Rj9D`T+VLtY9mo4Yv&lC4YtFE|xCjiEoe#2ba8&Y9Qx81h7^o z{<0icY8-Y>44@!ar9&vI&(IMG36cw+p}up}usDs3 z&YSuv^{*{nhYP-YuZlK?YI#+yw-cLXo!U6tFDAJ@OkCIwJ2F$o@}DlOf*qxumS+0X zv;Dp7TZ;TT>Govg#ud4w-!|QMf_k$g+L{othlhQiRzA(0WH5JLR?`Jb>cisUbU{?O zZ!*@AYpgBOpqM^=b|S8{keZOHY^bXkhO+GX`RH(1D(uGM=4^`~%kQW;N7+^vw$UxC zHd$;zCvf6_)V#COO}Cd>blL6xPWSA1PCPR9iOESIG&Ui)6t0ARQ9I6@eKZv5+S|0r zLP^(Tu`2`T9ZjLEZu24wqUB4s?b3%>Jz7`L6&1h_5ab$&$pq$vsPUU9Rw%he;zf!T z@z@VRyNfg!p#jM0-w?KyJnLt48G7~Wb&!TaM9;J9#eYM2wNO3FmrPkO*>w#>s55kr z&U2Sx25P%vO<3Rg?8aQji#$va8x?I7&D&)$i>Y?Dj`e#)~M1GMyWseKhZ zB!)fv5{@ue2ymW!ytd4HeaFosRUjy$i=|M_PtCGu%$D;hC~HK|p+F3s_0wd}v>#l_)fi9NRpfG^Z{Sr~LbFxJd(e z?e=?{3=dP4$ zChl;1v4_K2_xs%;SAEBHrprCyT9gA1+4pFQjYKoY7y0yjr%>`LfqxTmrz0eGVx%Ii z%P?tcU^ssC8E^guBLp<_3JGv)5u$6sE|BAQqS(iLD{}i4Sw?81@=DHO)nLb3uye=F zHXjU67tYK9T$*^VeAqd>$S5(~v6x7b-KX=XTB~7eYOad-keG0^#C8XttV?8bwPyS$ zFT^|XR^G}qvLarT>K8eZuql6o4|b6_V6+_-S!qI%B_3uB6{*ALQc3 z92*v$TMzj{T(sYT?VkBU<3#i0p9_?_-d!FV#Y9KC z68(c?gU+Yg^p#qm0r9~j>7mh1Z#h+(7DBhl(l6OCy!DmVw?oqexa~B|{M-H6QFOLw zufv;E2;aMSXyONCXjZ?Oo%puC{>%d2EHPOm`L{$R^5$jM@3-4U(d|+uXw~aQ zN|ZNk@5lCsHOAx(f;|bu`b(t(iH2P*L1qmqmuHDL-D-7g*)u5HiC38{D|-Bbs`jF)FaZt=uydd;PQyR9t%n`vCJX>y6!=^X!{Ly_uF7kMH z@koO!EYN#2*0+N19`A38YHpO1h)2*Fc}2ypmfXiIHk=nL%{kaiXKcI8Bo}e10`-6w zGhK3r9ds7wWRZWrhHa4`0%^=lv2X7a)iWz0rTR>*fH*q*}I=Jx0Ka{QfiruX?|Pn6(6}cYojk zo5AZRM3R^!SHmT}??WjB$ z8=1VcJsH68U>pC>MGD&te$v?#>1Wu?2q-Oi>}E+ob)+WVl*#trOK#s^ce__;@Wd-w ztuLKMZiE|lP|s8T$BuI7R(1oGW>SNy8)!IY8@V0U8C2kBBOkFy-p0|5z2Yx_eWGm$ zL3}byVODz{@%SWM`Z!bwxNdjQJ*m=A`?jMGmYq)!pG;f+WWtKl!_da5-5AY*1-@Il zA+p9b3>LDx8;Ri|HJV08ik;wN4-CmOenNbCH~>!ZPBlzTSLR8G&Wvm4)LJ3%s^SNh z4HbP4!2$fDUs11T%{q-S#c~Ehqe~gM-$&lud^;hFDP)hNW3mX^{zxB~fTJsqPqF(< z1{#`E)c`VnivPBCz;*VGEaXDesyFtf`s05Xd-0%ZD)DE(VKL?K=5oRjsjQ>sh4om zl`?7DP)N{&HqEB&i;oxF^QH!asb?a;XW}5ctK_ev-9vAvUGunlNY^QP(Okww(gc|5^cCB-DOT-1n?Z-8z^ngFmDVL6UBD{Z|C^MF(;*+wO}u&FnUi z`9#L^x#`*2FYljwmq1|OABNNU*oFoN2j#K3tr%^v`u@h5eMfzv@I}^I%8lMWsQ<|o zK7-bL9oNLAw}*_M{o+zXY#4#q-(hsPHCkG7^e|GA8ZY$cNK91%rwMj>M!e2TO|RTW zzmp@-m+d-2*(dRJ_aeL&g1z%k&NL>DxvIb;)W{66^0Z|9)Vx~iFjDNTlY zllY9sL{=bA0!<;CVHD5pGD{c03YP8x`&)a~&iDk3%2fjPW|tb|wY!2bE%Df%6pUVS>v$ype)426 zB%Vf0iL0Byhc!a`O+bI@uI*@0Qiin~*o37->x$k+z;2zA&Wpotru;|6hFxrx%)R%SCZ zGjGzPV+tfN5acQlYf7$rB$j2CC@K1&`^tr_M%8INimdp1Uk(eiWK?>pE^3G8g>XJt zU*4uu7csUD93-)_A;7MFn5ZDywH;oQ7m+2Ndxk8YS@l@J2diE1CpQLFE=d2ftCUY6 zj$r3JXv>C-@;bjyXThuz$bA2T3tXFAJ|5#T9(CRIS~#!|meUM^<J*6OGz-d} zJzCsn*G3OYRMboJXg%^aNCVk-)`v!kC96{y+{HKPU{eS=B3Q#RA z%GMdkck(s$KkB0bbiJ(0y#Xrudh}>*w;EM`2*>*spHgYwX_U1h?GU=sZrn$WA}*S@W$~ ztJ*x0G7(~R!8Hl~;er2=L+)rX$S&eGON*7A6*8i`kfY|Dcx)-Y!HiSNZ2xlRk(d{D z-To3_jJd&XkwgoT^gx3ax=NWjWB3%r@x5jShuGNI#8xFQ!%&X6Pb+AevRzH-Z{qep)UUFe9kIsgB za5DZ!9iHulsOzwl)%xDsD5ODRF^E_z>IIC2u|KqP>7n|Z5%pkGdyr3=#Gx*tylQ|% zKDZJO%v-341IzppgL9}+Q86nyf&W|Po|EnHR6zs5BbD*E!NqNU$>PbQyZ4~39M*{p zQ+R-&=dT%!sZ!{o0wVY>KdW~gptnnS+&wb61(Z1RD;&?G3a{8??d(~c zjtW(P{@0N9@X0gC`smza30hDC&#@Wn?yE+5AU!Wh7iaXb5cGxwZRuicTvC-6=5u9z zp~Z7fxZkcHSy3-D%m(dwZ|teYmQ4VzhG_Ra#{bqi=5PQ!ZHXC%_2)73)muH#kD8~L z<@cHDw@FbNF##86>P@#o($F2u$>ei7ew*ll>t6$amC9`~-if^+oLGEfcO!1^0eg)c z;a~-f?iWpi6T3RITojOQdblz-qUZ74MR(@EVi%q4Ti)2(?dd! ze+Ld?3yWb+5NMq^*}i>-%Y=heJK#!VW`qIGs}!)T`FnqgcOp1+z(Wvp=iAK~zc{b9 z;x|0WTO2&~OuhbesnEcnAZT>Ub(tb~i8tPxG1?ZwNE! zy!{_>5^ImE!){imoVzN8S|{%3;B_+BFS0*dbu;so+!%>{U8~IjsXi&yIHXBQ-h_w` zU{h*Mm-!`X)PdXzWxDPYbVZEprJ>^F=&FW>1C=3ua~JS)$xwkN-1*fBYU;jK zUp(H)spl_CWIb*vVJ@DS)p#`{Mwfr-cP7xcah!%G>Cog^~HxhXAZsJW6e(+`1YqIYD8KIK;LS%-ez8Q&mn$bda)bq?T2g~ zv77~Jq-X+%0ar|dCxYkWlLiSd3uImQu42}zD{rnTqo-X=MFgl9o!RIu<65e zMM`o(Z!_7E#&jHQu5H-=~ndScbqS#Jd6OrZ+3<+y66@t z!{AD2olYlgdyE)Fk?4PN1$+q&jlNnu9u}PZD*6}Qa-rp9DAr}y!XwLrS91nS#=Z?B z2y{Yt34NG)_kpE_98nTapyZjVoMI@}h~LYHq8rN?7yHh6^Sl>J7&Hs?k?8x0r;Fpm zb894>C1GrjbXOoP^2ba2&%uA_}RmLNdED84a*= z#f%Xj3s)QIVAJFJH=!)o+B&arHHJB;Q;y{h zs*d5)L2FJ%E4|>ze=x8|O-RHvK`S0@{O=0K-*8`hLPv(y zn(Ewik=*k2NGKXtBUZyVC!3ydntnF;x2lJes8Msi(~rx= zzmoQ}zIzKAa$6cwI-6+^rR8L69l7u7p?_rKZicRYu{3nKku5yh}COB=NBl^|cB#t>}u2 zO+3`mes3&SWCP+vU&3~Q0$mqL0GguJVS(|dD$lvCb3g^eh}pdy(9 zxsb1Ei@P-j6#2jC*xnKa8cc6A!dK06W5Bup4SR@tV58m~uPM{jdnfuqrav|nFvid= zV_e|?Jnxx#)isJn8oxJW1vMmX8Rk+dd<)X1k@|XI<}IaKMfVrb-q*p%w|-4n`Cu;uY6eOt7N`_H3m02jT*$PK$R4L_WR^2RY~__ z21-jo-o}>+*#reLg5MNYE}iXE0>yd6Xqc^hb=oxc8(BbQ8wm@JS9#C$NV~|cUP})| z!u_3OEBQJc6XUMnw)R+LSlHlvFFFAl|Vq1gR)sCnmC9PN z+UElzfKFE5d*Y)?*phB!+gjtdx{$~NLVpFV^_wN=7!;UHLnC`4n(~&#;T!1I1k(@W zj%!=k{zQ)LgS{D*d=j5&;BpbLbgM{6r5(u*&mH&r2mK6B$BABJjCqw?FIZgv=mygX z<@*ny%>lV?C}dK_9=~g7{DY=nB-r+w$F6Ib2n_e$`{btpXfooi5TK3}|M~pkZnrpA z+zf*<*DM<8t|oC_s~BR2(Tl>383O}hpYVjBP9F!oe5@@N^xw}_4@a~+9uo8W@V5oqcIn-*`)QEnhPZP$YOnpH`acIyZ$gb zX5ZbSeD-%>j734}@C$L%g%O#ieu@8s1yMmU!F8iD5ZA9HB_d~(bs8pXN(ernO*(Zc ziBE~}>s#o#^16s)INu@L&&;*vd2n;(mmMOoAGQDQx%Fqw=T&uG9~aUsnn=GtZl24V zn%O`6PAF5#n)tGjCJ=`CXM&j3qw@v)N^R(?hIFteR@JWVXN0n}iU%5J`S>#(JdMJI zcGHwLjx5YuJGu_>`g6{ELy^p@g7*#DNn3vf3jU^KUS6v%OVR8EvKfNYXs5@kW}U8_ z*qe%j@%X*zYz~I-v^$>HDzB&c{wB*bZ5n;%w#0T(A1u9j zUyWo@lcmYoq0;~rqv#)J1H~z}4QD*kU!uH}dT(-cGPs|XE78Q)!LAi%d@n!ED8Rl= zs15IV3x4H(ZwTdVDhrXn!5Us7u#3LY!F`Dy`K$8-0}8z!a9)Y(#PspD5UQw8ssFI* z^Z2f%{<;q<0SZV)Z(3n?rypa;sjBHhXO1b}V6G)|y+-}YVcU>aCfgk9KfsjpS|hPD z)R0g2<7h$mphh0Zvtzi5M2iMD+gZ9~;<@e%B3`IvM%#$E38K>I>d5FS zQNlgKfun~RQtBI~P0HKn`Gp)P*jm%n`NUJh)B;6&edXD zNip%wh8ulDG4fQaU$wcIkX(`nTk^D(NK=e$m_M*hE28$g4&!;?S=(+?04Zu#2UJ;- z_uef#1m|5k{Xw<)V@=v$llb)LGk`+fx#D);oxS~ieoNYfeJc5nU7a(ev-_t;3>98F zCWp(YX8UFyGHNyXUKYBbAL1RH0!KS+amTd2gjGLXt|NnrkKyJp zBDm^bZ;}+ni5)4(auP)$C>Hla-|?{n^)dn8`u=v5NQn?q(pam^?QLPG?jKJ9H~bt= zCm#RXvHZ0!PgDbGIh1CBMS4bVcQnJ#t3^`G46sgCUIGN_;52>IGPoAXuN3zNK8F=` z%$IoM@*J!BZJ~F1PFzl^vo6NgTN4v>I&mZPV~#`f{e6yDTpBLe?m>~u`lkHC2W5kv zYeJQ+k>(Q0QjPUy|K^j(W65u8zAc0)Oz9_e{b`K}fKMCI+M_69#>vv4W|TwOz{YM7 z&Bd60UrFHi>4ZT4CYNH&Mjr|2(V!m5EirhHK6X_8;c(1>o65YaL9Z1FWL25#tEXFu z3YaP=8D>U%SxWf##yEhv_u3f9HPZa0L2NwC^OhWmg_Nsbf0n;c4ZOWcnYs5VLt9J0 z12h`V|8O%b#7&^b{Iw9JZgnRxiQsP^nG35Ud9zCth&`3KNpr6M&!$$NZ**EMU z#Lfq{zD1H!R`v zB2i80Ir;6^_xLZN&|1yN40r7$d>V8^YY*5U!NQL+9dy6*lK_GKXDrA!BD4^Auq+Ry zO!0NW(NG;Hg4q`>Swv5>vhDz&LF?ir$+3yhqcgQoN@Vt3D#xV7WR-iKmN}LX41+}e z+b=)rzzAs-bd2UFXS&1TsMYijRWH?Y_IKNrBXEHQuT{Ja>yxn|$;Z<*ClHK9Wh1!N z%Clwi6dP?kPWXMg>g#zGX$$?6zDxOzY5+sR%zEDd&5K-ZJ|Lr(mK6PWwXu1g2s;6M z02RXiU;NkH?2so2!jXxBl*1pxNWu=wBG+N%)se0e$Wc*CNs~3yw6|7Omd672#B4Vr zdt?2C4OseTG$GP(RWBF5{sC5`&@YdeRe?s3oV+0tdM?4J0{^NRMgN-!saA@?I;z6m z;lz?WILLd#<)}r7N;9={r*i*DbBtEs8CtauVh~Go{*yGvc%4lM_d%Z=bR4EK#nbd^ zD3<4wu)@360F?O5xH}$ZIjiXcs17IuL)-H~V;-_kG+VoT{ny`ZkXfP_^)W>uUcUyA zBjswFLB+JM4IqIGY3SYm6$&hZGEBjCKG?`DPei53={uBM05{Vl)RPN*@Q4NfYXh)I zePrVA<&!~~ff4nRJd*F;`Q6}GA3b8uR|O)1ke6%pftn5m6+wpPbHdZOm${7QQW4ru zmEiGFQKEVybYN0j@M-{`eT#Eppk?}(77F`B=B5-~i- z@(U2Ns693iw))ve!jl95>8oz|^o+0TcHOM&cEkboQ}9rrB7#J+T_o~vw;Z7_YX|tJ z>32v10qS;i3b`5^vQ~)$Vvep%2qzT%58ns9opSX6EPN)63SLDvn2p5AuKyx@D0&g% z-{5wyvO}LGKR&qRDuGHr#@_bc??6#MDxviXpD!-{=@S2IYW+wdJXNP~CTKgE>&R`3 zBTesJh)a5s_0q5vb(=$hO7kMn&+!8v1dK2=L049LCaPnf8|lK$#{ zbpX?m{U5RnyNWU)?LpAC10WdXR@)TjiVfE zlCX=IDVufP`w0@8atu1(;)r^kUs4XzLk(PG7&^3)yY;R<%95hnfj)*B?@!%n7nT@| z3De3J`^hXt_*DSM|2DbYoy z@yr^b_LMPx?bfTY@l$gx(K(l_4m8mnRZzzHvB(5CPYFUPE97u~%2|eGz6^=A!~589 zU{w=&v|6<-gME+-fsut{_D+k=-8%LAIE;bxgZSV(@xwfU>u;|or|iTEA#>3>M;s@^)SJJoSF{-$gsd#F(mPS(@q>%4n?tl$fjLAxTTV}x?TOo9%1 zibuPIv-`~2pGEHY{Y&5zBr$q`yfDZ}C~Y~5H>11;#$8hdriShfU6)vOv?3sUD8Jdh{QOa9W(0zhAgt( zJ@eo8_|+!;3f(2%eAx4}>lTKzs!S^U2W29#_2@iwLI|7#f`LRF)q>7PLzX!QD@;2# zNA34|shoso@j&82XFg^*=IT$9n@X;Znu!XCPwwSq*e-%(&$)Ygd6U5MyxXrp`B-|mwtO4Ih?(v zhBgS(C$7QvD02p5GnbG%+XPRokz96*@yX-DkT@(@LF2-Uv`5^i6W4W3OVar_;n+Dr z&yx3KoN*sxZTP9)PPEj{PXF4sD&Do-nc#;2qKCNI=i?GBq$`9#gWa&!9R6jr3IBOE zU%oPwG*@i=>`pJ&QycG7zz*-x5A-*B4Uap^gfU(SiW$cn1~W$z;uuGaZvFOW*{qUl zK4d&U>9H(zq2Y=N!u1>NzuN2n;DO9f-%?-xk)XV=-->Y;r&O)OB^kW1w7WhchK@YL z3B!2{i~FjyQEK8=l|)SAZ8w3>d%ryfrZ!~I-Gd|??}$~f4II#){@LC3oc4FNikhI zA1B;oMSn?4ZiqzU!3R%}NXt616evI&XWxD{a)zy7QMVEN#D8=iCj>Y=Jf?M!HVNWioOY&vh|*S)qz*hDaL4S5K7hs}=iMunl+e%kf6uw}z*ZL$){W`Ni2@^LQ*qS-#nZ0}T=}PpbcP7l zdDd&fO;=s0T55r0Iuki23@@rJo%TcU&m8QEv^F@v(Z|X^na2z+Ga5_sL8vZk*)BJ{ z0m=%|4tx=|^7Fz!iE-8LWn($hJ5XJY&e*jPz7L~+lEJSq5{Wf&Tx4}$-=G(ts>ndb z>ny11mzw1=hd>}yeMf)iVF%{g;Rk?b@#Adcb$nHFE-OlbIgp5Yf2tKPfXV&9j_@@( z%;rRI26w`hGT;fUir4UPg)@GBUnu9mV)SZCs+0jTWzR~%4h0Y+9qnapIZ0VPsww*IZw)yQt%m!43g<@FPeoIrF( z&2hG7 zUkcF8!5gomLK25bzr+srkI~6lrCgP6)crVHZYOdG{#Hn6$Q|74wQ-t?gYtni5f599 zsE3++ewRLXqOBzUL>91o2ds!tnBRFfN0|{Ji*46)AE12;;4-$czXTarI51DayI8F? z+e9+>GeANgYQj|!7Jp}YuKyWnwOA2u>*L(%E&>;L_aNT97_UfcQ*8d_71JMbT$%&t zdcZ@hXdj4tpJIl~!QbIENNGM`PRE)3yH`rX)2J|xLL>Irc7{(hb?PV=&%M4aQ3A0u-QO4WX^rl)3(wNj(iJyZUB(k{G{)Q zLF*srryx-+L+4-wt2IgvKJ}Mw82fINpBDB9JKbQTMyqnG5~OyBSegpt^)S}#h%G(0 z&jo;&%sP2-a2QZ%Z+LfgEVS8dZ8!QbE@yOD$}s*mg^cj;R>RV%IqX5T5?)N$qUZea zlCBN)~r2jZgFMxhhol1h8XOh;`Ysn><^8UtrWy~>{cE1w~;0+eGSMoqA=hwgR61z}MK2~4zG@SPAjgcvm z^s|CX4?2k$2(LTsB7w*x&8>!xnsp9@S@fEKCQ9aF%}bAcYaS(lK;s-9JEij+k=B3s z4*%0%kPxmCJK!asgs1OA-q}X558C`GybRnNh-N!A?<;c?d_SXc<259G z(cy4KlNPlcCG_ll7p*@hoRqTj>mlX+{($~pGddIP2u0gHxj!KYVwsJ7x*QzMH=*yj zw^it2V(N4P(t>7e=3e#iA-yk5qc)r2O25Y&j5W;1kF`292YSR-tUdC6a;J^-M7i%p zEakAi{H6lTX9_y?oqU51U&uFWYe8=-Y{82eFZ7lG!NXl_^l0Qh7hCS#@$q@R`GOd9 zKut6Q!1ves#yV|^;eW0X$#tK5LAU$NJ;L}HtW+5*Vkas<2;zQF*#Ui2Y2rGN)qr3A z5P!SoXzIFh_PNLI{qwd^P0?qQ>4Xz<6zah z@+=42`75DYe%_-ac$@(DRT2_BLdsQ{W%JsVY<$W;#fw?R7>vJmdG{`a!>FN}>btfH z1t$eG;KA~~DiH^r?h9_KWv9d!VFabI4vlR9}81yc-%~4Bc%`OD$tFD3E^D} zVFtkk@hK%t0$PW2x9sr6GC9vzf4Jxyuo@ZDXCAea@+=SOAu-Xq?pv0y4?3(RlZ?Rp za*RIU^xZ@=oW%0F)95)4nQvKz}V~FVQDkpl{bIN|?FiPBpB5$UTWG8Vd zfI4MN%>?ag1rbs?56|u{^Ic3vggM-G-`VczrYr=mV+WajO3=S3@_!P3lX6)UR~rAx zHXm$QIIr_V&;IKhwY2qrc+=j$ViG43h92x!1(xC@PIiWOYRI!;@L0?Tj^UUKcof1o z>`rG*Cj#soBww68{&#oY*4bMxMfQ+3eZiQRbc&6fN-rW8 z+p`nO!B{A8cMWOr-@;ZBf{h>Pj{UXLPP!RvqxWb{!^c#e{*yHXmw(y0H0Kb^Es&ADk>L64x zhT3O^%b2WfwaJ&{gnZIpwb5>`s(>%#7fEFB&^Z@sc)Xwy?^3WXa7-^O4uU=RSMWyj zs3gb3eltV2xP}Hn%ow7EgR(D&qC+gqGE!6Ln66)y#1v%7YHIU<5*=GCHKFer4W71N zAV7?}3BRD0!U=0XkbJL=3O<9fPoALX_ zTU(K!i@U{%<5ja1wThr&bWabDX*zKq?iYK1>wAIzV;5)VaHquv8l?N=tA40lx+{_u zS3c;8y&nC%OAvS1^+RguFF_DDwByq=P#v0R&

YTwDpB&Y6uSef*F=+2e!^+l~3Z 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 PdB$dLj5R7XaKir&Th(Uv diff --git a/frontend/appflowy_tauri/src-tauri/icons/32x32.png b/frontend/appflowy_tauri/src-tauri/icons/32x32.png deleted file mode 100644 index 6ae6683fefb9797ef769e75c1efc625051b5296b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1922 zcmV-|2YvX7P)DA(5(5cqpii=b;RRuCvi>AAF?fUHFq@kgqyEc*vgPjo8G( zCsSf%WaQB-9IX%t;6$Lf{nV*buaPGmsu*mg#ntbf1;%~?O#dBdEJ}=k%|pOLF93@h z`1=^pY;t{_a0Ys4-`BYQ)OKJ?0f>2CTVn*(Q4}=>1_pMNN~N+EN?o6POU#p)8WYQ! zte*b4!>@J&6MqzJ0C@^2l6h$qc=gA?TAHvyY*_>3xEB_wOb&Qy7WmZbz_F`b;~A?H z;ZZJ^%kSO0_tDI7oeE4qA%OF42?9aGsplj7Wj`6vio#84Uh||SjtdlaxkRB{d+=lY z*Wky}S}VTeIyY+Me*ZY|%3P|1^74jCNx_A}mCqELPPti)dMP_6+_&m_tu8uXH~bl2_0 zM=PHm<$pKv?msko*0x*jAbDcQEUH`F`?N*h79SF!AMONhCEJK)Cs3VAh!=i&#fK*` zLhCTA?z*jJD`$O`n>rc6ECq?R$ssKYDlA@&k>6aySAT7>^GiArt^;3)gx}H62%r)u zv?`oSrCcCJ-l=C5F-KFqnz~zQzA{yZ3F-+`6&h4)Ra~iD#=zI-@yMTYxaD(U;+afA zVI?siCQyI$2?}la_{%4SK~gD0)hc)`fnm3bs0o%z?GoaJ5cR6TrrSe&`HA(o^}e+p zkOZ4v_GT^x-}koyGu(f!oSLJ#+GNOX*4R;8S^vXst?788u|u`xbuOv7}{Mx zK$N@Gw>o_PW^{;vyyqhh&t5c`XoR@y<^YGc!81_Tdq_w9Ur(APr4`c}`TypOa~Eck zh2%Ll|Fhm(IcuZVb0cmzw=n8lbH>HivzeTnJjY6`R(c@Kw*;(vUqXEJ1b+w6w{3xb z<^kZoPZMO^4fJZ`2UC~nt3n=&ph^eP^X~Jo3sbc4cBoyi3#dBsx|5UfdE$t&OKd2$x7%|A-Lk-M z|80V-dOW-n_!fAu0Q=aBi2qsg)uvqu7yFc;)i^%_jDLk^E&v;IQ0sVhom3>i z%Oa4drfw>FEi+e9mr z(#KqiFR@_+E|V3l@PG`E)f!7)8V&vIgFYYRpuV2CnhNC*q4GrPh3Xk#{;wV{%Y{p2 z4;|v^b+Ai$nMx9=i=?asXvs_YA=9f2kjZ<^*hv`NlMgXo3s8(1a*QEM29ud>wTSxh zvuK{Ka4_I2;y0B`E1B|wM0{USDw|l5ZKEDZBpvyc1OB=C5`Wu2#F2ZQZ){<1)S8O_CW- z#wf-=%mrzeSIK14HhK8COM1sqBrb^-mlhYxB#HTvBQG3fud6J-8Fjtqb-Tl5pvfkg z)kyMNicGirY`1IKs>}qKGA3Go`LQ>0Lr(IXY((*-&UNAOUqOn{`jmN%0ssI207*qo IM6N<$f^|T`)c^nh diff --git a/frontend/appflowy_tauri/src-tauri/icons/Square107x107Logo.png b/frontend/appflowy_tauri/src-tauri/icons/Square107x107Logo.png deleted file mode 100644 index b08dcf7d21ab955d1c587b5725620607132bc01e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7695 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 JPDHLkV1hA7>?QyJ diff --git a/frontend/appflowy_tauri/src-tauri/icons/Square142x142Logo.png b/frontend/appflowy_tauri/src-tauri/icons/Square142x142Logo.png deleted file mode 100644 index f3e437b76e43efab7651f695cea467ec49e42f1b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10254 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 diff --git a/frontend/appflowy_tauri/src-tauri/icons/Square150x150Logo.png b/frontend/appflowy_tauri/src-tauri/icons/Square150x150Logo.png deleted file mode 100644 index 6a1dc04864d952fd21030b1586d25989415d2c92..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11031 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 R=fnU2002ovPDHLkV1k&8tAhXl diff --git a/frontend/appflowy_tauri/src-tauri/icons/Square284x284Logo.png b/frontend/appflowy_tauri/src-tauri/icons/Square284x284Logo.png deleted file mode 100644 index 2f2d9d6fe630caa77b6679a907d8f183d74b8ef4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22113 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 r6a*_)u%&@JlQ^*aU!M2m&rRwC;6+b zt*w;|YU-dJXX1VP_U+Z6`pB0KO8G^ll(loD{CF<4yQJJvCS~W3QeNYT{gzr2wKLRy zrFK%vrRStPA64sJ-Y)>(wryLD#xoh+CE-A-s;YX%A23594!h=^9)8#W#Cm~(nG|f( z=sAnV!$3J-IQol>Y zpyaT9VC`p4nNTPc<^`is&p7oVX;@-{(_e})`A6fzAx&RW-<$6N7MB1Hfm=axI`ZM1JlFcKpdDcfakw- z@znN^Pk2598(H)2Tm&pCr1fOph@m#tV~LE_Oaz$=JIBRS8QkUx;}RDyH*6CzGBJ%Ha1>Ok7+7yw~mF;E>6Ai5>HS2l9n~L^BsvE+bJC>G{;* z^bmnHUy`zaNM+^LgL51X8$Z-j~q`B=I)9XJByK_4*)C|6l%;hJ6=_`38OVhb-DY5kc z#LxiDa3Ks@DfcwNS+!Gnh(X8Q?NWuV!A`viqjeqZ3(rC(njwqAenud6R;Nx5VBHrb zzU#V09-%yUJQY2W88A*?Kx)f@;Kr5X7cppmBXItHAWqL27K~DA1?1ZT*2JE4`J0nU zf)a|5vSKDc)yI3MTrznnyj@2z`g5BfG(LOih27hL4tB*b0a3cifLmqoIvP^v6@qzs zlBqKeFsmPB;-knj$JS9`K8T4Pp`H1@2gbx8^?;hR?afh&+lzK?%)0^%lj{6SO|S0t`M{fPub z%;$BLz#Akd+rn2a8jVFqMn)3jH0~wo>+9=P)^eq_ektVc4diJ$pqn7JGRV&c?EDCs zE{2hY7P`LXCdPu;#%Jv>9rbb5_NJl$c34_ zgL35Pu|xTJdEv2zrID+c=US|$re-VGNL;QIk=TYAqBVwT1o6nxo>?vUq6F9LoH87_ zQ4;oPLw!Si-BpF07*qoM6N<$f_%PGd;kCd diff --git a/frontend/appflowy_tauri/src-tauri/icons/Square310x310Logo.png b/frontend/appflowy_tauri/src-tauri/icons/Square310x310Logo.png deleted file mode 100644 index 230b1abe58c88622d22d15e4546f25527c4a1971..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24225 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 diff --git a/frontend/appflowy_tauri/src-tauri/icons/Square44x44Logo.png b/frontend/appflowy_tauri/src-tauri/icons/Square44x44Logo.png deleted file mode 100644 index ad188037a377ea08307915d91900bc4f79820a9d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2808 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 KMNUMnLSTX?Xm0}m diff --git a/frontend/appflowy_tauri/src-tauri/icons/Square71x71Logo.png b/frontend/appflowy_tauri/src-tauri/icons/Square71x71Logo.png deleted file mode 100644 index ceae9ad1bb025a48c0f4b5bfabae54e1bd0fcd6a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4873 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 Hu0mjfsYf*K diff --git a/frontend/appflowy_tauri/src-tauri/icons/icon.icns b/frontend/appflowy_tauri/src-tauri/icons/icon.icns deleted file mode 100644 index 74b585f25dfbd90bdf3e8f3c8f0a98b2055f10fb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 181898 zcmdp6WP(HyYz1t5VpzQIQ%4)s$S>Q+pprl!0O zRs%MrQZb*t_bUU`J(F(|C9*Y+z5|ztLPy zJBE2e;Ga3N-Cbir=#TsH2MS~COP#%o^v_{B+}pTQ1ET-#+fOh8ComxxHely^6tLhO zzBb|U{+nmtaMdkhRYh(C&CEthx28}dJdKUh?^-qe!QGG~+MP#KhGX*fM5kbU;M1j* z8_uKa&#bfG0XYBY$8i8uj^skb>1 zE(V8E7foHn&j`0PE4TgluV z8oxdr_Zl3(xR6AaK1x2zQj`p?_x$qA!1Vg%ybxhHDJmxh*=o}(VIP3yuq=DG>-c<~ zVREyITnuSaTwh&wy$XXkeLT7x#)GZsRR|B+acC2Lp|PgX_uD(RlesW$t`SJh&}(V1qii{rmWk zRf@S?!}@!ra4F2!LNF~2CX4g3U+FI4Z3er}K`jf;v(5Y%hL`WUaCmzU4AR_8&CNSZ z_4VgsoxhITehQSR=d9lxPwN>_MHp7qPFej^=}#oIjSu)E&A@JM;SuCr3uOqRPYSgN zg^2D2a+X70s53qoS@Pag1&;H^ZI~e*gadiEgjzpKVY*dwSQ=_;PtmlswN=!FR{kJE z-A<F$xt4|R1juqlF{R=1Jfhc3P%u#w!*5UT`S|!YchoZij+;DuWln|? zhmLoDc#IwMKKN`l?4JJ>46B7PkZ<9|7oZ41t2jSi3KX1r(O${{>|=}$G}K{o41JCb z%3;G;@j{f;c|BjPaymj7C~Al+;SC*+NAL6&DJ~w)4&8z;(zhS{9y2mBTA10m zy{M|K$-dfGfREhq0|=+%*RPNGQkN4&7_{!4?e{{8wC{WAgMxzYUxmgfw_-Uc_>3;6dk7${ zd&ey&W*I})-4G5Yg^X)Amj-{^lF^9rIC|HglaC60+7lx8d~F5+(uWN)tgl_E*0(#U`dwlGok|9MGEKLJgDUi$6JmKmSNk&r=I{H+8fqIF*1F;ZPMz*E zOJBI(`rPVE*@KAyl3-f5?^V}+gksOq{w~)#Ozr)CKE8asmlr2}d;0YA5fS$YYm>WY z_W6aeMDW?_fi?_46*G@W2`=-~Up9s!;#Z1fRseiV;-uA~D->1 z1lBrPnh7K7re}?>#hYhYZ}v4qv(SR2THtYZ8&Gr}kYc=9kE`k3aK?B?K?1b6?GXUj z6g|Ofq$kSZvgl&uh=wX+cf3eUSBr>FTcYOb-x^J**_cUmm=Q0RSzXRJR2@v#4DKbW z*CB~yu(;EvqwQU5KRTz`;WC080wB#g>6ryFZeq@M%!mO9j>7})u!~q%*dil538CcI zKpnNYu`s!Lydhbiy0XQ z7!W=D#li!CQYt>?o$$g0QdrK}q>NxYae-w}@PYgW2o8A*r# zI6okL4GM1G{AQPVD;j`qohZ1FNqB`4Gr5Y&jy7qn)})I3nfUhm`x#yRAoE%?F(#@? z+W<5B769hRCg_oK9$0;feCh#LT&N8E&Y}PA@E`-?^a=AQT{C#RU0gJ3-udn% z?Y|k#5uCO5o$25q-?qLA4g#|Gt41*LKMQsowrzZ*X%E~lDnJx>beOjm=F=H&-6c;JY4jxgJJ<5Wqn~-eYFJ73X}4Er66DQS#{QNBX)(r@&K6=HC+2 zzt%c)baGM0kA5R z?XM74v0^*ZXgK7hFIGXkWVGnL*`LB$Gf`ha-(lMZ-mFfT5cK z0EXj6arRD5)MVf17llLhP!?pV>5CBB;A-$KW?L)f;jR0LZ_OA(jFmB0*NOF zcm8D>DRpvk3Nj?>^tf>IjYY8klaffJtUSkJ^BPvWDS5 zhXO-yYVs^*_z7TO!Pg=z50jYyKxo>n+^KGW1<(EQ!oy#e9lhI#4&_%~RD9$q1_1Nj zI5jys{c&`*{D_v>1Lfr?5#s=`J2{0^9)Bjn*>?yw2ms(rHVK&fiyHWK_xvP&49mS( z6AQLZji&Wi5&KQN+5Sz|^7~-?|8A^eqs51lN20W=HNS<~$+qf(!sk+`2jA;NK%N~u zjA}vBj|d2!$VqQv=kIO&r#)uNwod$K2}@sX!GaSomJvDmx`+^ca?)3hAMV{{)niXK zhy8i}g2(BFu1VzaeJB>bGsuvcQw1#=uazWL;2U1{4WCSV7KRnxq5Ght;KVgu!=}LL^JsDD@4lWQ7hiXF9Ta(p(tB{Qv$kZp z3Re-7sKb?81KWpa^I2`65gYwiLIBTg^Woh0_kf^K z=>!3eu?hyxO=|P#q{<~VL& zI^>E8%P-9lxr_{W?^H<(J)m-~P4>e}^+7t)Ibw*f|LrnDJ@7d%&a(0~YPK7J3t`43 z2Y+x}1hVfbEskfxS{MFM=#X^97xYA+Mq*=rE>x>b3HJS(Y;bYo!swr!u{VjIThL-K zVOO+X!mvNWpmvWgmq{QcpUc5I^2y%OhzB)5gy`sxOl<(m!(Q*RNkStPt24k1!WukRN8?MdC8nx#+6z*1v(LENgB6zvq33d@Gl& zg^ArQB^cWM{z~XL?()*EDE!5o=MU~fDLFJK1InwWonz( zN&1rP)=*!y3q5_KUi^W8-P?J;46?B1Bt2|-B40(ENl=-*o68@|h@83Y^n>o78h%kr z=F3RYwZMzr|8kAF&Ic{VJ2y<48@sL!8XiEsnpuMfi#hx=GKNVov7LA#TCJwP2hLk5 z=?&Rkz{@z;*gZ_R{!82OR(LP#P31(PWyn|SE~c@buc7iG<{H>Pu=P30vg@iI^%pXb z1KaYc{A@-GDk!s!oQBNuF7}-W*P<%5la~=8M-ikl)tct*4bK-?Dmyq)T;xx_$=0fW z8WW}TFH?@}ayAk&P1}a`oHI7=<$hZA+K8Z#W~8?`L)Bl5$Q_}UeUE(6f}l7nYGV_U zuTPRc=uPXyG!Nd#_uvR3KW#gVIrhznV>|qA_IkQ4w9t-kAmcW0&A$wY!qCXaLc42v zAjE8KIP5=L#n|lt$5jk-ndT8#|>0xftKt!Y4zHVaaW(j zjvo@p;bPv3yZ&{uj9}AgX(Bxovrte?Rb{IxQixjPS!n{J848nb1%W1Dd)Rfh9b{lZ zD>E%3`q!@#J3^(3;md9O4AM%k5OGhI>s0w z^r=={FLaRS7@PJ(m0Low428c-1V8`wTez&Otl;YAM&CplxMMHU>Uh(%aCi-6Xh9-f zHxcgl15u!y6MXLjyOwA@$mr}Ii+o9@^#;-{7lALmRubWw+m9L8z<7vZ()HpF?(uTW z4cV@RElH<5Xm@#Q6w9N&@Xc!Ug#CK}D-U`Axirg|8HnehZy&P7m3yzBNc@oCPNCog zZ5=GDv&0$aM)^?W%uPFZbM32#`Zi6-fU2^IkUuE|e&v)%;Qn|u2i^ResNTN36Qw(R zr5gmTbPv~h*}j@soWN4_bpLKI{L*hU(W3LdC1MJfgR(DcPQLBxSdsh(G*ODlG*hq?nscHcp(>B_J zRJ58V?j_S85}VHXs8L$>v~i};ntFEeKqICQ_s0CMjNiktJNYF4#>=fzK2k5ruJIVI9N^)3{6ko%3!!abaPO=C{6L< zaolnaX%+a~P9+qh>xzNzqxA|-LMw+Xa%L%7q_uQqfA$Xt%jFiir=D3zI6j_I2DG$REP=6y<-hd{cCIPPGy}Vdc9WQS)uBAyPd;7r36x z>%#7~l^!7%8(v!5roed1bjQjAF8Iw$N-LWw^P(yThCm<=o@n^n6kX86Vw^`vcCpD; z=Gy|2(k4F&6ZbBni`{XgP>T#AG4VxZC#eF~t_0`97xyqAZ_2)i}Jz;^ri9xDdOJpW6)jAP0qDJ_hgA7JuLkAW!Q++7e89 z@kEsy<*%(3z2X6%KNm?NPPLv;j*MtCc%UDNTe(?usz`m}F8g-Yx&RMQbp_TjovR+A ziKP9Yw2!zyfrhKM(V;=vvp`*L$DyPfe%AudmS%F(XCI{nsvaAg;RByc8m7qz73^c{*g5bZ^PV?-wQ0l9M7F?PTg}Kll1R- z_|e4<^6yUI7fTYWki6C|P? z_Rdgv4})~ae+;rulwZKeq^?fR8k4M>h>1BAIW)4ciR7N#q`|TA>0>Tu?}AK7hPcvb z2LFuhI`AQJ2@s^I)Alm!wDtv;-XmSx^*j7nn7h{;+1~vd`JkLgpygfKAcJVXA~ZOG z;W24!wBQ|TTNkr>SN*AU$D7<9iJrCfk^(KvOpr5o+sY@#T6OnndoMb0Aa6R5kERde zTsg3pW~#beKy_VSqq~8}(tbCNKvF5ve{vPms`!Dj>*O;lyk>FqL`N$S$~@11c`mcG#60v1K6t%*uD;zY) ziYPM8n7=}5)82mdzh+LpwQBU#UXczP`WO8P6in>Ooj6F=<|~{B-y=bC2xSgY-6??qCvW?kRIWycc~ z1nG&hyAZ7Z6n9f;~>PC=B}$qbDU-k|~Ja405p;bkn|NRMGXEt3zo>&Os~nI*0< zvZ4cV2Z5ipRU%a4ytY2U5?&&$ob#p6B=G&b&uQl*;dY&YyP?e%$M zL`p9WL=0wK4Nk$!U1l(rtsS&NxO9S2VFC?;qWv@iMFGBh;b(wkqa%v&t);D4RrZ|0 zk|#hIw3Oy&fBA4D2d5}#3Tn%L|LfD<&Up1rV@>6mCfkira$C}`VN*Kd@N(^Xa#uFG zeZeNM_a~b-orhcRuf9M@+Acu360;6&Fl>rW8jTO1DHtslKA;AGVt=MRd4$P2X`P0d z4T^jjba(mO@-a2GQ-C$stNl&w>Akq78rfOV)Ci(DNX)z_-d)CAVsdPNIK#K7jY;Q{ z$Q4o@ncPZu!ek^xr2zXIx3C{PGo7V#&GwHLB=%WRMwgs*p~b8NF?}r#$rjc%c!udF zgVkcyg;NP@0|rj5oRv}aJv#&??)@+r0#EUIAJkK5LSvx$l%TMhi7ab<|^(^q)bN)6V2{jnS5_@##=+N&UyXfq_5yzO^LGt4?ujMI)L-UhMj}$6TFb& z@(Y{wRaLUR!{*3mlMrbo&pA=ge@2b>BcFT?SUWa1vWUw9TPv5VNvBT-zq@Rn*qDdf zie4A#<&f*4vKmg1LK1@h>A12+zD~I$3|be;jg#tGZEV$>KL>f95;L`+axa`0sR!*p zKYVZNrYcST;nzDdpy*bBjV4a|ZttVt#ZE?lx4AC3bow9zw_6WMl1gHF@wD-7oK$p7 z{%j8`VW4YUy-EO|G-=SIn7S85)wKV1)XPfOmfKF{6uD<$X__EUz~zd86-C60s<_(V zA`o7=4Qu9uc$LDmGVh7Tt9{cJrUHjwVbHyh`@W#_?aK=?iO;=!EOCl-UZjc&4j2nv zISCaFKjuTFElZHcZu@PAQ)&jYqD43TQ|V}LzC>_93w9Yg2I(n{_pLcd!UXPlBXKh) z@I)gqp_*WMDy6*u{+l_HpZ1Fk?Z2oUp9344 z-l63ljwLDQ?@eo~KUTKdy5O3&W8xY-#>QTmdnkA93*?VbeWq{1s1Es)*pb?X5}Htk zJ-=X`pDNsKCAtKXb8YrZa){$G~> zA{lV)oK8#m@5JOQHthY?eISeXI?Wk<;seG3lo{2iGD`5D*&ENPCh=K;V_T;yE0O5I zGVIG>Y?@T^R-ms{)SK~W@@KMi$Fdpl`S0j*;2%-B6#Va%FuNBo&d`-WISp0ah)A1= z;SfwL(h57}XN)wN_ca?$9RJutz8;s6i6zZn<(7{$zKeeledU~+rFt!}rh1u5vJeAL zjlSyHk7I4eLK_UmX> zNLEc~%h;)6DWn_KKMU<_RuU;1@ch%oq_<#QrQm{bvABQpxrt$?61_HqhSE!!Zm`1< zo`x>?cJ=X&EZ0X$zV@ejBK77RBh}Y-LFs!Y(Ef5|17E!D{+7a1 zx~T5SkItP`bHrRU&2D!~;Zpg2j z>A8Es)>}s7z+bLVp}RlTB)6;XxKj#1N*7k60&Nrrzk*_0Z+RI z7{%OwSmhC-`_jDY;0?oMluUvNWN$1 z+A#TJW<^547^OU`uA`J4!8u?CWBpXN-6nrxt=`#2la2bhsAO0R7MMAIw-(U`ER8gA z@?(@zf*iJxhX{&|wci^s#$hx{b{d|Cx%S`P$luA+kJLEG9da#U5~c?p>|~5ia??3w zd^At8o0=ok58b#abizh5NwqgJKyqVEd^r_kCd{Z!Hd~+|6>GJXT)C67(hvBTl9KYM zGxeQqUo-dV-)N9)ZXJ)S{BcppxX)w=6^7A?6NHtRjh={>lT|tqBAb|~1TuG^al`%C zZ0`rerckwbP+DZNqU?+iqQd}g_4{+FE9y&IS+Vwt0V(Cn3SkZK>rbO7@Lmq-v+JE5 z+aZ+f!iAq?IY8R$E5oT=me`l?%)Kn5kOV3QaOCY4&(J|}!A>*R-&dx^Cg3t)(0g>H z1ZOuU!W7Sa@yEFd&bKx$8LGwR*b}$JRXLf(NBBULt7+t)t~~jxKRVoX>gMKIl3#vA z>vGWpb%-qC@)x3)t2rKwy|5+d``B{v0Jd8x;b|=i6q}$DVDf8__qMJ0s8%O)uy^<8 zTB`z*(Zac_9U}BXLRB-^TD0|Up#TE)B(o7^Pd*)Z8>}0u<85`H*HHssLRsRo zv705#c=jnNBaw4vFY)Ayw6U4$l}tYB^oPb|vZ1Xfy}~C|4@`(p`OSo%gFrh@@_aC3 z8;cZ1Y^M06lc07kGGfN})5~H(W4FBcZw%lSzhZ6Z>t;t82YT9zXF{oQU;X0|5BBBJ z>dyTs3sM1%#J#~MWn&yN%RCOwy^YI#+exmG>ELJ5=6^-MWAfA5Re=-WJ#i_u5%s_p z3GKp0uHs2Q{$nYzKjNWsbo%kCJatD1PpZh#-4l~@$(Z_|10N!6@i+P_Gpsg117OEs z`Frt&_wv?YRn2prMc%2}2&;1yl-^C=v0wJzTm%2Py(D<@Hu&qE$X2H_K>9*N&8Bnom(aFgVAGEqU`g z2}T-#Vs^snf3kI#64f1Pii%-niil(ghSO^oyw&YtRi6B(d+*Y?3p^eOXbi?4+?)o@FMbB5vP@2iNuaQ3`-tckJ}jW;sXy6mu3ln_7r zc~d|e3Dzo;Cn#n2U)ze1h8fqqiOU}w8>53)3e64L17s|mhVYS9J&8z~wql{Y(=i6` ziRW`(npVbiGk**8f@*X;dcrLLnU4{2=7?w{NEp5qVAuq059xGK& zd){=cmRr$o?Mm9xl>&#BU+30JVzr}qzD5VbkNAs0gh@3LGTfRyoE1@6o^`AaH4~Rbi z+&+%onWbkzvyqe52)%extLqabqyH$wl(9fcqc#r9wPt`uTYZSyUBLFLbn{Wi64z+? z{P*lH3D$`S8DU~vP9T32;jZ>T2IRM;i z2C5w4o^Q*oIst%Fl=!`E|3q2)GvetLma^|~L zp<+`>ldlXij*2CX$RmviiN~ZAV_i;6N(KdXj0Yy5-x=1evA1x4>6!MhuKQkU^hr5Q zd=?wFf|6ZUrj}ypT)@vMi$!kJ_qjT~qbVeXcfVlKHr26}E9xHQ2(Sn-gnY5G{<3|W zK6`$DZlYDAs>%1N6g7(1PZMku?dr_Y44;vGXP=yhN=9S7#Aa=mk9k|c{Yo630H0e~ zk>XgMp9z1m|Dn3+&fJa$_nItMaUU(#H0RB{=B&uvw1+^CEb9T<%h2Rl7TeQ{ zcL5-0dLY;6tDGdLcQs3(B^kKB_0A&o>WEXn5Rz>$q`bC4vnX-5(nsO0NsoZGLp0t%ztLwU6rs+b zYg;UqykT*)&JnH!sBg{2}-A zrx{F=iGZUDXAc% zogywjxu25VWzUEi1g*c$AaeX0lZAiC4xt-4_dIA2E$rpza4(AeWxT4ez6dvTyOGn`24j|spDeC2TakR06bT@G}lOM|V!Qqj^P zp*6JsR8igYjLM2PTapj|@$)YaApa-`@b1~nK}17Se;IZ2LoKEznU0Z*zV}zmk}F(Y$X$S9fMYunVF5%o$_$(SGqbf(%H^2g2I`#j%N)p{Mg8N)#S9X{Q-1 zU#b+fy=Yulo3qmu@Puzfjwf|{eYn+BGKeb!TTlxK>H-BHX`22t9YIAj#>KOib!3XN zziO3&J2AoOu|#Enb3jz^k@|VH*Z~^+m|s#)+rMZ&9E0~9co)|An87-kPmA3ckf#Tf z6e?$5=`{>F3dcmDupTo3#%Ps%%Y(^LqjylmFM9tCa_`56!M*pIrx?!lvRgG`2cy9A#&wFa{De-P^J^x8gJl{g!D@&G2^>1xoYJ7)gsa`fMQJT?&ewC?zl~= z`=UVv$)k)WQ0(qvNUMn!>dPOHA_YK9G+*kY&AamVJ2G>d7ZdLTtg3lW*2t}_s=kL# zzBw=((ky;Uo-}JQ3ZW$0MvN051%3{eS~!M&a(dRe_D9Kc^;(Bk0EP+u3+hE5pGJqf z=>cZ@70oVTOMz?Z3fm)3+0x`G6J-yC+Nkl#5+w37j zxL7aOSLp-IEiH++f$eO|gJd&Kzv-GE?~j}(hlo32ru215x#$K+i(cu(1GdF$3)4sS zq(RO`B;UDiFOJ|BIlw%52Fr|yl$00aGg-Yzr`N%){ajFPf*dI45pW!nzIU$U@*%xO zi7Eqkeoyk3d^qhnch@6Jkt1M^vg8)W>)C)Huiy?}&z zxb+|8?3e;vh6v#&OE2^JR84mM(~3OA^eN(B$y)rmdHhuv_)QrTG_BX|h1-|nYzjYk zYgF@6vB-ZY>AmhI1HkgG79pazx%QFG&;OyAzu4v7!P-UARhBgwFbko? zPrh9ne~+XK1>HcC-XVJ|YdWBnCH%zCk2hru@F`VvWEpQJ@u+ffQong5i*+rG?uWmQ z!F*qVVnH)S^`V@(@nn~7x@JuK%mmxwF^mrv^F6W#e42v9b;WCv28F6oHuU`=xd@rU%AE(>GK%4@zys|{$Zx|d7r>Pv3h!~#=t&N#@T zX%?lvpFue2-6y*g>9xr&MY|kz9m0Lbr%iPrw-T&Xgo>8UzuZ`LMAd)PbXkSl<{3~i z^l+m))4@9X7)GSg-4SQC>vrZau6-4_+3%^d6y7OUKpf;tLr-FBIG^^4lCk}9!50*4 zu@BZspy(v8_)ly}0>_VRv$v)8OJj821f^?e63HPZc|@xvMAxHNG$~lCpuW$$c1~Zy z+L5~`$}>$>gAZ%8*CoiCa(S1n1&&TVwB8+=v%?W~ifrOGpx$@ozoGf5*4*Du zRl0zxp+a$4*%yn98X+?k&I@FOvFD+=F_NnuabWQqX8ASx*^)x-N{-*+QFypkNS?u! z8}IxIxS?DaD`fR$sSK4MCpPJUv__E`5F!T%R|`Ba zzu&OUG65Hq->PG*+JU@3(j&7SoS>oFyaXE4ScKJxxgah75`GXJ=*UYir5?hWAKpQ8 z|6-n~Jh_%4n@{2L(e#ZzChd^`3ocV8kkyol z#{e(LBt&G-gDe4;@>@wEDuE0y%65Ad!n~;=ZkB&cvs%v>sdWPpUY`a!EfR7ctRZv= zx?>jM=oqiZxZXG)J0J5e<#>YoBV&Z!fY9r0rL$4Q7c7+Cz_PiCN2l&x_6TKcP%pM| z*-;h5>+A8B|3CB49=QZ$oET2{o63elJFD1h2?6yb1`XrMa->>+%p1(*s?0rEZ5j(5 ztPpQE7%dYnp?2?EVu88mE6sxO8Xz+ulKBq{Pge+K%W?KQWbEpt-B8|RQn}AO3t^$k z|Minh^IkylezmFQeKj#B8!GDgk+Cc-rBvLXf{t zTX#ut3>7pHNOgoZ>2T0p^iVr~|H=$8zm0~nSURD!=0)s*uTdxL9Z}|I08Er#@^sur zX*o};7HNtLK^boDHX?f1Gm^K6rAk)yA!_4p-DRX#S{{r^BhgHM{I&SsjwtV!g=SdL zhG8c;HrYDR^^a#CH2^&n9aQ}ahGl|x%GM8>gCNf5v;OxtYEsR6asLKmxe>-R1u7u6 z!#~kK<&F+o{<+xg{)5HUb2dQhaG+fu!G9!UgWE8QsGL9L0Djt%fSIm{p~8B)U}raf zF2x->d0#J?FAx)MMg5#SDqjHHVP%-P>T8gIG}$7;)VYMbacD%A(DOY=ECtv{KKVb* zO^xf)GrKYe=JS3k&=rH)loKccy_4p?B<|#0>!FdjY@*aduWeHwo@2qt#$;8s>dzyg zNxte^@-GS~Q;beJULZ!QS6{_#LT2!Bpefd-8B){L`W$UHyHn_QYQkkV40w9$$nkP{ z598N{?3BV!?-o~jQ%a*sFY|B%%?PZdkP)L<+6(-2a>&LZ=c_x$IQxUua zDL){4zsWN!#YVjH#?fO^Mj4<^MrX8yNlvcNTs5V+5{8Qdx^sS`e9+rt>HPzX8))?e z1uNp@33C)H#N1t4H*;y%q`Ymj7FS!+Ou0uxxnk-ZoSzts^JdtN4Z(bJIgTnljAopP zxob^n(5w{9sV_&Pa=hJw(Sp3XL%!HV>c$Wwn%)looSnqr|MqI>Sid9!0{)Tb1mQlU zgnMui);28wPsWSGLs_0vsFkibf#P0?k%GB5Z{i*no4SsXzZq{JnlBLqQ$NKgZVZ#U z%MbmBSw$v_kfMp+{_1q$$d^w}PJB&+J*eX#rwcf)k+wI`7(^RSmyP!c;=%}Jz7p~c za|2&9gv;s#`k?+25ApYFX|FG+8)vR4d zfVqqyu~E$dh!BRM?R47HW!nKam1ls({AUJMP)c*n_f5!(lcW*iXCrbYSKy*$C#97^`-Y9rl^|?RHn-*)zKKc^KzGF}o9xnH;u5Ho*S4Ov zxmyZ81j^lpOr7HT>Ro%P__u(Sb<_+Lsm{`Dp-3HE0r>G*0!ay0xe z=P&B-@9$l(%Ua34bNuk(pyNqiW73q$=QbPaCCqf`Y3S@0+6LeC-_yYiDqI(6V z?1WY4$Hv1UT$|=Vseh5}WzUsBzIV}kw9Iy7 z;gR{T0LywjM8~KL&FAkxRqqa{bymHWiJ{}<Lx>|Ku7O-!!+$W3@4Jjs$=o~ZA_P-S2zWt2rRgUwqq1CDQl zo*Tc!=zl5Ay-v;Sw|11eZ<1LsX5*(^$%rX(i&Y@&`pU}^4GMAj>1s3-3Pte?&b*U& zxf*DK612IvPiPj$FUkMS`2?<+_04u}xyikF zO~yUb{PI`7it#EU9aCjn7`ikE{0!z{@y&ba)XkUK=aNUBd8(ReA6z z-q-_Sk}F~8W?Y%T(UGKwOS*{`^-%wg@v|DLN#<0ARkQHvPcT}xyg@^SFOX3EK+p}* zv+rW4oH)@9&7yl4KB|~?VI@u)uM}Y1o$A`HpLnRK!;8JLVvhOyc&TRS%0+l7fx#~eUq3>c{dz2{4jSG0eIEQ9GN zPJyICt755=wL$H1bakvR8!3gT6WGilb+jLxw|D0kDN4-WJ;oaT;)-&=G4Qt*4_dqk zM;G78<(ih{DN4O*B1F7@Rg4?Pe1XAj{ zWVrX0AxX-%`X0(clk%5yjzXh3c!~{%R>k5b<{p%p)t(0)P+q6cezucfPY^aBW28bbC)&9tkR*iot@fo8wJy|c{*Dh(Z2xv}hQ zYmzn=Yz%XOOWYjylm2jY8;KbCbzAMLrrQ9A4CV8@_`ZtZ*_q5AtKRBt@SwlNhDLCV zUq1xGp}bF{mE`pg0t?z%HOdn8Sg{Nr1nvdD#JcfjW5vYrT`gAex(y>^U*w?92#nV5Kh#niuCLb>4Vi%}eGfV`3vWhpdsF+~w4h;Hddk@Su{ zd2itEm3N+cK<0Gzvxl#H`U^n{#i+mFt7d0PEFmgbpsi-e}= z3i;Ce_K}?xcDR^Ooy!sv=j2YG+BEJEvBZQWMM8v7bL1C|u>mwmfGyV}q9CRZcLn_N z^L*ZB!tA%*ctNYaW_aK1IKIKXp_jKkJ2EP3W8YnE+&^oF-b95X(2ZJcO^mE~Pd@_B z*k+zPgyJ__vz^rBD34k!&CQ10$d~Kr^Pt4r`Y!{H)K*?5Pqj!SBzF78)dJMlZ(r7O`>fNSPNqW( z=sYEzJ-kQ33Sxe6LG;>}J%)@whj8E6@3=bD-xd7vzCVyX1##c$vWbvZs+E-!oaPL? znL~|#%EU@j->1z0LGNsUl!k=&*l_pDhoZE+&Dkdflx+0*zU_;+4PHd+hqGM=UVcPQ zl>7J<#xPF}QZfBsR7I_(ON~VQ-mVN>pO0C-m*lidtXX;e#B9qWsFcz|L5B6cDz5wM z>dIUc|4zz#$?w^N8Gi;ZyYD_PU-sK@@7yzI&Ybf< zzsaW?PH`K|y6PO*2_l9=@J8}LYZhlO+P>#R;7jgxnTAp^I>KVErL%1Nh5tDW#6#Yr zvZ0PPHMTiT=kXX~P_+LERGl48(V2D#`kaK`((j zcZ)~g-%1?jxEQZte>vm%{svgh)h&GfJJRG1<__9i0KS}H9`QB=GP0t_Q*Z(Xe(~;5 z!Q8<=YFl2=5&RoX=e-pcxgt<8=qju)FIg8;-gm9hp2|eTH$U84HjS%r`ykiOob=E7 z4zXF@Rbou$sCxFrD^Nj|9hIg6S^Dsane;!2B%IMD%7`F(;@LUnhORwvP1vAs#ou9Y z1USRr5p8$v30Q1;R>#PEDw_s3M<6A1N9Qvsl<&>w#p^e(7RF!xI_9)XVKY_}fj zi=(i)B59`s7*TnUwp3v)qNe_V4MT4syP&v7C~nQqE~Q;yv76{?4^maWz>^9Wvnz-x zpt9pGkY*eFVS`2}f}+9V5D)h3D*>wRx1Uj!y*thid7s6rKzyqwiOl&RQo!U*rXjZ< zo0hNFZ%Igk=vj6MD*G*PO7TVQ{#3X&KEB=UaHB|u7-9Wvu_Pp*4srLVl{jdZ>-0_c z%FqiO(_U7V`JSP*E@_jy*4`YkPbl*wF`HN)`%{*T{4!KXZ(2FuMkNK*S>muhhf$+8 zmNchzJ$VqB`-z@ItF-;FJw+t263SE1L7DOXN$;kmJG@B7+9qm}vu0PzRq5Iddpgba zT)ai%s*FIeyliv)&sdK{i4vFN@3Wz4=rqOv$NG?{;3LtH8OSo+mcd1P-h)nKr%u+L zbT@l5y!+L;rrgHgpLp<@wp%3|)=H}&Er$AFZH#fjwr_8MQ_igZ*%Tq$!t{H@uT#EM zR1f|c1I9VTMguSzi=xh(B$rCcRp@X%I-_M_?!6xOgE1RLlZqb} z5BIz6U(|&-e9;q zNO1~?0_&oG{0#ewmI!$%%3lCb=qT>w&Rf%>hq7VK-@vfBe5(e2E}0}o)rg;M#mrD%6M+%?`8{-X{`Hhun zHMO#K%(_h*f{2SC6&}VfA728 zmC~tqPe_c1SJ?#^xv5VP2UwB*f4$_*xppHqSI_!-!9Sk{OY^NMBKIfFBMX^@z@26BDR40Y{LM{T8RfH~c6aXEB zj}6M{D{`bgl^SP4lTE_?I_{dedw61pOV&pzbQ-L3 zdDQ?s2{e|TKrWMK*H>CDN9I1t>OZf*n3W#b1Q?>3*J~GIkk{s_1Q^=QQLZ5CFxd&o zA&?Uw z^)vC>xWm8ZB?6en%h)#I258@{)NHKLVXOEV=QMnd5schen%`YiP{NG`vRuhG`C&I! zv?zb}oaT}WlhNRak;E+pYvwfAY{YfQXJLACuXTFSv3)>g7|y*~;j>GBNnHl~Paefte`oBD%(vC_?d!}@p#=6c z(?i6M0up6~+8>-8E z^&*a2w&kR$okrf@=2g~oYLXSkARbN7s%Cx|RY@DlhUIFTX=cV(q)W_K=eQSXhf>c` zA+21##oof7q1s#xC9p?K%uVcu(sL{A^2Kg+@AuNMXdlwt-<)O ztx;V0Z*7iw-QR2FIPnW&XQQv7t2s#lsy>XZ2&oe^E;rRy;N1Kar>|s7>02nzu8TPRC)JN zWA*ijSxXVROlbsE_0prxLZD2u>YP;U7S#)yx01s^<7J5|-TI=dOC}jceaO2T(^P2G z?U-PMt1YY*EM^u_G?+}R_{=-!{D2;d z9o;+C3ec6jvcUyP*Jf8|aqX8A3;oDM_99w5L@GIICqEo7{d4MWOjmM9XF$_cR5OKx zc!rNKf6H+fbnM!=AAY7$@=>yccn{keBbl9*vH$dy8JTnsRlxWQW87hbS}|+u_wIRO zJ*{-pAN86!7$e$?n}SF*w2KH`^ebJ~P1G7;WrY-4_NmOouzp$QJHd&75$%$OK-8+`u%~o67?aar-?;@P=-!( zfY|n7hB+1q+#d0fGWVi0OZ6^xgD7zWySGgxleGUI!D4yder+e|QxH|2*rAtkFA?`E zP;X6qVk~l+5OY&N_AUckeTnUt&z)u9Pc{q?V5e8a0BwLq`=^%7)Vq)$CM2(|MEivXKBH}s zL~U(L*O7+SzJHV`!d{p6bkFIrYpr)Mpa&tQ)VW!cK8Mq?ZNCKAQT~`-zf`vM(Co{8diEm z>czJ1$`umMRGO@c%VAbKYK$Bwc?Gzt2~HS22Ol|)dqSy(xN|5tbB zi=cb<2Ge&_WHkFOXRhB%pPP>UHNaQ>7_xy~;B4%)bjsgm5w}LZ7wDZ~)swyt3*+Ei$)*yxSHHZ;}c{fw}$>A~_MJc4teBKVzNXJXc{>pNtYYDR8T^2=435=S}< z&L8GhKh?=EEq*O$;6->%@SnIVNS_3$vYdG9OS3s$JJ7PCxG5=vvcZ6lFy#ElTtQfb zi2~SE=^9&x((5CTN7cXos|h$c4QST6{>o!zT5mC>o^Lwu<{_XE?cer)Oq3`_igzHd zNAhkY)t`eyk9qAH5Ngjb)76qS-jD4?*}l4@um?=|B-m5>Bv@T~Vv$Yy{K}CIir+^h zJFER>h7*l>CC6kD>|ZK#x>S}HVkQx7cuf)9C&GkQ`3%GMF21JRn=>F3$Mu~Uvb7bZ zLnMD%K0S=&vb#hrhqvZ>WZ?fclyYdU91^}_dT6<)V-Z{V{#eoYxRUf)TEMzajH2;= z>!+OWbn6B`UOw#6>#IdK)!$C6ds|d(VKW{YGdaCMw^5vOnpcvJO5xX(E{Kov#qyNbCnV~AT6y|A!$f0G=B(|?m+dUtQW6l!GnBI?#+?DVO{ zMUOkcN|n?VEX!9ygXiz#8%EE13nUhBc91r^^o=Fx?WVt6BLQ{>1Z}V;Ks=Z z4nYYjPIs6#$sI4S&F*sgWj`17w%w(H@*O%&i}Z}oq50KMVL5nE`&c@rKLVz*z)XxP z(%&6kx_q@{m>Vd;z)S z6fpl=^l|q8S@EjY@8aVd&mJrciW%l*02Eo13k-PSDh2M6O%|D}mKT@h%0%xzGlf0d>?gL_qvX}}%U_9gYd9niZZIVpPC!bO zMpI(|R@5ASB;)7DJg>Y_lHu$9e{vN7W!27Gno^Fk(rE)8Tv53d4@@Emx^*%(1cU#H>}N;=LM%aRSAhz;6e#Sk!^luDA8MYmf&ci6`b^sY{Xn=e{-Z(yv?F9u z$RPlr0)?+a{mD|Gp%^j%K$(2Oe?I^NoI`*<`lbtJYulWWZ>8LS<9JK{DL6Mj)6tAb zmcI?^#P+`n(ng)U^qE^)_GC9*kM=*m1@+9HJw8ke_~Td{cD0)m zlN8TjI!%ihBY-MXGghoIUu^x01Ue)M`F z4NWIH9W!a2xMQW`FXWNLc{+Hc-T-t!1(6kg5YPEfG|6ES+w`lz#>}!ag1i93^ZGBG zt$E;TD~4z+6m+E~OHp4kV=uq^&X*_Osfocxh9O3l3pIs4Qd{z5?_7Mg_b0W_|2g7O z@-g}QV`A1;vzRVx_8syU1=D{MpK4pj5{88Kx>hjRYvPkIOltm#^3KzowDcJE!zY@3 zt`U|N+6xb=^aKF7sd=syo!cT#)TQ%`I`S9~0~7c1?60qa^8&h_F$nwqC_JbCd8>26 z{!3}XP(d%AJ^O}9RAKkyrT{%~h^>uY4V@g)W`kKvG0eLX_Sf?J#yk~D8D@aDcD&K| zM@eEZT~NuF(PUHWK`3J{jN$Q-%lPkPOxTqD)vk}DiBK7feS*pMaVwoT_7vM*V!Nt} zc1#%C+0R!1Sw#|RCg)uup6dWBc1LJRLTA&)nN>}Mw9dQiBo^B}x*UAs!RK(X?N@7E z-yAR*^x_$p4+5mlx>&9|GmT;hP){YqT2mYT#6Xz@-fC2Enops;`>2Zs(y>as>f>yo zYKrv788}y{^SRMIKev&|<9HAU2kViEW7oYD1dF*RtcEE|VHiF?*9g@@i&Y0iv27Z6 zz5k^!g|Be*X;x`r>v;a`&z|BS&pX%P$z62gKAD2&&cG}W-N8dEpGZSNZ@A2~x0NW! zT8tTM0OelIwP`+6g*&&c4>pUE~_D z4*^l~ZjXNgr4$iHG{(;tL%!*)Yo0!?y%p}v!fL|y#RNp*&M#|G+MF#J@P2i*+4@(@ zn|Nj`@NRyaDyjaIj%9nt=SuPJarafS$`ib#wyt$kv1k>ngsFwLn`{HbWF(AYr0Sar z754QPzo#;v3WbU5W;g2g`qRE+u)LaN|OXE=@auW7NkPjN$^iP!T zGQo`EN~fmhc(Nn_#%#{m)nB)$8tXi)0`$=d>e_uFkz9YN5o)=ibI_B*i8jbmcMX$x z=a!MrBvdyn3yhLreytc~C(UO5#PE5XZ+{`ZEoFXGZtX|IdoloKvwEE-2vs{rW@AUy zm?%sJOC~=iJq?R{fYQugyGd(f05Suy_}aI`*;~{3p1}Lm@WG*`Jxkmm7PqwDQ+m&# z1&H{Mh)1J^@%cSNPjaZe6-!!TM%k-=u>yxc zBRnqjXkdau-M;N5Ww-Jq(HZMrm)~~qKiQ(eI_8c39BkS4`mCGz z{xy2;1R_rmC@F#N2vib%X62VjC_51>%C5$}2MhrDs1@~#@{27H(4fSwr{M8^FfI=| z0s9~bmlEkfGN2gVDb*ozD2c5$pNNbArL^wBIPVUm9kYrq9m~$;!fP=hs{Mn9bYz`p zZTb7_3{0j?MAMZZ_R*>tdC*6jlhNe_ND@;Xc81Fw!n1+YtPv+$&fdJ<=B>)f;>-E>H z54wpWA;q)$!&eo?5M}tV?rQtmk^%3{i@Jry0a5P|8Xb*YDVprFflP*O;8Lc?nVuWP zc+nbSDAfBM8(eBVi0rKSi2KDZWY)?4dP56Ppkupx3LSvnGDywea`<2R1_yTuYEpSS(PPb_$hEjOa$+B)n?mA zcf`G*pX-y1Ei|B#5I>3vV+{Zp9AxY}J1hy*>WZvJz^<+o{o|h#!#{iG)40CPV=yu| zL={L;{$^zGS6|o;-8S7VKAxpnUq4iC;i&eD6O}FGC)y`=rAaY_<`Oz0<1&FSLJRCIfq|4~2&@iL%sf%qYRIxhMXVQEAYn@-5I>?<@W~|G|-e^8QEfE07RM zDGHv+xTLtxX5*;H*c-iR&C(Mj9mFSxs2mO19a*YC_NaZ&g*%^83^?IGPm&hig|M1E zoklF^t|LH2$9iKoth?_^0D$ZQZ7wbIe+?(6_zYXGk&;n{NHzRnfd5&`r>NZ$t z|6ob4R{Z3-UJp@~HI)0G`&$3fFJc;7#NBvucnfZFy!sit|56CO%d4&qj6 zzQl^|Hv(*j!OKlV~6CHM-ZJBY=rC?-!1gzN~@w#kd_q= zs;6K6KztFk^o(7KAoc?m%t!>3h34~mHGW2Q3Sw9P~|+-yBIMFOOU)B`9aFXg^Ug?L;PUL7OZ%jEXGesva8Z_Z81$1p{s>6%tO1 z@$aLO5@o-E!@vB=BmJ1YmVv+5n~BN=rW=a>eGERbLx}y@^0n^26HiEkysbdOXqrw} zLB;T70;z-;Qh*w+LarMHNM_z*>J-t9Vz%}e;$U&#$raK)w@{92;F%`h!bmQgjk=jS z?cA6gAYZwV0@W(cn37DZ%A$+#nB3S+mx{ooc>G)E!zaQ%&a-ykl_~pa%~a722l5|6*jw3O}6^7pC&*i?G2dz zg8fDR0)0#VLvS$5vfDS5#Z!bVjkfT->9fjbG`OTPI8u%01oBm3u&x`IrvktlMunb& zfE@)~wnl@q2h8JBu;jE8)e%fT*-Cxosqb}yd!PVndAFes!`xr%h)yll`)Fv3-3w86 zYJ&Hg%|Pq=!QC{V-&OqWK0&HAR>5_6Rq$sm==tX&js&K|V5Sb0;Gejk>T49`&u0}s z>XN9JTb0H?yo;B(dL^>!voaCrUEk9bIJEM18eTk?C&U!Z&dDcnJC*yVIt0MYqB&Vy zK7wW1xUXmm>lt%cj@vs7e0DMZTKN|Sn>^b+TeEd?Nl+|kC=^_ND=$t?pyu_ONkGYF zxL7TW>?l_VK_LF?g1tj?jka|3^JlVKkMIe|AS&;pYd!YI?uvZn5f4@26K`cqgO$al zL2ksPRm%l5f21RWTyfbM0A-229Q~lqFZ(-FWYuk^Fp;$FjC@*Gq+7p?&~{zu+S2$L zAA6#i7{EcuwTFpi5pYTsy zJqd{~Bg63?lzGssXHaVgZ~PLvfiUtE&=O@P3rZ&LuFOJ?G~hOD>MdwHCM>1O_+oPc z3vhDP2Ah_eYx7s(u_ouWJ(IsCQg+DThQnVld!)Z~T&Qs!4_#X91Y9(gkwfUY^TYsO zamOJgz2uL$lzej<&$ht2pHb-x4c0p$wIk3S_NC+lN&sez^)(~pWskFI?L#B~e&eA; z?&?`v2?b&^Tq3J)pnI)XsZspJczZI(CdH=2%&sH{$N3X{^BNuI zwQ8c;Fs}q{Si>TF~fZRN=QGbQUNWCC%X$m5N~=2 zUye%!2eZXsgy;}Echn#c^j@DrOSK1EjSa!Z`;iup%+~{l3B_rkvBHc`XX+%_|0O#0wf6w%~s`b#L9D)i;(Ss=B^7AC5qU+<*Lh5=xXT>Kd4eDNct zc^0-9J4#k?E*J(8lzZowzpiyX^W!GHyIV~|1d97$tmF$k&VuL8sC7~YPwZ}|k z{h#;nkIkk4j7i||0E(iwX`@LuByMKCE>32_N3n~-ION*Vfp&X6@ewFTuhz6p>T;p= z>yY8apVreSN2-T6e>u(H`s%l;ouay`FHr7vF?T@-m#YDnps$%$Q9p#ShfgH1HV{#} z^{sOF?AMH}bu}V9`2KJqiXKd*vj81(y&;-dBO1Dn?0|&~Qu4jf$SGR|kU2pA`xNBc zzN8<|6tCV=XNkQqS?nAUw#}$1h@XNJ#OY3itKHv9O@$(54FNqrBvqM(yerr7SaTM! z3PGYX#jbmd`DLM4Tm*^QC5(G|b#mkYUae?uZYFI)??=gKti$^&`NR9CpAz9=+Olb0 zSbDQcgGVzA2QC3(Qc}0Mk8fj+#*(|;ZesL*vffInpux5UW1 zOui(mjK!nr=erk?8u60?mTGIs1a==8PtA77OxH5}QuYtFqAj~15vrO-*LsFv!HNVO zV=8d$f$_ob$9>KBKTqGbwR^w%l5@9PvNwM2a6iterd%kmFRy%8xyE(YM@z`qGgB;?4{7%e@@b z7e?5&7qPvOu~1KE6^U{TS;UZ!iT#_O8AZ{+G6!dV$oL+x^T|SVPXg+VZp+|HpoY$K zY8ROf^OCVnhgsBQ+`81H)>6Qkw-pz~vfevzS!$b;3cEY6bJ_%p4p;(M0bi+5*`h?k zRqk2>{4j|7Z1AawGljn?E<}t25uO3#yQiOiA4%*5{Usmxsw6Pd^l{U@i};~rXz4OX zPPAMN_y(Jr3iVoYL8fLDW~T#;XvPANZ8uC^Mots8tUasZ_$x#?7|;x~QFnm#AlMYN zx^nhQYU8@PT)!{MR6SkVoP{*DGYI0z?u$^<{5McGN4h>*?n^uL!nKup$SR`7Ov?h= zlk!jlHstZZ!TgnQei~GXC_B2Vg@W&Pg1pOO3Wx;O8-G&2X@|cy^G)DGGN!!b0a!c4 zVgIM^sOJ^@W~YKp_*>w8XcX)`{qRMGgv_JZ<-$#Ys&y3(+ry3+Y4^EMkv*uNDVMM( zp-=(prklSK=yb9`T=i78;k?^rPXSo@aq*w+M`2sXG;_$A6TjEmhBVxmEJ}_D@1$3M zziq6ocxZ_xctiF?AHXp^vwr0DRHJf@&vCOf(?t-z7(83o3CH>grBX&Khs5+-04W0O)xo} z>?xg8+K+SkpX+}8UW5F`UZmDxqZr+<$E_v4H3IP{T+(Q@!4LY}~I_I64(l47!tP&(qw+r#cR4W=IK8i=d4Y-j)paU0I!qP?e_~pTukM(-iRrkg#n*Kk22EN_deX_(`OM0 zWYRFEB4>_usiaG|Wl#Yo)WK8vF!sb{4Wk*tB2^hD&0bj%DhB>hc|Q_VK5<0g*B{EDKis_l6GpX& zp0_la5igRW_N%zh_mYxY_V@WS2HV7#Q4}vrW(nwMD^wZH=-Oo*k#|;}FRjrG(8j)BLz;Y zy^k1|)TMCKep#?F@Scw@QaJCMH%H%=QUO-))lkQK`Fnm6rvIs+f(@OI{ar zsTNTLVWV%fhcDOLlsn-@+0_9@_nIPprSY#!D~hN;8Am^Y@)@D|a~`Y!%}i9YyzUK) zDIUZcBed~RT^A1}KKK>yX4@jyz9ca_+}OsHBN;COudXfX%yl5gAom>SHOzo+Lxjx!yA@@ zz3>X$!3rX*l#WSIz4f@c7zC3^dr5HRLPy%*V$+kdibA2-y=NLZe2}exD+{p~pWdNb z0=^q)FmFhekIGL0e>oU_HJtTYJDn3{I%-;A~*219j-AZfOs~oX0>Mw zA}X>QxfIK}L+SX=pEyp|3EnRtVYAtHU^u&hQb5=f3J_{e-h(>Y&aBuzwQUKzduOV0 z;*e{-RmW;_tuQ;(9$GR=TAz+22#c9N&*M56bGbj}QmB2(=0k*A{>i^)x~q&Rd}wMz zzT&b#d;R4C{P~e1Mdk4H^p?N-dWK#+V``7%Mp6#QiK(hbFk31++2?VuNErx_X{2z!bMB5SL`*$HYq?dmM4flt|v{|qYq4P{bq`pT#%#+gYB@-~(S}KzYFak*^#8959TaScnPDvI-iAH?j8%dpdIVq^+9;POjg+weNj0t zoJ?H(dQSBn>p*rM;?>IO{MR52xB;!=$|sXbns54!V#jB3=yi# zn}C%Py$|+YG=@Qqk@p`;jFGzCqNMYTs54GclS$81oXGa%dal(6<4{y;mnEkfm-wwb zP!rwD%AWxb!=**;O5zl{J*40ooX-a~;u!{n)5*?1L3LVutEg>F$sx*~C~AUjzxYQ1 z-GAtvLjy4S89PU`aGhdq?Lr7+%8(`SO?~G>b$2ucBB2c1A>qZ70!Y$4FWxKG|&ryt1;qFc%7A2 zQ?UxMpx!m189#t_B1H9DX{g6a8?u|f1#!inj8v6fp#U84uO=bBc#FseWv{@XaoQAu zYhP>F0a-3#N3$(`SrN5|+Oo|YACZi zcqLFGd-Tp18{GjZqijm*ujidx*k#*{vR%R?WuCW77`4SB!=1*Vz&^s}YQMc$4 zmLHVX(F2YYH!8t{kY2CsWxyd0TOgBIo3ZsFcaU}6Dfo{DuccDh+CytYshW-lvk*xy zx;FpUxwCH_Q>ERszwmI;XTP>=)%W@=+1Fi2;&Z}QRC9@qT?zp?*9*Z6)1$~4Oo~9? zzFy*f8=Vt)hV}rMw)LMXk{Q=6G~6iKn?RgOM8%YW0bx`viY}6~f0JODfSLqP~*Aw+@hW)&uNWByQZia(c*eR=q!jm~mQCZP>P9jYG2 z4F4Igqs0Tzv}H#|Prr!1Gh*We)Tb0p4iPfi;`I@{F#E6(MP)tfp@$l(se*W)&!sm0 zWnaa20js^k$|wosuNNt?FQL}H{ik4~g(sp1ZuWcElq8Zg0aAO+UOYtJV&MA}%$j6i z!lFf}IXZ4AqvhgEZ*flm^KpvGXlre8oE3B=P??PfFnE)1qSEwF?Y&zDq~hnN14>PV zlj{rz_kv0cYcH-%=i4rIycEpSU^t}g8YoD(Jvox+WyVWD`}Tx-QFhfxMqgSIi{Ezh zta^4-R(Lc&jFSn)7W+Z0C(-%>($WKJH^BUPJ8J< zVdjubW}gN-bLew7&JBOO2c9PNIONK6{*)cv)BaqLCRugE5fyIRfbH0;tW9n1a@v%YEh$j0Sfj?O@2MChu@I zur=#v}C*NUPx|EIk3Y1oG~905o-<7Vg07dVpZ`A~LC;ap4pxQpNZL(<4^OdGqnISm_v&u|-*vgz_|8x_8&!Rry|W0v zK@0ps0YUAYzbH@>d6j608}f0K-$c`5z1?fWoHxlOIAX_&uo(~V^M-<(_sOHovD1k`TEfx&K*N$kM) zM1W*HH2Ho>!%V)5k#r=WpEeJ@(QRDluk<+^h8aZ>qq^p4ddGn6i4>+a9OZVIEv&E6w!3!nt?D#x^eBEJ#ID}15{H5 zye!86S-4g~yKm5DONQ^kS2x3`wsG&TlFt~UsCui5doZs{eZH!F5oH3HF2fySd45)rpIG?HiUtFrL6Y=?d1$qa=TEe&1$({Q~hC)at6N||x znwGPy`0V{nj<4?c00+}=A)~H`fAVhtf`PF4d2rb&IM{^;sYh224wyT}U8GJEAb+F< zRA6Ar_Ov@wjK@G8*E#mdGL_#iJXZu#!}`U{n9c_l%8!&#!M)BDCXPDg!l2~h1328C zEKBo0e8scm;4AYvxK_%7j#ab>vQ>ed+v+>16yQPw?ODq2zdt30&g3TH-FJQr-gsZ1 z&j5#-#D8n6NA#ZNue@19F(09}bp!_0kIg*};WfRxSdmfEG>?0TWUHU^U~Pk-LCY!; zQxddHK+<26atE=UNRA7T*D?=8T*OeBzPHWQgn0So-CumXJ?7|b&hzVOB%03NP3bO3 z6%jh&N#*q9VRa#KG?I~x{A7@Y@OG5NDKPfUkvC?JwbXM&dA&cta&>Yrh z=K+$E-4i$S*5&!v_+~Z`b$?37e5+k&I`H9%;;oM297qst{vl&OQQh88$qYGXyMm$L z_Ajq2uzXQ1V{q{8bSd|b?aa@`9aMB?oFrduL+a(ik!|Vdrn3zw_6hQ7_I@G<`zCsy zc)uS+FS)*qEV3s-F=qZ-5H`qruL<(&AC(gi3=VOOI1=L?;ef$dz6!Sm3!iAwof6&h zHp|gONt<+th7%*Kb0|eMOVL?GbWsX|`z{^xcKw3(X7z3bV$b<@?Fc#a)NvfmjN&)f%nuj%A%Q8AdfRlC;K>Y>z=-*t$hkB>@pdAn?Ue;Pv?Fe>vBKn5 ztWEs-^$6;Iji2b+yst4f*iuIF%7h)J1@Yn^8(~SLT*^3KF6>@ad9s$oeJjH(IzCm_&cD~B6^eW+0X^joW|#wlhb87bP<&+rU%Q?zPL&!7Mx=4zG$Id;P-GOm@> zR?-hkg*X}6TNftF3+95e)F7+I=D~AphzOmq@ErpVRP$>}qaiM-qTTo5HvO0YA9eW+ zp9B{B))N!k@dA14XNi)jMa<(~=$)AS#Wl$8zt^`ar~kYcKqe2^y-`6Kc$KRb-Mi~k z-z!8e1k^K*I#4H3Szi;yfUmXhKOD;d4_Mo&^3B^EiSOgZFB*EWDDMa(Ast{phI^+@ zjwO8SFXo>JI+15#j||TeG~i@HL-m5-_k4ad*%c*gNaaNy*B?05rm>yavO(baGSO!r zw?~;i$^%jB>U3yrpt#&9A3aI2db^l;8(2m}CLkMg>9n8=t36%VV;{q}jtH7Uufp)m z{2=S_oiQ8-Tz(^f2Qj(IHxn;8%wbaqC@V?Ccs+f}UI~0%s@skS=};xZyPM|Pc7BD8 zLhP@+%(j+>2v#Tv1^|QoiTqq!v}~fSufzgQ7x*o)V>qcxW3P3Yj67{=?t=_Ecfk&0 zQS}{@UGcv*qEB!{P0}=de8epL4@y)kNB6GMxF}p)AT1K^$TR(*zX||Z>?mGQk~+@T z;dGo+bE{!YPE-SWrXyG#7UqTw+m!4!M*IVA)G*UL_1=J}P0$?D>y&J6qr4P2(XHHoiP0U`?$8N3tetk_=`AkMty0s1jo0jK*K3>D{ z15qKnqo|?LNU?||%SFjQLNUAVq&%v=g&pe75^|@mnzpu*it*gU7$TC%t+6qg*o>Lx zkdX_tOz0ovw`e2>4kRR=zXpu@N~vt%iEY;^`xB}fBvUncd-zSvIiSw4Iho<5rU*`d zI3eaNtyD%RQamXQBIBM!Os%qA!y0=Zg6g63K76?EelTw*2jrYDz<7azWht>foeR&6 zFMr@6^j)iwn{{gQ(&;*5|FPkWc>uIVI&s6F)8=T64$^dEDH<7icLhHoOKa&>vhH1f zAOZ#2jbBiU$K+u%QVYnQr6nXoODomAfaO~lwXKvd`L`N+N@GrWRvLsr3hhc)5a%8+eVC*FJyiCczE`iX7t){UzH)36Q!qbw zi)q&-JOmrB8Nqg&&$x37!}^EVXMCfLK77?h;av6Bj>o!?)E!_xU4sJmuuzzB;oE(* zUWKogZFX`@OB@z=9a#s*6SL_6oV|KQD$8LNLpSZZ{=h2tBr~qg4r19KhLUyHuhBc< zI}1(L-N@lGLlsuivAc6l`H{nVLx6hMx{Isp57ey#R~mlrVtDn5a9y67;m~`1k7cH& z$s;C#Gzcj%NlYG&LsDGm>)ziU_iv8VVPB~Veo>{ERT}|_RRKmK?nj+;&A+D3$_rO6 z%Z*Q4cPRBn4v#Ste=PrJV82dnYS((VogQ=crH$AA=K^ zl2DDUeI3G%CtLL8{|ZiO83CV&E1fE|H{cUJoucR9x@Gwc-nmrDpia^z zDOf1h$51W{rv=OZ)z)4kNv@91C!n2xoH5sV{AgmMB%oxt7~ntjt&=^@cDoZYJJ1J z4pP{0YaTj^%ZU;fKjCR~-jZ#%b1iS|B#{w^Omc-)vC4tcu2FnANvne6H?>6MG29ad zg27_!4~_$;mkX6vb|!a^-OW;GzWZc3_SOV^H08AslZv5>Jb*$|z?f~8;@m76HDL;Q z=9;TycJPyU>Yh&L8jK5{&03esgdVcdL7P6MJ5@pyfiR86aW6sAFlN&kU#ps&)q3CM z$XhC*zeFfSM_?hH#ZG0&nx^)x+ThW&>R!i!%EF>3#_BI!mOm&^4 zpTF<1ft&fP3QaqE&2z*TN??)QvYFl86X;7het53{1i};LMWIA{{M&U-r zRbaoay}W?DmvQmcqg73th=ssSq1gwkA-xTlZpM)|NBZY9ica#n?|6XDy~e37m8?gN zJRO}h>#K>BSL(O`oRAnLg9ndULU6_JDLfqHg>^>B`sX3K(bvsm^TqNgD6rv}DtpSz za04kICk;Q|B!BFM{S{2JAYFiVthil?;@oi$c1&qdaGe@hz-Hc)Z*ur_Rm`@pv@0Yo z-9!ZN*BoGOHN=y-#olgRDE>WlSKk2^^w=G@A8%LwsDt8F`sNY(Te^?1sfm4BX+yC@gAi0&uTdIQ z9p-2;vUn2Y7kWM~s^h)nsZLDRYYeCP1by{dXHU@_V5G3b2eAH_eoG}u?=gqHfQz0X} z!<^9x_qSfsvq-3XX>DFQm1D0*i+l~xjrDB9*7CY%nPpi`&U6|T8%rN zo)Y2Oq7zI&iiiOQd`}DPK5iJx+x{)ASg&j`fWkqa8zcmwNS(IeS$;zt+HK&oFaxy3 zOIImZdX)b#m&srp8q)>Olz4r=0$Z_^mpqDtqZO^FkfPtEs<>ctfU!wlAM=1Dy$*C3t)7I39?jKi*HX&m7M?9dg=1zTmZ`^FlJ!=@88GKoeZm*`Q7D)8=a!$XVM>6Sn*MJTF|GG z$#A2^edX469mQg3=b7-m4pg1U)sV}3f|O&74Azp8p5y!ptU8}?gmhBsC5}*i7J-bQ zE4wJpx-nh?xI+|V5!u4FR^L>DwWLLFx%gFYudlDAh9jMebQ&Bt$O>)V*7-iV(D$;S z_}vgGRMncCnvJo2OE~*t@m^mNd9uAHnmiVY@?|QxxNgGFRb_=9983^VoB&!ZUq8N` zJPUERn%DU*mw@bqkbEklkMS@yHG2sOiPI!9 zov%oIiV{F(379YvFb{*l*6p>7W<=7THHIXPV^>3cK^pfHCXS$x-m%27vk^P%e+0w& zra!pIg`zPR;6l>9%kXU=uX&p`7eE=ET5arrAbeIcbdOfy6=j}M zIh|cn##spl572K&2RD=s!?o8KcXY{)n}|_yNhw1+@n+n;m)SB(@aDap2t$X3=WbO=Q`du;&vv;?#!I4G>W~+|60M(QA7^-xTVis!ivxkTlM4tZQq-- zrU)>{RuLw4T#6^mV4upqqDQ`*V?YubqWKRe-MJTS5Y)8;wJ}x+%5Zl=O0*yS2C!*WzvgZr=MF?uUEU%Ez2_l4s7yo|(P1Ry){?0WPgopIS9;O}iu} z3s}$Jf9n94sFA3c2uemEdSn)M0*`gROLYLYFcaH=V04UxsO&U>H!^bwrbwo6V2a=> z81L~*Kl-#!_6WHGUL+uCgrix`>OnwV_S4Hr@|)xxYs)uEZa*~|-4H?kMMUo0of(v=;*2R6F`qw-lOM$6x1$}L&s#N%nFs@f zBJs;CGm0q$`cffod_RaEe*j{)@S4!wre*(brU3}T!{GMW&K0!ZDO#npS9qdbY$ftmY&cKPcp#Hv{DgMx@(-*6gSUgaJ? z=-QtWwLi~QdBVkjjfSwt@G#8emC5QA#XEpeOB1}i9Fzs+fuEhabu^bqr+*$r)44U9 zuR}($Yof*Z7|^1p)0R=KJjKd$CpR-N{fO&|xh1Q7`t0veez@_NFlk7Rc?8D&N;UbT zoKbpnDXRCJ>?s!;yL2AsW6f~r^Z{lvoi(H2 zgRow*Naw~xC8Em$40`?meo3j4D)$%mVr^Vt@(g^Ld*_!x4u1@9L$581n7sL4M!tO^ z*rF|p$g)~7uc75O+Btljd{?!^8)yN;aH00a&Y|BCti;(GY7WpBz(mVkj8^@bsy+C& zhL~ZB!c5nlyA}5b0OJO%RhOPywg1{`cglrnbZ|+%E_OIW_HKS*E(L z1f!yTI)S>FRWIP~rsB(?Pw*TFn)(s41L_YN%}vvzO_mg&DN(s1;xL9qz;)fQuk0Fm z3zMpSH8|G*?bYIU)n}z~#BBMnaCg>&BQ0P-x89G->c|Km*TpP#+p9DTc7>j-l!+8N ztv<;m+ls=au`Ve`*PoN<&WblG!M)lXf-yFjz6csLKffX}ELJh2rfD?{7%oXa2pDRW zh#`uZbmY;NIx^2?-{9OeBWGfqCD*>-larHs!kJ^Sc6dg=M$3r+z z?QjguW6&JK(vn~tVjB_wEBrVKP~+Y8qOcdFLsC7y#k9P8Ix3Dmvu|ur69$C3KO-oz z*Mh_yzx?S6S9y6_nhNkhMGsUE_uQ^SF&aSm7fal2>)?QC9Qb^*@^AsJtkr&ORD1j9 zE{=xElG12&4Psb!W};TczTd+4xUcNb@Ih>6oJ?7uz!tHa#`DYcj-TKH}$&(piCuvlj8d9jQ zb(@)(!T0FLUbN~fE(N&wWURve+PiK4$!KY1?aJ~o=X zkewyoydqdUGj25?_-n_?7k+ECe||bfYKf;*lVlfvqh4&D6ZFNcGtfJ>t;_R+Ri1B1 z|Evq1EqNZr7A~hNwac}EH`ySjG)}d6(`l1J1TWq zf0GnIyUVm4BkoUYEAiVj@Ms&?SK(=T((B;pE<|sZ_mfj7QI(Xa0kpKPid}hs7+=Fy zPq5H$TDku=1>d}a4PR0ECmtD@y$o0=EYTOP2Y)va5XaN5kXC+yJO}{q2(Ja4@V6L^Fj}DUMRCrrx!BaH&tO!ML+G~1GnNS zOFKt1>RhwkhE&k?qmPJ9lS(;n_{(@7yzT#T`tCwNc2(e9Z0%zce!H0lG1bBGanX#L*z*U!FNI4j zK&hYEi~dNnSc0O0Dtxo&!2vsZM=fliK7}5oryipLQ zmYxC?=sjs$!MxH2!9`^RG$fK^2}!+a1-BWhhWoqBipA71>bmEH&1Q8&$;jhp2I`Ni z^_o-E%exXk+HzVil*6SPcea*QGiD=FPA58f!?@KZf2l;pF*)?HOacJF0sD4`U`T2~p1wu?zdt%95*<{Gq?F86y3#j zM-hWBf6$fl;5?=6ekpGr*C+_e^gQm)1twg^EY#HJrv=WYR;Y%U7noV+fsJt)`L(E& z&g%BEdmy#@FZBu6nOQiBzh!wZ*$-HM``i8H#qL0ehEQm;<_8I z8+wuXEHCf9*&7DEKaUWD`L?zvVdG@oqN}@KI7KbVXT#fhZS7}WPYUaIo>mW(mzu!} zX2X9ZsM~E&Z(hv4DZU-|Y+#Y;cKPBa0z_?$%XdL37^+EZzN%3Q=|qqU>%^qJ-#eF# z5ZiI3jj*4}KC@68W#`@AWGI@m=Qv3J0C$0{%wcH@5h z&uJFU5)iY7nDjIlcKuD6vDJ`V{_wShJw;8U>Gv>AA90oo(pX~iI$ZtME@`C5&LcmL zTss3WB)~zPg=FOU*PSuX?T^9&e7vsS9L|))jG@oB@$=u0u1d<%To#Ig;~Aw?-<4E% zAgYDnV_{_w1%~(P9{@NUro4j^(~#DHWVpgZ@dzruTknD`y%zR{B5I+U-IiCihuYjE zEV|dT7J>C{SQzx)Sn?@(zzKZ#z8D%W6)_&DeYxvW45kCWxZF57_I<(@yy0Pb4$2y~ zUDCP3FZFvitO{JZ-hVu*7JK?nDm*ri;X%tj=93njYCH{Tet!ND-q~5W+#Z4Vd+CKg z#+~5m3tPk(0teNe&b$=pF>lxJ$#z!+$}pYC6p)k|(3DUsUB2>-CWR`O!0s`=z_to- z316st*B~&TAC1uIQSr}lxFx?($)%@RaOSaoPVZO0xh@*E2;#_q1*jP~{jtD*V)9AD z&3j>Ue5Jsm=KE(A=z5#g&@UGMur6vnnf>7J?_c}w;RBwwA*Pxnu=NwW*X@!urO@;B z%+yxaB9(r2s8!JK9!GvU1mzT<$1|g6wyb+;N9sSZ4$5NBY?+=xVehdI-s}hJ^N>+u zd4`H3_DsC-fN8m(#9axQT|PxRp+8P%ze=J8N6G#u@s`#U8%>5O>wwoR?VNsIAZO&| zjEDBrbT^;+Yv!4t`g@%1uiF&E{^&Sd?Bj)PiI|%EONaUew%6UyFd8eI9(x`*l#ba| zBQ4qU_nZct!C%FI^^@lHE}{6b7k=Bd_kj;lG(=E%cVbfTq8~rd?(c`WRfcba?9>F3 zy=<>%+OwX2d1gKPTvPnrq7=xK&%gZ{2?-YaIw|h=p++Z>Zttw6arUwPPIKFOU3)QU zA!_CvD-3B?DPYYVUHxf2lAqo5$VtTvvkb5IGkMOVNZ|QC!Dc0f-|h7HN8EeCG{UL3 zmk;Mc|58BW-n#Ag_&pmp1*T;0!}y+8n~N6{KHM=Wl0RsEeQT`}exAnn>P_@Af}-T)D z+1~!UW}A234~}yaMyU9a^aWcxvTq`f7Rhm7`dy*Ia}+hCjn9{2OpVp=?rD>)?d6(q z56tY=?HK=cMU-7J1biAE?XGNtcP;;tr+l;RMeajdj>xJ)!4vKsQ;HokPP!3(VI{f` z@HC)gy+-gjsl@c&5m^|#>0=Lgk2@5`Y`kl?&;<@yn)v$9F~x7l8hz|}^!Rq+FT;6J zIL27mklsCIw<2Vy4bmqhaje}@aaNuLV^n|~0~f!G0M%+i)8X1cF4_)4mq-|TW!Y=| zz--_c^xe}rDXW~2cfh1esoIw_$Wi!rno%cdy?4%Nn>gBxLDjU46FRf7xWcl{q7EKUDo&Gb`~VU0S8pStj*`^XH%`U^ zrLx!IfE`t?WQ$q+7lwZDXm|I-iP^3-pD*(F%i7o#a}nt0u=3ai8m zG3c^;E}TWr*lbHNe38B;uooX&_z5@0Znyc{ zlbPjx129r`sW#2RYpRH^LNcu3OGdJYd;ewuy!B1{rhs99Ov_;H>}-q%eeL%etLpj zAU00=2)dGLVZ0~<#mO95K9neapI=K5WZJk@!6S!JWYJm}6})P@Ut`&l5sd$*baBgmJ!o-Wzxgdt>x z|3+yK-o^Mk|Lop;sWFz?OAkppwk=cBG}qyw_smr-H?sctbxJU0E%3vQfg)W5G#T%! zBcDDDkdhn)Q(VvY0JL)}2jd-vmcp(ET}VSeTo;Aq^0Y zkU|i|!Y!H>(w2S?oW^FuWwH8^^A}sfU7B%k>;Vu-$$!X^HDSCmJ9}*$;8w(|yo&x3 zi}-0p%5-xI9&)Dya#e*6i_|X4QDp9Qe>|bshdUE>g@Th#^OO1!xcmgAZT2Uq*MhQj zy~u@Ty`OTe>OhfR_WYk8j7sPs%O9mnloU9u}@cCng;8tfMzUDKNLoV@!Dp*Kw}b zEv&EXo&-t#IFcfq06-*<#-0FY@9UB5z0$;M11zGVn+V3~X4;IWUdpfdcod9; z-sL;1F83^^0}|FjW~kPohf|zz(ITxEov%n?i(IdSUW79tMM zKvE&ER`eU_A3*>BmFQyQ3+(zW(MxG99+kGKkq%nL(6s;+B4<-Pe&lzinz4fzGOT-4 z^jE~*{~pc1w>J;7rGn_cpHS;br4G_+e?z|!IDi1(^aKECVrK8|s?PUYpLpY+bsNu# z^8v94OMKHzKrGK9j#@ltb$QgX*|UyC;-Vc zn}l9GHyY2G+OrHj4v)$K?Y}!SP_Cz3yA!kOoj;e9lq6gg`b_Ls2IR_aEhD~3BJsi+ zN)4Dx*m{%AX@x$QQ$Z&4Q){M5Xmnc-`0%mlc)2 ze7>puy*~AXK9LZ;IMk(biv7p|0`f8BOQ0mier|ugw&NHnJXc*_>8wg6?y-?5AT+(? zswqK5HhEPpj$><(%7^QqB!6Zu#s&cZASJeYym8Cj|4=l@urOlR*`7IPBj1kPSjzKF1&TM%WU71+98Ve1aP3Y z!>y|XIwqV@g@$$$F7Er%0ruAcb-@3#`u_lAj$xf-SQA0!WML}Y`ri*{kJ9y@TUQ?~ zcu)Fsh~18ou{vpu4T^39YY+dpE9!ePA1UWlR(Cy8i@HCxuRotkEfMl*uWI43Q-#70 z(X>Ne*@Wo1yptmXodA z5j1OJ{BWf}9)?>ENfRCPX}*O|R~h(f!WZVEy2Q*J7z1$Q;_X@rRbd!9M!*(6zp3`y zWg)(ztsUUdfieo}+LY8HAekn~ABd;+qkwb(9_BI|LsdB0Ow@Kw)11W0Ad@T~xDEAYP_0}E5W^WTXKF3tqe z6Dpjt2$L)Ly?thi(91Y;{6*l^^^6*{p}JblScyP931D`khSQSCh>Lp(XNY#}9%0Ob zrNKRc+m!EV>wZKM9aC=}3a|iz1IT}y*s9ueKvb8_nuZCH<7PeZd3ska~PReo(K`pkFg}E&HZHbZjOTye!P}FuTn%Xfww)? zDWLL9v`VO(xU=-bp0i%JJsllwH#11i@6;uP_pB9wZTX+8C!FCw2CC=3-aBy7O z^jT*HcAdCQwUzDHI=t)I+YWwcVhNvj&v*gIB|lrq$-hbTwI_tXJWTXKxQ{(J1srB$ zaujftYiQs$>3Yk*9u&$??5+gx+cD#yxpdeL(o&rUPSK%_=Za*hirmaLQ}`a&zPc=$ zMdbAR^crujb5(++Z6TiUxXB7HK|`dI9=yJ|^hxHGmJ*-B=Y~n0;NHTnx^*uzLig7v z+jWhVy9y&;s(VIEa8Np}0&g8{NjYJ@EFKHODT?TMkbgY8GLgFmkAZF>FNp~4ya^+J=Ik} zP+5>vnd~6}An_Yhq)y>~R9$CEHtG)0521cePG@5xA|lwv0S~+Ag1Ld~gu0(DmjBIF zq6CPCcFl4IWr_KinpgK{Rmf%cNg5%aK)AbuP2&4o_jB9fwAoMpt0V3;<-1nz1b1Mk zu-og+#}I9d9nQ=yOv=z1wCMyuB(_fX%Kkr2F7FJURWroS=cSvRoSdoij`LNkG^1M* zvA|d)C==iZ7z1W1fA9n>3b5?yHyROG0Nd0aJk*S#qNEaj-c+kXAO8XwlL-b@JbMOEg^1d%$zedmkMhkA`A+tS9{*Z&#e)iaqUTr&X1eg>j?; z;em4IkC&}U=s4mBLkP;h%pQm+JabqP4D)*aF>rJ?0EpSv=yMhMVON6=8FAD*6`?$mZ94PDm3qZT22Hudnx zet^UF%NK)QE|{*~xn~V1(L%bT#W-&LF>4qnSImj`S*f^f4S zjxij1SAm{r@l3+tcd+-#jAcuH0YYj_GkRv}gV71J1M*prX<&Vt?jTb+xm zR6o_Vv4fluk*#&g+oW=wr@J^wJS=L+`#Wo1;9`-ceFe$z3!-Y-OZR8F07Jv9pHfsD zR`}SJoV@22JW$7tJA?bq@M-m-z{+^W0v{ckfBI2nyZc{NKd)p?t6O)4z~XbFvXo7} zpV9MZ`yU()w;H(GUv}=k)#t*sLi}J6{CmR{dL66XH8Q%TP%q%{$hfMT zatLqWJlmc++YiVvV-fC~_u>#c+jZTlJK}=Uimgv<*JV996Qf_M5*_@7F*S-w$54WA zMm)(SS|Xs$mRCwT(6{T=HBTXQnYn7Lx8(=^QnCQD6X z-mqh^Y*+gtx)vWzWy#GC-f{d#4FGL0>Z7V0CfeKoI0`Wp3Ld$FU#2+=A%sutB@}I@2FT)5OH7Yf+AQh@E&DT7|79;|l zsEr?iYO68}7O4m9(QV9?8(YA3rr_G? zjDcZosj}d%0{bO66dS36lg+m7r2qZoeQ6n2pPRC)^wlm7qWv5KK?99{x&X7;P>jb_t}a2$7DIg53AS9zQ=4X!+E>D zTX@9#IPiQcTnPavfp9De4o3FlAo&_UZSx%=2*8#3IrB3#)>vI#LRwoGKB$J~r#9Cg zh0nsMlBq==Gcfw?J@ejphNiK$wzfHmEUs}KNwv0~j*bqe`@dPv7qUU6&~tD!E#06H z9Xln?ehwlL!M^L#*L*G-bDM zBLUIRdmqb{CI^(Gqa#np#Tv6ChJN_CgSx8yy6?^~ZYdRr09k?n&5~U2kN>%l9}8eO zo3><#(w%W;VSavZ3Z63F!b7F+t>Aw474^=OK0Q7C_J!JJ*Du-$Z%0A{eq2aLfXVhS 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

Mik>o^lfQ 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 M^7n!xE;9Om07AgfUH||9 diff --git a/frontend/appflowy_tauri/src-tauri/icons/icon.ico b/frontend/appflowy_tauri/src-tauri/icons/icon.ico deleted file mode 100644 index cd9ad402d1c9d4c4a96c21c3b5596f26c7394fc9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32830 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%0AY=UEapfpDaZvc}sy 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 b&_DKs^Z)l<;XXj|?iex$_|QcESB&;Q(k;u` diff --git a/frontend/appflowy_tauri/src-tauri/icons/icon.png b/frontend/appflowy_tauri/src-tauri/icons/icon.png deleted file mode 100644 index 7cc3853d67255311995dbac598d7edc6ab1e00fb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 41650 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 diff --git a/frontend/appflowy_tauri/src-tauri/rust-toolchain.toml b/frontend/appflowy_tauri/src-tauri/rust-toolchain.toml deleted file mode 100644 index 6f14058b2e..0000000000 --- a/frontend/appflowy_tauri/src-tauri/rust-toolchain.toml +++ /dev/null @@ -1,2 +0,0 @@ -[toolchain] -channel = "1.77.2" diff --git a/frontend/appflowy_tauri/src-tauri/rustfmt.toml b/frontend/appflowy_tauri/src-tauri/rustfmt.toml deleted file mode 100644 index 5cb0d67ee5..0000000000 --- a/frontend/appflowy_tauri/src-tauri/rustfmt.toml +++ /dev/null @@ -1,12 +0,0 @@ -# https://rust-lang.github.io/rustfmt/?version=master&search= -max_width = 100 -tab_spaces = 2 -newline_style = "Auto" -match_block_trailing_comma = true -use_field_init_shorthand = true -use_try_shorthand = true -reorder_imports = true -reorder_modules = true -remove_nested_parens = true -merge_derives = true -edition = "2021" \ No newline at end of file diff --git a/frontend/appflowy_tauri/src-tauri/src/init.rs b/frontend/appflowy_tauri/src-tauri/src/init.rs deleted file mode 100644 index 4903e1fe34..0000000000 --- a/frontend/appflowy_tauri/src-tauri/src/init.rs +++ /dev/null @@ -1,78 +0,0 @@ -use dotenv::dotenv; -use flowy_core::config::AppFlowyCoreConfig; -use flowy_core::{AppFlowyCore, DEFAULT_NAME}; -use lib_dispatch::runtime::AFPluginRuntime; -use std::sync::Mutex; - -pub fn read_env() { - dotenv().ok(); - - let env = if cfg!(debug_assertions) { - include_str!("../env.development") - } else { - include_str!("../env.production") - }; - - for line in env.lines() { - if let Some((key, value)) = line.split_once('=') { - // Check if the environment variable is not already set in the system - let current_value = std::env::var(key).unwrap_or_default(); - if current_value.is_empty() { - std::env::set_var(key, value); - } - } - } -} - -pub(crate) fn init_appflowy_core() -> MutexAppFlowyCore { - let config_json = include_str!("../tauri.conf.json"); - let config: tauri_utils::config::Config = serde_json::from_str(config_json).unwrap(); - - let app_version = config - .package - .version - .clone() - .map(|v| v.to_string()) - .unwrap_or_else(|| "0.5.8".to_string()); - let app_version = - semver::Version::parse(&app_version).unwrap_or_else(|_| semver::Version::new(0, 5, 8)); - let mut data_path = tauri::api::path::app_local_data_dir(&config).unwrap(); - if cfg!(debug_assertions) { - data_path.push("data_dev"); - } else { - data_path.push("data"); - } - - let custom_application_path = data_path.to_str().unwrap().to_string(); - let application_path = data_path.to_str().unwrap().to_string(); - let device_id = uuid::Uuid::new_v4().to_string(); - - read_env(); - std::env::set_var("RUST_LOG", "trace"); - - let config = AppFlowyCoreConfig::new( - app_version, - custom_application_path, - application_path, - device_id, - "tauri".to_string(), - DEFAULT_NAME.to_string(), - ) - .log_filter("trace", vec!["appflowy_tauri".to_string()]); - - let runtime = Arc::new(AFPluginRuntime::new().unwrap()); - let cloned_runtime = runtime.clone(); - runtime.block_on(async move { - MutexAppFlowyCore::new(AppFlowyCore::new(config, cloned_runtime, None).await) - }) -} - -pub struct MutexAppFlowyCore(pub Arc>); - -impl MutexAppFlowyCore { - fn new(appflowy_core: AppFlowyCore) -> Self { - Self(Arc::new(Mutex::new(appflowy_core))) - } -} -unsafe impl Sync for MutexAppFlowyCore {} -unsafe impl Send for MutexAppFlowyCore {} diff --git a/frontend/appflowy_tauri/src-tauri/src/main.rs b/frontend/appflowy_tauri/src-tauri/src/main.rs deleted file mode 100644 index 5f12d1be81..0000000000 --- a/frontend/appflowy_tauri/src-tauri/src/main.rs +++ /dev/null @@ -1,72 +0,0 @@ -#![cfg_attr( - all(not(debug_assertions), target_os = "windows"), - windows_subsystem = "windows" -)] - -#[allow(dead_code)] -pub const DEEP_LINK_SCHEME: &str = "appflowy-flutter"; -pub const OPEN_DEEP_LINK: &str = "open_deep_link"; - -mod init; -mod notification; -mod request; - -use crate::init::init_appflowy_core; -use crate::request::invoke_request; -use flowy_notification::{register_notification_sender, unregister_all_notification_sender}; -use notification::*; -use tauri::Manager; - -extern crate dotenv; - -fn main() { - tauri_plugin_deep_link::prepare(DEEP_LINK_SCHEME); - - let flowy_core = init_appflowy_core(); - tauri::Builder::default() - .invoke_handler(tauri::generate_handler![invoke_request]) - .manage(flowy_core) - .on_window_event(|_window_event| {}) - .on_menu_event(|_menu| {}) - .on_page_load(|window, _payload| { - let app_handler = window.app_handle(); - // Make sure hot reload won't register the notification sender twice - unregister_all_notification_sender(); - register_notification_sender(TSNotificationSender::new(app_handler.clone())); - // tauri::async_runtime::spawn(async move {}); - - window.listen_global(AF_EVENT, move |event| { - on_event(app_handler.clone(), event); - }); - }) - .setup(|_app| { - let splashscreen_window = _app.get_window("splashscreen").unwrap(); - let window = _app.get_window("main").unwrap(); - let handle = _app.handle(); - - // we perform the initialization code on a new task so the app doesn't freeze - tauri::async_runtime::spawn(async move { - // initialize your app here instead of sleeping :) - std::thread::sleep(std::time::Duration::from_secs(2)); - - // After it's done, close the splashscreen and display the main window - splashscreen_window.close().unwrap(); - window.show().unwrap(); - // If you need macOS support this must be called in .setup() ! - // Otherwise this could be called right after prepare() but then you don't have access to tauri APIs - // On macOS You still have to install a .app bundle you got from tauri build --debug for this to work! - tauri_plugin_deep_link::register( - DEEP_LINK_SCHEME, - move |request| { - dbg!(&request); - handle.emit_all(OPEN_DEEP_LINK, request).unwrap(); - }, - ) - .unwrap(/* If listening to the scheme is optional for your app, you don't want to unwrap here. */); - }); - - Ok(()) - }) - .run(tauri::generate_context!()) - .expect("error while running tauri application"); -} diff --git a/frontend/appflowy_tauri/src-tauri/src/notification.rs b/frontend/appflowy_tauri/src-tauri/src/notification.rs deleted file mode 100644 index b42541edec..0000000000 --- a/frontend/appflowy_tauri/src-tauri/src/notification.rs +++ /dev/null @@ -1,35 +0,0 @@ -use flowy_notification::entities::SubscribeObject; -use flowy_notification::NotificationSender; -use serde::Serialize; -use tauri::{AppHandle, Event, Manager, Wry}; - -#[allow(dead_code)] -pub const AF_EVENT: &str = "af-event"; -pub const AF_NOTIFICATION: &str = "af-notification"; - -#[tracing::instrument(level = "trace")] -pub fn on_event(app_handler: AppHandle, event: Event) {} - -#[allow(dead_code)] -pub fn send_notification(app_handler: AppHandle, payload: P) { - app_handler.emit_all(AF_NOTIFICATION, payload).unwrap(); -} - -pub struct TSNotificationSender { - handler: AppHandle, -} - -impl TSNotificationSender { - pub fn new(handler: AppHandle) -> Self { - Self { handler } - } -} - -impl NotificationSender for TSNotificationSender { - fn send_subject(&self, subject: SubscribeObject) -> Result<(), String> { - self - .handler - .emit_all(AF_NOTIFICATION, subject) - .map_err(|e| format!("{:?}", e)) - } -} diff --git a/frontend/appflowy_tauri/src-tauri/src/request.rs b/frontend/appflowy_tauri/src-tauri/src/request.rs deleted file mode 100644 index ff69a438c9..0000000000 --- a/frontend/appflowy_tauri/src-tauri/src/request.rs +++ /dev/null @@ -1,45 +0,0 @@ -use crate::init::MutexAppFlowyCore; -use lib_dispatch::prelude::{ - AFPluginDispatcher, AFPluginEventResponse, AFPluginRequest, StatusCode, -}; -use tauri::{AppHandle, Manager, State, Wry}; - -#[derive(Clone, Debug, serde::Deserialize)] -pub struct AFTauriRequest { - ty: String, - payload: Vec, -} - -impl std::convert::From for AFPluginRequest { - fn from(event: AFTauriRequest) -> Self { - AFPluginRequest::new(event.ty).payload(event.payload) - } -} - -#[derive(Clone, serde::Serialize)] -pub struct AFTauriResponse { - code: StatusCode, - payload: Vec, -} - -impl std::convert::From for AFTauriResponse { - fn from(response: AFPluginEventResponse) -> Self { - Self { - code: response.status_code, - payload: response.payload.to_vec(), - } - } -} - -// Learn more about Tauri commands at https://tauri.app/v1/guides/features/command -#[tauri::command] -pub async fn invoke_request( - request: AFTauriRequest, - app_handler: AppHandle, -) -> AFTauriResponse { - let request: AFPluginRequest = request.into(); - let state: State = app_handler.state(); - let dispatcher = state.0.lock().unwrap().dispatcher(); - let response = AFPluginDispatcher::sync_send(dispatcher, request); - response.into() -} diff --git a/frontend/appflowy_tauri/src-tauri/tauri.conf.json b/frontend/appflowy_tauri/src-tauri/tauri.conf.json deleted file mode 100644 index 11dd7c206c..0000000000 --- a/frontend/appflowy_tauri/src-tauri/tauri.conf.json +++ /dev/null @@ -1,113 +0,0 @@ -{ - "build": { - "beforeDevCommand": "npm run dev", - "beforeBuildCommand": "pnpm run build", - "devPath": "http://localhost:1420", - "distDir": "../dist", - "withGlobalTauri": false - }, - "package": { - "productName": "AppFlowy", - "version": "0.0.1" - }, - "tauri": { - "allowlist": { - "all": false, - "shell": { - "all": false, - "open": true - }, - "fs": { - "all": true, - "scope": [ - "$APPLOCALDATA/**" - ], - "readFile": true, - "writeFile": true, - "readDir": true, - "copyFile": true, - "createDir": true, - "removeDir": true, - "removeFile": true, - "renameFile": true, - "exists": true - }, - "clipboard": { - "all": true, - "writeText": true, - "readText": true - }, - "dialog": { - "all": true, - "ask": true, - "confirm": true, - "message": true, - "open": true, - "save": true - } - }, - "bundle": { - "active": true, - "category": "DeveloperTool", - "copyright": "", - "deb": { - "depends": [] - }, - "externalBin": [], - "icon": [ - "icons/32x32.png", - "icons/128x128.png", - "icons/128x128@2x.png", - "icons/icon.icns", - "icons/icon.ico" - ], - "identifier": "com.appflowy.tauri", - "longDescription": "", - "macOS": { - "entitlements": null, - "exceptionDomain": "", - "frameworks": [], - "providerShortName": null, - "signingIdentity": null, - "minimumSystemVersion": "10.15.0" - }, - "resources": [], - "shortDescription": "", - "targets": "all", - "windows": { - "certificateThumbprint": null, - "digestAlgorithm": "sha256", - "timestampUrl": "" - } - }, - "security": { - "csp": null - }, - "updater": { - "active": false - }, - "windows": [ - { - "fileDropEnabled": false, - "fullscreen": false, - "height": 800, - "resizable": true, - "title": "AppFlowy", - "width": 1200, - "minWidth": 800, - "minHeight": 600, - "visible": false, - "label": "main" - }, - { - "height": 300, - "width": 549, - "decorations": false, - "url": "launch_splash.jpg", - "label": "splashscreen", - "center": true, - "visible": true - } - ] - } -} \ No newline at end of file diff --git a/frontend/appflowy_tauri/src/appflowy_app/@types/i18next.d.ts b/frontend/appflowy_tauri/src/appflowy_app/@types/i18next.d.ts deleted file mode 100644 index 6adbb4a512..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/@types/i18next.d.ts +++ /dev/null @@ -1,8 +0,0 @@ -import resources from './resources'; - -declare module 'i18next' { - interface CustomTypeOptions { - defaultNS: 'translation'; - resources: typeof resources; - } -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/@types/resources.ts b/frontend/appflowy_tauri/src/appflowy_app/@types/resources.ts deleted file mode 100644 index 479f05f013..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/@types/resources.ts +++ /dev/null @@ -1,7 +0,0 @@ -import translation from '$app/i18n/translations/en.json'; - -const resources = { - translation, -} as const; - -export default resources; diff --git a/frontend/appflowy_tauri/src/appflowy_app/App.tsx b/frontend/appflowy_tauri/src/appflowy_app/App.tsx deleted file mode 100644 index 9381737341..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/App.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { BrowserRouter } from 'react-router-dom'; - -import { Provider } from 'react-redux'; -import { store } from './stores/store'; - -import { ErrorHandlerPage } from './components/error/ErrorHandlerPage'; -import '$app/i18n/config'; - -import { ErrorBoundary } from 'react-error-boundary'; - -import AppMain from '$app/AppMain'; - -const App = () => { - return ( - - - - - - - - ); -}; - -export default App; diff --git a/frontend/appflowy_tauri/src/appflowy_app/AppMain.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/AppMain.hooks.ts deleted file mode 100644 index 9c46b8ab38..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/AppMain.hooks.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { useAppDispatch, useAppSelector } from '$app/stores/store'; -import { useEffect, useMemo } from 'react'; -import { currentUserActions, LoginState } from '$app_reducers/current-user/slice'; -import { Theme as ThemeType, ThemeMode } from '$app/stores/reducers/current-user/slice'; -import { createTheme } from '@mui/material/styles'; -import { getDesignTokens } from '$app/utils/mui'; -import { useTranslation } from 'react-i18next'; -import { UserService } from '$app/application/user/user.service'; - -export function useUserSetting() { - const dispatch = useAppDispatch(); - const { i18n } = useTranslation(); - const loginState = useAppSelector((state) => state.currentUser.loginState); - - const { themeMode = ThemeMode.System, theme: themeType = ThemeType.Default } = useAppSelector((state) => { - return { - themeMode: state.currentUser.userSetting.themeMode, - theme: state.currentUser.userSetting.theme, - }; - }); - - const isDark = - themeMode === ThemeMode.Dark || - (themeMode === ThemeMode.System && window.matchMedia('(prefers-color-scheme: dark)').matches); - - useEffect(() => { - if (loginState !== LoginState.Success && loginState !== undefined) return; - void (async () => { - const settings = await UserService.getAppearanceSetting(); - - if (!settings) return; - dispatch(currentUserActions.setUserSetting(settings)); - await i18n.changeLanguage(settings.language); - })(); - }, [dispatch, i18n, loginState]); - - useEffect(() => { - const html = document.documentElement; - - html?.setAttribute('data-dark-mode', String(isDark)); - }, [isDark]); - - useEffect(() => { - const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); - - const handleSystemThemeChange = () => { - if (themeMode !== ThemeMode.System) return; - dispatch( - currentUserActions.setUserSetting({ - isDark: mediaQuery.matches, - }) - ); - }; - - mediaQuery.addEventListener('change', handleSystemThemeChange); - - return () => { - mediaQuery.removeEventListener('change', handleSystemThemeChange); - }; - }, [dispatch, themeMode]); - - const muiTheme = useMemo(() => createTheme(getDesignTokens(isDark)), [isDark]); - - return { - muiTheme, - themeMode, - themeType, - }; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/AppMain.tsx b/frontend/appflowy_tauri/src/appflowy_app/AppMain.tsx deleted file mode 100644 index 76bdb167b0..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/AppMain.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import React from 'react'; -import { Route, Routes } from 'react-router-dom'; -import { ProtectedRoutes } from '$app/components/auth/ProtectedRoutes'; -import { DatabasePage } from '$app/views/DatabasePage'; - -import { ThemeProvider } from '@mui/material'; -import { useUserSetting } from '$app/AppMain.hooks'; -import TrashPage from '$app/views/TrashPage'; -import DocumentPage from '$app/views/DocumentPage'; -import { Toaster } from 'react-hot-toast'; -import AppFlowyDevTool from '$app/components/_shared/devtool/AppFlowyDevTool'; - -function AppMain() { - const { muiTheme } = useUserSetting(); - - return ( - - - }> - } /> - } /> - } /> - - - - {process.env.NODE_ENV === 'development' && } - - ); -} - -export default AppMain; diff --git a/frontend/appflowy_tauri/src/appflowy_app/application/database/cell/cell_listeners.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/cell/cell_listeners.ts deleted file mode 100644 index c5c94daebc..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/application/database/cell/cell_listeners.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { Database } from '$app/application/database'; -import { getCell } from './cell_service'; - -export function didDeleteCells({ database, rowId, fieldId }: { database: Database; rowId?: string; fieldId?: string }) { - const ids = Object.keys(database.cells); - - ids.forEach((id) => { - const cell = database.cells[id]; - - if (rowId && cell.rowId !== rowId) return; - if (fieldId && cell.fieldId !== fieldId) return; - - delete database.cells[id]; - }); -} - -export async function didUpdateCells({ - viewId, - database, - rowId, - fieldId, -}: { - viewId: string; - database: Database; - rowId?: string; - fieldId?: string; -}) { - const field = database.fields.find((field) => field.id === fieldId); - - if (!field) { - delete database.cells[`${rowId}:${fieldId}`]; - return; - } - - const ids = Object.keys(database.cells); - - ids.forEach((id) => { - const cell = database.cells[id]; - - if (rowId && cell.rowId !== rowId) return; - if (fieldId && cell.fieldId !== fieldId) return; - - void getCell(viewId, cell.rowId, cell.fieldId, field.type).then((data) => { - // cache cell - database.cells[id] = data; - }); - }); -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/application/database/cell/cell_service.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/cell/cell_service.ts deleted file mode 100644 index 950f5becb3..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/application/database/cell/cell_service.ts +++ /dev/null @@ -1,140 +0,0 @@ -import { - CellIdPB, - CellChangesetPB, - SelectOptionCellChangesetPB, - ChecklistCellDataChangesetPB, - DateCellChangesetPB, - FieldType, -} from '../../../../services/backend'; -import { - DatabaseEventGetCell, - DatabaseEventUpdateCell, - DatabaseEventUpdateSelectOptionCell, - DatabaseEventUpdateChecklistCell, - DatabaseEventUpdateDateCell, -} from '@/services/backend/events/flowy-database2'; -import { SelectOption } from '../field'; -import { Cell, pbToCell } from './cell_types'; - -export async function getCell(viewId: string, rowId: string, fieldId: string, fieldType?: FieldType): Promise { - const payload = CellIdPB.fromObject({ - view_id: viewId, - row_id: rowId, - field_id: fieldId, - }); - - const result = await DatabaseEventGetCell(payload); - - if (result.ok === false) { - return Promise.reject(result.val); - } - - const value = result.val; - - return pbToCell(value, fieldType); -} - -export async function updateCell(viewId: string, rowId: string, fieldId: string, changeset: string): Promise { - const payload = CellChangesetPB.fromObject({ - view_id: viewId, - row_id: rowId, - field_id: fieldId, - cell_changeset: changeset, - }); - - const result = await DatabaseEventUpdateCell(payload); - - return result.unwrap(); -} - -export async function updateSelectCell( - viewId: string, - rowId: string, - fieldId: string, - data: { - insertOptionIds?: string[]; - deleteOptionIds?: string[]; - } -): Promise { - const payload = SelectOptionCellChangesetPB.fromObject({ - cell_identifier: { - view_id: viewId, - row_id: rowId, - field_id: fieldId, - }, - insert_option_ids: data.insertOptionIds, - delete_option_ids: data.deleteOptionIds, - }); - - const result = await DatabaseEventUpdateSelectOptionCell(payload); - - return result.unwrap(); -} - -export async function updateChecklistCell( - viewId: string, - rowId: string, - fieldId: string, - data: { - insertOptions?: string[]; - selectedOptionIds?: string[]; - deleteOptionIds?: string[]; - updateOptions?: Partial[]; - } -): Promise { - const payload = ChecklistCellDataChangesetPB.fromObject({ - view_id: viewId, - row_id: rowId, - field_id: fieldId, - insert_options: data.insertOptions, - selected_option_ids: data.selectedOptionIds, - delete_option_ids: data.deleteOptionIds, - update_options: data.updateOptions, - }); - - const result = await DatabaseEventUpdateChecklistCell(payload); - - return result.unwrap(); -} - -export async function updateDateCell( - viewId: string, - rowId: string, - fieldId: string, - data: { - // 10-digit timestamp - date?: number; - // time string in format HH:mm - time?: string; - // 10-digit timestamp - endDate?: number; - // time string in format HH:mm - endTime?: string; - includeTime?: boolean; - clearFlag?: boolean; - isRange?: boolean; - } -): Promise { - const payload = DateCellChangesetPB.fromObject({ - cell_id: { - view_id: viewId, - row_id: rowId, - field_id: fieldId, - }, - date: data.date, - time: data.time, - include_time: data.includeTime, - clear_flag: data.clearFlag, - end_date: data.endDate, - end_time: data.endTime, - is_range: data.isRange, - }); - - const result = await DatabaseEventUpdateDateCell(payload); - - if (!result.ok) { - return Promise.reject(typeof result.val.msg === 'string' ? result.val.msg : 'Unknown error'); - } - - return result.val; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/application/database/cell/cell_types.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/cell/cell_types.ts deleted file mode 100644 index f36f68ad8b..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/application/database/cell/cell_types.ts +++ /dev/null @@ -1,163 +0,0 @@ -import { - CellPB, - CheckboxCellDataPB, - ChecklistCellDataPB, - DateCellDataPB, - FieldType, - SelectOptionCellDataPB, - TimestampCellDataPB, - URLCellDataPB, -} from '../../../../services/backend'; -import { SelectOption, pbToSelectOption } from '../field/select_option/select_option_types'; - -export interface Cell { - rowId: string; - fieldId: string; - fieldType: FieldType; - data: unknown; -} - -export interface TextCell extends Cell { - fieldType: FieldType.RichText; - data: string; -} - -export interface NumberCell extends Cell { - fieldType: FieldType.Number; - data: string; -} - -export interface CheckboxCell extends Cell { - fieldType: FieldType.Checkbox; - data: boolean; -} - -export interface UrlCell extends Cell { - fieldType: FieldType.URL; - data: string; -} - -export interface SelectCell extends Cell { - fieldType: FieldType.SingleSelect | FieldType.MultiSelect; - data: SelectCellData; -} - -export interface SelectCellData { - selectedOptionIds?: string[]; -} - -export interface DateTimeCell extends Cell { - fieldType: FieldType.DateTime; - data: DateTimeCellData; -} - -export interface TimeStampCell extends Cell { - fieldType: FieldType.LastEditedTime | FieldType.CreatedTime; - data: TimestampCellData; -} - -export interface DateTimeCellData { - date?: string; - time?: string; - timestamp?: number; - includeTime?: boolean; - endDate?: string; - endTime?: string; - endTimestamp?: number; - isRange?: boolean; -} - -export interface TimestampCellData { - dataTime?: string; - timestamp?: number; -} - -export interface ChecklistCell extends Cell { - fieldType: FieldType.Checklist; - data: ChecklistCellData; -} - -export interface ChecklistCellData { - /** - * link to [SelectOption's id property]{@link SelectOption#id}. - */ - selectedOptions?: string[]; - percentage?: number; - options?: SelectOption[]; -} - -export type UndeterminedCell = - | TextCell - | NumberCell - | DateTimeCell - | SelectCell - | CheckboxCell - | UrlCell - | ChecklistCell; - -const pbToCheckboxCellData = (pb: CheckboxCellDataPB): boolean => ( - pb.is_checked -); - -const pbToDateTimeCellData = (pb: DateCellDataPB): DateTimeCellData => ({ - date: pb.date, - time: pb.time, - timestamp: pb.timestamp, - includeTime: pb.include_time, - endDate: pb.end_date, - endTime: pb.end_time, - endTimestamp: pb.end_timestamp, - isRange: pb.is_range, -}); - -const pbToTimestampCellData = (pb: TimestampCellDataPB): TimestampCellData => ({ - dataTime: pb.date_time, - timestamp: pb.timestamp, -}); - -export const pbToSelectCellData = (pb: SelectOptionCellDataPB): SelectCellData => { - return { - selectedOptionIds: pb.select_options.map((option) => option.id), - }; -}; - -const pbToURLCellData = (pb: URLCellDataPB): string => ( - pb.content -); - -export const pbToChecklistCellData = (pb: ChecklistCellDataPB): ChecklistCellData => ({ - selectedOptions: pb.selected_options.map(({ id }) => id), - percentage: pb.percentage, - options: pb.options.map(pbToSelectOption), -}); - -function bytesToCellData(bytes: Uint8Array, fieldType: FieldType) { - switch (fieldType) { - case FieldType.RichText: - case FieldType.Number: - return new TextDecoder().decode(bytes); - case FieldType.Checkbox: - return pbToCheckboxCellData(CheckboxCellDataPB.deserialize(bytes)); - case FieldType.DateTime: - return pbToDateTimeCellData(DateCellDataPB.deserialize(bytes)); - case FieldType.LastEditedTime: - case FieldType.CreatedTime: - return pbToTimestampCellData(TimestampCellDataPB.deserialize(bytes)); - case FieldType.SingleSelect: - case FieldType.MultiSelect: - return pbToSelectCellData(SelectOptionCellDataPB.deserialize(bytes)); - case FieldType.URL: - return pbToURLCellData(URLCellDataPB.deserialize(bytes)); - case FieldType.Checklist: - return pbToChecklistCellData(ChecklistCellDataPB.deserialize(bytes)); - } -} - -export const pbToCell = (pb: CellPB, fieldType: FieldType = pb.field_type): Cell => { - return { - rowId: pb.row_id, - fieldId: pb.field_id, - fieldType: fieldType, - data: bytesToCellData(pb.data, fieldType), - }; -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/application/database/cell/index.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/cell/index.ts deleted file mode 100644 index bc6bdc4417..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/application/database/cell/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './cell_types'; -export * as cellService from './cell_service'; -export * as cellListeners from './cell_listeners'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/application/database/database/database_service.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/database/database_service.ts deleted file mode 100644 index 74ebfb1df0..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/application/database/database/database_service.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { DatabaseViewIdPB } from '@/services/backend'; -import { - DatabaseEventGetDatabase, - DatabaseEventGetDatabaseId, - DatabaseEventGetDatabaseSetting, -} from '@/services/backend/events/flowy-database2'; -import { fieldService } from '../field'; -import { pbToFilter } from '../filter'; -import { groupService, pbToGroupSetting } from '../group'; -import { pbToRowMeta } from '../row'; -import { pbToSort } from '../sort'; -import { Database } from './database_types'; - -export async function getDatabaseId(viewId: string): Promise { - const payload = DatabaseViewIdPB.fromObject({ value: viewId }); - - const result = await DatabaseEventGetDatabaseId(payload); - - return result.map((value) => value.value).unwrap(); -} - -export async function getDatabase(viewId: string) { - const payload = DatabaseViewIdPB.fromObject({ - value: viewId, - }); - - const result = await DatabaseEventGetDatabase(payload); - - if (!result.ok) return Promise.reject('Failed to get database'); - - return result - .map((value) => { - return { - id: value.id, - isLinked: value.is_linked, - layoutType: value.layout_type, - fieldIds: value.fields.map((field) => field.field_id), - rowMetas: value.rows.map(pbToRowMeta), - }; - }) - .unwrap(); -} - -export async function getDatabaseSetting(viewId: string) { - const payload = DatabaseViewIdPB.fromObject({ - value: viewId, - }); - - const result = await DatabaseEventGetDatabaseSetting(payload); - - return result - .map((value) => { - return { - filters: value.filters.items.map(pbToFilter), - sorts: value.sorts.items.map(pbToSort), - groupSettings: value.group_settings.items.map(pbToGroupSetting), - }; - }) - .unwrap(); -} - -export async function openDatabase(viewId: string): Promise { - const { id, isLinked, layoutType, fieldIds, rowMetas } = await getDatabase(viewId); - - const { filters, sorts, groupSettings } = await getDatabaseSetting(viewId); - - const { fields, typeOptions } = await fieldService.getFields(viewId, fieldIds); - - const groups = await groupService.getGroups(viewId); - - return { - id, - isLinked, - layoutType, - fields, - rowMetas, - filters, - sorts, - groups, - groupSettings, - typeOptions, - cells: {}, - }; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/application/database/database/database_types.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/database/database_types.ts deleted file mode 100644 index 627cd94013..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/application/database/database/database_types.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { DatabaseLayoutPB } from '@/services/backend'; -import { Field, UndeterminedTypeOptionData } from '../field'; -import { Filter } from '../filter'; -import { GroupSetting, Group } from '../group'; -import { RowMeta } from '../row'; -import { Sort } from '../sort'; -import { Cell } from '../cell'; - -export interface Database { - id: string; - isLinked: boolean; - layoutType: DatabaseLayoutPB; - fields: Field[]; - rowMetas: RowMeta[]; - filters: Filter[]; - sorts: Sort[]; - groupSettings: GroupSetting[]; - groups: Group[]; - typeOptions: Record; - cells: Record; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/application/database/database/index.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/database/index.ts deleted file mode 100644 index e656d98287..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/application/database/database/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './database_types'; -export * as databaseService from './database_service'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/application/database/database_view/database_view_service.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/database_view/database_view_service.ts deleted file mode 100644 index 87d99d9b75..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/application/database/database_view/database_view_service.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { CreateViewPayloadPB, RepeatedViewIdPB, UpdateViewPayloadPB, ViewIdPB, ViewLayoutPB } from '@/services/backend'; -import { - FolderEventCreateView, - FolderEventDeleteView, - FolderEventGetView, - FolderEventUpdateView, -} from '@/services/backend/events/flowy-folder'; -import { databaseService } from '../database'; -import { Page, parserViewPBToPage } from '$app_reducers/pages/slice'; - -export async function getDatabaseViews(viewId: string): Promise { - const payload = ViewIdPB.fromObject({ value: viewId }); - - const result = await FolderEventGetView(payload); - - if (result.ok) { - return [parserViewPBToPage(result.val), ...result.val.child_views.map(parserViewPBToPage)]; - } - - return Promise.reject(result.val); -} - -export async function createDatabaseView( - viewId: string, - layout: ViewLayoutPB, - name: string, - databaseId?: string -): Promise { - const payload = CreateViewPayloadPB.fromObject({ - parent_view_id: viewId, - name, - layout, - meta: { - database_id: databaseId || (await databaseService.getDatabaseId(viewId)), - }, - }); - - const result = await FolderEventCreateView(payload); - - if (result.ok) { - return parserViewPBToPage(result.val); - } - - return Promise.reject(result.err); -} - -export async function updateView(viewId: string, view: { name?: string; layout?: ViewLayoutPB }): Promise { - const payload = UpdateViewPayloadPB.fromObject({ - view_id: viewId, - name: view.name, - layout: view.layout, - }); - - const result = await FolderEventUpdateView(payload); - - if (result.ok) { - return parserViewPBToPage(result.val); - } - - return Promise.reject(result.err); -} - -export async function deleteView(viewId: string): Promise { - const payload = RepeatedViewIdPB.fromObject({ - items: [viewId], - }); - - const result = await FolderEventDeleteView(payload); - - if (result.ok) { - return; - } - - return Promise.reject(result.err); -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/application/database/database_view/index.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/database_view/index.ts deleted file mode 100644 index b2a6e1a5f1..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/application/database/database_view/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * as databaseViewService from './database_view_service'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/application/database/field/field_listeners.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/field/field_listeners.ts deleted file mode 100644 index ef36daa20c..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/application/database/field/field_listeners.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { DatabaseFieldChangesetPB, FieldSettingsPB, FieldVisibility } from '@/services/backend'; -import { Database, fieldService } from '$app/application/database'; -import { didDeleteCells, didUpdateCells } from '$app/application/database/cell/cell_listeners'; - -export function didUpdateFieldSettings(database: Database, settings: FieldSettingsPB) { - const { field_id: fieldId, visibility, width } = settings; - const field = database.fields.find((field) => field.id === fieldId); - - if (!field) return; - field.visibility = visibility; - field.width = width; - // delete cells if field is hidden - if (visibility === FieldVisibility.AlwaysHidden) { - didDeleteCells({ database, fieldId }); - } -} - -export async function didUpdateFields(viewId: string, database: Database, changeset: DatabaseFieldChangesetPB) { - const { fields, typeOptions } = await fieldService.getFields(viewId); - - database.fields = fields; - const deletedFieldIds = Object.keys(changeset.deleted_fields); - const updatedFieldIds = changeset.updated_fields.map((field) => field.id); - - Object.assign(database.typeOptions, typeOptions); - deletedFieldIds.forEach( - (fieldId) => { - // delete cache cells - didDeleteCells({ database, fieldId }); - // delete cache type options - delete database.typeOptions[fieldId]; - }, - [database.typeOptions] - ); - - updatedFieldIds.forEach((fieldId) => { - // delete cache cells - void didUpdateCells({ viewId, database, fieldId }); - }); -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/application/database/field/field_service.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/field/field_service.ts deleted file mode 100644 index 219aeb3ea5..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/application/database/field/field_service.ts +++ /dev/null @@ -1,215 +0,0 @@ -import { - CreateFieldPayloadPB, - DeleteFieldPayloadPB, - DuplicateFieldPayloadPB, - FieldChangesetPB, - FieldType, - GetFieldPayloadPB, - MoveFieldPayloadPB, - RepeatedFieldIdPB, - UpdateFieldTypePayloadPB, - FieldSettingsChangesetPB, - FieldVisibility, - DatabaseViewIdPB, - OrderObjectPositionTypePB, -} from '@/services/backend'; -import { - DatabaseEventDuplicateField, - DatabaseEventUpdateField, - DatabaseEventUpdateFieldType, - DatabaseEventMoveField, - DatabaseEventGetFields, - DatabaseEventDeleteField, - DatabaseEventCreateField, - DatabaseEventUpdateFieldSettings, - DatabaseEventGetAllFieldSettings, -} from '@/services/backend/events/flowy-database2'; -import { Field, pbToField } from './field_types'; -import { bytesToTypeOption } from './type_option'; -import { Database } from '$app/application/database'; - -export async function getFields( - viewId: string, - fieldIds?: string[] -): Promise<{ - fields: Field[]; - typeOptions: Database['typeOptions']; -}> { - const payload = GetFieldPayloadPB.fromObject({ - view_id: viewId, - field_ids: fieldIds - ? RepeatedFieldIdPB.fromObject({ - items: fieldIds.map((fieldId) => ({ field_id: fieldId })), - }) - : undefined, - }); - - const result = await DatabaseEventGetFields(payload); - - const getSettingsPayload = DatabaseViewIdPB.fromObject({ - value: viewId, - }); - - const settings = await DatabaseEventGetAllFieldSettings(getSettingsPayload); - - if (settings.ok === false || result.ok === false) { - return Promise.reject('Failed to get fields'); - } - - const typeOptions: Database['typeOptions'] = {}; - - const fields = await Promise.all( - result.val.items.map(async (item) => { - const setting = settings.val.items.find((setting) => setting.field_id === item.id); - - const field = pbToField(item); - - const typeOption = bytesToTypeOption(item.type_option_data, item.field_type); - - if (typeOption) { - typeOptions[item.id] = typeOption; - } - - return { - ...field, - visibility: setting?.visibility, - width: setting?.width, - }; - }) - ); - - return { fields, typeOptions }; -} - -export async function createField({ - viewId, - targetFieldId, - fieldPosition, - fieldType, - data, -}: { - viewId: string; - targetFieldId?: string; - fieldPosition?: OrderObjectPositionTypePB; - fieldType?: FieldType; - data?: Uint8Array; -}): Promise { - const payload = CreateFieldPayloadPB.fromObject({ - view_id: viewId, - field_type: fieldType, - type_option_data: data, - field_position: { - position: fieldPosition, - object_id: targetFieldId, - }, - }); - - const result = await DatabaseEventCreateField(payload); - - if (result.ok === false) { - return Promise.reject('Failed to create field'); - } - - return pbToField(result.val); -} - -export async function duplicateField(viewId: string, fieldId: string): Promise { - const payload = DuplicateFieldPayloadPB.fromObject({ - view_id: viewId, - field_id: fieldId, - }); - - const result = await DatabaseEventDuplicateField(payload); - - if (result.ok === false) { - return Promise.reject('Failed to duplicate field'); - } - - return result.val; -} - -export async function updateField( - viewId: string, - fieldId: string, - data: { - name?: string; - desc?: string; - } -): Promise { - const payload = FieldChangesetPB.fromObject({ - view_id: viewId, - field_id: fieldId, - ...data, - }); - - const result = await DatabaseEventUpdateField(payload); - - return result.unwrap(); -} - -export async function updateFieldType(viewId: string, fieldId: string, fieldType: FieldType): Promise { - const payload = UpdateFieldTypePayloadPB.fromObject({ - view_id: viewId, - field_id: fieldId, - field_type: fieldType, - }); - - const result = await DatabaseEventUpdateFieldType(payload); - - return result.unwrap(); -} - -export async function moveField(viewId: string, fromFieldId: string, toFieldId: string): Promise { - const payload = MoveFieldPayloadPB.fromObject({ - view_id: viewId, - from_field_id: fromFieldId, - to_field_id: toFieldId, - }); - - const result = await DatabaseEventMoveField(payload); - - return result.unwrap(); -} - -export async function deleteField(viewId: string, fieldId: string): Promise { - const payload = DeleteFieldPayloadPB.fromObject({ - view_id: viewId, - field_id: fieldId, - }); - - const result = await DatabaseEventDeleteField(payload); - - return result.unwrap(); -} - -export async function updateFieldSetting( - viewId: string, - fieldId: string, - settings: { - visibility?: FieldVisibility; - width?: number; - } -): Promise { - const payload = FieldSettingsChangesetPB.fromObject({ - view_id: viewId, - field_id: fieldId, - ...settings, - }); - - const result = await DatabaseEventUpdateFieldSettings(payload); - - if (result.ok === false) { - return Promise.reject('Failed to update field settings'); - } - - return result.val; -} - -export const reorderFields = (list: Field[], startIndex: number, endIndex: number) => { - const result = Array.from(list); - const [removed] = result.splice(startIndex, 1); - - result.splice(endIndex, 0, removed); - - return result; -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/application/database/field/field_types.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/field/field_types.ts deleted file mode 100644 index 00e7e02d4e..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/application/database/field/field_types.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { FieldPB, FieldType, FieldVisibility } from '@/services/backend'; - -export interface Field { - id: string; - name: string; - type: FieldType; - visibility?: FieldVisibility; - width?: number; - isPrimary: boolean; -} - -export interface NumberField extends Field { - type: FieldType.Number; -} - -export interface DateTimeField extends Field { - type: FieldType.DateTime; -} - -export interface LastEditedTimeField extends Field { - type: FieldType.LastEditedTime; -} - -export interface CreatedTimeField extends Field { - type: FieldType.CreatedTime; -} - -export type UndeterminedDateField = DateTimeField | CreatedTimeField | LastEditedTimeField; - -export interface SelectField extends Field { - type: FieldType.SingleSelect | FieldType.MultiSelect; -} - -export interface ChecklistField extends Field { - type: FieldType.Checklist; -} - -export interface DateTimeField extends Field { - type: FieldType.DateTime; -} - -export type UndeterminedField = NumberField | DateTimeField | SelectField | Field; - -export const pbToField = (pb: FieldPB): Field => { - return { - id: pb.id, - name: pb.name, - type: pb.field_type, - isPrimary: pb.is_primary, - }; -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/application/database/field/index.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/field/index.ts deleted file mode 100644 index fa993023e1..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/application/database/field/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * from './select_option'; -export * from './type_option'; -export * from './field_types'; -export * as fieldService from './field_service'; -export * as fieldListeners from './field_listeners'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/application/database/field/select_option/index.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/field/select_option/index.ts deleted file mode 100644 index f0b9e58852..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/application/database/field/select_option/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './select_option_types'; -export * as selectOptionService from './select_option_service'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/application/database/field/select_option/select_option_service.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/field/select_option/select_option_service.ts deleted file mode 100644 index 5757b8185d..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/application/database/field/select_option/select_option_service.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { CreateSelectOptionPayloadPB, RepeatedSelectOptionPayload } from '@/services/backend'; -import { - DatabaseEventCreateSelectOption, - DatabaseEventInsertOrUpdateSelectOption, - DatabaseEventDeleteSelectOption, -} from '@/services/backend/events/flowy-database2'; -import { pbToSelectOption, SelectOption } from './select_option_types'; - -export async function createSelectOption(viewId: string, fieldId: string, optionName: string): Promise { - const payload = CreateSelectOptionPayloadPB.fromObject({ - view_id: viewId, - field_id: fieldId, - option_name: optionName, - }); - - const result = await DatabaseEventCreateSelectOption(payload); - - return result.map(pbToSelectOption).unwrap(); -} - -/** - * @param [rowId] If pass the rowId, the cell will select this option after insert or update. - */ -export async function insertOrUpdateSelectOption( - viewId: string, - fieldId: string, - items: Partial[], - rowId?: string -): Promise { - const payload = RepeatedSelectOptionPayload.fromObject({ - view_id: viewId, - field_id: fieldId, - row_id: rowId, - items: items, - }); - - const result = await DatabaseEventInsertOrUpdateSelectOption(payload); - - return result.unwrap(); -} - -export async function deleteSelectOption( - viewId: string, - fieldId: string, - items: Partial[], - rowId?: string -): Promise { - const payload = RepeatedSelectOptionPayload.fromObject({ - view_id: viewId, - field_id: fieldId, - row_id: rowId, - items, - }); - - const result = await DatabaseEventDeleteSelectOption(payload); - - return result.unwrap(); -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/application/database/field/select_option/select_option_types.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/field/select_option/select_option_types.ts deleted file mode 100644 index ec36639a7c..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/application/database/field/select_option/select_option_types.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { SelectOptionColorPB, SelectOptionPB } from '@/services/backend'; - -export interface SelectOption { - id: string; - name: string; - color: SelectOptionColorPB; -} - -export function pbToSelectOption(pb: SelectOptionPB): SelectOption { - return { - id: pb.id, - name: pb.name, - color: pb.color, - }; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/application/database/field/type_option/index.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/field/type_option/index.ts deleted file mode 100644 index d0b9122d90..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/application/database/field/type_option/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './type_option_types'; -export * from './type_option_service'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/application/database/field/type_option/type_option_service.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/field/type_option/type_option_service.ts deleted file mode 100644 index 90a0dd3106..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/application/database/field/type_option/type_option_service.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { FieldType, TypeOptionChangesetPB } from '@/services/backend'; -import { - DatabaseEventUpdateFieldTypeOption, -} from '@/services/backend/events/flowy-database2'; -import { UndeterminedTypeOptionData, typeOptionDataToPB } from './type_option_types'; - -export async function updateTypeOption( - viewId: string, - fieldId: string, - fieldType: FieldType, - data: UndeterminedTypeOptionData -) { - const payload = TypeOptionChangesetPB.fromObject({ - view_id: viewId, - field_id: fieldId, - type_option_data: typeOptionDataToPB(data, fieldType)?.serialize(), - }); - - const result = await DatabaseEventUpdateFieldTypeOption(payload); - - if (!result.ok) { - return Promise.reject(result.val); - } - - return; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/application/database/field/type_option/type_option_types.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/field/type_option/type_option_types.ts deleted file mode 100644 index 57de7b828c..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/application/database/field/type_option/type_option_types.ts +++ /dev/null @@ -1,148 +0,0 @@ -import { - CheckboxTypeOptionPB, - DateFormatPB, - FieldType, - MultiSelectTypeOptionPB, - NumberFormatPB, - NumberTypeOptionPB, - RichTextTypeOptionPB, - SingleSelectTypeOptionPB, - TimeFormatPB, - ChecklistTypeOptionPB, - DateTypeOptionPB, - TimestampTypeOptionPB, -} from '@/services/backend'; -import { pbToSelectOption, SelectOption } from '../select_option'; - -export interface TextTypeOption { - data?: string; -} - -export interface NumberTypeOption { - format?: NumberFormatPB; - scale?: number; - symbol?: string; - name?: string; -} - -export interface DateTimeTypeOption { - dateFormat?: DateFormatPB; - timeFormat?: TimeFormatPB; - timezoneId?: string; -} -export interface TimeStampTypeOption extends DateTimeTypeOption { - includeTime?: boolean; - fieldType?: FieldType; -} - -export interface SelectTypeOption { - options?: SelectOption[]; - disableColor?: boolean; -} - -export interface CheckboxTypeOption { - isSelected?: boolean; -} - -export interface ChecklistTypeOption { - config?: string; -} - -export type UndeterminedTypeOptionData = - | TextTypeOption - | NumberTypeOption - | SelectTypeOption - | CheckboxTypeOption - | ChecklistTypeOption - | DateTimeTypeOption - | TimeStampTypeOption; - -export function typeOptionDataToPB(data: UndeterminedTypeOptionData, fieldType: FieldType) { - switch (fieldType) { - case FieldType.Number: - return NumberTypeOptionPB.fromObject(data as NumberTypeOption); - case FieldType.DateTime: - return dateTimeTypeOptionToPB(data as DateTimeTypeOption); - case FieldType.CreatedTime: - case FieldType.LastEditedTime: - return timestampTypeOptionToPB(data as TimeStampTypeOption); - - default: - return null; - } -} - -function dateTimeTypeOptionToPB(data: DateTimeTypeOption): DateTypeOptionPB { - return DateTypeOptionPB.fromObject({ - time_format: data.timeFormat, - date_format: data.dateFormat, - timezone_id: data.timezoneId, - }); -} - -function timestampTypeOptionToPB(data: TimeStampTypeOption): TimestampTypeOptionPB { - return TimestampTypeOptionPB.fromObject({ - include_time: data.includeTime, - date_format: data.dateFormat, - time_format: data.timeFormat, - field_type: data.fieldType, - }); -} - -function pbToSelectTypeOption(pb: SingleSelectTypeOptionPB | MultiSelectTypeOptionPB): SelectTypeOption { - return { - options: pb.options?.map(pbToSelectOption), - disableColor: pb.disable_color, - }; -} - -function pbToCheckboxTypeOption(pb: CheckboxTypeOptionPB): CheckboxTypeOption { - return { - isSelected: pb.dummy_field, - }; -} - -function pbToChecklistTypeOption(pb: ChecklistTypeOptionPB): ChecklistTypeOption { - return { - config: pb.config, - }; -} - -function pbToDateTypeOption(pb: DateTypeOptionPB): DateTimeTypeOption { - return { - dateFormat: pb.date_format, - timezoneId: pb.timezone_id, - timeFormat: pb.time_format, - }; -} - -function pbToTimeStampTypeOption(pb: TimestampTypeOptionPB): TimeStampTypeOption { - return { - includeTime: pb.include_time, - dateFormat: pb.date_format, - timeFormat: pb.time_format, - fieldType: pb.field_type, - }; -} - -export function bytesToTypeOption(data: Uint8Array, fieldType: FieldType) { - switch (fieldType) { - case FieldType.RichText: - return RichTextTypeOptionPB.deserialize(data).toObject() as TextTypeOption; - case FieldType.Number: - return NumberTypeOptionPB.deserialize(data).toObject() as NumberTypeOption; - case FieldType.SingleSelect: - return pbToSelectTypeOption(SingleSelectTypeOptionPB.deserialize(data)); - case FieldType.MultiSelect: - return pbToSelectTypeOption(MultiSelectTypeOptionPB.deserialize(data)); - case FieldType.Checkbox: - return pbToCheckboxTypeOption(CheckboxTypeOptionPB.deserialize(data)); - case FieldType.Checklist: - return pbToChecklistTypeOption(ChecklistTypeOptionPB.deserialize(data)); - case FieldType.DateTime: - return pbToDateTypeOption(DateTypeOptionPB.deserialize(data)); - case FieldType.CreatedTime: - case FieldType.LastEditedTime: - return pbToTimeStampTypeOption(TimestampTypeOptionPB.deserialize(data)); - } -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/application/database/filter/filter_data.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/filter/filter_data.ts deleted file mode 100644 index 72526b577f..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/application/database/filter/filter_data.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { - CheckboxFilterConditionPB, - ChecklistFilterConditionPB, - FieldType, - NumberFilterConditionPB, - SelectOptionFilterConditionPB, - TextFilterConditionPB, -} from '@/services/backend'; -import { UndeterminedFilter } from '$app/application/database'; - -export function getDefaultFilter(fieldType: FieldType): UndeterminedFilter['data'] | undefined { - switch (fieldType) { - case FieldType.RichText: - case FieldType.URL: - return { - condition: TextFilterConditionPB.TextContains, - content: '', - }; - case FieldType.Number: - return { - condition: NumberFilterConditionPB.NumberIsNotEmpty, - }; - case FieldType.Checkbox: - return { - condition: CheckboxFilterConditionPB.IsUnChecked, - }; - case FieldType.Checklist: - return { - condition: ChecklistFilterConditionPB.IsIncomplete, - }; - case FieldType.SingleSelect: - return { - condition: SelectOptionFilterConditionPB.OptionIs, - }; - case FieldType.MultiSelect: - return { - condition: SelectOptionFilterConditionPB.OptionContains, - }; - default: - return; - } -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/application/database/filter/filter_listeners.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/filter/filter_listeners.ts deleted file mode 100644 index 323f8dac82..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/application/database/filter/filter_listeners.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { Database, pbToFilter } from '$app/application/database'; -import { FilterChangesetNotificationPB } from '@/services/backend'; - -export const didUpdateFilter = (database: Database, changeset: FilterChangesetNotificationPB) => { - const filters = changeset.filters.items.map((pb) => pbToFilter(pb)); - - database.filters = filters; -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/application/database/filter/filter_service.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/filter/filter_service.ts deleted file mode 100644 index 6283763d28..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/application/database/filter/filter_service.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { - DatabaseEventGetAllFilters, - DatabaseEventUpdateDatabaseSetting, - DatabaseSettingChangesetPB, - DatabaseViewIdPB, - FieldType, - FilterPB, -} from '@/services/backend/events/flowy-database2'; -import { Filter, filterDataToPB, UndeterminedFilter } from './filter_types'; - -export async function getAllFilters(viewId: string): Promise { - const payload = DatabaseViewIdPB.fromObject({ value: viewId }); - - const result = await DatabaseEventGetAllFilters(payload); - - return result.map((value) => value.items).unwrap(); -} - -export async function insertFilter({ - viewId, - fieldId, - fieldType, - data, -}: { - viewId: string; - fieldId: string; - fieldType: FieldType; - data?: UndeterminedFilter['data']; -}): Promise { - const payload = DatabaseSettingChangesetPB.fromObject({ - view_id: viewId, - insert_filter: { - data: { - field_id: fieldId, - field_type: fieldType, - data: data ? filterDataToPB(data, fieldType)?.serialize() : undefined, - }, - }, - }); - - const result = await DatabaseEventUpdateDatabaseSetting(payload); - - if (!result.ok) { - return Promise.reject(result.val); - } - - return result.val; -} - -export async function updateFilter(viewId: string, filter: UndeterminedFilter): Promise { - const payload = DatabaseSettingChangesetPB.fromObject({ - view_id: viewId, - update_filter_data: { - filter_id: filter.id, - data: { - field_id: filter.fieldId, - field_type: filter.fieldType, - data: filterDataToPB(filter.data, filter.fieldType)?.serialize(), - }, - }, - }); - - const result = await DatabaseEventUpdateDatabaseSetting(payload); - - if (!result.ok) { - return Promise.reject(result.val); - } - - return result.val; -} - -export async function deleteFilter(viewId: string, filter: Omit): Promise { - const payload = DatabaseSettingChangesetPB.fromObject({ - view_id: viewId, - delete_filter: { - filter_id: filter.id, - field_id: filter.fieldId, - }, - }); - - const result = await DatabaseEventUpdateDatabaseSetting(payload); - - if (!result.ok) { - return Promise.reject(result.val); - } - - return result.val; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/application/database/filter/filter_types.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/filter/filter_types.ts deleted file mode 100644 index f9f80985e5..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/application/database/filter/filter_types.ts +++ /dev/null @@ -1,202 +0,0 @@ -import { - CheckboxFilterConditionPB, - CheckboxFilterPB, - FieldType, - FilterPB, - NumberFilterConditionPB, - NumberFilterPB, - SelectOptionFilterConditionPB, - SelectOptionFilterPB, - TextFilterConditionPB, - TextFilterPB, - ChecklistFilterConditionPB, - ChecklistFilterPB, - DateFilterConditionPB, - DateFilterPB, -} from '@/services/backend'; - -export interface Filter { - id: string; - fieldId: string; - fieldType: FieldType; - data: unknown; -} - -export interface TextFilter extends Filter { - fieldType: FieldType.RichText; - data: TextFilterData; -} - -export interface TextFilterData { - condition: TextFilterConditionPB; - content?: string; -} - -export interface SelectFilter extends Filter { - fieldType: FieldType.SingleSelect | FieldType.MultiSelect; - data: SelectFilterData; -} - -export interface NumberFilter extends Filter { - fieldType: FieldType.Number; - data: NumberFilterData; -} - -export interface CheckboxFilter extends Filter { - fieldType: FieldType.Checkbox; - data: CheckboxFilterData; -} - -export interface CheckboxFilterData { - condition?: CheckboxFilterConditionPB; -} - -export interface ChecklistFilter extends Filter { - fieldType: FieldType.Checklist; - data: ChecklistFilterData; -} - -export interface DateFilter extends Filter { - fieldType: FieldType.DateTime | FieldType.CreatedTime | FieldType.LastEditedTime; - data: DateFilterData; -} - -export interface ChecklistFilterData { - condition?: ChecklistFilterConditionPB; -} - -export interface SelectFilterData { - condition?: SelectOptionFilterConditionPB; - optionIds?: string[]; -} - -export interface NumberFilterData { - condition: NumberFilterConditionPB; - content?: string; -} - -export interface DateFilterData { - condition: DateFilterConditionPB; - start?: number; - end?: number; - timestamp?: number; -} - -export type UndeterminedFilter = - | TextFilter - | SelectFilter - | NumberFilter - | CheckboxFilter - | ChecklistFilter - | DateFilter; - -export function filterDataToPB(data: UndeterminedFilter['data'], fieldType: FieldType) { - switch (fieldType) { - case FieldType.RichText: - case FieldType.URL: - return TextFilterPB.fromObject({ - condition: (data as TextFilterData).condition, - content: (data as TextFilterData).content, - }); - case FieldType.SingleSelect: - case FieldType.MultiSelect: - return SelectOptionFilterPB.fromObject({ - condition: (data as SelectFilterData).condition, - option_ids: (data as SelectFilterData).optionIds, - }); - case FieldType.Number: - return NumberFilterPB.fromObject({ - condition: (data as NumberFilterData).condition, - content: (data as NumberFilterData).content, - }); - case FieldType.Checkbox: - return CheckboxFilterPB.fromObject({ - condition: (data as CheckboxFilterData).condition, - }); - case FieldType.Checklist: - return ChecklistFilterPB.fromObject({ - condition: (data as ChecklistFilterData).condition, - }); - case FieldType.DateTime: - case FieldType.CreatedTime: - case FieldType.LastEditedTime: - return DateFilterPB.fromObject({ - condition: (data as DateFilterData).condition, - start: (data as DateFilterData).start, - end: (data as DateFilterData).end, - timestamp: (data as DateFilterData).timestamp, - }); - } -} - -export function pbToTextFilterData(pb: TextFilterPB): TextFilterData { - return { - condition: pb.condition, - content: pb.content, - }; -} - -export function pbToSelectFilterData(pb: SelectOptionFilterPB): SelectFilterData { - return { - condition: pb.condition, - optionIds: pb.option_ids, - }; -} - -export function pbToNumberFilterData(pb: NumberFilterPB): NumberFilterData { - return { - condition: pb.condition, - content: pb.content, - }; -} - -export function pbToCheckboxFilterData(pb: CheckboxFilterPB): CheckboxFilterData { - return { - condition: pb.condition, - }; -} - -export function pbToChecklistFilterData(pb: ChecklistFilterPB): ChecklistFilterData { - return { - condition: pb.condition, - }; -} - -export function pbToDateFilterData(pb: DateFilterPB): DateFilterData { - return { - condition: pb.condition, - start: pb.start, - end: pb.end, - timestamp: pb.timestamp, - }; -} - -export function bytesToFilterData(bytes: Uint8Array, fieldType: FieldType) { - switch (fieldType) { - case FieldType.RichText: - case FieldType.URL: - return pbToTextFilterData(TextFilterPB.deserialize(bytes)); - case FieldType.SingleSelect: - case FieldType.MultiSelect: - return pbToSelectFilterData(SelectOptionFilterPB.deserialize(bytes)); - case FieldType.Number: - return pbToNumberFilterData(NumberFilterPB.deserialize(bytes)); - case FieldType.Checkbox: - return pbToCheckboxFilterData(CheckboxFilterPB.deserialize(bytes)); - case FieldType.Checklist: - return pbToChecklistFilterData(ChecklistFilterPB.deserialize(bytes)); - case FieldType.DateTime: - case FieldType.CreatedTime: - case FieldType.LastEditedTime: - return pbToDateFilterData(DateFilterPB.deserialize(bytes)); - } -} - -export function pbToFilter(pb: FilterPB): Filter { - return { - id: pb.id, - fieldId: pb.data.field_id, - fieldType: pb.data.field_type, - data: bytesToFilterData(pb.data.data, pb.data.field_type), - }; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/application/database/filter/index.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/filter/index.ts deleted file mode 100644 index ac10d27d0a..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/application/database/filter/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './filter_types'; -export * as filterService from './filter_service'; -export * as filterListeners from './filter_listeners'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/application/database/group/group_service.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/group/group_service.ts deleted file mode 100644 index 24f24d65ec..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/application/database/group/group_service.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { - DatabaseViewIdPB, - GroupByFieldPayloadPB, - MoveGroupPayloadPB, - UpdateGroupPB, -} from '@/services/backend'; -import { - DatabaseEventGetGroups, - DatabaseEventMoveGroup, - DatabaseEventSetGroupByField, - DatabaseEventUpdateGroup, -} from '@/services/backend/events/flowy-database2'; -import { Group, pbToGroup } from './group_types'; - -export async function getGroups(viewId: string): Promise { - const payload = DatabaseViewIdPB.fromObject({ value: viewId }); - - const result = await DatabaseEventGetGroups(payload); - - return result.map(value => value.items.map(pbToGroup)).unwrap(); -} - -export async function setGroupByField(viewId: string, fieldId: string): Promise { - const payload = GroupByFieldPayloadPB.fromObject({ - view_id: viewId, - field_id: fieldId, - }); - - const result = await DatabaseEventSetGroupByField(payload); - - return result.unwrap(); -} - -export async function updateGroup( - viewId: string, - group: { - id: string, - name?: string, - visible?: boolean, - }, -): Promise { - const payload = UpdateGroupPB.fromObject({ - view_id: viewId, - group_id: group.id, - name: group.name, - visible: group.visible, - }); - - const result = await DatabaseEventUpdateGroup(payload); - - return result.unwrap(); -} - -export async function moveGroup(viewId: string, fromGroupId: string, toGroupId: string): Promise { - const payload = MoveGroupPayloadPB.fromObject({ - view_id: viewId, - from_group_id: fromGroupId, - to_group_id: toGroupId, - }); - - const result = await DatabaseEventMoveGroup(payload); - - return result.unwrap(); -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/application/database/group/group_types.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/group/group_types.ts deleted file mode 100644 index b75ecc0bd4..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/application/database/group/group_types.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { GroupPB, GroupSettingPB } from '@/services/backend'; -import { pbToRowMeta, RowMeta } from '../row'; - -export interface GroupSetting { - id: string; - fieldId: string; -} - -export interface Group { - id: string; - isDefault: boolean; - isVisible: boolean; - fieldId: string; - rows: RowMeta[]; -} - -export function pbToGroup(pb: GroupPB): Group { - return { - id: pb.group_id, - isDefault: pb.is_default, - isVisible: pb.is_visible, - fieldId: pb.field_id, - rows: pb.rows.map(pbToRowMeta), - }; -} - -export function pbToGroupSetting(pb: GroupSettingPB): GroupSetting { - return { - id: pb.id, - fieldId: pb.field_id, - }; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/application/database/group/index.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/group/index.ts deleted file mode 100644 index bb872d6677..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/application/database/group/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './group_types'; -export * as groupService from './group_service'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/application/database/index.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/index.ts deleted file mode 100644 index f44da5b857..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/application/database/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -export * from './cell'; -export * from './database'; -export * from './database_view'; -export * from './field'; -export * from './filter'; -export * from './group'; -export * from './row'; -export * from './sort'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/application/database/row/index.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/row/index.ts deleted file mode 100644 index 69260223ef..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/application/database/row/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './row_types'; -export * as rowService from './row_service'; -export * as rowListeners from './row_listeners'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/application/database/row/row_listeners.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/row/row_listeners.ts deleted file mode 100644 index e8a638403e..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/application/database/row/row_listeners.ts +++ /dev/null @@ -1,95 +0,0 @@ -import { ReorderAllRowsPB, ReorderSingleRowPB, RowsChangePB, RowsVisibilityChangePB } from '@/services/backend'; -import { Database } from '../database'; -import { pbToRowMeta, RowMeta } from './row_types'; -import { didDeleteCells } from '$app/application/database/cell/cell_listeners'; -import { getDatabase } from '$app/application/database/database/database_service'; - -const deleteRowsFromChangeset = (database: Database, changeset: RowsChangePB) => { - changeset.deleted_rows.forEach((rowId) => { - const index = database.rowMetas.findIndex((row) => row.id === rowId); - - if (index !== -1) { - database.rowMetas.splice(index, 1); - // delete cells - didDeleteCells({ database, rowId }); - } - }); -}; - -const updateRowsFromChangeset = (database: Database, changeset: RowsChangePB) => { - changeset.updated_rows.forEach(({ row_id: rowId, row_meta: rowMetaPB }) => { - const found = database.rowMetas.find((rowMeta) => rowMeta.id === rowId); - - if (found) { - Object.assign(found, rowMetaPB ? pbToRowMeta(rowMetaPB) : {}); - } - }); -}; - -export const didUpdateViewRows = async (viewId: string, database: Database, changeset: RowsChangePB) => { - if (changeset.inserted_rows.length > 0) { - const { rowMetas } = await getDatabase(viewId); - - database.rowMetas = rowMetas; - return; - } - - deleteRowsFromChangeset(database, changeset); - updateRowsFromChangeset(database, changeset); -}; - -export const didReorderRows = (database: Database, changeset: ReorderAllRowsPB) => { - const rowById = database.rowMetas.reduce>((prev, cur) => { - prev[cur.id] = cur; - return prev; - }, {}); - - database.rowMetas = changeset.row_orders.map((rowId) => rowById[rowId]); -}; - -export const didReorderSingleRow = (database: Database, changeset: ReorderSingleRowPB) => { - const { row_id: rowId, new_index: newIndex } = changeset; - - const oldIndex = database.rowMetas.findIndex((rowMeta) => rowMeta.id === rowId); - - if (oldIndex !== -1) { - database.rowMetas.splice(newIndex, 0, database.rowMetas.splice(oldIndex, 1)[0]); - } -}; - -export const didUpdateViewRowsVisibility = async ( - viewId: string, - database: Database, - changeset: RowsVisibilityChangePB -) => { - const { invisible_rows, visible_rows } = changeset; - - let reFetchRows = false; - - for (const rowId of invisible_rows) { - const rowMeta = database.rowMetas.find((rowMeta) => rowMeta.id === rowId); - - if (rowMeta) { - rowMeta.isHidden = true; - } - } - - for (const insertedRow of visible_rows) { - const rowMeta = database.rowMetas.find((rowMeta) => rowMeta.id === insertedRow.row_meta.id); - - if (rowMeta) { - rowMeta.isHidden = false; - } else { - reFetchRows = true; - break; - } - } - - if (reFetchRows) { - const { rowMetas } = await getDatabase(viewId); - - database.rowMetas = rowMetas; - - await didUpdateViewRowsVisibility(viewId, database, changeset); - } -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/application/database/row/row_service.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/row/row_service.ts deleted file mode 100644 index 7993f709b7..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/application/database/row/row_service.ts +++ /dev/null @@ -1,129 +0,0 @@ -import { - CreateRowPayloadPB, - MoveGroupRowPayloadPB, - MoveRowPayloadPB, - OrderObjectPositionTypePB, - RepeatedRowIdPB, - RowIdPB, - UpdateRowMetaChangesetPB, -} from '@/services/backend'; -import { - DatabaseEventCreateRow, - DatabaseEventDeleteRows, - DatabaseEventDuplicateRow, - DatabaseEventGetRowMeta, - DatabaseEventMoveGroupRow, - DatabaseEventMoveRow, - DatabaseEventUpdateRowMeta, -} from '@/services/backend/events/flowy-database2'; -import { pbToRowMeta, RowMeta } from './row_types'; - -export async function createRow(viewId: string, params?: { - position?: OrderObjectPositionTypePB; - rowId?: string; - groupId?: string; - data?: Record; -}): Promise { - const payload = CreateRowPayloadPB.fromObject({ - view_id: viewId, - row_position: { - position: params?.position, - object_id: params?.rowId, - }, - group_id: params?.groupId, - data: params?.data, - }); - - const result = await DatabaseEventCreateRow(payload); - - return result.map(pbToRowMeta).unwrap(); -} - -export async function duplicateRow(viewId: string, rowId: string, groupId?: string): Promise { - const payload = RowIdPB.fromObject({ - view_id: viewId, - row_id: rowId, - group_id: groupId, - }); - - const result = await DatabaseEventDuplicateRow(payload); - - return result.unwrap(); -} - -export async function deleteRow(viewId: string, rowId: string, groupId?: string): Promise { - const payload = RepeatedRowIdPB.fromObject({ - view_id: viewId, - row_ids: [rowId], - }); - - const result = await DatabaseEventDeleteRows(payload); - - return result.unwrap(); -} - -export async function moveRow(viewId: string, fromRowId: string, toRowId: string): Promise { - const payload = MoveRowPayloadPB.fromObject({ - view_id: viewId, - from_row_id: fromRowId, - to_row_id: toRowId, - }); - - const result = await DatabaseEventMoveRow(payload); - - return result.unwrap(); -} - -/** - * Move the row from one group to another group - * - * @param fromRowId - * @param toGroupId - * @param toRowId used to locate the moving row location. - * @returns - */ -export async function moveGroupRow(viewId: string, fromRowId: string, toGroupId: string, toRowId?: string): Promise { - const payload = MoveGroupRowPayloadPB.fromObject({ - view_id: viewId, - from_row_id: fromRowId, - to_group_id: toGroupId, - to_row_id: toRowId, - }); - - const result = await DatabaseEventMoveGroupRow(payload); - - return result.unwrap(); -} - - -export async function getRowMeta(viewId: string, rowId: string, groupId?: string): Promise { - const payload = RowIdPB.fromObject({ - view_id: viewId, - row_id: rowId, - group_id: groupId, - }); - - const result = await DatabaseEventGetRowMeta(payload); - - return result.map(pbToRowMeta).unwrap(); -} - -export async function updateRowMeta( - viewId: string, - rowId: string, - meta: { - iconUrl?: string; - coverUrl?: string; - }, -): Promise { - const payload = UpdateRowMetaChangesetPB.fromObject({ - view_id: viewId, - id: rowId, - icon_url: meta.iconUrl, - cover_url: meta.coverUrl, - }); - - const result = await DatabaseEventUpdateRowMeta(payload); - - return result.unwrap(); -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/application/database/row/row_types.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/row/row_types.ts deleted file mode 100644 index 1b964a6bb5..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/application/database/row/row_types.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { RowMetaPB } from '@/services/backend'; - -export interface RowMeta { - id: string; - documentId?: string; - icon?: string; - cover?: string; - isHidden?: boolean; -} - -export function pbToRowMeta(pb: RowMetaPB): RowMeta { - const rowMeta: RowMeta = { - id: pb.id, - }; - - if (pb.document_id) { - rowMeta.documentId = pb.document_id; - } - - if (pb.icon) { - rowMeta.icon = pb.icon; - } - - if (pb.cover) { - rowMeta.cover = pb.cover; - } - - return rowMeta; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/application/database/sort/index.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/sort/index.ts deleted file mode 100644 index 6c7d4bd60a..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/application/database/sort/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './sort_types'; -export * as sortService from './sort_service'; -export * as sortListeners from './sort_listeners'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/application/database/sort/sort_listeners.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/sort/sort_listeners.ts deleted file mode 100644 index 808c62e0d2..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/application/database/sort/sort_listeners.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { SortChangesetNotificationPB } from '@/services/backend'; -import { Database } from '../database'; -import { pbToSort } from './sort_types'; - -const deleteSortsFromChange = (database: Database, changeset: SortChangesetNotificationPB) => { - const deleteIds = changeset.delete_sorts.map(sort => sort.id); - - if (deleteIds.length) { - database.sorts = database.sorts.filter(sort => !deleteIds.includes(sort.id)); - } -}; - -const insertSortsFromChange = (database: Database, changeset: SortChangesetNotificationPB) => { - changeset.insert_sorts.forEach(sortPB => { - database.sorts.push(pbToSort(sortPB.sort)); - }); -}; - -const updateSortsFromChange = (database: Database, changeset: SortChangesetNotificationPB) => { - changeset.update_sorts.forEach(sortPB => { - const found = database.sorts.find(sort => sort.id === sortPB.id); - - if (found) { - const newSort = pbToSort(sortPB); - - Object.assign(found, newSort); - } - }); -}; - -export const didUpdateSort = (database: Database, changeset: SortChangesetNotificationPB) => { - deleteSortsFromChange(database, changeset); - insertSortsFromChange(database, changeset); - updateSortsFromChange(database, changeset); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/application/database/sort/sort_service.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/sort/sort_service.ts deleted file mode 100644 index 2546ec780c..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/application/database/sort/sort_service.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { - DatabaseViewIdPB, - DatabaseSettingChangesetPB, -} from '@/services/backend'; -import { - DatabaseEventDeleteAllSorts, - DatabaseEventGetAllSorts, DatabaseEventUpdateDatabaseSetting, -} from '@/services/backend/events/flowy-database2'; -import { pbToSort, Sort } from './sort_types'; - -export async function getAllSorts(viewId: string): Promise { - const payload = DatabaseViewIdPB.fromObject({ - value: viewId, - }); - - const result = await DatabaseEventGetAllSorts(payload); - - return result.map(value => value.items.map(pbToSort)).unwrap(); -} - -export async function insertSort(viewId: string, sort: Omit): Promise { - const payload = DatabaseSettingChangesetPB.fromObject({ - view_id: viewId, - update_sort: { - view_id: viewId, - field_id: sort.fieldId, - condition: sort.condition, - }, - }); - - const result = await DatabaseEventUpdateDatabaseSetting(payload); - - return result.unwrap(); -} - -export async function updateSort(viewId: string, sort: Sort): Promise { - const payload = DatabaseSettingChangesetPB.fromObject({ - view_id: viewId, - update_sort: { - view_id: viewId, - sort_id: sort.id, - field_id: sort.fieldId, - condition: sort.condition, - }, - }); - - const result = await DatabaseEventUpdateDatabaseSetting(payload); - - return result.unwrap(); -} - -export async function deleteSort(viewId: string, sort: Sort): Promise { - const payload = DatabaseSettingChangesetPB.fromObject({ - view_id: viewId, - delete_sort: { - view_id: viewId, - sort_id: sort.id, - }, - }); - - const result = await DatabaseEventUpdateDatabaseSetting(payload); - - return result.unwrap(); -} - -export async function deleteAllSorts(viewId: string): Promise { - const payload = DatabaseViewIdPB.fromObject({ - value: viewId, - }); - const result = await DatabaseEventDeleteAllSorts(payload); - - return result.unwrap(); -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/application/database/sort/sort_types.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/sort/sort_types.ts deleted file mode 100644 index a8089878d1..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/application/database/sort/sort_types.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { SortConditionPB, SortPB } from '@/services/backend'; - -export interface Sort { - id: string; - fieldId: string; - condition: SortConditionPB; -} - -export function pbToSort(pb: SortPB): Sort { - return { - id: pb.id, - fieldId: pb.field_id, - condition: pb.condition, - }; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/application/document/document.service.ts b/frontend/appflowy_tauri/src/appflowy_app/application/document/document.service.ts deleted file mode 100644 index 0db128ec7a..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/application/document/document.service.ts +++ /dev/null @@ -1,284 +0,0 @@ -import { - ApplyActionPayloadPB, - BlockActionPB, - BlockPB, - CloseDocumentPayloadPB, - ConvertDataToJsonPayloadPB, - ConvertDocumentPayloadPB, - InputType, - OpenDocumentPayloadPB, - TextDeltaPayloadPB, -} from '@/services/backend'; -import { - DocumentEventApplyAction, - DocumentEventApplyTextDeltaEvent, - DocumentEventCloseDocument, - DocumentEventConvertDataToJSON, - DocumentEventConvertDocument, - DocumentEventOpenDocument, -} from '@/services/backend/events/flowy-document'; -import get from 'lodash-es/get'; -import { EditorData, EditorNodeType } from '$app/application/document/document.types'; -import { Log } from '$app/utils/log'; -import { Op } from 'quill-delta'; -import { Element, Text } from 'slate'; -import { generateId, getInlinesWithDelta } from '$app/components/editor/provider/utils/convert'; -import { CustomEditor } from '$app/components/editor/command'; -import { LIST_TYPES } from '$app/components/editor/command/tab'; - -export function blockPB2Node(block: BlockPB) { - let data = {}; - - try { - data = JSON.parse(block.data); - } catch { - Log.error('[Document Open] json parse error', block.data); - } - - return { - id: block.id, - type: block.ty as EditorNodeType, - parent: block.parent_id, - children: block.children_id, - data, - externalId: block.external_id, - externalType: block.external_type, - }; -} - -export const BLOCK_MAP_NAME = 'blocks'; -export const META_NAME = 'meta'; -export const CHILDREN_MAP_NAME = 'children_map'; - -export const TEXT_MAP_NAME = 'text_map'; -export async function openDocument(docId: string): Promise { - const payload = OpenDocumentPayloadPB.fromObject({ - document_id: docId, - }); - - const result = await DocumentEventOpenDocument(payload); - - if (!result.ok) { - return Promise.reject(result.val); - } - - const documentDataPB = result.val; - - if (!documentDataPB) { - return Promise.reject('documentDataPB is null'); - } - - const data: EditorData = { - viewId: docId, - rootId: documentDataPB.page_id, - nodeMap: {}, - childrenMap: {}, - relativeMap: {}, - deltaMap: {}, - externalIdMap: {}, - }; - - get(documentDataPB, BLOCK_MAP_NAME).forEach((block) => { - Object.assign(data.nodeMap, { - [block.id]: blockPB2Node(block), - }); - data.relativeMap[block.children_id] = block.id; - if (block.external_id) { - data.externalIdMap[block.external_id] = block.id; - } - }); - - get(documentDataPB, [META_NAME, CHILDREN_MAP_NAME]).forEach((child, key) => { - const blockId = data.relativeMap[key]; - - data.childrenMap[blockId] = child.children; - }); - - get(documentDataPB, [META_NAME, TEXT_MAP_NAME]).forEach((delta, key) => { - const blockId = data.externalIdMap[key]; - - data.deltaMap[blockId] = delta ? JSON.parse(delta) : []; - }); - - return data; -} - -export async function closeDocument(docId: string) { - const payload = CloseDocumentPayloadPB.fromObject({ - document_id: docId, - }); - - const result = await DocumentEventCloseDocument(payload); - - if (!result.ok) { - return Promise.reject(result.val); - } - - return result.val; -} - -export async function applyActions(docId: string, actions: ReturnType[]) { - if (actions.length === 0) return; - const payload = ApplyActionPayloadPB.fromObject({ - document_id: docId, - actions: actions, - }); - - const result = await DocumentEventApplyAction(payload); - - if (!result.ok) { - return Promise.reject(result.val); - } - - return result.val; -} - -export async function applyText(docId: string, textId: string, delta: string) { - const payload = TextDeltaPayloadPB.fromObject({ - document_id: docId, - text_id: textId, - delta: delta, - }); - - const res = await DocumentEventApplyTextDeltaEvent(payload); - - if (!res.ok) { - return Promise.reject(res.val); - } - - return res.val; -} - -export async function getClipboardData( - docId: string, - range: { - start: { - blockId: string; - index: number; - length: number; - }; - end?: { - blockId: string; - index: number; - length: number; - }; - } -) { - const payload = ConvertDocumentPayloadPB.fromObject({ - range: { - start: { - block_id: range.start.blockId, - index: range.start.index, - length: range.start.length, - }, - end: range.end - ? { - block_id: range.end.blockId, - index: range.end.index, - length: range.end.length, - } - : undefined, - }, - document_id: docId, - parse_types: { - json: true, - html: true, - text: true, - }, - }); - - const result = await DocumentEventConvertDocument(payload); - - if (!result.ok) { - return Promise.reject(result.val); - } - - return { - html: result.val.html, - text: result.val.text, - json: result.val.json, - }; -} - -export async function convertBlockToJson(data: string, type: InputType) { - const payload = ConvertDataToJsonPayloadPB.fromObject({ - data, - input_type: type, - }); - - const result = await DocumentEventConvertDataToJSON(payload); - - if (!result.ok) { - return Promise.reject(result.val); - } - - try { - const block = JSON.parse(result.val.json); - - return flattenBlockJson(block); - } catch (e) { - return Promise.reject(e); - } -} - -interface BlockJSON { - type: string; - children: BlockJSON[]; - data: { - [key: string]: boolean | string | number | undefined; - } & { - delta?: Op[]; - }; -} - -function flattenBlockJson(block: BlockJSON) { - const traverse = (block: BlockJSON) => { - const { delta, ...data } = block.data; - - const slateNode: Element = { - type: block.type, - data: data, - children: [], - blockId: generateId(), - }; - const isEmbed = CustomEditor.isEmbedNode(slateNode); - - const textNode: { - type: EditorNodeType.Text; - children: (Text | Element)[]; - textId: string; - } | null = !isEmbed - ? { - type: EditorNodeType.Text, - children: [{ text: '' }], - textId: generateId(), - } - : null; - - if (delta && textNode) { - textNode.children = getInlinesWithDelta(delta); - } - - slateNode.children = block.children.map((child) => traverse(child)); - - if (textNode) { - const texts = CustomEditor.getNodeTextContent(textNode); - - if (texts && !LIST_TYPES.includes(block.type as EditorNodeType) && slateNode.type !== EditorNodeType.Page) { - slateNode.children.unshift(textNode); - } else if (texts) { - slateNode.children.unshift({ - type: EditorNodeType.Paragraph, - children: [textNode], - blockId: generateId(), - }); - } - } - - return slateNode; - }; - - const root = traverse(block); - - return root.children; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/application/document/document.types.ts b/frontend/appflowy_tauri/src/appflowy_app/application/document/document.types.ts deleted file mode 100644 index e6eb1d6923..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/application/document/document.types.ts +++ /dev/null @@ -1,242 +0,0 @@ -import { Op } from 'quill-delta'; -import { HTMLAttributes } from 'react'; -import { Element } from 'slate'; -import { ViewIconTypePB, ViewLayoutPB } from '@/services/backend'; -import { PageCover } from '$app_reducers/pages/slice'; -import * as Y from 'yjs'; - -export interface EditorNode { - id: string; - type: EditorNodeType; - parent?: string | null; - data?: BlockData; - children?: string; - externalId?: string; - externalType?: string; -} - -export interface TextNode extends Element { - type: EditorNodeType.Text; - textId: string; - blockId: string; -} - -export interface PageNode extends Element { - type: EditorNodeType.Page; -} -export interface ParagraphNode extends Element { - type: EditorNodeType.Paragraph; -} - -export type BlockData = { - [key: string]: string | boolean | number | undefined; - font_color?: string; - bg_color?: string; -}; - -export interface HeadingNode extends Element { - blockId: string; - type: EditorNodeType.HeadingBlock; - data: { - level: number; - } & BlockData; -} - -export interface GridNode extends Element { - blockId: string; - type: EditorNodeType.GridBlock; - data: { - viewId?: string; - } & BlockData; -} - -export interface TodoListNode extends Element { - blockId: string; - type: EditorNodeType.TodoListBlock; - data: { - checked: boolean; - } & BlockData; -} - -export interface CodeNode extends Element { - blockId: string; - type: EditorNodeType.CodeBlock; - data: { - language: string; - } & BlockData; -} - -export interface QuoteNode extends Element { - blockId: string; - type: EditorNodeType.QuoteBlock; -} - -export interface NumberedListNode extends Element { - type: EditorNodeType.NumberedListBlock; - blockId: string; - data: { - number?: number; - } & BlockData; -} - -export interface BulletedListNode extends Element { - type: EditorNodeType.BulletedListBlock; - blockId: string; -} - -export interface ToggleListNode extends Element { - type: EditorNodeType.ToggleListBlock; - blockId: string; - data: { - collapsed: boolean; - } & BlockData; -} - -export interface DividerNode extends Element { - type: EditorNodeType.DividerBlock; - blockId: string; -} - -export interface CalloutNode extends Element { - type: EditorNodeType.CalloutBlock; - blockId: string; - data: { - icon: string; - } & BlockData; -} - -export interface MathEquationNode extends Element { - type: EditorNodeType.EquationBlock; - blockId: string; - data: { - formula?: string; - } & BlockData; -} - -export enum ImageType { - Local = 0, - Internal = 1, - External = 2, -} - -export interface ImageNode extends Element { - type: EditorNodeType.ImageBlock; - blockId: string; - data: { - url?: string; - width?: number; - image_type?: ImageType; - height?: number; - } & BlockData; -} - -export interface FormulaNode extends Element { - type: EditorInlineNodeType.Formula; - data: string; -} - -export interface MentionNode extends Element { - type: EditorInlineNodeType.Mention; - data: Mention; -} - -export interface EditorData { - viewId: string; - rootId: string; - // key: block's id, value: block - nodeMap: Record; - // key: block's children id, value: block's id - childrenMap: Record; - // key: block's children id, value: block's id - relativeMap: Record; - // key: block's externalId, value: delta - deltaMap: Record; - // key: block's externalId, value: block's id - externalIdMap: Record; -} - -export interface MentionPage { - id: string; - name: string; - layout: ViewLayoutPB; - parentId: string; - icon?: { - ty: ViewIconTypePB; - value: string; - }; -} - -export interface EditorProps { - title?: string; - cover?: PageCover; - onTitleChange?: (title: string) => void; - onCoverChange?: (cover?: PageCover) => void; - showTitle?: boolean; - id: string; - disableFocus?: boolean; -} - -export interface LocalEditorProps { - disableFocus?: boolean; - sharedType: Y.XmlText; - id: string; - caretColor?: string; -} - -export enum EditorNodeType { - Text = 'text', - Paragraph = 'paragraph', - Page = 'page', - HeadingBlock = 'heading', - TodoListBlock = 'todo_list', - BulletedListBlock = 'bulleted_list', - NumberedListBlock = 'numbered_list', - ToggleListBlock = 'toggle_list', - CodeBlock = 'code', - EquationBlock = 'math_equation', - QuoteBlock = 'quote', - CalloutBlock = 'callout', - DividerBlock = 'divider', - ImageBlock = 'image', - GridBlock = 'grid', -} - -export enum EditorInlineNodeType { - Mention = 'mention', - Formula = 'formula', -} - -export const inlineNodeTypes: (string | EditorInlineNodeType)[] = [ - EditorInlineNodeType.Mention, - EditorInlineNodeType.Formula, -]; - -export interface EditorElementProps extends HTMLAttributes { - node: T; -} - -export enum EditorMarkFormat { - Bold = 'bold', - Italic = 'italic', - Underline = 'underline', - StrikeThrough = 'strikethrough', - Code = 'code', - Href = 'href', - FontColor = 'font_color', - BgColor = 'bg_color', - Align = 'align', -} - -export enum MentionType { - PageRef = 'page', - Date = 'date', -} - -export interface Mention { - // inline page ref id - page_id?: string; - // reminder date ref id - date?: string; - - type: MentionType; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/application/folder/page.service.ts b/frontend/appflowy_tauri/src/appflowy_app/application/folder/page.service.ts deleted file mode 100644 index 7d988b9866..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/application/folder/page.service.ts +++ /dev/null @@ -1,166 +0,0 @@ -import { Page, PageIcon, parserViewPBToPage } from '$app_reducers/pages/slice'; -import { - CreateOrphanViewPayloadPB, - CreateViewPayloadPB, - MoveNestedViewPayloadPB, - RepeatedViewIdPB, - UpdateViewIconPayloadPB, - UpdateViewPayloadPB, - ViewIconPB, - ViewIdPB, - ViewPB, -} from '@/services/backend'; -import { - FolderEventCreateOrphanView, - FolderEventCreateView, - FolderEventDeleteView, - FolderEventDuplicateView, - FolderEventGetView, - FolderEventMoveNestedView, - FolderEventUpdateView, - FolderEventUpdateViewIcon, - FolderEventSetLatestView, -} from '@/services/backend/events/flowy-folder'; - -export async function getPage(id: string) { - const payload = new ViewIdPB({ - value: id, - }); - - const result = await FolderEventGetView(payload); - - if (result.ok) { - return parserViewPBToPage(result.val); - } - - return Promise.reject(result.val); -} - -export const createOrphanPage = async ( - params: ReturnType -): Promise => { - const payload = CreateOrphanViewPayloadPB.fromObject(params); - - const result = await FolderEventCreateOrphanView(payload); - - if (result.ok) { - return parserViewPBToPage(result.val); - } - - return Promise.reject(result.val); -}; - -export const duplicatePage = async (page: Page) => { - const payload = ViewPB.fromObject(page); - - const result = await FolderEventDuplicateView(payload); - - if (result.ok) { - return result.val; - } - - return Promise.reject(result.err); -}; - -export const deletePage = async (id: string) => { - const payload = new RepeatedViewIdPB({ - items: [id], - }); - - const result = await FolderEventDeleteView(payload); - - if (result.ok) { - return result.val; - } - - return Promise.reject(result.err); -}; - -export const createPage = async (params: ReturnType): Promise => { - const payload = CreateViewPayloadPB.fromObject(params); - - const result = await FolderEventCreateView(payload); - - if (result.ok) { - return result.val.id; - } - - return Promise.reject(result.err); -}; - -export const movePage = async (params: ReturnType) => { - const payload = new MoveNestedViewPayloadPB(params); - - const result = await FolderEventMoveNestedView(payload); - - if (result.ok) { - return result.val; - } - - return Promise.reject(result.err); -}; - -export const getChildPages = async (id: string): Promise => { - const payload = new ViewIdPB({ - value: id, - }); - - const result = await FolderEventGetView(payload); - - if (result.ok) { - return result.val.child_views.map(parserViewPBToPage); - } - - return []; -}; - -export const updatePage = async (page: { id: string } & Partial) => { - const payload = new UpdateViewPayloadPB(); - - payload.view_id = page.id; - if (page.name !== undefined) { - payload.name = page.name; - } - - const result = await FolderEventUpdateView(payload); - - if (result.ok) { - return result.val.toObject(); - } - - return Promise.reject(result.err); -}; - -export const updatePageIcon = async (viewId: string, icon?: PageIcon) => { - const payload = new UpdateViewIconPayloadPB({ - view_id: viewId, - icon: icon - ? new ViewIconPB({ - ty: icon.ty, - value: icon.value, - }) - : undefined, - }); - - const result = await FolderEventUpdateViewIcon(payload); - - if (result.ok) { - return result.val; - } - - return Promise.reject(result.err); -}; - -export async function setLatestOpenedPage(id: string) { - const payload = new ViewIdPB({ - value: id, - }); - - const res = await FolderEventSetLatestView(payload); - - if (res.ok) { - return res.val; - } - - return Promise.reject(res.err); -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/application/folder/trash.service.ts b/frontend/appflowy_tauri/src/appflowy_app/application/folder/trash.service.ts deleted file mode 100644 index dfbe742ca0..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/application/folder/trash.service.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { - FolderEventListTrashItems, - FolderEventPermanentlyDeleteAllTrashItem, - FolderEventPermanentlyDeleteTrashItem, - FolderEventRecoverAllTrashItems, - FolderEventRestoreTrashItem, - RepeatedTrashIdPB, - TrashIdPB, -} from '@/services/backend/events/flowy-folder'; - -export const getTrash = async () => { - const res = await FolderEventListTrashItems(); - - if (res.ok) { - return res.val.items; - } - - return []; -}; - -export const putback = async (id: string) => { - const payload = new TrashIdPB({ - id, - }); - - const res = await FolderEventRestoreTrashItem(payload); - - if (res.ok) { - return res.val; - } - - return Promise.reject(res.err); -}; - -export const deleteTrashItem = async (ids: string[]) => { - const items = ids.map((id) => new TrashIdPB({ id })); - const payload = new RepeatedTrashIdPB({ - items, - }); - - const res = await FolderEventPermanentlyDeleteTrashItem(payload); - - if (res.ok) { - return res.val; - } - - return Promise.reject(res.err); -}; - -export const deleteAll = async () => { - const res = await FolderEventPermanentlyDeleteAllTrashItem(); - - if (res.ok) { - return res.val; - } - - return Promise.reject(res.err); -}; - -export const restoreAll = async () => { - const res = await FolderEventRecoverAllTrashItems(); - - if (res.ok) { - return res.val; - } - - return Promise.reject(res.err); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/application/folder/workspace.service.ts b/frontend/appflowy_tauri/src/appflowy_app/application/folder/workspace.service.ts deleted file mode 100644 index fe066b7377..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/application/folder/workspace.service.ts +++ /dev/null @@ -1,141 +0,0 @@ -import { parserViewPBToPage } from '$app_reducers/pages/slice'; -import { - ChangeWorkspaceIconPB, - CreateViewPayloadPB, - GetWorkspaceViewPB, - RenameWorkspacePB, - UserWorkspaceIdPB, - WorkspaceIdPB, -} from '@/services/backend'; -import { - FolderEventCreateView, - FolderEventDeleteWorkspace, - FolderEventGetCurrentWorkspaceSetting, - FolderEventReadCurrentWorkspace, - FolderEventReadWorkspaceViews, -} from '@/services/backend/events/flowy-folder'; -import { - UserEventChangeWorkspaceIcon, - UserEventGetAllWorkspace, - UserEventOpenWorkspace, - UserEventRenameWorkspace, -} from '@/services/backend/events/flowy-user'; - -export async function openWorkspace(id: string) { - const payload = new UserWorkspaceIdPB({ - workspace_id: id, - }); - - const result = await UserEventOpenWorkspace(payload); - - if (result.ok) { - return result.val; - } - - return Promise.reject(result.err); -} - -export async function deleteWorkspace(id: string) { - const payload = new WorkspaceIdPB({ - value: id, - }); - - const result = await FolderEventDeleteWorkspace(payload); - - if (result.ok) { - return result.val; - } - - return Promise.reject(result.err); -} - -export async function getWorkspaceChildViews(id: string) { - const payload = new GetWorkspaceViewPB({ - value: id, - }); - - const result = await FolderEventReadWorkspaceViews(payload); - - if (result.ok) { - return result.val.items.map(parserViewPBToPage); - } - - return []; -} - -export async function getWorkspaces() { - const result = await UserEventGetAllWorkspace(); - - if (result.ok) { - return result.val.items.map((workspace) => ({ - id: workspace.workspace_id, - name: workspace.name, - })); - } - - return []; -} - -export async function getCurrentWorkspaceSetting() { - const res = await FolderEventGetCurrentWorkspaceSetting(); - - if (res.ok) { - return res.val; - } - - return; -} - -export async function getCurrentWorkspace() { - const result = await FolderEventReadCurrentWorkspace(); - - if (result.ok) { - return result.val.id; - } - - return null; -} - -export async function createCurrentWorkspaceChildView( - params: ReturnType -) { - const payload = CreateViewPayloadPB.fromObject(params); - - const result = await FolderEventCreateView(payload); - - if (result.ok) { - return result.val; - } - - return Promise.reject(result.err); -} - -export async function renameWorkspace(id: string, name: string) { - const payload = new RenameWorkspacePB({ - workspace_id: id, - new_name: name, - }); - - const result = await UserEventRenameWorkspace(payload); - - if (result.ok) { - return result.val; - } - - return Promise.reject(result.err); -} - -export async function changeWorkspaceIcon(id: string, icon: string) { - const payload = new ChangeWorkspaceIconPB({ - workspace_id: id, - new_icon: icon, - }); - - const result = await UserEventChangeWorkspaceIcon(payload); - - if (result.ok) { - return result.val; - } - - return Promise.reject(result.err); -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/application/notification.ts b/frontend/appflowy_tauri/src/appflowy_app/application/notification.ts deleted file mode 100644 index c63a5d9823..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/application/notification.ts +++ /dev/null @@ -1,157 +0,0 @@ -import { listen } from '@tauri-apps/api/event'; -import { SubscribeObject } from '@/services/backend/models/flowy-notification'; -import { - DatabaseFieldChangesetPB, - DatabaseNotification, - DocEventPB, - DocumentNotification, - FieldPB, - FieldSettingsPB, - FilterChangesetNotificationPB, - GroupChangesPB, - GroupRowsNotificationPB, - ReorderAllRowsPB, - ReorderSingleRowPB, - RowsChangePB, - RowsVisibilityChangePB, - SortChangesetNotificationPB, - UserNotification, - UserProfilePB, - FolderNotification, - RepeatedViewPB, - ViewPB, - RepeatedTrashPB, - ChildViewUpdatePB, - WorkspacePB, -} from '@/services/backend'; -import { AsyncQueue } from '$app/utils/async_queue'; - -const Notification = { - [DatabaseNotification.DidUpdateViewRowsVisibility]: RowsVisibilityChangePB, - [DatabaseNotification.DidUpdateViewRows]: RowsChangePB, - [DatabaseNotification.DidReorderRows]: ReorderAllRowsPB, - [DatabaseNotification.DidReorderSingleRow]: ReorderSingleRowPB, - [DatabaseNotification.DidUpdateFields]: DatabaseFieldChangesetPB, - [DatabaseNotification.DidGroupByField]: GroupChangesPB, - [DatabaseNotification.DidUpdateNumOfGroups]: GroupChangesPB, - [DatabaseNotification.DidUpdateGroupRow]: GroupRowsNotificationPB, - [DatabaseNotification.DidUpdateField]: FieldPB, - [DatabaseNotification.DidUpdateCell]: null, - [DatabaseNotification.DidUpdateSort]: SortChangesetNotificationPB, - [DatabaseNotification.DidUpdateFieldSettings]: FieldSettingsPB, - [DatabaseNotification.DidUpdateFilter]: FilterChangesetNotificationPB, - [DocumentNotification.DidReceiveUpdate]: DocEventPB, - [FolderNotification.DidUpdateWorkspace]: WorkspacePB, - [FolderNotification.DidUpdateWorkspaceViews]: RepeatedViewPB, - [FolderNotification.DidUpdateView]: ViewPB, - [FolderNotification.DidUpdateChildViews]: ChildViewUpdatePB, - [FolderNotification.DidUpdateTrash]: RepeatedTrashPB, - [UserNotification.DidUpdateUserProfile]: UserProfilePB, -}; - -type NotificationMap = typeof Notification; -export type NotificationEnum = keyof NotificationMap; -// eslint-disable-next-line @typescript-eslint/no-explicit-any -type NullableInstanceType any) | null> = K extends abstract new ( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - ...args: any -) => // eslint-disable-next-line @typescript-eslint/no-explicit-any -any - ? InstanceType - : void; -export type NotificationHandler = ( - result: NullableInstanceType -) => void | Promise; - -/** - * Subscribes to a set of notifications. - * - * This function subscribes to notifications defined by the `NotificationEnum` and - * calls the appropriate `NotificationHandler` when each type of notification is received. - * - * @param {Object} callbacks - An object containing handlers for various notification types. - * Each key is a `NotificationEnum` value, and the corresponding value is a `NotificationHandler` function. - * - * @param {Object} [options] - Optional settings for the subscription. - * @param {string} [options.id] - An optional ID. If provided, only notifications with a matching ID will be processed. - * - * @returns {Promise<() => void>} A Promise that resolves to an unsubscribe function. - * - * @example - * subscribeNotifications({ - * [DatabaseNotification.DidUpdateField]: (result) => { - * if (result.err) { - * // process error - * return; - * } - * - * console.log(result.val); // result.val is FieldPB - * }, - * [DatabaseNotification.DidReorderRows]: (result) => { - * if (result.err) { - * // process error - * return; - * } - * - * console.log(result.val); // result.val is ReorderAllRowsPB - * }, - * }, { id: '123' }) - * .then(unsubscribe => { - * // Do something - * // ... - * // To unsubscribe, call `unsubscribe()` - * }); - * - * @throws {Error} Throws an error if unable to subscribe. - */ -export function subscribeNotifications( - callbacks: { - [K in NotificationEnum]?: NotificationHandler; - }, - options?: { id?: string | number } -): Promise<() => void> { - const handler = async (subject: SubscribeObject) => { - const { id, ty } = subject; - - if (options?.id !== undefined && id !== options.id) { - return; - } - - const notification = ty as NotificationEnum; - const pb = Notification[notification]; - const callback = callbacks[notification] as NotificationHandler; - - if (pb === undefined || !callback) { - return; - } - - if (subject.has_error) { - // const error = FlowyError.deserialize(subject.error); - return; - } else { - const { payload } = subject; - - if (pb) { - await callback(pb.deserialize(payload)); - } else { - await callback(); - } - } - }; - - const queue = new AsyncQueue(handler); - - return listen>('af-notification', (event) => { - const subject = SubscribeObject.fromObject(event.payload); - - queue.enqueue(subject); - }); -} - -export function subscribeNotification( - notification: K, - callback: NotificationHandler, - options?: { id?: string } -): Promise<() => void> { - return subscribeNotifications({ [notification]: callback }, options); -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/application/user/auth.service.ts b/frontend/appflowy_tauri/src/appflowy_app/application/user/auth.service.ts deleted file mode 100644 index ec258abc87..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/application/user/auth.service.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { - SignUpPayloadPB, - OauthProviderPB, - ProviderTypePB, - OauthSignInPB, - AuthenticatorPB, - SignInPayloadPB, -} from '@/services/backend'; -import { - UserEventSignOut, - UserEventSignUp, - UserEventGetOauthURLWithProvider, - UserEventOauthSignIn, - UserEventSignInWithEmailPassword, -} from '@/services/backend/events/flowy-user'; -import { Log } from '$app/utils/log'; - -export const AuthService = { - getOAuthURL: async (provider: ProviderTypePB) => { - const providerDataRes = await UserEventGetOauthURLWithProvider( - OauthProviderPB.fromObject({ - provider, - }) - ); - - if (!providerDataRes.ok) { - Log.error(providerDataRes.val.msg); - throw new Error(providerDataRes.val.msg); - } - - const providerData = providerDataRes.val; - - return providerData.oauth_url; - }, - - signInWithOAuth: async ({ uri, deviceId }: { uri: string; deviceId: string }) => { - const payload = OauthSignInPB.fromObject({ - authenticator: AuthenticatorPB.AppFlowyCloud, - map: { - sign_in_url: uri, - device_id: deviceId, - }, - }); - - const res = await UserEventOauthSignIn(payload); - - if (!res.ok) { - Log.error(res.val.msg); - throw new Error(res.val.msg); - } - - return res.val; - }, - - signUp: async (params: { deviceId: string; name: string; email: string; password: string }) => { - const payload = SignUpPayloadPB.fromObject({ - name: params.name, - email: params.email, - password: params.password, - device_id: params.deviceId, - }); - - const res = await UserEventSignUp(payload); - - if (!res.ok) { - Log.error(res.val.msg); - throw new Error(res.val.msg); - } - - return res.val; - }, - - signOut: () => { - return UserEventSignOut(); - }, - - signIn: async (email: string, password: string) => { - const payload = SignInPayloadPB.fromObject({ - email, - password, - }); - - const res = await UserEventSignInWithEmailPassword(payload); - - if (!res.ok) { - Log.error(res.val.msg); - throw new Error(res.val.msg); - } - - return res.val; - }, -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/application/user/user.service.ts b/frontend/appflowy_tauri/src/appflowy_app/application/user/user.service.ts deleted file mode 100644 index ec64fb810c..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/application/user/user.service.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { Theme, ThemeMode, UserSetting } from '$app_reducers/current-user/slice'; -import { AppearanceSettingsPB, UpdateUserProfilePayloadPB } from '@/services/backend'; -import { - UserEventGetAppearanceSetting, - UserEventGetUserProfile, - UserEventSetAppearanceSetting, - UserEventUpdateUserProfile, -} from '@/services/backend/events/flowy-user'; - -export const UserService = { - getAppearanceSetting: async (): Promise | undefined> => { - const appearanceSetting = await UserEventGetAppearanceSetting(); - - if (appearanceSetting.ok) { - const res = appearanceSetting.val; - const { locale, theme = Theme.Default, theme_mode = ThemeMode.Light } = res; - let language = 'en'; - - if (locale.language_code && locale.country_code) { - language = `${locale.language_code}-${locale.country_code}`; - } else if (locale.language_code) { - language = locale.language_code; - } - - return { - themeMode: theme_mode, - theme: theme as Theme, - language: language, - }; - } - - return; - }, - - setAppearanceSetting: async (params: ReturnType) => { - const payload = AppearanceSettingsPB.fromObject(params); - - const res = await UserEventSetAppearanceSetting(payload); - - if (res.ok) { - return res.val; - } - - return Promise.reject(res.err); - }, - - getUserProfile: async () => { - const res = await UserEventGetUserProfile(); - - if (res.ok) { - return res.val; - } - - return; - }, - - updateUserProfile: async (params: ReturnType) => { - const payload = UpdateUserProfilePayloadPB.fromObject(params); - - const res = await UserEventUpdateUserProfile(payload); - - if (res.ok) { - return res.val; - } - - return Promise.reject(res.err); - }, -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/add.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/add.svg deleted file mode 100644 index 049be05cec..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/add.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/align-center.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/align-center.svg deleted file mode 100644 index f4f4999514..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/align-center.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/align-left.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/align-left.svg deleted file mode 100644 index 23957285c7..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/align-left.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/align-right.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/align-right.svg deleted file mode 100644 index bca2d14fc7..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/align-right.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/arrow-left.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/arrow-left.svg deleted file mode 100644 index e4ab9068be..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/arrow-left.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/arrow-right.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/arrow-right.svg deleted file mode 100644 index dc40ae52a6..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/arrow-right.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/board.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/board.svg deleted file mode 100644 index 0bb0e3fabe..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/board.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/bold.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/bold.svg deleted file mode 100644 index 878b6329b3..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/bold.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/close.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/close.svg deleted file mode 100644 index b519b419c0..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/close.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/copy.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/copy.svg deleted file mode 100644 index e21e6cb082..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/copy.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/dark-logo.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/dark-logo.svg deleted file mode 100644 index 80d8c4132e..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/dark-logo.svg +++ /dev/null @@ -1,73 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/database/checkbox-check.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/database/checkbox-check.svg deleted file mode 100644 index 15632e4ea6..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/database/checkbox-check.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/database/checkbox-uncheck.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/database/checkbox-uncheck.svg deleted file mode 100644 index 6c487795c6..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/database/checkbox-uncheck.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/database/field-type-attach.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/database/field-type-attach.svg deleted file mode 100644 index f00f5c7aa2..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/database/field-type-attach.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/database/field-type-checkbox.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/database/field-type-checkbox.svg deleted file mode 100644 index 37f52c47ed..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/database/field-type-checkbox.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/database/field-type-checklist.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/database/field-type-checklist.svg deleted file mode 100644 index 3a88d236a1..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/database/field-type-checklist.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/database/field-type-date.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/database/field-type-date.svg deleted file mode 100644 index 78243f1e75..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/database/field-type-date.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/database/field-type-last-edited-time.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/database/field-type-last-edited-time.svg deleted file mode 100644 index 634af3e361..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/database/field-type-last-edited-time.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/database/field-type-multi-select.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/database/field-type-multi-select.svg deleted file mode 100644 index 97a2e9c434..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/database/field-type-multi-select.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/database/field-type-number.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/database/field-type-number.svg deleted file mode 100644 index 9d8b98d10d..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/database/field-type-number.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/database/field-type-person.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/database/field-type-person.svg deleted file mode 100644 index 2fc04be065..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/database/field-type-person.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/database/field-type-relation.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/database/field-type-relation.svg deleted file mode 100644 index f82a41d226..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/database/field-type-relation.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/database/field-type-single-select.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/database/field-type-single-select.svg deleted file mode 100644 index 8ccbc9a2e3..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/database/field-type-single-select.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/database/field-type-text.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/database/field-type-text.svg deleted file mode 100644 index 7befa5080f..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/database/field-type-text.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/database/field-type-url.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/database/field-type-url.svg deleted file mode 100644 index f00f5c7aa2..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/database/field-type-url.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/date.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/date.svg deleted file mode 100644 index 78243f1e75..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/date.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/delete.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/delete.svg deleted file mode 100644 index 9e51636798..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/delete.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/details.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/details.svg deleted file mode 100644 index 22c6830916..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/details.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/document.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/document.svg deleted file mode 100644 index b00e1cfb38..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/document.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - \ No newline at end of file diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/drag.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/drag.svg deleted file mode 100644 index 627c959f9f..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/drag.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/dropdown.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/dropdown.svg deleted file mode 100644 index 95e4964b53..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/dropdown.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - \ No newline at end of file diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/edit.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/edit.svg deleted file mode 100644 index ae93287114..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/edit.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - \ No newline at end of file diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/eye_close.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/eye_close.svg deleted file mode 100644 index 116c715ca8..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/eye_close.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - \ No newline at end of file diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/eye_open.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/eye_open.svg deleted file mode 100644 index fa3017c04d..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/eye_open.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - - \ No newline at end of file diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/grid.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/grid.svg deleted file mode 100644 index c397af8130..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/grid.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/h1.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/h1.svg deleted file mode 100644 index b33bd52135..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/h1.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/h2.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/h2.svg deleted file mode 100644 index 7449c57391..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/h2.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/h3.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/h3.svg deleted file mode 100644 index 0976945974..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/h3.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/hide-menu.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/hide-menu.svg deleted file mode 100644 index ce88af8ea7..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/hide-menu.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/hide.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/hide.svg deleted file mode 100644 index 22001ef65d..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/hide.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/image.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/image.svg deleted file mode 100644 index 0739605066..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/image.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/images/default_cover.jpg b/frontend/appflowy_tauri/src/appflowy_app/assets/images/default_cover.jpg deleted file mode 100644 index aeaa6a0f29b2dd9639999e5ad43f42c39420caad..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 281498 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 z Ykz8m(?jPNvnt>meuY>tjb8Aeb&n>H6U&jxCSE%Hw`g?oeRg3lWOUJy`>mbXC! z*zriAAwpJ_TVmCG;d=lZCI1wn$ZFNAre7R+H_ima%s~em za>+}$_SdRv$#`^BCdWsg!RmlxvAGFfSrx?d2*NH;`f#}dmf*DmfnK0ZVv-PBv9rD?l0MZ9?j7r%Dqo`_dMZ#*cDFsy zVbzOJKz+Arzdgt^!}u`NbOsxBQT|Wk>0iogKYtQ^(0c3fGpnpjthw2XTIt`eUFXlG zV(=`9wyH!|$<*j2LJGHVUBsdrn(gw*f#S1QlNXeeLpkSeM3r z8?$#I$%nX76w~G{{J|Wr@vdH&E^T#o@H{>HM|8#JQ+-_HEeW}Og%@+!Kkqj;SLPQLV8@8hqUY+0wl2Wa(Y8CQ>Fkfw-Joa-HN@kJ~;p=#~4CB)!3W z8nwuyOjuZhhLf+89!#}`;L1J(U$1~`6cz2a5qP7Kjrn$pTD9HHkCfl*RvpC3y<)j* z2l=a_+A}P9?WOb}k@p^EGdOoL*R<$uSD$TtEe#y2{H)t#tm)XVc^WmirWp=0PtE|v zcRJ%ayLiM20Aj-y`BusBLsFw}zwaG?CI9C{?;c2Kn zl=?kVSg*4#>X?#XgbRvOmm==k5#O!puQYhP-XHj*mo>9N#VH;tkeGyc$e*0!C2QD7 z?d?JN@%wWQ+=kV`W>ZrJ35x?JF-m9^pc=8=+atbj(r-OUrYN$ATi9OeTWaw(w)$k> zFjeCFI54xE%F`Vp7r{XQVy2CA^=VYkGS!zWfUsOWn2iF@4e@3>mR}M{Yw{RVq^?7) z>d@Ww=w1u6?TNTlAELV@oSR0SCnJ9*JT&s3*6F{(ta&R7K7V&PbGwRDR|=h&iA`6R zC+5=iXhbj$v)dq|Mhf&*^0UVaK&#xZ3{1gIjjnbalGjj&ANgTXhqiY+Cr9tVG7{Xa z)1C6dx)roUL=AMZ&EC|rEFs8~yEq{VmR!H$L;gkZx%kPa(vIX#1KykJ@i1j0?k)(s zJPsOrB0Ra#J=j=Nm@QT-V^*~9D*tg}@F-sWzI>tZ)Y{y{cch9>`QO+0ck!lIZvV7U z{tua={|7?3Mgx?{C)wv3DoXC@xc%*=#CX_U659;?5x&t%JAhTNZ*Uu~ixn-XeakME z(%r!~exh#^&(z%X&HKx%Y|8Gi0ik^xdaFcD|o8_V* zhZP0Gx3dvKx&&P7qceNM{e;Oa;}IXPZaI{(A9)AQrN+$Mm3Z$FsPCbA+8*h(Rr{lLShYJCDqJ0s8p^F7-#1*d^_I zTAUATWi5>7&2O+nMjTzVWd*G#u8M*>b+y{ckin9xNN@meF3pT@wg5+#L78JB8PbK= z6MB^aB;}%=t)V2nMT1~b_Bx#I%Q;0nmX%V|wu8r)ft9U_MXv&N#> zOpqX8T9F`XT=a8VfxK-Jo7nbK|A#%JXd;5oB8UmAhc|4A6gtflzozhmtF0i5bGj1f zqMlM+xf$Q>4ADUD7D~aUd^3;M2Lrz)n@k_FFnXWRJgw5m}z(e|K2lsW4gmeif^81-c$0dDw!JsP1pDmpCs9OWRSN^2_{A?ZT= z!2sj@0ypoIQ(BPk*`Sj}no`=Zh8M;m^+kt4tM+>7EG70)=^Th)m4H%Xq%hXk($XSA z4%Jt=K8&3ry$|hXx?c9lLnR|B@Qos5?7(`|d|cA&l$si0=1)6S5hrHomd8TUhP^q_Ay>G!Z^O=8zGfd*vs=SU&W90yli zjdahqG+?Ur3Vm#tz66m2Pm-o8(YIu7a?1OlO}@4*04^D zPbjZ`l=K{w`3;g4BwE-fq&tY_F#^ML3&DTY>8$dD+t(%Is02v=+W)rWDX!HcvZ_%wR*1Te%ck z&oH#FUEbtVM)vOOks_Qp)MO?w%o`;SJBh^qlJBbA*U?Q}4GABxM}D%x_)oc?$7 z>%TJ-d0BleLi$yW3kXS!YltjgI>wFJy+h@Gy*!YH)mK@nV~m4)U&3uupmm{m9;qFF zPcKa#U?i|<30(YYoc3Hs3@thExrLfG!wC@oxtMQ>7|q$^Pu2`mZ8z4Hhtfr}{Q(EC zqKLwRvmJbUE8<#6Xzk@glFm#h?*d7wvuz?Vju}x^phmJVY*{WuTCsHuIlg&4BYC5j z?ftFb=e3QkEtZ5Jf8PzG4ZtQ#08a$_5PXMHz{keYM0`?%(xt8Q*x!0Hmr z@(h0{F(?b4E>Ul|Q^FJO1JetvQ4&Mn$l74Sld|$PIBM8ND?%Tp5_!K^DRR!NEwZq3 z)w(z38mdH6l}IoQN-2@7%T9Bh=_onwR&u0qyN^ex%$5^vRm-8&QH6V3mm2?~{P%we zRUuIH6fjs3kv*mld5T<9G#y*R7p<1&7m*oih(#)b>K}RvjR)(omfxTJ{3C%Y6uCMR zZ|k*c#OoBw=W|Dt;*r{rhy(sJ5MQ)%^s5+^=pPU8ZGPz%9^t8GO;mjxEO-?uL>p5C z0VXe;^pEJZ(=177cKl1-{;Xd`qhv44%b!b|7MYv%n43w%Jhge?C%#J7Bg}kMlS?z5 zh6v?}-0dl(ihnB5trNq`Kr$d0VW@GEOuEhz_O)?FwY3v}CNCIO*T(8P-?e!p_d_=C zcvNm&-{0T&L0!(%4l|#A-rBX9q~+hP_>|_k0&(1)RZ1iUaMS=mqP*8NJ#_Rm2h;<` z;MT&2@UyuP`}mCdK1XR-IWad>gEPL;q_H-gZ)Z}fPT1E6ka^UweNT=gxFbcdJHVV5 zz{I|Bb&#H2G=E}Ryz2lOFN*rqEIfwL%Tr-{&1BE!4+CCKgrA@BY!jXEKW z_cfUIZrAHu)7f$Z1`C!;5G!4E#3y+|u!N0no`n57#ZmqYJxOu&V=#m2$D4~AwI2Hqn#=z%*vU?|HOtn`Evy*YKHy6uHqng&mgZ;Q=H z4zES^TcDt{9JWVsq_DU)+EsN0T)YO03(T`?gK3&W>8m;`E0q$>vXcC5`)Ma_*z0<>kd5H#aw$8E{aLsm`tT`ZoHy8CFF_Mwwn3 zDGXX{LT{JL7xkd}nL!x;x5Ah{09jN$>476;ww!DMD@kM$9fQGnOqy*Q7wKi^Sx@bn z=h_?Q@`2Joy4oTGfZS%6TByg6yG|9b-8JZ+@(eJ^2kV)tzqyTqk#!m}ufJR17p?`zt7bYoFlSKYRBHUO@)@4SPjfEkvSgd8wwkd60$%~bAy zXTbCh%2&hXBP?HX4J}FNF}pf_{>m(j^97nR!bSjL5Y~<6@U_s8NziR;q*Hu1u>GKP zVhDefj5L2Z*HksS_?=5l7EhtFPY5C-`G$ zNFWcRUtCwH4oKb}tCzE(lDkkERSj<^q#NYs6iJVKKc~mUO=>IKD=F$A@PXv7mPC}< z_;0X4@4G9ub}M#v@F{hsK{w~|5k4xw#}tT&Hp??Vb)CuaOSA0xXp}W#=q`amvOnmf zlX8Q`zrRmj4V7D7s*H9hAKafbUf*b6NXuPhoEPEGSlfRivI`V43S9Ds0(jvt z2WV30?W>}W8@mb|IvB|t`lSMOD$@m5Xl#3!_H^B4f)E5h6LaG0>pGtvQR&v`zhI|z zYbm|ZO>wT?fY*3JK8N;!Ngej9-=2co2h{uC-oXU3s?~EBO6{M80v7Pv_+)@TiDuU} z_KVx-#R{*v8qwJ)8onahA=tH+W#XpC4PLpSN^)(CCd0f7h{Di)zLc5PMHb9t!xVQKp3g4Efrg5VjO>utAE3)$#D+lCFL+}IPZNPi;WT>Ih)ivI=@r(c8cIt% zUWIkgl==?5UO%O=#KM?7Zsvx)f&{*%R!oqjCg5- z)GZzeb~!Vb%~S!+@4$5%X{D5oln?6 zS9!GLk!ve<=EINdpgY+OWr5;nMWrjYgTvyz>-*o2Z5K!RUt&~FQPBCWGIx{y5~6m0w>ldu zc--jfUB`0IUYJHIz5|@%;NgJ(ShWMBn0UR-%@R2L+)a?W!Vl+~`i@F>%sw}&SCJtU zf?ZChNXYy0O*ve9@no8kLQcFmTk8XH=S@+`IB?j2^#$J)5bFLxrUU}vw*;?>!~UW9 zS^?ZYSVouPG3>bK8O){+au=`4;+m?Zo`Ttm%z@G_|LLuc(g=!&)n zingr15)+EH$=rDIhd?go zDoR}Zwyw|Oj5jjmzOM@@kNAxr<*ti7;7@rK8*dW3LJc%@7aGw%lsC+Fvs*S8Xx1}% z<0PW69OLmKSywV2RksDe%wF;0Z8{g4Lc2u)sr(0&k;n$;<@q%%f_Ci@R=<(h_V=~h zkg<~#e0`gNU>li}hmnUfjQDummBf)RWJGh{O%!+Uu2`t|mHA*;eP_d)JZQxwv?4^( zwWIPh=lG4|+>!sKVc4?G{G|qvBjqtVlc3mLndhoKTO`k#RwufJ30LIO*IB3m}e($K2I+rh3?lTXG8TbX`j*f3HPdRU&+6nHgfXC(O z=sdC&Y7v8klER|h>^qfZZYl{)>6GMQ*wNgQHKf-&kd8;=M%35pv}((y=5J`Gl27L^ za-G<_jWcvLed9mvO-%taI&4gjN$GL2QQDo$5lTSX5r`U743DCJ5y+-kxCtAR}m%6 zT&Cwa#>U}6UP}!l@J{g_r@D0`x{>qsVdcr>)bHbUra--Vs`*~3#eQqU9AI67bS?kK z`qjTGum7L_6^gAe5~B_FWghPDYl#_9#x|30HZa~*$*6;ldmceQ-Yb4?oaT$%(YV8z z_9Ff9<@LrXxDgHEH&+4t;K%!|+Mh!kS2uKZcrmB46+vP$BP`u!!!`2m{M$Wui;SP2 zPZ*$`2B4hmS`e8oaZ@*q0Kt2rU@cXQ1x9Ky=xCwACO{GG+|rRHd2|j z&V5`x9=|2B6#>B0EqHAf^8IHC$+k6CG~!?lNT7 z#(+<6{#C?v8(uoMhqDsv<0lX7)W1kQ>B;dIN{<*!dXI}gYGm;J)g#ZsuS0{?1R<;bdkyq#n^7R+J`Pjf}C4lsz29R zOZtOofAH;Qzacq*Z4s%FHd6E+1DtT(sf-3Zx-_i*NjAE123Pf zE2=|cSR~}B0yHMzb1Y+0vkkYdwz>3Y1M2|%OXEbInYi9E!yTAe0rB+mSx4Gj)XjA> zauOG1671=5+-Fy3p8L+T*6En_HqlJIdM-&C;N)RD>j|aTOt3z$Uv%&PEsaKlAAkNG zNw)Z1IWVOFA-6+iYfyKiKJw?+^R`C1U++T6)@>P@RvIN0-BbCP1*_2H%GIC#gM^ zkFgu*_#W)cp%#x-V&w_Og>@nW9ly0UtC`#Q`abuP&GcyzOXGxs_kj144Fa%t|TiO zLlwW0CUjQ1vwk|A3Nyq|DBF=UoDeOZcEn zd@D@~Dz<925=+UksVS*B#_^`9 z*}AnGO+&Fqp*OJ-f@}X5b?*VyBxbi5G~n6r2Y`!KeaU@_5i%|lHs-q}o6j~?0eg-CGn6RyUZ#Y0 z>a;>Xkkzcim#h851h|Ru!8Am7v|4@Dm78rO)3Ty&8q|DbGqXmCW`20Zi1)oEi#*ol zjCQ% z79nKQ_=uvUOBSt&8*(}S{TGE{*#FFv^}lW+|0gi^|F`!^_tk)Wf42Y2`Sr7>i2fBg z4q-Fi{)lX_%g(6Z18}l#U&Xhw7j+c%+)fss_M@ZxN}dt$L(HE&uN@bS8_`0eciU3n^TrtF#Fh9;~xd6Zh7%>=NVk^;HM|u&^AqQk? zy%;!=FBb4+4mbso*asV{+jZesagG?t_R&m_uV&|WOW;vk;~V*+#vIM-={51|!6l(q z0O?6`0mdIuYV3R=Z^_i_$U?H zWgh(c4k^^VwSI}ULEyI_*RL=7{O9FCGjwwzhkz_TreU?hfT=P1$}U-?)1!3#P_<3` zmAHL>W3SPLd1s~lfYBM3EQwRYT%=hsf#0}P+|t;CY@Kv&a}nMx1*BsJ_=rE;$XA0p zj}MkCY@=oUlV>dRBO(t`0x>mhGGsjNG#?c6f7xpOcdOv)5f%m_4&@Q`#khaJQr-@r z8uKkK={>Ro{vIoob+t{xDIYp7qa`Atnl(+;*@MB$Tw5;Js&4Fe1oQxQlJxi++%y%U ztI1xpbyhD+HH1AQb_!9U{-U?KjdGL@g61%O&RXXk-}=CSy?rp{Y!5R;> z=B_$M7dS0fFMoQZcT-)|foy@lXBzqdJ$dG6HSy=Hik4aa{b#`%#amHp2E*w$j6Ls} zL4W>?o{}UZZ{lGHxV58b>J_+Uh1^ks$xVWnf5ZIgSC{i`Ked-ffS1PGJFP4GBA?(G3atpq*Foa<~-Ft+-6RaZ6D8T09-!k1^U3eVQ&qkIh?WS5H3u z*X`}veM#X@cb;6MfBR%5>`|B^PsF@4)}c<%J#z~n2^UT3Z&t9oI<$_jHP>hH=lBkU zy?9$=F)$_V_=90`IQfC4rvXiZBo5a}D2OY>`0etflyQ|>52x=B`5jmkh0ItdMoCB5 z`<8YJ&P1Dt1nY$I?jkzOmPigJemVGmZm_OZJ(1n5zFC+tM(j)2*p8&UI+aTq7&{dy zHv;jB2pR;7keo&_FyDd^(?X&C)^8u$k7wGoE^tv28=cwxPSIyQI^s81>GKzk=y>Qi zb45lgvTRDe8bK-~_=f1R3vru6Yyyk>t{d;cP-7J7?&a3E=uvOzp;tue-qe=pxhfWB zlPTkK|5AxKNOvY>H^j9N{&DB4>j2

1rhuBp5382z!v%`%^a0uDfTQJghm9_c3~Z zqSXTKLtY-}RqOl0y5dPmK|8&X3nzU3mofi8Z7J7$z#lb5$A0o8e)ITgsLTD?M4h$} zfAbt@YT8~-SZDa+h3g*ZgP3&s=#Nr$kdANlL<~f0l!k^L{?&hh{U^hb&tce`>LBWW zb{zSJjMb;d=rgVs{*&VfM3<<&f<0#h9T#}SAo){Q8p7o60f$CEfX~xq9^@=)KpCfE z;VfufU0$^(y$NScQHZ*iQV7lLX-L&$9KbsJ-#*t}9g8&p4P&~LRD-@XY5HGaV5krH z3avFuYWcgQ`2*k}XC#eKx2@L&0n80F7-ryc1I8UCumXs5=~JHw6f zY;SFVp{}opJxb=iF;>{CL^<>dFx5D&0T?PQ34mN$c_m%%R<&j~iA8-A9 zNQ;uF=;4sYjl7r97bcimsiZs5wSXDdm@%1G{R+J;v%_9!k`dI+=(xr^QcwX`^S2NB z>>T!HW%SS4D9h`q2n+n)4YL2+CxFB4(!IfbYSEL2`b9!3lC_(*KGeUzK*1$sdP`5D zjN9sy5wPt~c3y1mkEq_;@NW`4 z%`z2B8K6I1K9{h+ZOd#0l1$JS-|KS$YMF+?8*A2cAz*OGMfM|=b&AtP1M!Tu{XO#a zMfSOFTqnb6nS0R^_K)e0xT#%_oAaXU_#e~bYKNlYeV+l+;q6t4$YAZ2=(u1%yaMx+ z@`E>x;Vdb$J;IOzUD;0UREE@zMwpMg;irug`G*ptL}9%V8>vG($TTCNV*<`Zpay2< zY!3brDZZpeC;dY4>|z2>lN2NWO!lFxmhXm}(ubYEHl#zy&HusG=wBcI{^!lke_MtB zpWdP@3>G2Vm!F%FAY^OJrrIjRj<}!2+1avWr#*Ilz~fqKfZuE;0!eZ3 z$m=B6>P@$eQ}MxUpSoY^=We44c-|-SI|8#29!oP6Q}aqqfbG`%MKz1CP8%hrSMWy+ z`fW~-V>CNfi@qE0CAVdgvwKTA;Q^#DUtfo$i_fhi7W7KsL);0Y$J$j5)1ngP&pI62 zCMz+GVQf78oHuCjS44iW5?Hxs%DWf)xH!yUHxGuW3 zTNI}XogFpWi7L*-ed`!yWPV{M^+y*Ln0~V7yLlP;c16u}WoYT+Gibfk>vc~R5aABC{sFk-u?>M>K}?zQtY+q`gK zL>8cdl-2zmd+O38;ldelV?30>=t?9`mtLZiT()_&%aX6(n9O}IYED!|NtvW^+^Wh*;$aQ+^D>KOJEIn(^|xR!Q7 z*$Q;8!EQ_Ix!#+}O}~@nx{a{Q*u_SzG47(dwCXbzLgmEPr2E|$=sPf%s89{HN2g+V zMI(6v?SsD%WxRxDa(R1_U5Zh^4JLh^EGkBA^6G}n?PM&KqV3;4lpRd!7|j@Lc#kf< z_I0DViM(|$6m+`OIfM80K;_MIl}^dCK+lI7%7*TwOj-c)PTzi3`iY}=T3jMik6}9GSn^HnX5HU zCOsN-Z*sX(x>t&gBZc3`O^b{K9b{*F9v0}8R-)m7wZ=y2DdgnGQ^6uQmrKs+3B@B- zhUZO_9;R&9KAs#`*FGZ-{&wUa)3h}XmGxS_zR}J}%3~mMGZ^M;0A3P% zl7IO)*|>`8Gp$PR{7yU)#(Uk8JSM}=j6dhPIe_8n3MAfEe5xoQem(~gX_I(y=wt|k@#uWv{GV`W5TS#y|?`3D;o#-nc*KP;U1SjI{PfH@F8sv9rWREVz@*0VLTOwj{ z@?Pfpk%)%GV0A*h>{>$KOcXmhSf3~OsMrQbx@F8RBUCZkK#I7c>wCt@xI2p5Ld5N; zDiz(Vi5`L=q(bAi232?hUrg_+lsGt%RkUyD)ysp}!DAb-%yD{c{~V>qv5Jlk}LIt4Oe z#2-Wno%habO11&vFRx4h`WlA4cDV)zDC-YhjLMkq z*(-ZTIO;z=xOEu>()fE%#?S6du7YB@Zsql5!V3z`H6AYYf7qO@0L{xqPku|e9loK- zp<$co$C#EjB7eX;{@0{fTVqlHs3rLJDFwM~bu6NBjEU^8ici2&!7E)gBKB2g5dBGn zxC}8DvWm~?413};aRYFXT@)^DMIipT8W*1rLu4X2?-nHB$O`v>w$|tmL~}CLx+3)? zo9P{3;6eKGblh0Fan(k@?)pwCkkgr(#EwzXLyUhj{yr6k5k5NkKKYA6F_|m~c(?kU znINZ@fhnVg>LwAHSE94B94!wm0cS*+y%AhLj#Fndn(ZIKrhfdnN~j zL?!6(Ui2y0m~MOHsykR6Q{UMG%9ur{xD;V{Og>^w^qNbEfp7xOY3&PC@Ofsu?mL1- zuDEC2s`7rk`>dKUZkqug_6Nv6H~<(!Qe2b;Uje=o0ZCEV8yBP!+0hTf9Tmvk*XFvi zTi85y&JUC5%A^0X8Q7OoQQshz3G|yi%t-EOo7tm1YSFj%}PbGQougC zm{P_(Y1y!D=&di64t1D-4JD>tI`=%#p`$V?Ke-N(t=U9}nWy`!KDwRv;V@^8&k zug-@TWb;#3Tvn^-v+;UeQdm6!<-eIacM|90rk5b8I+_Y)wbuNP*mSP|>9nP&0$Sq& z?l|w%T3g(Q9}9E*N3RWLLsyFOhe!7U ztTWa%P}-$oor7W?01~FHw-=iT!wN#39|?Ej22l zRYTHfn_s}{GSl}on0tPu2EhBdeft34cBAVaY;Z!={l`6Z!hwkbbCvUNGc#6?gNyuo z8#z;1v?*bUfky&2r%N}z0iUKt6nFbW}P~@m5rPP3~Q0LfBan4dPUQ3Sls7PbU~qxz6`TsXQqHlLq*6DxH)g z`_N@(1H2Nv;>YR7>C8V!Pf0ae;73B&E;NudFR0``6s$Q^!m7ku>mxKbv zJ}>_S0)}Nt8fvhf5pHVOhPS5P1}2f(QRf`^7KN!`O~Th8ZQ^J+w)YZP>af|m+aV&l zHOb)zU#+d4{N{;tP6V4CL1&-yHS+Gvo}7^g4~Em`&s7)%w;1 zLRXfT=bJz)+2f&|8tMHY>SZmD@YUm(tQCAJkZ>6>2zs;oCn$lX*K51#i!Of7Z<(wf zoW}u*Tr#^0qEo4=uUFBgJAXJ4oPLT2colA;c0t1D%jTykJ!iQ?+rgJ(bNkjg8uLr( zwX@4-oXOch(m=zqD*xf($5C59`6@r<$3Bx9(((Q0!H;qzGFkx~mbj`Xpfu={sHiSfixV0Z z-o-ElD7+kfsxu>Uc0;QP;eVzqmBoletY%LJ!3a)7$0E7WtqGWmw;b?o)-Dw~a9ndBD15uw)rPs(M+ zdwoboSt&qvr(|40A^r zMBSw8LKJR%V;~^;2BnQLJ5^@ILo9*^5{_^F%n5#Z@}X@m1fKJB(yeeCoE?yw;)w6i z0kKG4-atGcaL4+T=s2*^YQWQJKbL;5izj5yJRfZIpxbfP5!K|&T9s@}a1+$3c)B(v zg+>d%D-g_62X+vxOL2>H@WqFtWSZ~ZeL!9>vX48e`FceomFvA@U;{Ih^(s%nzRz4W z0DSOK=C5S+KtEjud)sSz&o9}=SO}5`hmIOD66O6$^nS&4DDCp>%M=uPhHr>Av)cFL zRNK^K$HB(VIx(3Zj~kyAAUykl*6!nySS>@O<`Jss@|aX;J#JRAy`C1nozc~Q z^qo0S(ymr|0Rbyn3k|~e+d?3OpfQ>OPjiXf)79T~@^fjJM-NV|J3a1rPB}qV7749R z1EyJjRmrg@DxC!kVxHAwwB2Eqts6!+HvCMcbJ@VsN$fcWfc17DIvd|SS(%4&?&zPF02q~*cjqU11lHK~LN2F~8k1fA(HHh3z-6~-z2h?C z-7gB|3`beD5n~1pFAr)lI`+Ml# zSFfqzDztbTjweJ2!`%!U12Tjah1b8FHozC=uoEa_wM|#U&=rSfZBFoA-lv~^e>c(O z)_hKJUX}zwEua7y@Q-hcSx4?W2Wx;8xDg5~02qY`flL=(h3Gu^M~wbTI6*i0;j?#7 zx-E4$ug+e};3f*O8D0Kt#A%CQccr{MTny3L+r<7Ks8o1#MXKR(<9D61Qpp_XR>jTp zsiML*x>1RR^y5g6H`X_Qh{=i+M)&`sxMOX`^>XO-8Ltzax5yOZRGzrCB!5cZc^)re%O~E3-Ohwt7_R zz4B4b&0?^LcW{ezcNZd%9LE|LI+7*xNTx-mrONR&Uq(ifc}9}1%v~LTGdr-}F%|`| zZ5G^j+bld@FPn~?TK*HeHo@kmrq-P}!vs4fr#>lDLx5BZ{JA;R1KXW zBm!0|zbHOZb}ffJ$9+NGM}9ti0|;Ho(^Woh9|R|kVqg9Et=tH<*nbN+YE5bgWKb{9 zE%C(izPkAFJ5K>D zucku#Oe4BJfV zMwVI1mq?!4x`W`Ll^oEB_UEYz8zT zf{g~k>%A|fx;&wAdz4Rnrv9n|(%w23p^3*XLgfWAJ;wO#ZZ_yVLD;4y>E4G$Npn45 zZl!~`(ieXvxrdmm)*;TpwIm{XobuC|@Fy;|yK7I=H1o8_w&_KV%CoNW!w$&WKZsu*%u`4$hsSoeouKamq+$L5GiYY?>>9p#&$ zk1np1Y4#aN+8j>)t5>7_gr|(1An_HkIKZmd(fVF|udOSmK$jBbwQp0>TpXXYucRB6 zKx(2@!P8{~SJ`b_FSEFboL8EG*&pL`bkxAco6i9px&WN51U_Fpef%e#DbQB-jm08a zi0H8}S%bT}j`;1!)B&`*uso&3dCOss2JaTP9CHhn& ztUYZBgb2W=J?avtUmZMwUbAyv_k9p<4mXG$mL8V(7p@s0&dGblf0`NIZ&uuTpP=b1 z%Fmxo1jJ_#S<=TS<0~Mo9rB|<&JDm>Y0QmgSwFe10uN9(gQZlG%m&SZO)PK4bx!xK zY(k@0n3Uw-7YHE=8QYnc+e&FC4lYxEw zCkI7#VvmiKv48{W`wW(iPjHYyr>jta3M`Qy}@tfCGBFUIu>jhs$mq?cYeM&0e%@?=UrjIi& zNE~)~QJpQ$KxZx0C(Kv?WiY*aY>eOWeB1pQwK=p(7Uwh)0#2F{8(?2X7VKw!&<7~IRZgR2O2`a=TfV7*NzMV@D z5Z}nihnVOnk90fl#TKO@DLG>9^b68>rvoBbPnh*w)YT+H%9u%M3S&6lQ+jB3Q)8uZ zxm%5vM9X@;SIMH!c*qfArlLo!|L}S&_SRzid^j$$MBh*H$nt>@q-3~2@nGWE$2%48 zC#H2~HYHJKrN3_RRU6XM*xuff{UdYz#}+h~5O9-%B@OEMisFhQ1@*0Sf3-0mq~PcQ zl9xc;O`ql1bm^D`$aBY79Jhvv?!uS!CEk*qbSpN(-t1Cvn{!!c5rU@pMN$M-Vo7BL zt*VtQkLcM;o-_{{izjZ*-)_4aw>DN+3(ve(2n_qZA)UGY((;L23dG7LmfzX%x7kDg zJ0YUyfrMl#Nv@k4mR{COpIE8+tDoVwBRIaL8Y`K+hF3iKa{Ph5FI^gH)h>bbnqdXha<3I@H_pgZWRye z?{>LMge8KK+mHL1(k82E@)VKp@zF_KGV3_!unRcrlb@_-RYRL^qB;0os0p~p_mVwwocD03^vGlk^5>=164|t^e=4Q z+rP)^n+OhKU^0;~856Y(5M%;gzHO&P~ZE56Bsas6;IHw+oZwD?&ZUEk??@ zf7Wk0Ub$m2zc6pE$y?p)?CR#qDA{{gN2OAKQZ(i1z8-J2jM9;c6)CUB$Lg0tm%+L~D)IYEnCY>3GAnwuuF8(#wLCAcc=ti|-00#4=exR@T} z9CL4C=yl@qZ}y-s*;47{9lvdlv6w4QlYq$8=C@e=A_4lCo~bfZjCoh0phX6-G}Lo| z_`NTPavZ4GFfn=PvT3>vGTME;Q!p;kjq5s@4pA%{?ty_R;BDT+xnnT z&t2m30Rq?U50z5GU@bKYWtAD(qtbc(*_qo2BNFhBX+uVFk1{~>xGxvEh_ODkhYoOV zo5sxYVCogl8n!-5ofO;s1ZEq9Ge@Ng633iO5Fot@rvoeQS5hdgl^dl#p~R223$Ptkg#rIagb+MzCML2FDRt zZHx-+Q?#W?`1rX(xG_-ndp}tXcM1ie|85H|*htprO!3DDq=udF!pm+hUh--{F>>@& zEixaW#Z8+>P1fh8|BB$~nlKx}=l$lQrWmbhz_dh$s=Fx|#C3I98I382B<9BY`TOL< z7B=6JQRE;&Oopy}6usyxta1`^0?ShdxtyqV_6d@M1dlWfxjU?1dI`)D_7%3MC|F$% z=^se!w5j2gv*Z^~sZg&5d%E>5iGigPK1>w|4hU%(Iv2HAu@WQ?y?hioM#a6va%vDzDrw$%AY=2pfj-5G#{bee>^8!Tkuqr3M2EpaWjPk}V{MvZez ze*Q-WW}a-UMp(debU(_1kO(ntlwDT%x_0t1l2K@F_Q_L$lE}bcT zu_mx(l}AS3ExrBT+AQ5_Xf^0j*}N*?^zbp%j&7mDm=bgI%emSkuW&4F>D(MazqARf%beR*_Nn_?SDDY1SL!n3 zf9c?edLa`hm7iR&Yom{x$SzPaD$##kOH5tTDCL6`JhjF zJsg6vXM>HDXkr~|oxiGbKy`3TrBFD*GknV4RQH;Tc7nY4U-{oT53Hx%q;I$2BjUDFapXC;X<3 zWbHJkD(n7aQFdmW+rYMfkj@|k@S|J+WGf5u`9PpEO7M9Y7MxjoKkKz#{ZZ=QzyJHA zCD+i3KmRdzK?m{JW8pKh-|?8iAW4CAIl7BKB3d|keOJ3@&BPr|aL8!q3hhG3E|YZ` zI(;5k$%nA38eVI^mwZVtkWGdJwK1;(G-0TBG7wAYQ7Tb+a0W*PoPpdtR)e@a9Cx8T zn4v;|@$TV-O3%4Kzd7BQZ3{c?R{$xRiI$wr)p#^Q51J#jc3S1Ry*- z1x|+3hdGeG1HEb4mhBdf1?P&5cXaX+RMGwTQoNM!^ea`T{i&h_s7?t#h8e@A1*~+y zG&RM!D52~#CbmGCX{8iWT%p)fHdh3o!=p`~|eI*Plt(V=y02 z&_Emj9!C+E>_l{8+8g}dErfqJyX>si>G8tT&ni+y&VhlJF3|p6SW%fj(-N+jwS4yn z?pxKQ?~sL>WsTscmxqqpS2VtK6`pJL720MdxX(jTS^bo6x>ke+ zbD8sa9mSm+g!Cck@+?%=Yu(?`DbtwO3*^%_nezWiK>iOq!~Q#j$g4+r$4u1?WJ)6C zao9!G(U23=@Z-1uIe^&YVJmVx;zDqJsFJoV^s>3OgQU6~CA;YuM#URO!_h;(Cx!WD z_cNK=xjV8E8n2?>t1et8DqEeMIxiCR>>0)&r&CvFvMjMAtUyRhtV=N%)&edx*!S2R zPv}AXT>UwI_TBFt!Btnhf5TTrouqCiTxxAJ@bS>AG*adF62Y@9siVVzMwKA};FzjyWms&LtK!L1#bdOy~@ zOInGF6%X}u6ajo|TubPVaaJV7KhO$)E0dOPmU&okLmOg(My;8fy(kl+2)lVdA<0Rv zDANsqXvd4{oe!NL365-lS{AR^IC>XshfG=dz; z25%Dww>|;`&QEz?m#+cnM&8}vNA`r}D~K#@AWB4z0y$FV)bI{&!Nz2Wsn4@HaFcNB zi4iXsUky8+{-W)3&0v4_@X{jFey;CsE=Z&gPa-RKTo0CeIu$vg*Jb*%WIA$IRX9#g zsKL2QLgQRu)&1(K1WjW+M^|t_w|PGmi3>hEwDZz z>N3WK_o|B*vA3B8^;=kN4E(QScAU_7`yunkkErZzwi`%a0Rn+Dmt= zdDkRH1tvW&K>iFSN4}f56cpy9|Fr7782KfS{Tmri8tPRBa$F9`y4?rdw-DkiuO(?% zwBM~{LBgaN9FH%t$6uY;9_pg&TWf0@2WlR7Y%Iqu+K9`nstj?1%QwjFRb!`~A-TGX!ZdxVi=|BRKui-b-me_rThN8Ug%bv4~kpj3}$@w(2`o9zm_6NeMs zQ^MXl^d3B7$O4$#=nHLL7bbzIGy%%^Ui?l~s`rU|hXSZn{og`kOF(v@a1gOA)CowG zrf#Ws&7(Ej;wGjcdfZUmfPB3p3MQt0I#FWbz$ypUzT5vr z@i_0sayYkE<;@{4vd{~1Rzitx50`Kx%o`gUW!FgvL&A`oinqmLZ~GEK=tauYg>`a&1R^N8(I{5&VZ8Ue zQa`nqyG{o2M<|-0gcf~ml(n+8;`PN***g*jDT%I-({E#&#HnNXT$qGruvGn6%7h+! za;y9!DpO-GGcb@27FbocIa45~2p-+U2$uEf^npn!&b6VT%hi;-s(6Wo+sC0q>poy; z6p}e+IQ6F$9YG>;3l@2Izf-ej(B{de_Lh?a*R87I;qRl7M+hTcE~ z`bPme4F^^iqYAAYQBCVZiSALUE>b!jKwMvACE%}XaH#)8Q=16(nqn#|GzVp{0_ES0 z3m`gR?*wETZlh-J->Ss_{{8rOuGs%FPx=4n|1KYPtbF9IO+J?Zv|+b<1Nq;D+?x$k zqwJRGN~R6T_~vR+VfVBx*W;ecFAC6KKUuhh(+)IQOn*^)pAEE*u8p;EhW#R91r*&)6sH|hT}CnrdakL6zak#}q6m!| z_8A_{G10ef!T?MhC}U`Ouvb+=N|Y%HueF$sQxKK);FIOVAL zXMt8w)b)ZL^zc8|EBU|M4E}bRig8i?QC8%Xsae=?T3=Iaz6rBRpkUkcyQjYXNLS>k z29uH4$ndxZtLPPfinmG?0N@pe)24~W3fGaCoImG-*#|K$HGq$6+~iXB8Y@md86-dA zy%R4ye9*egqI(7P?F1GV zl}XP)D~q?68g~{Qcl4e3L%S=N>xCoW2}fxlJz{V0)uajy7?hzV=C9U94i&ffWHv6@N=|Cim7&v%~#P${@&Vx5o_^Y{FODbz*wC?R* zgpmEwLIRiL9}(Lt_Dg=mhFl5imNU9aAq6DNa6B}=T@^WyBr(93G}cN%fLYq0wdADk ztR#u`;Jqt_3sYM?n@odC@QtBLsV!cYQRK&J!nXmeQ}`Ffqp{Q4e|tl_ z@Vr3Loy%zNIFiPW3tG%}S{+FYAewfDu?Hz4A0f`QoyD6I{A>2s^m@fEv+_IdGI)rz zrih+W*Prdz0CZBOyPbd4CDv%zC+OP=_19G+9dL3zhA#+LF<24MS-9w3!QYe$8sNrw z10IKIr}l_@hgMx+#1b`ee~A}SrJjj}-kkbAF}!5;-Y8up0n=UUeU7DxN!ZfLfveUQ z8>59D?mx-M$@rS_^-E35-QnSW(cyl+jM%@Sv2O>~Ve)?iAO3Dz3qkd#^`RA22{x=< z^gwz+aTh&UD#5nmpZ&c4hSfwK2(tcbGR;36(EhhZ=>NPc{GYf_WOit$Sg3nOhwWa{ zR<2}gOZp0^*We@BmC&Abr?JtBRa3M8=)LdMo(Wz{(J4Uyh~Mkhq?-b{zbLxX=ciH3 z2QHL)L7@i+$FU)FP8TAroLTs6arV3f_`M4sHR3Y2;paaOppYDe98Y~96rhWd$f+0I zJ#Gd=ml+m>k9h`|3fBdi@g*mEjJL|J2DY-~8C949=udT3C8N(xac5yuH%Olr{S2hL zNyj)&Z>oE$;wIK;uJIRz=O!O!q%JKV3!mQ#G%c}r1&GiU5u~qXE}2&6LOyoI=9XRi z8eXpc9=tvpvJe$-tu0c^LlN$iYZZX7s|Of-pYuPI06g|gA6|LCMjn)tBm=`tN>fBI zoQ@^BjMXGRrW=Y?HYow~mnFVeT$Q0R>M7@*7P+)1jKx$D`yl)zwa?$;mcGiPD`xprQ6Ik zn`xPSc?esJpyX<+AwTyAtqnSSPb+O2s~fG+gCmshyqO;;pprN34iP1X4kTM_bQM9+ zOUoh=e_j*k{cIXvvQjlBGE7oRx?)pf$s|n9M7%lmyZn{UCS!8ha;koZjQ1{$_a12$n21Va;43&7%qsfmgW5S`-Mi|*Cg zPpn8<7&*wWio~&<7C1G_EZytx$b|mJQvb1V-N{rWcE(lb-52`EAZ@epZ+W}71}*{) zM^BmX$ap1>s~b()T*icRTpUSpr+^`PxVlx%F!RNMWy@`C78O(GT<&}EQ6i$fkPXvr~MaUS>mpOe%rHAOyHE$bZ09Q7#(_rgzX#5 zWgjv6Vs##P%9@*?5wlslt4X?{9uC1M1=ct$X%4iCGq})YZ_CU`ODR zB!2A=KR3dWVO#6BhZ3)vv`(xwH&8!lluwmae`$JBa95Dp))vOkFE47GdLB)PzOouD zPGU(6Rvz{uKbz#}4A#jc{&*<$siglluxZ4XM4Oe$ZYevbC!!K9lw zaP9Mc4%uhFC^r3L@@o3&Kjmqjs;OxAB*;7VD8f1ZQtVg4H?Gw>eKX((Xq`2go%U?Y zvseqJ_b-Ijja~W~i~x5jH(@_XGMG_7MHRd}+^TdKi_Jq6sQnf!2fdwcGF(HQbv;An z*+%l|59_0d?Nv#&E=f#uk1ma(vSINS9(V$E;5fO5bSc~C9Q96cH8Ev|UjVu^t?+Q%j*q97rA?5yma_i{b+^9{JnQCO43{ zfiG`N%DeqyYb~S5KVyB2iW4cWNL`epWYWFX$VQN?1=hMA?9jP;9vZ2lN*U8PDmTNi zb=09mGD=R5T6_>kFi9JdtDrVzxf<0K1dT2vu{P;w74bJ?O^v@p5(ET>^dTiibL0B7 zxY{UeKX=403SzN}mD`^hWk7EGmo4RI?7fLoZ~5w`Jcs=J9r@Z8sd!`8wdJxCQnh|J z`u)3lVZ5mGp|$Q_0pwJIob_g_t&^m0@P4+-Xy`zg)l$nQW6kfQQrcZ~ z;9+}}1`FCWN5zVgQ%!^phAPlUI{{fG2d!KrDKe&;>7cpE`dDA)O2*7!)%!re!M9t# zLPd>*=@!`qh(CvqVni(&0T)+{@Y_u9Y(9M-Qs$*G2&(3EQH4~2U5V9E6i9AHs!BUIS^MzOuH6s$sB^5x=y>a}v z$_8M_+lE*lx6O7q|8>btp6qXK=wZENLAYMh|4!nMJ?^-+-JBa)ycNn+cb4O!KUJR9 z)6PFp=R^$?L+yWOzh6Z%o>k+?GYqAf`$aK-!@gJ1oxRV-Ta?*EUy-weE~2~C7nXmE z6)_&+Ne*VZ`D8LtRF%&0rsjU{{+VE3wr$ZbilJAtF&h!X_FBGtRt@a$Gbf8L3O6i% zrl7?0WZQl?gAJCmyneb3|Dwpyv2%KvA|MD8Kz!*H_31NbEs+|zJwW}}Q$D`fB_$Tx zq{06}-FpT#wf=kGSdk);UL{*Pp$XDO^0xp25~Oz!>AhosK=wwORHaH6Aw;?ep%)<( z>Cy!hNa#JG1_<$9`?=ZEA>=OhW&7cVL(hkXPieF}>-y z2P5pgKD*ekzzVZA$0spTf7G%~vEBSMgT1WjQ~GP@SVDdU{B7i(QHlemdN;nT z$usAUrmB(XL{>!5nv3s4g}_sbU^THm9;I|S2a?wN44p|IpZFK~5Z|0s6WQ|9msNq= z=MrXnNP?*sN$3V3*;s~s4+D{GiV%R4J+f*s=?xTwCjuw-Xh=h=_PdHnu^I#B6A5WN zs-j&uvtToKf6m6Gb4_PzdvG46f1Jk~U?t_C9Pl6qpPg#cRD@z1`*mLt2p#UohG|ZZ z=6!ljeF$uSuBHG-cl9AKED*bRPYykQLP|Q0Lq?Q%EkPL(iX=bW1*jvCBK@xAF|L(g zeWEI0kLSUBYr%WjR}Itd6%zip1q&y3JwE2^rx?|0N=Q_y^2ao^F2m0hMP9U32S2Z} z_AA{8NlE=!p!Gd>#aVt$DnP!l|Hy64&+`0Sfms*C1I3*1>ldL^bpv%!9Og1pqaJ-5 zq2fnQ2O~>KGz)OLp0>LLdGsr^&89UyQ6ol0_>+zEiFRnwybb)7ar5Zz?qTx;ZAMl= z7tq)M^eM67XYiT-Vz?HX3yH-*j9wNM8UtuJO><2cL{rm088+k}41P#c`Zi+B1ty)P zV}=sQ((w=8Y|d$Q)*FWlcLwJtA)IjF%j?NM9+m@E-ZN0dVJ5}`Gk(|*0A<{)%X$#e zly+W*N{e`9^6tUPT$u!>A=y#?*6UpJcZFIgGod9QM5w9e2NW0ix{8&384U<#Rx0(* z)wD@xO#PMi>tlAHl~sBSC8~X~!iMm#80cbFehu}XQ0F92*r*rLePqdl8aL7vN@qc^ zYJV+VNy^Zil9N!dASlcP>;$cj;j$mL)wGWWxU5Xrg}a67-&Qxpx70>2DZYxE4@#;C z&!gL%LMa~#b5&L~(x}TsT6FRr80ct;rEhfiIwB}W`ACc_Fc8LH#{=Q3)6G?PgpLU16KH}PQcPIm_c?Jv`Lcn2pMlwB?kx@9bjtm?Lty^ zmx8%uoC_YrK?*KY7Ukowag#Vb=p&75+W(>kZ!JA6+gZ49M2c3e}T zz)=F6OFhlLzM?tL4K=Xuu35Gddw|X;nm2a5+hgHY(kh;PFVQO< zS94>`|3OnpFwByif-?3mPCd07;dNHv^erp1683MFA;0X!iQrRjL!D7@g`oiv*z;ul zFRLbTHDAX4b4Q6D!O<^(FI5wCasK`RaQX@WZY1CX<8ohMgyQ}Mntc<-){SeHUi2r~ zw{h6;XyX)!Wp3@WxW}h)Tej0Hlg}? z^`ff&IIqv}*fOWBd-4lu50Irpk*9%(g|n~i7-S8lauVuwWaBn?dQqhy3>B7W&CbhS zNIA(k7d}w4@eTdsIrS{CD}ass*xWlNT~wlQE`#N*~s?A-1#o>)~e?-It-G=AuICB%cyRl)ecdgNLJyMe}vlUZ`V%um>h4P{El zU^XB1GC!RVVM`RU_Hg#=qt&UjYY#dK@ zRqJPp&v`k1Vvk}EF}r_0Wtpk!e4u6I1hMY-cPtd-R8rT{BD&OO1-n@mH9RLGuz{3;_q)~}9(L7P0eVX=d#i)l>|uGii8S16 z=gydh?&n%A^c!45!liH<`jP|IS-F&>_Urq~W=Sl%N7y^uWv}v2I|-io|9&X(Rk{vi+}Y)abiT)UsKs$ z(n8udFHbw>`QuT6-ODTWO9)<}ivg7yA%EDBv}y0rXRff|7pc8l2X&^gU#f zuRz7vB%xM%1}4|xD;{X}rqhle94I&D0S319R{@r9WuC}bTX!Kji13V8w8kikv&CUC zvTjV}(dA{$03Bm*U=hw{AE>R7zN2w?s>m>B7CO8pg^;v(i51MACekri&R(;2D`h0Po zeSlvq6sgp;B$QIG;!A$u8W?J6SVQaUdb}EMm91HIta1FryIaL6CXCO=enWI1n#6uH zML%1o)Tp5_rXY4Lu`WT=mgI^N6!QyFnv8YP(y|FZBEylb|8&^YYB&&`8?PeKCaQ%K zw`KRLl2m0i)^fkdg8nc0!g8C3w$7DbZ@)V|C`f<2-^~%k6REFZZSz-`BYd0JDk8eJ z+pgO1?t3^=&RxxPtT=nUv8XY746?JHl#8c`_L0DmiYf7=*S9`rveVj|RWUk}G8{J? zW>l{O73?Bvm=S{?&$CcEA(O6(2I0QRx`$;HT5WtTxI>fRbgV_N?4>Z~hpXNk^9ooD ze;W1+pKJ0gmW~E<6VN{0Jzp%4S(jX{q6AJNlXYn@a9CmE#`W8jl;4Gb|n9q=qb?m-k8c z2>)jJ6LO&u`K5fg0ujp7R-<2~Em@L#E*_i57QhtAOaB$4`A8NmHK0uX*KaEQ@P9DD z{I_1v|1a-2P;@?t50F~5rdFrbE@CK(TN{@Sbn3TvmTsD_aeGP@1CVCg#N*W`%a*$0 z8NAm+%91kYZEnoT<8YxW$dRNS=QH~0NW}Dp3yxPuxxLvmiAT{%q9AY`w*lxkUC%n7 zM;so@x{Md}+`^8Y*wP|&tmS`Gnfjm)Gs5Tj2d)?=(K%wOV7e(HWyXO9`f0#qMo_3t z)kVvcM$4knmbs?j86`zH+``40Cbj5Y-IT%Et&lQ$R$Hv-Rc|*k4O*Yomg_BIG`R^` zxII2vG5(#>j51~|>8Vf>edM}9aXEHbAD@&Rzf&gre|e(*-~RV27Eh)F@^5Yi&JO=o zG7rcc+4n4Le!z}Tq=RBczkPiAO-RYkG0}9+-?wLoC-z$xxCqB9jjz0nr>AE1wz=il zBHa~axv6_8XZc6}51v5oIsIH(LV~MITpEuWZ5lv;HCzKO2m1bQfJxam?!XU`4H?-d zs~hAx0OLD}Zjo|zI&O8=b;IQxSA&#@aV`H%bs&Y3fQA*>DQd~Yy2w@dnG$(Xe&t)U zxw}=R)-sqON`a5y&|ou*fUEtx_c8bcW5t(N)mWx-A3pWc0v|69A6;Ij(`Z(o{9BGc zb-RL!a0cLfsVkMzHb_kH4g@Q?2RB=2B}5+Wow|PB3CNL zTY{Dz@IP!Lv}NK$J$tdUZ{J$4A5Wh!nhpwaG}s8*FiMP(Gh9IF^)3;OOdsfMP|Mab zqzWt#XAWT2oxkJbsr&WxtcqtYlE68250v_{Fg7kqo{o;TR53UxL1DW_8E*0@&q;m4 zhuAfyMYrlh=IjhA4tR}M>5cx9;~0{$uOj3U)1g-qU$k-mdj`g*Rnr!=hjTE=Gc}us zzk=!1Bv%cAifO6RIg9JE#NvV+!tpeASv0tJAG0_=z94F|d;+pZ3P$i{b^7=#qF1k2 z;H2T@*Trroe#QH6sCa@HHP~PG*oqlq@t@z9KX^KY#e4sV1=5)uv8=YVwQl-3uKFb$ z!%XoXvxX}mMjJ*2)@jNUVg|X83D-_JQ6rSTB&(GFOyF(zcALY(?NgaKt)|S&+1YA7kK3B0)1;N$bgMN3bJz>LvfL2Y}a+! zjcY%R`w-wE5e-fe8!w#+Q)=nUwZt?#3$`z031z0PiD+m|0Gf3Nq8?kK_Z%&%?9Zwd z8~*|=a2iY|oc-~Ep%AMI9PMktmh8?pzR6k|DcmhRdWc2Ml; z)T$=EjJkd3LIEihKmDeS1%FRPH;U}n)xvI}+}TT=t@2v7>&2b-&(Xu{p{@x^AzTFh8mu`>kmdId(fPHVdZtQ-%VkktC$x%fBFZ;yy_vO4 zm1PA|RK0F?{1kgV;-jJ)i6f&yU5eduZ-zEre8S$=n@Ex{J9psjseqq1#C~5Xi%CpF z>+zt%Jb|an>v>54f(txR5s(1VYZ(jB=A=*0IA&)u|MjoI3hwQ6A>%m#7*U5)NU0#` zPMWnsfGS|eBbqP~V4)d48aJF8t8N0Fp5{jTKeq&XU&@essrCKW9#wtm!l7Xwfx5HF zvZ!volr$b=HjKMGU#>Z8sX<15mV=pN(4So^W<6JEGFN_lgTQmx(dp|#5I80Qq~GbZQT5P6&N6)+E{u3*eDrWE!m*?Nc_`NYm8Wq zZ;2e?PcrY{vR|uDfbYe(XyEBhSbY|l)_6?1Ouy-&Y~+Wl(|j80AA@+GsdD9%s#*e< zKenaEbkULG4_H>VH#D+LdIi4udU6kJIG8b*z3%tjl*!q^-8Hm*l$@&(oF?$GO20y* z0>HME!UnQn-@2<(9@w3B0Q11V!j!W?H_5CQIkL^IHEt)BcJ3&)hbRDi?nY=2 zxAPlJa{J`^O7(6-^1YQFNq=r7C}LA3=_KV?kfdClc@n3p;;3ltY9{yO8hj=@vb3$~;Z^Hjxjv+LF2I@iXI84HWoFcz9yo}!wSJTQImZ$AI zhp3RAT@JN`80E?9xbV8GfLo@UNrHrd<8K$G6gsj5vAshoVxgA3J1H7T)5d>MLt(bL z1lQ;Z?smw#p6 zdTL$$_p3(Lt#YXdcoYW}2SDfUAAg=|yR@XaZ@(H-4MSH-!-M1pX; z-Jh5!_pcjE=$T8PgH061>5n6zz6Z`(o`ozXQ_7*O6MBO!i~};HCBdXb5QxXU3ZaE! zHL7d0Y^*3+0S6jblM4$yoc+Xk+2rO$Z8yXp&)4&#kLfrsx31QD_6|d>v2NAl>1e(%zcbd|Fk&`*0>(&;ll7N4SPn6*9TIwYi~ z<-hgFa1*#S44FUA(vQz@%*%7DsWH!m_ADgXZ~|a2DXCjDJ;w@dNtgKq5HY|laeNFF;)zd%Pz82q?VGdAueBhXcI^L0Z_dn)QH7zfK$>F+#9uxDJiUT6P%#kn7-SG z-n!hE@Mh`|T|GJbO)%R!+x}@rG%K)BHd9NN?g9aAEpTV9t6p|&>uKon9`{zCSkKw) znw&c?JF_CJjW^9@oFu4E3sat-tuQ*OX=AsUgmmb)4nTN&wfnkHr ziru^zZ10v^q4vltL2BqZRn-xCK@h&T&dV89&KwW9c`=o_jP<_w(SaKbm2De6a(VY zCNdufld6}JUQ^^6#yo4e+i#KBt(r%rst&b*z5vhKgr^qu?@C-!tjuP9VG<4%*gAK| zrZ()@*$Cfq8d}(x!P-BT?jBalrlw9nIy6O##!|h?9ULdeVM7&}35W;uh7a=iQpFx^ zYTb)<(X;}jM90a=+-xftGoWnUQu$l!3=<&55v984`G{*tDz>GTZegOt=r}pIU$45% zDP{f|xuK=5P4VONR>Ma60y6!bBK)y8jPArLPMgcR*ls*Zp!Ejm-i9vYXx1B##2h%f z1v0kECYtFFja2pGXxVJ*B7HKl9W@$t^bh7;neuNlr(fD@dKK9Y;?9%gPGV zR{87`MBnrJmG7sW z&53V6FqD0zC==0GDRwv#I1da63q15Pe62G$x2*=3!~uV7K7qK8w*hTV6=jENs81bc z$60B>TH?gnLM&p6ehf#(-WUD1z1wg?uy>hOktN_wVpW7m!TV)kGP*TShHsV77?j)? zX14qifk$x)H~GBM3C@co?g6Mm;<+Aa5%Kh*piLY;2Miq&#`|BtldMC3&&xZ9xl!&(;4;qk4`c&3*bveaCJNX=HLeaWL?;|^Q=D^-}F3<(}tk3;*vx~Lx9maX*rV2&0^zMPVmcYkCV zZsE4ZvT94F3XSifjdRkIBbG!kh0iXiNP>X02LT9Q%%O45h-_7$?si&>zFQgE#KCE1 zQq+ATdVx3?vB=^mW;I3hi&cP%hLvVI~9w@?xi;`%+0*iKx;nt_re(#*NMYGR(axTh1gQO%1+v9wjWGTrIxg!iFh za4`fXnQPe_zPWFIBr;z6bHm;MH>iGR!nZ0RJ3#^b1Nm1wL2qPcdWoZY?8w7|!bxhb-H;0E9L`xYUabxBSlG6Ehw^StY& zwZ?lxSFq9G3YWi=!k2<1Gt{GGItMM#6WAKcVW4JtE9H`7%fAdsA!CjCS{85y>bG)#>A9sC0r z5+h`qK3_94W#Tji5HUcE58rM8ciV}{J+ zN7DICVy875V4ExiV^mw}XR}`+HcyBgkX1XMh@bb%=y41PCgf#I>E9 zOAnXf;9~^Au>vnzC7@={MJYgSK2qY>)av%0b&3`zKMYwZD}U=M<2-gbLBPYN27WKi z7QnOCz$6Akf7V3oZ;~1fUF42RYsJh^V(MX7YF&2yX_#Ez?O#^+$RighGBO*OIz3p_ zEu6j%_38yb<-OP65S+-s?34Y?o3q$*4OX<_XN2v&hgWd|Ew&a$lo$_etjR7Hl?(%% zP!xrO_5RHpYu&cq4cn5!*AlSLdtOwhirMo5f+mY;th@X!-mH}UmA(9^f_8d5<;s#f9=WX;Y~+L^>~&h(#00%d=VHs_M=I! zep*xlg5+h3K=DAgp1kX$Ph$C`|3uJ=<3%`h_#qEtIL%cqptiI?aTwz5s+Zj4=Ga~_ zwXuE#{dh;wFv5vO^brteo2l9gXbOJtbTjtg6_TLUCi+%qB(fS-jU&(OkqV^+omt6#_62#7G_L z5R#&NOr4@e{8}P?)|gA*8WN#%2ruZ)NmO@_ME6TyL=|hgJ#$TSlmUa=NM`#1wp-;l z)y2!D@^fyQ1DyAds_hnL7~!nWysd0R2dISP!zlkity#@!gKtb9>z%Iu!!_`K#0KcU zb`ZQPK`E;+Rgmxa1g82 zt1EnvBV;=3;CkkH^P6xNSv%FfM0@6CwO+cU-m3M;m50itz5}(8FK4aF?~3O%r_vM; zgat;hVaZ{Vwq$z3<1Q%Entt-23{O<6A4D~I2TvN zT>nOe)$?1`q7+AVf*p2rF8qo{VPl?zE;gR!+i2yRP?wj}ptboQHXi#U3*J?4R17*{ zxjbxgOEN!X^Nim6;qS)YHokqJ-ga|y*3`9jL#fTRwsdyn6x_Q|AoXYd)2H|YUO`+8 zVU6o0ztZC*_ygPhusbC&E)5gIe|$0;P)wB6%5l|J{)#7#VzED7RFinUoZCa8j!ARa z#oisB^k5$LszsYOOngQ2*NEbTUZFz81al2S<>zMcmWQ6EO}*~d(wjbt+GnfB{!sH% zID%e>7hqegOK`S&E16hLZSfv0h!M(u?kk?PwK*_#0G ziLkkRU%W6nP@YX;20L~VO?~zz3WCv806T$JucmNOH z;Vc33!uO?qRjgG0S%%sIIR$X%V`hGKdx66dDnxfvhY%=ZkHF2J)xg=+% zeH*toIxcBR{WZ-CD4#P%iaKUKP;q1{VxN?L@A@M)$D`|R*$8B!Fdo;Rn4MHzqPzd7 z{Tg)Y>pz^d|Bq9_|7E|-W(#O3i5&ooAfr`~i&D2X!&rzJ5g0ceKWrY~=q}f^-WWz( z^y*v7xo?+@Zv*{r_UTi*vzis2xV%{xTVxKN!idXL#l@e5lSTvvNGXvG84_J9oVyxX z&rRbQ>@x6+IP*wXj}o;V{e)nBV8Cn=DGuYR5Lbg3*^a;NdHI>6l;bCB@OyV-t|GVR zRa%x9(TUehsWv!~+9i>CPGPoj$|}$lk0d-#xpvRBPH6D)8QpR|%jd|FSq8C?^ z@52#lUY8%b&qE23%?g=5xKQ(bHTL5hPA!DM3UAr6r3aBy(PeJPed@!#vs*r1-`A2# z9f2qbHU>e5Cl4^HQsYYiSl4n|X@4D`3Trp(60erZ<8+*@aCpyjx0yh3FwAEk&cND(p;`a*YGenzhS(D_H%-k zuR%oGl3>k+fradmu6m`KvFZPy+vTO5e5x>>btsGKA6Q&FqpJxUm%q6YrPQ`2S;Q;A z%zb`_T?dnAm}dJjgUJSl=d_m(R1gKH?$+M|@~9WIWSYf=B4|Q`=jFfm3MuxcVcf&; zL{rPkHZWZlkyjW}mNANcyYiz&NBr5{NkQ$~HVQpPK&-=T#Z28+D;zsnLhs4G59L-o zP$KMwL)K0iE+;C04P-hTN-H6(5S+}cX~RolaV)cX&PIQ^;krS`Xj~bLjy)bbjINEa zqDA-aMJuy+0=pb0xEFRd{^ZQRNhhk5uQ^?G&%e)$cT;|RPw)+&Ns2@5t5IeauVd{k zkB+20%QIIvBOo1UAi1^(>{kGL(-wc1_po-C4|dfwM^#E5M^<;SxLaVrKP0W_stACk zAf~jOZCv`;=cW6D3zv5I^(d6;#e7~TUOs{vuvdEURj8*hoY$xeH8>(OrHn$%i76#H{*lS;6Bwm@Vy>O3H&hkYnhhlYj66U@%xtIpG-RO=*%^rp%Pn&VacCe z23_j83Y>W1sIN`_Vj5erPDk&jPCg$}AXWP>D0Q?{8)Q}h5tQFO&qdgwe8@6N$5d=I zIZSr2Lf0N zHu@75=Z+S0+PPvz4Y9kv?n_ZNJKQcG>+3|>9Bx^5^W8nLkNf~>dkNaLK~Ik|V?l-1 zAnf207U({BkcBT-_ei%R`Mr_;;XcACoMuaCh|GH-4R>d_G=^PZrWb2;&qXh60Ao}X)YHU02>PNC{0@*NWzKs zOU-nayNN=b6!s&L+0=)hpHojR)HneV0)$x|QD-k!PDhO)Nia9JE~ChDZr18fYplz4 zw2%1lbjZg+ots5&x8jFbc9h;dQb&&2e1MLEu;8LPps;+dOO=1bxs9zV&A|)_@*V~- z?yItS9#P%XmJbuV?Cx@A)M5{8(UAkrk3n2A-L};=J<8t`AX2Agy?Y#rg;-^>QUva2 z{Uy09ENg6+qf#+%oA6D!h!U;*<4{_E9}Z9R^fIo7tmIFtC59hAebVfATg&HmHV)$; zBHtkHqc_FMOxK&H3W{ep#a4b(p|CE#grbC%d)=#4&4SX^@!u*9R=!r@7T8GlA%v4I zit3_!l1enw3XfoS=KRA9r`n5z8gWUpq>4zCHPR&a+5O5is8b9)sQ-1>qr$@TdX{!S z9M2kim!%Z;boc%h@fyzT6-6E_^Pd^eb2&?+8%Al0=TCdkf-6HhhOt>_k&^RirGJ;% zK^U^#xe|%}A&Hf`OAO4opi`ys(Ya~PddSb!-^1w0xb_#?d{&(AX?^HtwU=9d0zy%Y=_b;wM3^;$LB6w19Hc7Isf(&=#oTsID#jjuip!NK@Tx>5#|_EnlCqL?`1 z6>80jyies!m$vYmFA0ZJGWQaKtFgbS_`R!=cpP%!eHMQaGZ`4twG9-8zQinU*b?dF z2kg>&ncV}0nYNSzg2GBS?OqZHgkUob0jeTZ`-YT8TB@VHzvih%+XNAh$5kY% zTxB+9-KVCfQ$o{CZCl&qp9huLItUPTxJIN0lbM-6W1?0HFaN6?if)VHr6trS%_{;z zKr{kS@qNXkSu@r+E5*+5Ge>ngp0<3+;qNl&^UWd+s!B#k!;9znYV)`-PgfeAA=cI9 zmS8^Q_8wmOAp~^M9UHT6c}+Y_HJzV20(BH?OgqR_W=u`26~Bcx3Ur9mbk4Tcd!W-OjnIr+n;o-evjSXs7>_M%i(Lh5V&Oz)CS zcZaz5Jc%1O^5zIg0)49~wXDLDuf7^xmQ3da#zyLtcudj z>|gF{3M6XZZ)O_a{x|YKip_}_ELT}}W3&*)QlcN>GKPXlM=JZ{SUG@_TNKBmzxx3G zyvtjfg8dby;rYhlF3dDX(5t>b|0vpMNmD=23$pQ7S5S(J2W}btbyc-IPK&O9biU*E>GJChZgi z1)Zx>hx`Vu>7*06(r#!a8)oar=WLy8D{L=k>DATZCmi-lJ(~ zkJ4QAgH&HO?Ha}2)udtwH~sil^wN@gf718gR3Ism-DZ>lppS3q;`hk6(8qTPP%>kj z?nQIU1L9uqs|7BTMd3dwO3*h_2wfZvg&F?3D^KhT;czyoQ|BJmc%C;}zux&s0=CzY z#S`7lM9Pxl>H?cFJzRso964xFU~^1^=b1et_@}dP1$@ql%{7XN5N-T~cTiAZ-|aTa z-<^coU;OgWBUkVoaH`-I9Uf&B?rc6^(aXogzgTgtw44eSR0?Tn$wuCzXp0>_-UQHe zPumw$z}0(L&|LHJ21(;wp8SHiTDSV-4 z>lp%zm=U@72Kq&p%wlk?PHHCBEJb&F@&14x_u_fm30v4EDQprG{FP=BF0>MOF`HBq zZQ}fY**u}uCI3Uxv9-B1MT`8KDi)5G?(^f3XQzq&xZZTP zLdhgzx8a|Hl>yrrruUEMAJ0+PCfUHO^L`FZK&_xnFQ<5 zD0MRX`1r=#<4@v}b-GW>X8w+MO#qEU$Ep&hmbke-eAVu2&EXF$8qpF>3? z$6m9^3fZmpTbr>-+;zDhJN$%D!E8@|%vor$PT-;f9D!~x{kv(J256<8oG?hDwD0qz zlcH(Jf&DP-R?dXwPdiZ&iDi7Yp0zI~BOCe&Op>T4GqNA61H*{`N*|OqOcB8hd?F0z zz4ep_gavGZ8@8P>0I)%Av9+`CPqy5z*gVUKLx|cHIcMG8jSeXsDiZv4gespMj*<0v zG<*GAa9i4Xvz@y0r@{L*0GCV?i`6qU;+&3O?<}$gr+de{O!%fwOr(})>*dRM8Sa2H z`~1qSZR5%wVIZ!wIi>1`OCn9isrnJwsi`TgDS>IzMjt6gWrtTbFaHS*L@vGtz=qJ7 zX$q|#ab$`5HPjgy!z)QRen)7>?Y_g0fJp3Ll~*Hi0K{)|&va^KOzMdF)0VP&y8@X% z50oGQt&=#ckP+((05ifvizdq}`18gMP9-Hqq#JpeZ<<_F&smA7fcTY}Gv_L6N$cya z#D8eqbn9Oc^X&}V3iKfy{2X9R|6Zu_Kv8q@6Has@9>pQZYhRudy0PWfY%Vj}Y69#l z2nN5YW=wD++q&oeh>(j+3NvZ*91s38jN2^?9Q|-QAf!U(+~!MCR~Q4~BzCt&0(?2BeLo8@M>KJ{xWt#y5UU(L>avvV1I>)5}Vd(P`yP zC{rNFg;2wvQy-PK6CQc}03uo)%+WjR?YCR3e^c=QgUhVti$a^OF*gc08V4l{AZPf^ zfJ7)OIEvw|mwGir2`A}E#t=S8&aEmIB=IBaK4(RXn9J$H*-R#~0y+aWL?7u^g=)|2 z>u&Mn{A{;4&Y4>A0en97H7lkeV)IAfex#=zw63ji`_8s%%~2TOq_VcyKM(VCGhb+{ zmq?RPXj#}dviS%$X1R2dL1yMZHY9*{YR-Mg*Do?vMc}(zxaq&bgxtdJ)K9lOM$H4a zJTie4(vinUG^&qQk$zPUWXYeX3KFf3qdjm#tVi5p4UxNO&hTFSwsWD(kY%UFNcSp8 z_tLt)QR(*xo;!lVSJ#YLer}31^mS%s=QNs+DL0h$d9T7t-;3Ww=!ihlMAcQQT^i2E z<9Uicj<1BLq?S*``BzT=f)cEgNJ8lCBj8^A11Ld{?gZELCbsC6laP2cHbJ|4w)fAL zfJzrtKBCEARsJ6E^}yLI06CrUahSqJ3=jR_{CTsEdA_@h;3aBJl_MOSh8RA@0pDwjTA^I1tPYDGyXotj(Lpm)))BzfXwx*QF~bQy1m>VNuB zY7iIim=ly$<9kwomdmXWSYg^SS;PZef|KE!zRR0*qQ+kt zC*%w6#`b|~eu`G|z#RVUSlCi*VzF=QDitykl?bam&lzkT^+nIp@uL#;F3xdKXmK4ZY`WY-gO_UMn$E&e>> z5%jE}ujbt<4B&j*QVBAQ1wXtR^iT+X)8HdJ^}s?gc;m z&GV}6)2d6PpvbINggjKKsw?Ohb%-E6n8DVkQO?N$$g3~$7bp8@_BD!})uo*OvCeo^ zc`aTpWT_)ytX%vMb)pthKSk54v%>Dp`J?eGvCOAV$mFc;dZg+wMk_79;=`xV>A>BQ zElH+}Bmh6aD(W*GZ)uyaMND(G6nks`QxrDWp9)!>Z0PaToK>9}D_%{>s4iMA1gVW9 z9g`yJE+HL`1tvW)~d_k(wrV)r+R)&crt%*LoL3c}J(Z45-g z7X~Q_xb~TpL-lWly!XRNuj5?DpJO})mSB8wzOhq&1=#n)yd4vM!Nbg0t+-%_i*sO| z4K;=R2h|@0;i&v8S9Y50x!x>IY|{B_o(QD;oWAyKPoae9sEGMZWxd&-LCIQ%di1qL zBh|{Iq!X#X+R&GM>Q+O#6WXrCERVf{>EpSAiqMh=DZXy%v)?x=TEI9lmC%$<%5BW7 zujo7u|vYf6?gGM5x0`f5c3A1s;2C6Y$JOXQTXhsGg_uj3GnJ!6t zlcY`GBcobPDf(6m^N31lXA+&C1<(Tycl*kjAy;)oVZi-#*y(e-u#+qa2~iGgn;!-b zaNAlTBuQRy&HS%U!PhgdCiKXRS?w0`iEv^|Bhds`t z^e25B_88t@!$awn{Cvez;y(X)Kkk*0KJZ578AU%{d>f@8gpzbo=wI5-uW^b#+?#Rg zqTD5-qqY~iRqsrbg#&P*bkC;+qXY5}3 zwsZ}Apu15`NqR*eD%9W<*`O_S1|ud>c^5zIPE|z9ci6S4^uveIj4cwPws&X3?y>%f z&!f&W;WDMS_R>n1%%u;7zy|}_!Qz5u4ikiiq!?gVHdEM}Q9nKVk%U&C_QKFL$8Bx3 z>RV||QOi2Bwo&xMX?%U-19LA;NtZ)Omn+$4|pD`t+|H~$*gCmV^MME;_` z&J+)zwd9#;qWWtFS-Ie@cLCTgbv#Oo<&HCqn4lEYgCelw)3Y1faopz!?WFmQrl{Ok z4S54z$EhufyJ15~!Mmugpa&LG6(RCxMkr%2qLBx>??;#yI=dVSQFNOHo)ludD4%Uz z<}RwD;L*{3)`4dR0|5A8-AU#EITNe;>ifZvL1&xrs7ydU$Sv4Bo&t!(+O4%QOG;wm z1Od+$=0?+!D}TJR2K-^njAVLa-kKd4MyWk3KHECMk`jooqE&gm=%D125pC(yIq_jR zj~|M&+=F!XJhzaNBi3L(B{2QSr&{+SaKfCO zKCXH|8yw)qN{LVTn+71=h5Jkze1H&=jN~z9W>v4`W{vC`i$tgjgRGfQ=8?g2c94Ki zQ)p2Gz|iSH`xdaLG}I-{?)|8t&{_m}`4l1`7e#V*ubl1y4=#TA$-~%sHeuI?4rn+r z{myZ-$%|?b$5Z^~ul^zY;{f`M;FbKt=xAP(n8RcZEzG5;a#p70UkkKR$+|*Y zUXMX%Zyt2anhwMJiy`@>lEcx9tcGW5^lRO^g;DyS-}m3G%Jcz8G;sN*gLxX!Hr($C z0uDwO2CO$4jSyfMh%P2PjhEZhe1Z_fd+$V*v!Edg2rJp;(JX0x$YTdMW1NO6`rQyV zP5yVBR<}9IO6ZHiZz6Tn^>Y5!eg5v9HwRc3Vm*KrLq;Ao{0<`fuzf>D3?f6~aqsF59Z;#^=J1j|Is zsTWypiO#{D!2&>du>0hpo!cLW!+(9{?4vlIJsH1Pqn_G;D6N3VtR~_>;<*_*g+A{) zzwNVGMFU?*UzqP$BQwWg=HKKkC`ZO$?Hc*FNON>? zQOq%J4!*cNkS9zI)vfXrmA=vtFY`Pr*ZpJKGvlz0k#trO2iA|vl~ps81t{nElE#kk z()|m0f)(7PgP|RS5MI&dsY^nWTti|o_s=yV^+hX=ycl)LUFGYq47#`re~H%-xQneH zCF;gxFcK5(j1yo82Gc2nKUb2|6tt<8{vLjasz0xaOXn*WE=vx=Za;N=OPtQpCD+8AwvyiF9}n2=NF< zre-EYGcQ6MW`=d%kNXx{$7lT=2c1A$*x(V7l6OLt5bBo0(h+$H^FQ(5>bXV}$=PoJx#d(*&Tn#r3{@owmml;1SQQ`VJw!H;3`XRCBuWTVA=mQ~im4oeqhh|y=GqEZt};cnjo z7zYBx{Nghq2e|upMqPR1Ei3$fWnz80Aj=_0<2X8MUJyshpy`v;;wZavpMF zPyBkIAo@JpMN6XJG^kI%VlbC>GVHJslQ(7@MiJ-5+k%Z zt^YEL{RBO$U1*hG*}HS-Hr8ceB&w+;wHLR=DKU{@cww9%)9UQ{UvQ59*NjvDc_hUD z^OwvIeIAsLTFwqhV7eH-bf^I0`eVrLJ^}q6qb2_rv`u#%`2Toq1u*9B0Zq--r(sge zfA+rEG5`>c4Ui-62EfUknz~_ved!Ei{bY<5K*1_Rd%TljGCKc@y7!KTbMM=}Wv56G zqD75KqW3ycg4v0UAiB{@5Cl<19fC>pGD!3;q7KGHixM%SMvq=2h9LTkIvA7p+}E?- zb+6}s_P*A2|M5Q8_5NY4F|1{r=kNTL@Ao)9hh<+qm0T7;KaG{CW)gmAw5f;IVSYAI z3U&tEz%F_@JV1^3-O^;djOgHC*N?q#R`c4v-uGFumR3#j0&)%m98$A1SdX3TbkW8T zP{N>O2C_rCdubm4&=sQNdh7|{YWRS*vqe@FH)C|J=0v$>-#$uSW5y~rk}L9NyrS~W z2+n(4NFzbL0v)DjqoK1*>xMv>Z`B*5fmO8d>uG`CewZf|TrN7g@!eX*-ly-_Sl@E+ zR&hVyJ(;7QOKh2bRbzK0EotaqS(ktmhwz#1(tPbsY$YuG15Q*;k#(`5=tb_|mkm0_+-yI+D zV4|4KYF|J{$i)9spYKZUpR11Lr*Yrwntx>Lx3*x@Zb^}?iy(wR}B8E=c>{keV zDQ4!P5Sh*053rC-wRk*elFTdzUAen%3lZ+O{T$>#3H{VC2DIbg4F|JT-`A5BF<162-HorwgfA%nZuv6 zuR7Mm`VXDFs~3BcVAA{TuG*~{FCFc`ROx|@4~9`+T-Ml%E#t05qmb=C>ZNHc_j2xu zHOSU?+3u|y!xiLWTSkvk*GJ;b_O-HfwpJ1-FVk2(4w8Taoj|}K-Z+i>2?+9#qP?0W zybp|Xrk)e4|Yh&s43DQ_*vm*~eEg(%`Dn zbss<_TuIB9AbmNe^9cd&@&+B%W$OwUr#%nf?gCcnkrbg(6lJB8j(&I1SRp?+% z?Gvy~n~00hgxa#FY&_H)4adnW%heYGyFB-(Z&p_Sh+^}G1^C|g$XnfT78B$1q7WAu zt?lK0a-nHD=fa?C#;RGQM||h1*#`(mmZMx>!Uu7Q0T`6fI1e>W@`cG&KHiwZ=ocmD znkNB{nwh(v#OKPi5sLR1>C~ws3%NAqf|LaG3N%?f(sG6LGpOMZYUO@oeoG5Ic36?oPa&bph=a`?&CD*fD z9diM8)gYQNpQ2dM~zf0zVYgDlTOM^1q1vYyZtny_o&v0%gPjkmgbN2YxutU{;(N?*E8z1zqyfnlEACiyJrL${Qzt+Wg95RGQ)Q ztoToTZBJi!GJSNCHz<~GFIqRu)$~8|PT>|DYb7N)_sMSdP4s$(7qe;7rxfCQyX4i* z_C8d#s*9U50W&wHHf-60?Hk;((SjG#0e>htlW-b(uO6#G$yY=qCY!H+(7urP>D z!*5o#n6w%&FqL-nW?s`Vr6qn<+1m&ulu%x(4NcNdgqor$KRqNukIeoV8=XE zX|rp&V`&Ttx|GP_n!dnCNRI*U-Xta)`J@{XC}R%eRjjALY~1m861280pp74~AAMqF zVz`!;)J#Wi+$g8W@>w4R0^M$uUL{GCvbNJ7APuyB!~n7BW$lvT#v`%%v77IVwX7bq z*Mz=QNTee!cegTB6KFGWHo$%AQ1FQJ!4&}^czEDM2X73Q9ZC($#Q#V$%{dTf$ zC4k4)0j2K!oL$}ardiC856Z|hRTI#klNAdEMhu(kt$6%Zq01sY32vrJA|&MlMj!JFHE;!u+yKIa%iX z!XFt~AJXbbAT4u$EHmZg2gsy;&z2AT`t~htSUXvSU=j+>_Mj(<3?78f?{{EPqot?F`)s2l+GybNgraZN@7jZEw zPHE1osk0gmZ?ZIa9R2M&lm8^i^Q0!3^Q85P#V`S{E7M{`leVIrU+Pin<;f8$Lm1NA zt}W9s@xS7mxY8sWn%v1=L)5f-#siDDv!SJ~F6=G8x)du}rj+^d8q+hr+1M)~ed%>a z3w`+?^nV<+ygACj_7A(e+X&Bn$$RAOy{Dcxxwc|CxjpDQ*LU=ft3rL#KRqz`8~PlG zmyAD*?*w9mKX~`M`8)Z$2@U>f!Jq~#7&rItYF#2I06Yr7g3)(X8?a0O0b*dEv5}Iz zcyRNTKaagGgGz?FK2!GgU0lFbjz7%Krw4e43%3b~Xb)rNmMNgpzM(*__4_~&xw+|G z?$RLZKUR6;uFBOkfBHY-*xghqv=w~TRUf7ToS#nxI+EegLqDW}=B~-p$F{6urfL$g zvyz&EHo**!);9Y7?Usd$PAd4Fk4vr-V=a&(7xu2rCmAwlX71)PJmT5XYQ>_sOQf-( zk@j4xac@f^(in72d!lo)6o7RbWyN~DP}|u{f7T_h9DSy984q}2b%xgQYf&wCIyvog zjr5b-JjrIU11`2d`&{xTt7{8%jJ!xpJfjr}JO6N@D-FZ-`U)o=MMwre++d3F3(PQ%*2UutECtM@1C;s=Yjx8RUj^J(%_%K{xk&r#dahnklX z?A^0dNJ5(3cV6W1zNOp5W6Sk1#NH;|S+qs%kGj>Cavi7BH#n28Q9t`}bQ9H_O1L!L zTqn5i&Bo;T^iy0;Gr_rsC5k)~AA}L?;z@y8#)eP?cTnK$`3LRc@2mlaGE+R zoh!lT9=tf-1-eMYfi*r;UlD5tNYetE7gK|si#oqgb~uu?UOAC=Rap&vDSylV^8)2~ zL1PPnGD;DmXpHZN`3C!zn4f&L_{^xGIOz8}F7?qnWB(aG&&n^0#e6AtZ&$v|x$*jrlfn{69C+!N_htzmODs`bAbWfU%}sHsLFxIqNU2g} zo5M7xsHNI{4nuHZc!HB=0t=-kI&diA4w%ZaSN`Em(kH^@$@CXhE>q^biARo!$_2iB z>80sOYIX0O^S55NB=ri{#nBGSq}#6EoQr4!RoNDp=W!t=nU&D+u~4)@ulM+h<({S> zPY%ZIX4(d#-z3_slzPq5yKAQ5(o@Jr4<(>e26y5c=$eVh$9p-U@ILH}91s@`C4e`U zoEK9;Xieyb`X()=)+H(2Sk*nq^Iy^XQf}^&7_mp|9#O|xPHh;Z`8a0L>LFZ2@xsz4 zO-}-mj~wua9uWa;O7Tc+_aFUI;qXGH8$>g|9ScoVX-o>{I0mZIiaO*zlzOCG6eE8c zG{xD$bYrx+-=)v8y4@tFKH;~@T%h~kYQIB$$w?xeGIc?&JA8DHue2F`PfQ<}Ofdd{E~2r1!MjsNqOD_4nU{k)4dJ z+$YQH*VXfda5?Jd7gyQhnYIA}X~uJrg^!qH=2ENg?TJTWNAhb_>CEuu*WNq?66AAc zd*Xc^ErEgBd8VR*7aHnk?t+6QxS=Fx?eF+VY#QD1%fEaI{_!RRXdypcgg6dQ?=NGv z4SnqE2wGzK?3HM=Uyx*ie%QPd+irf|#KI#5PMD(6{b0FVwQE21CkW6U)`7*bJa*hvXm@R}?H^`<%cIr%yH z+x;onlIEB z^r>o7T_g0_O5`&~4Yc^d=B6`W&4dj4;lif&gwI)AE=0esskSkTl8C)QQe4C`6Qv>e zvrc8Meqw1DG5?|>R=(F;K+{N4#zA1b)Hv1LusG(k{rgm$7EAi7oX~Voh*f-{3dgHX z9KlG?ILh5BW7#Xm9VF3T%`DM4ATpr|UI!1dJs;DS8_HRl7c$Io&?({-FhUmaRFwc;3PGFPO!6(OgB8*C1jFU$J;-uX|+q*75`@bd$+!%^Re z|1;b5!hh(9yg8$f*}a8(kLFy zzME=TbU@To?!Wz4){_66YkL7)U*EOTc`xm?wy&?Zkcx+!8zTV88kKmo6v>(jh*DW2 z9*vmIL{nXpL!~TV?ek8ty;>@$qpc(RcPWLcZv))BhBAl1+&Eal`%?yy5 zsBaN$v&6Z#?ic^Tlyu(#aK<_hG<{)Dou*?BeSgvS6E@dRzpodJ{At|E4_-R;$thcT z11+h_6MA<(ZgZ{>nNVmmC}wKq%+@vki^5hJ3B)~kqMt(1*o z;|+9;sZPmWL&{~###Tgr5gd;VL#3^Jw;aqBsws``ews0BP}Dl{RZ;dW(fqT_Fzxz1 z1=ZLmCpY!}G#w@0Te5a+%1QjB)R_cGIlAs(!aA3#|17Eq)oUJtW>I`wA60`S5}$Pm zR7L-ya0`Oq$6f{re-ZG+Mp^Q{!H1}-05P?^ZDSq5j+OSkhm{?S&i2xZK1*m5P{xv^ z>FnGDMi$jzxi;RXoMW62Z<5LZgVT_d;Lg{~ctfZk&I>U?(pRE1ej^)1_{OEoW}u^f*Y(*iPgNDpvjk}DZK*=6uR-bVOrFtBVXWm2AHxv%~H(UMA_hb46a(kR>c((sIJ{jiGZtojE{YUaL4tJ*O+c zbt7zgfY_l1tyl0`DgEW3{EI5*`QeQfAbGjf=|CfaZdM(xB~1YJ00q^w!LFOx;tZIi zgBmC!&pj{;iF1RCBiDl>9 z#0ue@ap$!VhP8wB&rKL-WE%`})!x}mRa3O%8qJI2ljl{-`jBtqWZ32j#V?9Ny}$EV z%98kT@%G)V0{FGC7Kq2uC;u+<)l}R!t~rrQr+Kx(S90w+YARCHni`Rha2);GLl3B2 zD=vg~I-1UN+59gKuMEK9b=@`YgvJYL)a54UOnl2t{jSos)XLWrH|o0(7kIMG9_z=XvWVUY4PRiPdt zT0vkuSN~T>_J4>7?=L>?b%3}F$8?e}6ZqnmL6?bPrg%&&Nw#dxWtK9U1S$LGtS}jm znw3kMJ<}O~UL3hGy2`?)VVlZv3B_yniXfL5Nkkx}0H7%m!`nwpjFO(e9@L=lrM8k5 zB-K~@7|TdWMO5^oWYMVtF+Qxc0=h=>YU@DS1-O-iG5YgVacsLuMP%pK92b4e!y?q5 z08v7&!fSvrv6BU5F>nE3hH zk`r>!>ye@{B)UZ;!r5Ch&(@$26Ft-@m8#wwzC@KOWM9)P6-gIeXgJ-YG=ME9$q{t& z{6&;avc?nWAmK*OmRLj;@3}0KV!XQ3q_l_`7B7Sb<#Ag@_yerNp*F4S-LVJFuGU3| zN%ZTtS^G{O6@tD~e}=5kGD<|ecvxIu;TjBVej*MK-`vl$*Uy<>4GJ!~>7Y?lGol6h zzbIgz@{c*NdkyIKt|Ll+`vm{R4gLRcsL^?y=lKjrHDfznU3Fum%*5i((}Vf)X-mCC zWj>0=kE;7yL-XXO;&8%@4+m8){k-|2NKY6o3s)~Q^v*p&P{f>Q7)HAuTz zb+=Cb7R2?^d4Ufms#&)4#YgeT(HUi=%$%Svt51JP%a}<03{Uf!TXDp2wxS2s9JOF^ z?6o`&i>9jQUsaC0J($kr;2v#cX2P;mU(ls42XG_wsk{!iV!J2VR&gq7FHZt(BSD_% z_p*G^xh!DR-f@AmkU6?zkpO+4>FyNQv*J@2ahmep)M?S(p+_Eb!L~XGp~XTovrO>8 zGuP56dS*t7^F=5^CM4x%xrUI-#->roVx3|aJ#*+Sye-%6S{= z7Qr(m8xn2nc#B1Ff$GwS`Y8_6=GKYHxlVS61IBOhd`euOcMJ^JC!Rm8OPHO+f$wMX zM(W@9sE;}iKD`h3H~Mw($i;IU#-`AR zpMG3ricFOFJXsh?9?Nr$Pv9~dGfhX9#-O4*_0nt}<|@?XvhLXlk?!04pg#|1x-bss zo9md$6UR?r;@r)LJ7n1>^KUkJRS^QrUFW;S%!-jRue@$}wgIQM@^Z3G@Y^Wc6^@`+XQB$6xj!J#3 zD$6#COdlfnaVxdsu{Z4CPIR0mH}v{o6H-Bn1yp%s5)s#06yg=)A6AZU3;8&n2#xEP z3U_Nmim)!%FD_2Tu!IkHILinezDTUUr8(7+73y+N%K72d%cyxyD$@`TWltkBG`KtZPcIdc0FGxWvEh)^fnYOIP?XAG`48o zvwm^ZsdtL&u(da#i$q({qGUF^^>amdAL$iq&75hpo!rC@*q;UoT-?OY0Fod|a3{fr zB;xnAWb)3L!05K3YKY9)AEOI`5i1j!Sv#`}OFwn19X+y^`eY(a%_SA%ZPveU?Jg$1 z>tWR=0FGtDuxd-g2=Y)OokNayP7iOtAb#8O9>94>FcC3!NMD3C#de(u!BOP>TPb?Q z5uLXDYI1!OEPy6&R9KNQ~QKmoS>;Mg^+2=G?4TaAe)e4LkA6hC48g$qRk90IE z?iGXlBE^cz$I457w(}2$fE)0KrEx7^<|-xXmDKf|rex2BV#P-z}fkAu|6 z#Q1DZgQdk|Fp4VNKwZIkcEdbyzAoD|Zv10IW4(ovvV=qZjg$t*UlcYZGyLXHNa!hG zz19AFl6Vcn@QY$1fwl9gSrr`wo_NDsM-HRdp)@LBOPeUC6;1NOEQd&o3`v~S;_|m2kLE$HS%@1EUr@jKY=@{N$kF(`-^iEJ zy{0G{$eL+aJytKGRWWQWN%X>gxp+n+aB-S^aXs(12{MoKJsbMcx~Ps_k3d36q2F_v zxm^9b*KOU(tWMrvMgQ=SOtpp=Ha0yHeJYlmQA931Wd}NZp(Neq;p;2P_m`yx#4k;> zt=2y?9F~?%e#%O|TNs7h3&P$XAXkS)f-aj|FZirabm4y5!^YnS1Uo}Zo%E7sDh zoUW<(n+|b{LULJ6KU{Hl3`{x2r@U#DtrX(z0YI>9ZRLhZ=n*WXK&|MfUheZhn0Qs8 zrA=q*0D+iR1}Lh!;U|N^2QxA+oPA%ou@l7WDsDZswCWK3R5{Wsqu*2)J8Ds3y5iH& z=s%p!-69!`80(23QQ~yj%N7-X>wXIMVxJdHUxH_g<19zhU>%tdW)VN9$j&D2J>Fk8z5p(%OJC207Ag!Y`*vJRmFM(xTVx((yX_}q1rO5k$ zG-)9piF)U$P6L!qs>WwK7dvMh0W8=K{n>tRE!@_)^kfn7*&rzBJ3Yd~W>y-?SUEXM zi4<+Q`0UMV`wuoz%{NmQ=cbun((Z1%ap-4xc11U3`BFZEf({DKlb}pnmpa>UWbBkaQZ$iVpqA{K*^6C;utXk=b*zXH+gikbPK8wG+zDz=ug(HIGdXotfd&;PMH!t$=_oU zKx*8MSa_t;7_$;@`(~Nuq2e2!+De`NP(tOHkfadrYl^i4gw5t2DG|-~7%-P%Jb-_r z7E7Y12E^>4Y;f--r_dI4d=g^zok!@x0;L{A_!(JBn3O ztE(yJGrqCphpq=*9%nViR3#W-yxTQZ2@c}kS`YxGC(lIl?7R#v5`U7 zE8(N!v>SAbZj3$FcM|q>6m%moq~AqbdU?m6i+K1xs{6!m(!_qRpiEUfq7$eZ$*T#K zK-v0PKu#V`bk4>yP0QO3nm5zIs?_Ic;{nCYgRHfKgKKf6FVTt&$yH4uSNC-!RxS6! zpYQU5_(nf!u7h-el3s{1Gk_r?GmxhiSVtgFTmgRoUqmpzuS1`R#UUcNcmQ)U2G zs>V&wCe6Sku%hk_(cZ@Nh3)U4$wbnP!XqFxGE|!M_&iZr0bY8vI!V5^Kzd$H2*J|D z%IqS)Gw8mmM_dl#9X$}ex!*DSy~Fj&6iLZneC|*X+%rznz`;-7qjs|218q1VLLXIRy`DmD%p9yM{?Q!mS!;?%sZ#;)4EwidH93%CIC`mx$HX z#Z-p}?TQKU%DxI!1_tr!{k-p1>KvZU?pc|Wp)D{E+vjX) z8dYg=o@_kc!cU9#2Sxc_HyC@0a2WTH@vUwA)Uda_=l(QJDX!t!`ESW(>ju7stP?(q z8{J}rcy;Ds*4Ao+X5Dh-63(3)z6^%2L>>GuG0OXHlihdjO&@}dugmU}8M|TkxTU)d2Mc52&qi~~6`^zv?4}V0LO0%? z-2U`)f8oX_W$19>ir2~WMEV?Pw@IYDv5yhY+BRxW0J^FvS#n}mZ#jhwUVsM6VE3&4 zmml#AX!~6u{>k|Z^$)T>FCq)ALmCEmJ6?p91l_A`AqwLlNM+#<_(Z@oyE(ckN{1A4 zY8nSy(LJ70guZ)LsEeQ@y8oikYt)JdVre>?f3Pd93`Z?tN0lK+nEWKi#i%sBiYk|F zyK-Apb85k5j!_MPAt0f`+j~&eT$A2Q(=RM#7oYqBiawWB@TFVDi-~|kJq+KorCs5iHMRAv1fHnMb@}60@Yqs5RTsh+ z2clabPU+zNW6aO=gT33$Z4%rS)l1=lqy;NApUr1(+u~pjR#9K!Vf#C%3}~OHuk@on zx%G$EKz7+sajHhS0g75ZsR$*Y>C(U8R+kO4I(bSkY{RX@>&T_Ey-*d{)9Lfl^Nvp! znP;+0O~U{qjL7gQtVQ7B>3Q-I;9v;_VM#Y)A+h|OHrH_H<%{R}V>UKS4Tarbci2dRTygvnEBFBrO`)=K7C5P0m*v3;b_!l`?skghyR)*( z*1L5cdzydbl5~Dt@3{hH<}g}4iY=@<2ciqJPsA&oZ07R%(=Zbu)&2cjKxvMQLM(YV z&Hx+c`T6-F%+4e!@#HtmC>@%L;vBe+p%!rQBLiZ@8Js+vM|b`Kj23@*p5vQO@2U@< z-R$JLo!uV7(mamCr(wMpgZaDi1Vi_AlRFkzCKY|@qGZ1L8_FKtB&;7x|7o>1Hk@}! z3sFXxNED{n3$4IX2-V?MyjO)%nSzU$%?wM%)==zXUg6KbOBo}x^n=)n2u66h7y!c_ zt)F-o8So=H$7r70X;7dev&-xwn$b^p7eka2#<7qXF*xyUHvGHcqYD$QtXq?D%E zY%Sb)*RCUFDPjOF*Kf?YA*`Ln_*lWZ6ulnEALF}y=a7G@<-QZD`*!VEov33lU!NOn z*VhIVRxLlteK|Uy)Ag+W-TQh;nQuZ9x0k*djTZt8fOK)5d0(#_qpXO+&##RKgm4=z zffdDnlnvE!3TcYI*o3kObj_t{yRUGxT8OuqpIuz|zgwdC->V z20S``=DlQnP*;K{@q|O84t)*tV5~aV)t3B0nVD zL!V*CPYGA;PO(wnS^`?IMn8(iHY)pmQKT>Z4vJB{umfjS&q|#&(cGCz){os!$<~y# zXu6kV;#L%58)Qi+AWS8It&FdkYYUA;rWS| zBrQT}YffdJCZWOG)wj;ke`Lq)ZY>pRZmRc&l#)IMiw8Xqxldf{vNUBC#L?ODvL^GNk_ z_%2FLx72b(3`l@Wqq?7Lj&<5@J~$iS7)?sCZq~3e*Gq!sScH!uvk;K39O2in9-6fl zP?x+TNDeo|+egT}=bdKv?i;7|#an7d!ecGwKyt9uT0wN7fC+#ER8->v;&f5tiHBWh z;vrwzS+;qBo%b&adYS6Wc~wU1TvA`(r94^y5gfwxfFtPiviBpAoj=}hWc`u!Mr2m| zFnNGsc_

wayiMV$2q>*IweQH7(^R{IEK6+YusKk-BH$B?0*5locVaq&a6tgvpD zFj+%6nM|>U1-KC`@UV`Sk*RJ6AuZX(ts^wQjyzCNw%?c}4|IP|lUb0aSvT(6ck#h! zCb+%oei)Yrnw~%%fP``kSM*zVg7To~}UVg>10?bvp+@ z)Qo;7@w-nt?#h75<$Otk320@wM|&#}QJE?U%oaP)s@c9ZRx%)8V2 z?D4{L+3k0YiO0P{A^nS!(5QfJr>@L+T$*Q^l?{hN{QY1TG*zFNY#slWX9~gBG62Jl zHTFABNk2iIX~BO{$R3gd)2H}kPIN7yG^=u6lSw3?yZkR%(>*U z2c@7&=Jdj$->MLBxJ?@yBqHP%-X=<(#x2#;gVrH{ma9-7D(T&ZE6`=hSdzED+Ru`? ztX!R8&(He6sLRe(`=O9!MHoip?ESy`)cxJ<_}{1cf6I3l2wEZxJ&Ft(H25z`9Wpv2 z{Y=&G;Xe}oxU-xjd*RJ^r16E6&ELYDQ+yE0b646iEB!~AibA2f zztt70Ro$%@QKIs$u>SN&zfd#BvK(4rtjV>Lji>1!a8~38gzMN`sd}2GiW6Qnk z)sr0peIX9if5F}TQUm)U-3TrS2-)4Q^ySZ8BZ^P922E2@ePZD{~%<&b&-Hz0W+ zH81+u_|94N=nZY}i83;~r*42U29RqjF+yzd!k@Xor@s^9li}WkZI?w_(wHsjK2|z?>v{)C!FQm zpm?l@nU5B^oSXUY={^5ZYp^Z$DDHwGq#P6~ORoC}A1p-ysRaXBlj4~6zp zvXFKkKWrr(O1WdgE^=O4#E<%X*Ufg9lDce2U%r|! zB!el}b&_?QIDuxpNj1+Gfk*WWb0XUBEXHiP@$DVvGo73&B}{g{HqDqib3BBFDt-i# z;O`k=Zgm&Z5R#re52n1n0;Dhva2vor(SSq))6F%DzrQct&#_W(h7}87V?Z7Gy1M`l zX^9Tbk?}M@O?rdmyo8x*ynpuyDlaL=8EYDd*enUq8In$%*sHXnNu)E*!P(Gdz;06C zigH_Y@gjRqZSa#8BK`+ryx?y@83c#|C`sJfCnU}GcbbP;kfeyQlX9P z@zh=0?2&r2;DOOOn_lE~M*Z<3VV=pO0)#HU6sTZMh~0@$v@)Sdm-WGCM#*HAMxh4D z{Tbl$RxYm;A?)cDwAoDGjRt)F*zwPS*4M4HcCeQF={@vIPEoKf0v%~R5Z++i{H8FfR7U0q-fQ)3#%gb9K8gI;m8kfOqSEv4wj2=i5DCHabnIe& z{`C;ryP}Dmy0hwr%1VTJ)tka+y0mxK0~@PbBF(t=`c^rfk(l4MC|gSO@;1a6(m$S( z>^~d6f#_z$scCog0_1d7lRwGnPs=;5q;jg)qS#~&QkE~L;Vj))8))o~pRTbXlDX)5 zg9RYZC}Wf*b-a4u{s*4@EEn*iPqM`fl?X=Cded|YI`d=@+arlUbp3mRZKT%umm@-!yXu!LCUxAEakDhwU zj=Cf`EkPo+gn)KbkK714x^r16uEVn@`f!<50q!nV92veqDeuAnKu-s_*sH`!>yFq~ zLDB`floq1r7B5LUJ)S_3U^Qe);!92;Ye#lAb6Sr@|CJw8b%o_#?K+xN^W(glIHi65kApi}oZA@N$Mg3s6aQML9-@sXT|oKwkBT?U=q z2j5%%Av0c?2tX?bf_x*o^}D(cR(5#<#?q3b$~Q1|sZ)ht3#=&h{_~}=ltVz4)Au;t zR}cq}SxDEQJqU{<&zhn_Gd^=rHVlrCt~NaR9(_xg`~F+sG(K@F4&XdYCH*h>3Gk?3n}Zo$*u! zW{PP6JbG9vo~-(^b2W3)Kf_k4vQ+f*m@mwf)B&<)ea^*5ryT@!okCH0Y8MCa^QF$7 z>k}dP?tWZ;6Ntbgk=YO( za1I9ATUp&X3n1Rl#>ZK`@W9-_MnxKXvBx@maMDD3u4T6m>U0p^bNElQ1{o-M76Y>` z&BnUbB~5(TO0RgqoVT-}Tmy7F_VY>pw?lZK0vZQr2Z|dfK>rr2%$HNtI>^865c2)w zsy<=~Ca8fr{1cq@n>V0!#QdUQCPK@tzJDVl@N_JUo&BabiR~kl)!Z)%!OtI0tl#Y? zK0k3vv(MywGZQFqDzHr0G#4nY`9&eU1~c6$xIXZ8p<28U%o})Vp7dw<^Jn>Sl!1Qu9Tq_i)q4TE;Sg7knHQGt$CcTGg3t38 z3I5@R=Gb&~VrqdVsN3^_yw=m4Lggs*`sDL!>U?okg2)BCy-_#zz3r!2HXE4h(h1kT zG`eugc9D2#5R)({j99sOO`u;gBIplW9hs`)s7}Meh(ao#^arev4T~F>ohj_2cTAgRWJqDQupu7DfKh%b^vXnr%<#G1#8Q0w$i%;{| z>*U*(;L_;Idq+#tM7pY%7om9Ll$}vF4*E-VekiP3ewCJ$Z)`4T`+uIc!$iNZ zPL*p~oe2~jJuDs5y+h^k_ue1Ei1T5$z){Jac23Cz|4H1|iQo@<6x5&;FW!X#LI_RT zHXA+=bMgC~T-H-=B6qB2D%o%Ezqseg$ROcAH?7-dT4lH zT&%>14w6R95jB1Em*wa*o6c!={~QMq#C1o{&uegVSgyjG)2_#K6E1rSDZ&$l0!Ha^ zCPSdQe|@e4jrNcjRgN+ttgTE99T27s&@*BL{@v*4=ckRSKLDHKt7t7ZYSREdzogaO zM{f}C!6sH+45I8it7?rC@rb8za5rL;_K&^izV1PDcT07Wypw$;0~}wsFL5Nh`wD;$ z&9|-lB&FiXa7Y(jxi@< z=&?T{mlDsI><~N%?O+=0iqN0M&D!|azbMw%L0?i47eR*ue?8G(I2hgv#y=(bofjx` z5!p8;s#@-Im9_E^UhhplGHDOt6fWCHJHC_qu&YwhZvmu1@wx{FTw3%C8mO-## zoyFO#*Zs9%ILO5szhG+U=iA_R#$4C#t(F)|G1OhpbyRi)>J&DtaJ=}o8hN^z~1qeMAD!?kX zoLo{Xe#n0l@=AdPTybBw+f}Ex5l3Y?z=|_X$c>9+xjuLWZ8e)ifGy$RZgE`yhE_UD zM6$cA&Pc-D@YcaV%kkM1Tc|<#iYCNlBZLdypl#p$C{pWp9S7ahRm94>M3u8PN;BF= zWV`lNST1Tw^x9Ur=pAu_c+Gd;9YEc3u5~#_tr%s82Hr;l?_;X@zrBbDVvN47UcLSL z!6nWc@Y@M*6FN;WtrbQ`PR&^^n?{G)&%dVp?LGew8I&O&fWF6q1c;BW0u%SzQD`pD z)5b{=MMhl3lc+1Y#7D~9aCMze$ebH2XJW3$Fw!&R%h0yOo!yySpewb>GUnYzQpB;1 z^m~g5E>R6>Zh)p0uXoPm-Si_nBzEAqDUX~4`U_5?kd2sfxJPG=Hp;m8ReSfhkowHU zvcbR?f<_+A+P;uTs$Ue<(xmhMADwkCZeeCDaDCstS&{yjaGO|sA5b?;TU2({bsKeb zuFagDU(>}ujV?AY8A{tvy!XvanFT4WepN-JP+0R_yrrQo+3MSwf_CZA>{?dfUYSoa z_Uyr#pmO{{ZP3`gQ#F{e4b?PfY^!vnqI^MIA=|?e__{uMVJT3eLVF+KfCDsGb}Jd> z-aeDkEICDK;$p=LHa=5pKxg@{mZ0C}y!i5%&qli;i>~?o@}>R!Bj-Kf$L_T01W8jk zKrfa7K<8YpFr)@5>XlMK~N-k9pQ*^_gD-D5^j(^(gi&mzFbC?mv);6)d&{3dqsv#tzs1R@oz z;p+hukflRQ8SQNF@5h~gQ6w0a|DxD4(FnQh_o>O_#JLVp^BeiC(_y(@?D}yIvv*4$ zdTLTBD_HR1jkDsuwOumy4c_HV5)hPr2 z=m=a~+nwd;DQqV*D05Y(yVov#DXs03-4*qL&SvGTQLe}We|YBo4r`J;#$0;U!XYF* zQfe2y98zl_TM!D%`>1qdQp^opxb4g>F|B z7cYEUZ;nstg*hN>y}`NL#hp$*-m~8JS19|OSahp%T&g?r`%1n_us`w$1`*HLiHWc9Ck>%Nu4*;ii_+g|D_U+sRU}Ks z@zW@L=NiowX4`UJ1A1IJ32Zg4>A^fu^};rte68cmuv)4Fea0K4J${j4!|gWy@?_zC z;c7V(e7N5Ykfc!x(O;=UYdFIIY*Vy~M3HaN*5cZ z`G2T;@2IBsZfjVNBGN=aI)VyF?;QdhL0XXBiy$Jsh8mD^RC-maQV)h0LO^8DVGd-&$+Vx#nDg=CDlY4F@=;=rC{k zuaL{5gb^o0NC!f$NM1NxSl@^j^cV@&Q}emLwr%QfTU}2H{Z_m`f@9{rkc(EW&Hf^) z&>M+rjRfJokfhO5P}>L{EH8yabpp$|DGp@L2{O>n1aZ={gceL>t1>u1o$GWa*Haix zk@|VyJfBVre;B zV`w_eF7wZOs~7$!k(sg9kc5$jvx=7LNJoaRpQYI6u2#+!n5A@Xgm}wlc&r{#cQC56 zzwQ|;?6jdtMC+GR9>Y)Fjgj)YFek;Ko&VC&eQNL<7b{U6Pmb-2YUd*av{t)XbSXRP z(%1U_AaS(yw6_#39#xi~Bupr>$?p% zASpuRCW}1|h>2-8xqblw!9WK%BA7~)811XTIw}=|}$QWWEM(KE}RSw(6 z*=IApf5*#Bc)!RhO#$2Q@p3Ro#OtNoeFm##b>*_{8ntcJj>DloirjSqX!NR?c_*&& zxJo`WQFCiLA}f76 z3M#IXz0W+rNt>+huQaa3dL}`C+k%UMHf&LRJ2Gu1(M+UWI?gRGvKk(^5vI^!I%) zhyw63%rh-@Tycd1qQP3U~6RaLegS%(Ptz6#zE_u`HD~q zJ@7Ivp-?&`B>T|8zH{^P#s8%nkEkK_e~B0_tlI3epa1l|&kZNW#Z43?=n7SfOf)un zP{@cVx?)3OK#zZh!k3EE9zRUQ;C+_%Vw^8=uT*ij@E7p}EF8JPS{m|D{ozE8yLWoI zCRo&dKLzfdv5+b`;Ip?Z8M!t)(b-X)JxTRt^kX}t0dwc#HF;t&uWNvomO32N zAOSZn<8Q(5h<~tSVAjvK&_u-DQf*x?*Hldx9@-h%8QJT}|CZ(KtN?ro5B=%8#mARJ zyj%aLRY;4E`NC4ebj{@pz$2~qHsD67p?N#OD9Mr0wO7L2D9QNGAGf0Twd?;6MD%|Z z%~vvrEu?D=!DshcGk1%{M2O6W(c*llm#KDXyW2UY_$C8&De9$79*kb&FPJ!7*6}>K ztFrD~Ikm&r1ROgaxk4XS)7J>NS5T$y&rBoo+^LNoeAqpPQG5kO?wZ0LWs0g;ukl7Q z*d$YM+&fE%%I?mAC5JbsY1M}a*<@;O%62YhwPe@8RS03IhEeNoHr4~MWeo8j?>5Xu z7~dL}32b#?!Wh%H3aS}L;rMlWfWQKKynY8kB=$EFD47;RyQ}426edpS6L|Av927d) zSxi(Uq=#@nxu1eT+_r`?^f0{g@!-r243>@lyiJ?WcBJ3ZUV~qZJmNdXU8d(bt}YA{ z_0Bajr|wmh|%w)QXoY zQvn_xc-yNqeexg3Z$Z9fLT{}N*DyS9!m=b>KfQT zojDS8V?g}4CqF(c{PgSI0Lh@^dulJwv{W*T6ODcSgV&QLiE>-y@uwVQlok!M{);U% z-rIK|jU7$lX3E|b-sN1qmyy$th`;_-qx~PDr2hp-!&UFD-XA9I)svndrR=tugJxvk zzP_5Zz4zkv@;%Gw8#*r@Uw?bUPn(fZP%Tq2(_O{VII2|tY0=Fl9b%g22_f>quslqq zU*3fWt;?PdVe-jZv(mgJnZ%noEEVR&(IJM`^oK+_*)zw%b-;c}&o@J)(_MDB-n7bj ztO8k6-y>LS(CV-)yfy2UzIo9rl6FsTU%9%sw^p$J<%&;cBO@V3i4hbeMweKSR$*>Z zPpq21eH?Y?S?fC>#H`>J)GZULgz@=>;FK;uO4hlk{A$1{KFL(IR*H-{ZbZteN0U;< z-*dTLNvbU4L8hneE=xJHt}SBA;cq56_nnNg?rS0M(GHEbckBBim<7|59yDaE!5Fs} zw99_h_y2K4M2_n*S%=Xfsm`4~T;#{4?U9Fzl@~iA&WxwoD5=zb!pLs)k1KqF^`AOA z8MuO;b^N|W+|Cr;-?PW}eXxlCj=JCw(K!utVT+71d#d72(g>PzQ4M$yewFWtkBhKt zjNpv%*!4z1|$ppH`(^qn}=|hi(;hLWEMP;nGum6&?a(2rD2xM zC{o+u#2;5CA*p9?rKG|)fW1O!I&nL#em;~#s@(i@o#0f-#fV=aDdXEb^}nq>l(P+h zXfbmG7A|RHWof-=w`{Ar_joDl$C`=C?0(0O9_g;!UfPJ@e$kQs+yeXmDiT0*?Y z&QGab^GwvqPR%vy7IIx0b_ET8^BjN|FV3#b{ow&;d0LO`rE6JNU4Oa7jMMbNtt5sS zVL6?IHH2@7(H}0~89+@3nrn0^c!6sVL8UphZe56~{3mG1Ov(V{^O~{kYeR%v?gWK^lpR$|Dc2d_% z&t??^(o5I01YgXKy$G}Dwv{t7?e`CJCi7PSM+v@sa#wRV*n-T;v}-E8+II5eTGoUX zZdnj|b#*rRy}&b~KK>{c`U!?$Gp(`Jq+4xFq|m4Q7$pwU%`zY>e1`0t%4?xp5666H?D5`s|$BkyR&Y`KXvSBojuK2FTM@$ zZjclE2<^IA{EjyB_kjsPt@Cm&M)}Bi0W<}ci(>VN<_>r6x^F;L25e|5yx$BFk6=L# z_(MIrB)n8?g>!WxuQ{v!>cia1*o&1<{0=#OgH@qRtwkmxF)N(U%^lFt^N}Q8b3%eV zwVhT{g*7zP(omLY$eIP2Np~ zhynu&s~-Qrk9=c9!lyhehRpuz&V1M?6SfI1FNTOrCf#u4f1&!s2euLZ*1?YV*%mY# zvwIT<^=DNuD=rd4P50sagkNNFwhCe4nUEKdjK%OIXawWNcOwEHc6R5-0ctgHVIfSP z5Q3$pu?g8&Ua|FXngU6MKcvg_1nGWK-iQy_Nz+_$Nx9fH@}V2OZbF_MDKgrq6)*1$ ziwVrYR{CzN#Q^kTr&*{p(ve^By<0t;`D^J>?`ei354NL~`0)HOK?D~=FP};c81a*M zx-ZD$x?e!Lb;YorG)g0qS*kHL^3RT1MdU~ROf~*QzFik`7tFW~xJv8xpf+je{|gnD zb}p5`>$0mBK;suV*U~HZJx45VPfR8?rXg68%!Za;{z9_?fm_6KrL6mmL9UjxG|B3q{QqD=2<&vm*V}IxQN4Q-=vo0Ow+k}h* zqXxqCg{evsm1;kKIFG#2Re?=gh1kwD67Mto)FM0;)2NDC2N`0RgX6vmj$~PV7&bvF zevsuRk4Gu8*d`PsE->~}eFXmhP#(!QVu_3%y%L0%r$9JelrKQT;r2NR_Ueb~^3K&0 zG@FUuE?sZSIpjibq_Hz~FJKxV*IR{LT_*xra|N9%;S&VEkw}6@y zl0$_Ph9H-}&^hdsUQ3;*APCULAE#bd$J?Lk;5~Q3Xm50SnFn+Q^;=kur8NYr}IdV`f<19XtT6p<*7u|&kJeRrv$OPfxD2C?=%GllSHVN_TmnM~knGABB6;~w}gG5zF)oHBZGUath7I_VO z-6mfDT0oq_{NmRit9mYOtY__Cllw}QCmKP%l&_)po+VsBq1SMCwFTdyImqI>0ipXldGU-HLktbUSO9`q(@8bQmMn zMw?-Ou|D;sNNHG0`^%CCSxXVN@0yMB%TpV_owee+R_G3Ig6n<05DnfG(WWv8=dxQ+ zP-*3^^o+qQXlR3W{mm@aGb{RqLI~~bqj(8_KnRn}uN9g~VQrd~JyXUl=%Ii3{X)ri zjgi;#_LsMhSnj3VymxpLbUW0dOB%cQ0thjh`Z%d4y)$WGIysIgbxD}i`&2fQtUS5X z_4f7Odu+G_I``Lr_@mRM{UK?n{3DT{K$`FCsdz<~tEcp2YB{(S&};Sf)3nv9ev@nP zk2;Wx_VhABLk-N{iHR6JQ{kE^6DhaXnCkc%i6GxWtZqIJ8EgsyX;tt zu1MzZ5h{4GxS}N|)D|-p2(qYIGOOr}aJ#2F%xyhc@Ho+E{6y)co{3&#w?)uc|10|= zGOT?J=Y4`mZ&Sq;LMEEE;l@FPjzu}3;5GD>A@e* z{fL2F%MMkH4ndJd>#V(0GF@Xshe=)_eYVmz#bZAFz+g_k_LicVLxgp$d#}`+Ru{6W zmJ$L4w=*|*meKta1OKOlMZ3TUr-F#e>!FVJx?j@X}{RO(e?}d00HW%xQ z(cx0)kszViiw@%rmd$qL zAPLNf^y|iz?HS)ynQ{8+@iJL&0vGTdnx>@dQN-Y#;*#*fwQtg?RVt~sOQuwQ^}DWR zQ|CZ2mNX^6#}#<&pCJZIfVdTITBHJbc&c56H*3OtsMeri!3lYe*#iMU3bx*du}=J+ zNfKh1?LBp0eCfo{or7a%7>6diz@WdjgL5gSDihy0AR|70s2%CPWN3S%uycmiqtRJD z&?9c|mo*oLw!EsWUI;1WDo?C8HX)j;4g5rQSJBMxj9Ma zI5*PeA3uHk(H30NQlLvQ_FfEUMmd&q2R? z9S7V+EGc9Gg?f{>C{$ilKX8{5RNIM{zAVFryi-|-mK+?g%6Hx3bcdRr_aoTq!s)#; z?w)v;Ppl_KuF>wwoj+D5&t8p4bfMc?Hx)2A*rsGyRiK0tx?qnl z7Zcudc!poJ{s&fYl;O97VTR&&mDFW7Z+y{E&?sFJ@pRvts3AZNDPyNch!%$eyZGbHW^cKw;| z6ty+=29jaz#T6RH3)I}yh%tNGG843&RLexW43yK7Ecs__JC9AWmQmpoS3A}n+<_v> z-9pt(QzL_rqDpVGqnGIVQHLr5{+7_$^FfdI&5*kx7E}*xF#3MzR<3h1fAE_~w`-5ddp>2Jh8RJ>mF0k`x$19ni8v`&MbYDp%$ninM^ z(exR9i*nNl)`2w4K%QLOX{N$@LOMcEYkvL*iK6GinE7R+OynFF_UT~@>vIpl8_B;! zyK0nMLVKYL;&!@w8iy03MI-m#A3hy!H9Jm0L~-~88_TEsGUC3}#7CAfQd_Pd_X1A` zrwxCI%K&@*2<~S%(8ff%u8c#}Siomm6AS1k*Na%jQ5>ekM`)+xP2*_!9(@RQpeY)9 zX$RNtYKW^9ue942W_p0ctuS`1=Bjdq(KrFF@?7YX@pc8MV8Df5OcQ+Qp~L1{cg!{r#AwDp10+ahly(8ZaB0g@Q4<+2nvp!? zmnYd93K2f9(as^}O5`8b8|RHG-V8|+&8WSpnAIZ5JY~gb6l;YhvgeTCVvJ~V#t*5= z|CEdPWh1|2Ws5C3rW|f(yER{s9pX{4H6NWf>J#D^>7-~ow`{%mcu<&Gc9u>{nn82m z0G#l>GlOj+*}XBe7Bz-clqoTdc9(JM?}7`~t|5R$L?$?p#c2#AvqtNib?n+PlB?juo&ozv*$se8EtKn!N<%h z9cfWmWxp%uaPfrrplW8{xh@vV6|GVK=70el>**9|7Im54%`}6)p)A%tgYOUbWDLvo z_X+rBe*q zReq6&>Bkf2k~mTlNz}30mXX8EhZYLQ`mfMRSr3@qIYgS<+kBOfj}0`D#*}HeK7)uF zpMJ}uc~T>d)K43Nfw`Vpd?!D=`S1EH@}mx5E!&GwN1FhwVyI*Ea|3Fuh~jwSd`#`T zcX31$oP<;8X^mG9y-Z=Or!*@@v~)}s70kk=luk^?&4hFK+;%!+WIZzJ_f12^>wZ#_ zI1RMrb>7=r-EweYFUJ0udTRB@74^t!i!S#?F$^s;0rkPbNsAR5eA{U7&}vHmMBe5N zryLoz8K^T=x)DaZLkn7jd4HB?FbCiAl>TaSO?KYw_54h(eA17{S1zd;Bw-; z1NG@t;dlW_J@vVP->`Z&QC8G1>=pKqlZxxZO!B;C-Ygej=$BAa(~T{yO>-0dr4_dS zZ_(mk{@!!jUq9E^8t`1`6^1a)nH#B?7FFq9Cf!{%T$r`@himjq9zWnZptf~Ob!s=> zVYJZA`{8MKE-Gk=3fMyWnv%6enb)VEDP<(iQn=@h2M(L*H`dEjdo=S6Tt;xbYSHqh z-B>49!8`7LroN}upf{3O4(l$bsgOM5%+M1BSS4AM5quJV&4AfXpW!~^=Q00Wd2QPZqmFHReyVZCnmkox8dPw&b=T)&Z3K$}I>H@?+A7j&JBvHGih%$H0Q+vY&OB?HtJP^d! zBGXJ;!IG|0es)K*Ym}uo?pz&?G{g9^*b3jL3@F;ult_f6Hh(N<=@RgpOtNTU>E!vC zHq*Cd`h5~{2gdkO$F)}XaoW6q2))gN^`M-%fim%dGG&%uRJp-MNttj)-gzCFpW<%H z6A`7Gc~CTl8C`L=IJP^2Y0}$!&D;pNpmj=PMqy5AVN@f&*qBs*lHii{%PMnx?<3-9 z5|IPHkXmGN>4bSw!;pw%)o^aKwx2s|lg07(8V&jC#|XOTW`o?!^Q~{WDNN)vM=Iyy z5wx4`_VcXHoN%j$?KS!;=sUG;GI6keqr-+k)&axz0}pjfWI|!JL0qc@<4D~qdu}SD z>ljK;_MNmlNXLZ*bVj%Eap8w{e3qlv-SYj%30+~*bwmd2=-5TPymgk468?fA4!f9@ z#AjV^V4Y{du)2jPMNgt9Q@E6(?uY1LkQj*;wJiI%+S0-Vh-!35j1AlSp#=DoN%HHP zAYp8lBUtE+`(`QhD;yPo12<@o<>#fQY72rZ1qZb6y}o&G^)>CiH=X2fJqSGrCL9lr zzaGw%YBJ0KaBEK#4!TRjU!mLE! zXP0{x2;zefoAw00-c#9jXv|>u`ON~B&+~KMpF3*mblpT`^6&5Qg^L;0Ni9-D7O1>i z(HJF<$XH|}sP{y$Bi`#$a6BgXCrJKr#pyM6gueH=av~}bLtGz6nb2@Nt|UZOI1v({ zc_^u5O7S6a`9epH$#YS5Y~E%xq{DUqBASz%(2a4ogFdn4eyL8U^+7=XNOx{3G1*$* zIAB{V$AUD`@ob+U$oIv5Arf3a=DEj!spz9p5|gtd=wfv*f*L!-HK?9$w_lynZg|sk z&emGS#@kSN@UY;1{)zI9h>PZ0qsNVHUvfyt}YiP zW+Pl7y{=-KSNg2NJC6vFbRd@+35i6oYO10lIZ~kp4vHF>KP2X?0F*!Hpx$0j> zCYof3Trs`oXeEs!we!iWs*|VBi8GfbRp)jLT=qA2Df~^m_cBfrk5VmF<-Z?q=zqmj z9HYuX8yYJV@>XN?v14-3Z>Q$l{+>_7OYMgH1KG#h-k5eH>on5u$LR{gYDC zJuHNBAk^>DfO=ohC%14hFT`Ebe$ZYgbOzoz#XOHJm`MY4cz*BI5%zA%g?c8<9%tDV z=QOGr|DRN`|JG$!nYv6p?vWxaSOuP?iEur6g@@8B?@mu_vTP32$diNoZdj<2^`3+; zl95f%qIe?)^6n!>TrS)$?n3S8`=FTKK(}Q$hu95#?=KBPKON&x`5R5;DlVKBL-;xA za#qEVY>dO*NG?IV4l1$ z*p3#Tp%{^^P(Mx76FwDR)C?N+Kr9q{A#HzM>68*aJ&or_cZ=p&g7w(63$@Tz0TSQl z%*r`KS+skLKayCd)7^n-q4ywTe(sQe;J{0DRdt>bH{-Fo9|cvpi?I!>UKTr0WlZ^s z4@sstbgXW_`^6AFh4n;Vl_{ro0z-E?_+bgjC#c=-r#CTz@XFbN1VbYZpO$+7p}O^KiT-Nb$@zHiQeFc$R9vvpw=LTZAC zW#(X${r-8gvHoE-Dqjx>c_@Ycs&BTVLPM2iPdUuVxn7r3Ycey&qV77FC%gOR*_Ccj z=!+oEqw%9XxIRwm@sppF4b|N<&Bd-)OsC4ebq1LM!qP@3X5YT)dyVl14>}$USE|kt z&IwMzdNPV4ds}+`YWt{!pFHOS!hc*r`oQ>-g!&A~8s)kvw=+RM7^9Gx5KYb`2g(3w z?!yv1Ywgy=r$twXBgza+?lb&P?-X)zv`M;OqDzP!O5D>?R7$@-qUIK*p4E>HGxuEtzbcT?+#mF1l;Us_mKMz@l(9mox@d|h zrq7nQedB{6`8DQbXK|dWgzG%>GKzD)m@*r`SjUX;;fn0k-=cheZ%3_c3Y zQnY?8U(&FLQN#jO`m-aBrz}$_wS2+CY{k#W@bc~8^LB*qtaS`LSb29HO=cp})1Azd zME6S{oA-0>1R3p*zgG3evz4aKx{f1a;rh`mCC2cZlL*FO*B&b&z)hmgt9}W%6-UpW zV@Cu=8*%Y9d*axnuFoFbwtBgaHLaWCD7Jw0gdr)8#|QEW?_iqJ-yOaqr+m>E-{UQZ z%35v%nix1ZS}*{oR;L2}cPHSrZxMHhk%U3KCc%yN52T0~; zGPE@Akmc`k6V+(8ke}88H6KO4tv|AV^bXjn@|`wtOLP9#O1a^edDCFtK~QJ@5cQ_4 z)WN;K1U{@NnFb!vCIS<{l`!^$hU<7cKEg}%qL~a>n%}uGNx0Hh{WYA{9b<5{yUFPZ zQ}`=5t2t8DA!f+UFmHX>IAiYTM*c-O@g8B;$p^EtzznQo&m@=Un*M6;DF*08G zK7DgvVo1fV%%Jvt-FP6$^YK9xoz4R;p+}j;rl}{^DuTX$T%k9KVdz$2=-goA*OD7p z-EtJr`cQI_hB9E#moUlLz|TcJpAT!al1(F#4MqI6ECv-y?uIar;kL-T!??NJOqfm3 z?V#>5bj-m?J`1?~;Epd#YbvW-HY6*nco`MpDW%~lBSt>#!8fq`xvlZSq>1np z4Je$U6|~R>ssv>JE~j3u&ScMIby)w1^w#q&QP%X=+k5B z;`i$V!yf)@HSAB-y4D|7O_e!4c*%b8GFK^D2lGzrXO>ycGyk#{iX5jpTwJH4Yi}K8 zl?Ns(Eo#City}9V*}chFVDpZL!eEyIt7)kwrjY5-A`N_XqP2g<2S$T zM=y&L3b7%MOq?tepBihcB;!8K)FET%j`eN~Vd9%-WE4coV7^INTf*7F>>^&9nIILO z(aJEXG-uWsS>3s3CE$Q9f4@P7`0SRLuE}xI5uH_6T97-Xe(~;2uc&;7zZGz zprwXB&sbKCud$X7GnX+AbX{wbVctC|yI`qcJh6odBjS6f-sHK&99Osbv5L z@doUK4r{_I9M>HvuG@kp@H3&oZW^Rs`owBZZvzz~vsQ&}dk?R@VgF#}cF-d)4#Qq1 z`Dv}QlMfp#c?%MK&d~lQY!#uD(0th9F>iqsOfza9OO+Gmu6+gr@`kxiDLOgsrx7AM z7sb91m10wvxiP}ALYSS;2}#_%Zw4v@d;?0&*OJVh=mqL7U{Nxo_6)}5p($p9V1w+> z+}nSNEYQyUaV4>+!>NP7H;OKdl6Zw6YF-xC$pX=`*!h|BfPIo2X&7vml|kvm?-_6e%iW zx)?5I&345wx9<0zrpj&BLYGg|k%ExUso1sXh?|k+MStI+?r#jhUomR`O!8e_36mp) zlo7!PBK5D}0x?(PPrQt7+g!SqNp%&3Y_$N6ig$Nre`>CDp;u zSV)DQd-9Y+(St5`qXrISQow+Km%WD#A{jqS8>%`TnpO`ik!rd5g5bJPm7llPSVp*q z+*W;2X>10J2^qFXj4R*1X?{aqJEinqJ~&w`N8BV&)f~jU+D87xnx-PQRoPu+SETps zQ+RoB$@8zB?KX8G-G*!v%drg-|TE2z7fSDTLD&ozaB2HMATTS8xiLn_XK$fbW3>dV zngD^0sNe(&SPf&`nu1v;>r9kumhtMB13)*%I3Mf}_C-=4Si5wl(=-aoaAI~Y2DtciJ*%V=o`2U7{kKM(e_>zwe<5X-Zs#z;9m~GwWRghCmS8)`7dVi6V9D$tT5cm#m-K44` zI9$mhJSjXRe&P!ywn%i1^#Y!&PafY@B-lFBGEg>#>2ua8fb*>xkt3PfI=v8x7qdPb zEOXx+xDqIRU{BE`qX_V1+z0S}ccTouQX@A&s&YIIjFIO6yR~b-wf{Tu{$;&WxR(B9 zZ*~!BEHu1E7U^mY#nvTa3u0a9o`PrO;jZfkpWjd`M$uW@-1UDnGL5{6uRtc#7kKu_-=7+Egd8Jz^* zM_)cB_;k0kA5_F1SKJ{O*p3rTFyRrsBI=s6wIXVh7UOl{Dyz;yEi`HaR=c9RYgb|?iFd-83 zA>)EocEE?gHt#WPZL_4)CVy6xwXB)oIxoOn+SqxXYWPz8tMa10H~ z!hml{*}??q=n>|W0h_FyyZpdw51SFr%^wA;|8>>=AKtltzh76)iX_O}5p)iOPTeOZ zTgpMSxC`trjlME>BY$lm%OmY()7PtR;4!w!||IT5uxW$#BX@F z22M@9f&tx3T9QS3ah3zKoLG=fr@G}3N&6f;)$Y<&Tl-U7Azv>aFmsIS(z2-Q2>fVW z?$fNA4;&3av>Sg(o&5d&y}v!||Kq<&J03Y_@hDrD5E+78ysra#RHpax#`?Toa+#aM z)q5q5?bzi*Q_l=&ar|39s}3)Y!E@yeqQQHkOg7T}gA^&HF9MSRpALmlG+Ot*fWf}WI)dMb9dbS6$YDVl-}iYk zU;zPeU_@R;y6PnUA4#Wcxy?L_dIc?CiR9OgdjilHyFx(v7=Kppxpu_im_?d{lURz2 zfaZcfb2}A8s5mW8fS{m?K{Tf>lXaFKLAyuTrrMA%cdv!`+qg8b`;S>H<2_QV`n2=| z%@f1VY#+i7TVF)RN7kiMrKU1L!+8!HMF)9JVA@fvFMxSg#^szMgD=!XD1b zy&M2#o4;0hQcfc+ZArw{wiwg0&_`Ny_JW{F!fO-mc6;R5(b zX^-Dw0y4K5=}eiNpELf-^Y(^Y@q*67g>jx-ueLN@^OeVSQYiB*&P;1ULT%+Sze&FP z$&cx1wmbY2iGW)X$}B@*90sP#*fvA*Pa^)zw0)jypQp-3;%P{21v^Y`_+!dx;B^y* zQaAT`c|?}WzN!&tJrlAzGo*tcT{o>>6b5fw-7u|Qx$4lqBrsuyxKY(ay9R&x`DmRdc0|d&NCUGOjF)!Vd78f~ z#k%y7DSEBZlP$yTIfP|em8Ako68y{CU&L}{#Vt6iIpsr0Fv6WlsxQzf(h(v9U(X*B z=44N_K?SG048i(xDtrt`lf9bx>dN#4$~;=z9A*q>tA(>pqQHKC`UnFx%h%m7!@AXp z37{7IaFy~LljI45HNu?C5`za*f*436br5HY7VsnULDs0zR;ACCtWQDc`L1$dJ#VJGEGso> zo>E0$%p86xSTS$1M|W=0==3NYsL89B$YAyhJ&RKvLS|S?(&?_7&nkN&jwRcLHd5_>SK3@@ywbzM~Fd|65H?De)ixlc|rg9W^bmLuxdMx=jwa zo4R>KSQf^W(eBjxUR9KBr9pct-~BZU1i*wb|W!j z-5d|;8PZR~s!--Css}bC3e>@VCzC2ZElkOa8CUokk+>VAIEwxem2H*tb?(!~X@c04ef(NE`wIsN}fEh=5 zv9B4VWgphC!^s#i5qTB5z)t!Mjpz9BWm&q&V!LrCx0@QY_2L+U%AR+?bx)NT7rjXq zb=eg>ki8aDhU48iiB;VsA>iBAB9}9nWp1$c*DaaA7~XDlD395ebcs!(^WgF7e_@#Dp_o2 z`G&+5>Mc@=QL8n=4a~89s}dPJ^U0o~z<~6biw>+O4Wxv~%jOqEvkmS2<#PbMcGpstgu~PyS8VVN-EQ~@h4s0-ZBxu*xbBRO!k|cxZkEtpP0GVe0iL2H7}+pC z|4cF5JiEGr1ib{cM-%LegQ@zG2wYTAorJ+ID4&5y-ns#mda|FQXt^7{-YGseT2TI6 zM$oj|cf%zHoR6^2 zj?aDFMBPE9mf$q1T$}eiI=>e%L*en$;~y*;F7q|(I5YsWGG*()?-mHkS`Q%f$ruGL zHr<*+7}NCn=2-)Yu;{iZmp6uKX7Cx8>i}!faGt^==8Fi@H@}GScDckZJF1qZL=5?>3Q-0v@}Ktj z_a5ITt(Tzf*!!_e4;L@9(LoKYIm?}UVjH?1<<>8S46yr*VUNP>KWJUen#zMl3L|-A zFI4AQYS$??7*a@s=|7?J2FuZWiq(esAts1FuIR1&YEX2d3M;%mja`=RawsLV^|63J zM=>3t1oeBsK7X0qO^5B4PF&V~VGH`_m8Y<{J~ZubFpxg802&7=EC@UkAVvS>9M1KG z)VFfOpw9<5LHA#MF1P-HuG&t-8nDueyoe6t!*n&LAbtE;r-ZH1eO{R3w-nUDY^}8G z+E-0ZBe?MU=i@ye^sVF-FpNH*ofY{Qf9ssDgX-^E%f}Q&8P8@x<64wcHzMJGTuDNG zseo<9f&8O;w5YnxB>T`|9GAt&nQX=bwydX_Sn-kD>6x|#j1r(nf?ktm;%6Grm)3sn zx-FL-?UIOI)p>(nhOO)eK9q#(2y`J8k6x|`P{H>eOz(S52X&3Rt8nL9G}br9ljnZ| z9>z~+^ej1T28=+K94*TRJxs;(mYFB)myefy)EpatWZXle^jrUPJ_`80(W&6BrZB@jv+4IG zHA0Vex&zZ}4^B?zu%~wpm|m8XTnmPQi(!(>cQU)V-C8XfgnnHBLmwH`! z3hFV3%}~n{16C`w&kk`Z?F)1+dzyL=zZ-63DpHhOv`%xFZtIia_9(6=0ii)E{ajUH zm%-`8Dtm*hx)a3+{f`l?hmq-85nKe_X_C|Sp4`8j>O|sY8HpN>(m60dnP%I zHN|5d)M<~SA$J6N#D11e#xTgPt79M*rP+tjWJ??I;6+xQCZTst!v*LiXhDu--xn zu@e^WRug=1kA($2!TW^m`Rpu-sg=`f^EF6UT5`0(e1h@#$3jQ70oIGE+xMHbbq`VZ zxas@`f%mw%e;X?g6Acr<+EHDzphW|j?0cNapbIMg30;cUSYe@4sJF;h0W*YFuE@|j zbr1v1Nsht|$UC!MEQC>IB?|ge2}3y`Z(V3J$R{JLX%U~1Pl38PLK|2;JD^dBI86)K z6t#NJKWms|ahiH@H)5Hq=I3q=hHG~;ip0azE9;&Ai~$m}09Z zuW4DqlYtqF2MEf_v*)7`k{Oo^R zVfUz{k5y_#U*=u^t;7(;G|^pYf3ANS_g>%NMwoM-wdyV>pHrj$ixs8wLdsPkYw*mv zy?ra2SA)P34=fTlTAZ}sZL4fx@6U@;3b@=*tPMDd7_`^ei1@|g!Z}j@@Be)LYv$^I za<2c|O3m4ZZ@_Ri98ej)#=3e-?^8Vz4Ww~McbR9B<#fu*-k@np74hM{roY)%s#g@kOiRI;j=Ji#tmo&G{q>ZXK@*=BL5>_5_%ATtq+#;9g zT|MHwi$`5cFc~Fum6NMqXcll5hqvo=jna`*_sMn7n+iJK*KfB^>ltN84TR8jIRSAR zllHgftlhFC-0E-h)@4T5mBZn!jubR?t(q*)AN6->CbXsjm@H2Yv5>(yuoPUb0~qlt zjQG7A@C%#pnXVxffE4;A3s}BZ=E=NWEm;RP@3R0r*(loqH>ggQwf@cGO((|f;bIfN z!*`QUdAaFE8yWD23*UwEE^q!*U)VLpEuaD{C%wH^c$z`Reb`C#2=zW_@R=gm-A9A7 zHN06kN5$;(Hm4obf}Lf3o;%Oy1%!yTb)y0trFCd9 zDfj)tVv8JeON_7ILH81hu{FWkQRiE!WkBA~%ZFw_&S=0Ve3SuhF7E}v(TbTal+rbd ztj^qY@?%AY#BVLN&6Q0B1LyNEl?JV>*@2F`fGUnxJvPO%*bKU4KxXxbGxNNRkMeCY z*#94VtGd7d(mNt)p{KG{c%+fLc2+^V@t&BjJP4Unfn;LcfqAAvs%xgx5zo?0?^_OE(+;uPGxy@ zNq98cD4IS09eXZgLec9YkmdvTJFCt6d=s~A2K?qF>|g6uwBcvb`jQ7eNs~(y&<1{5-V)P zM~>W#0)-T*B!;V?LuSHK<@VbFf)ky+b>M*ee3P^H&*T1V;I7rw?d-1nZg}sXz_E3) zc`nWEL{;5C#&ZZnb~eub^ITATd!7K=KS#9K3HiqsK$iB z=7*gv<0AI*a=LnTWQ@z6<~uj{W~3W-5Zm;@BWWFHN*7uY86DdrM)Bt=cu}YRZY$EQ zXajnG4o3Hk16ICUI02I$2dFWln>=tA$NSdtKd*@^%}S-20pe@*>!?Ys|A)A@4r^;& z_eJTfP`nhU$SlR(T~?t$f#MRJ0>#~-NLt)!ae|d1AwZzGL-FFptw@4K(%8m&;-4* z6bf9LvT1JKjUDvuX}9nbM+VyI_4c1`#Ws<`r$=?WcCXB;heA8Tn2;Szh_bvXX5ds& zjBIi~$*=3yOR3)k;Q~SsGb!3?5N{jiwA9bQ%g1)$V8qs4@z&ZjQI}CoWsa%#TB9D+ zMsJ&<9;!0Tbm!fV@f^zJzPYa|2-gYeAwYUdtuE{ysb z$X)(|w)5dO&0KG4lK8LFa9fbr>N`wcBvN=7no_bckJ#wI#Sy4^XMh+l!1)CJ{%V;; z!wUvvP6sR~-1X7+{?;2h>`w@BVX4Z4xm8p-ptwCuTx&}h0eO&VWb9x5rmn&8=y{`y zN%vxnDS6DsocSZC*_IA!_bT%4zd#?g1jgm@Wq`1yZLLS|)a((b`B2gSQQpgw?6YREDZf%7H5mOIX-Bj@Kx*8(aDJ<^mu52c; z3WmX=w(=5mb6?M@3_x;Bs_*s@jFXyOBy=cLeq_k=4T+1lyPWkHUwD9xscyJxypzH8 z0MXXBuC1eVdI|YT(a5JAW}K;IW#U5tD~p{6S3XXtedL?P z#7>m$2~xrNXW^<7BgrxE#wl~h1d?ZK#s9s-!q!aP!XKWm9Rl_n9gx%~t%?vx2GRh>ZG+)N019fvK<^s?^{= zzcp2nN{ba{ypt7NL!HgCia-g8HIa`<2s9U_zHd|L1*ellvunk(Z%W+h!DgVUiTXLC zZ><3i5P0XNyMr4wmlz~hj4AKB-pWGC5jz=FuQ)*ZKl@N@Cj5Fz7@FKW@a}WYfJ|Ef zFAvn{&!$+8LjSp&`}dQ&w)|D84?4+|X~KHD^a`ST#zZ*u!VcLFrY`O9jNOynbF7&s zf8*qYa3VBV)OxK<-Ty`?h*KIBd$7sJ4~hUFzmk-kczJX2-}3&KF8qA zHz)b`AVT_Pt#{l2pi8G6-tzfGfq*xi>p+Au} zoD47=olE29eHv~T-8);XLyt?hS1z{0XncmL4Q7_7LmHrKe&B~ zCK&Q@S&R=j70xHTie{Kncwn~om~!9DMvyfL3Vwk}LH4u7$~35dZfTNb?Bsc0qmjoa zy6Lb|)+s_rF|&)PI^-B&B*o9B^pdASwOFj1xccP(VCYH6=IUkT zA8KK3vd9ogn7u7cj=B1$!KJ)C*QENQxpupR)Uuh9jViiw$ndh;ZQ8Yos)Y(4&|~=s zR(!)I%M~t8TOqB6HczG3kMiD*OtiksPYlm4JbckZUTT9O)U%(y@WEW^c!4{@<(L<; z7IGJIm*b?WxkpAkfghvHWI*LlLsJC9^%pF*(RT&j*y>hpcRfYjPYNIrx}N}e1oA@a ze$I(_tBOMEqQ8E<=IO`(AF}Ctmuv@6RF}LOTF9{R*YQOh2|n`&OL7_)A9X_x)2sMb z5SPM8E!f>xBgT=&WVvI$ubL5`6C^v>?F>U4#`kH!kEcr03LfksgiH~N{n$x$y&;sB zPHenfT~2|RE{RV5VF(p!Qq@#B4(fYXOjs$XI>^Rpm}6|vDdcGAsIfP^Po)WYnzS@? zHybiWey_Dy*6OoVVdO0x!0#CA2;<1znTF0%NOb5LhX{OLiVQg@Tv$D}KmpmH#;3nC zzVIOt5$WZY0VR&26Xp)r5fvAphLp-3R~J_+d%IA@bq;2YAM<~%pT-}nCM zoIG^!f$A{5_F%|`4FKjO^~Bpnv<2q4x7$dpkpufukk0gIMTn_iuM8z(3Nf824U}x6 zGg0BIv=pGrfDW?VhV=zJG=DZ$ZRCBlW3jYW)_D~eR?ZPfBR1>4nMT_#1D9A*6B^k+ zS>O7vA8y~T#RQ_M2Ecf?mIb$e!#E(90SjCa)biYtuqK(mlTPzwy%Sy*15L72ww(eC ze6udaXpd4T#<~6(%@H@>;gRzZTc~E*y6dC0lVr!MMh>GkujHYZ)Fi7-=(CtIQ z0iJ#x-2`KuT*6n(FeHEd?quKF>Qpmnb6gswNFiMFNX`h(pZnj}tFv4=6U6|EWEXNr z4U+hRL`QG$p&XFm7ou}fc`Y;|9VFs6(@Hb|aI>wM33Q!}SH$-DoxdT;I2 z7~>@G2UoN&WII*_dukjF7n#k}nYQMrZzjyd9W=#Psw}Q=0rEBC>v^pjbR=zG+xMtr z4WmzW(r)^8*{=ssEo~M`Uv^4HFPZz2ADRPmpN z3H(w}}%&o_YTq_TD@C-hml$O#^n>9+e z%Ex|6>c;<#V>`b6;qMXbKlO(HMi-V2~WN`DBcDg8;!g|wU=}fIGZV=6u{psV zZIC5;{~2x2z3wK+f{3(gk1d^9wuDU(NBW@njp6KQdDpZpL(aCnIQV5az!#um(k#XSeER|vgiDup6F|Np%F?~bnUXe=Oe!4V*PR2WEr44pox5sanU7nUN-^FJ0JmTMOpc5^F-T z%T-*AlRXn!2f2o_k4NB(Z{T_XQTNtKA{p?}gQxX}3}T&nq$TBcFBTkC%S6An_c9X{ z#lKUeIPl{iNY)Wy6WM+16%=A|geb39Z*38I3R7up)z0|73R3Utfaj0r3EWWyk)dbR zXa;`KN5S*EzSp84X@k{rVPC64JQoY;N~*4eoS& ze!1;IPUJH9J8PD9&J`HY`k0hHQ&&>WJQ|yz-#C#l#KF0DnH7HyV^sfG@{Ga)l6_XY zCf6}pL+7HCz@MupkEGU#k#Ogy16rkaJefS8@+g-Ie-7lCRHy)S)fOltN_^=RmD9{US`!^;r|F$_XK`&)NqGB9)_Wl{!^ z{N7IMLZ-9u$DX?7*>=>;NH6Yg@2MQv09h zaYc-ARV`QDlJwq%rmJM>*IswlYQl}WDbEv@HE%!FK&-*TDtdu`@?7UO-6p7&wXoiK z1AYqAg*{}x{_%P*R)pVfgKt8D012`0G!DWyK6^CdH|hf$t>72=@1t}yYtAgTy@21o z^5K$@FZOMjDLn+Eru60pm;IV;EF)wOUdwdHWBteXbvD^r!2!FvT?ha;O+X}1E2rxd-va# z-OS%k30`o~$LhVHp47LYE-m_+;|v{2kCon2XDM#@8Cr_`3otM&%N?Xzi}68hHzgG0 z_tM4=yA>Ep{qbh$v5V4{w1B86;UIIF?_G&5#n+aADq-1vwKPrP-qLz@1WbfmQ4!i2 z%%S)jXM8B#k?(+cYid(F;{-Z6w=0kmOeZZ>mH87EHh_uf^{zGo-$UIw^t&2vs&rS8 zZW+27HNP`@u%}%W3+1!BgT|&)u@@Uh&6ZoCz>ti_M&pFd*@9r{3{YbunVH$Ik)_Q% zWB{#E$>$_cvXZU!o<9y~Z+f>o^|?ll9>-rhUC~$|UcZpMmAL{uGUk_S`%5LqHs@vD zI$EOACwGf_WKMJhoknkJE+;TB2aD~uLQ^mfn2?&oRcn3KAR2>QEXEZl|Erv@+Abgf zx@@v)Xl6RUvZ$J}xMDQUQ$JIO{${_`*c6d$Zrf^2}(oqc}f?9Ux@_USWVjBQRl&r|8m7yYxL7$%eV zGg$#Q_EUnb3cbD_y-)p(;WWPqceTiM;>&ehs&}YFDthybCz}@=c~s(YaHFaE9P31j z!>2CHor{3g8mL~z75oU(dLxca#4;nf>~i?hZ5c>BqU!u}L<13{hBnR49yw#)dvgc} zXv!26PgTc>De+}&PL64HLukyA#o{VTgJdzqedcTwbfJn#32dZ-1R9%Q@R6uvDK(R& zLBxY;ZO$L?zBoWk zV!9wrLqqf;T@{EdZP1IvJ1Xvz_^H2OMQ)*603di^0HFPi$P#(-+R!O6KSQu?u~&6kZ+<~{$=LOvp z)%`%-<7eYXCnh@iVd#z5tHgT`;z=!1=L#$cj7u2Flqwn8IXq_y%yV`PpgdQOx}}BU zs-q6_ol#z;ln>c5Cl`8?q8H(dTvDbNx-P%UYwoF6lKxcwPg?6=^!>ZHD=YP1m4${ zo?#6!QKVMss~czMlAe?LeVC)e69h?y0DeW?1Q!zZDSOm*jN!-ZRm3 zk@qe)S-wb=-Mu|rznpODIxb4oB*cx*k)8DjE7VC;?>C~`+5BacTn0I@^y08YlcW6= z}#p#=+#J%pW#9yqI@rU7rdquB(|t&WXRlM zkzsxPM<*M3q@~K5PTJ+2Xs5pX1)22>d}K9s6@gGC16qy6s}waa+0-V6i`M51IYVfY zg^yKhBd%|&Hw({mBC3ZF0Z$u;K> zEI@x=?y969St9!|c*tPKPbqQfpzh|xfG8Ky_Fv{|YW&?}l2WP^#gr9O4(03q*%yer zlr|26aafQSN^$2E?+euE@?1P?5RN>vIeFczVe2~_cq6V>d^8FU|Dn zn)Yw{Y?&%oEFYlJe!(3#j)Br5XMbKBdYIX34+jygJCo&1(`&GFTl&k!^DcVjnsfDS zOlT!!dz#J}Z0dN%X7qrm0C~+ShDU(-a~;r5Gyh|z4ODjEzlNqt#JwPiT+8OPZi-Tz zI@+46&-QFhTWfAX)eYTN#24~hWjyh}OV;$hBWCDhhk$;zMN&HwFgjDWz$?IMVq zNA>CErHz5A{vLBX+^F8`E(&F`bBrLFxM2y9i==!0anKRTA)oG)kL1hrPryhPzR^Vi%(Ie^WuR+Ps!af=5HdV2S{?U`g_R0xs3oa#*5E3NON zfk*)1cSD@PgGqX-2rEhTp%2-=^xgFgvSmA22&&?{NTRN83aU&ZLJ^yBtziQB3Zna# z($59z?Qb}rmZeu$mP*dUb}4|FyORtL)&@JeLOmvaEP$@vb?et-VPHb1#GRT#qX6;u z0o)#%5S#OZ*}QvWXM39|qt+Sks6mGUWIen@-ZDiN^%wbRJ{Siq>>tP6|5uLgE!|3> z^tXE%sqL{;^?c-hwOJwrb*6VwY7GoNcXNM!@s4=oi+sb}=(*IbVMB2lLW`0W>NOV{ z^anlOR?U-aEzd+E$gzcKmez;0gk~E#?O-)Lw zwQI3lhDDFUypRte@0UA4fFduKO!K~Tm#B6?Fd2{_Em!d z)f{WzCEr$nH@?p;%aBM$qCgN83RNIc&BYg9uj9}O4QFb9LEb5N?)U5=j{UA%!y44# zNm8GC&l%Cx95hXDvsc_3re2I?Uj$}yEIN|>M^jE$DL1p7Tiz6B%I^PmTVmwE&dL$X zA-GYTm@3W2Rk%OfSX;`l>97~#3NWB{>~$Aheak^v60zjWZ?YP0c8M!DD5o|~_#0;y zdoPJ{a~|xF=PuUJKYiiRZVDov+rZ9!J+$L1mGt&9RCOs650aG?ULTL~yY1}bWx&o1 zQaVIMi3+i9!+G_O^}cJY8i&=iUPxE>mMjIjKg9TY)?ma8wSPM4MwMH`DH>r+baEqedL}%{KY7IOSNBQjM6OIav^TgWw z%{4-DR=3=y)Gll>YG=ifQt=qGx6*v*uO4Ye9=;|SeeZHCj5uo&cqNDU9W&;teH`_) zf^@@Uk@$KAN_o6S{Umr1-*xW9Lfh0wn~bKqtlCQ)lFM1bV7iwWk*^((>W&8=q+!d& z&V85MFNAXTLB!{xhK_uvoY~(VrCMUg#CdfqdDMnNG(`$`5Bw(^S z@*8K-oNH+$>RoKP1E`4ut^;}!F1wtWZ(06-TzW__YYJB zNL1SXIbkt^7;%iE3`l2(0<5Un*YlR2zrO|F z{|UHDzT9}?gf7+a-hkqX4>}_c0E|Z~Ja6REQpdXps^J;FUiWp;>1gK~4w=~TluU}E z5mS4sR;BAYdYWXxGrr1r(7e1e=FYX)V;i$bo$qr{R(#Zn?sM5oYb~mmjl#|8H-N@n z+SI@){(2ZN5RKhD`dT@rpY}OcvPLjR5Kux2ek}gy4XJszql$Pv^SQe4l)1Y7_@?q#v$z7(jM94MQ_kZw-yCeW2rXHQraEcBZt*u(W^sO5VXlCF;xp{#-RdT%PN7AJd|OP~}F# zLa=6V((Hv z9AwNlayZ}LB>SF9zV#H80Mx`X??i+r9ZDeTB4vO`cCfC=!~6d|ZYLDPG}{+i?`f;t z{(z3$bhhW5^Jf}Yp1V(T%AOW#Xdb7U&|ElyuhtpwPT03HPUYH$Z^Za4PH`iF3Y{Me zVR#-Ba-f~`l^rXe=pb8lZ2JvpRRUCOqNpeg`&7~)TL}`3Nz7gJldf@=N<} z>yV2Wt(FCaN`0O6$ySPX!X|V6W``iAy_*5o{gEG%gvP2+^IqEIML>v=YCUH_2C1D$ zAxp-Fgra2C<@|xq>qZWCjXVNeHL5J+6~8#Qx-mG*To@e*U(bJHjtK*O%5tSHhL^Rr zcVt^79=bpmAKiTW8-0wp0mKfnWjZPxzgqE@U)b|jL|Wuwk^Z)5a++}h3ukS^Nm@p# zRUySu-wUjzv=cfmuFVHcAc@9}7ZCKX=&mTWk$A&3Q=WlZQBg@9^7axc)j-z{)&orYH|kl?6WnLgj59mFZO0oh(N_K$2G(`Gl) z&C_nXW^Xq(HbMqVrJRkgd-6eLt6+y!l$K`1ZyXxoVNC+;{A%rSWSb9up)p@C$%7@< zTx5bEjkQ31A3S$vxuMuWI{ur?uSJr$Gymx5HejzYla|b>*FLyHD7xwWWFd#I81Kg)oiGzM5efE;kst(Nq&rMabXUaS6F+QxAdi7M3 zR8m=HSznW|NjJ7oS~IH6C=zDYuP^uQG(eRa;yUC?nqLb|h>&!GaQg83Lp+mf`|4@l z^x&PX1C&Bs3!>yZ0okPHSu(B&V?z-nG)mYWS@XtJZ_g5jbV6VOLQI8$5RJZe6^JAZ zX_`c#lmLr3`8HqO<*w2S&tBWw@E>JhzHy9LjK7tOorDq>fm(LDcvBzf`}AeA^IcemS#ibM+XoGXG*HVL+H!RjX>mxr-igL9o0Pn75o9M z#Zeks8J)Wz8>{E$C8&HM+C1khnyAwT{Z40={^)zXDQw?=DU@UW6)Fr=k#j#Zyd@F zroB~4EDL5GY0&VFHY&bN8Rq2jAepn|r*BE(1|bHaP?O+{<8zawcto|8^$nZEb_)>RHVpoCR+(!ol=N5{8%J8dJs;6HEy z{&}JPk7fJ+?C9P#%?+DBtqfQ9C04BULxMtYt%@~Ns%(pgL^IpEPFZT&V$Wm57K9_| zaN)_S-zau1ai*}w8K8`BOXnB=W!9$myzH&#!gQ=gx|}Q~>a)go-Xe;%yYvKlv=O7g;DVB&vrzK=b>ZZHzooMOAyE*E zq;WZ7O0;K7KtO2p%{i>nR8`2Mzn2d%34jb98mT;Z;v_k3kF=lZL2~F;$+vT+I!%;6 z9!qlstb=p_xrx6#oB(73GvI|Xr7p}f15*CLpD@X_YipRxxA-DgS zMXmSBvLGZl?m+hweNG@XI(Jz0q;QWc8$~uB9ry0pNJdijW+m|!>R7)mD063GQz2{K z%e*ZbYt4m;itWf0&DCy@Y9#|`Z$_fbhqguEQv8@QU^B!u2=i|x>wn=A!fgn;clWhJ zAY{biiT80#=R;{H7)Gr3f|b3pV+iTOW-&$SU&;EjO}$XPrN?Z7cZKEjfUn@Qe&{db z_WBJi)4nUdcr32J+1ban96ucY>h1tAISR|82aCGyl}WctUf)zI#y9GJ3DMc?-?(a^ z6=6B>$dvq+`gK!*lon%LU#fS0VtOfzIB*ssgi`4eL`S4Ho9%*1ii&oS9CDuZG{RMQ zP&)_3eg_rsJHX~#6O7>kqS0@hrF+5_k*lPIQz1`6e%2PZ$D7l%bg%(Wkc!-U#L|oj zEMNHsd3Mumoo>07Yh@%7r=R}Jj7tv3R#@2!mdjx=O z0z0+Q)E|lie+!fZI*{Mdxy70<%1Pqjks<4Cd~gXTs@++>(V(L`DgLHCjPgx0AW?c! z%+(}&=o88Hsv&FbJ! zcXhB%s?ZUd(aIIBl>IuH&Y5H05La`jz?>V;mCN=sj7u=DVVY8rLD5AgSPZ)w) zC;N17)>tzZq?uOFrPTJVW3tkwaMkJ)$ss7>Ue$y3{2yjapn%WWJ;l9C+A9$`N1-u`@)* z3Tqi{d|uEd|HCL6=iQ_W^OCQc!O9?SK||HMK;>RAfYC*gbRXiXqZwl?CvB$Oj$YJe z?t+d3dD8-gHMuEUl4a^!aI>s3nJ)w_jPRIFR(2Uoe6rIc1rwP(!|9rnTeb|g)cokp z7wr$umjW%-NHr>QtOJXK^($N9U|hS+_BPd)(SL2JHbNKgTCn}YzN-!9CdNnD<3-lj zjh>I!LjrJjjb?zHR;T3lQ_kUPI+qU?yV6nE5BzTc}ky^uh z(Ghj=YiJiPqHZ73Vzg)i@E+$EUwvC#FnQ_Vf{z2EXs=TPpfn$F4A&JE|H9AU-|7@? z>UhCKHziY2x7pf?MK@V*mt1PG9VbChz8`||RZK(f)8C8?)H2?B=G5ZKlR}m8GB)QO z4W%h*1x)g)}#f+$J-EQFs49GG*RN7dYwfr`g9zjdN)o zPs_!-Whz&WYt)QnV?@0tzQhVAyl20^zZX)pk?&;+UvB($Cq*Jp?MPm#i{W_r($9BA zb4=qT#3ercaS8S3mok~|Jv-onALoxE*yuZsBE{Q?sh%;IDzdQI7HS99KM$#f01$T5ofZUD^!CtPf$eMW2VVt{VN~nw(#h{7AW5D$$Wl%6`HZ^ma-ZE$ zw++5?HJiV!e)%9^S!CqMfc?29M1}g^$7D(VCdc`qGZe;OV!HGo<5i<)%A!DQYs!1( zq&R?2!$5{=3_GoqTn}1IRPSjfrU1}ZWqIeuGt5`~^MsE8AkDKZ>-{E6cWO?t^5|Z@ zaEiiFajXkq&-od4`+6?nuDiV?QE|ufBXx! z2y6{4%{5JtM`Or9wekQ;lfZ0;qEIBB-sZbV`z+>IMd|WFuKPb{-fy{Mg=&iREROE6a?q`cs#0~Fgc>Ip0xmzE#;RoT zUuP49j!6J*7wyhPJ_NkYm@!5qjxnBrgx%$+jwD8Fu);#7JbL!^8v1WGb2JyPUc~rh zowq(t+mKs07@+}c{gAdm-a-h7;(K+FgdJmK-P;2Gv6aMwrR$Z|>I=ZX&0|g0uxNHE z=?o7ty~|luws=}i;nneM5WVum<2|I;((9P0Y-)&~zwhOAn#3H!kLH~_bX#od+psrmIwfLms&9cPgqRF6P($i!*Ip6j`Ue zmyKc&z7NGCuSB7n$}Mit>v6!G2YVXT>rUyRUwmla#9ih?2Z?ok;!mNuvIUdObcXL7 zo1}|nAgWmI3c-r|(kbFYvgsYq)~s1k5;-2a>j$#LKNA!f0&v0`+ zHkr$ybhV2!9>p7-w#nAmD}2>-@%kj*tsin&c|tz>$4seofMG*L z01U$;-d!JElhP2g%s1P{dsnpb$ivr~mkKQfY>xXL{j@Dz>nEB|-0KTcEzz~G#{V{Khm5n=b zC^}e@?y9Yg6PwTfQhTa8hkfxXe(U1-%}T&;9BSq81r24!Wji`Mqb-S0^Fzb8ruK8G zEblg-X&Lzv7jx7V?X5qx=#9au_!}3V72Orc{)vGZ^YNp$cgE8P!u(~u;+;0<^EeYs z$2kjPi)n_0%O$fanueoMh6gg7wS{!J#2P+ihLYna@|_RlW`F+wfZpYGXrQ94EVL|M z%3kj7M7hx|a;L1!vy1hwMnU~hNi zLt)}w>^H`3iEtw~$=aNaUqwsU?(YZ=8;msXqtJRfR)%zjycl!gFJFo#ryvGfDf_HT zt+w^=x3z$h^O>HtWwyj~lQekQafl?KMR@7bV zCfF+Bb#;t&pO44Jw{Pv#3 z_D9U${{1`p&o)>7UE_r~Q8{MPFn2v!%;-h3DErQi!iz!}VP(aAE{=z3h(*&i9aH@rKDle>Iq{K=Q zx(l06i;1ZR=HME_d+r+!`taDh@sGD=y=yXwpgy7U_^!aZovoE~Q&$S};r%$Hb@uY| zS#>0=Pjr@bSK;~x$}hf+4NcI!NR@C)faGE9>v)0|&3yz%07B;eA=p*jiSuB|4Kj+E zg!A&j2NTJ(V)#qQEap%fmBP@#-!3;V7g1u*MBm!}WG}+wiM~CkP<*=B zAg3XfjdIG2QEy6PRM0|->d%y!&Or|ttD0n@|4k(mk3;cbs^vG% zhZ|_wUNF7nr*aBOulTi#RI|BTTtGQfPto8XcF=#}dj01~>Hkon1^0yQHx4e&OIqw4 zBLS~c&_bsjAJO-1=>T6qKTVcPg_5e8$cICxB*>{bTQ^! zgc+>K-V0v}T&R%dZV};f9bacG@&;Y6rwo@PDx@unyxz=I-Erhcq+x&LkAM6>If!Kd zRwEGs`URGv3GAF9Snw6xY32IXpre8Rx}MMJ(D|xr)aj;e>C||x{d{F^b|7lbs7VV~ zZ4GE-?uR@?vt}Wyh<~v(z>9{N)5p23?CI@7O-!PBpEKjVZEDD~XHg$oVrHRd#^291 zb}z@%g|yjpDi^jmT&lCaLC}77Plp{}?f5W~%W`9Ckt=YUa{n*8fTpGGVtq+IbXRoB zcz4!g2&onoE0tZolmXeCp$)KWO^c3YR{z`|E!2b9MSNY?%sQ27R)MlDy-UkbV^-wk z{|bpX_j74&TzZ$D7nKEiS*nHGa4UZIMfT5I-k&~v#Qu&htY)HEq}mUq^0${YyP(=v zpEZ3-T1=O|IHa?ybp_L(XD0Ir@Mv!~IW%*v?I77`#@O1nlz&ULT!zC#wb0;RG8TaC;KvW<|lWU`TR0|2{Rj@+9^tfzw|<#n9KHwag=Qrg=K&%KOqip&D$ zjyxN_L1jE9*j&<2#O4{#X zY?N(~+FRlZN+HsWb^gp-=DT#R`!NOaJ$jq*?RhVV3@A#+(^dLS)1K3K4vM!Ll_bq- z68MKs^_kO8+$y~5f}fjESN@IhWXRRJrNPzYsqylFOkJVZJ?1y}`Dx_h*ZHTqkmZOn zwo#>`0>6vGHr9qocMr&f1It&reYftaC!YBfZ4zs%q=J&4^33|O7|4XQqHepbUASdk z()BL)SfYo#jH!RsQc&o8jby_!;4GTd&G4{v)(V&;6e48GD}$CZ$}i)(3*8-wb81}k zs9NDOUpGcG;-*Q@!i8qj>V|^d0ouM)R{NV-?tv!B!2n!&vL!{qS$q-EA|jSMhWcR zbue&Kd^uhU=jcDqI%qfrkl(UyawEYBY@tQJaRQCy+MV8e=L5|i3bU5Qe9^o!qd1d% za`Jm|Kq_hCE-p+7cGHpH=^Tm#~PaV}zswrjB=Hd%$#;c}l z-Czz(<1SlYr(tif-3!#si{4pN#;&27RQ{iidU<}iEjr#FZHftB7t8e}5GNxtDmDw= zxngmB>$ffKVONI#ddKwsYzSqLFQy%(t3#XLL&AuO>vpwCbjp<-LX8(V@{m!!EW^CN za;=lm26;tLT?co)WDXU~p3e__fs6;P(SD$FhDhJqvs4>Ny*)2WCqO{jExZgGS(yg# ztT+~rW?SW0(Q4nT9=ACTn;Rkha8Q?&FLhwbL`nT?GlZAC35KPko2CjM)2XC~HhB#t zf))6CU@hLuin6bZFR{QR^Wl9A00DYahpIaKjKb|Pm;1;>>2WKM(l0W0ixZyDdqLi) zwW#Um5bWY5Rq=Fr6tPNSZ?L8`O8lAk2T#;qoiu3BKQcj@Bsz#IZ1ip6!K_RAl%B_# z6%x5k>j~4SU}GEROa`=GSW9wPr?$N7U=gglEXe%aE7qbz2Jiaj1-<>NmN+FpktuCQrgj|d-@wAl90ly}} z*+5)2#pb>dkya0qn-d5UiS4)MEECy7pn}vsBTFaB zwl$+HB~$sMfWABx*tRo~@sA@0xtM(7^Fv(Cr~b9P-$IJ1YEE2u<>QycSnQzpAI+X_yn4!=z_z+&C-lCK5>^=KXDV zh{B_eKkZ~!uUKEZ-ylKaHEQuHD)rV6dr6gQcinemgSzlvuMEWAzG@1KuO+dTTJwy1hheT7bOxj( z^_e;tSJqi9(mhl(!tLRDx^fbC`5nY+-6Vx}kGcD!YLWR*uG*B5#%UBBRutwh3Zh`2 z*UI$cgBx$Ch$e>ggJj;vji_#<%m>g?}63{+`yH8k)PKY&Vb6?;&5E41dT(n zX4_|^Sz000RpC(PYvHVOX$mC~OgVFSL!y9z1+qsM79F)LsqHj!=Tdd?7z@M4(IR;A zj&^KY(X`IdQjLWup4n1Il!}WTMN6ojaMQD#__b@+kR67?lwWhx zmhO7r(3)u)@RZcW_I!H_gxWA@<{+a$BlT^1Mux$)9B<#yf#^S;Iccwp4?n4T!ML|x zwz@$L#tQol8#rbxKUtDM)(kIuR37I16rE`P3VCHpS=HC^nNEh71!b4S@0u@TN~|t@ zLpTX|NB@#BNcUjV*fU6bBTL-DU|J5*E7uD*_G&(%^ZQ^Fg}1HO?8YgsoKSKYpkzp+ zO!+Hl6JIy1p;9Q>BtPLC%f;GJKj=Mv0AOM7E8qDDhp?{HBm|Ij_2Za-DsG0vb zf-2gzQVu~s)7J9<^uFVxd&Cy1DlTLIzt06eKfnX;QFex0q`?;!yPO!&7LzwS0&AC( zH&HqM&_@`%UgxU&(sKBi>}1Owu&Yg5Ea0DDl&b|v;C+Fpg6sP@ZHf<0_G;-?NoNZT zd5Fi($J|xnqGvt2Fi96icTPOz+Gpc6 z!pP$I!CE}|r_ktP+SEKbwt=bctt;=cN&gCFdr&u0fN?fQs4qZ{Vl7>Wt16&N(F&#S zLT64~l+k6JyNp~68LJnQ6mh;{-K6Q4GnzZKJS&zOCUDxh(4=O*U!#b8J$MAao!-=R zcY049+Z5oXo0hQ{H!FB9yw`kVa>$4Q_?II1KIr zRi(z}MZ0kF!KJ`{)0zCg(7FG|z42doM6wZxCB7f3cvlue`s4C&m+b}qK zm9!|8Vh_ip^jlzZ4@Mn4#dn}uju*@*t@kDSBK7|v?!CjB+WK`-T#77^A|k!1^xmtK zg-QuXklu+%2k9W8E4`ys=^}&>s+7=+kOZVFRk{hiC-lDVT;I8OpMCc|-#&XiXMfLq z;4esd=9qJg@f+{&EthuaiIj#o+9sZehuVv+8EmjAE#Wmn^{A4Nr#(n11v}$uRWn#e z2gJ)4;iEdIQD?vYC5zvGVSW6szvSjNWZE1@X68&EO63bO{x+WNs*f5GQL-vx(VU6O zk63eTMMm$uuhOM+F@G8t9eLX!Jfu{g5=#WTU~J-Q?v-L3D!aUNo4DHw9a2d&$BX-?`HRL zbYQ(G>y*1)$NGRwxolHF6eadvfKaM8x{ai`qr7@PCcP2hBdW6fNu-UeG&%sj>HqjO@)IL-{s^WR8%&YdP09VQhagp}u zx~OHHM0c~F@~-VL;)j2)*P-5$hR|O=unBajB*8t`b3WNO)2u13d5Vklow7{OJ<~0z z^Q`=-mI4Ti?yU)vT^GHFc4E#XjhGo@;AqCc_y~M6+Uij`T!4>vDb=K8&Y0gM7U#sp z7qZgmd-)LXuUk2nhh|z;`J$SoWxCl!3^`)E*A|#3;Ay}3a2uN5Fule*{rA3Mf1fBo z=I%Q)aw`>YjE2sL>N;EDeg7o-t23w7=5o$me!+FA*=dugrR8GMM(PS4Sq0RU zNA)i+N^MQ^DR+l{QFdJX;htei>6;!b*e3l|-J(@8iNBiF8ZU`xNr!>+1!yS+GNj7f zsotSpdUrf7BIZ}fGF)Cq^>y%tc1d2#p6ciTE`xBa-AQzZ!YSP?hw*z`MtOx!=+LVWQg;Y%1H5=E-QOab@q5 zIeT6VmonSa=p~Nn0Zp$rD4XCLkJF(KG5*o#mAHJ87zL80Tt&y12jn?P_>alk7E1C) z?v$rd88lQ1&HC{PTZXuxqG&!nJxE(=``X!2`TBXsyVCjf^W$`cV|IH)CGl})`OmqH zmc+CT6w1%W|309PMqrDwIemADLv&+MIMMVnYl4A*b zOM}nZ%HI`=Wcwv9TS;?m<*F?6Rv6B8tKSWYC%j=62fA`R6)x6dKjo2Me#YsU^}v|w z!q=zsO!EKX&iPN_)_=@|8|Tv(5BNid0G60cZ}m*i;T;IwY0LaIV-RnONu5}(1uS!R zJDefphYL>}T&wkT(rbX^?e4U3fNSbWHPz^^>l?WI$B12zJZN{Ydq(^Mr&`NN+xvl{llgVPKjfJ5@ zG)5n5p@7c{27T8}4^ik=^I9%YRPIx=f8+25$c-7^){>B5sgq#Iu;&E)(^6B1LdXEq zm08#M#&Xx)+0E(edNr{Ko8Q$$BOVp~uFBgZ7FTb>P|n-K_t(oSB0gFF>x#;MTUGw! zY7`*St!U4B69)-HYDJmsV5cf}b~(Kx;wb)T$deof5pn9yOD2H68gddK#ix(+bg_H) zW!a_B!;<)MXgEnU+AwD&l8G@s!`!TXc}DZT6K=)GwUxJPVe9ESRG*QYihB8HcA{FY z=Qv_K^^I=Sqf`H(R^XD$Q{APu>cdymc!ixj+s9b zP}y!hNStKRrAEMY!p7F7j>dne6mH(Zi1vD83#iTeCGMZoRg=GEp$PkY6^35TSaz}$j7$rScUnD+n6@?4g=o809WJ3ERH9ETr zBBid03($rC$~(bUh<9Jo!B7Dz0m^8?UjTW6ulvo@CDgv$mq{N4hmvdCtkK@{@ohJo z0-Z_8z(ZZS!eZx!#sJ@KYa`r@cB%2f$o)1x{7Fw0+0dl|K^f3M31eAysZ9pU#UrNb zsx|j0k_QMM{eSe@`Ik_7`HGt6TgISlzNESt2%xe-=W<4_AD2JQ$R!!a0p5}?+*2y0 zk@2U9@rcw&Gkr5~x<-HQ6o%e5)G-tfc0U!!?B1JYn$iM7a0S;F0iP&GbR(ek?|v*a zGky9L_pIj2Q*S)(K(8%hLF59Fud^LLr1VD#TcFGa41t)~a_$ zF6(Tr#x80#x)N>{K<;D#iElA&MYUzfw9uXopv84^nzj3@F80ZIjd8A|X6>XWkOOz& z6e@l)*`nPpyTOq{@3^9t^i0>a@FRy1530T)cgjlDk=l?{oQ8!T0nd z(#JpVW?y&g7W=CH$LaXDu=IaAssF~O+_=mR?dpeP?sa1JWF&;BiWg8Q2AX;CbUO}KA2CK0CkeN=K6X4_*?^1 z#fd#T%utt|9cH+E8hUPak^B`vh+LA_9{g8PrvEU<|4T(W6}%Ih+4el-UTF6y({VQB zntBO0hFz>1SPkt~5>l@3_;H0;JA=4h4-9S8UNF2+pS~Wq|Hi@&fgcr=?`tg`2c2B_ ztuzWXtd5=>Ad?`KsM$baV}h4AWXEdu&0l}4IiT>%Q~84xjvBeS8A^R=U3B^wls_|h zV4SiKAH0VR!W}2u;U7I#HqBHN~@=lTY6O*N%Cn{qyk=$ZnI8YC&+45 z1T8=Ri?sO}OS+v#+m7Qmp-ds<&Y`!Fi2^!p6Fa^GLeBjz?aI214T1DRF8r!~?w)p> zCu)ojonHF*G-u1%WNFy>rbrIliA&-aPl*qYuZ#Dax0KwjK6%7zY>1ly3kwg)1Rwi- zC&X<6=*hVbmVSGx8ps2#g5z=^cb#Tf=uR4$+|phBoD^rB{W89&O7`GPxRWpB2Nltx zf;fTcX$&g(gC6fp2n$%AA>cy^maieLcuXr@dK7uh1KatGMD7fk(=bWA@bq%EoQByh z;tm6x#`M=*wjL+SA0Ozns<2K(PGIr|t<$?L%=+=q-+ix@HKl%Q`>Nu$!F!de@$rEfxP30A&&?gr*nu=PWhfKZ%?xd*H;X;_$MJ z(&Q~^@l2doAm{9%*%!CI##-Z0j`igDf;y{Cd`|a_dz{;)N{Nfn3H_UyH*i-MSH)QQauB5D2CbX5OWQJr*+MDv_ZNQsY>w*-He~+RC@<~)l9Y0 z<+!JAr3^7Wz5T@eZf42I-jR}P)l%`_d@@Bg%<^{Q5gX?nITGfJ8<`m=lit-PyFzxR zpI^y~N2@eb^>3xWH27d@1Oo#y4gTKjJQK}a+gg+4PQU9?yL>MHkw;<)(G6aRrCAW^Im?jOMTHUg8@Q1KHWV)-&uS^ zFXsd?ZYd-yxpQoZJJM!9Ciw-4&!S4ozYkQWm&&|PDbUS^^374dNq8Yr9e$c>krddb zpQRvn#0Mv%1Sr52OSJK*CsFdr(#*7K#pnG?%I5zJm-r1fH0SY~tpDQ&ii$svSqbCE zOCLPLGgJ1i)l)yNj*C)zXd9xDyFYZW9iwGaU2J8__v>px&4P@a{(#+M&6Oe%>)UMs zpA;kYu2*8To`w!)^>drro9A4kV&-aMW}FR-&(Zkg*aFrdqeD-C;9S4aTzw>Faj-~`xqr#-^>c;9mLmNt@2Pv zuL!dbIBSXJY3ic@nq=yX9=bEUO$!(7jhU%9AMOklI@(4@3VTx2Kh8>-9KCVT>f#vB z`o4##&*9bRaa8afUB{2L6vn8j0pG&SxYk_W&aquG zQcmi$1@y%<`+bK?8S`1&<|+}Qxgx$%xPHzZQ_Or|5SO8Xluy;pjuUw54RZQSCeDf& zs=8!kwm(ACL=xRpOO~!F`#MDCb}69qbjGN;^W#{^zcjBJ^Lcd*Y%)f%YLAs{^6jgN zd{^YSCZonr;jXhBusX9Gzo?=@9?bNzy!mi?(KYGmbj>^Vd1ioX(8!NIBXhEmW?)6j zUnc6(@Y9p~+RIMj$-1+jeYJC*q3i20?4p2 z$e?14Ol)oz-9LF5S}+ZmniF`sSwYmZp=K&({Fks;k6|yq!^SvU93EYs zhytRkS&Y~sMO?H<)MI6hl`59kFM?)Lk3XiYOl?bDD1>~!YvH0?v$#W2q*Jb-?E~}^ z)v_I>;??#e2fsg6{<#yR@8IAV@ilcG>JC9>I4;lQNbkv{IVqDjX~kjxq56zY_oJ8j zu)3cnc7IVsp@=b19=hqXQSGY<6vc8Nt*gr7WiRUxpvmP<hJ5?@E4lGVdVD5;(oRP&Rewd~ z27ylfsvOT>ez1v0H+XU*vF1@e2OJug>cxty=`8zdjF|L^q4xcV)IsD^=EzG_jI2QH z=_{QJ8=%3pUXtdShO3cfY-mm~8GMXT0hM<;$aR<1wb2VSo1d-u|CI z=5A0MRCaHIg8C<@4(d1svem&o=`&BFUK=A8SgB*w7S-KDOjfbj-kdMR$u~p#N|eIq z*B594iZSx5HD60ph1;VfdMdC?+m4KE{y~ayLk?ii?#ZUxbtzZi>2UWI_U*SO z7oNeo?ImVi%+os=cCzw6w_W|^H2E$R6vNVwcLWwd0-@RLbve;>p?AGQ znXfa0AkpPIN`LwNC}c)rW70udB3PDkb`~`Wiiirti^8~pr6gFo#}3(#1eiA70&%^m zS5X#XYK$W}&-feV=D#Bg{^$4QfBTt4oOe~aSihwbw z)Z`U`-$EZXE_&v7_eE~CS!;xy4oNQe=yBSCyn&_WXq?pmJGrH>Az0GE_&o64X$p%< zn!cekMEvWMe0J8`S7+0*KQR8i)C_Xk)lv1%J#%HkR*R690!Nd2*GAETb4U8?^Rv)O zpI<9|ai9Jq+Q21WlkA4>^rYpLshrL3ecyE1qWb?FaEFydph;9@LE?l4q{3&qc+`0$ zEXRQuDFmh)^Fix|fn2s)gMP6hnulpkl7|#*aTdy?P|IGq`GB&7Qv9w>cvr*rTGnbB zSB9Mru42}^7l+@NRxs>O0AA6{Z*=`@p+=j$E}-=veFF|z_3BA;i{WoG!j*<(4L zG8*e;IX=0ok@{4e^FvEZIpu46D$=%;`z7_oAI>5IIn&EopX$d#MCJzsVoI#Jkg!fP zapD8JL^lPemF95;U7kzAm9nxe`pbX;RA3AO9dQI+x+Dt3dl&S$BZgP+D zjjr+HTmhVmhkkCk>d<#A7#ge<4Z!u!JV@d#7Es){mdVU!b<#ZT#^KqOM1@i9Rz(}y z#M*eR9D0Y5*wC>K!xc7@q*)l?~%GuLl}bFH*U0#RN+LX+i7LtUZ$`%3LM> zDkkIbHVB5)g)uaBd$N7EULOB(<`iU=)5eYg8+&99Y!B3@7X+5WyPTQqhVxI;{m|%` zwLjj-Slqno{W{#E)T%|K9ZbheuSLV|IpY*KzKCv{I#BzQ$SG*lt#{H|Y~1rh8@tf6 zYI(ho!yw>4=iVX$#q$X>n~(n<$Nz8E^Z%@h{dXr+<+SQkS*3aUrRcBBpUekyutu$J zUq5`|YK*V4O(>mh5_4(3nS#EHKpI2|{Yj+3Uh`Yq3f3V>*1o7uEBW+Io0(4D2sH}H zHw0RkLs-8A0*=A@txhoZW%lA+y%AaGfCW=9zX-A@q2=si_xu>(i6|kje-#Y9`)07thO$83Z1#Hxh$H`jr<&4JI;+S2;Axnw3<3Vp6~j%yE)#!QTb_WrZ-9| zo+9**abd}Jo96{PbTX5eXJ0@yMQ*HCa*r~2QW$>$9BCXWXBc!jN?k3sTj~*362>SX zc@A`p%}bCvhNUouVPSFO_#Y*%(5c>Q;nUbM)e2~@X^v@U*!1h2c`UL%Y+>4RdB1Xd z`(;CYgk;jb#*eSn0(qkn)}E53V@32#FLbr%H)q|4`WmjUNF!zSExsi4x1YCmO3%9Q z$)1mY`9!4(K3i{Mh|;a(?T1P6sXM%>Z{OaY67N*^?R?`PFS#kXM$Fw1P$^SaXKcK_ z+i~o_)cS3QGhujlaCaAzHymc*$ zG;O*TR!H;@j)9ey`h)t9qo@}}e42Suj9O~D3pvx4tpbNe=5N$IioiKu5%jnhWFMpH9+Bv*uNq=+VkKBM^mf&LxY_iO?Hfq+dCXozW$;tXVPDWxaDTwYWrG? znpF7e*hLiTwHl%y-(!x0~+U7n?gX#a&k;H^H ztV;2zYC>NZ@S}P)b$CFj{sUb>%F6EhS(*dqYOA3V< z&Mu0Jo63loLAZ6Sn2fbvZ*$2h#n$b$$zCDY)!bxyt-U%Plw&%WY{VoI8E~hayeQ*u ziftK|W58P5x>wun>|9Iq*K+Fn687Vq zp&1WXs;j)#2h`B^l?}5PWDQRdx@7@=>ivckkwRKZ_H+O0MOd0q|Ccl@ky0 zsX_`E)Tanq&i;P3I+v4{UB_U*5_)c z%D;?=aSBgxmvkiJc_a{G6k~85n1Ar|*s(ilighvJe(6MtN0hQCl%-1I#CZw9&Ty%6 z_32Uvhb++k4lqQYzOSWm=ewS6j|gjZt1fyeRP@2$CGc*9-RF$WRgUlze+P1`C|Jr< z8iS)PXzTO@TVk~@wSCgH4xR5hoK_x2UKK9zLTkuFDWIWD*9;zt{u4!VD?gk6B&zaX zAk#FMSC~>Lltl>wE+rk#&2QA1#5H|IKUPdhpg@SAi>cH;MeSg|>j3G$#9FDYe~ppR zY(T?8WqbF>u>S^r%x{4%>8UJvNePsq=St#>|;|jiuL0{vqzXY-RcXCy6{QYSu z2}>+qF862SJhiA5v^jIy8LfKpe}SQy>q|Jhfaxi!W#|a0Y->J4E_W~%W9F}RJ4rra z#hA*f`c6O1HybA3<#K}z-Kaj#BQ9`(unhQE^aH~x0HvaW|wGyzS z6rGC~%yZ%_8r&Fzir8nbmh=C#sMYgIC$NmeNO@ zN#Ing4C=kK_;&YE$^m%dk|Nl-W%>BVxT^9od1j`lv$lAXm^wDm2CWCGH4=MP)_SS1 z*Hy*Yr3bx?>Y$4AYQ~HgK?KXGfOhYcU`N>|m^L@{#OMxnyxXXo7zlEt3^;K(LBiT} zJ__F_xCr2sZ$S5`jPj|(`(>AI#$~8S_cnnhJ{@dA6_hxH5*by>kg^3l*iE0a4OPQ( zKCRS)CDR{L1(ch{0WvX?i8@av1-YOi^_BMBThGTU;E8b{iuuIt7nbJzNfp&BKRC+y zjEKSn_Jh=zX~HY@sp3&n-8eo_PbIl3f0}@PitAsRF0owBY$t@O&7Gd~M=}V!k<-Ob|)fRm$a=SJ`(PEDs!9Aw$=G--XUBJ&kv@jnd5ZMQ~v$c50sOlA<4-e-Vbt@{6vL#4oJ?vTGXgB@{2jI1i%l7aLL3V0j}EriB!)d@j+>Lj8@MnX z?sj0Wke)CnfnF+A{X!DEUH!z7T`JPH$NfQE_^~hr>Aw4HkghhljO7p2;M~%R5D|(e zA`(N+dg+=~c|nU3`680&-WhE{HQ_Swq{4n%>fyh%0rt-iJ~zwo4hctlH(ZYMAK{Ot zruT$8{UmjoE8TZk)8St_BE-)o>$WWi(xJ;HYwvJPQGEJaP>w`EH%WCGO^VKyi$Cv> zp$PCT9m$41N0G=qG2Filrscf|;^s(E__3O(>HAQJ`Uy->n@P%g)Ap?AF-Nc7peSu{ zo^!}HZg<1Dc*eWXjI=61zqX~tGbQ2Il5CIG5j{2;Q%xSEGj{9TwtK|1F_yCYlch$1 zYu)>|Y$i&3v#;+gs|J$23yb$=t>3n0-yDg|7r{5iWtpMv3@7hAM(KqQH3ib@v0HT+ zIPpys;;#Zle1%JwN-+Ym>NHw+Vp0p)Hs{&R)a95Pph3K@t!Fu3qn$xc|Q*Ors30bTV1z)lfV1>Ih)J& zm^OEVlhpc=qG6d%Q+?0BFMz3aJc%o4EatUok;pc8$rGAy@4Ie^A^f?rO&M*HWXI|J z-WVF*sv)wkMZe;;<~72+D*u79{&EP1Py_xEkxch_N8|I%Cdv)coJL__(qUg7-fn8J zo)h=CT&yNg>3PDzxfX!R_eI1*T7ONiRNaJEsczcEDXk7GYdZpjeyW(31SML0+rCVM zq!v8G1tS=XW7WO+tkF4_25wsTK~Uzc8=I-yyx6h3$Ytrd&gsEZbZ&n#sYl79;wG6# z7%=knYl}ikjv)j>FG=|BkG48u51BXd`79x+v=PteWj;hf3_`f zV#hSr^c9r*yHxf5(~YwWF4CVsN!$ z@qB*7@A^?Bv3yS$Ui)fX8OLmlM>?BM>YuL>K?~xrjavM}tQKync)c`ZB>|z;^s~}= zxcTonZczKVPX$VhaC)`sW7l7yEYPPoNJNn0(jpNw8ANao4>B$>-(q7Wutoc^?q-&4 zOldy%qZ{i9C?|=+k>2u?&eYX*@+FrM)xHD!?maczh41EnOfHt^O(~1X4SPx;Lqp% zQbIa+$9ZZI^>)`HE2(?<&fWE7*w8gezpo__hLP$8Ex;fH2AM z%GMK(9pvXK-`Cm@1ZAR0pWUmU6a7sYWN)UuTZDxwA$6ebV6_Ygmpt&L4 zKFo0zIG=G8Zue6>1H}D#@*nggHTr4dCp2lm;_>@V^_N*2wr3p}J=C?iAHHSXPowy>+c1F|vaX7t$%@H$}r-zjU2TpgnId47< zZJ3$9UHSQ2P-eEt_p`SqkwVYfKX zJf75`?w;8s8pmA(DKS$;l$2rtOikOB%v0BRcc%j20ciK_&MNmR$r0`(DQJ%n6!dW< zv<|tD0$f#@z5lK?gw-rh*a^&F!Rg%h2bn3uQ zs{vL_3-8yf`T2teFX}%qK_MdH_SJ%Pk7zjnsFrYNdMLmEPS4o5M&{(_a9)me-*Fre z6NQBl0>kQpGHHwL3Otm!tcuxE#a|T9l z#@04&F>2MYFvL@Mwb&J^txR;fo6cuzCF$YHbo4pbg9Q|i!e`ix*bS*M2=k&=7!p(+ zrN>MhaU4>N<1qQO&zo%TC@W6HVqL2W^EpOM$i;i0B+=Ua&-Ynt9hF>gzi9zD=}Q#v4-ykE-NXY*EZgw|Q{ z5iX6yHAMjRuo(5g*2d+V5M$~!btmka&Q?58X#(YO#P3`9fcCiJ1! zl_SCBt$5k@_|&fhvHKN9j<_7~i+&H;Tq6fP8L$jIB4yR0lP>!GY4~v#UO1aG`B}q6 zkh*;MO4|!o;jY<>YR`~zXt)BMrL=C6+UD_>Ii8Obb!c!BouYnbLuqE3QzOaxX3^PA zH2aL-FJZf7i!~6su_TOcqDkHxRMAni`+#Ah%-5bNFKkTTUqpp`pjw^Bx;CG%n z+rt#p%Otm{aW;-sJ;Y zp+C=VaL#2pZJ)c6%Clc#1ZAJ?5^J09Z=3IAS)AdNSDc}4li|1nS37lRb7XKf!Y`4W0w9grJwuqI<(dH$TUbi#?54B7=WTh9cdlhG%}+%Tq0T{~1mL>c9twP`otxe` zDbc2X;llvgvHXqT=A&r9i@jY0pWEJIn_O)lOpUGEWvSfRe?CQ@*@xsCgX!l`m{zuT z(0$HlYHta2)^F63*ss2?K!s2332M9$L0{j%E(pWOykJp1_6%HF@&i~If5CF)F0DKH zc3>UTwpLM;_l}ZX;WE<@MwtBd{9Ejv11bCTyjtD^Oz-geyqBWS@I+`tkg@Sg%2xWwX@VmN<$@}qM%X-sZ==f88&v)l|2SP7; zu%V(jN-|ssL+bUveIt7$CNsNdI4 zR^F~tcO18R&Z^rzgmDxh-64fw#3q^z*YlP2mWiU0Du0rv&H&Wm6fs}>_2JtDeZvPU zztZ=6_f-B@&o{}I4tg}yZt;|-;H?tF*DV_Y^KrrWY)`%rbxUuk>#qaKtTJYOLFz1< z_=|@@%!Po}?(k>zi6CtjXM_1!_@3O@pG0~=T>Z5sNFA9av&9*c3&}E+$Vw3;HCrG{ zeRE3~1x{NjuTIngLq+y&P_or9svR<5A)qOLVzNiskzi^I6^Q`MqN{FF_mEZDrT5-P z7?&cz)yHj30}9=?z$@l&`ixG}N(dH_hWMF$rA_KH3o=yB? zg)v2X!W-Q%>Ad{0@HQSgtZe^1r@bgNPSke;=aF89CEN#cYLJZmv=6x^G=-D_-s(n4 zj`~a}1;B6So!UN!pa&Sq5mAEUh|@0^Zwhltl`?U8Mr+c&zb6gB>=(%luBsHJun zEU?>2h<0MrUTM3R;1$-{A*-L*q@MWYZZ96luk_!gpMK(Xp2Sas#uhy;Rc8(>;Fh^z zxR2cSYzm-FM}{f83`hi3%X}(d`M51DoyqV1a~uqlJk&(eJCEBHo$!xEbhh{$9jTZ6 zKPQ+QX2<6P;D(Ho7j4Kdb?^K}`~p2`E9(Vv-7@^=A0>MIG0?=gXVEnGhD4})ve#aA zP#6-1>do(vV2&w@?EW4(x>^}iE$@Tq*%5Xc=HC&>GOARk0vp!QI=v_&_MfQKlSFj+ zrL8V8R7?Lj=u8BnsX`+=?wM8|&Yrc|l$9Kr>rnCcy{aX8!<^_GDvbK}hS^M85h?e6 z-!uhU7?N>-wqE@I&Yc#!rD# znVLK~N^Ts1pVZZ>5Jfg~uW9;M@oO6oxv1=e`1><%)ADsUu-|x6<_);3%CI_EXb-6& zRl^6Wa=k7EixJ>q0yI1E0B4_<2Om(?6wBr9)?AA*#`0hy|K%7AuqWR*R|o1eASEm; zHOTlXA_yXc%=Ke?e9D6hJmn#bbZ^a`gh}PBMCsgz3h(L)%9F%DXt=?xq&udd>PpTO zf~3!Kg*j)d<3q<>)|s)rV>D5GI5COBt_4B-H5roaE`jP!c%o^4X1fuKA_nRlGF;`2 z@-1(a*P0PTai9&$6Z@ULj0e3gNA_enlD&l~F|rsf#WtBV$H|{W5ZmcV^MYTztENWW z47oyfZDxji1|Yfb)!@r}B@Bl+hhc-K?g z*Fdhji?}d|_0F*wvVcNTyo*CsT+-_yO2C)>d-%90C-pZ@Z-@$Mcd-HXFU^Y76_B90 znV)c;?fcM(8UfbyNNGu#zD86brr|}P4~GB$5dZ#B3X&WU16FKQvt>XIr+(|4mQG=i zbt7NTgt_)pY+KydBd(#X+apAemJrqFcdw$5v;7?@vTlaX{?U=h@#=ElFLVCdn97Ru z-+=(lw!QDyy60L?ekM`n9r<`SnO{ou1Ga{J;?K{W_PPDTm2pHt&CH;?^}+zqF!;KN5VK9NMh(Rh+$4}34iop^JB zXDUdwY}%p(;a$9O-169jxF%35&^)?z95c~AHs=rQ6S}Nr%=g|u%p7??r{;P&XfI#F zR^il5?UJquwwRemI!>3H&2E*?YC{POS-RN?ud*8@ygC|PP0eP_gLP7*m{qJxAtse* z85cW$Z%5(EpNj3@8uGC8cSSiRC2k!&da1$N@8xZkV2R7iZUK+@`UWaX`oewkr$EzZ zX`4|NW0l0$g*LCI?#>A9tRM?pnqFoccdzSb2*pfa-!rpT(=NiJVfw@SAv)rGW% zjL8w#A{oM%3RPfEYSYKI48Vk9Br5`*UjhE%)`8%syW)~*FIS`b2tTluEgzI zvcy|lv7GA;%lS2ePNsw`Gi)DY4*)Oey9L#~uFz)|PNEJ*QA!gvkfdLkyn z_YzX4^AlYN(YVOGmRS`b4eLy!)p(>M5J3wiBHfJ``$hEI^}z~@@C~Vz5srR-t$foE z=B&*#r&0mHK#Qg3XiWcHng878iqFfUBj5BLVG6BNrdNW{Gub|_a*2(UEM(}G-JX=4 z^Ge|Oy(inGV7`YC&7YRKB$XIwPN0~Y^__?4O$v9Xn=aZE(S@23B7RH;OWHEXnK;MU;MOn#ZAH-tP@_PDR{}O3(i1xRYAv2l^cdt!9ug zoMAuULMYV-BXvkVjF@Gqi5sQaaLs_EP9&;Hl6&nYGB`wfaCoVSN(f18Wn@LgZlzuX z7KpaDI!=UmYe1tRFNmh3-VzbrKrFl^`bBi>l!%guHgn?3;vv;jfRv#BC(*J_Npej7 zgC}AUvTJ{3z^jQR(>UlR*w8xmh?$J1>HWn!6L^$}IoesEm|vL~mJj}nGp!kh9Jr8zJv#*ky~(Yl0Qb55Hf`sIw)QpGh1T@4 z5#IlKq5r&X`R_=T{|3$fZ{A1xGC$B1+k(tCcgb0#G~tin4Rw(&sT1%4ClDt!!V34zaytO_oF&(#+-55 z0Edmj0=v16VW1ovF)5OTMo|C_8_1FbrMgAnm)#lpjq>0_Vwu8x-*#7^J`EHs@=^w`h161YiAW(X+K%4l$HA`lUV(ZeNw7FV)u9G@reDpmnR=q@kY_BeiW0OK* zi&V^cfT$!s!viWH@&pK+Ph?{r%fS@w-|LAgZHk(w!Kvyu#MVj8s z9%iTTT2I4m+x=D8=7NE*9dtF8)l~D#Eow+APV{FktMkVoE4B`LYE7_|<10RcLbKdd zrydx8O#+s4Ok0z$B-`PfB-O!RhT5*_`C9ZrP@Q7G*kK{$X7=*WOJRqMC0>KLJo%r` z-;h-X2|r51^7hm6P5@jFKGpg$2?3h1|Mv`1O1etvpp@x#PX?O&O6((UQ%v{ITU4hOrT@SajJGP%YFmiI_uqVSYinx!;_$ulnY~$*cA)Yv^cS}c6{Tw;Rm79 z{#=?q^O{zZa=t;&>T(TeH30dRo3O`fngQ2F0g%_15Kjm{P9yk_IUFbm;SbzV$>hC^ z>8^uT%=tJ)uVdM)wt~aOd)nm;!Vlu43bdl1>#zQ_oh~RkH7sZi^Bgc-g(1YXBuf!wibV5*k_);I^X3U=r?oOf86JJ}DvxT8XdN0qba z;246Qx-gu%UV^MCKlP6Hl|DUIw9*4Kf99o43};VLrmrrdLeha&!YEubNd0LHzJ6yQ z3irAr>eZs3zUU{s>5%z)0dlY;y_%M8d2x1Sfy>j)s)#;0N-6gZ{NwjXzdwoGjiYNY z1J?^^WdWB^%Q0owk-KQ%-riWB+9!8+l`!cT)oodGwlv#%-YnZ=8}u?fcR8dl{YoC1 zl<=solWO0h`!i4nSa~P)VRhv^m`FpEH(;`gtVa zT!cRLS5@XyNTH~fpHFJsY^uwFzrxw74z%N-6KBA^j1AyoQ#`m5n3b=|@~fYgy1^%RV92YI-5VB|9D( z!sis7X^+F@9k`MMgTPtoFCL%z&FY4n+$8vg%W3~hlm^}}s)7`2{-SC8NUwQBwlN2! zr)JE^>_ur1_4RanY-6AYS;Up<0U@{BDJc@{rOCD*rtfG#P4+GEaW1)3nU~C&1(o-s zPy2F)q$2voH|eLR5V07kj?UH$TIu?EvyBZ0yH%#e9d$y1DHRhmgMx|lHWMoiXsl9W zxgcd1cc&fbHzxE#i{DD5ri2QND5ZBuuW9;V(WgNvk=k799R1>R0%i@(Vjtre27B_V zUB-Ga^_f!h08rx)8w805iw!#c!U|!-yRjR{jtS={mC!x+H)Yel;{;b1bjKqCCvFWb ze_T@qCKV8_Mp!uuXK{XDY|VxU`OBIdmxaG2!gbyqku8da=C_~;>~|eZk9=kmEtVvD zRwdwp8z9`4;jyl<(~|GRB^SS+Ym!VO|NmVsXFhnDkmPTQ9QB zh&B1Y+I#P)Ciksf6qg0*A|M@9K%`5PPL>5QARsMt5HPezZ$Sfb34{(xmA;S?I?|CU zgd)B7njj!O0fYb{o_Fmt&L8L8JHE2-UTc5fJ^Rm)5k|`U&flERd`d_8RowqxB>&V$ zXM#t9=XGQWGA4^T2UtO1UMEf}D-@q6b>nz3E|7g310DR}(OU?#uE8Ao{F=k zhiA^t;ttdrQePy^>5uIBxUd?KO-@Y%t}9EPS`z&X-V%3%EBlW{PrD@E${&cgkCbNN zUER#m?SzRw`i4n%@pc))%m<6S!kB zWn%Y<1u78Qr5EX|Uvr-E$}c`UED9>D{|EC>vDz8wi>%bx@xccj4G?wF2qS=f+1SOS z0_tpG9A8`nF%y|OY;W^$P7imo0y<6g9v9WmUAb_$>Bd-Yrs zHJ7Fl(j7AnEYMH=p1sk<1-9-JTo67=tEof|y!Olww7%F_F^vx0X7ac!=_ugpvnzqw z`E#3$I|(lDAMCH`9dx}qb2V6Cpe&f3wgKGy3^AH`zewUT8IUXO2plvFZ?)UOp05vR zFVdYI&K*1ar1;w79XHiH8Je|ZV&SxZ;w91RX_x2^X6E_*2(pZf5+1c_srcXnV?NR!d_u)e!dk=AU|b{abx98IZIs z;~WjO&u&_XB9%G`r|be0`xSLjdjXu(2-ll5lH^Y)3>^Uf&13JE2%M0yu9`` zGAGwpEhcvcU~_BYCQE6FUM>SrWUH5OiOsLq=jL^tl;t2*11?@}!rE>gZhRPJrpIk7 zN$hseI3YWCc7An;`h|qI7jl6S0T^d93(I9VzyHwnHXHxpVqn$)+p*IhmxWCksW*-9 ziA(IJFx_G-6VFYuEsU?xdiPoWmjpXD5p~Ik9p<>R#CLIekO;6ZSm5|}$g}o0vNGp> z=A+M>Ye#FMji1(ze>_voaYJvi`np?xc_f575^>6j*}TmjnymQsgFUCa_S0-CnN_x) z&P8!OZS;brYk>UJ z>rTL2ZJkU^TSfP;`s*CO&>& z{~GZ4?*s3wzn{fv6_S*2S+pJB+gZlam0KJ(iO3t3O-Fy&Z(E4`Hdv_UDcW~IN50?@eWX!i`70E2*-s*xHRfJ3rUy0;HkTB)T)9hlGQNeEmy?E&=|JR+T`^TX^+ zGSwPOy6}T;#wg5lg102IP}?(f2!_B);~{IJBVJv_Ca-_whdvKS+Aq!0b4HsVfzGpK z3Vzf@qk*I>V}cZT$H#=aDN<_g>7;LqaBW36Dag_pXnBbRRx_8NeSHTl48e7x1n< zOC9gIr7w>pMg(#gWQX@|9Xw1gXi{Udle7?t=!U#4H!J0^P{Y8(9&)|^x}s>5g0K6# z4GFsc6AF(?tTxFFIKmDH+cUqFil1G&`pvw{?N|(qr?M*_Fq2XlFhv^#)dKe8lWlQB& zR{5@G3m)+-8bC7bjyTNLDRouq%g}L@H_SVc>L2U}B#GOm3*q+mc1iYje~1zRY{=8U zyzOr;ca60_T$<+s%t4b+y6yo$QrER$Lz}C4Y+aHN08E0ib^hUUm-sH>zfCRq$JaI0 zcjrj}96c13RY}=N2i(|gt&BZ+n~Z6%tr>~04*Kiytue|V?r}dkRZapVp;EoB!Dq1j z*5{C&AKJpctfwmHAtaA^2`ujuWKL{ID|nB=W09Mf_Z}nJW@toExZxLa>{WL7!Gd+>g)^@mo+^aLIlhCNuCtEdqz+sAPz;BQ0l zW=_a^#H6#8dA&AHM^>nANYg!4AJn)v9dxur)IIYc3Dyyg-?%bq5UgU7d_mV$IhU-| zKw-*MI4Yd|wSFTT7hTM3+9MATiUhvg1Zik4F?b|%pQ961n!B+?m^!RKFV+3;aMP^5vib`6Q`zFkoy|z&`uRf$#G3h6o8F{2w zNsIOqmZsFZ30%Hl(!m_rh&MY>NL{Qb% z$+Oenf4}M~hHVJX&baix&PZW7Cge#QggfO4-(*<^cr-ObLB8kZ)VHw_^n?nX5Tx*- zbDogAj*{$Lk<)_AX@06aw}E<^uH9{Wm_Ga+L#lG-jc4-QZvt6jk4<*UbK32)!!0VS zMFdLRavSUYc2^n>G}kAWy*S|2*&+sINuOHcD<=*2TK#wi1GVNHks2=UtAfsHb( zjgjeZMLhZ#*fVzN&fOon(UM94S*J%9-M${p&{%T){SK$`*}xfH(E|_GQPuc3Bf&VL zo&LS8yZYT89nN5Xbt5PAx(rexKp_6KoU(UTRNXgidsSD&2{CRxkCX&#Ssyv}53h#a9_ETCMSxdBSxj~U ziefA(k4oJ=AzK~{C|!Y&G*)=86vQLJOj~A5+tL|o6+&x7J#AscVUiBW4mH`LR9?B4 zK5V8Q_TC&R)%=*KSY0%hkgcD-au;Cg$9cH$dXoTC=C|SGkyaX;Q1!{0Gq*@p`u&iM zDHNH%Tf9bTV`xoapr$+=(J?iVSNFaYr>(zdI_82i$>Fegck8!S9Uw4@Rlb!!%6&TB zPBBE%@&`vq_mdOVUT`XpuA zr%Dt8lS#0JYiVrT)bi8Ka#Dwl0;qD`OmC!TEFbTj0OfIxD-G_0$_bK(*9GsA>BvQa zBf1~APl&qVQssGmC{rXRjzC$5@JIDIPE|$UU6FLhf$fU)N(z_1ug*W}sz=B;io76T zMnZ&JU<4&HRW>0F)8IjHXgh~tczF>t9;~y6=f-VvM;@@`8=`XE?hX9OQxLPbCKsm1 zEu2$6bK%FV<}ZGwB2<-Mfk#qlY>`0WZ5|L~`7=$~{n_f3hz!A^RE%PLo~g`~a2u|| zAgT0$7(jT(_Z=k>Wjhoiu)3`KR)Tnjm`^_f3qPz{z}|EjeZ>>MpC#VgAKX}Wub;1d zR!Ch@EYA0}QdEWBh|1RC{wV2q@=(kRW0o-)iBsV;8kS9CS1w)?12gpDa$Cs0z}Aw?Q&F*+a{NJMN?1DQ! z+p{WS``3ks_FsCdl+oPpk>=|#MG%) zkGXKef zso<*ZR%VhIk-_0Ln#lQ#@Hmr^ke3vhAAp8PX`Lq*SoXNAQq?dbKvlQuQ{GFwcq>wR zD>njngGpLBEs&KmjljGy4y0b)kh`GY?5cU+YBqtnyiaR}e54n>AS*~c>5<$JXAD@P zl8jD8&VP|YY=r`$e+vdslfenVa^0_QbNR}ZumZw|bkabSd=#N3X#o~M?KD>gzuO6I;hoK2m0 zr?3oRn3|`VZm#1a(jY9^=-iXll_^Ia0G7H#n@IcvK9Iz*(48pqoNHI>gxjMC8sxA%OWMpa~Tn6QrQ-&|)GZJ5> zj*>IkiE7IOBGv?!g`hSd>kU`xpnGrHs{i`yWYoU4s*6EK-0$x+q=*wQt{9qVA|Deg zQl7h;@USdLr7=BXO8S;!&&z4eh{{k*kc2lGaXu@{qZ+pu%MqTQ>vCl>jf?ZNC`ArV z4Kuuf_bIM01ZL>>n<5)Lo%pa7MVzO<(;~R)BpHR@1KV6{O3Q)#Ra5;#4HHcykLeDjs% zWLEL?wDXPDSop@WE()-oyiCOXNx01dbb|fWwEQRgwLOCQ(d3!Kh=CS%|FoJ7#fa>Fv z2Z?@Mdbc+3Mvi`ZHso45vN3B~;^>rUaOQ`Qc4H!H^#RmrYr1Y1H-ZQjdX&SxR)JO8 zI2Ppy2^ora1mx*8#xd%^a*i&ep^?8KFx$nZ$lSsdP4YXJ3Ph>$ozoM~-2u750fF_R zLshx;nEKHSy&QsnD8^Zb6(yt#q*0)1)%trfhbDu4NCl{zk?}ETC;G zx)tTRD9@i1B{rp@-Ou!5K;r4+o7nuLbOFbPY3FDV)KXopDtLwZeS-1A50ZC4*t1`x zii`OQpwEpG-$0+kZw9qp^0|`gDyAuxp8;fb%2sK=>}Hp*xm(AsmYz`nJ&7Xx8X{)Ix&8j7nUxYuvuC?#BN1 z1Y|Wk+*#H)L<7o^kx;9bicJS$Rmgx*N<=Qv@9Y_@v_unxE4b!gie(vX`teDj6*BUc^HG7iH5#LQt3te7f)fSC{6UlUF9oM-$|#?VVjO^Q6Az=FfYuojo_#P|U;@H}+Sa zG`tt4g_U64kwxpsBh`Fgm1kVMOn+auI`Wo>C`8U+#zYp&x7|}$JwDg=J=q4Uza zA$~87q3{e%Z(*IdCR}1-IjlOZ&dPA^#5q-}ROn&C>Scfa(!8A_?YYt3e6;E6CJCmF9E)k*HU84A6z=y@r^o09gj z^TbB4kxz9g`+mWJ2&@u3^Fyn&f!)U0u$W3Qb!JAh$6DLBa;#zR8)Co`h)brN169Sg z?F9#_;swKHTRAKuGi-ZL*Cw%`I_M|}%RUx2NhRiEz-2Jk6*#j+`u zXB9@2_zE(qm)ruIiQQ@LlyDMh>~^S4RUe%^p5T&CodEpFpI18pv*qBPZZg!NJe!b| z!&2Ik9dJ3ir>O$9zb2woiIVn#aqT7@-;uiTNLe^8;UIbCbX!(5pYP8OEc~dxCUNvF zwUxv_YjIT7qgr5>igTRW-s5h}TT4i4@Moo?nN!ks#rwlWb?($SwHGz%yPT!B41#lW zKALakmWxRQa1-4DJ$T{WajQ)o@}2}=OXR`G!pR;oym&(CpEN-J{Y)Jd4Qy=JP$cL| z4POx;xH%3ZaeMG*x(u`=BL;Fe89u24t!UZg`s0b(->pTRUj+HAuU+wd#uG!&5O9}uHira>Sqt#n zWby9Pr4F2Kr67qAl%AsQ*Tr^aGFQCCbebVts{LyxS(AN7%`LxLVRywz3b0tm2c zjoV%-v1zjClkSzl^W1W;rBuve`C7$A2Mo>nF`0)#hUr|wN&|!W44iKvzR_ZqKPiTy zwvON)-68%!7$P||Q-xrRGR-jUW;6(rFXp!9ZJ!A-IRHV!yO;4FQsL*y*$*hx6>`^%FpiWSFJYs-4OZsOP07^Y5k<|gY zpX$HMiJaRG${pYV;SCbl)9tqK3Y}U9@E$1Zs8J_;8tMemZ9FSYo?O#(FAPU(utZ`- zT@`C2ob<4c>rE12dDK#|M;gk``*33v5{iuAz0ktnQ;dM&tB8J2@U`!YwoYBkl9SBJ z!b`OruEp+{xg2GP_50DF=d{JMWv&OLJ*!H2sWy?TdpC-F8Rte%V4xhsyf~?)HhBs0 zf*87cg_s!?*nS_$kZ==bDb&#mJb$U8(C#c91vAUNIRn5D$O?1}>H4V-JQP5KpXjjT zUYT}>{-jXPWGU|AdR~zdXqUV~bPGE`v?^Gr=qmJ9Oy4y|8ujkGiVl5B#CsJ#%$E!O z^y3~p%2KHiVF`HeFfIT-YNzYrZgNyL(dsz7dcXDd(e|8}m{H1y?ds|$E_R$Td?CLs zQ)4cNplGN<+aF=GHSaY**5ZuQXCaebo@rzL(gbU^Ch&mrbAm=p)6+qfT*)SAhd;`L zOI9+KOisCx}jlw0R```x)7^viytp)G&QUfGb!Jl2o+PS z97`RtPaQtxr6RuvOml99qs=iOM)}atd+*ABM0~#9DcX&aOcWR_(&EG@3hgh#-LiRX zsRZsfRD}FCEwdlF+B&2aT5*ENcp5KNs3W%qx6oS46ZD%(FX}Ae=H6pJrTeOSv)c+aY%kc*M01TXdqwi%&kx z%~9|`Z=nX3V%X9pW!@4v%$U%PVc?!~=D6|R&C9P~8dfMOcSFmCsxgV7M#F`!-z(Dd z#^mUM7Ff{zcdI9(FCd&@lH-!zQlusE3{f@zhs;Gb^H7c${crl;yb`$lYtpf%1;uCK zeElnXzfT1rpx9(q`1+v*VM`Q8e+f(5DlEsfBP^3IllPwS6z}S%|EnP~$_mm&;t4_H$bp!HZH?lkB!NA! z%79nRDwy*y9~A zL%ay(p{9ET(5;*-_u2sUv7ptw4^Y#PyF;7oyK1rDGM<(z^=@iUepx9%!zM7cPPTwz z*Xnv}aooCT%!3cZ15yq97~H4*kv*MKIX9<=%}RNY5W`aawEYANXLY97DPNA?Qi4?YgcA zE~+ej10&ZfsJBNO_zOI}j_F||xqEp~y05-l+$2gVhL<-q%I*VnKXKD$Q-wv-WoDv% z!l^+vZcpc9^pjq}MIVxDz#oQoT=^B*lnXw#AL5ge7Cb_Ctr%xc*+Ro z<=ULEIMZoYeP z>H1Z^ON`@;(=oW_@x7u66$4A%Ty2?DKV8GgLi7aH|C@aAPli^JidlH~_FF`sGwH8- zPa{`!!@??AE>G|elVk=Xi1S4|T+s1rY+WJ0-L3yg;RUkUUTab#Y^Ek82(($;Qg|TO zCxlTR;G>e?UcIdUG18e}kP6ZFBRiaG3Y^Ec5B6?J_FoUshda|1ANd;ZL8r-PTjXfn zZ_oeC5R<>Z#rmg=aR1iZ{U=hfLvdLhtzq}bLxEJoq}MBeeSdK$k4TN9i>tCm!Gd#Y zW(roKn$EPE@H&9cx`AJMBdIJoR;dfXXWuu>&CTT^LE(d5R^m8ETQ3U?ONtHAjphd} z-BU{^TS1!fVgUx2s?)gDBH*1QpwO!+^Sw=0=UOmwOj!ekxNyjJo-v~vO6GM(J^I3P zGeC-1PQDG|eO~`sBu1Mfh0Ak0$;VmwGH=mnpYu{s!Z_Qh5V@2>n4C_Z9Yrl4Gv=Yyg8<8W|5KnUsZBDNe zwU>Fgm0LR)xGP{CpzBVZ>L|q+Q_AGEAC=n($6hbAS%}*Y4b*s^=~&k@?EG44aN&5V zPIAwWBZrf_|4Z@tUw-5A=e)uHMyJ^4GpfLOfW*@3>K2@oc^@Az&=R{caAmQ*8<&iE z9T}q79i*%#Bk299YY;k)5Gv848vvY`iEXIUNrjiYo%wZW=UJCzl2gDva+vC^^J`67 z9r!F}n=Siv?sQ0(owD*u1M`c@LE*0(Mi$kZgOY}LWpLtHacE8lyXb6jPzR8eNJ~)A zQ4;G08tF=+Qk2xLL?2&dd#_AIg56bnc`B{rdd(fz#61H#qe3kPODB0WmFHxLnfuwR z);NYTps-!coH%^kMCbAH7t0GtR=?=}Hhg4?Y#?3ve*UN&{kFR|(y*w-SxK;|@YF99 zzFQaTunnC_|FkOulN06?auYV*S>#@6SXA6cT>!N+ zSW`^C#(S*6X?x5(XEWz+udtr8UYw2;uxXpguXc*gzxNl{{EH_5S`&nN5rt4bPstX& zR0$u#iH2o$jC5ubZC==HN6Y+r)-ZLJcE;c|Kn!1*FuJjUc}rAHM~_MuX4+SxS=aB5 z7EC3ulZFlxU}qvBIRRESAF3M*u%9+s;}Zf8PF|Lu%7EVgx5E+sNr#-$QV5v|*eA|) z2Xf&L-vq>Lo(qmgUN1gRc~Rth)O-Rti^`UDPaTgA}5N_jA+Yljyd;W^% z(!o*AxBMLkA9k>g8A8(GkaEPWU2O#!YsCfk3yZSSO?4RenAZ~e{E&CE3rw|kvO}rQ za(6J%0&^j#IB|KM<9%wk-KUDT39D?aw&(fV%65J$3bLF-44aS^rqP#T-UIYx`Af4l zbS6Us5=RS#3G%t+iA&SRu1rX zVd>R`ai>nXyMRBnf#2jFKuXT~#v`@PBQu42x&j~ry(9swXpk^Aka7r5WLtsTng-k* z>s(Vn@o~oYu_nKmPRA(q!?5nt%A;j8_X?AjD`&kh4@Qn;TAr7ef%=~R&R|HCSl%OQ zlW#jJ8jgg^Y$?P5IjdK{dhbZaIiMc9Kk9z%>EeBS0o=K`%Y1`JH6VsO)F&zXaR=gY z#O$X#JEW$e^F@m8|FYQ*W?u9%VEqrL9(Eh{G zPI#Fh15@`XMGaOdtRg^W)J)FojcIzpg8Da9<%xY{ChM@-)kA6j2KyAjqZ{uY+>wUn zE5C0VNnh^vM@%IbxTE{(e^rO%gyKQZdhv&=oJJ)r73QtgMe%xp?)ll;C!NPC5@?O6 zp{}9aP_Fe4qSll34j~mHZ;0y8($No1C1@vss0p@tCD+mHbcaN*sI}nnn~S{nvZ*>7 z`~o$sW=L=TFF!di{K|87CPN-wx&eCelOmFXvWp?;^z1wN_Q=xqX-tO4+7K|O+#m+} z+s5kyhM(Tkt06ur3d8!8(jri@0*N@QQtaBjJEDZj=uVUW48sFNWSkKnhF z{(a>^)uoFu*}%5RLN-GWtMb$O^QlLz7^E(-}k z+5mAwkD%k3rjeMC%~nCd#M@U7ON2+!M`B3vEd4*|!Q!R48U=zWJkz_wi1H|^U%B`{ zkP=Ts#u9Ojc#|2MygrfM$XDGZya$hd#0-re?vuLUU{9R`OwShgJm=!GpA>UBACpd1 zbbc{;@m3?^h2ytJD99$m{>_+2zdZWoD*rXu#w~fnh;)7+pdlaF*2I4wn^58S{G)pF zVF@4b{;>jZfOEB$I)k>)jJ^|A6#3Vdc7}BtVD|(CYP!aesYP^m2l57+xH_&lAAVKx zVj;EZ1HLz=4*+S^?0Kjpaw~cC1_V)zLVzL@GvWbvReGW3__(N4^BQ(@d!%H50CHYl z+vjx61)awQ7{SOMXZ7tMM-)i^L~n#YdGh`Tm#}Ys;iD!QD{*pwI@dNPCRj>Q@m=rc z7pcm3hHoBR(su&Fc=A)7it{t0We%N6M@p%G;A_6)KGhUqE99N4w*XfjVV&q#EZ+wV z^UR{nTxErf^7JJ$6yGH{06{il;5)C8!AM8zF!BwP9akTIf_g&ns%jPv;OgJYXd6{| zBISHD!6mF}-eT@!h;>z^IOc%3$lgovOBYfWu-`4hn&p3PkkcGYI;~@C*EJpp(A~AZ zigb(})oAa9BZ{SQW;{af8H(0MrI;4J(ZY<$Bzgp)STppxdL9p7REBU&G@Ctpj6?D; zn%X$7ls3+orQ4!WP6&uqzKni3V=yjk@5 zE1d-(AA*=B^pnD`ly-@R_rwoEUX;|F6#^TLm?z0>8Cf6hd7aXn`=4iIX{wrdEM2v4 zRm-|AIeNZ+%^j1i8$^K5)^PhbOh;X@G!BJwOypUa^auL4Bb34t?tDC`1KDpJSn;2y zDO=mQoR==|JklZ@h2B1SXX!X~O(THcT2e|Z$3ol1^Y_aXCeEz!PX_kCQ1JWMz>6>I z2X=Go)LMq=jE2B_#&os0#_jIC8^;l3(RR<-3dv4UXY#?bl&tc$UQVc2H1@JYllX z!7GoL)lr3Pm&BgEo`zPp)KUyM3z!JTu>YPSZvLz_PT|4<-^k9qguJV0Z?1I7fka=y zpzNm)Cg*NU!kz|b5XyHg65!8W)=7mqSZR$NF;G-mtbY$VyiXYF4ixS=P-P)#boATd zTQQ_or8e9B^aIf-fymws9QI8&7RZyLmr8On2!jVPhL+`4uvyXf6+h%T7SMfPyra8M z@t z`8ZVhk;}zsg6ieLl)$2?SK`GiWoYQxG%Y}wPz?|uM+b5R2$Aj$Afy5I#aYAr^dcwD zf}7*|5L_wDX)M9re-~_3Z7uM`a=3dr?=H5=q9LD8qBC%}G6R&2%@pXbHrn-9Y? zegESZ?wOD&sA1ce1tWB&^m3AkS`WHr>%uyS z(RyH&xQO;1*{KZRs1`dHSanwBq(K--pIDO^dnQ^CKPi4A^-Y~$&FX#dPe8E$!RP2- zw^yRn!nU%*(QU^d7=Ey^;QM~nmG@$Tw`Z55>~BM z)`{IPj_DaxB_Z4N%-jRr6HHItP?^&3!sT8212@|GgD|2Ji{ksj@VtbTwaWhXXRE8G zWm@4TQE75$o+oBW4sSX~Eh>&xCR|%_l)zS(g#;dx(U$A5eophT&j*xdbI}~>Xko#x zr{UpG7560gh>$zd<&tz&a~^#5&K(vh)^pF_$U@CRqPiL0e!oxv1ZAt8w-FKY?5VFC zTovhj-M!8G1e7lr2?D#aEeCQCSkJC76Gd24?G{CDeKG0-O>$xGWd~zy4!Bk*MRoY7 zXnieFc~RM+{!ryB^o9pq+IYQ;d{S9+(8>Rq;Q0T)djB8#L=06(8(>Uu!)0wN5qzg` zyl>VO>q346IpmWxiTM^f!$X#K>ecbtOyq(vDf@Ub`6ly9YUa|Z=ZF&M{Xf-Wk^YO#mvw~8#yOriY& z)KO5_z(rQkpjZ=qJcYlwvV_r{wZnn{kss;Kl--o_E!-SSrWo16EI+)kgGJVwj)>~y zPuOU=IlOy3%2(CCgzvZTCrLAqov%iL^#s;)*B{nn!0!0)y`CWOLMj=WK5+ zD-%7mvQYEmXdX3Q`%Li=3O5XyZ2)>20rtKb*gj_B4~$e!f6QjwW_B9PxM*o zLXO?I?^)lFRf)JT+;rjf_$mRDV>#s{9$Pl_sP1xfVNjuuzd^j!XvQI|VMli4$IN&C zg@NkHcp0W_Fw_3?S5mw<$hOAt@>|NW(PHXhBcZ-ji9OvSSdOLfhJ8RZ$2fs`1(NVN zTcTODB5p%q0x>c6wA_p%Qg1(mX}z)6yDRW!{D(y7 zBsL;onDdN3#HNqs`TXsvb80f?C1(uyFt%TG$65y8O*sIy$Jgt~mqR7O>(4!RRBq>{ zPCg~;o@yDkhy>sLNg+VHnb$zx&o}3;oti=Hd_&3`k&k%$+WvJ={P!9!|GH!G-}_zy zBavGi&_NSn+qGcI&4SbizBpZ^ZI;Ahea-L4;Y@WBG4>%yQRK# zX)VU|v4jFfNS)7g2$RCqOxgN+`q7%mMdNmW$4b6P99d?dLFf^oI4y=7M9!{PhKk*x zH9HFFZGJEz$8QmiP?O!4<=d`yncW6s5;k?MW3rsEVw$tpuN$4Rb%|I44Q5voUmPtT ziCF}Ft!R?%B#C^O=sjpk<7O+GeyJ_X-kv91y{eCrxEzTY2mCt;M9rE{rl>0|yqrPh z=5$txravj{R!;z)Q=82JZ7U`6QNJPFPzVT7@3q<33>$%jI$IN z8@Qik#lUV%X^_c~%0Nt*I|kZHi7FAxmwPwH4$>OIC2Nj?mSs(ERkz9=9xdpu(LcEG z+r^FVlW42dLhS{WDU+6pxbIUW7Vk0D-jG-DRF7g!mU?JQ5*n zTQjuWvRd5Ytul_4Ey(ILb7GaN1JU~wx89j(xkv1n6 z=$1Ggy$5)=DWGce?=*4eBK7ynIo;Kns1k|CZ8Jts+;(~5P3pXrp`&O7ghBRe>FBMw#gIu_X{4wtUM{W>^Yk}9&dQdM?Za`K!{ zAvtc`6Q~@t^=yw=&1&Lmb;XdI$E8^23dIIVeLCdVB%io7l0w=V0@T574-y!XvIrCr z8Q6~kPKS#r(3GSr)%fXtdL_#65JjSe)s1F_k@APtxAYUAXfj1O<(%$DJIklaRIt6O z-g;K5!{RuZCqIscWIQQ+n5WQLKgblRAN6t8DL80dQsLQ*IqNj1{V%7P!4~3Jyr| zKF~(5%lXEe_7CecTKZoKn6erSZOH29dVBE~%5l9?on~46w1(GZ6#;evBjrTljr@Q_ z>O+DI&`FlFHk|1tn*dHbByXqcF%d}qU({+{AbBI({AD{QkS+m~x)4e-W7Zq;ZSDVnP}Bd=WcHULIR7gCe@*dcSZpQot&><-TJ3kdw9nI2mA3R-zqs9iJp@t0 zs|=bFaYO~2iWRoM(0z;>4M5t3x^h-YZE~ zCMqQwghnCc5)}x#bj$auR}4D2B1N-Vbi9oO*o{{Z$rsc{dRGOE8M9Wa@4k>r5}}GW zu&yLp<9Ltgoc-y&jGEGzAk!Aw;6|v=W}lJBg2qLwoVDU=_a6eH>hCh_k@k#V<34IJ z<-Y$K$?vJ&Hu~6|of}~1sOz(vf@!bbdt9b0r0VQ)|C?89k02n2 z%exV;Ei*B+nJ@-Md;b7*aRJXZ-vP(WFVWrdZ82_@-D82$9im{FM2Yec%m-S z0|~uAw>Rt;S*Og4kzJoDi}gM$Mo$EHPP7}Y=kKw)Icc~0!m75XW|B6J|0rZhUZzin zb?R+;Kb^%1{?B&L=*@Gr02QESF;8f^ZtOO6{)kFeZ&|A`etIUVdOJxtt{A+a!hvKK zn3S^QI7_Y4&QF2V%Kg3P-+$_L7l}Y6dD{t(A_kGEnF#GHE=P;pHAFXTA7s8eQWs^# z*wSoS+V20VHm5+XCJ-{S7O)P`5X*UI%-@wHe^QRFtrk@R58M z7%f|KCkxKF?%(-a{n@An{-{;*qVedpY|(I`IrqN@O#CAj5xnaYXI}76@VTR}=jbLi z&J>qjRXLLS*mj;c|3UT6#g5s~(8C#{HxwFTsALy@r=es{QRs)Vm7f$Yphr3!2!TVr zQhvcC7r|aY z)v-^tzwn`P^9Hs;x_u1RGnR8BSq)lRhA6fkqwM}Cc)a{MjN`xJC;Zo!_5Z+jg{wqF zL|CA=5)!PEBGYS=@6hI-i*Un$#pbYhgk?Dj(UZL`VUIg6gi|o=o6KO zm!sww^)y>}=)K_%GNr;YMzoQvzhR)(<2k>*#Lv(K&7}EGy2N z(q{e5(!AX{AfbPg#5FZ)Q{i19vVf?vW>G+Bjh!T?p5z9&GI2j*<@MMf*ILXI+!H?{ zJD!xMnzPr8`~KP6ge!94vU3bHa6c=;{em?WX-ukkVcDj5y=W4LTGVOCT!AvwW#=28 z@(W{h+^T%jnS8IO@+FmgkU~G6U5A>UOqP)B)NjLP=ePWD9yWs&#@uOE7UyM*DB97Y zQ!jm0UZZ#~5!{+!J9OYWRg*TvxRp8-=5vFt0M<~JQ?gXK4zqGjj<;Hn+7 - - - - \ No newline at end of file diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/inline-code.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/inline-code.svg deleted file mode 100644 index 3585603096..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/inline-code.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/italic.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/italic.svg deleted file mode 100644 index b295c230f0..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/italic.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/left.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/left.svg deleted file mode 100644 index 0f771a3858..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/left.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/light-logo.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/light-logo.svg deleted file mode 100644 index f5cd761ba7..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/light-logo.svg +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/link.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/link.svg deleted file mode 100644 index 5fbcc8d787..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/link.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/list-dropdown.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/list-dropdown.svg deleted file mode 100644 index 4a8424c5f8..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/list-dropdown.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/list.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/list.svg deleted file mode 100644 index 97a2e9c434..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/list.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/logo.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/logo.svg deleted file mode 100644 index b1ac8d66fb..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/logo.svg +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/mention.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/mention.svg deleted file mode 100644 index b98318132c..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/mention.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/more.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/more.svg deleted file mode 100644 index b191e64a10..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/more.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/numbers.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/numbers.svg deleted file mode 100644 index 9d8b98d10d..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/numbers.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/open.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/open.svg deleted file mode 100644 index b443c8b993..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/open.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/quote.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/quote.svg deleted file mode 100644 index 57839231ff..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/quote.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/react.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/react.svg deleted file mode 100644 index 6c87de9bb3..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/react.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/right.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/right.svg deleted file mode 100644 index 7d738f4e69..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/right.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/search.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/search.svg deleted file mode 100644 index a8a92df509..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/search.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/select-check.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/select-check.svg deleted file mode 100644 index 05caec861a..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/select-check.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/settings.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/settings.svg deleted file mode 100644 index 92140a3c23..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/settings.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/settings/account.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/settings/account.svg deleted file mode 100644 index fddfca7575..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/settings/account.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/settings/check_circle.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/settings/check_circle.svg deleted file mode 100644 index c6fa56067b..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/settings/check_circle.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/settings/dark.png b/frontend/appflowy_tauri/src/appflowy_app/assets/settings/dark.png deleted file mode 100644 index 15a2db5eb8d0b0bfb2fb3e22821eb56f3a8f709e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16280 zcmZ|0Wl$VJyEaUMyF-8=!QI`L;2LCccbDKE+}+*X9fCW--QC??-%Xyc>O7}TeLtpl zN4k42K$rkz0AOG>QSh&NkYHc}bP~bNE9|w2&aqDA16fvpm3QeRdPIdK?;JwqVx;7kzU9ZL1Z7#m8tYUzhA6> zv|qfi`u|yP--vV`SjwCoEyPbXRxdmn@(c8zYMgV#Pc8I(;#hw{BnRNS+3PeQ>F9Z&2aeOsl}4AeI#C=jbwG|%mCBV8#;3& z+;sx5R|F^SW}XLkm1-hiM19;rQn`3gLv-XTj!&L)L?r>BocHCYY(BVdI<=z0=D{hf zPdB)-uenpAOx_54Kmd~IrJFY7n;qs9EV>*hH<3fX!1^*)ga=1+GEe&!Zb12V^TPNN zNwa)MjlB+cU@0&o7WE|>MF8#c9lq>jR~`URAoK!%piVDWOzg@_e7tn)qWBWQumqry zCY2pTiG+iOFq7tSI*nNH>sBeqC5P#eHu%}6?yP8=A3_8gYbIIRc!dPO$9Yah3Tmhr zAu&^(o=XxE4Qgm7JxPSW2EchC-!t;hc4Wl=hU2HxP~v zkA9|CkcSVti%WUx(UFn7Y_ejS@sX4ohh9y{ho>`>(-qx2&^;Z*feWK`lONkhSTda} z-wNn@ zhxj(_+(l4UaX9;8;%RXs znXTxTej1JG$+z|HCXPu?=5&yfl=Sasrn|r5V<`}0i<++S2oiY`-|1e#h;6D5{(7v) zmv^8>OYds-`#NS|gRsCh1wcE@w58PlWhJ`V^hn)h47omnD(pR}h~x)3JTc5be!k(S zL|V1*($X|euUCJxyzOmQm+g?jpvEnj{e@01a5lCE#$`=YMQZUDCAhFAe?s~E722Wt z2v2eQAwtPuI30xPox9dTry*jd(3YSM=*ZaEd^sUTa$3nm>Pw~&{ltJnSfe_H&SVZRTm9E`_Nih_+DR9-Iqn*px9z5V-&v9Ylr%b}~IBuPGhX|k(C z0KQ&_JGRw|a}u{_6#V5A+hWBQRqF_}k!2KQ(iPh|Lv7$w)lR`dxJm=MKMoNI3BLUS z*5-7<2qg`Tn1%*kZCxGkHx?nGfc}cnNaEJ@UYV)2wa-+t0>J$8dFmLEXoX;Xq<-?g zVRecV3P?vzdpyNNL&Xa)Kbu#wnNDVn>2graj}Ncjgpz0%k+k%^DlRQ0Ml}zb!4WBR zj71~u+z6UaWOv~uNFLKsZm}>o2hC@?!i7rF^op>Zi4asH#yAVX`(4GWXWyxU*bHn- zd&2iz0fw3J)frS%1>W6o=he=Y`L zODi+Z@HJZG;w@AqJ}X2M2%CRB@93mo?&$Ha0~)}`dhvmI65!0G>`xkzIOoOn6~{ML zAqb*`4_)mHcAJc!tu%cT5RijKQ(0O(eP3SC##xSxIPZKe`zQ^!$-`M$&TOwbD%eZ7 zeDX+!YaV7-F1jabqLg{Tn=3gJve`Tb)<067&)!EZm+*-@mQlURo>7K2vPVso@cmjZ zCuyFD%62e^fA~f#X=#hxlW47?@(>^XL|KIHl?6Q(aTg3>H33g5Zlp*d9`2ewXzHxM z+DOjH=-#DRO8$*|^=P0<-R;K(e7HOQ%H+Fr&WNxR!-Ijn1ehz;H`G=Mq1!1gmUTWW zfM-!)b=m|~)@-eKvT|AuUEv0FrP0ijI(tXt8N}MMW4O0oIuY;cDz6Tv_M3ac?8x*3NwaY4`mr3Uq1SOM|xyYF8NiKb2i;R=BG zA9}$2#TdYUpB)hYiz>q6f4iW1f%nz@LMj9TAjaUJe~n@Obwu#5+5cY`wVj|&_0*Ls zF(J0LzOmD5uMz(p!e63)?`KX~9eRvz3_oaea&)bQt%ny>*zIqypkeYAHU46ok@w&6 z|J|3KWSJ#-92Co%v@yDLYN7R{5LYh2EGj8D&6i13m`j^m_a6v!0GH$x1wd$o6A>R9 z)}O1X;4r(cqxv3mrm{g^ZGmSkZ3m~;$=PMU`AF^NRel)X%S&c5UsiWzwCmcq=y()1 zQo*L3YK_^5CaNKsz>%Hric-kd^9?yI4cNX!ydKi2M0RcisPYo>7FCN3kP(1|S2!39}Y<8US9m@;@kAv<%k%s3-G z^nH6+O;(h6I@W*@xNAsWEU5{U!CNIjCGFmShfvioKngTiEg{nPIHmP;x23oAQ& zFCmQXZ&05DnQKvcHvCmL|7iw$d`h?;|kCv5K(s&DGC0A0_!O_TI1BvMAu`HD72JPK5 z1(`w=@I?q}YM3Y~hkCIoCbrehxQ>tUlJ=UJ2aX+~@i$$`Tm{ z$k8Yw!_m;u#Iv90oDc|U2p68hlH@(huppo6uEwgo6Z(p=#RPbm@^TI7>D8Y78t)_B z2@a1)g*DgZg6;K7YZM{r1%npWm&CRTU}72VFSGZDO!uzI7F(?9LL=XgC_4L)KTZdm zo!UQt3JvXe-eytlbixFtj2)hwh+!jMc43G}Ikh6LG+Op$z5nqRG*M|n>us?&Hdxt5 z42}%VHJ~1RA;j&&vo82MI<(y!VCS@L2cngT*Qi(A zdTYr5&|v>((mb!AvXi&Nf*az|uSrH~4!ORm<4B-{?UBA(Ws%!%P|+sxF2oji>bI%> zm?3et?Cls;(Qy$Kp*-CSZ&)+z>X_S#+ED29{y=_b@Hn_rp^TvINwevU>ii%S5Zj9| zUp@1@Y{X{J0yY(8EeGUv@>JfVQ_0Ixh?oNEn?&}-L@>Vf4{|5Oj0p^Ghfrko+cS6| zS}a)kES!jVdXC|*U`*%)t8~2MpL#t*Iyltl)Z|!?zNu9h&n5NsU|7fZnbe(WaO|XY zKA`Cv=-al}>-X)D$H6jmKGJG?cp;}F1#$Dh^r*f*PJBRAb=0dA7<{~CdCB@c^cAnL zv9lR`bmwt;+_DIZaMiqA;j0RQXU*jN5%UWgSkU=s@)nv}(m)# zy2dZ(jSoZN!#1lCH{M2jntjZ1d2*tlMqfr;T1q6zsQ0^vcNk2JyW3m;N^;%rE-Q(M zw{OFWbe<|z30pgFK~pOWMG!uDdCJ!o+U|QIm+ySG#}g zN&T}nzEP(y(`jikZ+CR>>^#)E_NsdTKC>kb$zr4KX~-j$9b>{l$NdRkU9$hdYV(O+ zo0G-CTVDZgnx$^jmb>SCEIg!k1{&}En>~bKo=3`rLJH22OCrJ$$NDnI?W6K=ozy<| z3_I3hdCuFl!0L<~e`rROWzK;}PL}ZPRphn;Q7mmVbmr+E^q#Zd(QTo*jRT z=hE^7n$^LoNZmQbCgbpjVU->0LA#8PTv=6r}eR*v@DieAD8xf(Spx1hTgWWE{O= zaeAv1`$NlA3B7)zG?^b$7F-v@cu;SJZhS&RzYjkzC_T7xk(V-dw(=ywnoI^!LcbZG zRru3`WSK44*cJTW1ay3S@Jn2Eb7aIGg((8ws3CDWu<-dVm_ zJkTRabjq&uK@)!@U~OjvGAFQ8KRD?460YAehK8jFdtp>1mbd)H;m| zxJlqXv(V6>ON78SYb!@?czP+MEO_uZ?6R!pE190Bdyr1ST9GOHZ1UDQh*q55R zsCe2`)+tf)%{VT(Mu!L{Jgyw-U3*sEa7tjb>#Ts;+w;4@5Pfx^iG?(TV}tO|V))jY z8W9oRrk%nUPOII_d>ii_%(lx{#nPO(mCs*T&c3gS7o(vQYjl{_1{X6a$P^U@6(L&D z$=r(4C2Oz}a;i;o*) zdJfg+1g=?_!INm!gGJ!O=A2%g9to54n#03c*<8QsXbeUe@);`pWK%%-f-Umy;ky%u zp;8JZLPgFkDYD4N=c%NX`TW$r2yJdS@rUY^7?#2OpsTx7Ak3e=|A=K=`Tr6^2@E*+ql|V^rE4Q%GP7=rQ;N&^`yhEqX3W67VNZ8e} zOLClA_2`SzPq!yK?|p;5!WMY{xO@-FDxMv;tU-Xp_=;1rwYEN0AiK&UAq-?ESoK27 zw~aW{XtE|kxiuJH6)M+m2L#k5F=PVAlArm6c{R_YZl8(htvc?;p&z5XFZqnLF9d9b zGZ1}y{Tos|3P}bd^N1L*H;BMr{i2ZdXP()_rDVFH4S%GL6@BAvTMPu2P+KdNgkpqE zeq6O*OiP@!{q`OiXmH5o3F{pcO%$v`jlq5{`8nF4ywPt1|FCQb3vc-*r{~U~Pars74 z*t7v&#lQ^ME)E(yG05@;RGr8hEv-yIjqOsr?H+V03-wuj-f(>b8p&$6c>uMlhsF$g zdf`=awgW|o4(K%pki8yBUXOLwmBClGs&_8luMQsNJ|4U~c3P+9)~DxA_L8X|W21fB zmZEWD|#X``gkGPfoO(m4h_y@MqW`*wX^e z@H2h_sLt=dXNo0HBORCIpG&%O{6{8(s}xx2Djua;Fq>~z~Xvx|x< zB7C~%&;}+&y~e-J{xMxnkC77dFwE~CxXO=)ud*N>-V8e_U2Ru2=>dBy%=v z&IL8SuxJSXqa{~=4T@3n%vKROQCFb^fBYu>nvda9;oqjq&%_n|N zwPmHAB+0Syu>m)<{4u=w;ulq6n;6QPhvC7Yg@%$@Yl)Clilfi2ayGF|+%6F-!hhEY zHAGza><#KLVbe-FR~ri#{Q)PA=0LP8?`QIMH2*1G!VveRMdAI%n8|ojrsLRHeM#?Q ziEt?BC+ShTzQKXqp@pLU{{CW7L-skIA+OE-K>{=}`s?Z^uUPAEe(pb&pHqs7M9N9i zh1iJ7q=74o5|}k!s3hGem$M|6KQ`KcMf-y4zgk{DR43ADRH!dp+a0_&j8I3Ix~|#V6X$Zu4=m!DRX;R zF+FcWyq`3(MuMaY?rQ1Nh>p_j<{A@ana>|&f{y~Yb?&5ilh+q{a3 zvkq?oUtJ|~k!V^h_}V%Xy@&I)+oVcsEv>cU;$mSnH7;&$?(-8soB14^7IG*~Qb-b~1C4#ki(JC;E`@T51J z)m9f0N#kHi*Oqy1b`?4Bh*PB@SG8ROX|;ZTK}5VWwX_t{)BE;57v>%lpPt`nWoFi2 z<#lHfpQNI@R9mq4+Yw$tA$DqQE#mO-%j=<-w~tz#vEIe{L3Db0d2Tt4O6B(9lmR6j zogc`p(3{e1d)$T>&#k9MfuY=b8M9gcA;49-^mK^SCoHD;e3&y!132613t_mtU%AMw zs*-~pU2Z6=u|GgEj4p;cYEDf})otR@ty8-y@_x?x2GeuPXar=xk9Vm?PVMUNzlpEz z>l?@}ErkQ=ufs{MPkU9wBqSM7oieb3nwnn~6cndsR=&q1%7q|?p%!_iQ;%ioguC8^ z&e~(sCP!6u+*YB;O|(b~%}q`QkB&;W3B9DNDCH|iK*NTuSr=`LN%kct(5Mu@*PXxngnfz{-F9SU83OITP2U1w5qT#usy6SM^qZD1!A;Vnlaqo z@4jfEx{Sg4q2inh65`_77-kjAiHekA{iV6MbzHNo>$9`rQc3iAIURtvv7FC-bppf(zrEvWT z4_NIk2QE))zc9UOzNh7tp!{AQyfh6a#D>s&PH_Nii|>Rn$dTs5zI`G5jQ=) z9&$+xL`gwG%5)3HwOwNIti{!~NNH|=L_(ej87+Zk%8pV+?R6j;-FhUAv8ArFlT)s) zEWdJF#QSOjjDjSVrfz=(Ua-GRRq`M`p1KlniCI-e2ZXCJor2=6&?pjFDkBo<* ziVt-KaKGh`d%okP#`)MpDR zKI71#qH4CZtthELcUjnHtjYiiXM$JQlF1`4(Bg{30pGX^Z+^g>lZKx7d6j7q*9HLp z=#tAQO@S&VPRqY`;*?`D=XBRf1oJ=@KL>Ft3t^1i%VL4_j8!hQT=kCIs9`jteG|Gk zd#=`=Va)@wQ@1_|HK}fmjf#!jeBk;dLr8O=CcM2!0A8f#dkC`-04M~!yj%f?>({KW zAv1Wk!b(Orh7+hvX#UV&Rkrl0)%$h1Lnw{l`^dh1!G{K2t?6OL1;d@om;t(S#tI3d zs5rIo?xAZ{6W-opC-?>2tPW*p)X%y>FSpOay1c%A)0_yW;x{r2SMp#s;>o*(qL9 z@x)RZ7^lXrSp{T!DY~{B6_RKT7{2568x8oqVvXG8OVv_nPpyjP<%Ce>&WLk1-S0(} zWx=SpM)O~}(@10S^dH@Pr^8>mbYTF@O{`r`@075L$i6sBte1!I?6gX=e*}>WjpdgU z5$&}wExiQVYOXHDh{Q|O1seNWHq#p*N5&8-e?H`m5`sH76GDawGlIGBG2T5Wh?m7M z4k+MSsPv%Hrw&4nT@+xvuTv4vE=LBrHcrBcI zG`-Oi2aaH3-4*{nj{Fl*EyT-AqA1$5HtCZHA~7-BCt-$bajjz zjo>u7&$df5L3p3gFtxxR3ayG-b^8Dio4e(y}#ZcdhnFlDT+x?YMeJ zS}RoKx`pMlyvKze?GNO8&V%*_FjW~cex1;4D8CUER`u<&iTEkC|~D0hwb67}M*C-j6)JUQ50yX0J5$@II_)lCVN7P&#Bm5|Zl zU1NPul11D;R5emPVZwOApp$QsZ))HXDjDGMwm#bw3GiF~o1lmSE6h{sx0j3cygCoQRUa_lkfTzO4X1s^ zuY~WPGZE#4qNId>jlZrZtN`o3lX>@;+xWV7X~J91n0?fi1twCo=1^WQ0M93DT!;qm)^YcYvxxjE+HM`qfG8=h z>}!!*3`^kVUB1~?LH+ZvbhG=Nz^3ZXX-WXXGZ5pbd&|F|5Fygw1tovbGvM|@Dhpc0 z0*Em{fs@69VRX&Jt$)Z_0se9LJhYJKSrRXkR6< z`cj`k*8R9?LW(7E_=M~oBq&<>d&6yurSElDPQq}V!cIaY8VXMgD>Cmzx5YtIg!}!8 z1{9QdK*?WdBJ{*dOejA=mrrK=QTz97(lQ3Uw$2e&Kw<^n)g$*n-q+|CyR|;~n~XkDl7j;le{6HoMal zhc_)NFe{S_{(QBU8d^TsNHwV=g3$6V z6T3*HHgFEa#$pUZkpg0=y-JJZ9U+I_ujselZ^&C}t?=_S z6kD%zI=#ZEQ9%kg8N~6F&uMQ|j-wvkyNsiVO(=IGA3{eaB+ni1V$~TuqWbqyw`0{m zFzPVgTf&s^0+WaZE^6s^mq@8=>=j54CbEzx5>0f?ZG7%d<@ZoaguugKMC{2W&A9sa zi3Qm& zNDzmFlRT_@@ttm{n=~l;n1<3buaS0o^7;|}Wsss%S+B+Y%^D)EXu-upMI+En1vj&* ztV?@CBHwto+Yk>$)M|3)>AL;o6ZY?wGP{CC0`H9))R%H8vD2uR-1>2LNhbY-?(xq? zJa;8%(OWJ}(_ltZL|4}_lg&;1Qf8up1-erLPu_Y3#etBN;(j3yTX~-@Xw*c^!gNG< zvfo@)EP+QEC1rP!5B+nEg3A2QI`7WZZ{V=`e!1`!6I^){JWYA@k2tLnK40|y zHNBuKnRxvk|7*IV872F9=wSOmfsgB^6WXM_ zy~$oH{dK9!bmZG-)@yrf%1&WYcoi*jq)!C?DGB?kn;ThCOLBpIAmCSH$Ic4HnPRR!J)_)pkMyW2dR(Z-kly$JVIt~lhO;m zK$#Z9OR{Lyfmn}ehjm6Y6ARhPGrp7{j64hvo=63M?%k2z@CWsw<64d9uTTtw@tFRO z+pY!$%bzFljXzO^@p%doeNJ1x`K?G&_;zG6Y#@J3?7_jdUJ5cD0ULFFYv_MF%CpYq zi{+(38y&EfvdWX{=hm?SXH0jeb&2g~XTY;)pvSuJOj#mO$EI*HX0Yt%*d*tWX^XRCCj2osDrX-{+)oC)4$7iddHIW%%%^$+kVK<@)rE zxvr6(MA>drCQXc*Xi$}obT#}!i(l=rN*bY0_Ry)%cHuG#UV9FyFdpI&@9_|Yz0_@@ zB+dM;sHm5Bh6%)jUt2_|sdu2%)=qz9eS)VIvq(HCnmf5VNy95Wa=IBYue|Ki4VSfg z*S9O-=JcBLZ^v3!PHEdZO_S}t>L$Q=ww@F`6r4QuqsZ>wYw9va?6QXK67pm0HW;vE z^?!|LQc6YCCg{4K;Ll!huO`i(xpGm$9F{Y2AuPfqh4DB(kc__Dz8F$@5*Lxxtbwcc z)`xGPyWHIIF{0utf(Y|OO}LMaUP5k{n9wPhu5s@fm>Sna8xN2)Gugh`gyuGZ;RiY2$zGFusJX_z zKAH1nlnz6MVKV9mwWBZvqL7}?ZAEX)P6=i9L@+7ho>y&I#S@|-RP9>i)zi`~Mm`aQ za)0(bJc7?%P79XggD+{c-d$daj?_l05XFERLA7$6(aC$@w6o7Oc~XWLBHjOF)8Z=g zM7>^cQlj+Ud@Qwn%Q{p7l`zir8?>JOl``^FY!xqnI8>oLQTM$oVDNSsBgVDyqCjb| zbol7C*@i4(l`0qZNjZ^c1Kmb$&q-En8@-PaJHu zO}!Bz`^wB+=rc!L7PFlbn>9xmZ;yY%rd?yJd|*QQ$C_+2j^*?Tsk_=WZ(Rg{nD{~K z3i5Fw1UHy;0X9eEWR>A2cSv({<1%@#{G7+PbSwF?vfaWdM3(@em75+5H#0RdkB&=X zq{6+u5BxQU`Bd8Gz7Z2?d#je?G@P-oM$*H(r(`=WWQdy;rG&=33ceXGtBxoiaxchw zY)`iGG+pjfQ9Hr*=%Ky|i+fzZ=HA1^r(bNRYrlGI%9Aut(W*hQTL0xtu z*+dD!myTs`>7mtI`laIW&11JJ#%jpb;A^MtL;<9OT$RlWbZxPc1{7`P28LK4&*{d` zJQCH7&V<>tT|q7;(1^i|kY=k?s`WpL>doBxg11;FpT-4>hcT1(THy9wF1)F;#DL@H z)2(?MWlNR9rq_i3t{e zWjt^jFz3|og9mL=jJGO^i|p1E{2gpaa7P~=0#ehbTe4g*!E?Wx}Xf-I`^tGo!iHe@U-l=m%ot>ENy59p^HC;H;q4YDsQR zlmCe20JG$^pV4!cMU`H=y#V22Djt`opV2ch+t&NwvU1flC~1>h+Zn+NXCcPI``|+a z`NslAVQ&!nWN~G3#~O|J!S6?PPBCzS@=`{Uq?eltk~>!<+~2)#dEP&nC1@4_dqzUow{PT3REHZ$>}AD7|`c(d$)4JR2hrASy8lMed6U10f<$zd+&qKm#Kh3kQ%lE_!^0m@ z{R0EJH8rR}An@??RB3znVPdtk02HnYLQ zYg||I>FmlSuyLbJnxuSh{|nHv`>if`N*vWCXS`g(spdpu4JnhQ)#O&wne z5@mZ`+duNp$0a5z3&UObJdy!!WkJF>h28O4NXi? ze47GE-|>Dj~wee+5|;A0eP7*Fx9HPyc~ z+gxA|#xp?DD#1_t&h+3+5;B6vy**5q+bR6us4y{UY1Y)V!F3Z&P1^lYDnVssa*)!0 z_{4VXg+&_K7aJQ(3E3v`d792m1l&) z=;ZE`&dV|lr`p!~8=LqfeJB=E>Ze7!b){F9sPaYmQ5)zyUYnVE&T6kZvl zqX=k$CJEy8u^%kS^N!9B*_D-L6ONTPbUGqy55$i(KUQeZ%=;8GH;j5CGY)W z9?5y<&pql&MF>XqK3#~%X13?5+H0st{Fe;070P= zEnz#m7&rd3wJdM1&t=LL3i2yUbtXXE!J};=TK1Fq3INE`Li)-nUSeE#U5!OY4fynA zToI-&Z_c3j{zc#(Wb%{-SLf#oyUNp=`d%_JmTWj=O;k}ELP9>7qtq;o=(EnWfA5t# z$k`CvFZmk7%F4P4(i0^qfBrN^gi#;|CEH<;MR;gY&d$z?D=J9P`91q?PEJm!xUUgU zi(+!~*^T;;dqfE@q9kNx{b%%oQuqlYt8@Rr?@W|s^@Z!~9M4&N@4yJ-6tno+4cpV( z1^PQ^xp`5L33$uj_kG5|(@7BSgA4I0hie3Fj|8}%N61I&>sjY+c5@AIF(lWmt08}h zwTJt9W?$h9;|GaOIYkl!!nB5uP}4l1@gHSpnrdfQpGiHj6(ZB`}ZRoJHTSEm`t@RlSQ($da^f zYZSA-qvO5$zF)KF#`_RZA5CKsc+k51Vi4nWy^arc z%Jg#M0G7ked8R|Pznr@((UmFcv*9+WTrP^~gN!q*jy9uusD9ATA_*igKu^{2%gY8d zOE!Cd6iG%mpH93PG^RPDt_O%1Ao8JcRfZZ(-uq(9VKhYWpgnSY5Pt z_dM`A%5{ckNfLds;QtarNj-aLXakCL8qa;n%^WR}w9#SMpI}hlbL`M{WPPk5E#00| zFhmXg+T#b!CPwPP@_8ra^>zI*8HBV!h~Ht*bUcO$;ERAEA+XNUe9XaP#$rYy1=rq- z3oY2;tj&WwSSk782Wz)1KG~m2! zljL2(ldA^Rn~mOd35(N;S}5P3iQQLcDJs;0l_^&y*93fVj8!Sdw|L{gTIgO8 z-sI|^qi#=)2SV;#ca=}2r>2J9-y@~-y1MnbSvdC|+?A?Px+V_S1V=>lU+;}5u>#f9 zN)k!80b}~thj~1fWqVh{@4c3~V84Khb`t_l-y%Vwo9 z07@(1_nOuFu{9{h@cd#uB?dCE(IB+vgH`tNF-Wnfwa0_8BM?-g*{v7Bf@UIr|5hgx z_W%`~2stRRa`Uv#YHp`-*a!iE38~XbkReIa^puo8d^u57UtPRS#?$?Xz9c}C3*jO$ zfOUah`hmXp^v?r>gJo1sXZH*<04zZ)8Z1`hlliggSk4rvM9u94Q`dJJ(7{Z|cmk}^ zKS4|SKSYcn&u{9M-FsjXFM9BnSQ(K+*LP76Bgo_M$J(^9XddPx#L$T-V++5k5 z&6DF}gR5g=06ccjk%J&I)YRf^xxc+_i!FeWIuMgIQr)FNEl?#L--Zl{z>*gQlsYUC zAyNx-nOu*E-z7x@NA&6H!$w$?lvnf6Y*RN6iAu<$)Gq&2eTgG9Dyk)EXaA@ACtf-#;Ow$VQFY+pkCA0F<);Zw#;Td1KGD**9A0-??ddEYFaHn zA+Z^>l|P&31pmoTnPs|B`0@IcpFle@{h`;F4F-PBocY`pQcIgJ343^SV&ePxsV41- zR_VOG6nV_rZ^tlo(N}IDN;^Lx2;`}yXkZ^812ITtV-gv3LfhT52!CJ9matHh;e?GO zRZ#+(lWGOhW^-!2rM_rbbO{E8l0;2~0@LTuHoT)F@8UxZIdlKy27>~vGVPIrGE{Nk zoH9L%fOjl+ML*k6*S%rj9Z!*4n=iu)us4qb%PcZiwHk?#A1Z6Y@A(;u%nS1A=@7I+ zHKQgR1i@*I%?9+bYuou6XsH4YVB`)w{VpjZgY!+)uai)~OMexpQv?U6FZ%eAmoTBm z*uFlgutrNhH^%`i9r(aQk__;%~8V3KQ9#5)Qc*~j5AXto0SvmK32R^ zO}OXO8k3Mxpbo#7Z>$qMLlh`==T~iWR}fW6lV!@R=S^Kd%6>VM^)CH}&s?JzPjn}r zP}AHj{v_4X!bNRvh()I^{+{4ij4y-|9{Np@oxtHV3Lyx3V@+IKylX6Nj`QcO>Y}GO z@2?h*2|f`6!Z0bT!5E~pKTl7tk`Rl7TepX+EjKVpmFM(^!bZhVvNS1kugkq#Z%;!v z5+pVjXUmTD7j?JlI`8Oq5Cd-#w2ZCJ0wuf@*5{+c!%F-sA?Or;q%4TRS0oDWxj89~ z9HvbE0Qn-z&l9iVd{G%K6C$AZ+q*hY@tSExqXasn?ufM7T#hOWydynavezrzQ^q#s z_?N08gAflu%NH_-*LcvvQ1MM7k7;?t>wd`&w4jou)ov3H**r7mPP%N7Gu;a{T5PuC zbAv85@Uz8ri3iPNj$a}B`>o`o>Y|j?)hok2z2rRFEG?_l+SOfufNJ!5?@1K>;RgCY zzjZ(RUa#nV+tw!aj9teQ(+)zySAN+Ww=QgIiYnD@3iQ8;;8-V>XD#z03hWPzaG_tj zxxw+`$D8-l?=zdv#+i`+61LIp7gWscB-21LJ3mj;y;J&CW$dwmnwlC|pPZarO#JOW z%q7c%mR8h5YrV!Giz+WuCkWBniw=v_tGz}+5y1HQZ&8I~e4+~Zs)evVYom=f#8RE{ z*6P!Z`DQ?#NEp@K)sttl!%?kdz_XFVHH6#tZex=@wnOp$MVgJ5Lw4HS?1Bm^|o(t^W;9kzshyd(Vnw}N>q#C>u}eI+G6 zGY%#s7SswX4(%J$r?59@mp zVoA^yV9pLGi8@@4+Qei46k}g`jZ`iLI92?SY%_025UDlOUp-p74qez!8GHOh5N&M@ z%DY5{Xlvr#d&Ic9YM_r!S!X7@mKB;wIY8WGTfl;D{flm{?`Sxy$R`ZhcB*#8)UGq!5d?ZV3LYirDy8TYXx|?3GeBprjD( z9v9|V`DIF`pCwr(CV-I*kpaA{sIL{V8l}sMtO&?yC{buX%=}M;aHB+#fLFn7z#S1| z1uI4Yiy|-~U`L?RhK|cg^!BB6+fS-?z+GlRBAG$^#RP7Y5sR=|1%mZunBU~Zc)uNI z|BB<-nSR6*L*%gq%H0-NEFzl{&@U;oKhank-JujFRN2uqqXKJI)#7wp6ertaXgL$b zNKpG&zxk{z=BB1u6$*Ff1(DBwN4yt=~Fwsk7m*u?Os%Uog%3aPKy+Zp)cr;E{@8*l)YUl99Zso(cREJLFWAU5r!|(TuP=2aQ8Uhg z#}@_g<#(N??l(P(N{|IwOs#j3IgLt4DGDz+jLEew!*-@b%$sxT%IxO5jr%qw zf`i(v;@%m?W^5>;uG(oT4y4J~N1|z%Aw}^e7B?3IT;v6-0}kCbhNEkoV8>I8pp$$3-;})Pt=-4?_`tpZ+YJ zJQvTq7N3ONc!_inD#jNn9&Jp)Tz)qope0#1Q;C<9jhXQ>hAeG8? zS6|~)odU0{797dH-`|6MN5asGIY3pnJ zKN7ZOLdbTEbp*(2#NsU*g9o#3hq3m}(^XEz_KXynRkSe8)Z*r!Bx^tq&O#EhRY>{G jV43p%d#?nq9-aIPF|y-&XIdO-00000NkvXXu0mjf(m&a} diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/settings/github.png b/frontend/appflowy_tauri/src/appflowy_app/assets/settings/github.png deleted file mode 100644 index 597883b7a3e241064e71aa57130d4053bf551577..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1105 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 mOXU0&V(tBgay-zAH|KxYWKKc diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/settings/light.png b/frontend/appflowy_tauri/src/appflowy_app/assets/settings/light.png deleted file mode 100644 index 09b2d9c4755f0ff9e749292a058682b0ae47ec77..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13240 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 - - - - - - - - - diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/show-menu.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/show-menu.svg deleted file mode 100644 index 8baf55bffd..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/show-menu.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/sort.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/sort.svg deleted file mode 100644 index e3b6a49a56..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/sort.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/strikethrough.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/strikethrough.svg deleted file mode 100644 index c118422a15..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/strikethrough.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/text.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/text.svg deleted file mode 100644 index 7befa5080f..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/text.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/todo-list.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/todo-list.svg deleted file mode 100644 index 37f52c47ed..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/todo-list.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/underline.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/underline.svg deleted file mode 100644 index f5d53f0ec2..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/underline.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/up.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/up.svg deleted file mode 100644 index bd8f3067d3..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/up.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/avatar/ProfileAvatar.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/avatar/ProfileAvatar.tsx deleted file mode 100644 index 1248882238..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/avatar/ProfileAvatar.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import { stringToColor, stringToShortName } from '$app/utils/avatar'; -import { Avatar } from '@mui/material'; -import { useAppSelector } from '$app/stores/store'; - -export const ProfileAvatar = ({ - onClick, - className, - width, - height, -}: { - onClick?: (e: React.MouseEvent) => void; - width?: number; - height?: number; - className?: string; -}) => { - const { displayName = 'Me', iconUrl } = useAppSelector((state) => state.currentUser); - - return ( - - {iconUrl ? iconUrl : stringToShortName(displayName)} - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/avatar/WorkplaceAvatar.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/avatar/WorkplaceAvatar.tsx deleted file mode 100644 index 079342b528..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/avatar/WorkplaceAvatar.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { Avatar } from '@mui/material'; -import { stringToColor, stringToShortName } from '$app/utils/avatar'; - -export const WorkplaceAvatar = ({ - workplaceName, - icon, - onClick, - width, - height, - className, -}: { - workplaceName: string; - width: number; - height: number; - className?: string; - icon?: string; - onClick?: (e: React.MouseEvent) => void; -}) => { - return ( - - {icon ? icon : stringToShortName(workplaceName)} - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/avatar/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/avatar/index.ts deleted file mode 100644 index 772056737a..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/avatar/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './WorkplaceAvatar'; -export * from './ProfileAvatar'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/confirm_dialog/DeleteConfirmDialog.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/confirm_dialog/DeleteConfirmDialog.tsx deleted file mode 100644 index 058335d30c..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/confirm_dialog/DeleteConfirmDialog.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import React, { useCallback } from 'react'; -import DialogContent from '@mui/material/DialogContent'; -import { Button, DialogProps } from '@mui/material'; -import Dialog from '@mui/material/Dialog'; -import { useTranslation } from 'react-i18next'; -import { Log } from '$app/utils/log'; - -interface Props extends DialogProps { - open: boolean; - title: string; - subtitle?: string; - onOk?: () => Promise; - onClose: () => void; - onCancel?: () => void; - okText?: string; - cancelText?: string; - container?: HTMLElement | null; -} - -function DeleteConfirmDialog({ open, title, onOk, onCancel, onClose, okText, cancelText, container, ...props }: Props) { - const { t } = useTranslation(); - - const onDone = useCallback(async () => { - try { - await onOk?.(); - onClose(); - } catch (e) { - Log.error(e); - } - }, [onClose, onOk]); - - return ( - { - if (e.key === 'Escape') { - e.preventDefault(); - e.stopPropagation(); - onClose(); - } - - if (e.key === 'Enter') { - e.preventDefault(); - e.stopPropagation(); - void onDone(); - } - }} - onMouseDown={(e) => e.stopPropagation()} - open={open} - onClose={onClose} - {...props} - > - - {title} -
- - -
-
-
- ); -} - -export default DeleteConfirmDialog; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/confirm_dialog/RenameDialog.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/confirm_dialog/RenameDialog.tsx deleted file mode 100644 index cb8b7a80ed..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/confirm_dialog/RenameDialog.tsx +++ /dev/null @@ -1,81 +0,0 @@ -import React, { useCallback, useEffect, useState } from 'react'; -import DialogTitle from '@mui/material/DialogTitle'; -import DialogContent from '@mui/material/DialogContent'; -import Dialog from '@mui/material/Dialog'; -import { useTranslation } from 'react-i18next'; -import TextField from '@mui/material/TextField'; -import { Button, DialogActions, Divider } from '@mui/material'; - -function RenameDialog({ - defaultValue, - open, - onClose, - onOk, -}: { - defaultValue: string; - open: boolean; - onClose: () => void; - onOk: (val: string) => Promise; -}) { - const { t } = useTranslation(); - const [value, setValue] = useState(defaultValue); - const [error, setError] = useState(false); - - useEffect(() => { - setValue(defaultValue); - setError(false); - }, [defaultValue]); - - const onDone = useCallback(async () => { - try { - await onOk(value); - onClose(); - } catch (e) { - setError(true); - } - }, [onClose, onOk, value]); - - return ( - e.stopPropagation()} open={open} onClose={onClose}> - {t('menuAppHeader.renameDialog')} - - { - e.stopPropagation(); - if (e.key === 'Enter') { - e.preventDefault(); - void onDone(); - } - - if (e.key === 'Escape') { - e.preventDefault(); - onClose(); - } - }} - onChange={(e) => { - setValue(e.target.value); - }} - margin='dense' - fullWidth - variant='standard' - /> - - - - - - - - ); -} - -export default RenameDialog; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/devtool/AppFlowyDevTool.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/devtool/AppFlowyDevTool.tsx deleted file mode 100644 index 5d3ed1e3de..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/devtool/AppFlowyDevTool.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import * as React from 'react'; -import SpeedDial from '@mui/material/SpeedDial'; -import SpeedDialIcon from '@mui/material/SpeedDialIcon'; -import SpeedDialAction from '@mui/material/SpeedDialAction'; -import { useMemo } from 'react'; -import { CloseOutlined, BuildOutlined, LoginOutlined, VisibilityOff } from '@mui/icons-material'; -import ManualSignInDialog from '$app/components/_shared/devtool/ManualSignInDialog'; -import { Portal } from '@mui/material'; - -function AppFlowyDevTool() { - const [openManualSignIn, setOpenManualSignIn] = React.useState(false); - const [hidden, setHidden] = React.useState(false); - const actions = useMemo( - () => [ - { - icon: , - name: 'Manual SignIn', - onClick: () => { - setOpenManualSignIn(true); - }, - }, - { - icon: , - name: 'Hide Dev Tool', - onClick: () => { - setHidden(true); - }, - }, - ], - [] - ); - - return ( - - - - ); -} - -export default AppFlowyDevTool; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/devtool/ManualSignInDialog.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/devtool/ManualSignInDialog.tsx deleted file mode 100644 index 364b334a07..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/devtool/ManualSignInDialog.tsx +++ /dev/null @@ -1,114 +0,0 @@ -import React from 'react'; -import { CircularProgress, DialogActions, DialogProps, Tab, Tabs, TextareaAutosize } from '@mui/material'; -import Dialog from '@mui/material/Dialog'; -import DialogContent from '@mui/material/DialogContent'; -import Button from '@mui/material/Button'; -import { useAuth } from '$app/components/auth/auth.hooks'; -import TextField from '@mui/material/TextField'; - -function ManualSignInDialog(props: DialogProps) { - const [uri, setUri] = React.useState(''); - const [loading, setLoading] = React.useState(false); - const { signInWithOAuth, signInWithEmailPassword } = useAuth(); - const [tab, setTab] = React.useState(0); - const [email, setEmail] = React.useState(''); - const [password, setPassword] = React.useState(''); - const [domain, setDomain] = React.useState(''); - const handleSignIn = async () => { - setLoading(true); - try { - if (tab === 1) { - if (!email || !password) return; - await signInWithEmailPassword(email, password, domain); - } else { - await signInWithOAuth(uri); - } - } finally { - setLoading(false); - } - - props?.onClose?.({}, 'backdropClick'); - }; - - return ( - { - if (e.key === 'Enter') { - e.preventDefault(); - void handleSignIn(); - } - }} - > - - { - setTab(value); - }} - > - - - - {tab === 1 ? ( -
- setEmail(e.target.value)} - /> - setPassword(e.target.value)} - /> - setDomain(e.target.value)} - /> -
- ) : ( - { - setUri(e.target.value); - }} - /> - )} -
- - - - -
- ); -} - -export default ManualSignInDialog; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/drag_block/drag.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/drag_block/drag.hooks.ts deleted file mode 100644 index 85f0507fff..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/drag_block/drag.hooks.ts +++ /dev/null @@ -1,87 +0,0 @@ -import React from 'react'; - -interface Props { - dragId?: string; - onEnd?: (result: { dragId: string; position: 'before' | 'after' | 'inside' }) => void; -} - -function calcPosition(targetRect: DOMRect, clientY: number) { - const top = targetRect.top + targetRect.height / 3; - const bottom = targetRect.bottom - targetRect.height / 3; - - if (clientY < top) return 'before'; - if (clientY > bottom) return 'after'; - return 'inside'; -} - -export function useDrag(props: Props) { - const { dragId, onEnd } = props; - const [isDraggingOver, setIsDraggingOver] = React.useState(false); - const [isDragging, setIsDragging] = React.useState(false); - const [dropPosition, setDropPosition] = React.useState<'before' | 'after' | 'inside'>(); - const onDrop = (e: React.DragEvent) => { - e.stopPropagation(); - setIsDraggingOver(false); - setIsDragging(false); - setDropPosition(undefined); - const currentTarget = e.currentTarget; - - if (currentTarget.parentElement?.closest(`[data-drop-enabled="false"]`)) return; - if (currentTarget.closest(`[data-dragging="true"]`)) return; - const dragId = e.dataTransfer.getData('dragId'); - const targetRect = currentTarget.getBoundingClientRect(); - const { clientY } = e; - - const position = calcPosition(targetRect, clientY); - - onEnd && onEnd({ dragId, position }); - }; - - const onDragOver = (e: React.DragEvent) => { - e.stopPropagation(); - e.preventDefault(); - if (isDragging) return; - const currentTarget = e.currentTarget; - - if (currentTarget.parentElement?.closest(`[data-drop-enabled="false"]`)) return; - if (currentTarget.closest(`[data-dragging="true"]`)) return; - setIsDraggingOver(true); - const targetRect = currentTarget.getBoundingClientRect(); - const { clientY } = e; - const position = calcPosition(targetRect, clientY); - - setDropPosition(position); - }; - - const onDragLeave = (e: React.DragEvent) => { - e.stopPropagation(); - setIsDraggingOver(false); - setDropPosition(undefined); - }; - - const onDragStart = (e: React.DragEvent) => { - if (!dragId) return; - e.stopPropagation(); - e.dataTransfer.setData('dragId', dragId); - e.dataTransfer.effectAllowed = 'move'; - setIsDragging(true); - }; - - const onDragEnd = (e: React.DragEvent) => { - e.stopPropagation(); - setIsDragging(false); - setIsDraggingOver(false); - setDropPosition(undefined); - }; - - return { - onDrop, - onDragOver, - onDragLeave, - onDragStart, - isDraggingOver, - isDragging, - onDragEnd, - dropPosition, - }; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/drag_block/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/drag_block/index.ts deleted file mode 100644 index e0cb540f75..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/drag_block/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './drag.hooks'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/emoji_picker/EmojiPicker.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/emoji_picker/EmojiPicker.hooks.ts deleted file mode 100644 index af82c82df5..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/emoji_picker/EmojiPicker.hooks.ts +++ /dev/null @@ -1,165 +0,0 @@ -import React, { useCallback, useEffect, useState } from 'react'; -import emojiData, { EmojiMartData } from '@emoji-mart/data'; -import { init, FrequentlyUsed, getEmojiDataFromNative, Store } from 'emoji-mart'; - -import { PopoverProps } from '@mui/material/Popover'; -import { PopoverOrigin } from '@mui/material/Popover/Popover'; -import chunk from 'lodash-es/chunk'; - -export const EMOJI_SIZE = 32; - -export const PER_ROW_EMOJI_COUNT = 13; - -export const MAX_FREQUENTLY_ROW_COUNT = 2; - -export interface EmojiCategory { - id: string; - emojis: Emoji[]; -} - -interface Emoji { - id: string; - name: string; - native: string; -} - -export function useLoadEmojiData({ onEmojiSelect }: { onEmojiSelect: (emoji: string) => void }) { - const [searchValue, setSearchValue] = useState(''); - const [emojiCategories, setEmojiCategories] = useState([]); - const [skin, setSkin] = useState(() => { - return Number(Store.get('skin')) || 0; - }); - - const onSkinChange = useCallback((val: number) => { - setSkin(val); - Store.set('skin', String(val)); - }, []); - - const loadEmojiData = useCallback( - async (searchVal?: string) => { - const { emojis, categories } = emojiData as EmojiMartData; - - const filteredCategories = categories - .map((category) => { - const { id, emojis: categoryEmojis } = category; - - return { - id, - emojis: categoryEmojis - .filter((emojiId) => { - const emoji = emojis[emojiId]; - - if (!searchVal) return true; - return filterSearchValue(emoji, searchVal); - }) - .map((emojiId) => { - const emoji = emojis[emojiId]; - const { name, skins } = emoji; - - return { - id: emojiId, - name, - native: skins[skin] ? skins[skin].native : skins[0].native, - }; - }), - }; - }) - .filter((category) => category.emojis.length > 0); - - setEmojiCategories(filteredCategories); - }, - [skin] - ); - - useEffect(() => { - void (async () => { - await init({ data: emojiData, maxFrequentRows: MAX_FREQUENTLY_ROW_COUNT, perLine: PER_ROW_EMOJI_COUNT }); - await loadEmojiData(); - })(); - }, [loadEmojiData]); - - useEffect(() => { - void loadEmojiData(searchValue); - }, [loadEmojiData, searchValue]); - - const onSelect = useCallback( - async (native: string) => { - onEmojiSelect(native); - if (!native) { - return; - } - - const data = await getEmojiDataFromNative(native); - - FrequentlyUsed.add(data); - }, - [onEmojiSelect] - ); - - return { - emojiCategories, - setSearchValue, - searchValue, - onSelect, - onSkinChange, - skin, - }; -} - -export function useSelectSkinPopoverProps(): PopoverProps & { - onOpen: (event: React.MouseEvent) => void; - onClose: () => void; -} { - const [anchorEl, setAnchorEl] = useState(undefined); - const onOpen = useCallback((event: React.MouseEvent) => { - setAnchorEl(event.currentTarget); - }, []); - const onClose = useCallback(() => { - setAnchorEl(undefined); - }, []); - const open = Boolean(anchorEl); - const anchorOrigin = { vertical: 'bottom', horizontal: 'center' } as PopoverOrigin; - const transformOrigin = { vertical: 'top', horizontal: 'center' } as PopoverOrigin; - - return { - anchorEl, - onOpen, - onClose, - open, - anchorOrigin, - transformOrigin, - }; -} - -function filterSearchValue(emoji: emojiData.Emoji, searchValue: string) { - const { name, keywords } = emoji; - const searchValueLowerCase = searchValue.toLowerCase(); - - return ( - name.toLowerCase().includes(searchValueLowerCase) || - (keywords && keywords.some((keyword) => keyword.toLowerCase().includes(searchValueLowerCase))) - ); -} - -export function getRowsWithCategories(emojiCategories: EmojiCategory[], rowSize: number) { - const rows: { - id: string; - type: 'category' | 'emojis'; - emojis?: Emoji[]; - }[] = []; - - emojiCategories.forEach((category) => { - rows.push({ - id: category.id, - type: 'category', - }); - chunk(category.emojis, rowSize).forEach((chunk, index) => { - rows.push({ - type: 'emojis', - emojis: chunk, - id: `${category.id}-${index}`, - }); - }); - }); - return rows; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/emoji_picker/EmojiPicker.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/emoji_picker/EmojiPicker.tsx deleted file mode 100644 index b8dcb3f6c7..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/emoji_picker/EmojiPicker.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import React from 'react'; - -import { useLoadEmojiData } from './EmojiPicker.hooks'; -import EmojiPickerHeader from './EmojiPickerHeader'; -import EmojiPickerCategories from './EmojiPickerCategories'; - -interface Props { - onEmojiSelect: (emoji: string) => void; - onEscape?: () => void; - defaultEmoji?: string; -} - -function EmojiPicker({ defaultEmoji, onEscape, ...props }: Props) { - const { skin, onSkinChange, emojiCategories, setSearchValue, searchValue, onSelect } = useLoadEmojiData(props); - - return ( -
- - -
- ); -} - -export default EmojiPicker; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/emoji_picker/EmojiPickerCategories.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/emoji_picker/EmojiPickerCategories.tsx deleted file mode 100644 index eefea8db11..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/emoji_picker/EmojiPickerCategories.tsx +++ /dev/null @@ -1,354 +0,0 @@ -import React, { useCallback, useEffect, useMemo, useRef } from 'react'; -import { - EMOJI_SIZE, - EmojiCategory, - getRowsWithCategories, - PER_ROW_EMOJI_COUNT, -} from '$app/components/_shared/emoji_picker/EmojiPicker.hooks'; -import { FixedSizeList } from 'react-window'; -import { useTranslation } from 'react-i18next'; -import AutoSizer from 'react-virtualized-auto-sizer'; -import { getDistanceEdge, inView } from '$app/components/_shared/keyboard_navigation/utils'; - -function EmojiPickerCategories({ - emojiCategories, - onEmojiSelect, - onEscape, - defaultEmoji, -}: { - emojiCategories: EmojiCategory[]; - onEmojiSelect: (emoji: string) => void; - onEscape?: () => void; - defaultEmoji?: string; -}) { - const scrollRef = React.useRef(null); - const { t } = useTranslation(); - const [selectCell, setSelectCell] = React.useState({ - row: 1, - column: 0, - }); - const rows = useMemo(() => { - return getRowsWithCategories(emojiCategories, PER_ROW_EMOJI_COUNT); - }, [emojiCategories]); - const mouseY = useRef(null); - const mouseX = useRef(null); - - const ref = React.useRef(null); - - const getCategoryName = useCallback( - (id: string) => { - const i18nName: Record = { - frequent: t('emoji.categories.frequentlyUsed'), - people: t('emoji.categories.people'), - nature: t('emoji.categories.nature'), - foods: t('emoji.categories.food'), - activity: t('emoji.categories.activities'), - places: t('emoji.categories.places'), - objects: t('emoji.categories.objects'), - symbols: t('emoji.categories.symbols'), - flags: t('emoji.categories.flags'), - }; - - return i18nName[id]; - }, - [t] - ); - - useEffect(() => { - scrollRef.current?.scrollTo({ - top: 0, - }); - - setSelectCell({ - row: 1, - column: 0, - }); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [rows]); - - const renderRow = useCallback( - ({ index, style }: { index: number; style: React.CSSProperties }) => { - const item = rows[index]; - - return ( -
- {item.type === 'category' ? ( -
{getCategoryName(item.id)}
- ) : null} -
- {item.emojis?.map((emoji, columnIndex) => { - const isSelected = selectCell.row === index && selectCell.column === columnIndex; - - const isDefaultEmoji = defaultEmoji === emoji.native; - - return ( -
{ - onEmojiSelect(emoji.native); - }} - onMouseMove={(e) => { - mouseY.current = e.clientY; - mouseX.current = e.clientX; - }} - onMouseEnter={(e) => { - if (mouseY.current === null || mouseY.current !== e.clientY || mouseX.current !== e.clientX) { - setSelectCell({ - row: index, - column: columnIndex, - }); - } - - mouseX.current = e.clientX; - mouseY.current = e.clientY; - }} - className={`flex cursor-pointer items-center justify-center rounded hover:bg-fill-list-hover ${ - isSelected ? 'bg-fill-list-hover' : 'hover:bg-transparent' - } ${isDefaultEmoji ? 'bg-fill-list-active' : ''}`} - > - {emoji.native} -
- ); - })} -
-
- ); - }, - [defaultEmoji, getCategoryName, onEmojiSelect, rows, selectCell.column, selectCell.row] - ); - - const getNewColumnIndex = useCallback( - (rowIndex: number, columnIndex: number): number => { - const row = rows[rowIndex]; - const length = row.emojis?.length; - let newColumnIndex = columnIndex; - - if (length && length <= columnIndex) { - newColumnIndex = length - 1 || 0; - } - - return newColumnIndex; - }, - [rows] - ); - - const findNextRow = useCallback( - (rowIndex: number, columnIndex: number): { row: number; column: number } => { - const rowLength = rows.length; - let nextRowIndex = rowIndex + 1; - - if (nextRowIndex >= rowLength - 1) { - nextRowIndex = rowLength - 1; - } else if (rows[nextRowIndex].type === 'category') { - nextRowIndex = findNextRow(nextRowIndex, columnIndex).row; - } - - const newColumnIndex = getNewColumnIndex(nextRowIndex, columnIndex); - - return { - row: nextRowIndex, - column: newColumnIndex, - }; - }, - [getNewColumnIndex, rows] - ); - - const findPrevRow = useCallback( - (rowIndex: number, columnIndex: number): { row: number; column: number } => { - let prevRowIndex = rowIndex - 1; - - if (prevRowIndex < 1) { - prevRowIndex = 1; - } else if (rows[prevRowIndex].type === 'category') { - prevRowIndex = findPrevRow(prevRowIndex, columnIndex).row; - } - - const newColumnIndex = getNewColumnIndex(prevRowIndex, columnIndex); - - return { - row: prevRowIndex, - column: newColumnIndex, - }; - }, - [getNewColumnIndex, rows] - ); - - const findPrevCell = useCallback( - (row: number, column: number): { row: number; column: number } => { - const prevColumn = column - 1; - - if (prevColumn < 0) { - const prevRow = findPrevRow(row, column).row; - - if (prevRow === row) return { row, column }; - const length = rows[prevRow].emojis?.length || 0; - - return { - row: prevRow, - column: length > 0 ? length - 1 : 0, - }; - } - - return { - row, - column: prevColumn, - }; - }, - [findPrevRow, rows] - ); - - const findNextCell = useCallback( - (row: number, column: number): { row: number; column: number } => { - const nextColumn = column + 1; - - const rowLength = rows[row].emojis?.length || 0; - - if (nextColumn >= rowLength) { - const nextRow = findNextRow(row, column).row; - - if (nextRow === row) return { row, column }; - return { - row: nextRow, - column: 0, - }; - } - - return { - row, - column: nextColumn, - }; - }, - [findNextRow, rows] - ); - - useEffect(() => { - if (!selectCell || !scrollRef.current) return; - const emojiKey = rows[selectCell.row]?.emojis?.[selectCell.column]?.id; - const emojiDom = document.querySelector(`[data-key="${emojiKey}"]`); - - if (emojiDom && !inView(emojiDom as HTMLElement, scrollRef.current as HTMLElement)) { - const distance = getDistanceEdge(emojiDom as HTMLElement, scrollRef.current as HTMLElement); - - scrollRef.current?.scrollTo({ - top: scrollRef.current?.scrollTop + distance, - }); - } - }, [selectCell, rows]); - - const handleKeyDown = useCallback( - (e: KeyboardEvent) => { - e.stopPropagation(); - - switch (e.key) { - case 'Escape': - e.preventDefault(); - onEscape?.(); - break; - case 'ArrowUp': { - e.preventDefault(); - - setSelectCell(findPrevRow(selectCell.row, selectCell.column)); - - break; - } - - case 'ArrowDown': { - e.preventDefault(); - - setSelectCell(findNextRow(selectCell.row, selectCell.column)); - - break; - } - - case 'ArrowLeft': { - e.preventDefault(); - - const prevCell = findPrevCell(selectCell.row, selectCell.column); - - setSelectCell(prevCell); - break; - } - - case 'ArrowRight': { - e.preventDefault(); - - const nextCell = findNextCell(selectCell.row, selectCell.column); - - setSelectCell(nextCell); - break; - } - - case 'Enter': { - e.preventDefault(); - const currentRow = rows[selectCell.row]; - const emoji = currentRow.emojis?.[selectCell.column]; - - if (emoji) { - onEmojiSelect(emoji.native); - } - - break; - } - - default: - break; - } - }, - [ - findNextCell, - findPrevCell, - findPrevRow, - findNextRow, - onEmojiSelect, - onEscape, - rows, - selectCell.column, - selectCell.row, - ] - ); - - useEffect(() => { - const focusElement = document.querySelector('.emoji-picker .search-emoji-input') as HTMLInputElement; - - const parentElement = ref.current?.parentElement; - - focusElement?.addEventListener('keydown', handleKeyDown); - parentElement?.addEventListener('keydown', handleKeyDown); - return () => { - focusElement?.removeEventListener('keydown', handleKeyDown); - parentElement?.removeEventListener('keydown', handleKeyDown); - }; - }, [handleKeyDown]); - - return ( -
- - {({ height, width }: { height: number; width: number }) => ( - - {renderRow} - - )} - -
- ); -} - -export default EmojiPickerCategories; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/emoji_picker/EmojiPickerHeader.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/emoji_picker/EmojiPickerHeader.tsx deleted file mode 100644 index 177ac2e7a0..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/emoji_picker/EmojiPickerHeader.tsx +++ /dev/null @@ -1,134 +0,0 @@ -import React from 'react'; -import { Box, IconButton } from '@mui/material'; -import { Circle, DeleteOutlineRounded, SearchOutlined } from '@mui/icons-material'; -import TextField from '@mui/material/TextField'; -import Tooltip from '@mui/material/Tooltip'; -import { randomEmoji } from '$app/utils/emoji'; -import ShuffleIcon from '@mui/icons-material/Shuffle'; -import Popover from '@mui/material/Popover'; -import { useSelectSkinPopoverProps } from '$app/components/_shared/emoji_picker/EmojiPicker.hooks'; -import { useTranslation } from 'react-i18next'; - -const skinTones = [ - { - value: 0, - color: '#ffc93a', - }, - { - color: '#ffdab7', - value: 1, - }, - { - color: '#e7b98f', - value: 2, - }, - { - color: '#c88c61', - value: 3, - }, - { - color: '#a46134', - value: 4, - }, - { - color: '#5d4437', - value: 5, - }, -]; - -interface Props { - onEmojiSelect: (emoji: string) => void; - skin: number; - onSkinSelect: (skin: number) => void; - searchValue: string; - onSearchChange: (value: string) => void; -} - -function EmojiPickerHeader({ onEmojiSelect, onSkinSelect, searchValue, onSearchChange, skin }: Props) { - const { onOpen, ...popoverProps } = useSelectSkinPopoverProps(); - const { t } = useTranslation(); - - return ( -
-
- - - { - onSearchChange(e.target.value); - }} - autoFocus={true} - autoCorrect={'off'} - autoComplete={'off'} - spellCheck={false} - className={'search-emoji-input'} - placeholder={t('search.label')} - variant='standard' - /> - - -
- { - const emoji = randomEmoji(); - - onEmojiSelect(emoji); - }} - > - - -
-
- -
- - - -
-
- -
- { - onEmojiSelect(''); - }} - > - - -
-
-
- -
- {skinTones.map((skinTone) => ( -
- { - onSkinSelect(skinTone.value); - popoverProps.onClose?.(); - }} - > - - -
- ))} -
-
-
- ); -} - -export default EmojiPickerHeader; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/error_boundary/withError.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/error_boundary/withError.tsx deleted file mode 100644 index 9b9ba159fb..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/error_boundary/withError.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import { ErrorBoundary } from 'react-error-boundary'; -import { Log } from '$app/utils/log'; -import { Alert } from '@mui/material'; - -export default function withErrorBoundary(WrappedComponent: React.ComponentType) { - return (props: T) => ( - { - Log.error(e); - }} - fallback={Something went wrong} - > - - - ); -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/image_upload/EmbedLink.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/image_upload/EmbedLink.tsx deleted file mode 100644 index 34a99007ad..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/image_upload/EmbedLink.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import React, { useCallback, useState } from 'react'; -import TextField from '@mui/material/TextField'; -import { useTranslation } from 'react-i18next'; -import Button from '@mui/material/Button'; - -const urlPattern = /^(https?:\/\/)([^\s(["<,>/]*)(\/)[^\s[",><]*(.png|.jpg|.gif|.webm|.webp|.svg)(\?[^\s[",><]*)?$/; - -export function EmbedLink({ - onDone, - onEscape, - defaultLink, -}: { - defaultLink?: string; - onDone?: (value: string) => void; - onEscape?: () => void; -}) { - const { t } = useTranslation(); - - const [value, setValue] = useState(defaultLink ?? ''); - const [error, setError] = useState(false); - - const handleChange = useCallback( - (e: React.ChangeEvent) => { - const value = e.target.value; - - setValue(value); - setError(!urlPattern.test(value)); - }, - [setValue, setError] - ); - - const handleKeyDown = useCallback( - (e: React.KeyboardEvent) => { - if (e.key === 'Enter' && !error && value) { - e.preventDefault(); - e.stopPropagation(); - onDone?.(value); - } - - if (e.key === 'Escape') { - e.preventDefault(); - e.stopPropagation(); - onEscape?.(); - } - }, - [error, onDone, onEscape, value] - ); - - return ( -
- - -
- ); -} - -export default EmbedLink; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/image_upload/LocalImage.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/image_upload/LocalImage.tsx deleted file mode 100644 index d94e5f2889..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/image_upload/LocalImage.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import React, { forwardRef, useCallback, useEffect, useRef, useState } from 'react'; -import { CircularProgress } from '@mui/material'; -import { useTranslation } from 'react-i18next'; -import { ErrorOutline } from '@mui/icons-material'; - -export const LocalImage = forwardRef< - HTMLImageElement, - { - renderErrorNode?: () => React.ReactElement | null; - } & React.ImgHTMLAttributes ->((localImageProps, ref) => { - const { src, renderErrorNode, ...props } = localImageProps; - const imageRef = useRef(null); - const { t } = useTranslation(); - const [imageURL, setImageURL] = useState(''); - const [loading, setLoading] = useState(true); - const [isError, setIsError] = useState(false); - const loadLocalImage = useCallback(async () => { - if (!src) return; - setLoading(true); - setIsError(false); - const { readBinaryFile, BaseDirectory } = await import('@tauri-apps/api/fs'); - - try { - const svg = src.endsWith('.svg'); - - const buffer = await readBinaryFile(src, { dir: BaseDirectory.AppLocalData }); - const blob = new Blob([buffer], { type: svg ? 'image/svg+xml' : 'image' }); - - setImageURL(URL.createObjectURL(blob)); - } catch (e) { - setIsError(true); - } - - setLoading(false); - }, [src]); - - useEffect(() => { - void loadLocalImage(); - }, [loadLocalImage]); - - if (loading) { - return ( -
- - {t('editor.loading')}... -
- ); - } - - if (isError) { - if (renderErrorNode) return renderErrorNode(); - return ( -
- -
{t('editor.imageLoadFailed')}
-
- ); - } - - return {'local; -}); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/image_upload/Unsplash.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/image_upload/Unsplash.tsx deleted file mode 100644 index 01da8323b9..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/image_upload/Unsplash.tsx +++ /dev/null @@ -1,154 +0,0 @@ -import React, { useCallback, useEffect, useMemo, useState } from 'react'; -import { createApi } from 'unsplash-js'; -import TextField from '@mui/material/TextField'; -import { useTranslation } from 'react-i18next'; -import Typography from '@mui/material/Typography'; -import debounce from 'lodash-es/debounce'; -import { CircularProgress } from '@mui/material'; -import { open } from '@tauri-apps/api/shell'; - -const unsplash = createApi({ - accessKey: '1WxD1JpMOUX86lZKKob4Ca0LMZPyO2rUmAgjpWm9Ids', -}); - -const SEARCH_DEBOUNCE_TIME = 500; - -export function Unsplash({ onDone, onEscape }: { onDone?: (value: string) => void; onEscape?: () => void }) { - const { t } = useTranslation(); - - const [loading, setLoading] = useState(true); - const [error, setError] = useState(''); - const [photos, setPhotos] = useState< - { - thumb: string; - regular: string; - alt: string | null; - id: string; - user: { - name: string; - link: string; - }; - }[] - >([]); - const [searchValue, setSearchValue] = useState(''); - - const handleChange = useCallback((e: React.ChangeEvent) => { - const value = e.target.value; - - setSearchValue(value); - }, []); - - const handleKeyDown = useCallback( - (e: React.KeyboardEvent) => { - if (e.key === 'Escape') { - e.preventDefault(); - e.stopPropagation(); - onEscape?.(); - } - }, - [onEscape] - ); - - const debounceSearchPhotos = useMemo(() => { - return debounce(async (searchValue: string) => { - const request = searchValue - ? unsplash.search.getPhotos({ query: searchValue ?? undefined, perPage: 32 }) - : unsplash.photos.list({ perPage: 32 }); - - setError(''); - setLoading(true); - await request.then((result) => { - if (result.errors) { - setError(result.errors[0]); - } else { - setPhotos( - result.response.results.map((photo) => ({ - id: photo.id, - thumb: photo.urls.thumb, - regular: photo.urls.regular, - alt: photo.alt_description, - user: { - name: photo.user.name, - link: photo.user.links.html, - }, - })) - ); - } - - setLoading(false); - }); - }, SEARCH_DEBOUNCE_TIME); - }, []); - - useEffect(() => { - void debounceSearchPhotos(searchValue); - return () => { - debounceSearchPhotos.cancel(); - }; - }, [debounceSearchPhotos, searchValue]); - - return ( -
- - - {loading ? ( -
- -
{t('editor.loading')}
-
- ) : error ? ( - - {error} - - ) : ( -
- {photos.length > 0 ? ( - <> -
- {photos.map((photo) => ( -
- { - onDone?.(photo.regular); - }} - src={photo.thumb} - alt={photo.alt ?? ''} - className={'h-20 w-32 rounded object-cover hover:opacity-80'} - /> -
- by{' '} - { - void open(photo.user.link); - }} - className={'underline hover:text-function-info'} - > - {photo.user.name} - -
-
- ))} -
- - {t('findAndReplace.searchMore')} - - - ) : ( - - {t('findAndReplace.noResult')} - - )} -
- )} -
- ); -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/image_upload/UploadImage.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/image_upload/UploadImage.tsx deleted file mode 100644 index d39da68caf..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/image_upload/UploadImage.tsx +++ /dev/null @@ -1,93 +0,0 @@ -import React, { useCallback } from 'react'; -import Button from '@mui/material/Button'; -import { useTranslation } from 'react-i18next'; -import CloudUploadIcon from '@mui/icons-material/CloudUploadOutlined'; -import { notify } from '$app/components/_shared/notify'; -import { isTauri } from '$app/utils/env'; -import { getFileName, IMAGE_DIR, ALLOWED_IMAGE_EXTENSIONS, MAX_IMAGE_SIZE } from '$app/utils/upload_image'; - -export function UploadImage({ onDone }: { onDone?: (url: string) => void }) { - const { t } = useTranslation(); - - const checkTauriFile = useCallback( - async (url: string) => { - // const { readBinaryFile } = await import('@tauri-apps/api/fs'); - // const buffer = await readBinaryFile(url); - // const blob = new Blob([buffer]); - // if (blob.size > MAX_IMAGE_SIZE) { - // notify.error(t('document.imageBlock.error.invalidImageSize')); - // return false; - // } - - return true; - }, - [t] - ); - - const uploadTauriLocalImage = useCallback( - async (url: string) => { - const { copyFile, BaseDirectory, exists, createDir } = await import('@tauri-apps/api/fs'); - - const checked = await checkTauriFile(url); - - if (!checked) return; - - try { - const existDir = await exists(IMAGE_DIR, { dir: BaseDirectory.AppLocalData }); - - if (!existDir) { - await createDir(IMAGE_DIR, { dir: BaseDirectory.AppLocalData }); - } - - const filename = getFileName(url); - - await copyFile(url, `${IMAGE_DIR}/${filename}`, { dir: BaseDirectory.AppLocalData }); - const newUrl = `${IMAGE_DIR}/${filename}`; - - onDone?.(newUrl); - } catch (e) { - notify.error(t('document.plugins.image.imageUploadFailed')); - } - }, - [checkTauriFile, onDone, t] - ); - - const handleClickUpload = useCallback(async () => { - if (!isTauri()) return; - const { open } = await import('@tauri-apps/api/dialog'); - - const url = await open({ - multiple: false, - directory: false, - filters: [ - { - name: 'Image', - extensions: ALLOWED_IMAGE_EXTENSIONS, - }, - ], - }); - - if (!url || typeof url !== 'string') return; - - await uploadTauriLocalImage(url); - }, [uploadTauriLocalImage]); - - return ( -
- -
- ); -} - -export default UploadImage; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/image_upload/UploadTabs.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/image_upload/UploadTabs.tsx deleted file mode 100644 index fb65c709ce..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/image_upload/UploadTabs.tsx +++ /dev/null @@ -1,128 +0,0 @@ -import React, { SyntheticEvent, useCallback, useState } from 'react'; -import Popover, { PopoverProps } from '@mui/material/Popover'; -import { TabPanel, ViewTab, ViewTabs } from '$app/components/database/components/tab_bar/ViewTabs'; -import SwipeableViews from 'react-swipeable-views'; -import { PopoverCommonProps } from '$app/components/editor/components/tools/popover'; - -export enum TAB_KEY { - Colors = 'colors', - UPLOAD = 'upload', - EMBED_LINK = 'embed_link', - UNSPLASH = 'unsplash', -} - -export type TabOption = { - key: TAB_KEY; - label: string; - Component: React.ComponentType<{ - onDone?: (value: string) => void; - onEscape?: () => void; - }>; - onDone?: (value: string) => void; -}; - -export function UploadTabs({ - tabOptions, - popoverProps, - containerStyle, - extra, -}: { - containerStyle?: React.CSSProperties; - tabOptions: TabOption[]; - popoverProps?: PopoverProps; - extra?: React.ReactNode; -}) { - const [tabValue, setTabValue] = useState(() => { - return tabOptions[0].key; - }); - - const handleTabChange = useCallback((_: SyntheticEvent, newValue: string) => { - setTabValue(newValue as TAB_KEY); - }, []); - - const selectedIndex = tabOptions.findIndex((tab) => tab.key === tabValue); - - const onKeyDown = useCallback( - (e: React.KeyboardEvent) => { - e.stopPropagation(); - - if (e.key === 'Escape') { - e.preventDefault(); - e.stopPropagation(); - popoverProps?.onClose?.({}, 'escapeKeyDown'); - } - - if (e.key === 'Tab') { - e.preventDefault(); - e.stopPropagation(); - setTabValue((prev) => { - const currentIndex = tabOptions.findIndex((tab) => tab.key === prev); - let nextIndex = currentIndex + 1; - - if (e.shiftKey) { - nextIndex = currentIndex - 1; - } - - return tabOptions[nextIndex % tabOptions.length]?.key ?? tabOptions[0].key; - }); - } - }, - [popoverProps, tabOptions] - ); - - return ( - -
-
- - {tabOptions.map((tab) => { - const { key, label } = tab; - - return ; - })} - - {extra} -
- -
- - {tabOptions.map((tab, index) => { - const { key, Component, onDone } = tab; - - return ( - - popoverProps?.onClose?.({}, 'escapeKeyDown')} /> - - ); - })} - -
-
-
- ); -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/image_upload/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/image_upload/index.ts deleted file mode 100644 index 28673cae5f..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/image_upload/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * from './Unsplash'; -export * from './UploadImage'; -export * from './EmbedLink'; -export * from './UploadTabs'; -export * from './LocalImage'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/katex_math/KatexMath.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/katex_math/KatexMath.tsx deleted file mode 100644 index e6c7cac5ed..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/katex_math/KatexMath.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import React from 'react'; -import 'katex/dist/katex.min.css'; -import { BlockMath, InlineMath } from 'react-katex'; -import './index.css'; - -function KatexMath({ latex, isInline = false }: { latex: string; isInline?: boolean }) { - return isInline ? ( - - ) : ( - { - return ( -
- {error.name}: {error.message} -
- ); - }} - > - {latex} -
- ); -} - -export default KatexMath; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/katex_math/index.css b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/katex_math/index.css deleted file mode 100644 index d127dc343b..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/katex_math/index.css +++ /dev/null @@ -1,4 +0,0 @@ - -.katex-html { - white-space: normal; -} \ No newline at end of file diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/keyboard_navigation/KeyboardNavigation.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/keyboard_navigation/KeyboardNavigation.tsx deleted file mode 100644 index 7db90c4e8f..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/keyboard_navigation/KeyboardNavigation.tsx +++ /dev/null @@ -1,317 +0,0 @@ -import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; -import { MenuItem, Typography } from '@mui/material'; -import { scrollIntoView } from '$app/components/_shared/keyboard_navigation/utils'; -import { useTranslation } from 'react-i18next'; - -/** - * The option of the keyboard navigation - * the options will be flattened - * - key: the key of the option - * - content: the content of the option - * - children: the children of the option - */ -export interface KeyboardNavigationOption { - key: T; - content?: React.ReactNode; - children?: KeyboardNavigationOption[]; - disabled?: boolean; -} - -/** - * - scrollRef: the scrollable element - * - focusRef: the element to focus when the keyboard navigation is disabled - * - options: the options to navigate - * - onSelected: called when an option is selected(hovered) - * - onConfirm: called when an option is confirmed - * - onEscape: called when the escape key is pressed - * - onPressRight: called when the right arrow is pressed - * - onPressLeft: called when the left arrow key is pressed - * - disableFocus: disable the focus on the keyboard navigation - * - disableSelect: disable selecting an option when the options are initialized - * - onKeyDown: called when a key is pressed - * - defaultFocusedKey: the default focused key - * - onFocus: called when the keyboard navigation is focused - * - onBlur: called when the keyboard navigation is blurred - */ -export interface KeyboardNavigationProps { - scrollRef?: React.RefObject; - focusRef?: React.RefObject; - options: KeyboardNavigationOption[]; - onSelected?: (optionKey: T) => void; - onConfirm?: (optionKey: T) => void; - onEscape?: () => void; - onPressRight?: (optionKey: T) => void; - onPressLeft?: (optionKey: T) => void; - disableFocus?: boolean; - disableSelect?: boolean; - onKeyDown?: (e: KeyboardEvent) => void; - defaultFocusedKey?: T; - onFocus?: () => void; - onBlur?: () => void; - itemClassName?: string; - itemStyle?: React.CSSProperties; - renderNoResult?: () => React.ReactNode; -} - -function KeyboardNavigation({ - defaultFocusedKey, - onPressRight, - onPressLeft, - onEscape, - onConfirm, - scrollRef, - options, - onSelected, - focusRef, - disableFocus = false, - onKeyDown: onPropsKeyDown, - disableSelect = false, - onBlur, - onFocus, - itemClassName, - itemStyle, - renderNoResult, -}: KeyboardNavigationProps) { - const { t } = useTranslation(); - const ref = useRef(null); - const mouseY = useRef(null); - const defaultKeyRef = useRef(defaultFocusedKey); - // flatten the options - const flattenOptions = useMemo(() => { - return options.flatMap((group) => { - if (group.children) { - return group.children; - } - - return [group]; - }); - }, [options]); - - const [focusedKey, setFocusedKey] = useState(); - - const firstOptionKey = useMemo(() => { - if (disableSelect) return; - const firstOption = flattenOptions.find((option) => !option.disabled); - - return firstOption?.key; - }, [flattenOptions, disableSelect]); - - // set the default focused key when the options are initialized - useEffect(() => { - if (defaultKeyRef.current) { - setFocusedKey(defaultKeyRef.current); - defaultKeyRef.current = undefined; - return; - } - - setFocusedKey(firstOptionKey); - }, [firstOptionKey]); - - // call the onSelected callback when the focused key is changed - useEffect(() => { - if (focusedKey === undefined) return; - onSelected?.(focusedKey); - - const scrollElement = scrollRef?.current; - - if (!scrollElement) return; - - const dom = ref.current?.querySelector(`[data-key="${focusedKey}"]`); - - if (!dom) return; - // scroll the focused option into view - requestAnimationFrame(() => { - scrollIntoView(dom as HTMLDivElement, scrollElement); - }); - }, [focusedKey, onSelected, scrollRef]); - - const onKeyDown = useCallback( - (e: KeyboardEvent) => { - onPropsKeyDown?.(e); - e.stopPropagation(); - const key = e.key; - - if (key === 'Tab') { - e.preventDefault(); - return; - } - - if (key === 'Escape') { - e.preventDefault(); - onEscape?.(); - return; - } - - if (focusedKey === undefined) return; - const focusedIndex = flattenOptions.findIndex((option) => option?.key === focusedKey); - const nextIndex = (focusedIndex + 1) % flattenOptions.length; - const prevIndex = (focusedIndex - 1 + flattenOptions.length) % flattenOptions.length; - - switch (key) { - // move the focus to the previous option - case 'ArrowUp': { - e.preventDefault(); - - const prevKey = flattenOptions[prevIndex]?.key; - - setFocusedKey(prevKey); - - break; - } - - // move the focus to the next option - case 'ArrowDown': { - e.preventDefault(); - const nextKey = flattenOptions[nextIndex]?.key; - - setFocusedKey(nextKey); - break; - } - - case 'ArrowRight': - if (onPressRight) { - e.preventDefault(); - onPressRight(focusedKey); - } - - break; - case 'ArrowLeft': - if (onPressLeft) { - e.preventDefault(); - onPressLeft(focusedKey); - } - - break; - // confirm the focused option - case 'Enter': { - e.preventDefault(); - const disabled = flattenOptions[focusedIndex]?.disabled; - - if (!disabled) { - onConfirm?.(focusedKey); - } - - break; - } - - default: - break; - } - }, - [flattenOptions, focusedKey, onConfirm, onEscape, onPressLeft, onPressRight, onPropsKeyDown] - ); - - const renderOption = useCallback( - (option: KeyboardNavigationOption, index: number) => { - const hasChildren = option.children; - - const isFocused = focusedKey === option.key; - - return ( -
- {hasChildren ? ( - // render the group name - option.content &&
{option.content}
- ) : ( - // render the option - { - mouseY.current = e.clientY; - }} - onMouseEnter={(e) => { - onFocus?.(); - if (mouseY.current === null || mouseY.current !== e.clientY) { - setFocusedKey(option.key); - } - - mouseY.current = e.clientY; - }} - onClick={() => { - setFocusedKey(option.key); - if (!option.disabled) { - onConfirm?.(option.key); - } - }} - selected={isFocused} - style={itemStyle} - className={`ml-0 flex w-full items-center justify-start rounded-none px-2 py-1 text-xs ${ - !isFocused ? 'hover:bg-transparent' : '' - } ${itemClassName ?? ''}`} - > - {option.content} - - )} - - {option.children?.map((child, childIndex) => { - return renderOption(child, index + childIndex); - })} -
- ); - }, - [itemClassName, focusedKey, onConfirm, onFocus, itemStyle] - ); - - useEffect(() => { - const element = ref.current; - - if (!disableFocus && element) { - element.focus(); - element.addEventListener('keydown', onKeyDown); - - return () => { - element.removeEventListener('keydown', onKeyDown); - }; - } else { - let element: HTMLElement | null | undefined = focusRef?.current; - - if (!element) { - element = document.activeElement as HTMLElement; - } - - element?.addEventListener('keydown', onKeyDown); - return () => { - element?.removeEventListener('keydown', onKeyDown); - }; - } - }, [disableFocus, onKeyDown, focusRef]); - - return ( -
{ - e.stopPropagation(); - - onFocus?.(); - }} - onBlur={(e) => { - e.stopPropagation(); - - const target = e.relatedTarget as HTMLElement; - - if (target?.closest('.keyboard-navigation')) { - return; - } - - onBlur?.(); - }} - autoFocus={!disableFocus} - className={'keyboard-navigation flex w-full flex-col gap-1 outline-none'} - ref={ref} - > - {options.length > 0 ? ( - options.map(renderOption) - ) : renderNoResult ? ( - renderNoResult() - ) : ( - - {t('findAndReplace.noResult')} - - )} -
- ); -} - -export default KeyboardNavigation; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/keyboard_navigation/utils.ts b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/keyboard_navigation/utils.ts deleted file mode 100644 index 621143869b..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/keyboard_navigation/utils.ts +++ /dev/null @@ -1,32 +0,0 @@ -export function inView(dom: HTMLElement, container: HTMLElement) { - const domRect = dom.getBoundingClientRect(); - const containerRect = container.getBoundingClientRect(); - - if (!domRect || !containerRect) return true; - - return domRect?.bottom <= containerRect?.bottom && domRect?.top >= containerRect?.top; -} - -export function getDistanceEdge(dom: HTMLElement, container: HTMLElement) { - const domRect = dom.getBoundingClientRect(); - const containerRect = container.getBoundingClientRect(); - - if (!domRect || !containerRect) return 0; - - const distanceTop = domRect?.top - containerRect?.top; - const distanceBottom = domRect?.bottom - containerRect?.bottom; - - return Math.abs(distanceTop) < Math.abs(distanceBottom) ? distanceTop : distanceBottom; -} - -export function scrollIntoView(dom: HTMLElement, container: HTMLElement) { - const isDomInView = inView(dom, container); - - if (isDomInView) return; - - dom.scrollIntoView({ - block: 'nearest', - inline: 'nearest', - behavior: 'smooth', - }); -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/notify/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/notify/index.ts deleted file mode 100644 index 1086cabdfd..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/notify/index.ts +++ /dev/null @@ -1,27 +0,0 @@ -import toast from 'react-hot-toast'; - -const commonOptions = { - style: { - background: 'var(--bg-base)', - color: 'var(--text-title)', - shadows: 'var(--shadow)', - }, -}; - -export const notify = { - success: (message: string) => { - toast.success(message, commonOptions); - }, - error: (message: string) => { - toast.error(message, commonOptions); - }, - loading: (message: string) => { - toast.loading(message, commonOptions); - }, - info: (message: string) => { - toast(message, commonOptions); - }, - clear: () => { - toast.dismiss(); - }, -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/popover/Popover.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/popover/Popover.hooks.ts deleted file mode 100644 index 0fc1b5e61e..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/popover/Popover.hooks.ts +++ /dev/null @@ -1,237 +0,0 @@ -import { useState, useEffect, useCallback } from 'react'; -import { PopoverOrigin } from '@mui/material/Popover/Popover'; - -interface PopoverPosition { - anchorOrigin: PopoverOrigin; - transformOrigin: PopoverOrigin; - paperWidth: number; - paperHeight: number; - isEntered: boolean; - anchorPosition?: { left: number; top: number }; -} - -interface UsePopoverAutoPositionProps { - anchorEl?: HTMLElement | null; - anchorPosition?: { left: number; top: number; height: number }; - initialAnchorOrigin?: PopoverOrigin; - initialTransformOrigin?: PopoverOrigin; - initialPaperWidth: number; - initialPaperHeight: number; - marginThreshold?: number; - open: boolean; - anchorSize?: { width: number; height: number }; -} - -const minPaperWidth = 80; -const minPaperHeight = 120; - -function getOffsetLeft( - rect: { - height: number; - width: number; - }, - paperWidth: number, - horizontal: number | 'center' | 'left' | 'right', - transformHorizontal: number | 'center' | 'left' | 'right' -) { - let offset = 0; - - if (typeof horizontal === 'number') { - offset = horizontal; - } else if (horizontal === 'center') { - offset = rect.width / 2; - } else if (horizontal === 'right') { - offset = rect.width; - } - - if (transformHorizontal === 'center') { - offset -= paperWidth / 2; - } else if (transformHorizontal === 'right') { - offset -= paperWidth; - } - - return offset; -} - -function getOffsetTop( - rect: { - height: number; - width: number; - }, - papertHeight: number, - vertical: number | 'center' | 'bottom' | 'top', - transformVertical: number | 'center' | 'bottom' | 'top' -) { - let offset = 0; - - if (typeof vertical === 'number') { - offset = vertical; - } else if (vertical === 'center') { - offset = rect.height / 2; - } else if (vertical === 'bottom') { - offset = rect.height; - } - - if (transformVertical === 'center') { - offset -= papertHeight / 2; - } else if (transformVertical === 'bottom') { - offset -= papertHeight; - } - - return offset; -} - -const defaultAnchorOrigin: PopoverOrigin = { - vertical: 'top', - horizontal: 'left', -}; - -const defaultTransformOrigin: PopoverOrigin = { - vertical: 'top', - horizontal: 'left', -}; - -const usePopoverAutoPosition = ({ - anchorEl, - anchorPosition, - initialAnchorOrigin = defaultAnchorOrigin, - initialTransformOrigin = defaultTransformOrigin, - initialPaperWidth, - initialPaperHeight, - marginThreshold = 16, - open, -}: UsePopoverAutoPositionProps): PopoverPosition & { - calculateAnchorSize: () => void; -} => { - const [position, setPosition] = useState({ - anchorOrigin: initialAnchorOrigin, - transformOrigin: initialTransformOrigin, - paperWidth: initialPaperWidth, - paperHeight: initialPaperHeight, - anchorPosition, - isEntered: false, - }); - - const calculateAnchorSize = useCallback(() => { - const viewportWidth = window.innerWidth; - const viewportHeight = window.innerHeight; - - const getAnchorOffset = () => { - if (anchorPosition) { - return { - ...anchorPosition, - width: 0, - }; - } - - return anchorEl ? anchorEl.getBoundingClientRect() : undefined; - }; - - const anchorRect = getAnchorOffset(); - - if (!anchorRect) return; - let newPaperWidth = initialPaperWidth; - let newPaperHeight = initialPaperHeight; - const newAnchorPosition = { - top: anchorRect.top, - left: anchorRect.left, - }; - - // calculate new paper width - const newLeft = - anchorRect.left + - getOffsetLeft(anchorRect, newPaperWidth, initialAnchorOrigin.horizontal, initialTransformOrigin.horizontal); - const newTop = - anchorRect.top + - getOffsetTop(anchorRect, newPaperHeight, initialAnchorOrigin.vertical, initialTransformOrigin.vertical); - - let isExceedViewportRight = false; - let isExceedViewportBottom = false; - let isExceedViewportLeft = false; - let isExceedViewportTop = false; - - // Check if exceed viewport right - if (newLeft + newPaperWidth > viewportWidth - marginThreshold) { - isExceedViewportRight = true; - // Check if exceed viewport left - if (newLeft - newPaperWidth < marginThreshold) { - isExceedViewportLeft = true; - newPaperWidth = Math.max(minPaperWidth, Math.min(newPaperWidth, viewportWidth - newLeft - marginThreshold)); - } - } - - // Check if exceed viewport bottom - if (newTop + newPaperHeight > viewportHeight - marginThreshold) { - isExceedViewportBottom = true; - // Check if exceed viewport top - if (newTop - newPaperHeight < marginThreshold) { - isExceedViewportTop = true; - newPaperHeight = Math.max(minPaperHeight, Math.min(newPaperHeight, viewportHeight - newTop - marginThreshold)); - } - } - - const newPosition = { - anchorOrigin: { ...initialAnchorOrigin }, - transformOrigin: { ...initialTransformOrigin }, - paperWidth: newPaperWidth, - paperHeight: newPaperHeight, - anchorPosition: newAnchorPosition, - }; - - // If exceed viewport, adjust anchor origin and transform origin - if (!isExceedViewportRight && !isExceedViewportLeft) { - if (isExceedViewportBottom && !isExceedViewportTop) { - newPosition.anchorOrigin.vertical = 'top'; - newPosition.transformOrigin.vertical = 'bottom'; - } else if (!isExceedViewportBottom && isExceedViewportTop) { - newPosition.anchorOrigin.vertical = 'bottom'; - newPosition.transformOrigin.vertical = 'top'; - } - } else if (!isExceedViewportBottom && !isExceedViewportTop) { - if (isExceedViewportRight && !isExceedViewportLeft) { - newPosition.anchorOrigin.horizontal = 'left'; - newPosition.transformOrigin.horizontal = 'right'; - } else if (!isExceedViewportRight && isExceedViewportLeft) { - newPosition.anchorOrigin.horizontal = 'right'; - newPosition.transformOrigin.horizontal = 'left'; - } - } - - // anchorPosition is top-left of the anchor element, so we need to adjust it to avoid overlap with the anchor element - if (newPosition.anchorOrigin.vertical === 'bottom' && newPosition.transformOrigin.vertical === 'top') { - newPosition.anchorPosition.top += anchorRect.height; - } - - if ( - isExceedViewportTop && - isExceedViewportBottom && - newPosition.anchorOrigin.vertical === 'top' && - newPosition.transformOrigin.vertical === 'bottom' - ) { - newPosition.paperHeight = newPaperHeight - anchorRect.height; - } - - // Set new position and set isEntered to true - setPosition({ ...newPosition, isEntered: true }); - }, [ - initialAnchorOrigin, - initialTransformOrigin, - initialPaperWidth, - initialPaperHeight, - marginThreshold, - anchorEl, - anchorPosition, - ]); - - useEffect(() => { - if (!open) return; - calculateAnchorSize(); - }, [open, calculateAnchorSize]); - - return { - ...position, - calculateAnchorSize, - }; -}; - -export default usePopoverAutoPosition; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/popover/utils.ts b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/popover/utils.ts deleted file mode 100644 index dccaf2f4d4..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/popover/utils.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { PopoverOrigin } from '@mui/material/Popover/Popover'; - -export function getOffsetTop(rect: DOMRect, vertical: number | 'center' | 'bottom' | 'top') { - let offset = 0; - - if (typeof vertical === 'number') { - offset = vertical; - } else if (vertical === 'center') { - offset = rect.height / 2; - } else if (vertical === 'bottom') { - offset = rect.height; - } - - return offset; -} - -export function getOffsetLeft(rect: DOMRect, horizontal: number | 'center' | 'left' | 'right') { - let offset = 0; - - if (typeof horizontal === 'number') { - offset = horizontal; - } else if (horizontal === 'center') { - offset = rect.width / 2; - } else if (horizontal === 'right') { - offset = rect.width; - } - - return offset; -} - -export function getAnchorOffset(anchorElement: HTMLElement, anchorOrigin: PopoverOrigin) { - const anchorRect = anchorElement.getBoundingClientRect(); - - return { - top: anchorRect.top + getOffsetTop(anchorRect, anchorOrigin.vertical), - left: anchorRect.left + getOffsetLeft(anchorRect, anchorOrigin.horizontal), - }; -} - -export function getTransformOrigin(elemRect: DOMRect, transformOrigin: PopoverOrigin) { - return { - vertical: getOffsetTop(elemRect, transformOrigin.vertical), - horizontal: getOffsetLeft(elemRect, transformOrigin.horizontal), - }; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/scroller/AFScroller.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/scroller/AFScroller.tsx deleted file mode 100644 index 0527b6cc26..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/scroller/AFScroller.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import { Scrollbars } from 'react-custom-scrollbars'; -import React from 'react'; - -export interface AFScrollerProps { - children: React.ReactNode; - overflowXHidden?: boolean; - overflowYHidden?: boolean; - className?: string; - style?: React.CSSProperties; -} -export const AFScroller = ({ style, children, overflowXHidden, overflowYHidden, className }: AFScrollerProps) => { - return ( -
} - renderThumbVertical={(props) =>
} - {...(overflowXHidden && { - renderTrackHorizontal: (props) => ( -
- ), - })} - {...(overflowYHidden && { - renderTrackVertical: (props) => ( -
- ), - })} - style={style} - renderView={(props) => ( -
- )} - > - {children} - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/scroller/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/scroller/index.ts deleted file mode 100644 index 7a740a5bb0..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/scroller/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './AFScroller'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/view_title/ViewBanner.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/view_title/ViewBanner.tsx deleted file mode 100644 index 95e44ae9c2..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/view_title/ViewBanner.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import ViewIconGroup from '$app/components/_shared/view_title/ViewIconGroup'; -import { PageCover, PageIcon } from '$app_reducers/pages/slice'; -import ViewIcon from '$app/components/_shared/view_title/ViewIcon'; -import { ViewCover } from '$app/components/_shared/view_title/cover'; - -function ViewBanner({ - icon, - hover, - onUpdateIcon, - showCover, - cover, - onUpdateCover, -}: { - icon?: PageIcon; - hover: boolean; - onUpdateIcon: (icon: string) => void; - showCover: boolean; - cover?: PageCover; - onUpdateCover?: (cover?: PageCover) => void; -}) { - return ( -
- {showCover && cover && } - -
-
- -
-
- -
-
-
- ); -} - -export default ViewBanner; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/view_title/ViewIcon.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/view_title/ViewIcon.tsx deleted file mode 100644 index 009548df53..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/view_title/ViewIcon.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import React, { useCallback, useState } from 'react'; -import Popover from '@mui/material/Popover'; -import EmojiPicker from '$app/components/_shared/emoji_picker/EmojiPicker'; -import { PageIcon } from '$app_reducers/pages/slice'; - -function ViewIcon({ icon, onUpdateIcon }: { icon?: PageIcon; onUpdateIcon: (icon: string) => void }) { - const [anchorPosition, setAnchorPosition] = useState<{ - top: number; - left: number; - }>(); - - const open = Boolean(anchorPosition); - const onOpen = useCallback((event: React.MouseEvent) => { - const rect = event.currentTarget.getBoundingClientRect(); - - setAnchorPosition({ - top: rect.top + rect.height, - left: rect.left, - }); - }, []); - - const onEmojiSelect = useCallback( - (emoji: string) => { - onUpdateIcon(emoji); - if (!emoji) { - setAnchorPosition(undefined); - } - }, - [onUpdateIcon] - ); - - if (!icon) return null; - return ( - <> -
-
- {icon.value} -
-
- {open && ( - setAnchorPosition(undefined)} - > - { - setAnchorPosition(undefined); - }} - onEmojiSelect={onEmojiSelect} - /> - - )} - - ); -} - -export default ViewIcon; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/view_title/ViewIconGroup.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/view_title/ViewIconGroup.tsx deleted file mode 100644 index 54256f8eb1..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/view_title/ViewIconGroup.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import { useTranslation } from 'react-i18next'; -import { CoverType, PageCover, PageIcon } from '$app_reducers/pages/slice'; -import React, { useCallback } from 'react'; -import { randomEmoji } from '$app/utils/emoji'; -import { EmojiEmotionsOutlined } from '@mui/icons-material'; -import Button from '@mui/material/Button'; -import { ReactComponent as ImageIcon } from '$app/assets/image.svg'; -import { ImageType } from '$app/application/document/document.types'; - -interface Props { - icon?: PageIcon; - onUpdateIcon: (icon: string) => void; - showCover: boolean; - cover?: PageCover; - onUpdateCover?: (cover: PageCover) => void; -} - -const defaultCover = { - cover_selection_type: CoverType.Asset, - cover_selection: 'app_flowy_abstract_cover_2.jpeg', - image_type: ImageType.Internal, -}; - -function ViewIconGroup({ icon, onUpdateIcon, showCover, cover, onUpdateCover }: Props) { - const { t } = useTranslation(); - - const showAddIcon = !icon?.value; - - const showAddCover = !cover && showCover; - - const onAddIcon = useCallback(() => { - const emoji = randomEmoji(); - - onUpdateIcon(emoji); - }, [onUpdateIcon]); - - const onAddCover = useCallback(() => { - onUpdateCover?.(defaultCover); - }, [onUpdateCover]); - - return ( -
- {showAddIcon && ( - - )} - {showAddCover && ( - - )} -
- ); -} - -export default ViewIconGroup; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/view_title/ViewTitle.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/view_title/ViewTitle.tsx deleted file mode 100644 index 8d81b6d4b7..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/view_title/ViewTitle.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import React, { useCallback, useEffect, useState } from 'react'; -import ViewBanner from '$app/components/_shared/view_title/ViewBanner'; -import { Page, PageCover, PageIcon } from '$app_reducers/pages/slice'; -import { ViewIconTypePB } from '@/services/backend'; -import ViewTitleInput from '$app/components/_shared/view_title/ViewTitleInput'; - -interface Props { - view: Page; - showTitle?: boolean; - onTitleChange?: (title: string) => void; - onUpdateIcon?: (icon: PageIcon) => void; - forceHover?: boolean; - showCover?: boolean; - onUpdateCover?: (cover?: PageCover) => void; -} - -function ViewTitle({ - view, - forceHover = false, - onTitleChange, - showTitle = true, - onUpdateIcon: onUpdateIconProp, - showCover = false, - onUpdateCover, -}: Props) { - const [hover, setHover] = useState(false); - const [icon, setIcon] = useState(view.icon); - - useEffect(() => { - setIcon(view.icon); - }, [view.icon]); - - const onUpdateIcon = useCallback( - (icon: string) => { - const newIcon = { - value: icon, - ty: ViewIconTypePB.Emoji, - }; - - setIcon(newIcon); - onUpdateIconProp?.(newIcon); - }, - [onUpdateIconProp] - ); - - return ( -
setHover(true)} - onMouseLeave={() => setHover(false)} - > - - {showTitle && ( -
- -
- )} -
- ); -} - -export default ViewTitle; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/view_title/ViewTitleInput.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/view_title/ViewTitleInput.tsx deleted file mode 100644 index 2c69bb4d76..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/view_title/ViewTitleInput.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import React, { FormEventHandler, memo, useCallback, useRef } from 'react'; -import { TextareaAutosize } from '@mui/material'; -import { useTranslation } from 'react-i18next'; - -function ViewTitleInput({ value, onChange }: { value: string; onChange?: (value: string) => void }) { - const { t } = useTranslation(); - const textareaRef = useRef(null); - - const onTitleChange: FormEventHandler = useCallback( - (e) => { - const value = e.currentTarget.value; - - onChange?.(value); - }, - [onChange] - ); - - return ( - - ); -} - -export default memo(ViewTitleInput); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/view_title/cover/Colors.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/view_title/cover/Colors.tsx deleted file mode 100644 index 78b8bbcc46..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/view_title/cover/Colors.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import React from 'react'; -import { colorMap } from '$app/utils/color'; - -const colors = Object.entries(colorMap); - -function Colors({ onDone }: { onDone?: (value: string) => void }) { - return ( -
- {colors.map(([name, value]) => ( -
onDone?.(name)} - /> - ))} -
- ); -} - -export default Colors; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/view_title/cover/CoverPopover.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/view_title/cover/CoverPopover.tsx deleted file mode 100644 index bd8c178380..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/view_title/cover/CoverPopover.tsx +++ /dev/null @@ -1,112 +0,0 @@ -import React, { useMemo } from 'react'; -import { CoverType, PageCover } from '$app_reducers/pages/slice'; -import { PopoverOrigin } from '@mui/material/Popover'; -import { EmbedLink, Unsplash, UploadTabs, TabOption, TAB_KEY, UploadImage } from '$app/components/_shared/image_upload'; -import { useTranslation } from 'react-i18next'; -import Colors from '$app/components/_shared/view_title/cover/Colors'; -import { ImageType } from '$app/application/document/document.types'; -import Button from '@mui/material/Button'; - -const initialOrigin: { - anchorOrigin: PopoverOrigin; - transformOrigin: PopoverOrigin; -} = { - anchorOrigin: { - vertical: 'bottom', - horizontal: 'center', - }, - transformOrigin: { - vertical: 'top', - horizontal: 'center', - }, -}; - -function CoverPopover({ - anchorEl, - open, - onClose, - onUpdateCover, - onRemoveCover, -}: { - anchorEl: HTMLElement | null; - open: boolean; - onClose: () => void; - onUpdateCover?: (cover?: PageCover) => void; - onRemoveCover?: () => void; -}) { - const { t } = useTranslation(); - const tabOptions: TabOption[] = useMemo(() => { - return [ - { - label: t('document.plugins.cover.colors'), - key: TAB_KEY.Colors, - Component: Colors, - onDone: (value: string) => { - onUpdateCover?.({ - cover_selection_type: CoverType.Color, - cover_selection: value, - image_type: ImageType.Internal, - }); - }, - }, - { - label: t('button.upload'), - key: TAB_KEY.UPLOAD, - Component: UploadImage, - onDone: (value: string) => { - onUpdateCover?.({ - cover_selection_type: CoverType.Image, - cover_selection: value, - image_type: ImageType.Local, - }); - onClose(); - }, - }, - { - label: t('document.imageBlock.embedLink.label'), - key: TAB_KEY.EMBED_LINK, - Component: EmbedLink, - onDone: (value: string) => { - onUpdateCover?.({ - cover_selection_type: CoverType.Image, - cover_selection: value, - image_type: ImageType.External, - }); - onClose(); - }, - }, - { - key: TAB_KEY.UNSPLASH, - label: t('document.imageBlock.unsplash.label'), - Component: Unsplash, - onDone: (value: string) => { - onUpdateCover?.({ - cover_selection_type: CoverType.Image, - cover_selection: value, - image_type: ImageType.External, - }); - }, - }, - ]; - }, [onClose, onUpdateCover, t]); - - return ( - - {t('button.remove')} - - } - /> - ); -} - -export default CoverPopover; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/view_title/cover/ViewCover.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/view_title/cover/ViewCover.tsx deleted file mode 100644 index f207e07886..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/view_title/cover/ViewCover.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import React, { useCallback, useMemo, useRef, useState } from 'react'; -import { CoverType, PageCover } from '$app_reducers/pages/slice'; -import { renderColor } from '$app/utils/color'; -import ViewCoverActions from '$app/components/_shared/view_title/cover/ViewCoverActions'; -import CoverPopover from '$app/components/_shared/view_title/cover/CoverPopover'; -import DefaultImage from '$app/assets/images/default_cover.jpg'; -import { ImageType } from '$app/application/document/document.types'; -import { LocalImage } from '$app/components/_shared/image_upload'; - -export function ViewCover({ cover, onUpdateCover }: { cover: PageCover; onUpdateCover?: (cover?: PageCover) => void }) { - const { - cover_selection_type: type, - cover_selection: value = '', - image_type: source, - } = useMemo(() => cover || {}, [cover]); - const [showAction, setShowAction] = useState(false); - const actionRef = useRef(null); - const [showPopover, setShowPopover] = useState(false); - - const renderCoverColor = useCallback((color: string) => { - return ( -
- ); - }, []); - - const renderCoverImage = useCallback((url: string) => { - return {''}; - }, []); - - const handleRemoveCover = useCallback(() => { - onUpdateCover?.(null); - }, [onUpdateCover]); - - const handleClickChange = useCallback(() => { - setShowPopover(true); - }, []); - - return ( -
{ - setShowAction(true); - }} - onMouseLeave={() => { - setShowAction(false); - }} - className={'relative flex h-[255px] w-full'} - > - {source === ImageType.Local ? ( - - ) : ( - <> - {type === CoverType.Asset ? renderCoverImage(DefaultImage) : null} - {type === CoverType.Color ? renderCoverColor(value) : null} - {type === CoverType.Image ? renderCoverImage(value) : null} - - )} - - - {showPopover && ( - setShowPopover(false)} - anchorEl={actionRef.current} - onUpdateCover={onUpdateCover} - onRemoveCover={handleRemoveCover} - /> - )} -
- ); -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/view_title/cover/ViewCoverActions.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/view_title/cover/ViewCoverActions.tsx deleted file mode 100644 index fbf8063f44..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/view_title/cover/ViewCoverActions.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import React, { forwardRef } from 'react'; -import Button from '@mui/material/Button'; -import { useTranslation } from 'react-i18next'; -import { ReactComponent as DeleteIcon } from '$app/assets/delete.svg'; - -function ViewCoverActions( - { show, onRemove, onClickChange }: { show: boolean; onRemove: () => void; onClickChange: () => void }, - ref: React.ForwardedRef -) { - const { t } = useTranslation(); - - return ( -
-
-
- -
-
-
- ); -} - -export default forwardRef(ViewCoverActions); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/view_title/cover/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/view_title/cover/index.ts deleted file mode 100644 index 8df50bb41e..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/view_title/cover/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './ViewCover'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/auth/LoginButtonGroup.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/auth/LoginButtonGroup.tsx deleted file mode 100644 index 481b80a532..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/auth/LoginButtonGroup.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import Button from '@mui/material/Button'; -import GoogleIcon from '$app/assets/settings/google.png'; -import GithubIcon from '$app/assets/settings/github.png'; -import DiscordIcon from '$app/assets/settings/discord.png'; -import { useTranslation } from 'react-i18next'; -import { useAuth } from '$app/components/auth/auth.hooks'; -import { ProviderTypePB } from '@/services/backend'; - -export const LoginButtonGroup = () => { - const { t } = useTranslation(); - - const { signIn } = useAuth(); - - return ( -
- - - -
- ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/auth/ProtectedRoutes.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/auth/ProtectedRoutes.tsx deleted file mode 100644 index 523f0b5188..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/auth/ProtectedRoutes.tsx +++ /dev/null @@ -1,127 +0,0 @@ -import { Outlet } from 'react-router-dom'; -import { useAuth } from './auth.hooks'; -import Layout from '$app/components/layout/Layout'; -import { useCallback, useEffect, useState } from 'react'; -import { Welcome } from '$app/components/auth/Welcome'; -import { isTauri } from '$app/utils/env'; -import { notify } from '$app/components/_shared/notify'; -import { currentUserActions, LoginState } from '$app_reducers/current-user/slice'; -import { CircularProgress, Portal } from '@mui/material'; -import { ReactComponent as Logo } from '$app/assets/logo.svg'; -import { useAppDispatch } from '$app/stores/store'; - -export const ProtectedRoutes = () => { - const { currentUser, checkUser, subscribeToUser, signInWithOAuth } = useAuth(); - const dispatch = useAppDispatch(); - - const isLoading = currentUser?.loginState === LoginState.Loading; - - const [checked, setChecked] = useState(false); - - const checkUserStatus = useCallback(async () => { - await checkUser(); - setChecked(true); - }, [checkUser]); - - useEffect(() => { - void checkUserStatus(); - }, [checkUserStatus]); - - useEffect(() => { - if (currentUser.isAuthenticated) { - return subscribeToUser(); - } - }, [currentUser.isAuthenticated, subscribeToUser]); - - const onDeepLink = useCallback(async () => { - if (!isTauri()) return; - const { event } = await import('@tauri-apps/api'); - - // On macOS You still have to install a .app bundle you got from tauri build --debug for this to work! - return await event.listen('open_deep_link', async (e) => { - const payload = e.payload as string; - - const [, hash] = payload.split('//#'); - const obj = parseHash(hash); - - if (!obj.access_token) { - notify.error('Failed to sign in, the access token is missing'); - dispatch(currentUserActions.setLoginState(LoginState.Error)); - return; - } - - try { - await signInWithOAuth(payload); - } catch (e) { - notify.error('Failed to sign in, please try again'); - } - }); - }, [dispatch, signInWithOAuth]); - - useEffect(() => { - void onDeepLink(); - }, [onDeepLink]); - - return ( -
- {checked ? ( - - ) : ( -
- -
- )} - - {isLoading && } -
- ); -}; - -const StartLoading = () => { - const dispatch = useAppDispatch(); - - useEffect(() => { - const preventDefault = (e: KeyboardEvent) => { - if (e.key === 'Escape') { - e.preventDefault(); - dispatch(currentUserActions.resetLoginState()); - } - }; - - document.addEventListener('keydown', preventDefault, true); - - return () => { - document.removeEventListener('keydown', preventDefault, true); - }; - }, [dispatch]); - return ( - -
- -
-
- ); -}; - -const SplashScreen = ({ isAuthenticated }: { isAuthenticated: boolean }) => { - if (isAuthenticated) { - return ( - - - - ); - } else { - return ; - } -}; - -function parseHash(hash: string) { - const hashParams = new URLSearchParams(hash); - const hashObject: Record = {}; - - for (const [key, value] of hashParams) { - hashObject[key] = value; - } - - return hashObject; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/auth/Welcome.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/auth/Welcome.tsx deleted file mode 100644 index eadcf08c21..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/auth/Welcome.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import { ReactComponent as AppflowyLogo } from '$app/assets/logo.svg'; -import Button from '@mui/material/Button'; -import { useTranslation } from 'react-i18next'; -import { LoginButtonGroup } from '$app/components/auth/LoginButtonGroup'; -import { useAuth } from '$app/components/auth/auth.hooks'; -import { Log } from '$app/utils/log'; - -export const Welcome = () => { - const { signInAsAnonymous } = useAuth(); - const { t } = useTranslation(); - - return ( - <> -
e.preventDefault()} method='POST'> -
-
- -
- -
- - {t('welcomeTo')} {t('appName')} - -
- -
- -
-
- {t('signIn.or')} -
-
-
- -
-
-
- - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/auth/auth.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/auth/auth.hooks.ts deleted file mode 100644 index 89b7388e64..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/auth/auth.hooks.ts +++ /dev/null @@ -1,186 +0,0 @@ -import { currentUserActions, LoginState, parseWorkspaceSettingPBToSetting } from '$app_reducers/current-user/slice'; -import { AuthenticatorPB, ProviderTypePB, UserNotification, UserProfilePB } from '@/services/backend/events/flowy-user'; -import { UserService } from '$app/application/user/user.service'; -import { AuthService } from '$app/application/user/auth.service'; -import { useAppDispatch, useAppSelector } from '$app/stores/store'; -import { getCurrentWorkspaceSetting } from '$app/application/folder/workspace.service'; -import { useCallback } from 'react'; -import { subscribeNotifications } from '$app/application/notification'; -import { nanoid } from 'nanoid'; -import { open } from '@tauri-apps/api/shell'; - -export const useAuth = () => { - const dispatch = useAppDispatch(); - const currentUser = useAppSelector((state) => state.currentUser); - - // Subscribe to user update events - const subscribeToUser = useCallback(() => { - const unsubscribePromise = subscribeNotifications({ - [UserNotification.DidUpdateUserProfile]: async (changeset) => { - dispatch( - currentUserActions.updateUser({ - email: changeset.email, - displayName: changeset.name, - iconUrl: changeset.icon_url, - }) - ); - }, - }); - - return () => { - void unsubscribePromise.then((fn) => fn()); - }; - }, [dispatch]); - - const setUser = useCallback( - async (userProfile?: Partial) => { - if (!userProfile) return; - - const workspaceSetting = await getCurrentWorkspaceSetting(); - - const isLocal = userProfile.authenticator === AuthenticatorPB.Local; - - dispatch( - currentUserActions.updateUser({ - id: userProfile.id, - token: userProfile.token, - email: userProfile.email, - displayName: userProfile.name, - iconUrl: userProfile.icon_url, - isAuthenticated: true, - workspaceSetting: workspaceSetting ? parseWorkspaceSettingPBToSetting(workspaceSetting) : undefined, - isLocal, - }) - ); - }, - [dispatch] - ); - - // Check if the user is authenticated - const checkUser = useCallback(async () => { - const userProfile = await UserService.getUserProfile(); - - await setUser(userProfile); - - return userProfile; - }, [setUser]); - - const register = useCallback( - async (email: string, password: string, name: string): Promise => { - const deviceId = currentUser?.deviceId ?? nanoid(8); - const userProfile = await AuthService.signUp({ deviceId, email, password, name }); - - await setUser(userProfile); - - return userProfile; - }, - [setUser, currentUser?.deviceId] - ); - - const logout = useCallback(async () => { - await AuthService.signOut(); - dispatch(currentUserActions.logout()); - }, [dispatch]); - - const signInAsAnonymous = useCallback(async () => { - const fakeEmail = nanoid(8) + '@appflowy.io'; - const fakePassword = 'AppFlowy123@'; - const fakeName = 'Me'; - - await register(fakeEmail, fakePassword, fakeName); - }, [register]); - - const signIn = useCallback( - async (provider: ProviderTypePB) => { - dispatch(currentUserActions.setLoginState(LoginState.Loading)); - try { - const url = await AuthService.getOAuthURL(provider); - - await open(url); - } catch { - dispatch(currentUserActions.setLoginState(LoginState.Error)); - } - }, - [dispatch] - ); - - const signInWithOAuth = useCallback( - async (uri: string) => { - dispatch(currentUserActions.setLoginState(LoginState.Loading)); - try { - const deviceId = currentUser?.deviceId ?? nanoid(8); - - await AuthService.signInWithOAuth({ uri, deviceId }); - const userProfile = await UserService.getUserProfile(); - - await setUser(userProfile); - - return userProfile; - } catch (e) { - dispatch(currentUserActions.setLoginState(LoginState.Error)); - return Promise.reject(e); - } - }, - [dispatch, currentUser?.deviceId, setUser] - ); - - // Only for development purposes - const signInWithEmailPassword = useCallback( - async (email: string, password: string, domain?: string) => { - dispatch(currentUserActions.setLoginState(LoginState.Loading)); - - try { - const response = await fetch( - `https://${domain ? domain : 'test.appflowy.cloud'}/gotrue/token?grant_type=password`, - { - method: 'POST', - mode: 'cors', - cache: 'no-cache', - credentials: 'same-origin', - headers: { - 'Content-Type': 'application/json', - }, - redirect: 'follow', - referrerPolicy: 'no-referrer', - body: JSON.stringify({ - email, - password, - }), - } - ); - - const data = await response.json(); - - let uri = `appflowy-flutter://#`; - const params: string[] = []; - - Object.keys(data).forEach((key) => { - if (typeof data[key] === 'object') { - return; - } - - params.push(`${key}=${data[key]}`); - }); - uri += params.join('&'); - - return signInWithOAuth(uri); - } catch (e) { - dispatch(currentUserActions.setLoginState(LoginState.Error)); - return Promise.reject(e); - } - }, - [dispatch, signInWithOAuth] - ); - - return { - currentUser, - checkUser, - register, - logout, - subscribeToUser, - signInAsAnonymous, - signIn, - signInWithOAuth, - signInWithEmailPassword, - }; -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/Database.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/database/Database.hooks.ts deleted file mode 100644 index 2597c158a1..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/Database.hooks.ts +++ /dev/null @@ -1,204 +0,0 @@ -import { createContext, useCallback, useContext, useEffect, useMemo } from 'react'; -import { useSearchParams } from 'react-router-dom'; -import { proxy, useSnapshot } from 'valtio'; - -import { DatabaseLayoutPB, DatabaseNotification, FieldVisibility } from '@/services/backend'; -import { subscribeNotifications } from '$app/application/notification'; -import { - Cell, - Database, - databaseService, - cellListeners, - fieldListeners, - rowListeners, - sortListeners, - filterListeners, -} from '$app/application/database'; - -export function useSelectDatabaseView({ viewId }: { viewId?: string }) { - const key = 'v'; - const [searchParams, setSearchParams] = useSearchParams(); - - const selectedViewId = useMemo(() => searchParams.get(key) || viewId, [searchParams, viewId]); - - const onChange = useCallback( - (value: string) => { - setSearchParams({ [key]: value }); - }, - [setSearchParams] - ); - - return { - selectedViewId, - onChange, - }; -} - -const DatabaseContext = createContext({ - id: '', - isLinked: false, - layoutType: DatabaseLayoutPB.Grid, - fields: [], - rowMetas: [], - filters: [], - sorts: [], - groupSettings: [], - groups: [], - typeOptions: {}, - cells: {}, -}); - -export const DatabaseProvider = DatabaseContext.Provider; - -export const useDatabase = () => useSnapshot(useContext(DatabaseContext)); - -export const useSelectorCell = (rowId: string, fieldId: string) => { - const database = useContext(DatabaseContext); - const cells = useSnapshot(database.cells); - - return cells[`${rowId}:${fieldId}`]; -}; - -export const useDispatchCell = () => { - const database = useContext(DatabaseContext); - - const setCell = useCallback( - (cell: Cell) => { - const id = `${cell.rowId}:${cell.fieldId}`; - - database.cells[id] = cell; - }, - [database] - ); - - const deleteCells = useCallback( - ({ rowId, fieldId }: { rowId: string; fieldId?: string }) => { - cellListeners.didDeleteCells({ database, rowId, fieldId }); - }, - [database] - ); - - return { - deleteCells, - setCell, - }; -}; - -export const useDatabaseSorts = () => { - const context = useContext(DatabaseContext); - - return useSnapshot(context.sorts); -}; - -export const useSortsCount = () => { - const { sorts } = useDatabase(); - - return sorts?.length; -}; - -export const useFiltersCount = () => { - const { filters, fields } = useDatabase(); - - // filter fields: if the field is deleted, it will not be displayed - return useMemo( - () => filters?.map((filter) => fields.find((field) => field.id === filter.fieldId)).filter(Boolean).length, - [filters, fields] - ); -}; - -export function useStaticTypeOption(fieldId: string) { - const context = useContext(DatabaseContext); - const typeOptions = context.typeOptions; - - return typeOptions[fieldId] as T; -} - -export function useTypeOption(fieldId: string) { - const context = useContext(DatabaseContext); - const typeOptions = useSnapshot(context.typeOptions); - - return typeOptions[fieldId] as T; -} - -export const useDatabaseVisibilityRows = () => { - const { rowMetas } = useDatabase(); - - return useMemo(() => rowMetas.filter((row) => row && !row.isHidden), [rowMetas]); -}; - -export const useDatabaseVisibilityFields = () => { - const database = useDatabase(); - - return useMemo( - () => database.fields.filter((field) => field.visibility !== FieldVisibility.AlwaysHidden), - [database.fields] - ); -}; - -export const useConnectDatabase = (viewId: string) => { - const database = useMemo(() => { - const proxyDatabase = proxy({ - id: '', - isLinked: false, - layoutType: DatabaseLayoutPB.Grid, - fields: [], - rowMetas: [], - filters: [], - sorts: [], - groupSettings: [], - groups: [], - typeOptions: {}, - cells: {}, - }); - - void databaseService.openDatabase(viewId).then((value) => Object.assign(proxyDatabase, value)); - - return proxyDatabase; - }, [viewId]); - - useEffect(() => { - const unsubscribePromise = subscribeNotifications( - { - [DatabaseNotification.DidUpdateFields]: async (changeset) => { - await fieldListeners.didUpdateFields(viewId, database, changeset); - }, - [DatabaseNotification.DidUpdateFieldSettings]: (changeset) => { - fieldListeners.didUpdateFieldSettings(database, changeset); - }, - [DatabaseNotification.DidUpdateViewRows]: async (changeset) => { - await rowListeners.didUpdateViewRows(viewId, database, changeset); - }, - [DatabaseNotification.DidReorderRows]: (changeset) => { - rowListeners.didReorderRows(database, changeset); - }, - [DatabaseNotification.DidReorderSingleRow]: (changeset) => { - rowListeners.didReorderSingleRow(database, changeset); - }, - - [DatabaseNotification.DidUpdateSort]: (changeset) => { - sortListeners.didUpdateSort(database, changeset); - }, - - [DatabaseNotification.DidUpdateFilter]: (changeset) => { - filterListeners.didUpdateFilter(database, changeset); - }, - [DatabaseNotification.DidUpdateViewRowsVisibility]: async (changeset) => { - await rowListeners.didUpdateViewRowsVisibility(viewId, database, changeset); - }, - }, - { id: viewId } - ); - - return () => void unsubscribePromise.then((unsubscribe) => unsubscribe()); - }, [viewId, database]); - - return database; -}; - -const DatabaseRenderedContext = createContext<(viewId: string) => void>(() => { - return; -}); - -export const DatabaseRenderedProvider = DatabaseRenderedContext.Provider; - -export const useDatabaseRendered = () => useContext(DatabaseRenderedContext); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/Database.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/Database.tsx deleted file mode 100644 index d5e7bba45b..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/Database.tsx +++ /dev/null @@ -1,202 +0,0 @@ -import React, { forwardRef, useCallback, useEffect, useMemo, useRef, useState } from 'react'; -import { useViewId } from '$app/hooks/ViewId.hooks'; -import { databaseViewService } from '$app/application/database'; -import { DatabaseTabBar } from './components'; -import { DatabaseLoader } from './DatabaseLoader'; -import { DatabaseView } from './DatabaseView'; -import { DatabaseCollection } from './components/database_settings'; -import SwipeableViews from 'react-swipeable-views'; -import { TabPanel } from '$app/components/database/components/tab_bar/ViewTabs'; -import DatabaseSettings from '$app/components/database/components/database_settings/DatabaseSettings'; -import { Portal } from '@mui/material'; -import { useTranslation } from 'react-i18next'; -import { ErrorCode, FolderNotification } from '@/services/backend'; -import ExpandRecordModal from '$app/components/database/components/edit_record/ExpandRecordModal'; -import { subscribeNotifications } from '$app/application/notification'; -import { Page } from '$app_reducers/pages/slice'; -import { getPage } from '$app/application/folder/page.service'; -import './database.scss'; - -interface Props { - selectedViewId?: string; - setSelectedViewId?: (viewId: string) => void; -} - -export const Database = forwardRef(({ selectedViewId, setSelectedViewId }, ref) => { - const innerRef = useRef(); - const databaseRef = (ref ?? innerRef) as React.MutableRefObject; - const viewId = useViewId(); - const [settingDom, setSettingDom] = useState(null); - - const [page, setPage] = useState(null); - const { t } = useTranslation(); - const [notFound, setNotFound] = useState(false); - const [childViews, setChildViews] = useState([]); - const [editRecordRowId, setEditRecordRowId] = useState(null); - const [openCollections, setOpenCollections] = useState([]); - - const handleResetDatabaseViews = useCallback(async (viewId: string) => { - await databaseViewService - .getDatabaseViews(viewId) - .then((value) => { - setChildViews(value); - }) - .catch((err) => { - if (err.code === ErrorCode.RecordNotFound) { - setNotFound(true); - } - }); - }, []); - - const handleGetPage = useCallback(async () => { - try { - const page = await getPage(viewId); - - setPage(page); - } catch (e) { - setNotFound(true); - } - }, [viewId]); - - const parentId = page?.parentId; - - useEffect(() => { - void handleGetPage(); - void handleResetDatabaseViews(viewId); - const unsubscribePromise = subscribeNotifications({ - [FolderNotification.DidUpdateView]: (changeset) => { - if (changeset.parent_view_id !== viewId && changeset.id !== viewId) return; - setChildViews((prev) => { - const index = prev.findIndex((view) => view.id === changeset.id); - - if (index === -1) { - return prev; - } - - const newViews = [...prev]; - - newViews[index] = { - ...newViews[index], - name: changeset.name, - }; - - return newViews; - }); - }, - [FolderNotification.DidUpdateChildViews]: (changeset) => { - if (changeset.parent_view_id !== viewId && changeset.parent_view_id !== parentId) return; - if (changeset.create_child_views.length === 0 && changeset.delete_child_views.length === 0) { - return; - } - - void handleResetDatabaseViews(viewId); - }, - }); - - return () => void unsubscribePromise.then((unsubscribe) => unsubscribe()); - }, [handleGetPage, handleResetDatabaseViews, viewId, parentId]); - - const value = useMemo(() => { - return Math.max( - 0, - childViews.findIndex((view) => view.id === (selectedViewId ?? viewId)) - ); - }, [childViews, selectedViewId, viewId]); - - const onToggleCollection = useCallback( - (id: string, forceOpen?: boolean) => { - if (forceOpen) { - setOpenCollections((prev) => { - if (prev.includes(id)) { - return prev; - } - - return [...prev, id]; - }); - return; - } - - if (openCollections.includes(id)) { - setOpenCollections((prev) => prev.filter((item) => item !== id)); - } else { - setOpenCollections((prev) => [...prev, id]); - } - }, - [openCollections, setOpenCollections] - ); - - const onEditRecord = useCallback( - (rowId: string) => { - setEditRecordRowId(rowId); - }, - [setEditRecordRowId] - ); - - if (notFound) { - return ( -
-

{t('deletePagePrompt.text')}

-
- ); - } - - return ( -
- - - {childViews.map((view, index) => ( - - - {selectedViewId === view.id && ( - <> - {settingDom && ( - - onToggleCollection(view.id, forceOpen)} - /> - - )} - - - {editRecordRowId && ( - { - setEditRecordRowId(null); - }} - /> - )} - - )} - - - - - ))} - -
- ); -}); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/DatabaseLoader.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/DatabaseLoader.tsx deleted file mode 100644 index b0aeab10a2..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/DatabaseLoader.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import { FC, PropsWithChildren } from 'react'; -import { ViewIdProvider } from '$app/hooks'; -import { DatabaseProvider, useConnectDatabase } from './Database.hooks'; - -export interface DatabaseLoaderProps { - viewId: string; -} - -export const DatabaseLoader: FC> = ({ viewId, children }) => { - const database = useConnectDatabase(viewId); - - return ( - - {/* Make sure that the viewId is current */} - {children} - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/DatabaseTitle.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/DatabaseTitle.tsx deleted file mode 100644 index cd94947d8d..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/DatabaseTitle.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { FormEventHandler, useCallback } from 'react'; -import { useViewId } from '$app/hooks'; -import { useAppDispatch, useAppSelector } from '$app/stores/store'; -import { updatePageName } from '$app_reducers/pages/async_actions'; -import { useTranslation } from 'react-i18next'; - -export const DatabaseTitle = () => { - const viewId = useViewId(); - const { t } = useTranslation(); - const pageName = useAppSelector((state) => state.pages.pageMap[viewId]?.name || ''); - const dispatch = useAppDispatch(); - - const handleInput = useCallback( - (event) => { - const newTitle = (event.target as HTMLInputElement).value; - - void dispatch(updatePageName({ id: viewId, name: newTitle })); - }, - [viewId, dispatch] - ); - - return ( -
- -
- ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/DatabaseView.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/DatabaseView.tsx deleted file mode 100644 index 98b16d5fea..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/DatabaseView.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { DatabaseLayoutPB } from '@/services/backend'; -import { FC } from 'react'; -import { useDatabase } from './Database.hooks'; -import { Grid } from './grid'; -import { Board } from './board'; -import { Calendar } from './calendar'; - -export const DatabaseView: FC<{ - onEditRecord: (rowId: string) => void; -}> = (props) => { - const { layoutType } = useDatabase(); - - switch (layoutType) { - case DatabaseLayoutPB.Grid: - return ; - case DatabaseLayoutPB.Board: - return ; - case DatabaseLayoutPB.Calendar: - return ; - default: - return null; - } -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/_shared/CellText.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/_shared/CellText.tsx deleted file mode 100644 index 01666121cd..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/_shared/CellText.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import React, { HTMLAttributes, PropsWithChildren } from 'react'; - -export interface CellTextProps { - className?: string; -} - -export const CellText = React.forwardRef>>( - function CellText(props, ref) { - const { children, className, ...other } = props; - - return ( -
- {children} -
- ); - } -); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/_shared/LinearProgressWithLabel.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/_shared/LinearProgressWithLabel.tsx deleted file mode 100644 index 7d4c0d1811..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/_shared/LinearProgressWithLabel.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import React, { useMemo } from 'react'; - -function LinearProgressWithLabel({ - value, - count, - selectedCount, -}: { - value: number; - count: number; - selectedCount: number; -}) { - const result = useMemo(() => `${Math.round(value * 100)}%`, [value]); - - const options = useMemo(() => { - return Array.from({ length: count }, (_, i) => ({ - id: i, - checked: i < selectedCount, - })); - }, [count, selectedCount]); - - const isSplit = count < 6; - - return ( -
-
- {options.map((option) => ( - - ))} -
-
{result}
-
- ); -} - -export default LinearProgressWithLabel; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/_shared/constants.ts b/frontend/appflowy_tauri/src/appflowy_app/components/database/_shared/constants.ts deleted file mode 100644 index fd1aab7a37..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/_shared/constants.ts +++ /dev/null @@ -1,9 +0,0 @@ -export enum DragType { - Row = 'row', - Field = 'field', -} - -export enum DropPosition { - Before = 0, - After = 1, -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/_shared/dnd/dnd.context.ts b/frontend/appflowy_tauri/src/appflowy_app/components/database/_shared/dnd/dnd.context.ts deleted file mode 100644 index 8954dc733a..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/_shared/dnd/dnd.context.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { createContext } from 'react'; -import { proxy } from 'valtio'; - -export interface DragItem> { - type: string; - data: T; -} - -export interface DndContextDescriptor { - dragging: DragItem | null, -} - -const defaultDndContext: DndContextDescriptor = proxy({ - dragging: null, -}); - -export const DndContext = createContext(defaultDndContext); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/_shared/dnd/drag.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/database/_shared/dnd/drag.hooks.ts deleted file mode 100644 index ce8afe6f31..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/_shared/dnd/drag.hooks.ts +++ /dev/null @@ -1,114 +0,0 @@ -import { DragEventHandler, useCallback, useContext, useMemo, useRef, useState } from 'react'; -import { DndContext } from './dnd.context'; -import { autoScrollOnEdge, EdgeGap, getScrollParent, ScrollDirection } from './utils'; - -export interface UseDraggableOptions { - type: string; - effectAllowed?: DataTransfer['effectAllowed']; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - data?: Record; - disabled?: boolean; - scrollOnEdge?: { - direction?: ScrollDirection; - getScrollElement?: () => HTMLElement | null; - edgeGap?: number | Partial; - }; -} - -export const useDraggable = ({ - type, - effectAllowed = 'copyMove', - data, - disabled, - scrollOnEdge, -}: UseDraggableOptions) => { - const scrollDirection = scrollOnEdge?.direction; - const edgeGap = scrollOnEdge?.edgeGap; - - const context = useContext(DndContext); - const typeRef = useRef(type); - const dataRef = useRef(data); - const previewRef = useRef(null); - const [isDragging, setIsDragging] = useState(false); - - typeRef.current = type; - dataRef.current = data; - - const setPreviewRef = useCallback((previewElement: null | Element) => { - previewRef.current = previewElement; - }, []); - - const attributes: { - draggable?: boolean; - } = useMemo(() => { - if (disabled) { - return {}; - } - - return { - draggable: true, - }; - }, [disabled]); - - const onDragStart = useCallback( - (event) => { - setIsDragging(true); - context.dragging = { - type: typeRef.current, - data: dataRef.current ?? {}, - }; - - const { dataTransfer } = event; - const previewNode = previewRef.current; - - dataTransfer.effectAllowed = effectAllowed; - - if (previewNode) { - const { clientX, clientY } = event; - const rect = previewNode.getBoundingClientRect(); - - dataTransfer.setDragImage(previewNode, clientX - rect.x, clientY - rect.y); - } - - if (scrollDirection === undefined) { - return; - } - - const scrollParent: HTMLElement | null = - scrollOnEdge?.getScrollElement?.() ?? getScrollParent(event.target as HTMLElement, scrollDirection); - - if (scrollParent) { - autoScrollOnEdge({ - element: scrollParent, - direction: scrollDirection, - edgeGap, - }); - } - }, - [context, effectAllowed, scrollDirection, scrollOnEdge, edgeGap] - ); - - const onDragEnd = useCallback(() => { - setIsDragging(false); - context.dragging = null; - }, [context]); - - const listeners: { - onDragStart?: DragEventHandler; - onDragEnd?: DragEventHandler; - } = useMemo( - () => ({ - onDragStart, - onDragEnd, - }), - [onDragStart, onDragEnd] - ); - - return { - isDragging, - previewRef, - attributes, - listeners, - setPreviewRef, - }; -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/_shared/dnd/drop.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/database/_shared/dnd/drop.hooks.ts deleted file mode 100644 index 7b3d79aeb2..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/_shared/dnd/drop.hooks.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { DragEventHandler, useContext, useState, useMemo, useCallback } from 'react'; -import { useSnapshot } from 'valtio'; -import { DragItem, DndContext } from './dnd.context'; - -interface UseDroppableOptions { - accept: string; - dropEffect?: DataTransfer['dropEffect']; - disabled?: boolean; - onDragOver?: DragEventHandler, - onDrop?: (data: DragItem) => void; -} - -export const useDroppable = ({ - accept, - dropEffect = 'move', - disabled, - onDragOver: handleDragOver, - onDrop: handleDrop, -}: UseDroppableOptions) => { - const dndContext = useContext(DndContext); - const dndSnapshot = useSnapshot(dndContext); - - const [ dragOver, setDragOver ] = useState(false); - const canDrop = useMemo( - () => !disabled && dndSnapshot.dragging?.type === accept, - [ disabled, accept, dndSnapshot.dragging?.type ], - ); - const isOver = useMemo(()=> canDrop && dragOver, [ canDrop, dragOver ]); - - const onDragEnter = useCallback((event) => { - if (!canDrop) { - return; - } - - event.preventDefault(); - event.dataTransfer.dropEffect = dropEffect; - - setDragOver(true); - }, [ canDrop, dropEffect ]); - - const onDragOver = useCallback((event) => { - if (!canDrop) { - return; - } - - event.preventDefault(); - event.dataTransfer.dropEffect = dropEffect; - - setDragOver(true); - handleDragOver?.(event); - }, [ canDrop, dropEffect, handleDragOver ]); - - const onDragLeave = useCallback(() => { - if (!canDrop) { - return; - } - - setDragOver(false); - }, [ canDrop ]); - - const onDrop = useCallback(() => { - if (!canDrop) { - return; - } - - const dragging = dndSnapshot.dragging; - - if (!dragging) { - return; - } - - setDragOver(false); - handleDrop?.(dragging); - }, [ canDrop, dndSnapshot.dragging, handleDrop ]); - - const listeners = useMemo(() => ({ - onDragEnter, - onDragOver, - onDragLeave, - onDrop, - }), [ onDragEnter, onDragOver, onDragLeave, onDrop ]); - - return { - isOver, - canDrop, - listeners, - }; -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/_shared/dnd/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/database/_shared/dnd/index.ts deleted file mode 100644 index 8688534359..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/_shared/dnd/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -export * from './dnd.context'; -export * from './drag.hooks'; -export * from './drop.hooks'; -export { - ScrollDirection, - Edge, -} from './utils'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/_shared/dnd/utils.ts b/frontend/appflowy_tauri/src/appflowy_app/components/database/_shared/dnd/utils.ts deleted file mode 100644 index 3aafa6f77c..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/_shared/dnd/utils.ts +++ /dev/null @@ -1,170 +0,0 @@ -import { interval } from '$app/utils/tool'; - -export enum Edge { - Top = 'top', - Bottom = 'bottom', - Left = 'left', - Right = 'right', -} - -export enum ScrollDirection { - Horizontal = 'horizontal', - Vertical = 'vertical', -} - -export interface EdgeGap { - top: number; - bottom: number; - left: number; - right: number; -} - -export const isReachEdge = (element: Element, edge: Edge) => { - switch (edge) { - case Edge.Left: - return element.scrollLeft === 0; - case Edge.Right: - return element.scrollLeft + element.clientWidth === element.scrollWidth; - case Edge.Top: - return element.scrollTop === 0; - case Edge.Bottom: - return element.scrollTop + element.clientHeight === element.scrollHeight; - default: - return true; - } -}; - -export const scrollBy = (element: Element, edge: Edge, offset: number) => { - let step = offset; - let prop = edge; - - if (edge === Edge.Left || edge === Edge.Top) { - step = -offset; - } else if (edge === Edge.Right) { - prop = Edge.Left; - } else if (edge === Edge.Bottom) { - prop = Edge.Top; - } - - element.scrollBy({ [prop]: step }); -}; - -export const scrollElement = (element: Element, edge: Edge, offset: number) => { - if (isReachEdge(element, edge)) { - return; - } - - scrollBy(element, edge, offset); -}; - -export const calculateLeaveEdge = ( - { x: mouseX, y: mouseY }: { x: number; y: number }, - rect: DOMRect, - gaps: EdgeGap, - direction: ScrollDirection -) => { - if (direction === ScrollDirection.Horizontal) { - if (mouseX - rect.left < gaps.left) { - return Edge.Left; - } - - if (rect.right - mouseX < gaps.right) { - return Edge.Right; - } - } - - if (direction === ScrollDirection.Vertical) { - if (mouseY - rect.top < gaps.top) { - return Edge.Top; - } - - if (rect.bottom - mouseY < gaps.bottom) { - return Edge.Bottom; - } - } - - return null; -}; - -export const getScrollParent = (element: HTMLElement | null, direction: ScrollDirection): HTMLElement | null => { - if (element === null) { - return null; - } - - if (direction === ScrollDirection.Horizontal && element.scrollWidth > element.clientWidth) { - return element; - } - - if (direction === ScrollDirection.Vertical && element.scrollHeight > element.clientHeight) { - return element; - } - - return getScrollParent(element.parentElement, direction); -}; - -export interface AutoScrollOnEdgeOptions { - element: HTMLElement; - direction: ScrollDirection; - edgeGap?: number | Partial; - step?: number; -} - -const defaultEdgeGap = 30; - -export const autoScrollOnEdge = ({ element, direction, edgeGap, step = 8 }: AutoScrollOnEdgeOptions) => { - const gaps = - typeof edgeGap === 'number' - ? { - top: edgeGap, - bottom: edgeGap, - left: edgeGap, - right: edgeGap, - } - : { - top: defaultEdgeGap, - bottom: defaultEdgeGap, - left: defaultEdgeGap, - right: defaultEdgeGap, - ...edgeGap, - }; - - const keepScroll = interval(scrollElement, 8); - - let leaveEdge: Edge | null = null; - - const onDragOver = (event: DragEvent) => { - const rect = element.getBoundingClientRect(); - - leaveEdge = calculateLeaveEdge({ x: event.clientX, y: event.clientY }, rect, gaps, direction); - - if (leaveEdge) { - keepScroll(element, leaveEdge, step); - } else { - keepScroll.cancel(); - } - }; - - const onDragLeave = () => { - if (!leaveEdge) { - return; - } - - keepScroll(element, leaveEdge, step * 2); - }; - - const cleanup = () => { - keepScroll.cancel(); - - element.removeEventListener('dragover', onDragOver); - element.removeEventListener('dragleave', onDragLeave); - - document.removeEventListener('dragend', cleanup); - }; - - element.addEventListener('dragover', onDragOver); - element.addEventListener('dragleave', onDragLeave); - - document.addEventListener('dragend', cleanup); - - return cleanup; -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/_shared/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/database/_shared/index.ts deleted file mode 100644 index 6bfa1f812b..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/_shared/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * from './constants'; - -export * from './dnd'; - -export * from './CellText'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/board/Board.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/board/Board.tsx deleted file mode 100644 index 790f841701..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/board/Board.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { FC } from 'react'; - -export const Board: FC = () => { - return null; -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/board/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/database/board/index.ts deleted file mode 100644 index 9294d869ce..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/board/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './Board'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/calendar/Calendar.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/calendar/Calendar.tsx deleted file mode 100644 index b8473fda25..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/calendar/Calendar.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { FC } from 'react'; - -export const Calendar: FC = () => { - return null; -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/calendar/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/database/calendar/index.ts deleted file mode 100644 index a723380592..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/calendar/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './Calendar'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/Cell.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/Cell.hooks.ts deleted file mode 100644 index 76bba7b152..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/Cell.hooks.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { useCallback, useEffect, useState } from 'react'; -import { DatabaseNotification } from '@/services/backend'; -import { useNotification, useViewId } from '$app/hooks'; -import { cellService, Cell, Field } from '$app/application/database'; -import { useDispatchCell, useSelectorCell } from '$app/components/database'; - -export const useCell = (rowId: string, field: Field) => { - const viewId = useViewId(); - const { setCell } = useDispatchCell(); - const [loading, setLoading] = useState(false); - const cell = useSelectorCell(rowId, field.id); - - const fetchCell = useCallback(() => { - setLoading(true); - void cellService.getCell(viewId, rowId, field.id, field.type).then((data) => { - // cache cell - setCell(data); - setLoading(false); - }); - }, [viewId, rowId, field.id, field.type, setCell]); - - useEffect(() => { - // fetch cell if not cached - if (!cell && !loading) { - // fetch cell in next tick to avoid blocking - const timeout = setTimeout(fetchCell, 0); - - return () => { - clearTimeout(timeout); - }; - } - }, [fetchCell, cell, loading, rowId, field.id]); - - useNotification(DatabaseNotification.DidUpdateCell, fetchCell, { id: `${rowId}:${field.id}` }); - - return cell; -}; - -export const useInputCell = (cell?: Cell) => { - const [editing, setEditing] = useState(false); - const [value, setValue] = useState(''); - const viewId = useViewId(); - const updateCell = useCallback(() => { - if (!cell) return; - const { rowId, fieldId } = cell; - - if (editing) { - if (value !== cell.data) { - void cellService.updateCell(viewId, rowId, fieldId, value); - } - - setEditing(false); - } - }, [cell, editing, value, viewId]); - - return { - updateCell, - editing, - setEditing, - value, - setValue, - }; -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/Cell.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/Cell.tsx deleted file mode 100644 index a092a1d75a..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/Cell.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import React, { FC, HTMLAttributes } from 'react'; -import { FieldType } from '@/services/backend'; - -import { Cell as CellType, Field } from '$app/application/database'; -import { useCell } from './Cell.hooks'; -import { TextCell } from './TextCell'; -import { SelectCell } from './SelectCell'; -import { CheckboxCell } from './CheckboxCell'; -import NumberCell from '$app/components/database/components/cell/NumberCell'; -import URLCell from '$app/components/database/components/cell/URLCell'; -import ChecklistCell from '$app/components/database/components/cell/ChecklistCell'; -import DateTimeCell from '$app/components/database/components/cell/DateTimeCell'; -import TimestampCell from '$app/components/database/components/cell/TimestampCell'; - -export interface CellProps extends HTMLAttributes { - rowId: string; - field: Field; - icon?: string; - placeholder?: string; -} - -export interface CellComponentProps extends CellProps { - cell: CellType; -} - -const getCellComponent = (fieldType: FieldType) => { - switch (fieldType) { - case FieldType.RichText: - return TextCell as FC; - case FieldType.SingleSelect: - case FieldType.MultiSelect: - return SelectCell as FC; - case FieldType.Checkbox: - return CheckboxCell as FC; - case FieldType.Checklist: - return ChecklistCell as FC; - case FieldType.Number: - return NumberCell as FC; - case FieldType.URL: - return URLCell as FC; - case FieldType.DateTime: - return DateTimeCell as FC; - case FieldType.LastEditedTime: - case FieldType.CreatedTime: - return TimestampCell as FC; - default: - return null; - } -}; - -export const Cell: FC = ({ rowId, field, ...props }) => { - const cell = useCell(rowId, field); - - const Component = getCellComponent(field.type); - - if (!cell) { - return
; - } - - if (!Component) { - return null; - } - - return ; -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/CheckboxCell.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/CheckboxCell.tsx deleted file mode 100644 index f6f3dcf0d2..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/CheckboxCell.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import React, { FC, useCallback } from 'react'; -import { ReactComponent as CheckboxCheckSvg } from '$app/assets/database/checkbox-check.svg'; -import { ReactComponent as CheckboxUncheckSvg } from '$app/assets/database/checkbox-uncheck.svg'; -import { useViewId } from '$app/hooks'; -import { cellService, CheckboxCell as CheckboxCellType, Field } from '$app/application/database'; - -export const CheckboxCell: FC<{ - field: Field; - cell: CheckboxCellType; -}> = ({ field, cell }) => { - const viewId = useViewId(); - const checked = cell.data; - - const handleClick = useCallback(() => { - void cellService.updateCell(viewId, cell.rowId, field.id, !checked ? 'Yes' : 'No'); - }, [viewId, cell, field.id, checked]); - - return ( -
- {checked ? : } -
- ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/ChecklistCell.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/ChecklistCell.tsx deleted file mode 100644 index 5ecac431c4..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/ChecklistCell.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import React, { useState, Suspense, useMemo } from 'react'; -import { ChecklistCell as ChecklistCellType, ChecklistField } from '$app/application/database'; -import ChecklistCellActions from '$app/components/database/components/field_types/checklist/ChecklistCellActions'; -import LinearProgressWithLabel from '$app/components/database/_shared/LinearProgressWithLabel'; -import { PopoverOrigin } from '@mui/material/Popover/Popover'; -import usePopoverAutoPosition from '$app/components/_shared/popover/Popover.hooks'; - -interface Props { - field: ChecklistField; - cell: ChecklistCellType; - placeholder?: string; -} - -const initialAnchorOrigin: PopoverOrigin = { - vertical: 'bottom', - horizontal: 'left', -}; - -const initialTransformOrigin: PopoverOrigin = { - vertical: 'top', - horizontal: 'left', -}; - -function ChecklistCell({ cell, placeholder }: Props) { - const value = cell?.data.percentage ?? 0; - const options = useMemo(() => cell?.data.options ?? [], [cell?.data.options]); - const selectedOptions = useMemo(() => cell?.data.selectedOptions ?? [], [cell?.data.selectedOptions]); - const [anchorEl, setAnchorEl] = useState(undefined); - const open = Boolean(anchorEl); - const handleClick = (e: React.MouseEvent) => { - setAnchorEl(e.currentTarget); - }; - - const handleClose = () => { - setAnchorEl(undefined); - }; - - const { paperHeight, paperWidth, transformOrigin, anchorOrigin, isEntered } = usePopoverAutoPosition({ - initialPaperWidth: 369, - initialPaperHeight: 300, - anchorEl, - initialAnchorOrigin, - initialTransformOrigin, - open, - }); - - return ( - <> -
- {options.length > 0 ? ( - - ) : ( -
{placeholder}
- )} -
- - {open && ( - - )} - - - ); -} - -export default ChecklistCell; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/DateTimeCell.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/DateTimeCell.tsx deleted file mode 100644 index aea4f79849..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/DateTimeCell.tsx +++ /dev/null @@ -1,125 +0,0 @@ -import React, { Suspense, useRef, useState, useMemo, useEffect } from 'react'; -import { DateTimeCell as DateTimeCellType, DateTimeField } from '$app/application/database'; -import DateTimeCellActions from '$app/components/database/components/field_types/date/DateTimeCellActions'; -import usePopoverAutoPosition from '$app/components/_shared/popover/Popover.hooks'; -import { PopoverOrigin } from '@mui/material/Popover/Popover'; - -interface Props { - field: DateTimeField; - cell: DateTimeCellType; - placeholder?: string; -} - -const initialAnchorOrigin: PopoverOrigin = { - vertical: 'bottom', - horizontal: 'left', -}; - -const initialTransformOrigin: PopoverOrigin = { - vertical: 'top', - horizontal: 'left', -}; - -function DateTimeCell({ field, cell, placeholder }: Props) { - const isRange = cell.data.isRange; - const includeTime = cell.data.includeTime; - const [open, setOpen] = useState(false); - const ref = useRef(null); - - const handleClose = () => { - setOpen(false); - }; - - const handleClick = () => { - setOpen(true); - }; - - const content = useMemo(() => { - const { date, time, endDate, endTime } = cell.data; - - if (date) { - return ( - <> - {date} - {includeTime && time ? ' ' + time : ''} - {isRange && endDate ? ' - ' + endDate : ''} - {isRange && includeTime && endTime ? ' ' + endTime : ''} - - ); - } - - return
{placeholder}
; - }, [cell, includeTime, isRange, placeholder]); - - const { paperHeight, paperWidth, transformOrigin, anchorOrigin, isEntered, calculateAnchorSize } = - usePopoverAutoPosition({ - initialPaperWidth: 248, - initialPaperHeight: 500, - anchorEl: ref.current, - initialAnchorOrigin, - initialTransformOrigin, - open, - marginThreshold: 34, - }); - - useEffect(() => { - if (!open) return; - - const anchorEl = ref.current; - - const parent = anchorEl?.parentElement?.parentElement; - - if (!anchorEl || !parent) return; - - let timeout: NodeJS.Timeout; - const handleObserve = () => { - anchorEl.scrollIntoView({ block: 'nearest' }); - - timeout = setTimeout(() => { - calculateAnchorSize(); - }, 200); - }; - - const observer = new MutationObserver(handleObserve); - - observer.observe(parent, { - childList: true, - subtree: true, - }); - return () => { - observer.disconnect(); - clearTimeout(timeout); - }; - }, [calculateAnchorSize, open]); - - return ( - <> -
- {content} -
- - {open && ( - - )} - - - ); -} - -export default DateTimeCell; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/NumberCell.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/NumberCell.tsx deleted file mode 100644 index 727e78de3f..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/NumberCell.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import React, { Suspense, useCallback, useMemo, useRef } from 'react'; -import { Field, NumberCell as NumberCellType } from '$app/application/database'; -import { CellText } from '$app/components/database/_shared'; -import EditNumberCellInput from '$app/components/database/components/field_types/number/EditNumberCellInput'; -import { useInputCell } from '$app/components/database/components/cell/Cell.hooks'; - -interface Props { - field: Field; - cell: NumberCellType; - placeholder?: string; -} - -function NumberCell({ field, cell, placeholder }: Props) { - const cellRef = useRef(null); - const { value, editing, updateCell, setEditing, setValue } = useInputCell(cell); - const content = useMemo(() => { - if (typeof cell.data === 'string' && cell.data) { - return cell.data; - } - - return
{placeholder}
; - }, [cell, placeholder]); - - const handleClick = useCallback(() => { - setValue(cell.data); - setEditing(true); - }, [cell, setEditing, setValue]); - - return ( - <> - -
{content}
-
- - {editing && ( - - )} - - - ); -} - -export default NumberCell; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/SelectCell.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/SelectCell.tsx deleted file mode 100644 index a951ddd9e4..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/SelectCell.tsx +++ /dev/null @@ -1,108 +0,0 @@ -import { FC, useCallback, useMemo, useState, Suspense, lazy } from 'react'; -import { MenuProps } from '@mui/material'; -import { SelectField, SelectCell as SelectCellType, SelectTypeOption } from '$app/application/database'; -import { Tag } from '../field_types/select/Tag'; -import { useTypeOption } from '$app/components/database'; -import usePopoverAutoPosition from '$app/components/_shared/popover/Popover.hooks'; -import { PopoverOrigin } from '@mui/material/Popover/Popover'; -import Popover from '@mui/material/Popover'; - -const initialAnchorOrigin: PopoverOrigin = { - vertical: 'bottom', - horizontal: 'center', -}; - -const initialTransformOrigin: PopoverOrigin = { - vertical: 'top', - horizontal: 'center', -}; -const SelectCellActions = lazy( - () => import('$app/components/database/components/field_types/select/select_cell_actions/SelectCellActions') -); -const menuProps: Partial = { - classes: { - list: 'py-5', - }, -}; - -export const SelectCell: FC<{ - field: SelectField; - cell: SelectCellType; - placeholder?: string; -}> = ({ field, cell, placeholder }) => { - const [anchorEl, setAnchorEl] = useState(null); - const selectedIds = useMemo(() => cell.data?.selectedOptionIds ?? [], [cell]); - const open = Boolean(anchorEl); - const handleClose = useCallback(() => { - setAnchorEl(null); - }, []); - - const typeOption = useTypeOption(field.id); - - const renderSelectedOptions = useCallback( - (selected: string[]) => - selected - .map((id) => typeOption.options?.find((option) => option.id === id)) - .map((option) => option && ), - [typeOption] - ); - - const { paperHeight, paperWidth, transformOrigin, anchorOrigin, isEntered } = usePopoverAutoPosition({ - initialPaperWidth: 369, - initialPaperHeight: 400, - anchorEl, - initialAnchorOrigin, - initialTransformOrigin, - open, - }); - - return ( -
-
{ - setAnchorEl(e.currentTarget); - }} - className={'flex h-full w-full cursor-pointer items-center gap-2 overflow-x-hidden px-2 py-1'} - > - {selectedIds.length === 0 ? ( -
{placeholder}
- ) : ( - renderSelectedOptions(selectedIds) - )} -
- - {open ? ( - { - const isInput = (e.target as Element).closest('input'); - - if (isInput) return; - - e.preventDefault(); - e.stopPropagation(); - }} - > - - - ) : null} - -
- ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/TextCell.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/TextCell.tsx deleted file mode 100644 index 38927d744b..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/TextCell.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import { FC, FormEventHandler, Suspense, lazy, useCallback, useRef, useMemo } from 'react'; -import { TextCell as TextCellType } from '$app/application/database'; -import { CellText } from '../../_shared'; -import { useInputCell } from '$app/components/database/components/cell/Cell.hooks'; - -const EditTextCellInput = lazy(() => import('$app/components/database/components/field_types/text/EditTextCellInput')); - -interface TextCellProps { - cell: TextCellType; - placeholder?: string; -} -export const TextCell: FC = ({ placeholder, cell }) => { - const cellRef = useRef(null); - const { value, editing, updateCell, setEditing, setValue } = useInputCell(cell); - const handleClose = () => { - if (!cell) return; - updateCell(); - }; - - const handleClick = useCallback(() => { - if (!cell) return; - setValue(cell.data); - setEditing(true); - }, [cell, setEditing, setValue]); - - const handleInput = useCallback>( - (event) => { - setValue((event.target as HTMLTextAreaElement).value); - }, - [setValue] - ); - - const content = useMemo(() => { - if (cell && typeof cell.data === 'string' && cell.data) { - return cell.data; - } - - return
{placeholder}
; - }, [cell, placeholder]); - - return ( - <> - - {content} - - - {editing && ( - - )} - - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/TimestampCell.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/TimestampCell.tsx deleted file mode 100644 index 5889a13915..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/TimestampCell.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; -import { CreatedTimeField, LastEditedTimeField, TimeStampCell } from '$app/application/database'; - -interface Props { - field: LastEditedTimeField | CreatedTimeField; - cell: TimeStampCell; -} - -function TimestampCell({ cell }: Props) { - return
{cell.data.dataTime}
; -} - -export default TimestampCell; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/URLCell.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/URLCell.tsx deleted file mode 100644 index 592850ada1..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/URLCell.tsx +++ /dev/null @@ -1,81 +0,0 @@ -import React, { FormEventHandler, lazy, Suspense, useCallback, useMemo, useRef } from 'react'; -import { useInputCell } from '$app/components/database/components/cell/Cell.hooks'; -import { Field, UrlCell as URLCellType } from '$app/application/database'; -import { CellText } from '$app/components/database/_shared'; -import { openUrl } from '$app/utils/open_url'; - -const EditTextCellInput = lazy(() => import('$app/components/database/components/field_types/text/EditTextCellInput')); - -interface Props { - field: Field; - cell: URLCellType; - placeholder?: string; -} - -function UrlCell({ field, cell, placeholder }: Props) { - const cellRef = useRef(null); - const { value, editing, updateCell, setEditing, setValue } = useInputCell(cell); - const handleClick = useCallback(() => { - setValue(cell.data); - setEditing(true); - }, [cell, setEditing, setValue]); - - const handleClose = () => { - updateCell(); - }; - - const handleInput = useCallback>( - (event) => { - setValue((event.target as HTMLTextAreaElement).value); - }, - [setValue] - ); - - const content = useMemo(() => { - if (cell.data) { - return ( - { - e.stopPropagation(); - openUrl(cell.data); - }} - target={'_blank'} - className={'cursor-pointer text-content-blue-400 underline'} - > - {cell.data} - - ); - } - - return
{placeholder}
; - }, [cell, placeholder]); - - return ( - <> - - {content} - - - {editing && ( - - )} - - - ); -} - -export default UrlCell; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/index.ts deleted file mode 100644 index 2440976340..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './Cell'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/database_settings/DatabaseCollection.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/database_settings/DatabaseCollection.tsx deleted file mode 100644 index 0fe9fb6d5b..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/database_settings/DatabaseCollection.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import { Sorts } from '../sort'; -import Filters from '../filter/Filters'; -import React from 'react'; - -interface Props { - open: boolean; -} - -export const DatabaseCollection = ({ open }: Props) => { - return ( -
-
- - -
-
- ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/database_settings/DatabaseSettings.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/database_settings/DatabaseSettings.tsx deleted file mode 100644 index ea1378eab8..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/database_settings/DatabaseSettings.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import React, { useState } from 'react'; -import { TextButton } from '$app/components/database/components/tab_bar/TextButton'; -import { useTranslation } from 'react-i18next'; - -import SortSettings from '$app/components/database/components/database_settings/SortSettings'; -import SettingsMenu from '$app/components/database/components/database_settings/SettingsMenu'; -import FilterSettings from '$app/components/database/components/database_settings/FilterSettings'; - -interface Props { - onToggleCollection: (forceOpen?: boolean) => void; -} - -function DatabaseSettings(props: Props) { - const { t } = useTranslation(); - const [settingAnchorEl, setSettingAnchorEl] = useState(null); - - return ( -
- - - setSettingAnchorEl(e.currentTarget)}> - {t('settings.title')} - - setSettingAnchorEl(null)} - /> -
- ); -} - -export default DatabaseSettings; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/database_settings/FilterSettings.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/database_settings/FilterSettings.tsx deleted file mode 100644 index d0b89208d0..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/database_settings/FilterSettings.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import React, { useState } from 'react'; -import { TextButton } from '$app/components/database/components/tab_bar/TextButton'; -import { useTranslation } from 'react-i18next'; -import { useFiltersCount } from '$app/components/database'; -import FilterFieldsMenu from '$app/components/database/components/filter/FilterFieldsMenu'; - -function FilterSettings({ onToggleCollection }: { onToggleCollection: (forceOpen?: boolean) => void }) { - const { t } = useTranslation(); - const filtersCount = useFiltersCount(); - const highlight = filtersCount > 0; - const [filterAnchorEl, setFilterAnchorEl] = useState(null); - const open = Boolean(filterAnchorEl); - - const handleClick = (e: React.MouseEvent) => { - if (highlight) { - onToggleCollection(); - return; - } - - setFilterAnchorEl(e.currentTarget); - }; - - return ( - <> - - {t('grid.settings.filter')} - - onToggleCollection(true)} - open={open} - anchorEl={filterAnchorEl} - onClose={() => setFilterAnchorEl(null)} - transformOrigin={{ - vertical: 'top', - horizontal: 'right', - }} - anchorOrigin={{ - vertical: 'bottom', - horizontal: 'right', - }} - /> - - ); -} - -export default FilterSettings; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/database_settings/Properties.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/database_settings/Properties.tsx deleted file mode 100644 index af2bbce218..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/database_settings/Properties.tsx +++ /dev/null @@ -1,113 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import { useDatabase } from '$app/components/database'; -import { Field as FieldType, fieldService } from '$app/application/database'; -import { Property } from '$app/components/database/components/property'; -import { FieldVisibility } from '@/services/backend'; -import { ReactComponent as EyeOpen } from '$app/assets/eye_open.svg'; -import { ReactComponent as EyeClosed } from '$app/assets/eye_close.svg'; -import { IconButton, MenuItem } from '@mui/material'; -import { DragDropContext, Draggable, Droppable, DropResult } from 'react-beautiful-dnd'; -import { useViewId } from '$app/hooks'; -import { ReactComponent as DragSvg } from '$app/assets/drag.svg'; - -interface PropertiesProps { - onItemClick: (field: FieldType) => void; -} -function Properties({ onItemClick }: PropertiesProps) { - const { fields } = useDatabase(); - const [state, setState] = useState(fields as FieldType[]); - const viewId = useViewId(); - const [menuPropertyId, setMenuPropertyId] = useState(); - - useEffect(() => { - setState(fields as FieldType[]); - }, [fields]); - - const handleOnDragEnd = async (result: DropResult) => { - const { destination, draggableId, source } = result; - const oldIndex = source.index; - const newIndex = destination?.index; - - if (oldIndex === newIndex) { - return; - } - - if (newIndex === undefined || newIndex === null) { - return; - } - - const newId = fields[newIndex ?? 0].id; - - const newProperties = fieldService.reorderFields(fields as FieldType[], oldIndex, newIndex ?? 0); - - setState(newProperties); - - await fieldService.moveField(viewId, draggableId, newId ?? ''); - }; - - return ( - - - {(dropProvided) => ( -
- {state.map((field, index) => ( - - {(provided) => { - return ( - { - setMenuPropertyId(field.id); - }} - key={field.id} - > - - - -
- { - setMenuPropertyId(undefined); - }} - menuOpened={menuPropertyId === field.id} - field={field} - /> -
- - { - e.stopPropagation(); - onItemClick(field); - }} - className={'ml-2'} - > - {field.visibility !== FieldVisibility.AlwaysHidden ? : } - -
- ); - }} -
- ))} - {dropProvided.placeholder} -
- )} -
-
- ); -} - -export default Properties; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/database_settings/SettingsMenu.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/database_settings/SettingsMenu.tsx deleted file mode 100644 index c6a9d244f0..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/database_settings/SettingsMenu.tsx +++ /dev/null @@ -1,98 +0,0 @@ -import React, { useCallback, useMemo, useRef, useState } from 'react'; -import { Menu, MenuProps, Popover } from '@mui/material'; -import { useTranslation } from 'react-i18next'; -import Properties from '$app/components/database/components/database_settings/Properties'; -import { Field } from '$app/application/database'; -import { FieldVisibility } from '@/services/backend'; -import { updateFieldSetting } from '$app/application/database/field/field_service'; -import { useViewId } from '$app/hooks'; -import KeyboardNavigation from '$app/components/_shared/keyboard_navigation/KeyboardNavigation'; - -type SettingsMenuProps = MenuProps; - -function SettingsMenu(props: SettingsMenuProps) { - const viewId = useViewId(); - const ref = useRef(null); - const { t } = useTranslation(); - const [propertiesAnchorElPosition, setPropertiesAnchorElPosition] = useState< - | undefined - | { - top: number; - left: number; - } - >(undefined); - - const openProperties = Boolean(propertiesAnchorElPosition); - - const togglePropertyVisibility = async (field: Field) => { - let visibility = field.visibility; - - if (visibility === FieldVisibility.AlwaysHidden) { - visibility = FieldVisibility.AlwaysShown; - } else { - visibility = FieldVisibility.AlwaysHidden; - } - - await updateFieldSetting(viewId, field.id, { - visibility, - }); - }; - - const options = useMemo(() => { - return [{ key: 'properties', content:
{t('grid.settings.properties')}
}]; - }, [t]); - - const onConfirm = useCallback( - (optionKey: string) => { - if (optionKey === 'properties') { - const target = ref.current?.querySelector(`[data-key=${optionKey}]`) as HTMLElement; - const rect = target.getBoundingClientRect(); - - setPropertiesAnchorElPosition({ - top: rect.top, - left: rect.left + rect.width, - }); - props.onClose?.({}, 'backdropClick'); - } - }, - [props] - ); - - return ( - <> - - { - props.onClose?.({}, 'escapeKeyDown'); - }} - options={options} - /> - - { - setPropertiesAnchorElPosition(undefined); - }} - anchorReference={'anchorPosition'} - anchorPosition={propertiesAnchorElPosition} - transformOrigin={{ - vertical: 'top', - horizontal: 'right', - }} - onKeyDown={(e) => { - if (e.key === 'Escape') { - e.preventDefault(); - e.stopPropagation(); - props.onClose?.({}, 'escapeKeyDown'); - } - }} - > - - - - ); -} - -export default SettingsMenu; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/database_settings/SortSettings.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/database_settings/SortSettings.tsx deleted file mode 100644 index 7f978120df..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/database_settings/SortSettings.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import React from 'react'; -import { useTranslation } from 'react-i18next'; -import { useDatabase } from '$app/components/database'; -import { TextButton } from '$app/components/database/components/tab_bar/TextButton'; -import SortFieldsMenu from '$app/components/database/components/sort/SortFieldsMenu'; - -interface Props { - onToggleCollection: (forceOpen?: boolean) => void; -} - -function SortSettings({ onToggleCollection }: Props) { - const { t } = useTranslation(); - const { sorts } = useDatabase(); - - const highlight = sorts && sorts.length > 0; - - const [sortAnchorEl, setSortAnchorEl] = React.useState(null); - const open = Boolean(sortAnchorEl); - const handleClick = (event: React.MouseEvent) => { - if (highlight) { - onToggleCollection(); - return; - } - - setSortAnchorEl(event.currentTarget); - }; - - const handleClose = () => { - setSortAnchorEl(null); - }; - - return ( - <> - - {t('grid.settings.sort')} - - onToggleCollection(true)} - open={open} - anchorEl={sortAnchorEl} - onClose={handleClose} - transformOrigin={{ - vertical: 'top', - horizontal: 'right', - }} - anchorOrigin={{ - vertical: 'bottom', - horizontal: 'right', - }} - /> - - ); -} - -export default SortSettings; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/database_settings/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/database_settings/index.ts deleted file mode 100644 index efb89af437..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/database_settings/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './DatabaseCollection'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/EditRecord.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/EditRecord.tsx deleted file mode 100644 index 13f29a7dfc..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/EditRecord.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import React, { useCallback, useEffect, useMemo, useState } from 'react'; -import RecordDocument from '$app/components/database/components/edit_record/RecordDocument'; -import RecordHeader from '$app/components/database/components/edit_record/RecordHeader'; -import { Page } from '$app_reducers/pages/slice'; -import { ErrorCode, ViewLayoutPB } from '@/services/backend'; -import { Log } from '$app/utils/log'; -import { useDatabase } from '$app/components/database'; -import { createOrphanPage, getPage } from '$app/application/folder/page.service'; - -interface Props { - rowId: string; -} - -function EditRecord({ rowId }: Props) { - const { rowMetas } = useDatabase(); - const row = useMemo(() => { - return rowMetas.find((row) => row.id === rowId); - }, [rowMetas, rowId]); - const [page, setPage] = useState(null); - const id = row?.documentId; - - const loadPage = useCallback(async () => { - if (!id) return; - - try { - const page = await getPage(id); - - setPage(page); - } catch (e) { - // Record not found - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - if (e.code === ErrorCode.RecordNotFound) { - try { - const page = await createOrphanPage({ - view_id: id, - name: '', - layout: ViewLayoutPB.Document, - }); - - setPage(page); - } catch (e) { - Log.error(e); - } - } - } - }, [id]); - - useEffect(() => { - void loadPage(); - }, [loadPage]); - - if (!id || !page) return null; - - return ( - <> - - - - ); -} - -export default EditRecord; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/ExpandRecordModal.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/ExpandRecordModal.tsx deleted file mode 100644 index 7056cd353d..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/ExpandRecordModal.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import React, { useState } from 'react'; -import { DialogProps, IconButton, Portal } from '@mui/material'; -import Dialog from '@mui/material/Dialog'; -import { ReactComponent as DetailsIcon } from '$app/assets/details.svg'; -import RecordActions from '$app/components/database/components/edit_record/RecordActions'; -import EditRecord from '$app/components/database/components/edit_record/EditRecord'; -import { AFScroller } from '$app/components/_shared/scroller'; - -interface Props extends DialogProps { - rowId: string; -} - -function ExpandRecordModal({ open, onClose, rowId }: Props) { - const [detailAnchorEl, setDetailAnchorEl] = useState(null); - - return ( - - e.stopPropagation()} - open={open} - onClose={onClose} - PaperProps={{ - className: 'h-[calc(100%-144px)] w-[80%] max-w-[960px] overflow-visible', - }} - > - - - - { - setDetailAnchorEl(e.currentTarget); - }} - > - - - - { - onClose?.({}, 'escapeKeyDown'); - }} - onClose={() => setDetailAnchorEl(null)} - /> - - ); -} - -export default ExpandRecordModal; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/RecordActions.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/RecordActions.tsx deleted file mode 100644 index 412c1a953e..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/RecordActions.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import React, { useCallback } from 'react'; -import { Icon, Menu, MenuProps } from '@mui/material'; -import { ReactComponent as DelSvg } from '$app/assets/delete.svg'; -import { ReactComponent as CopySvg } from '$app/assets/copy.svg'; -import { useTranslation } from 'react-i18next'; -import { rowService } from '$app/application/database'; -import { useViewId } from '$app/hooks'; -import MenuItem from '@mui/material/MenuItem'; - -interface Props extends MenuProps { - rowId: string; - onEscape?: () => void; - onClose?: () => void; -} -function RecordActions({ anchorEl, open, onEscape, onClose, rowId }: Props) { - const viewId = useViewId(); - const { t } = useTranslation(); - - const handleDelRow = useCallback(() => { - void rowService.deleteRow(viewId, rowId); - onEscape?.(); - }, [viewId, rowId, onEscape]); - - const handleDuplicateRow = useCallback(() => { - void rowService.duplicateRow(viewId, rowId); - onEscape?.(); - }, [viewId, rowId, onEscape]); - - const menuOptions = [ - { - label: t('grid.row.duplicate'), - icon: , - onClick: handleDuplicateRow, - }, - - { - label: t('grid.row.delete'), - icon: , - onClick: handleDelRow, - divider: true, - }, - ]; - - return ( - - {menuOptions.map((option) => ( - { - option.onClick(); - onClose?.(); - onEscape?.(); - }} - > - {option.icon} - {option.label} - - ))} - - ); -} - -export default RecordActions; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/RecordDocument.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/RecordDocument.tsx deleted file mode 100644 index 653f3c5944..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/RecordDocument.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import React from 'react'; -import Editor from '$app/components/editor/Editor'; - -interface Props { - documentId: string; -} - -function RecordDocument({ documentId }: Props) { - return ; -} - -export default RecordDocument; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/RecordHeader.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/RecordHeader.tsx deleted file mode 100644 index d2381ec165..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/RecordHeader.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import React, { useEffect, useRef } from 'react'; -import RecordTitle from '$app/components/database/components/edit_record/RecordTitle'; -import RecordProperties from '$app/components/database/components/edit_record/record_properties/RecordProperties'; -import { Divider } from '@mui/material'; -import { RowMeta } from '$app/application/database'; -import { Page } from '$app_reducers/pages/slice'; - -interface Props { - page: Page | null; - row: RowMeta; -} -function RecordHeader({ page, row }: Props) { - const ref = useRef(null); - - useEffect(() => { - const el = ref.current; - - if (!el) return; - - const preventSelectionTrigger = (e: MouseEvent) => { - e.stopPropagation(); - }; - - el.addEventListener('mousedown', preventSelectionTrigger); - return () => { - el.removeEventListener('mousedown', preventSelectionTrigger); - }; - }, []); - - return ( -
- - - -
- ); -} - -export default RecordHeader; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/RecordTitle.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/RecordTitle.tsx deleted file mode 100644 index c2f195aee2..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/RecordTitle.tsx +++ /dev/null @@ -1,71 +0,0 @@ -import React, { useCallback, useMemo } from 'react'; -import { Page, PageIcon } from '$app_reducers/pages/slice'; -import ViewTitle from '$app/components/_shared/view_title/ViewTitle'; -import { ViewIconTypePB } from '@/services/backend'; -import { useViewId } from '$app/hooks'; -import { updateRowMeta } from '$app/application/database/row/row_service'; -import { cellService, Field, RowMeta, TextCell } from '$app/application/database'; -import { useDatabase } from '$app/components/database'; -import { useCell } from '$app/components/database/components/cell/Cell.hooks'; - -interface Props { - page: Page | null; - row: RowMeta; -} - -function RecordTitle({ row, page }: Props) { - const { fields } = useDatabase(); - const field = useMemo(() => { - return fields.find((field) => field.isPrimary) as Field; - }, [fields]); - const rowId = row.id; - const cell = useCell(rowId, field) as TextCell; - const title = cell.data; - - const viewId = useViewId(); - - const onTitleChange = useCallback( - async (title: string) => { - try { - await cellService.updateCell(viewId, rowId, field.id, title); - } catch (e) { - // toast.error('Failed to update title'); - } - }, - [field.id, rowId, viewId] - ); - - const onUpdateIcon = useCallback( - async (icon: PageIcon) => { - try { - await updateRowMeta(viewId, rowId, { iconUrl: icon.value }); - } catch (e) { - // toast.error('Failed to update icon'); - } - }, - [rowId, viewId] - ); - - return ( -
- {page && ( - - )} -
- ); -} - -export default React.memo(RecordTitle); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/record_properties/Property.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/record_properties/Property.tsx deleted file mode 100644 index 279ee13f68..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/record_properties/Property.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import React, { HTMLAttributes } from 'react'; -import PropertyName from '$app/components/database/components/edit_record/record_properties/PropertyName'; -import PropertyValue from '$app/components/database/components/edit_record/record_properties/PropertyValue'; -import { Field } from '$app/application/database'; -import { IconButton, Tooltip } from '@mui/material'; -import { ReactComponent as DragSvg } from '$app/assets/drag.svg'; -import { useTranslation } from 'react-i18next'; - -interface Props extends HTMLAttributes { - field: Field; - rowId: string; - ishovered: boolean; - onHover: (id: string | null) => void; - menuOpened?: boolean; - onOpenMenu?: () => void; - onCloseMenu?: () => void; -} - -function Property( - { field, rowId, ishovered, onHover, menuOpened, onCloseMenu, onOpenMenu, ...props }: Props, - ref: React.ForwardedRef -) { - const { t } = useTranslation(); - - return ( - <> -
{ - onHover(field.id); - }} - onMouseLeave={() => { - onHover(null); - }} - className={'relative flex items-start gap-6 rounded hover:bg-content-blue-50'} - key={field.id} - {...props} - > - - - {ishovered && ( -
- - - - - -
- )} -
- - ); -} - -export default React.forwardRef(Property); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/record_properties/PropertyList.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/record_properties/PropertyList.tsx deleted file mode 100644 index 138c7543fd..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/record_properties/PropertyList.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import React, { HTMLAttributes, useState } from 'react'; -import { Field } from '$app/application/database'; -import Property from '$app/components/database/components/edit_record/record_properties/Property'; -import { Draggable } from 'react-beautiful-dnd'; - -interface Props extends HTMLAttributes { - properties: Field[]; - rowId: string; - placeholderNode?: React.ReactNode; - openMenuPropertyId?: string; - setOpenMenuPropertyId?: (id?: string) => void; -} - -function PropertyList( - { properties, rowId, placeholderNode, openMenuPropertyId, setOpenMenuPropertyId, ...props }: Props, - ref: React.ForwardedRef -) { - const [hoverId, setHoverId] = useState(null); - - return ( -
- {properties.map((field, index) => { - return ( - - {(provided) => { - return ( - { - setOpenMenuPropertyId?.(field.id); - }} - onCloseMenu={() => { - if (openMenuPropertyId === field.id) { - setOpenMenuPropertyId?.(undefined); - } - }} - /> - ); - }} - - ); - })} - {placeholderNode} -
- ); -} - -export default React.forwardRef(PropertyList); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/record_properties/PropertyName.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/record_properties/PropertyName.tsx deleted file mode 100644 index e7de3f1fb0..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/record_properties/PropertyName.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import React, { useRef } from 'react'; -import { Property } from '$app/components/database/components/property'; -import { Field as FieldType } from '$app/application/database'; - -interface Props { - field: FieldType; - menuOpened?: boolean; - onOpenMenu?: () => void; - onCloseMenu?: () => void; -} -function PropertyName({ field, menuOpened = false, onOpenMenu, onCloseMenu }: Props) { - const ref = useRef(null); - - return ( - <> -
{ - e.stopPropagation(); - e.preventDefault(); - onOpenMenu?.(); - }} - className={'flex min-h-[36px] w-[200px] cursor-pointer items-center'} - onClick={onOpenMenu} - > - -
- - ); -} - -export default PropertyName; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/record_properties/PropertyValue.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/record_properties/PropertyValue.tsx deleted file mode 100644 index 4bb33e7f05..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/record_properties/PropertyValue.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import React, { useEffect, useRef, useState } from 'react'; -import { Cell } from '$app/components/database/components'; -import { Field } from '$app/application/database'; -import { useTranslation } from 'react-i18next'; - -function PropertyValue(props: { rowId: string; field: Field }) { - const { t } = useTranslation(); - const ref = useRef(null); - const [width, setWidth] = useState(props.field.width); - - useEffect(() => { - const el = ref.current; - - if (!el) return; - const width = el.getBoundingClientRect().width; - - setWidth(width); - }, []); - return ( -
- -
- ); -} - -export default React.memo(PropertyValue); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/record_properties/RecordProperties.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/record_properties/RecordProperties.tsx deleted file mode 100644 index 16fc122615..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/record_properties/RecordProperties.tsx +++ /dev/null @@ -1,97 +0,0 @@ -import React, { useCallback, useEffect, useMemo, useState } from 'react'; -import { Field, fieldService, RowMeta } from '$app/application/database'; -import { useDatabase } from '$app/components/database'; -import { FieldVisibility } from '@/services/backend'; - -import PropertyList from '$app/components/database/components/edit_record/record_properties/PropertyList'; -import NewProperty from '$app/components/database/components/property/NewProperty'; -import { useViewId } from '$app/hooks'; -import { DragDropContext, Droppable, DropResult, OnDragEndResponder } from 'react-beautiful-dnd'; -import SwitchPropertiesVisible from '$app/components/database/components/edit_record/record_properties/SwitchPropertiesVisible'; - -interface Props { - row: RowMeta; -} - -function RecordProperties({ row }: Props) { - const viewId = useViewId(); - const { fields } = useDatabase(); - const fieldId = useMemo(() => { - return fields.find((field) => field.isPrimary)?.id; - }, [fields]); - const rowId = row.id; - const [openMenuPropertyId, setOpenMenuPropertyId] = useState(undefined); - const [showHiddenFields, setShowHiddenFields] = useState(false); - - const properties = useMemo(() => { - return fields.filter((field) => { - // exclude the current field, because it's already displayed in the title - // filter out hidden fields if the user doesn't want to see them - return field.id !== fieldId && (showHiddenFields || field.visibility !== FieldVisibility.AlwaysHidden); - }); - }, [fieldId, fields, showHiddenFields]); - - const hiddenFieldsCount = useMemo(() => { - return fields.filter((field) => { - return field.visibility === FieldVisibility.AlwaysHidden; - }).length; - }, [fields]); - - const [state, setState] = useState(properties); - - useEffect(() => { - setState(properties); - }, [properties]); - - // move the field in the state - const handleOnDragEnd: OnDragEndResponder = useCallback( - async (result: DropResult) => { - const { destination, draggableId, source } = result; - const newIndex = destination?.index; - const oldIndex = source.index; - - if (newIndex === undefined || newIndex === null) { - return; - } - - const newId = properties[newIndex ?? 0].id; - - // reorder the properties synchronously to avoid flickering - const newProperties = fieldService.reorderFields(properties, oldIndex, newIndex ?? 0); - - setState(newProperties); - - await fieldService.moveField(viewId, draggableId, newId); - }, - [properties, viewId] - ); - - return ( -
- - - {(dropProvided) => ( - - )} - - - - - -
- ); -} - -export default RecordProperties; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/record_properties/SwitchPropertiesVisible.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/record_properties/SwitchPropertiesVisible.tsx deleted file mode 100644 index f8937bbf21..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/record_properties/SwitchPropertiesVisible.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import React from 'react'; -import Button from '@mui/material/Button'; -import { useTranslation } from 'react-i18next'; -import { ReactComponent as EyeClosedSvg } from '$app/assets/eye_close.svg'; -import { ReactComponent as EyeOpenSvg } from '$app/assets/eye_open.svg'; - -function SwitchPropertiesVisible({ - hiddenFieldsCount, - showHiddenFields, - setShowHiddenFields, -}: { - hiddenFieldsCount: number; - showHiddenFields: boolean; - setShowHiddenFields: (showHiddenFields: boolean) => void; -}) { - const { t } = useTranslation(); - - return hiddenFieldsCount > 0 ? ( - - ) : null; -} - -export default SwitchPropertiesVisible; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/checklist/AddNewOption.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/checklist/AddNewOption.tsx deleted file mode 100644 index 027936d280..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/checklist/AddNewOption.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import React, { useState } from 'react'; -import { updateChecklistCell } from '$app/application/database/cell/cell_service'; -import { useViewId } from '$app/hooks'; -import { Button } from '@mui/material'; -import { useTranslation } from 'react-i18next'; - -function AddNewOption({ - rowId, - fieldId, - onClose, - onFocus, -}: { - rowId: string; - fieldId: string; - onClose: () => void; - onFocus: () => void; -}) { - const { t } = useTranslation(); - const [value, setValue] = useState(''); - const viewId = useViewId(); - const createOption = async () => { - await updateChecklistCell(viewId, rowId, fieldId, { - insertOptions: [value], - }); - setValue(''); - }; - - return ( -
- { - if (e.key === 'Enter') { - e.stopPropagation(); - e.preventDefault(); - void createOption(); - return; - } - - if (e.key === 'Escape') { - e.stopPropagation(); - e.preventDefault(); - onClose(); - return; - } - }} - value={value} - spellCheck={false} - onChange={(e) => { - setValue(e.target.value); - }} - /> - -
- ); -} - -export default AddNewOption; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/checklist/ChecklistCellActions.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/checklist/ChecklistCellActions.tsx deleted file mode 100644 index 61583c4746..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/checklist/ChecklistCellActions.tsx +++ /dev/null @@ -1,76 +0,0 @@ -import React, { useState } from 'react'; -import Popover, { PopoverProps } from '@mui/material/Popover'; -import { Divider } from '@mui/material'; -import { ChecklistCell as ChecklistCellType } from '$app/application/database'; -import ChecklistItem from '$app/components/database/components/field_types/checklist/ChecklistItem'; -import AddNewOption from '$app/components/database/components/field_types/checklist/AddNewOption'; -import LinearProgressWithLabel from '$app/components/database/_shared/LinearProgressWithLabel'; - -function ChecklistCellActions({ - cell, - maxHeight, - maxWidth, - ...props -}: PopoverProps & { - cell: ChecklistCellType; - maxWidth?: number; - maxHeight?: number; -}) { - const { fieldId, rowId } = cell; - const { percentage, selectedOptions = [], options = [] } = cell.data; - - const [focusedId, setFocusedId] = useState(null); - - return ( - -
- {options.length > 0 && ( - <> -
- -
-
- {options?.map((option) => { - return ( - setFocusedId(option.id)} - onClose={() => props.onClose?.({}, 'escapeKeyDown')} - checked={selectedOptions?.includes(option.id) || false} - /> - ); - })} -
- - - - )} - - { - setFocusedId(null); - }} - onClose={() => props.onClose?.({}, 'escapeKeyDown')} - fieldId={fieldId} - rowId={rowId} - /> -
-
- ); -} - -export default ChecklistCellActions; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/checklist/ChecklistItem.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/checklist/ChecklistItem.tsx deleted file mode 100644 index 5c6a55fa60..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/checklist/ChecklistItem.tsx +++ /dev/null @@ -1,117 +0,0 @@ -import React, { useCallback, useMemo, useState } from 'react'; -import { SelectOption } from '$app/application/database'; -import { IconButton } from '@mui/material'; -import { updateChecklistCell } from '$app/application/database/cell/cell_service'; -import { useViewId } from '$app/hooks'; -import { ReactComponent as DeleteIcon } from '$app/assets/delete.svg'; -import { ReactComponent as CheckboxCheckSvg } from '$app/assets/database/checkbox-check.svg'; -import { ReactComponent as CheckboxUncheckSvg } from '$app/assets/database/checkbox-uncheck.svg'; -import isHotkey from 'is-hotkey'; -import debounce from 'lodash-es/debounce'; -import { useTranslation } from 'react-i18next'; - -const DELAY_CHANGE = 200; - -function ChecklistItem({ - checked, - option, - rowId, - fieldId, - onClose, - isSelected, - onFocus, -}: { - checked: boolean; - option: SelectOption; - rowId: string; - fieldId: string; - onClose: () => void; - isSelected: boolean; - onFocus: () => void; -}) { - const inputRef = React.useRef(null); - const { t } = useTranslation(); - const [value, setValue] = useState(option.name); - const viewId = useViewId(); - const updateText = useCallback(async () => { - await updateChecklistCell(viewId, rowId, fieldId, { - updateOptions: [ - { - ...option, - name: value, - }, - ], - }); - }, [fieldId, option, rowId, value, viewId]); - - const onCheckedChange = useMemo(() => { - return debounce( - () => - updateChecklistCell(viewId, rowId, fieldId, { - selectedOptionIds: [option.id], - }), - DELAY_CHANGE - ); - }, [fieldId, option.id, rowId, viewId]); - - const deleteOption = useCallback(async () => { - await updateChecklistCell(viewId, rowId, fieldId, { - deleteOptionIds: [option.id], - }); - }, [fieldId, option.id, rowId, viewId]); - - return ( -
-
- {checked ? : } -
- - { - if (e.key === 'Escape') { - e.stopPropagation(); - e.preventDefault(); - void updateText(); - onClose(); - return; - } - - if (e.key === 'Enter') { - e.stopPropagation(); - e.preventDefault(); - void updateText(); - if (isHotkey('mod+enter', e)) { - void onCheckedChange(); - } - - return; - } - }} - spellCheck={false} - onChange={(e) => { - setValue(e.target.value); - }} - /> -
- - - -
-
- ); -} - -export default ChecklistItem; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/checklist/LinearProgressWithLabel.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/checklist/LinearProgressWithLabel.tsx deleted file mode 100644 index e8b9c95a44..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/checklist/LinearProgressWithLabel.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import * as React from 'react'; -import LinearProgress, { LinearProgressProps } from '@mui/material/LinearProgress'; -import Typography from '@mui/material/Typography'; -import Box from '@mui/material/Box'; - -export function LinearProgressWithLabel(props: LinearProgressProps & { value: number }) { - return ( - - - - - - {`${Math.round(props.value * 100)}%`} - - - ); -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/CustomCalendar.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/CustomCalendar.tsx deleted file mode 100644 index 4a36498bd2..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/CustomCalendar.tsx +++ /dev/null @@ -1,117 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import DatePicker, { ReactDatePickerCustomHeaderProps } from 'react-datepicker'; -import 'react-datepicker/dist/react-datepicker.css'; -import dayjs from 'dayjs'; -import { ReactComponent as LeftSvg } from '$app/assets/arrow-left.svg'; -import { ReactComponent as RightSvg } from '$app/assets/arrow-right.svg'; -import { IconButton } from '@mui/material'; -import './calendar.scss'; - -function CustomCalendar({ - handleChange, - isRange, - timestamp, - endTimestamp, -}: { - handleChange: (params: { date?: number; endDate?: number }) => void; - isRange: boolean; - timestamp?: number; - endTimestamp?: number; -}) { - const [startDate, setStartDate] = useState(() => { - if (!timestamp) return null; - return new Date(timestamp * 1000); - }); - const [endDate, setEndDate] = useState(() => { - if (!endTimestamp) return null; - return new Date(endTimestamp * 1000); - }); - - useEffect(() => { - if (!isRange || !endTimestamp) return; - setEndDate(new Date(endTimestamp * 1000)); - }, [isRange, endTimestamp]); - - useEffect(() => { - if (!timestamp) return; - setStartDate(new Date(timestamp * 1000)); - }, [timestamp]); - - return ( -
- { - return ( -
-
- {dayjs(props.date).format('MMMM YYYY')} -
- -
- - - - - - -
-
- ); - }} - selected={startDate} - onChange={(dates) => { - if (!dates) return; - if (isRange && Array.isArray(dates)) { - let start = dates[0] as Date; - let end = dates[1] as Date; - - if (!end && start && startDate && endDate) { - const currentTime = start.getTime(); - const startTimeStamp = startDate.getTime(); - const endTimeStamp = endDate.getTime(); - const isGreaterThanStart = currentTime > startTimeStamp; - const isGreaterThanEnd = currentTime > endTimeStamp; - const isLessThanStart = currentTime < startTimeStamp; - const isLessThanEnd = currentTime < endTimeStamp; - const isEqualsStart = currentTime === startTimeStamp; - const isEqualsEnd = currentTime === endTimeStamp; - - if ((isGreaterThanStart && isLessThanEnd) || isGreaterThanEnd) { - end = start; - start = startDate; - } else if (isEqualsStart || isEqualsEnd) { - end = start; - } else if (isLessThanStart) { - end = endDate; - } - } - - setStartDate(start); - setEndDate(end); - if (!start || !end) return; - handleChange({ - date: start.getTime() / 1000, - endDate: end.getTime() / 1000, - }); - } else { - const date = dates as Date; - - setStartDate(date); - handleChange({ - date: date.getTime() / 1000, - }); - } - }} - startDate={isRange ? startDate : null} - endDate={isRange ? endDate : null} - selectsRange={isRange} - inline - /> -
- ); -} - -export default CustomCalendar; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/DateFormat.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/DateFormat.tsx deleted file mode 100644 index fd5ba57889..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/DateFormat.tsx +++ /dev/null @@ -1,96 +0,0 @@ -import React, { useCallback, useMemo, useRef, useState } from 'react'; -import { useTranslation } from 'react-i18next'; -import { MenuItem, Menu } from '@mui/material'; -import { ReactComponent as MoreSvg } from '$app/assets/more.svg'; -import { ReactComponent as SelectCheckSvg } from '$app/assets/select-check.svg'; - -import { DateFormatPB } from '@/services/backend'; -import KeyboardNavigation, { - KeyboardNavigationOption, -} from '$app/components/_shared/keyboard_navigation/KeyboardNavigation'; - -interface Props { - value: DateFormatPB; - onChange: (value: DateFormatPB) => void; -} - -function DateFormat({ value, onChange }: Props) { - const { t } = useTranslation(); - const [open, setOpen] = useState(false); - const ref = useRef(null); - - const renderOptionContent = useCallback( - (option: DateFormatPB, title: string) => { - return ( -
-
{title}
- {value === option && } -
- ); - }, - [value] - ); - - const options: KeyboardNavigationOption[] = useMemo(() => { - return [ - { - key: DateFormatPB.Friendly, - content: renderOptionContent(DateFormatPB.Friendly, t('grid.field.dateFormatFriendly')), - }, - { - key: DateFormatPB.ISO, - content: renderOptionContent(DateFormatPB.ISO, t('grid.field.dateFormatISO')), - }, - { - key: DateFormatPB.US, - content: renderOptionContent(DateFormatPB.US, t('grid.field.dateFormatUS')), - }, - { - key: DateFormatPB.Local, - content: renderOptionContent(DateFormatPB.Local, t('grid.field.dateFormatLocal')), - }, - { - key: DateFormatPB.DayMonthYear, - content: renderOptionContent(DateFormatPB.DayMonthYear, t('grid.field.dateFormatDayMonthYear')), - }, - ]; - }, [renderOptionContent, t]); - - const handleClick = (option: DateFormatPB) => { - onChange(option); - setOpen(false); - }; - - return ( - <> - setOpen(true)} - > - {t('grid.field.dateFormat')} - - - setOpen(false)} - > - { - setOpen(false); - }} - disableFocus={true} - options={options} - onConfirm={handleClick} - /> - - - ); -} - -export default DateFormat; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/DateTimeCellActions.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/DateTimeCellActions.tsx deleted file mode 100644 index 78e3129d4f..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/DateTimeCellActions.tsx +++ /dev/null @@ -1,197 +0,0 @@ -import React, { useCallback, useMemo } from 'react'; -import Popover, { PopoverProps } from '@mui/material/Popover'; -import { DateTimeCell, DateTimeField, DateTimeTypeOption } from '$app/application/database'; -import { useViewId } from '$app/hooks'; -import { useTranslation } from 'react-i18next'; -import { updateDateCell } from '$app/application/database/cell/cell_service'; -import { Divider, MenuItem, MenuList } from '@mui/material'; -import dayjs from 'dayjs'; -import RangeSwitch from '$app/components/database/components/field_types/date/RangeSwitch'; -import CustomCalendar from '$app/components/database/components/field_types/date/CustomCalendar'; -import IncludeTimeSwitch from '$app/components/database/components/field_types/date/IncludeTimeSwitch'; -import DateTimeFormatSelect from '$app/components/database/components/field_types/date/DateTimeFormatSelect'; -import DateTimeSet from '$app/components/database/components/field_types/date/DateTimeSet'; -import { useTypeOption } from '$app/components/database'; -import { getDateFormat, getTimeFormat } from '$app/components/database/components/field_types/date/utils'; -import { notify } from '$app/components/_shared/notify'; - -function DateTimeCellActions({ - cell, - field, - maxWidth, - maxHeight, - ...props -}: PopoverProps & { - field: DateTimeField; - cell: DateTimeCell; - maxWidth?: number; - maxHeight?: number; -}) { - const typeOption = useTypeOption(field.id); - - const timeFormat = useMemo(() => { - return getTimeFormat(typeOption.timeFormat); - }, [typeOption.timeFormat]); - - const dateFormat = useMemo(() => { - return getDateFormat(typeOption.dateFormat); - }, [typeOption.dateFormat]); - - const { includeTime } = cell.data; - - const timestamp = useMemo(() => cell.data.timestamp || undefined, [cell.data.timestamp]); - const endTimestamp = useMemo(() => cell.data.endTimestamp || undefined, [cell.data.endTimestamp]); - const time = useMemo(() => cell.data.time || undefined, [cell.data.time]); - const endTime = useMemo(() => cell.data.endTime || undefined, [cell.data.endTime]); - - const viewId = useViewId(); - const { t } = useTranslation(); - - const handleChange = useCallback( - async (params: { - includeTime?: boolean; - date?: number; - endDate?: number; - time?: string; - endTime?: string; - isRange?: boolean; - clearFlag?: boolean; - }) => { - try { - const isRange = params.isRange ?? cell.data.isRange; - - const data = { - date: params.date ?? timestamp, - endDate: isRange ? params.endDate ?? endTimestamp : undefined, - time: params.time ?? time, - endTime: isRange ? params.endTime ?? endTime : undefined, - includeTime: params.includeTime ?? includeTime, - isRange, - clearFlag: params.clearFlag, - }; - - // if isRange and date is greater than endDate, swap date and endDate - if ( - data.isRange && - data.date && - data.endDate && - dayjs(dayjs.unix(data.date).format('YYYY/MM/DD ') + data.time).unix() > - dayjs(dayjs.unix(data.endDate).format('YYYY/MM/DD ') + data.endTime).unix() - ) { - if (params.date || params.time) { - data.endDate = data.date; - data.endTime = data.time; - } - - if (params.endDate || params.endTime) { - data.date = data.endDate; - data.time = data.endTime; - } - } - - await updateDateCell(viewId, cell.rowId, cell.fieldId, data); - } catch (e) { - notify.error(String(e)); - } - }, - [cell, endTime, endTimestamp, includeTime, time, timestamp, viewId] - ); - - const isRange = cell.data.isRange || false; - - return ( - { - if (e.key === 'Escape') { - e.preventDefault(); - e.stopPropagation(); - props.onClose?.({}, 'escapeKeyDown'); - } - }} - > -
- - - - - -
- { - void handleChange({ - isRange: val, - // reset endTime when isRange is changed - endTime: time, - endDate: timestamp, - }); - }} - checked={isRange} - /> - { - void handleChange({ - includeTime: val, - // reset time when includeTime is changed - time: val ? dayjs().format(timeFormat) : undefined, - endTime: val && isRange ? dayjs().format(timeFormat) : undefined, - }); - }} - checked={includeTime} - /> -
- - - - - - { - await handleChange({ - isRange: false, - includeTime: false, - }); - await handleChange({ - clearFlag: true, - }); - - props.onClose?.({}, 'backdropClick'); - }} - > - {t('grid.field.clearDate')} - - -
-
- ); -} - -export default DateTimeCellActions; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/DateTimeFieldActions.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/DateTimeFieldActions.tsx deleted file mode 100644 index 6c4b41a494..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/DateTimeFieldActions.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import React from 'react'; -import { UndeterminedDateField } from '$app/application/database'; -import DateTimeFormat from '$app/components/database/components/field_types/date/DateTimeFormat'; -import { Divider } from '@mui/material'; - -function DateTimeFieldActions({ field }: { field: UndeterminedDateField }) { - return ( - <> -
- -
- - - ); -} - -export default DateTimeFieldActions; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/DateTimeFormat.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/DateTimeFormat.tsx deleted file mode 100644 index 0107997c24..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/DateTimeFormat.tsx +++ /dev/null @@ -1,75 +0,0 @@ -import React, { useCallback } from 'react'; -import DateFormat from '$app/components/database/components/field_types/date/DateFormat'; -import TimeFormat from '$app/components/database/components/field_types/date/TimeFormat'; -import { TimeStampTypeOption, UndeterminedDateField, updateTypeOption } from '$app/application/database'; -import { DateFormatPB, FieldType, TimeFormatPB } from '@/services/backend'; -import { useViewId } from '$app/hooks'; -import Typography from '@mui/material/Typography'; -import { useTranslation } from 'react-i18next'; -import IncludeTimeSwitch from '$app/components/database/components/field_types/date/IncludeTimeSwitch'; -import { useTypeOption } from '$app/components/database'; - -interface Props { - field: UndeterminedDateField; - showLabel?: boolean; -} - -function DateTimeFormat({ field, showLabel = true }: Props) { - const viewId = useViewId(); - const { t } = useTranslation(); - const showIncludeTime = field.type === FieldType.CreatedTime || field.type === FieldType.LastEditedTime; - const typeOption = useTypeOption(field.id); - const { timeFormat = TimeFormatPB.TwentyFourHour, dateFormat = DateFormatPB.Friendly, includeTime } = typeOption; - const handleChange = useCallback( - async (params: { timeFormat?: TimeFormatPB; dateFormat?: DateFormatPB; includeTime?: boolean }) => { - try { - await updateTypeOption(viewId, field.id, field.type, { - timeFormat: params.timeFormat ?? timeFormat, - dateFormat: params.dateFormat ?? dateFormat, - includeTime: params.includeTime ?? includeTime, - fieldType: field.type, - }); - } catch (e) { - // toast.error(e.message); - } - }, - [dateFormat, field.id, field.type, includeTime, timeFormat, viewId] - ); - - return ( -
- {showLabel && ( - - {t('grid.field.format')} - - )} - - { - void handleChange({ dateFormat: val }); - }} - /> - { - void handleChange({ timeFormat: val }); - }} - /> - - {showIncludeTime && ( -
- { - void handleChange({ includeTime: checked }); - }} - /> -
- )} -
- ); -} - -export default DateTimeFormat; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/DateTimeFormatSelect.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/DateTimeFormatSelect.tsx deleted file mode 100644 index f0393139b0..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/DateTimeFormatSelect.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import React, { useState, useRef } from 'react'; -import { Menu, MenuItem } from '@mui/material'; -import { useTranslation } from 'react-i18next'; -import { DateTimeField } from '$app/application/database'; -import DateTimeFormat from '$app/components/database/components/field_types/date/DateTimeFormat'; -import { ReactComponent as MoreSvg } from '$app/assets/more.svg'; - -interface Props { - field: DateTimeField; -} - -function DateTimeFormatSelect({ field }: Props) { - const { t } = useTranslation(); - const [open, setOpen] = useState(false); - const ref = useRef(null); - - return ( - <> - setOpen(true)} className={'text-xs font-medium'}> -
- {t('grid.field.dateFormat')} & {t('grid.field.timeFormat')} -
- -
- { - if (e.key === 'Escape') { - e.stopPropagation(); - e.preventDefault(); - setOpen(false); - } - }} - onClose={() => setOpen(false)} - MenuListProps={{ - className: 'px-2', - }} - > - - - - ); -} - -export default DateTimeFormatSelect; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/DateTimeInput.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/DateTimeInput.tsx deleted file mode 100644 index 82080b7d25..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/DateTimeInput.tsx +++ /dev/null @@ -1,86 +0,0 @@ -import React, { useMemo } from 'react'; -import { DateField, TimeField } from '@mui/x-date-pickers-pro'; -import dayjs from 'dayjs'; -import { Divider } from '@mui/material'; -import debounce from 'lodash-es/debounce'; - -interface Props { - onChange: (params: { date?: number; time?: string }) => void; - date?: number; - time?: string; - timeFormat: string; - dateFormat: string; - includeTime?: boolean; -} - -const sx = { - '& .MuiOutlinedInput-notchedOutline': { - border: 'none', - }, - '& .MuiOutlinedInput-input': { - padding: '0', - }, -}; - -function DateTimeInput({ includeTime, dateFormat, timeFormat, ...props }: Props) { - const date = useMemo(() => { - return props.date ? dayjs.unix(props.date) : undefined; - }, [props.date]); - - const time = useMemo(() => { - return props.time ? dayjs(dayjs().format('YYYY/MM/DD ') + props.time) : undefined; - }, [props.time]); - - const debounceOnChange = useMemo(() => { - return debounce(props.onChange, 500); - }, [props.onChange]); - - return ( -
- { - if (!date) return; - debounceOnChange({ - date: date.unix(), - }); - }} - inputProps={{ - className: 'text-[12px]', - }} - format={dateFormat} - size={'small'} - sx={sx} - className={'flex-1 pl-2'} - /> - - {includeTime && ( - <> - - { - if (!time) return; - debounceOnChange({ - time: time.format(timeFormat), - }); - }} - format={timeFormat} - size={'small'} - sx={sx} - className={'w-[70px] pl-1'} - /> - - )} -
- ); -} - -export default DateTimeInput; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/DateTimeSet.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/DateTimeSet.tsx deleted file mode 100644 index 8e86b952d1..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/DateTimeSet.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import React from 'react'; -import { LocalizationProvider } from '@mui/x-date-pickers-pro'; -import { AdapterDayjs } from '@mui/x-date-pickers-pro/AdapterDayjs'; -import DateTimeInput from '$app/components/database/components/field_types/date/DateTimeInput'; - -interface Props { - onChange: (params: { date?: number; endDate?: number; time?: string; endTime?: string }) => void; - date?: number; - endDate?: number; - time?: string; - endTime?: string; - isRange?: boolean; - timeFormat: string; - dateFormat: string; - includeTime?: boolean; -} -function DateTimeSet({ onChange, date, endDate, time, endTime, isRange, timeFormat, dateFormat, includeTime }: Props) { - return ( -
- - { - onChange({ - date, - time, - }); - }} - date={date} - time={time} - timeFormat={timeFormat} - dateFormat={dateFormat} - includeTime={includeTime} - /> - {isRange && ( - { - onChange({ - endDate: date, - endTime: time, - }); - }} - timeFormat={timeFormat} - dateFormat={dateFormat} - includeTime={includeTime} - /> - )} - -
- ); -} - -export default DateTimeSet; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/IncludeTimeSwitch.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/IncludeTimeSwitch.tsx deleted file mode 100644 index f40e179ae4..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/IncludeTimeSwitch.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import React from 'react'; -import { Switch, SwitchProps } from '@mui/material'; -import { ReactComponent as TimeSvg } from '$app/assets/database/field-type-last-edited-time.svg'; -import Typography from '@mui/material/Typography'; -import { useTranslation } from 'react-i18next'; - -function IncludeTimeSwitch({ - checked, - onIncludeTimeChange, - ...props -}: SwitchProps & { - onIncludeTimeChange: (checked: boolean) => void; -}) { - const { t } = useTranslation(); - const handleChange = (event: React.ChangeEvent) => { - onIncludeTimeChange(event.target.checked); - }; - - return ( -
-
- - {t('grid.field.includeTime')} -
- -
- ); -} - -export default IncludeTimeSwitch; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/RangeSwitch.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/RangeSwitch.tsx deleted file mode 100644 index 76431af5fa..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/RangeSwitch.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import React from 'react'; -import { Switch, SwitchProps } from '@mui/material'; -import { useTranslation } from 'react-i18next'; -import Typography from '@mui/material/Typography'; -import { ReactComponent as DateSvg } from '$app/assets/database/field-type-date.svg'; - -function RangeSwitch({ - checked, - onIsRangeChange, - ...props -}: SwitchProps & { - onIsRangeChange: (checked: boolean) => void; -}) { - const { t } = useTranslation(); - const handleChange = (event: React.ChangeEvent) => { - onIsRangeChange(event.target.checked); - }; - - return ( -
-
- - {t('grid.field.isRange')} -
- -
- ); -} - -export default RangeSwitch; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/TimeFormat.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/TimeFormat.tsx deleted file mode 100644 index 89a9ad1756..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/TimeFormat.tsx +++ /dev/null @@ -1,82 +0,0 @@ -import React, { useCallback, useMemo, useRef, useState } from 'react'; -import { TimeFormatPB } from '@/services/backend'; -import { useTranslation } from 'react-i18next'; -import { Menu, MenuItem } from '@mui/material'; -import { ReactComponent as MoreSvg } from '$app/assets/more.svg'; -import { ReactComponent as SelectCheckSvg } from '$app/assets/select-check.svg'; -import KeyboardNavigation, { - KeyboardNavigationOption, -} from '$app/components/_shared/keyboard_navigation/KeyboardNavigation'; - -interface Props { - value: TimeFormatPB; - onChange: (value: TimeFormatPB) => void; -} -function TimeFormat({ value, onChange }: Props) { - const { t } = useTranslation(); - const [open, setOpen] = useState(false); - const ref = useRef(null); - - const renderOptionContent = useCallback( - (option: TimeFormatPB, title: string) => { - return ( -
-
{title}
- {value === option && } -
- ); - }, - [value] - ); - - const options: KeyboardNavigationOption[] = useMemo(() => { - return [ - { - key: TimeFormatPB.TwelveHour, - content: renderOptionContent(TimeFormatPB.TwelveHour, t('grid.field.timeFormatTwelveHour')), - }, - { - key: TimeFormatPB.TwentyFourHour, - content: renderOptionContent(TimeFormatPB.TwentyFourHour, t('grid.field.timeFormatTwentyFourHour')), - }, - ]; - }, [renderOptionContent, t]); - - const handleClick = (option: TimeFormatPB) => { - onChange(option); - setOpen(false); - }; - - return ( - <> - setOpen(true)} - > - {t('grid.field.timeFormat')} - - - setOpen(false)} - > - { - setOpen(false); - }} - disableFocus={true} - options={options} - onConfirm={handleClick} - /> - - - ); -} - -export default TimeFormat; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/calendar.scss b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/calendar.scss deleted file mode 100644 index 257467ed24..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/calendar.scss +++ /dev/null @@ -1,82 +0,0 @@ - -.react-datepicker__month-container { - width: 100%; - border-radius: 0; -} -.react-datepicker__header { - border-radius: 0; - background: transparent; - border-bottom: 0; - -} -.react-datepicker__day-names { - border: none; -} -.react-datepicker__day-name { - color: var(--text-caption); -} -.react-datepicker__month { - border: none; -} - -.react-datepicker__day { - border: none; - color: var(--text-title); - border-radius: 100%; -} -.react-datepicker__day:hover { - border-radius: 100%; - background: var(--fill-default); - color: var(--content-on-fill); -} -.react-datepicker__day--outside-month { - color: var(--text-caption); -} -.react-datepicker__day--in-range { - background: var(--fill-hover); - color: var(--content-on-fill); -} - - -.react-datepicker__day--today { - border: 1px solid var(--fill-default); - color: var(--text-title); - border-radius: 100%; - background: transparent; - font-weight: 500; - -} - -.react-datepicker__day--today:hover{ - background: var(--fill-default); - color: var(--content-on-fill); -} - -.react-datepicker__day--in-selecting-range, .react-datepicker__day--today.react-datepicker__day--in-range { - background: var(--fill-hover); - color: var(--content-on-fill); - border-color: transparent; -} - -.react-datepicker__day--keyboard-selected { - background: transparent; -} - - -.react-datepicker__day--range-start, .react-datepicker__day--range-end, .react-datepicker__day--selected { - &.react-datepicker__day--today { - background: var(--fill-default); - color: var(--content-on-fill); - } - background: var(--fill-default) !important; - color: var(--content-on-fill); -} - -.react-datepicker__day--range-start, .react-datepicker__day--range-end, .react-datepicker__day--selected:hover { - background: var(--fill-default); - color: var(--content-on-fill); -} - -.react-swipeable-view-container { - height: 100%; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/utils.ts b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/utils.ts deleted file mode 100644 index 129e84c4e7..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/utils.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { DateFormatPB, TimeFormatPB } from '@/services/backend'; - -export function getTimeFormat(timeFormat?: TimeFormatPB) { - switch (timeFormat) { - case TimeFormatPB.TwelveHour: - return 'h:mm A'; - case TimeFormatPB.TwentyFourHour: - return 'HH:mm'; - default: - return 'HH:mm'; - } -} - -export function getDateFormat(dateFormat?: DateFormatPB) { - switch (dateFormat) { - case DateFormatPB.Friendly: - return 'MMM DD, YYYY'; - case DateFormatPB.ISO: - return 'YYYY-MMM-DD'; - case DateFormatPB.US: - return 'YYYY/MMM/DD'; - case DateFormatPB.Local: - return 'MMM/DD/YYYY'; - case DateFormatPB.DayMonthYear: - return 'DD/MMM/YYYY'; - default: - return 'YYYY-MMM-DD'; - } -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/number/EditNumberCellInput.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/number/EditNumberCellInput.tsx deleted file mode 100644 index d2b538a7e1..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/number/EditNumberCellInput.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import React, { useCallback } from 'react'; -import { Popover } from '@mui/material'; -import InputBase from '@mui/material/InputBase'; - -function EditNumberCellInput({ - editing, - anchorEl, - width, - onClose, - value, - onChange, -}: { - editing: boolean; - anchorEl: HTMLDivElement | null; - width: number | undefined; - onClose: () => void; - value: string; - onChange: (value: string) => void; -}) { - const handleInput = (e: React.FormEvent) => { - const value = (e.target as HTMLInputElement).value; - - onChange(value); - }; - - const handleKeyDown = useCallback( - (event: React.KeyboardEvent) => { - if (event.key === 'Enter') { - onClose(); - } - }, - [onClose] - ); - - return ( - - - - ); -} - -export default EditNumberCellInput; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/number/NumberFieldActions.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/number/NumberFieldActions.tsx deleted file mode 100644 index eceb128804..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/number/NumberFieldActions.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import React, { useCallback } from 'react'; -import { NumberField, NumberTypeOption, updateTypeOption } from '$app/application/database'; -import { Divider } from '@mui/material'; -import { useTranslation } from 'react-i18next'; -import NumberFormatSelect from '$app/components/database/components/field_types/number/NumberFormatSelect'; -import { NumberFormatPB } from '@/services/backend'; -import { useViewId } from '$app/hooks'; -import { useTypeOption } from '$app/components/database'; - -function NumberFieldActions({ field }: { field: NumberField }) { - const viewId = useViewId(); - const { t } = useTranslation(); - const typeOption = useTypeOption(field.id); - const onChange = useCallback( - async (value: NumberFormatPB) => { - await updateTypeOption(viewId, field.id, field.type, { - format: value, - }); - }, - [field.id, field.type, viewId] - ); - - return ( - <> -
-
{t('grid.field.format')}
- -
- - - ); -} - -export default NumberFieldActions; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/number/NumberFormatMenu.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/number/NumberFormatMenu.tsx deleted file mode 100644 index 0f9be6a21a..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/number/NumberFormatMenu.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import React, { useCallback, useMemo, useRef } from 'react'; -import { NumberFormatPB } from '@/services/backend'; -import { Menu, MenuProps } from '@mui/material'; -import { formats, formatText } from '$app/components/database/components/field_types/number/const'; -import { ReactComponent as SelectCheckSvg } from '$app/assets/select-check.svg'; -import KeyboardNavigation, { - KeyboardNavigationOption, -} from '$app/components/_shared/keyboard_navigation/KeyboardNavigation'; - -function NumberFormatMenu({ - value, - onChangeFormat, - ...props -}: MenuProps & { - value: NumberFormatPB; - onChangeFormat: (value: NumberFormatPB) => void; -}) { - const scrollRef = useRef(null); - const onConfirm = useCallback( - (format: NumberFormatPB) => { - onChangeFormat(format); - props.onClose?.({}, 'backdropClick'); - }, - [onChangeFormat, props] - ); - - const renderContent = useCallback( - (format: NumberFormatPB) => { - return ( - <> - {formatText(format)} - {value === format && } - - ); - }, - [value] - ); - - const options: KeyboardNavigationOption[] = useMemo( - () => - formats.map((format) => ({ - key: format.value as NumberFormatPB, - content: renderContent(format.value as NumberFormatPB), - })), - [renderContent] - ); - - return ( - -
- props.onClose?.({}, 'escapeKeyDown')} - /> -
-
- ); -} - -export default NumberFormatMenu; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/number/NumberFormatSelect.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/number/NumberFormatSelect.tsx deleted file mode 100644 index 5a02c6759b..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/number/NumberFormatSelect.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import React, { useRef, useState } from 'react'; -import { MenuItem } from '@mui/material'; -import { NumberFormatPB } from '@/services/backend'; -import { ReactComponent as MoreSvg } from '$app/assets/more.svg'; -import { formatText } from '$app/components/database/components/field_types/number/const'; -import NumberFormatMenu from '$app/components/database/components/field_types/number/NumberFormatMenu'; - -function NumberFormatSelect({ value, onChange }: { value: NumberFormatPB; onChange: (value: NumberFormatPB) => void }) { - const ref = useRef(null); - const [expanded, setExpanded] = useState(false); - - return ( - <> - { - setExpanded(!expanded); - }} - className={'flex w-full justify-between rounded-none'} - > -
{formatText(value)}
- -
- setExpanded(false)} - onChangeFormat={onChange} - /> - - ); -} - -export default NumberFormatSelect; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/number/const.ts b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/number/const.ts deleted file mode 100644 index 38621cb114..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/number/const.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { NumberFormatPB } from '@/services/backend'; - -export const formats = Object.entries(NumberFormatPB) - .filter(([, value]) => typeof value !== 'string') - .map(([key, value]) => { - return { - key, - value, - }; - }); - -export const formatText = (format: NumberFormatPB) => { - return formats.find((item) => item.value === format)?.key; -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/SelectOptionModifyMenu.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/SelectOptionModifyMenu.tsx deleted file mode 100644 index 6c6cf37aae..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/SelectOptionModifyMenu.tsx +++ /dev/null @@ -1,165 +0,0 @@ -import { FC, useMemo, useRef, useState } from 'react'; -import { Divider, ListSubheader, MenuItem, MenuList, MenuProps, OutlinedInput } from '@mui/material'; -import { SelectOptionColorPB } from '@/services/backend'; -import { ReactComponent as DeleteSvg } from '$app/assets/delete.svg'; -import { ReactComponent as SelectCheckSvg } from '$app/assets/select-check.svg'; -import { SelectOption } from '$app/application/database'; -import { SelectOptionColorMap, SelectOptionColorTextMap } from './constants'; -import Button from '@mui/material/Button'; -import { - deleteSelectOption, - insertOrUpdateSelectOption, -} from '$app/application/database/field/select_option/select_option_service'; -import { useViewId } from '$app/hooks'; -import Popover from '@mui/material/Popover'; -import debounce from 'lodash-es/debounce'; -import { useTranslation } from 'react-i18next'; - -interface SelectOptionMenuProps { - fieldId: string; - option: SelectOption; - MenuProps: MenuProps; -} - -const Colors = [ - SelectOptionColorPB.Purple, - SelectOptionColorPB.Pink, - SelectOptionColorPB.LightPink, - SelectOptionColorPB.Orange, - SelectOptionColorPB.Yellow, - SelectOptionColorPB.Lime, - SelectOptionColorPB.Green, - SelectOptionColorPB.Aqua, - SelectOptionColorPB.Blue, -]; - -export const SelectOptionModifyMenu: FC = ({ fieldId, option, MenuProps: menuProps }) => { - const { t } = useTranslation(); - const [tagName, setTagName] = useState(option.name); - const viewId = useViewId(); - const inputRef = useRef(null); - const updateColor = async (color: SelectOptionColorPB) => { - await insertOrUpdateSelectOption(viewId, fieldId, [ - { - ...option, - color, - }, - ]); - }; - - const updateName = useMemo(() => { - return debounce(async (tagName) => { - if (tagName === option.name) return; - - await insertOrUpdateSelectOption(viewId, fieldId, [ - { - ...option, - name: tagName, - }, - ]); - }, 500); - }, [option, viewId, fieldId]); - - const onClose = () => { - menuProps.onClose?.({}, 'backdropClick'); - }; - - const deleteOption = async () => { - await deleteSelectOption(viewId, fieldId, [option]); - onClose(); - }; - - return ( - { - e.stopPropagation(); - }} - onClose={onClose} - onMouseDown={(e) => { - const isInput = inputRef.current?.contains(e.target as Node); - - if (isInput) return; - e.preventDefault(); - e.stopPropagation(); - }} - > - - { - setTagName(e.target.value); - void updateName(e.target.value); - }} - onKeyDown={(e) => { - if (e.key === 'Escape') { - e.preventDefault(); - e.stopPropagation(); - void updateName(tagName); - onClose(); - } - }} - onClick={(e) => { - e.stopPropagation(); - }} - onMouseDown={(e) => { - e.stopPropagation(); - }} - autoFocus={true} - placeholder={t('grid.selectOption.tagName')} - size='small' - /> - -
- -
- - - {t('grid.selectOption.colorPanelTitle')} - - {Colors.map((color) => ( - { - e.preventDefault(); - e.stopPropagation(); - }} - onClick={(e) => { - e.preventDefault(); - void updateColor(color); - }} - key={color} - value={color} - className={'px-1.5'} - > - - {t(`grid.selectOption.${SelectOptionColorTextMap[color]}`)} - {option.color === color && } - - ))} - -
- ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/Tag.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/Tag.tsx deleted file mode 100644 index 3e4677d57d..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/Tag.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import { FC } from 'react'; -import { Chip, ChipProps } from '@mui/material'; -import { SelectOptionColorPB } from '@/services/backend'; -import { SelectOptionColorMap } from './constants'; - -export interface TagProps extends Omit { - color?: SelectOptionColorPB | ChipProps['color']; -} - -export const Tag: FC = ({ color, classes, ...props }) => { - - return ( - - ) -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/constants.ts b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/constants.ts deleted file mode 100644 index 58a42f7dad..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/constants.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { SelectOptionColorPB } from '@/services/backend'; - -export const SelectOptionColorMap = { - [SelectOptionColorPB.Purple]: 'bg-tint-purple', - [SelectOptionColorPB.Pink]: 'bg-tint-pink', - [SelectOptionColorPB.LightPink]: 'bg-tint-red', - [SelectOptionColorPB.Orange]: 'bg-tint-orange', - [SelectOptionColorPB.Yellow]: 'bg-tint-yellow', - [SelectOptionColorPB.Lime]: 'bg-tint-lime', - [SelectOptionColorPB.Green]: 'bg-tint-green', - [SelectOptionColorPB.Aqua]: 'bg-tint-aqua', - [SelectOptionColorPB.Blue]: 'bg-tint-blue', -}; - -export const SelectOptionColorTextMap = { - [SelectOptionColorPB.Purple]: 'purpleColor', - [SelectOptionColorPB.Pink]: 'pinkColor', - [SelectOptionColorPB.LightPink]: 'lightPinkColor', - [SelectOptionColorPB.Orange]: 'orangeColor', - [SelectOptionColorPB.Yellow]: 'yellowColor', - [SelectOptionColorPB.Lime]: 'limeColor', - [SelectOptionColorPB.Green]: 'greenColor', - [SelectOptionColorPB.Aqua]: 'aquaColor', - [SelectOptionColorPB.Blue]: 'blueColor', -} as const; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/select_cell_actions/SearchInput.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/select_cell_actions/SearchInput.tsx deleted file mode 100644 index 5c8acb4759..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/select_cell_actions/SearchInput.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import React, { FormEvent, useCallback } from 'react'; -import { OutlinedInput } from '@mui/material'; -import { useTranslation } from 'react-i18next'; - -function SearchInput({ - setNewOptionName, - newOptionName, - inputRef, -}: { - newOptionName: string; - setNewOptionName: (value: string) => void; - inputRef?: React.RefObject; -}) { - const { t } = useTranslation(); - const handleInput = useCallback( - (event: FormEvent) => { - const value = (event.target as HTMLInputElement).value; - - setNewOptionName(value); - }, - [setNewOptionName] - ); - - return ( - - ); -} - -export default SearchInput; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/select_cell_actions/SelectCellActions.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/select_cell_actions/SelectCellActions.tsx deleted file mode 100644 index e2cd27019f..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/select_cell_actions/SelectCellActions.tsx +++ /dev/null @@ -1,162 +0,0 @@ -import React, { useCallback, useMemo, useRef, useState } from 'react'; -import { SelectOptionItem } from '$app/components/database/components/field_types/select/select_cell_actions/SelectOptionItem'; -import { cellService, SelectCell as SelectCellType, SelectField, SelectTypeOption } from '$app/application/database'; -import { useViewId } from '$app/hooks'; -import { - createSelectOption, - insertOrUpdateSelectOption, -} from '$app/application/database/field/select_option/select_option_service'; -import { FieldType } from '@/services/backend'; -import { useTypeOption } from '$app/components/database'; -import SearchInput from './SearchInput'; -import { useTranslation } from 'react-i18next'; -import KeyboardNavigation, { - KeyboardNavigationOption, -} from '$app/components/_shared/keyboard_navigation/KeyboardNavigation'; -import { Tag } from '$app/components/database/components/field_types/select/Tag'; - -const CREATE_OPTION_KEY = 'createOption'; - -function SelectCellActions({ - field, - cell, - onUpdated, - onClose, -}: { - field: SelectField; - cell: SelectCellType; - onUpdated?: () => void; - onClose?: () => void; -}) { - const { t } = useTranslation(); - const rowId = cell?.rowId; - const viewId = useViewId(); - const typeOption = useTypeOption(field.id); - const options = useMemo(() => typeOption.options ?? [], [typeOption.options]); - const scrollRef = useRef(null); - const inputRef = useRef(null); - const selectedOptionIds = useMemo(() => cell?.data?.selectedOptionIds ?? [], [cell]); - const [newOptionName, setNewOptionName] = useState(''); - - const filteredOptions: KeyboardNavigationOption[] = useMemo(() => { - const result = options - .filter((option) => { - return option.name.toLowerCase().includes(newOptionName.toLowerCase()); - }) - .map((option) => ({ - key: option.id, - content: ( - - ), - })); - - if (result.length === 0 && newOptionName) { - result.push({ - key: CREATE_OPTION_KEY, - content: , - }); - } - - return result; - }, [newOptionName, options, selectedOptionIds, cell?.fieldId]); - - const shouldCreateOption = filteredOptions.length === 1 && filteredOptions[0].key === 'createOption'; - - const updateCell = useCallback( - async (optionIds: string[]) => { - if (!cell || !rowId) return; - const deleteOptionIds = selectedOptionIds?.filter((id) => optionIds.find((cur) => cur === id) === undefined); - - await cellService.updateSelectCell(viewId, rowId, field.id, { - insertOptionIds: optionIds, - deleteOptionIds, - }); - onUpdated?.(); - }, - [cell, field.id, onUpdated, rowId, selectedOptionIds, viewId] - ); - - const createOption = useCallback(async () => { - const option = await createSelectOption(viewId, field.id, newOptionName); - - if (!option) return; - await insertOrUpdateSelectOption(viewId, field.id, [option]); - setNewOptionName(''); - return option; - }, [viewId, field.id, newOptionName]); - - const onConfirm = useCallback( - async (key: string) => { - let optionId = key; - - if (key === CREATE_OPTION_KEY) { - const option = await createOption(); - - optionId = option?.id || ''; - } - - if (!optionId) return; - - if (field.type === FieldType.SingleSelect) { - const newOptionIds = [optionId]; - - if (selectedOptionIds?.includes(optionId)) { - newOptionIds.pop(); - } - - void updateCell(newOptionIds); - return; - } - - let newOptionIds = []; - - if (!selectedOptionIds) { - newOptionIds.push(optionId); - } else { - const isSelected = selectedOptionIds.includes(optionId); - - if (isSelected) { - newOptionIds = selectedOptionIds.filter((id) => id !== optionId); - } else { - newOptionIds = [...selectedOptionIds, optionId]; - } - } - - void updateCell(newOptionIds); - }, - [createOption, field.type, selectedOptionIds, updateCell] - ); - - return ( -
- - - {filteredOptions.length > 0 && ( -
- {shouldCreateOption ? t('grid.selectOption.createNew') : t('grid.selectOption.orSelectOne')} -
- )} - -
- null} - /> -
-
- ); -} - -export default SelectCellActions; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/select_cell_actions/SelectOptionItem.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/select_cell_actions/SelectOptionItem.tsx deleted file mode 100644 index 2a855a4085..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/select_cell_actions/SelectOptionItem.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import { FC, MouseEventHandler, useCallback, useRef, useState } from 'react'; -import { IconButton } from '@mui/material'; -import { ReactComponent as DetailsSvg } from '$app/assets/details.svg'; -import { SelectOption } from '$app/application/database'; -import { SelectOptionModifyMenu } from '../SelectOptionModifyMenu'; -import { Tag } from '../Tag'; -import { ReactComponent as SelectCheckSvg } from '$app/assets/select-check.svg'; - -export interface SelectOptionItemProps { - option: SelectOption; - fieldId: string; - isSelected?: boolean; -} - -export const SelectOptionItem: FC = ({ isSelected, fieldId, option }) => { - const [open, setOpen] = useState(false); - const anchorEl = useRef(null); - const [hovered, setHovered] = useState(false); - const handleClick = useCallback>((event) => { - event.stopPropagation(); - setOpen(true); - }, []); - - return ( - <> -
setHovered(true)} - onMouseLeave={() => setHovered(false)} - > -
- -
- {isSelected && !hovered && } - {hovered && ( - - - - )} -
- {open && ( - setOpen(false), - }} - /> - )} - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/select_field_actions/AddAnOption.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/select_field_actions/AddAnOption.tsx deleted file mode 100644 index 0fb180bb08..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/select_field_actions/AddAnOption.tsx +++ /dev/null @@ -1,76 +0,0 @@ -import React, { useMemo, useState } from 'react'; -import Button from '@mui/material/Button'; -import { useTranslation } from 'react-i18next'; -import { ReactComponent as AddSvg } from '$app/assets/add.svg'; -import { OutlinedInput } from '@mui/material'; -import { - createSelectOption, - insertOrUpdateSelectOption, -} from '$app/application/database/field/select_option/select_option_service'; -import { useViewId } from '$app/hooks'; -import { SelectOption } from '$app/application/database'; -import { notify } from '$app/components/_shared/notify'; - -function AddAnOption({ fieldId, options }: { fieldId: string; options: SelectOption[] }) { - const viewId = useViewId(); - const { t } = useTranslation(); - const [edit, setEdit] = useState(false); - const [newOptionName, setNewOptionName] = useState(''); - const exitEdit = () => { - setNewOptionName(''); - setEdit(false); - }; - - const isOptionExist = useMemo(() => { - return options.some((option) => option.name === newOptionName); - }, [options, newOptionName]); - - const createOption = async () => { - if (!newOptionName) return; - if (isOptionExist) { - notify.error(t('grid.field.optionAlreadyExist')); - return; - } - - const option = await createSelectOption(viewId, fieldId, newOptionName); - - if (!option) return; - await insertOrUpdateSelectOption(viewId, fieldId, [option]); - setNewOptionName(''); - }; - - return edit ? ( - { - setNewOptionName(e.target.value); - }} - value={newOptionName} - onKeyDown={(e) => { - if (e.key === 'Enter') { - e.stopPropagation(); - e.preventDefault(); - void createOption(); - return; - } - - if (e.key === 'Escape') { - e.stopPropagation(); - e.preventDefault(); - exitEdit(); - } - }} - className={'mx-2 mb-1'} - placeholder={t('grid.selectOption.typeANewOption')} - size='small' - /> - ) : ( - - ); -} - -export default AddAnOption; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/select_field_actions/Option.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/select_field_actions/Option.tsx deleted file mode 100644 index ad363d4a1d..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/select_field_actions/Option.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import React, { useRef, useState } from 'react'; -import { ReactComponent as MoreIcon } from '$app/assets/more.svg'; -import { SelectOption } from '$app/application/database'; -// import { ReactComponent as DragIcon } from '$app/assets/drag.svg'; - -import { SelectOptionModifyMenu } from '$app/components/database/components/field_types/select/SelectOptionModifyMenu'; -import Button from '@mui/material/Button'; -import { SelectOptionColorMap } from '$app/components/database/components/field_types/select/constants'; - -function Option({ option, fieldId }: { option: SelectOption; fieldId: string }) { - const [expanded, setExpanded] = useState(false); - const ref = useRef(null); - - return ( - <> - - setExpanded(false), - open: expanded, - transformOrigin: { - vertical: 'center', - horizontal: 'left', - }, - anchorOrigin: { vertical: 'center', horizontal: 'right' }, - }} - option={option} - /> - - ); -} - -export default Option; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/select_field_actions/Options.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/select_field_actions/Options.tsx deleted file mode 100644 index 4e06236263..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/select_field_actions/Options.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import React from 'react'; -import { SelectOption } from '$app/application/database'; -import Option from './Option'; - -interface Props { - options: SelectOption[]; - fieldId: string; -} -function Options({ options, fieldId }: Props) { - return ( -
- {options.map((option) => { - return
- ); -} - -export default Options; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/select_field_actions/SelectFieldActions.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/select_field_actions/SelectFieldActions.tsx deleted file mode 100644 index a3d51ceb60..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/select_field_actions/SelectFieldActions.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import React, { useMemo } from 'react'; -import { useTranslation } from 'react-i18next'; -import AddAnOption from '$app/components/database/components/field_types/select/select_field_actions/AddAnOption'; -import Options from '$app/components/database/components/field_types/select/select_field_actions/Options'; -import { SelectField, SelectTypeOption } from '$app/application/database'; -import { Divider } from '@mui/material'; -import { useTypeOption } from '$app/components/database'; - -function SelectFieldActions({ field }: { field: SelectField }) { - const typeOption = useTypeOption(field.id); - const options = useMemo(() => typeOption.options ?? [], [typeOption.options]); - const { t } = useTranslation(); - - return ( - <> -
-
{t('grid.field.optionTitle')}
- - -
- - - ); -} - -export default SelectFieldActions; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/text/EditTextCellInput.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/text/EditTextCellInput.tsx deleted file mode 100644 index 005d185c8f..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/text/EditTextCellInput.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import React, { useCallback } from 'react'; -import { Popover, TextareaAutosize } from '@mui/material'; - -interface Props { - editing: boolean; - anchorEl: HTMLDivElement | null; - onClose: () => void; - text: string; - onInput: (event: React.FormEvent) => void; -} - -function EditTextCellInput({ editing, anchorEl, onClose, text, onInput }: Props) { - const handleEnter = (e: React.KeyboardEvent) => { - const shift = e.shiftKey; - - // If shift is pressed, allow the user to enter a new line, otherwise close the popover - if (!shift && e.key === 'Enter') { - e.preventDefault(); - e.stopPropagation(); - onClose(); - } - }; - - const setRef = useCallback((e: HTMLTextAreaElement | null) => { - if (!e) return; - const selectionStart = e.value.length; - - e.setSelectionRange(selectionStart, selectionStart); - }, []); - - return ( - { - if (e.key === 'Escape') { - e.stopPropagation(); - e.preventDefault(); - onClose(); - } - }} - > - - - ); -} - -export default EditTextCellInput; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/ConditionSelect.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/ConditionSelect.tsx deleted file mode 100644 index 0ca6c42a86..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/ConditionSelect.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import React, { useCallback, useMemo, useState } from 'react'; -import KeyboardNavigation, { - KeyboardNavigationOption, -} from '$app/components/_shared/keyboard_navigation/KeyboardNavigation'; -import { ReactComponent as MoreSvg } from '$app/assets/more.svg'; -import Popover from '@mui/material/Popover'; - -function ConditionSelect({ - conditions, - value, - onChange, -}: { - conditions: { - value: number; - text: string; - }[]; - value: number; - onChange: (condition: number) => void; -}) { - const [anchorEl, setAnchorEl] = useState(null); - const options: KeyboardNavigationOption[] = useMemo(() => { - return conditions.map((condition) => { - return { - key: condition.value, - content: condition.text, - }; - }); - }, [conditions]); - - const handleClose = useCallback(() => { - setAnchorEl(null); - }, []); - - const onConfirm = useCallback( - (key: number) => { - onChange(key); - }, - [onChange] - ); - - const valueText = useMemo(() => { - return conditions.find((condition) => condition.value === value)?.text; - }, [conditions, value]); - - const open = Boolean(anchorEl); - - return ( -
-
{ - setAnchorEl(e.currentTarget); - }} - className={'flex cursor-pointer select-none items-center gap-2 py-2 text-xs'} - > -
{valueText}
- -
- - - -
- ); -} - -export default ConditionSelect; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/Filter.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/Filter.tsx deleted file mode 100644 index fdd7bccb5b..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/Filter.tsx +++ /dev/null @@ -1,200 +0,0 @@ -import React, { FC, useMemo, useState } from 'react'; -import { - CheckboxFilterData, - ChecklistFilterData, - DateFilterData, - Field as FieldData, - Filter as FilterType, - NumberFilterData, - SelectFilterData, - TextFilterData, - UndeterminedFilter, -} from '$app/application/database'; -import { Chip, Popover } from '@mui/material'; -import { Property } from '$app/components/database/components/property'; -import { ReactComponent as DropDownSvg } from '$app/assets/dropdown.svg'; -import TextFilter from './text_filter/TextFilter'; -import { CheckboxFilterConditionPB, ChecklistFilterConditionPB, FieldType } from '@/services/backend'; -import FilterActions from '$app/components/database/components/filter/FilterActions'; -import { updateFilter } from '$app/application/database/filter/filter_service'; -import { useViewId } from '$app/hooks'; -import SelectFilter from './select_filter/SelectFilter'; - -import DateFilter from '$app/components/database/components/filter/date_filter/DateFilter'; -import FilterConditionSelect from '$app/components/database/components/filter/FilterConditionSelect'; -import TextFilterValue from '$app/components/database/components/filter/text_filter/TextFilterValue'; -import SelectFilterValue from '$app/components/database/components/filter/select_filter/SelectFilterValue'; -import NumberFilterValue from '$app/components/database/components/filter/number_filter/NumberFilterValue'; -import { useTranslation } from 'react-i18next'; -import DateFilterValue from '$app/components/database/components/filter/date_filter/DateFilterValue'; - -interface Props { - filter: FilterType; - field: FieldData; -} - -interface FilterComponentProps { - filter: FilterType; - field: FieldData; - onChange: (data: UndeterminedFilter['data']) => void; - onClose?: () => void; -} - -type FilterComponent = FC; -const getFilterComponent = (field: FieldData) => { - switch (field.type) { - case FieldType.RichText: - case FieldType.URL: - case FieldType.Number: - return TextFilter as FilterComponent; - case FieldType.SingleSelect: - case FieldType.MultiSelect: - return SelectFilter as FilterComponent; - - case FieldType.DateTime: - case FieldType.LastEditedTime: - case FieldType.CreatedTime: - return DateFilter as FilterComponent; - default: - return null; - } -}; - -function Filter({ filter, field }: Props) { - const viewId = useViewId(); - const { t } = useTranslation(); - const [anchorEl, setAnchorEl] = useState(null); - const open = Boolean(anchorEl); - const handleClick = (e: React.MouseEvent) => { - setAnchorEl(e.currentTarget); - }; - - const handleClose = () => { - setAnchorEl(null); - }; - - const onDataChange = async (data: UndeterminedFilter['data']) => { - const newFilter = { - ...filter, - data: { - ...(filter.data || {}), - ...data, - }, - } as UndeterminedFilter; - - try { - await updateFilter(viewId, newFilter); - } catch (e) { - // toast.error(e.message); - } - }; - - const Component = getFilterComponent(field); - - const condition = useMemo(() => { - switch (field.type) { - case FieldType.RichText: - case FieldType.URL: - return (filter.data as TextFilterData).condition; - case FieldType.SingleSelect: - case FieldType.MultiSelect: - return (filter.data as SelectFilterData).condition; - case FieldType.Number: - return (filter.data as NumberFilterData).condition; - case FieldType.Checkbox: - return (filter.data as CheckboxFilterData).condition; - case FieldType.Checklist: - return (filter.data as ChecklistFilterData).condition; - case FieldType.DateTime: - case FieldType.LastEditedTime: - case FieldType.CreatedTime: - return (filter.data as DateFilterData).condition; - default: - return; - } - }, [field, filter]); - - const conditionValue = useMemo(() => { - switch (field.type) { - case FieldType.RichText: - case FieldType.URL: - return ; - case FieldType.SingleSelect: - case FieldType.MultiSelect: - return ; - case FieldType.Number: - return ; - case FieldType.Checkbox: - return (filter.data as CheckboxFilterData).condition === CheckboxFilterConditionPB.IsChecked - ? t('grid.checkboxFilter.isChecked') - : t('grid.checkboxFilter.isUnchecked'); - case FieldType.Checklist: - return (filter.data as ChecklistFilterData).condition === ChecklistFilterConditionPB.IsComplete - ? t('grid.checklistFilter.isComplete') - : t('grid.checklistFilter.isIncomplted'); - case FieldType.DateTime: - case FieldType.LastEditedTime: - case FieldType.CreatedTime: - return ; - default: - return ''; - } - }, [field.id, field.type, filter.data, t]); - - return ( - <> - - - {conditionValue} - -
- } - onClick={handleClick} - /> - {condition !== undefined && open && ( - { - if (e.key === 'Escape') { - e.preventDefault(); - e.stopPropagation(); - handleClose(); - } - }} - > -
- { - void onDataChange({ - condition, - }); - }} - /> - -
- {Component && } -
- )} - - ); -} - -export default Filter; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/FilterActions.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/FilterActions.tsx deleted file mode 100644 index ebc9e8982c..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/FilterActions.tsx +++ /dev/null @@ -1,84 +0,0 @@ -import React, { useMemo, useState } from 'react'; -import { IconButton, Menu } from '@mui/material'; -import { ReactComponent as MoreSvg } from '$app/assets/details.svg'; -import { Filter } from '$app/application/database'; -import { useTranslation } from 'react-i18next'; -import { deleteFilter } from '$app/application/database/filter/filter_service'; -import { useViewId } from '$app/hooks'; -import KeyboardNavigation, { - KeyboardNavigationOption, -} from '$app/components/_shared/keyboard_navigation/KeyboardNavigation'; - -function FilterActions({ filter }: { filter: Filter }) { - const viewId = useViewId(); - const { t } = useTranslation(); - const [disableSelect, setDisableSelect] = useState(true); - const [anchorEl, setAnchorEl] = useState(null); - const open = Boolean(anchorEl); - const onClose = () => { - setDisableSelect(true); - setAnchorEl(null); - }; - - const onDelete = async () => { - try { - await deleteFilter(viewId, filter); - } catch (e) { - // toast.error(e.message); - } - - setDisableSelect(true); - }; - - const options: KeyboardNavigationOption[] = useMemo( - () => [ - { - key: 'delete', - content: t('grid.settings.deleteFilter'), - }, - ], - [t] - ); - - return ( - <> - { - setAnchorEl(e.currentTarget); - }} - className={'mx-2 my-1.5'} - > - - - {open && ( - { - if (e.key === 'Escape') { - e.preventDefault(); - e.stopPropagation(); - onClose(); - } - }} - keepMounted={false} - open={open} - anchorEl={anchorEl} - onClose={onClose} - > - { - if (e.key === 'ArrowDown') { - setDisableSelect(false); - } - }} - disableSelect={disableSelect} - options={options} - onConfirm={onDelete} - onEscape={onClose} - /> - - )} - - ); -} - -export default FilterActions; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/FilterConditionSelect.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/FilterConditionSelect.tsx deleted file mode 100644 index 8b793942da..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/FilterConditionSelect.tsx +++ /dev/null @@ -1,224 +0,0 @@ -import React, { useMemo } from 'react'; -import ConditionSelect from './ConditionSelect'; -import { - CheckboxFilterConditionPB, - ChecklistFilterConditionPB, - DateFilterConditionPB, - FieldType, - NumberFilterConditionPB, - SelectOptionFilterConditionPB, - TextFilterConditionPB, -} from '@/services/backend'; - -import { useTranslation } from 'react-i18next'; - -function FilterConditionSelect({ - name, - condition, - fieldType, - onChange, -}: { - name: string; - condition: number; - fieldType: FieldType; - onChange: (condition: number) => void; -}) { - const { t } = useTranslation(); - const conditions = useMemo(() => { - switch (fieldType) { - case FieldType.RichText: - case FieldType.URL: - return [ - { - value: TextFilterConditionPB.TextContains, - text: t('grid.textFilter.contains'), - }, - { - value: TextFilterConditionPB.TextDoesNotContain, - text: t('grid.textFilter.doesNotContain'), - }, - { - value: TextFilterConditionPB.TextStartsWith, - text: t('grid.textFilter.startWith'), - }, - { - value: TextFilterConditionPB.TextEndsWith, - text: t('grid.textFilter.endsWith'), - }, - { - value: TextFilterConditionPB.TextIs, - text: t('grid.textFilter.is'), - }, - { - value: TextFilterConditionPB.TextIsNot, - text: t('grid.textFilter.isNot'), - }, - { - value: TextFilterConditionPB.TextIsEmpty, - text: t('grid.textFilter.isEmpty'), - }, - { - value: TextFilterConditionPB.TextIsNotEmpty, - text: t('grid.textFilter.isNotEmpty'), - }, - ]; - case FieldType.SingleSelect: - return [ - { - value: SelectOptionFilterConditionPB.OptionIs, - text: t('grid.selectOptionFilter.is'), - }, - { - value: SelectOptionFilterConditionPB.OptionIsNot, - text: t('grid.selectOptionFilter.isNot'), - }, - { - value: SelectOptionFilterConditionPB.OptionIsEmpty, - text: t('grid.selectOptionFilter.isEmpty'), - }, - { - value: SelectOptionFilterConditionPB.OptionIsNotEmpty, - text: t('grid.selectOptionFilter.isNotEmpty'), - }, - ]; - case FieldType.MultiSelect: - return [ - { - value: SelectOptionFilterConditionPB.OptionIs, - text: t('grid.selectOptionFilter.is'), - }, - { - value: SelectOptionFilterConditionPB.OptionIsNot, - text: t('grid.selectOptionFilter.isNot'), - }, - { - value: SelectOptionFilterConditionPB.OptionContains, - text: t('grid.selectOptionFilter.contains'), - }, - { - value: SelectOptionFilterConditionPB.OptionDoesNotContain, - text: t('grid.selectOptionFilter.doesNotContain'), - }, - { - value: SelectOptionFilterConditionPB.OptionIsEmpty, - text: t('grid.selectOptionFilter.isEmpty'), - }, - { - value: SelectOptionFilterConditionPB.OptionIsNotEmpty, - text: t('grid.selectOptionFilter.isNotEmpty'), - }, - ]; - case FieldType.Number: - return [ - { - value: NumberFilterConditionPB.Equal, - text: '=', - }, - { - value: NumberFilterConditionPB.NotEqual, - text: '!=', - }, - { - value: NumberFilterConditionPB.GreaterThan, - text: '>', - }, - { - value: NumberFilterConditionPB.LessThan, - text: '<', - }, - { - value: NumberFilterConditionPB.GreaterThanOrEqualTo, - text: '>=', - }, - { - value: NumberFilterConditionPB.LessThanOrEqualTo, - text: '<=', - }, - { - value: NumberFilterConditionPB.NumberIsEmpty, - text: t('grid.textFilter.isEmpty'), - }, - { - value: NumberFilterConditionPB.NumberIsNotEmpty, - text: t('grid.textFilter.isNotEmpty'), - }, - ]; - case FieldType.Checkbox: - return [ - { - value: CheckboxFilterConditionPB.IsChecked, - text: t('grid.checkboxFilter.isChecked'), - }, - { - value: CheckboxFilterConditionPB.IsUnChecked, - text: t('grid.checkboxFilter.isUnchecked'), - }, - ]; - case FieldType.Checklist: - return [ - { - value: ChecklistFilterConditionPB.IsComplete, - text: t('grid.checklistFilter.isComplete'), - }, - { - value: ChecklistFilterConditionPB.IsIncomplete, - text: t('grid.checklistFilter.isIncomplted'), - }, - ]; - case FieldType.DateTime: - case FieldType.LastEditedTime: - case FieldType.CreatedTime: - return [ - { - value: DateFilterConditionPB.DateIs, - text: t('grid.dateFilter.is'), - }, - { - value: DateFilterConditionPB.DateBefore, - text: t('grid.dateFilter.before'), - }, - { - value: DateFilterConditionPB.DateAfter, - text: t('grid.dateFilter.after'), - }, - { - value: DateFilterConditionPB.DateOnOrBefore, - text: t('grid.dateFilter.onOrBefore'), - }, - { - value: DateFilterConditionPB.DateOnOrAfter, - text: t('grid.dateFilter.onOrAfter'), - }, - { - value: DateFilterConditionPB.DateWithIn, - text: t('grid.dateFilter.between'), - }, - { - value: DateFilterConditionPB.DateIsEmpty, - text: t('grid.dateFilter.empty'), - }, - { - value: DateFilterConditionPB.DateIsNotEmpty, - text: t('grid.dateFilter.notEmpty'), - }, - ]; - default: - return []; - } - }, [fieldType, t]); - - return ( -
-
{name}
- { - onChange(e); - }} - value={condition} - /> -
- ); -} - -export default FilterConditionSelect; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/FilterFieldsMenu.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/FilterFieldsMenu.tsx deleted file mode 100644 index e161badbf8..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/FilterFieldsMenu.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import React, { useCallback } from 'react'; -import { MenuProps } from '@mui/material'; -import PropertiesList from '$app/components/database/components/property/PropertiesList'; -import { Field } from '$app/application/database'; -import { useViewId } from '$app/hooks'; -import { useTranslation } from 'react-i18next'; -import { insertFilter } from '$app/application/database/filter/filter_service'; -import { getDefaultFilter } from '$app/application/database/filter/filter_data'; -import Popover from '@mui/material/Popover'; - -function FilterFieldsMenu({ - onInserted, - ...props -}: MenuProps & { - onInserted?: () => void; -}) { - const viewId = useViewId(); - const { t } = useTranslation(); - - const addFilter = useCallback( - async (field: Field) => { - const filterData = getDefaultFilter(field.type); - - await insertFilter({ - viewId, - fieldId: field.id, - fieldType: field.type, - data: filterData, - }); - props.onClose?.({}, 'backdropClick'); - onInserted?.(); - }, - [props, viewId, onInserted] - ); - - return ( - { - if (e.key === 'Escape') { - e.preventDefault(); - e.stopPropagation(); - props.onClose?.({}, 'escapeKeyDown'); - } - }} - {...props} - > - { - props.onClose?.({}, 'escapeKeyDown'); - }} - showSearch - searchPlaceholder={t('grid.settings.filterBy')} - onItemClick={addFilter} - /> - - ); -} - -export default FilterFieldsMenu; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/Filters.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/Filters.tsx deleted file mode 100644 index 860ce9f69f..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/Filters.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import React, { useMemo, useState } from 'react'; -import Filter from '$app/components/database/components/filter/Filter'; -import Button from '@mui/material/Button'; -import FilterFieldsMenu from '$app/components/database/components/filter/FilterFieldsMenu'; -import { useTranslation } from 'react-i18next'; -import { ReactComponent as AddSvg } from '$app/assets/add.svg'; -import { useDatabase } from '$app/components/database'; - -function Filters() { - const { t } = useTranslation(); - const { filters, fields } = useDatabase(); - - const options = useMemo(() => { - return filters.map((filter) => { - const field = fields.find((field) => field.id === filter.fieldId); - - return { - filter, - field, - }; - }); - }, [filters, fields]); - - const [filterAnchorEl, setFilterAnchorEl] = useState(null); - const openAddFilterMenu = Boolean(filterAnchorEl); - - const handleClick = (e: React.MouseEvent) => { - setFilterAnchorEl(e.currentTarget); - }; - - return ( -
- {options.map(({ filter, field }) => (field ? : null))} - - setFilterAnchorEl(null)} - anchorOrigin={{ - vertical: 'bottom', - horizontal: 'left', - }} - /> -
- ); -} - -export default Filters; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/date_filter/DateFilter.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/date_filter/DateFilter.tsx deleted file mode 100644 index 5c96d42b96..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/date_filter/DateFilter.tsx +++ /dev/null @@ -1,94 +0,0 @@ -import React, { useMemo } from 'react'; -import { - DateFilter as DateFilterType, - DateFilterData, - DateTimeField, - DateTimeTypeOption, -} from '$app/application/database'; -import { DateFilterConditionPB } from '@/services/backend'; -import CustomCalendar from '$app/components/database/components/field_types/date/CustomCalendar'; -import DateTimeSet from '$app/components/database/components/field_types/date/DateTimeSet'; -import { useTypeOption } from '$app/components/database'; -import { getDateFormat, getTimeFormat } from '$app/components/database/components/field_types/date/utils'; - -interface Props { - filter: DateFilterType; - field: DateTimeField; - onChange: (filterData: DateFilterData) => void; -} - -function DateFilter({ filter, field, onChange }: Props) { - const typeOption = useTypeOption(field.id); - - const showCalendar = - filter.data.condition !== DateFilterConditionPB.DateIsEmpty && - filter.data.condition !== DateFilterConditionPB.DateIsNotEmpty; - - const condition = filter.data.condition; - const isRange = condition === DateFilterConditionPB.DateWithIn; - const timestamp = useMemo(() => { - if (isRange) { - return filter.data.start; - } - - return filter.data.timestamp; - }, [filter.data.start, filter.data.timestamp, isRange]); - - const endTimestamp = useMemo(() => { - if (isRange) { - return filter.data.end; - } - - return; - }, [filter.data.end, isRange]); - - const timeFormat = useMemo(() => { - return getTimeFormat(typeOption.timeFormat); - }, [typeOption.timeFormat]); - - const dateFormat = useMemo(() => { - return getDateFormat(typeOption.dateFormat); - }, [typeOption.dateFormat]); - - return ( -
- {showCalendar && ( - <> -
- { - onChange({ - condition, - timestamp: date, - start: endDate ? date : undefined, - end: endDate, - }); - }} - date={timestamp} - endDate={endTimestamp} - timeFormat={timeFormat} - dateFormat={dateFormat} - includeTime={false} - isRange={isRange} - /> -
- { - onChange({ - condition, - timestamp: date, - start: endDate ? date : undefined, - end: endDate, - }); - }} - isRange={isRange} - timestamp={timestamp} - endTimestamp={endTimestamp} - /> - - )} -
- ); -} - -export default DateFilter; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/date_filter/DateFilterValue.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/date_filter/DateFilterValue.tsx deleted file mode 100644 index dd75d25852..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/date_filter/DateFilterValue.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import React, { useMemo } from 'react'; -import { DateFilterData } from '$app/application/database'; -import { useTranslation } from 'react-i18next'; -import dayjs from 'dayjs'; -import { DateFilterConditionPB } from '@/services/backend'; - -function DateFilterValue({ data }: { data: DateFilterData }) { - const { t } = useTranslation(); - - const value = useMemo(() => { - if (!data.timestamp) return ''; - - let startStr = ''; - let endStr = ''; - - if (data.start) { - const end = data.end ?? data.start; - const moreThanOneYear = dayjs.unix(end).diff(dayjs.unix(data.start), 'year') > 1; - const format = moreThanOneYear ? 'MMM D, YYYY' : 'MMM D'; - - startStr = dayjs.unix(data.start).format(format); - endStr = dayjs.unix(end).format(format); - } - - const timestamp = dayjs.unix(data.timestamp).format('MMM D'); - - switch (data.condition) { - case DateFilterConditionPB.DateIs: - return `: ${timestamp}`; - case DateFilterConditionPB.DateBefore: - return `: ${t('grid.dateFilter.choicechipPrefix.before')} ${timestamp}`; - case DateFilterConditionPB.DateAfter: - return `: ${t('grid.dateFilter.choicechipPrefix.after')} ${timestamp}`; - case DateFilterConditionPB.DateOnOrBefore: - return `: ${t('grid.dateFilter.choicechipPrefix.onOrBefore')} ${timestamp}`; - case DateFilterConditionPB.DateOnOrAfter: - return `: ${t('grid.dateFilter.choicechipPrefix.onOrAfter')} ${timestamp}`; - case DateFilterConditionPB.DateWithIn: - return `: ${startStr} - ${endStr}`; - case DateFilterConditionPB.DateIsEmpty: - return `: ${t('grid.dateFilter.choicechipPrefix.isEmpty')}`; - case DateFilterConditionPB.DateIsNotEmpty: - return `: ${t('grid.dateFilter.choicechipPrefix.isNotEmpty')}`; - default: - return ''; - } - }, [data, t]); - - return <>{value}; -} - -export default DateFilterValue; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/number_filter/NumberFilterValue.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/number_filter/NumberFilterValue.tsx deleted file mode 100644 index 658ef13d69..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/number_filter/NumberFilterValue.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import React, { useMemo } from 'react'; -import { NumberFilterData } from '$app/application/database'; -import { NumberFilterConditionPB } from '@/services/backend'; -import { useTranslation } from 'react-i18next'; - -function NumberFilterValue({ data }: { data: NumberFilterData }) { - const { t } = useTranslation(); - - const value = useMemo(() => { - if (!data.content) { - return ''; - } - - const content = parseInt(data.content); - - switch (data.condition) { - case NumberFilterConditionPB.Equal: - return `= ${content}`; - case NumberFilterConditionPB.NotEqual: - return `!= ${content}`; - case NumberFilterConditionPB.GreaterThan: - return `> ${content}`; - case NumberFilterConditionPB.GreaterThanOrEqualTo: - return `>= ${content}`; - case NumberFilterConditionPB.LessThan: - return `< ${content}`; - case NumberFilterConditionPB.LessThanOrEqualTo: - return `<= ${content}`; - case NumberFilterConditionPB.NumberIsEmpty: - return t('grid.textFilter.isEmpty'); - case NumberFilterConditionPB.NumberIsNotEmpty: - return t('grid.textFilter.isNotEmpty'); - } - }, [data.condition, data.content, t]); - - return <>{value}; -} - -export default NumberFilterValue; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/select_filter/SelectFilter.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/select_filter/SelectFilter.tsx deleted file mode 100644 index bd1d1f239a..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/select_filter/SelectFilter.tsx +++ /dev/null @@ -1,92 +0,0 @@ -import React, { useMemo, useRef } from 'react'; -import { - SelectField, - SelectFilter as SelectFilterType, - SelectFilterData, - SelectTypeOption, -} from '$app/application/database'; -import { Tag } from '$app/components/database/components/field_types/select/Tag'; -import { ReactComponent as SelectCheckSvg } from '$app/assets/select-check.svg'; -import { SelectOptionFilterConditionPB } from '@/services/backend'; -import { useTypeOption } from '$app/components/database'; -import KeyboardNavigation, { - KeyboardNavigationOption, -} from '$app/components/_shared/keyboard_navigation/KeyboardNavigation'; - -interface Props { - filter: SelectFilterType; - field: SelectField; - onChange: (filterData: SelectFilterData) => void; - onClose?: () => void; -} - -function SelectFilter({ onClose, filter, field, onChange }: Props) { - const scrollRef = useRef(null); - const condition = filter.data.condition; - const typeOption = useTypeOption(field.id); - const options: KeyboardNavigationOption[] = useMemo(() => { - return ( - typeOption?.options?.map((option) => { - return { - key: option.id, - content: ( -
- - {filter.data.optionIds?.includes(option.id) && } -
- ), - }; - }) ?? [] - ); - }, [filter.data.optionIds, typeOption?.options]); - - const showOptions = - options.length > 0 && - condition !== SelectOptionFilterConditionPB.OptionIsEmpty && - condition !== SelectOptionFilterConditionPB.OptionIsNotEmpty; - - const handleChange = ({ - condition, - optionIds, - }: { - condition?: SelectFilterData['condition']; - optionIds?: SelectFilterData['optionIds']; - }) => { - onChange({ - condition: condition ?? filter.data.condition, - optionIds: optionIds ?? filter.data.optionIds, - }); - }; - - const handleSelectOption = (optionId: string) => { - const prev = filter.data.optionIds; - let newOptionIds = []; - - if (!prev) { - newOptionIds.push(optionId); - } else { - const isSelected = prev.includes(optionId); - - if (isSelected) { - newOptionIds = prev.filter((id) => id !== optionId); - } else { - newOptionIds = [...prev, optionId]; - } - } - - handleChange({ - condition, - optionIds: newOptionIds, - }); - }; - - if (!showOptions) return null; - - return ( -
- -
- ); -} - -export default SelectFilter; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/select_filter/SelectFilterValue.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/select_filter/SelectFilterValue.tsx deleted file mode 100644 index 72576deae1..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/select_filter/SelectFilterValue.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import React, { useMemo } from 'react'; -import { SelectFilterData, SelectTypeOption } from '$app/application/database'; -import { useStaticTypeOption } from '$app/components/database'; -import { useTranslation } from 'react-i18next'; -import { SelectOptionFilterConditionPB } from '@/services/backend'; - -function SelectFilterValue({ data, fieldId }: { data: SelectFilterData; fieldId: string }) { - const typeOption = useStaticTypeOption(fieldId); - const { t } = useTranslation(); - const value = useMemo(() => { - if (!data.optionIds?.length) return ''; - - const options = data.optionIds - .map((optionId) => { - const option = typeOption?.options?.find((option) => option.id === optionId); - - return option?.name; - }) - .join(', '); - - switch (data.condition) { - case SelectOptionFilterConditionPB.OptionIs: - return `: ${options}`; - case SelectOptionFilterConditionPB.OptionIsNot: - return `: ${t('grid.textFilter.choicechipPrefix.isNot')} ${options}`; - case SelectOptionFilterConditionPB.OptionIsEmpty: - return `: ${t('grid.textFilter.choicechipPrefix.isEmpty')}`; - case SelectOptionFilterConditionPB.OptionIsNotEmpty: - return `: ${t('grid.textFilter.choicechipPrefix.isNotEmpty')}`; - default: - return ''; - } - }, [data.condition, data.optionIds, t, typeOption?.options]); - - return <>{value}; -} - -export default SelectFilterValue; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/text_filter/TextFilter.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/text_filter/TextFilter.tsx deleted file mode 100644 index 0c7eab6e05..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/text_filter/TextFilter.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import React, { useMemo, useState } from 'react'; -import { TextFilter as TextFilterType, TextFilterData } from '$app/application/database'; -import { TextField } from '@mui/material'; -import { useTranslation } from 'react-i18next'; -import { TextFilterConditionPB } from '@/services/backend'; -import debounce from 'lodash-es/debounce'; - -interface Props { - filter: TextFilterType; - onChange: (filterData: TextFilterData) => void; -} - -const DELAY = 500; - -function TextFilter({ filter, onChange }: Props) { - const { t } = useTranslation(); - const [content, setContext] = useState(filter.data.content); - const condition = filter.data.condition; - const showField = - condition !== TextFilterConditionPB.TextIsEmpty && condition !== TextFilterConditionPB.TextIsNotEmpty; - - const onConditionChange = useMemo(() => { - return debounce((content: string) => { - onChange({ - content, - condition, - }); - }, DELAY); - }, [condition, onChange]); - - if (!showField) return null; - return ( - { - setContext(e.target.value); - onConditionChange(e.target.value ?? ''); - }} - /> - ); -} - -export default TextFilter; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/text_filter/TextFilterValue.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/text_filter/TextFilterValue.tsx deleted file mode 100644 index 5718a3e2b8..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/text_filter/TextFilterValue.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { useMemo } from 'react'; -import { TextFilterData } from '$app/application/database'; -import { TextFilterConditionPB } from '@/services/backend'; -import { useTranslation } from 'react-i18next'; - -function TextFilterValue({ data }: { data: TextFilterData }) { - const { t } = useTranslation(); - - const value = useMemo(() => { - if (!data.content) return ''; - switch (data.condition) { - case TextFilterConditionPB.TextContains: - case TextFilterConditionPB.TextIs: - return `: ${data.content}`; - case TextFilterConditionPB.TextDoesNotContain: - case TextFilterConditionPB.TextIsNot: - return `: ${t('grid.textFilter.choicechipPrefix.isNot')} ${data.content}`; - case TextFilterConditionPB.TextStartsWith: - return `: ${t('grid.textFilter.choicechipPrefix.startWith')} ${data.content}`; - case TextFilterConditionPB.TextEndsWith: - return `: ${t('grid.textFilter.choicechipPrefix.endWith')} ${data.content}`; - case TextFilterConditionPB.TextIsEmpty: - return `: ${t('grid.textFilter.choicechipPrefix.isEmpty')}`; - case TextFilterConditionPB.TextIsNotEmpty: - return `: ${t('grid.textFilter.choicechipPrefix.isNotEmpty')}`; - default: - return ''; - } - }, [t, data]); - - return <>{value}; -} - -export default TextFilterValue; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/index.ts deleted file mode 100644 index 7da8d0eab4..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './tab_bar'; -export * from './cell'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/NewProperty.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/NewProperty.tsx deleted file mode 100644 index bb71befa8d..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/NewProperty.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import React, { useCallback } from 'react'; -import { fieldService } from '$app/application/database'; -import { FieldType } from '@/services/backend'; -import { useTranslation } from 'react-i18next'; -import Button from '@mui/material/Button'; -import { ReactComponent as AddSvg } from '$app/assets/add.svg'; -import { useViewId } from '$app/hooks'; - -interface NewPropertyProps { - onInserted?: (id: string) => void; -} -function NewProperty({ onInserted }: NewPropertyProps) { - const viewId = useViewId(); - const { t } = useTranslation(); - - const handleClick = useCallback(async () => { - try { - const field = await fieldService.createField({ - viewId, - fieldType: FieldType.RichText, - }); - - onInserted?.(field.id); - } catch (e) { - // toast.error(t('grid.field.newPropertyFail')); - } - }, [onInserted, viewId]); - - return ( - - ); -} - -export default NewProperty; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/PropertiesList.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/PropertiesList.tsx deleted file mode 100644 index a9865c467f..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/PropertiesList.tsx +++ /dev/null @@ -1,92 +0,0 @@ -import React, { useCallback, useMemo, useRef, useState } from 'react'; -import { OutlinedInput } from '@mui/material'; -import { Property } from '$app/components/database/components/property/Property'; -import { Field as FieldType } from '$app/application/database'; -import { useDatabase } from '$app/components/database'; -import KeyboardNavigation from '$app/components/_shared/keyboard_navigation/KeyboardNavigation'; - -interface FieldListProps { - searchPlaceholder?: string; - showSearch?: boolean; - onItemClick?: (field: FieldType) => void; - onClose?: () => void; -} - -function PropertiesList({ onClose, showSearch, onItemClick, searchPlaceholder }: FieldListProps) { - const { fields } = useDatabase(); - const [fieldsResult, setFieldsResult] = useState(fields as FieldType[]); - - const onInputChange = useCallback( - (event: React.ChangeEvent) => { - const value = event.target.value; - const result = fields.filter((field) => field.name.toLowerCase().includes(value.toLowerCase())); - - setFieldsResult(result); - }, - [fields] - ); - - const inputRef = useRef(null); - - const searchInput = useMemo(() => { - return showSearch ? ( -
- -
- ) : null; - }, [onInputChange, searchPlaceholder, showSearch]); - - const scrollRef = useRef(null); - - const options = useMemo(() => { - return fieldsResult.map((field) => { - return { - key: field.id, - content: ( -
- -
- ), - }; - }); - }, [fieldsResult]); - - const onConfirm = useCallback( - (key: string) => { - const field = fields.find((field) => field.id === key); - - onItemClick?.(field as FieldType); - }, - [fields, onItemClick] - ); - - return ( -
- {searchInput} -
- -
-
- ); -} - -export default PropertiesList; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/Property.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/Property.tsx deleted file mode 100644 index 3091ba4ea1..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/Property.tsx +++ /dev/null @@ -1,95 +0,0 @@ -import { FC, useEffect, useRef, useState } from 'react'; -import { Field as FieldType } from '$app/application/database'; -import { ProppertyTypeSvg } from './property_type/ProppertyTypeSvg'; -import { PropertyMenu } from '$app/components/database/components/property/PropertyMenu'; -import usePopoverAutoPosition from '$app/components/_shared/popover/Popover.hooks'; -import { PopoverOrigin } from '@mui/material/Popover/Popover'; - -export interface FieldProps { - field: FieldType; - menuOpened?: boolean; - onOpenMenu?: (id: string) => void; - onCloseMenu?: (id: string) => void; - className?: string; -} - -const initialAnchorOrigin: PopoverOrigin = { - vertical: 'bottom', - horizontal: 'right', -}; - -const initialTransformOrigin: PopoverOrigin = { - vertical: 'top', - horizontal: 'center', -}; - -export const Property: FC = ({ field, onCloseMenu, className, menuOpened }) => { - const ref = useRef(null); - const [anchorPosition, setAnchorPosition] = useState< - | { - top: number; - left: number; - height: number; - } - | undefined - >(undefined); - - const open = Boolean(anchorPosition && menuOpened); - - useEffect(() => { - if (menuOpened) { - const rect = ref.current?.getBoundingClientRect(); - - if (rect) { - setAnchorPosition({ - top: rect.top + 28, - left: rect.left, - height: rect.height, - }); - return; - } - } - - setAnchorPosition(undefined); - }, [menuOpened]); - - const { paperHeight, paperWidth, transformOrigin, anchorOrigin, isEntered } = usePopoverAutoPosition({ - initialPaperWidth: 300, - initialPaperHeight: 400, - anchorPosition, - initialAnchorOrigin, - initialTransformOrigin, - open, - }); - - return ( - <> -
- - {field.name} -
- - {open && ( - { - onCloseMenu?.(field.id); - }} - transformOrigin={transformOrigin} - anchorOrigin={anchorOrigin} - PaperProps={{ - style: { - maxHeight: paperHeight, - width: paperWidth, - height: 'auto', - }, - className: 'flex h-full flex-col overflow-hidden', - }} - anchorPosition={anchorPosition} - anchorReference={'anchorPosition'} - /> - )} - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/PropertyActions.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/PropertyActions.tsx deleted file mode 100644 index b319940996..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/PropertyActions.tsx +++ /dev/null @@ -1,271 +0,0 @@ -import React, { RefObject, useCallback, useMemo, useState } from 'react'; - -import { ReactComponent as EditSvg } from '$app/assets/edit.svg'; -import { ReactComponent as HideSvg } from '$app/assets/hide.svg'; -import { ReactComponent as ShowSvg } from '$app/assets/eye_open.svg'; - -import { ReactComponent as CopySvg } from '$app/assets/copy.svg'; -import { ReactComponent as DeleteSvg } from '$app/assets/delete.svg'; -import { ReactComponent as LeftSvg } from '$app/assets/left.svg'; -import { ReactComponent as RightSvg } from '$app/assets/right.svg'; -import { useViewId } from '$app/hooks'; -import { fieldService } from '$app/application/database'; -import { OrderObjectPositionTypePB, FieldVisibility } from '@/services/backend'; -import DeleteConfirmDialog from '$app/components/_shared/confirm_dialog/DeleteConfirmDialog'; -import { useTranslation } from 'react-i18next'; -import KeyboardNavigation, { - KeyboardNavigationOption, -} from '$app/components/_shared/keyboard_navigation/KeyboardNavigation'; -import { notify } from 'src/appflowy_app/components/_shared/notify'; - -export enum FieldAction { - EditProperty, - Hide, - Show, - Duplicate, - Delete, - InsertLeft, - InsertRight, -} - -const FieldActionSvgMap = { - [FieldAction.EditProperty]: EditSvg, - [FieldAction.Hide]: HideSvg, - [FieldAction.Show]: ShowSvg, - [FieldAction.Duplicate]: CopySvg, - [FieldAction.Delete]: DeleteSvg, - [FieldAction.InsertLeft]: LeftSvg, - [FieldAction.InsertRight]: RightSvg, -}; - -const defaultActions: FieldAction[] = [ - FieldAction.EditProperty, - FieldAction.InsertLeft, - FieldAction.InsertRight, - FieldAction.Hide, - FieldAction.Duplicate, - FieldAction.Delete, -]; - -// prevent default actions for primary fields -const primaryPreventDefaultActions = [FieldAction.Hide, FieldAction.Delete, FieldAction.Duplicate]; - -interface PropertyActionsProps { - fieldId: string; - actions?: FieldAction[]; - isPrimary?: boolean; - inputRef?: RefObject; - onClose?: () => void; - onMenuItemClick?: (action: FieldAction, newFieldId?: string) => void; -} - -function PropertyActions({ - onClose, - inputRef, - fieldId, - onMenuItemClick, - isPrimary, - actions = defaultActions, -}: PropertyActionsProps) { - const viewId = useViewId(); - const { t } = useTranslation(); - const [openConfirm, setOpenConfirm] = useState(false); - const [focusMenu, setFocusMenu] = useState(false); - const menuTextMap = useMemo( - () => ({ - [FieldAction.EditProperty]: t('grid.field.editProperty'), - [FieldAction.Hide]: t('grid.field.hide'), - [FieldAction.Show]: t('grid.field.show'), - [FieldAction.Duplicate]: t('grid.field.duplicate'), - [FieldAction.Delete]: t('grid.field.delete'), - [FieldAction.InsertLeft]: t('grid.field.insertLeft'), - [FieldAction.InsertRight]: t('grid.field.insertRight'), - }), - [t] - ); - - const handleOpenConfirm = () => { - setOpenConfirm(true); - }; - - const handleMenuItemClick = async (action: FieldAction) => { - const preventDefault = isPrimary && primaryPreventDefaultActions.includes(action); - - if (preventDefault) { - return; - } - - switch (action) { - case FieldAction.EditProperty: - break; - case FieldAction.InsertLeft: - case FieldAction.InsertRight: { - const fieldPosition = - action === FieldAction.InsertLeft ? OrderObjectPositionTypePB.Before : OrderObjectPositionTypePB.After; - - const field = await fieldService.createField({ - viewId, - fieldPosition, - targetFieldId: fieldId, - }); - - onMenuItemClick?.(action, field.id); - return; - } - - case FieldAction.Hide: - await fieldService.updateFieldSetting(viewId, fieldId, { - visibility: FieldVisibility.AlwaysHidden, - }); - break; - case FieldAction.Show: - await fieldService.updateFieldSetting(viewId, fieldId, { - visibility: FieldVisibility.AlwaysShown, - }); - break; - case FieldAction.Duplicate: - await fieldService.duplicateField(viewId, fieldId); - break; - case FieldAction.Delete: - handleOpenConfirm(); - return; - } - - onMenuItemClick?.(action); - }; - - const renderActionContent = useCallback((item: { text: string; Icon: React.FC> }) => { - const { Icon, text } = item; - - return ( -
- -
{text}
-
- ); - }, []); - - const options: KeyboardNavigationOption[] = useMemo( - () => - [ - { - key: FieldAction.EditProperty, - content: renderActionContent({ - text: menuTextMap[FieldAction.EditProperty], - Icon: FieldActionSvgMap[FieldAction.EditProperty], - }), - disabled: isPrimary && primaryPreventDefaultActions.includes(FieldAction.EditProperty), - }, - { - key: FieldAction.InsertLeft, - content: renderActionContent({ - text: menuTextMap[FieldAction.InsertLeft], - Icon: FieldActionSvgMap[FieldAction.InsertLeft], - }), - disabled: isPrimary && primaryPreventDefaultActions.includes(FieldAction.InsertLeft), - }, - { - key: FieldAction.InsertRight, - content: renderActionContent({ - text: menuTextMap[FieldAction.InsertRight], - Icon: FieldActionSvgMap[FieldAction.InsertRight], - }), - disabled: isPrimary && primaryPreventDefaultActions.includes(FieldAction.InsertRight), - }, - { - key: FieldAction.Hide, - content: renderActionContent({ - text: menuTextMap[FieldAction.Hide], - Icon: FieldActionSvgMap[FieldAction.Hide], - }), - disabled: isPrimary && primaryPreventDefaultActions.includes(FieldAction.Hide), - }, - { - key: FieldAction.Show, - content: renderActionContent({ - text: menuTextMap[FieldAction.Show], - Icon: FieldActionSvgMap[FieldAction.Show], - }), - }, - { - key: FieldAction.Duplicate, - content: renderActionContent({ - text: menuTextMap[FieldAction.Duplicate], - Icon: FieldActionSvgMap[FieldAction.Duplicate], - }), - disabled: isPrimary && primaryPreventDefaultActions.includes(FieldAction.Duplicate), - }, - { - key: FieldAction.Delete, - content: renderActionContent({ - text: menuTextMap[FieldAction.Delete], - Icon: FieldActionSvgMap[FieldAction.Delete], - }), - disabled: isPrimary && primaryPreventDefaultActions.includes(FieldAction.Delete), - }, - ].filter((option) => actions.includes(option.key)), - [renderActionContent, menuTextMap, isPrimary, actions] - ); - - const handleKeyDown = useCallback( - (e: KeyboardEvent) => { - const isTab = e.key === 'Tab'; - - if (!focusMenu && (e.key === 'ArrowDown' || e.key === 'ArrowUp')) { - e.stopPropagation(); - notify.clear(); - notify.info(`Press Tab to focus on the menu`); - return; - } - - if (isTab) { - e.preventDefault(); - e.stopPropagation(); - if (focusMenu) { - inputRef?.current?.focus(); - setFocusMenu(false); - } else { - inputRef?.current?.blur(); - setFocusMenu(true); - } - - return; - } - }, - [focusMenu, inputRef] - ); - - return ( - <> - { - setFocusMenu(true); - }} - onBlur={() => { - setFocusMenu(false); - }} - onKeyDown={handleKeyDown} - onConfirm={handleMenuItemClick} - /> - { - await fieldService.deleteField(viewId, fieldId); - }} - onClose={() => { - setOpenConfirm(false); - onClose?.(); - }} - /> - - ); -} - -export default PropertyActions; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/PropertyMenu.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/PropertyMenu.tsx deleted file mode 100644 index 55b314b821..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/PropertyMenu.tsx +++ /dev/null @@ -1,89 +0,0 @@ -import { Divider } from '@mui/material'; -import { FC, useCallback, useMemo, useRef } from 'react'; -import { useViewId } from '$app/hooks'; -import { Field, fieldService } from '$app/application/database'; -import PropertyTypeMenuExtension from '$app/components/database/components/property/property_type/PropertyTypeMenuExtension'; -import PropertyTypeSelect from '$app/components/database/components/property/property_type/PropertyTypeSelect'; -import { FieldType, FieldVisibility } from '@/services/backend'; -import { Log } from '$app/utils/log'; -import Popover, { PopoverProps } from '@mui/material/Popover'; -import PropertyNameInput from '$app/components/database/components/property/PropertyNameInput'; -import PropertyActions, { FieldAction } from '$app/components/database/components/property/PropertyActions'; - -export interface GridFieldMenuProps extends PopoverProps { - field: Field; -} - -export const PropertyMenu: FC = ({ field, ...props }) => { - const viewId = useViewId(); - const inputRef = useRef(null); - - const isPrimary = field.isPrimary; - const actions = useMemo(() => { - const keys = [FieldAction.Duplicate, FieldAction.Delete]; - - if (field.visibility === FieldVisibility.AlwaysHidden) { - keys.unshift(FieldAction.Show); - } else { - keys.unshift(FieldAction.Hide); - } - - return keys; - }, [field.visibility]); - - const onUpdateFieldType = useCallback( - async (type: FieldType) => { - try { - await fieldService.updateFieldType(viewId, field.id, type); - } catch (e) { - // TODO - Log.error(`change field ${field.id} type from '${field.type}' to ${type} fail`, e); - } - }, - [viewId, field] - ); - - return ( - e.stopPropagation()} - onKeyDown={(e) => { - if (e.key === 'Escape') { - e.stopPropagation(); - e.preventDefault(); - props.onClose?.({}, 'escapeKeyDown'); - } - }} - onMouseDown={(e) => { - const isInput = inputRef.current?.contains(e.target as Node); - - if (isInput) return; - - e.stopPropagation(); - e.preventDefault(); - }} - {...props} - > - -
- {!isPrimary && ( -
- - -
- )} - - props.onClose?.({}, 'backdropClick')} - isPrimary={isPrimary} - actions={actions} - onMenuItemClick={() => { - props.onClose?.({}, 'backdropClick'); - }} - fieldId={field.id} - /> -
-
- ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/PropertyNameInput.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/PropertyNameInput.tsx deleted file mode 100644 index 4e20531335..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/PropertyNameInput.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import React, { ChangeEventHandler, useCallback, useMemo, useState } from 'react'; -import { useViewId } from '$app/hooks'; -import { fieldService } from '$app/application/database'; -import { Log } from '$app/utils/log'; -import TextField from '@mui/material/TextField'; -import debounce from 'lodash-es/debounce'; - -const PropertyNameInput = React.forwardRef(({ id, name }, ref) => { - const viewId = useViewId(); - const [inputtingName, setInputtingName] = useState(name); - - const handleSubmit = useCallback( - async (newName: string) => { - if (newName !== name) { - try { - await fieldService.updateField(viewId, id, { - name: newName, - }); - } catch (e) { - // TODO - Log.error(`change field ${id} name from '${name}' to ${newName} fail`, e); - } - } - }, - [viewId, id, name] - ); - - const debouncedHandleSubmit = useMemo(() => debounce(handleSubmit, 500), [handleSubmit]); - const handleInput = useCallback>( - (e) => { - setInputtingName(e.target.value); - void debouncedHandleSubmit(e.target.value); - }, - [debouncedHandleSubmit] - ); - - return ( - - ); -}); - -export default PropertyNameInput; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/PropertySelect.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/PropertySelect.tsx deleted file mode 100644 index 0741bbc05b..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/PropertySelect.tsx +++ /dev/null @@ -1,84 +0,0 @@ -import { FC, useCallback, useMemo, useRef, useState } from 'react'; -import { Field as FieldType } from '$app/application/database'; -import { useDatabase } from '../../Database.hooks'; -import { Property } from './Property'; -import KeyboardNavigation, { - KeyboardNavigationOption, -} from '$app/components/_shared/keyboard_navigation/KeyboardNavigation'; -import { ReactComponent as DropDownSvg } from '$app/assets/more.svg'; -import Popover from '@mui/material/Popover'; - -export interface FieldSelectProps { - onChange?: (field: FieldType | undefined) => void; - value?: string; -} - -export const PropertySelect: FC = ({ value, onChange }) => { - const { fields } = useDatabase(); - - const scrollRef = useRef(null); - const ref = useRef(null); - const [open, setOpen] = useState(false); - const handleClose = () => { - setOpen(false); - }; - - const options: KeyboardNavigationOption[] = useMemo( - () => - fields.map((field) => { - return { - key: field.id, - content: , - }; - }), - [fields] - ); - - const onConfirm = useCallback( - (optionKey: string) => { - onChange?.(fields.find((field) => field.id === optionKey)); - }, - [onChange, fields] - ); - - const selectedField = useMemo(() => fields.find((field) => field.id === value), [fields, value]); - - return ( - <> -
{ - setOpen(true); - }} - > -
{selectedField ? : null}
- -
- {open && ( - -
- -
-
- )} - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/index.ts deleted file mode 100644 index 0b338836d6..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from './Property'; -export * from './PropertySelect'; -export * from './property_type/PropertyTypeText'; -export * from './property_type/ProppertyTypeSvg'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/property_type/PropertyTypeMenu.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/property_type/PropertyTypeMenu.tsx deleted file mode 100644 index e3021249ee..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/property_type/PropertyTypeMenu.tsx +++ /dev/null @@ -1,121 +0,0 @@ -import { Menu, MenuProps } from '@mui/material'; -import { FC, useCallback, useMemo } from 'react'; -import { FieldType } from '@/services/backend'; -import { PropertyTypeText, ProppertyTypeSvg } from '$app/components/database/components/property'; -import { Field } from '$app/application/database'; -import { ReactComponent as SelectCheckSvg } from '$app/assets/select-check.svg'; -import KeyboardNavigation, { - KeyboardNavigationOption, -} from '$app/components/_shared/keyboard_navigation/KeyboardNavigation'; -import Typography from '@mui/material/Typography'; - -export const PropertyTypeMenu: FC< - MenuProps & { - field: Field; - onClickItem?: (type: FieldType) => void; - } -> = ({ field, onClickItem, ...props }) => { - const PopoverClasses = useMemo( - () => ({ - ...props.PopoverClasses, - paper: ['w-56', props.PopoverClasses?.paper].join(' '), - }), - [props.PopoverClasses] - ); - - const renderGroupContent = useCallback((title: string) => { - return ( - - {title} - - ); - }, []); - - const renderContent = useCallback( - (type: FieldType) => { - return ( - <> - - - - - {type === field.type && } - - ); - }, - [field.type] - ); - - const options: KeyboardNavigationOption[] = useMemo(() => { - return [ - { - key: 100, - content: renderGroupContent('Basic'), - children: [ - { - key: FieldType.RichText, - content: renderContent(FieldType.RichText), - }, - { - key: FieldType.Number, - content: renderContent(FieldType.Number), - }, - { - key: FieldType.SingleSelect, - content: renderContent(FieldType.SingleSelect), - }, - { - key: FieldType.MultiSelect, - content: renderContent(FieldType.MultiSelect), - }, - { - key: FieldType.DateTime, - content: renderContent(FieldType.DateTime), - }, - { - key: FieldType.Checkbox, - content: renderContent(FieldType.Checkbox), - }, - { - key: FieldType.Checklist, - content: renderContent(FieldType.Checklist), - }, - { - key: FieldType.URL, - content: renderContent(FieldType.URL), - }, - ], - }, - { - key: 101, - content:
, - children: [], - }, - { - key: 102, - content: renderGroupContent('Advanced'), - children: [ - { - key: FieldType.LastEditedTime, - content: renderContent(FieldType.LastEditedTime), - }, - { - key: FieldType.CreatedTime, - content: renderContent(FieldType.CreatedTime), - }, - ], - }, - ]; - }, [renderContent, renderGroupContent]); - - return ( - - props?.onClose?.({}, 'escapeKeyDown')} - options={options} - disableFocus={true} - onConfirm={onClickItem} - /> - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/property_type/PropertyTypeMenuExtension.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/property_type/PropertyTypeMenuExtension.tsx deleted file mode 100644 index b45b670757..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/property_type/PropertyTypeMenuExtension.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import React, { useMemo } from 'react'; -import { FieldType } from '@/services/backend'; -import { DateTimeField, Field, NumberField, SelectField } from '$app/application/database'; -import SelectFieldActions from '$app/components/database/components/field_types/select/select_field_actions/SelectFieldActions'; -import NumberFieldActions from '$app/components/database/components/field_types/number/NumberFieldActions'; -import DateTimeFieldActions from '$app/components/database/components/field_types/date/DateTimeFieldActions'; - -function PropertyTypeMenuExtension({ field }: { field: Field }) { - return useMemo(() => { - switch (field.type) { - case FieldType.SingleSelect: - case FieldType.MultiSelect: - return ; - case FieldType.Number: - return ; - case FieldType.DateTime: - case FieldType.CreatedTime: - case FieldType.LastEditedTime: - return ; - default: - return null; - } - }, [field]); -} - -export default PropertyTypeMenuExtension; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/property_type/PropertyTypeSelect.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/property_type/PropertyTypeSelect.tsx deleted file mode 100644 index 28d62b82c6..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/property_type/PropertyTypeSelect.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import React, { useRef, useState } from 'react'; -import { ProppertyTypeSvg } from '$app/components/database/components/property/property_type/ProppertyTypeSvg'; -import { MenuItem } from '@mui/material'; -import { Field } from '$app/application/database'; -import { ReactComponent as MoreSvg } from '$app/assets/more.svg'; -import { PropertyTypeMenu } from '$app/components/database/components/property/property_type/PropertyTypeMenu'; -import { FieldType } from '@/services/backend'; -import { PropertyTypeText } from '$app/components/database/components/property/property_type/PropertyTypeText'; - -interface Props { - field: Field; - onUpdateFieldType: (type: FieldType) => void; -} -function PropertyTypeSelect({ field, onUpdateFieldType }: Props) { - const [expanded, setExpanded] = useState(false); - const ref = useRef(null); - - return ( -
- { - setExpanded(!expanded); - }} - className={'mx-0 rounded-none px-0'} - > -
- - - - - -
-
- {expanded && ( - { - setExpanded(false); - }} - /> - )} -
- ); -} - -export default PropertyTypeSelect; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/property_type/PropertyTypeText.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/property_type/PropertyTypeText.tsx deleted file mode 100644 index daae232fde..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/property_type/PropertyTypeText.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import { FieldType } from '@/services/backend'; -import { useTranslation } from 'react-i18next'; -import { useMemo } from 'react'; - -export const PropertyTypeText = ({ type }: { type: FieldType }) => { - const { t } = useTranslation(); - - const text = useMemo(() => { - const map = { - [FieldType.RichText]: t('grid.field.textFieldName'), - [FieldType.Number]: t('grid.field.numberFieldName'), - [FieldType.DateTime]: t('grid.field.dateFieldName'), - [FieldType.SingleSelect]: t('grid.field.singleSelectFieldName'), - [FieldType.MultiSelect]: t('grid.field.multiSelectFieldName'), - [FieldType.Checkbox]: t('grid.field.checkboxFieldName'), - [FieldType.URL]: t('grid.field.urlFieldName'), - [FieldType.Checklist]: t('grid.field.checklistFieldName'), - [FieldType.LastEditedTime]: t('grid.field.updatedAtFieldName'), - [FieldType.CreatedTime]: t('grid.field.createdAtFieldName'), - [FieldType.Relation]: t('grid.field.relationFieldName'), - }; - - return map[type] || 'unknown'; - }, [t, type]); - - return
{text}
; -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/property_type/ProppertyTypeSvg.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/property_type/ProppertyTypeSvg.tsx deleted file mode 100644 index 7ee4e6f83d..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/property_type/ProppertyTypeSvg.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { FC, memo } from 'react'; -import { FieldType } from '@/services/backend'; -import { ReactComponent as TextSvg } from '$app/assets/database/field-type-text.svg'; -import { ReactComponent as NumberSvg } from '$app/assets/database/field-type-number.svg'; -import { ReactComponent as DateSvg } from '$app/assets/database/field-type-date.svg'; -import { ReactComponent as SingleSelectSvg } from '$app/assets/database/field-type-single-select.svg'; -import { ReactComponent as MultiSelectSvg } from '$app/assets/database/field-type-multi-select.svg'; -import { ReactComponent as ChecklistSvg } from '$app/assets/database/field-type-checklist.svg'; -import { ReactComponent as CheckboxSvg } from '$app/assets/database/field-type-checkbox.svg'; -import { ReactComponent as URLSvg } from '$app/assets/database/field-type-url.svg'; -import { ReactComponent as LastEditedTimeSvg } from '$app/assets/database/field-type-last-edited-time.svg'; -import { ReactComponent as RelationSvg } from '$app/assets/database/field-type-relation.svg'; - -export const FieldTypeSvgMap: Record>> = { - [FieldType.RichText]: TextSvg, - [FieldType.Number]: NumberSvg, - [FieldType.DateTime]: DateSvg, - [FieldType.SingleSelect]: SingleSelectSvg, - [FieldType.MultiSelect]: MultiSelectSvg, - [FieldType.Checkbox]: CheckboxSvg, - [FieldType.URL]: URLSvg, - [FieldType.Checklist]: ChecklistSvg, - [FieldType.LastEditedTime]: LastEditedTimeSvg, - [FieldType.CreatedTime]: LastEditedTimeSvg, - [FieldType.Relation]: RelationSvg, -}; - -export const ProppertyTypeSvg: FC<{ type: FieldType; className?: string }> = memo(({ type, ...props }) => { - const Svg = FieldTypeSvgMap[type]; - - return ; -}); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/sort/SortConditionSelect.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/sort/SortConditionSelect.tsx deleted file mode 100644 index fdb508cb8f..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/sort/SortConditionSelect.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import { FC, useMemo, useRef, useState } from 'react'; -import { SortConditionPB } from '@/services/backend'; -import KeyboardNavigation, { - KeyboardNavigationOption, -} from '$app/components/_shared/keyboard_navigation/KeyboardNavigation'; -import { Popover } from '@mui/material'; -import { ReactComponent as DropDownSvg } from '$app/assets/more.svg'; -import { useTranslation } from 'react-i18next'; - -export const SortConditionSelect: FC<{ - onChange?: (value: SortConditionPB) => void; - value?: SortConditionPB; -}> = ({ onChange, value }) => { - const { t } = useTranslation(); - const ref = useRef(null); - const [open, setOpen] = useState(false); - const handleClose = () => { - setOpen(false); - }; - - const options: KeyboardNavigationOption[] = useMemo(() => { - return [ - { - key: SortConditionPB.Ascending, - content: t('grid.sort.ascending'), - }, - { - key: SortConditionPB.Descending, - content: t('grid.sort.descending'), - }, - ]; - }, [t]); - - const onConfirm = (optionKey: SortConditionPB) => { - onChange?.(optionKey); - handleClose(); - }; - - const selectedField = useMemo(() => options.find((option) => option.key === value), [options, value]); - - return ( - <> -
{ - setOpen(true); - }} - > -
{selectedField?.content}
- -
- {open && ( - -
- -
-
- )} - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/sort/SortFieldsMenu.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/sort/SortFieldsMenu.tsx deleted file mode 100644 index 724c28467a..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/sort/SortFieldsMenu.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import React, { FC, useCallback } from 'react'; -import { MenuProps } from '@mui/material'; -import PropertiesList from '$app/components/database/components/property/PropertiesList'; -import { Field, sortService } from '$app/application/database'; -import { SortConditionPB } from '@/services/backend'; -import { useTranslation } from 'react-i18next'; -import { useViewId } from '$app/hooks'; -import Popover from '@mui/material/Popover'; - -const SortFieldsMenu: FC< - MenuProps & { - onInserted?: () => void; - } -> = ({ onInserted, ...props }) => { - const { t } = useTranslation(); - const viewId = useViewId(); - const addSort = useCallback( - async (field: Field) => { - await sortService.insertSort(viewId, { - fieldId: field.id, - condition: SortConditionPB.Ascending, - }); - props.onClose?.({}, 'backdropClick'); - onInserted?.(); - }, - [props, viewId, onInserted] - ); - - return ( - { - if (e.key === 'Escape') { - e.preventDefault(); - e.stopPropagation(); - props.onClose?.({}, 'escapeKeyDown'); - } - }} - keepMounted={false} - {...props} - > - { - props.onClose?.({}, 'escapeKeyDown'); - }} - showSearch={true} - onItemClick={addSort} - searchPlaceholder={t('grid.settings.sortBy')} - /> - - ); -}; - -export default SortFieldsMenu; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/sort/SortItem.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/sort/SortItem.tsx deleted file mode 100644 index fe1074bbde..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/sort/SortItem.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import { IconButton, Stack } from '@mui/material'; -import { FC, useCallback } from 'react'; -import { ReactComponent as CloseSvg } from '$app/assets/close.svg'; -import { Field, Sort, sortService } from '$app/application/database'; -import { PropertySelect } from '../property'; -import { SortConditionSelect } from './SortConditionSelect'; -import { useViewId } from '@/appflowy_app/hooks'; -import { SortConditionPB } from '@/services/backend'; - -export interface SortItemProps { - className?: string; - sort: Sort; -} - -export const SortItem: FC = ({ className, sort }) => { - const viewId = useViewId(); - - const handleFieldChange = useCallback( - (field: Field | undefined) => { - if (field) { - void sortService.updateSort(viewId, { - ...sort, - fieldId: field.id, - }); - } - }, - [viewId, sort] - ); - - const handleConditionChange = useCallback( - (value: SortConditionPB) => { - void sortService.updateSort(viewId, { - ...sort, - condition: value, - }); - }, - [viewId, sort] - ); - - const handleClick = useCallback(() => { - void sortService.deleteSort(viewId, sort); - }, [viewId, sort]); - - return ( - - - -
- - - -
-
- ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/sort/SortMenu.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/sort/SortMenu.tsx deleted file mode 100644 index 88df70b2e4..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/sort/SortMenu.tsx +++ /dev/null @@ -1,90 +0,0 @@ -import { Menu, MenuProps } from '@mui/material'; -import { FC, MouseEventHandler, useCallback, useState } from 'react'; -import { useViewId } from '$app/hooks'; -import { sortService } from '$app/application/database'; -import { useDatabaseSorts } from '../../Database.hooks'; -import { SortItem } from './SortItem'; - -import { useTranslation } from 'react-i18next'; -import { ReactComponent as AddSvg } from '$app/assets/add.svg'; -import { ReactComponent as DeleteSvg } from '$app/assets/delete.svg'; -import SortFieldsMenu from '$app/components/database/components/sort/SortFieldsMenu'; -import Button from '@mui/material/Button'; - -export const SortMenu: FC = (props) => { - const { onClose } = props; - const { t } = useTranslation(); - const viewId = useViewId(); - const sorts = useDatabaseSorts(); - const [anchorEl, setAnchorEl] = useState(null); - const openFieldListMenu = Boolean(anchorEl); - const handleClick = useCallback>((event) => { - setAnchorEl(event.currentTarget); - }, []); - - const deleteAllSorts = useCallback(() => { - void sortService.deleteAllSorts(viewId); - onClose?.({}, 'backdropClick'); - }, [viewId, onClose]); - - return ( - <> - { - if (e.key === 'Escape') { - e.preventDefault(); - e.stopPropagation(); - props.onClose?.({}, 'escapeKeyDown'); - } - }} - keepMounted={false} - MenuListProps={{ - className: 'py-1 w-[360px]', - }} - {...props} - onClose={onClose} - > -
-
- {sorts.map((sort) => ( - - ))} -
- -
- - -
-
-
- - { - setAnchorEl(null); - }} - anchorOrigin={{ - vertical: 'bottom', - horizontal: 'left', - }} - /> - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/sort/Sorts.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/sort/Sorts.tsx deleted file mode 100644 index 7a4fa57a6f..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/sort/Sorts.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import { Chip, Divider } from '@mui/material'; -import React, { MouseEventHandler, useCallback, useEffect, useState } from 'react'; -import { SortMenu } from './SortMenu'; -import { useTranslation } from 'react-i18next'; -import { ReactComponent as SortSvg } from '$app/assets/sort.svg'; -import { ReactComponent as DropDownSvg } from '$app/assets/dropdown.svg'; -import { useDatabase } from '$app/components/database'; - -export const Sorts = () => { - const { t } = useTranslation(); - const { sorts } = useDatabase(); - - const showSorts = sorts && sorts.length > 0; - const [anchorEl, setAnchorEl] = useState(null); - - const handleClick = useCallback>((event) => { - setAnchorEl(event.currentTarget); - }, []); - - const label = ( -
- - {t('grid.settings.sort')} - -
- ); - - const menuOpen = Boolean(anchorEl); - - useEffect(() => { - if (!showSorts) { - setAnchorEl(null); - } - }, [showSorts]); - - if (!showSorts) return null; - - return ( -
- - - setAnchorEl(null)} /> -
- ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/sort/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/sort/index.ts deleted file mode 100644 index e64dba3a6e..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/sort/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './SortMenu'; -export * from './Sorts'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/tab_bar/AddViewBtn.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/tab_bar/AddViewBtn.tsx deleted file mode 100644 index 717bf1eb18..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/tab_bar/AddViewBtn.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import React from 'react'; -import { IconButton } from '@mui/material'; -import { ReactComponent as AddSvg } from '$app/assets/add.svg'; -import { useTranslation } from 'react-i18next'; -import { ViewLayoutPB } from '@/services/backend'; -import { createDatabaseView } from '$app/application/database/database_view/database_view_service'; - -function AddViewBtn({ pageId, onCreated }: { pageId: string; onCreated: (id: string) => void }) { - const { t } = useTranslation(); - const onClick = async () => { - try { - const view = await createDatabaseView(pageId, ViewLayoutPB.Grid, t('editor.table')); - - onCreated(view.id); - } catch (e) { - console.error(e); - } - }; - - return ( -
- - - -
- ); -} - -export default AddViewBtn; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/tab_bar/DatabaseTabBar.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/tab_bar/DatabaseTabBar.tsx deleted file mode 100644 index f7375e0c70..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/tab_bar/DatabaseTabBar.tsx +++ /dev/null @@ -1,110 +0,0 @@ -import { forwardRef, FunctionComponent, SVGProps, useEffect, useMemo, useState } from 'react'; -import { ViewTabs, ViewTab } from './ViewTabs'; -import { useTranslation } from 'react-i18next'; -import AddViewBtn from '$app/components/database/components/tab_bar/AddViewBtn'; -import { ViewLayoutPB } from '@/services/backend'; -import { ReactComponent as GridSvg } from '$app/assets/grid.svg'; -import { ReactComponent as BoardSvg } from '$app/assets/board.svg'; -import { ReactComponent as DocumentSvg } from '$app/assets/document.svg'; -import ViewActions from '$app/components/database/components/tab_bar/ViewActions'; -import { Page } from '$app_reducers/pages/slice'; - -export interface DatabaseTabBarProps { - childViews: Page[]; - selectedViewId?: string; - setSelectedViewId?: (viewId: string) => void; - pageId: string; -} - -const DatabaseIcons: { - [key in ViewLayoutPB]: FunctionComponent & { title?: string | undefined }>; -} = { - [ViewLayoutPB.Document]: DocumentSvg, - [ViewLayoutPB.Grid]: GridSvg, - [ViewLayoutPB.Board]: BoardSvg, - [ViewLayoutPB.Calendar]: GridSvg, -}; - -export const DatabaseTabBar = forwardRef( - ({ pageId, childViews, selectedViewId, setSelectedViewId }, ref) => { - const { t } = useTranslation(); - const [contextMenuAnchorEl, setContextMenuAnchorEl] = useState(null); - const [contextMenuView, setContextMenuView] = useState(null); - const open = Boolean(contextMenuAnchorEl); - - const handleChange = (_: React.SyntheticEvent, newValue: string) => { - setSelectedViewId?.(newValue); - }; - - useEffect(() => { - if (selectedViewId === undefined && childViews.length > 0) { - setSelectedViewId?.(childViews[0].id); - } - }, [selectedViewId, setSelectedViewId, childViews]); - - const openMenu = (view: Page) => { - return (e: React.MouseEvent) => { - e.preventDefault(); - e.stopPropagation(); - setContextMenuView(view); - setContextMenuAnchorEl(e.currentTarget); - }; - }; - - const isSelected = useMemo( - () => childViews.some((view) => view.id === selectedViewId), - [childViews, selectedViewId] - ); - - if (childViews.length === 0) return null; - return ( -
-
- - {childViews.map((view) => { - const Icon = DatabaseIcons[view.layout]; - - return ( - } - iconPosition='start' - color='inherit' - label={view.name || t('grid.title.placeholder')} - value={view.id} - /> - ); - })} - - setSelectedViewId?.(id)} /> -
- {open && contextMenuView && ( - { - setContextMenuAnchorEl(null); - setContextMenuView(null); - }} - /> - )} -
- ); - } -); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/tab_bar/TextButton.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/tab_bar/TextButton.tsx deleted file mode 100644 index 60dfaa2e53..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/tab_bar/TextButton.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import { Button, ButtonProps, styled } from '@mui/material'; - -export const TextButton = styled(Button)(() => ({ - padding: '2px 6px', - fontSize: '0.75rem', - lineHeight: '1rem', - fontWeight: 400, - minWidth: 'unset', -})); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/tab_bar/ViewActions.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/tab_bar/ViewActions.tsx deleted file mode 100644 index be545e51e3..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/tab_bar/ViewActions.tsx +++ /dev/null @@ -1,103 +0,0 @@ -import React, { useCallback, useState } from 'react'; -import { useTranslation } from 'react-i18next'; -import { ReactComponent as DeleteSvg } from '$app/assets/delete.svg'; -import { ReactComponent as EditSvg } from '$app/assets/edit.svg'; -import { deleteView } from '$app/application/database/database_view/database_view_service'; -import { MenuProps, Menu } from '@mui/material'; -import RenameDialog from '$app/components/_shared/confirm_dialog/RenameDialog'; -import { Page } from '$app_reducers/pages/slice'; -import { useAppDispatch } from '$app/stores/store'; -import { updatePageName } from '$app_reducers/pages/async_actions'; -import KeyboardNavigation from '$app/components/_shared/keyboard_navigation/KeyboardNavigation'; - -enum ViewAction { - Rename, - Delete, -} - -function ViewActions({ view, pageId, ...props }: { pageId: string; view: Page } & MenuProps) { - const { t } = useTranslation(); - const viewId = view.id; - const dispatch = useAppDispatch(); - const [openRenameDialog, setOpenRenameDialog] = useState(false); - const renderContent = useCallback((title: string, Icon: React.FC>) => { - return ( -
- -
{title}
-
- ); - }, []); - - const onConfirm = useCallback( - async (key: ViewAction) => { - switch (key) { - case ViewAction.Rename: - setOpenRenameDialog(true); - break; - case ViewAction.Delete: - try { - await deleteView(viewId); - props.onClose?.({}, 'backdropClick'); - } catch (e) { - // toast.error(t('error.deleteView')); - } - - break; - default: - break; - } - }, - [viewId, props] - ); - const options = [ - { - key: ViewAction.Rename, - content: renderContent(t('button.rename'), EditSvg), - }, - - { - key: ViewAction.Delete, - content: renderContent(t('button.delete'), DeleteSvg), - disabled: viewId === pageId, - }, - ]; - - return ( - <> - - { - props.onClose?.({}, 'escapeKeyDown'); - }} - /> - - {openRenameDialog && ( - setOpenRenameDialog(false)} - onOk={async (val) => { - try { - await dispatch( - updatePageName({ - id: viewId, - name: val, - immediate: true, - }) - ); - setOpenRenameDialog(false); - props.onClose?.({}, 'backdropClick'); - } catch (e) { - // toast.error(t('error.renameView')); - } - }} - defaultValue={view.name} - /> - )} - - ); -} - -export default ViewActions; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/tab_bar/ViewTabs.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/tab_bar/ViewTabs.tsx deleted file mode 100644 index e2ff336c73..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/tab_bar/ViewTabs.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import { styled, Tab, TabProps, Tabs, TabsProps } from '@mui/material'; -import { HTMLAttributes } from 'react'; - -export const ViewTabs = styled((props: TabsProps) => )({ - minHeight: '28px', - - '& .MuiTabs-scroller': { - paddingBottom: '2px', - }, -}); - -export const ViewTab = styled((props: TabProps) => )({ - padding: '6px 12px', - minHeight: '28px', - fontSize: '12px', - lineHeight: '16px', - minWidth: 'unset', - margin: '4px 0', - - '&.Mui-selected': { - color: 'inherit', - }, -}); - -interface TabPanelProps extends HTMLAttributes { - children?: React.ReactNode; - index: number; - value: number; -} - -export function TabPanel(props: TabPanelProps) { - const { children, value, index, ...other } = props; - - const isActivated = value === index; - - return ( - - ); -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/tab_bar/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/tab_bar/index.ts deleted file mode 100644 index fc0c62963e..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/tab_bar/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './DatabaseTabBar'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/database.scss b/frontend/appflowy_tauri/src/appflowy_app/components/database/database.scss deleted file mode 100644 index 492ff2a713..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/database.scss +++ /dev/null @@ -1,19 +0,0 @@ -.database-collection { - ::-webkit-scrollbar { - width: 0px; - height: 0px; - } -} - -.checklist-item { - @apply my-1; - .delete-option-button { - display: none; - } - &:hover, &.selected { - background-color: var(--fill-list-hover); - .delete-option-button { - display: block; - } - } -} \ No newline at end of file diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/Grid.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/Grid.tsx deleted file mode 100644 index beb90c66dc..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/Grid.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import { FC } from 'react'; -import { GridTable, GridTableProps } from './grid_table'; - -export const Grid: FC = (props) => { - return ; -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/constants.ts b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/constants.ts deleted file mode 100644 index eadfadaa89..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/constants.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { Field, RowMeta } from '$app/application/database'; - -export const GridCalculateCountHeight = 40; - -export const GRID_ACTIONS_WIDTH = 64; - -export const DEFAULT_FIELD_WIDTH = 150; - -export enum RenderRowType { - Fields = 'fields', - Row = 'row', - NewRow = 'new-row', - CalculateRow = 'calculate-row', -} - -export interface CalculateRenderRow { - type: RenderRowType.CalculateRow; -} - -export interface FieldRenderRow { - type: RenderRowType.Fields; -} - -export interface CellRenderRow { - type: RenderRowType.Row; - data: { - meta: RowMeta; - }; -} - -export interface NewRenderRow { - type: RenderRowType.NewRow; - data: { - groupId?: string; - }; -} - -export type RenderRow = FieldRenderRow | CellRenderRow | NewRenderRow | CalculateRenderRow; - -export const fieldsToColumns = (fields: Field[]): GridColumn[] => { - return [ - { - type: GridColumnType.Action, - width: GRID_ACTIONS_WIDTH, - }, - ...fields.map((field) => ({ - field, - width: field.width || DEFAULT_FIELD_WIDTH, - type: GridColumnType.Field, - })), - { - type: GridColumnType.NewProperty, - width: DEFAULT_FIELD_WIDTH, - }, - ]; -}; - -export const rowMetasToRenderRow = (rowMetas: RowMeta[]): RenderRow[] => { - return [ - ...rowMetas.map((rowMeta) => ({ - type: RenderRowType.Row, - data: { - meta: rowMeta, - }, - })), - { - type: RenderRowType.NewRow, - data: {}, - }, - { - type: RenderRowType.CalculateRow, - }, - ]; -}; - -export enum GridColumnType { - Action, - Field, - NewProperty, -} - -export interface GridColumn { - field?: Field; - width: number; - type: GridColumnType; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_calculate/GridCalculate.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_calculate/GridCalculate.tsx deleted file mode 100644 index beed71fca4..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_calculate/GridCalculate.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import React from 'react'; -import { useDatabaseVisibilityRows } from '$app/components/database'; -import { Field } from '$app/application/database'; -import { DEFAULT_FIELD_WIDTH, GRID_ACTIONS_WIDTH } from '$app/components/database/grid/constants'; -import { useTranslation } from 'react-i18next'; - -interface Props { - field: Field; - index: number; - getContainerRef?: () => React.RefObject; -} - -export function GridCalculate({ field, index }: Props) { - const rowMetas = useDatabaseVisibilityRows(); - const count = rowMetas.length; - const width = index === 0 ? GRID_ACTIONS_WIDTH : field.width ?? DEFAULT_FIELD_WIDTH; - const { t } = useTranslation(); - - return ( -
- {field.isPrimary ? ( - <> - {t('grid.calculationTypeLabel.count')} - {count} - - ) : null} -
- ); -} - -export default GridCalculate; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_calculate/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_calculate/index.ts deleted file mode 100644 index 2bd3b71b1e..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_calculate/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './GridCalculate'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_cell/GridCell.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_cell/GridCell.tsx deleted file mode 100644 index 042ba1777d..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_cell/GridCell.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import React, { CSSProperties, memo } from 'react'; -import { GridColumn, RenderRow, RenderRowType } from '../constants'; -import GridNewRow from '$app/components/database/grid/grid_new_row/GridNewRow'; -import { GridCalculate } from '$app/components/database/grid/grid_calculate'; -import { areEqual } from 'react-window'; -import { Cell } from '$app/components/database/components'; -import { PrimaryCell } from '$app/components/database/grid/grid_cell'; - -const getRenderRowKey = (row: RenderRow) => { - if (row.type === RenderRowType.Row) { - return `row:${row.data.meta.id}`; - } - - return row.type; -}; - -interface GridCellProps { - row: RenderRow; - column: GridColumn; - columnIndex: number; - style: CSSProperties; - onEditRecord?: (rowId: string) => void; - getContainerRef?: () => React.RefObject; -} - -export const GridCell = memo(({ row, column, columnIndex, style, onEditRecord, getContainerRef }: GridCellProps) => { - const key = getRenderRowKey(row); - - const field = column.field; - - if (!field) return
; - - switch (row.type) { - case RenderRowType.Row: { - const { id: rowId, icon: rowIcon } = row.data.meta; - const renderRowCell = ; - - return ( -
- {field.isPrimary ? ( - - {renderRowCell} - - ) : ( - renderRowCell - )} -
- ); - } - - case RenderRowType.NewRow: - return ( -
- -
- ); - case RenderRowType.CalculateRow: - return ( -
- -
- ); - default: - return null; - } -}, areEqual); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_cell/PrimaryCell.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_cell/PrimaryCell.tsx deleted file mode 100644 index b9a734de7b..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_cell/PrimaryCell.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import React, { Suspense, useMemo, useRef } from 'react'; -import { ReactComponent as OpenIcon } from '$app/assets/open.svg'; -import { IconButton } from '@mui/material'; - -import { useGridTableHoverState } from '$app/components/database/grid/grid_row_actions'; - -export function PrimaryCell({ - onEditRecord, - icon, - getContainerRef, - rowId, - children, -}: { - rowId: string; - icon?: string; - onEditRecord?: (rowId: string) => void; - getContainerRef?: () => React.RefObject; - children?: React.ReactNode; -}) { - const cellRef = useRef(null); - - const containerRef = getContainerRef?.(); - const { hoverRowId } = useGridTableHoverState(containerRef); - - const showExpandIcon = useMemo(() => { - return hoverRowId === rowId; - }, [hoverRowId, rowId]); - - return ( -
- {icon &&
{icon}
} - {children} - - {showExpandIcon && ( -
- onEditRecord?.(rowId)} className={'h-6 w-6 text-sm'}> - - -
- )} -
-
- ); -} - -export default PrimaryCell; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_cell/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_cell/index.ts deleted file mode 100644 index 949d5054bf..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_cell/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './GridCell'; -export * from './PrimaryCell'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_field/GridField.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_field/GridField.tsx deleted file mode 100644 index 3c3921abf7..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_field/GridField.tsx +++ /dev/null @@ -1,179 +0,0 @@ -import { Button } from '@mui/material'; -import { DragEventHandler, FC, HTMLAttributes, memo, useCallback, useEffect, useMemo, useState } from 'react'; -import { useViewId } from '$app/hooks'; -import { DragItem, DropPosition, DragType, useDraggable, useDroppable, ScrollDirection } from '../../_shared'; -import { fieldService, Field } from '$app/application/database'; -import { Property } from '$app/components/database/components/property'; -import { GridResizer, GridFieldMenu } from '$app/components/database/grid/grid_field'; -import { areEqual } from 'react-window'; -import { useOpenMenu } from '$app/components/database/grid/grid_sticky_header/GridStickyHeader.hooks'; -import throttle from 'lodash-es/throttle'; - -export interface GridFieldProps extends HTMLAttributes { - field: Field; - onOpenMenu?: (id: string) => void; - onCloseMenu?: (id: string) => void; - resizeColumnWidth?: (width: number) => void; - getScrollElement?: () => HTMLElement | null; -} - -export const GridField: FC = memo( - ({ getScrollElement, resizeColumnWidth, onOpenMenu, onCloseMenu, field, ...props }) => { - const menuOpened = useOpenMenu(field.id); - const viewId = useViewId(); - const [propertyMenuOpened, setPropertyMenuOpened] = useState(false); - const [dropPosition, setDropPosition] = useState(DropPosition.Before); - - const draggingData = useMemo( - () => ({ - field, - }), - [field] - ); - - const { isDragging, attributes, listeners, setPreviewRef, previewRef } = useDraggable({ - type: DragType.Field, - data: draggingData, - scrollOnEdge: { - direction: ScrollDirection.Horizontal, - getScrollElement, - edgeGap: 80, - }, - }); - - const onDragOver = useMemo(() => { - return throttle((event) => { - const element = previewRef.current; - - if (!element) { - return; - } - - const { left, right } = element.getBoundingClientRect(); - const middle = (left + right) / 2; - - setDropPosition(event.clientX < middle ? DropPosition.Before : DropPosition.After); - }, 20); - }, [previewRef]); - - const onDrop = useCallback( - ({ data }: DragItem) => { - const dragField = data.field as Field; - - if (dragField.id === field.id) { - return; - } - - void fieldService.moveField(viewId, dragField.id, field.id); - }, - [viewId, field] - ); - - const { isOver, listeners: dropListeners } = useDroppable({ - accept: DragType.Field, - disabled: isDragging, - onDragOver, - onDrop, - }); - - const [menuAnchorPosition, setMenuAnchorPosition] = useState< - | { - top: number; - left: number; - } - | undefined - >(undefined); - - const open = Boolean(menuAnchorPosition) && menuOpened; - - const handleClick = useCallback(() => { - onOpenMenu?.(field.id); - }, [onOpenMenu, field.id]); - - const handleMenuClose = useCallback(() => { - onCloseMenu?.(field.id); - }, [onCloseMenu, field.id]); - - useEffect(() => { - if (!menuOpened) { - setMenuAnchorPosition(undefined); - return; - } - - const anchorElement = previewRef.current; - - if (!anchorElement) { - setMenuAnchorPosition(undefined); - return; - } - - anchorElement.scrollIntoView({ block: 'nearest' }); - - const rect = anchorElement.getBoundingClientRect(); - - setMenuAnchorPosition({ - top: rect.top + rect.height, - left: rect.left, - }); - }, [menuOpened, previewRef]); - - const handlePropertyMenuOpen = useCallback(() => { - setPropertyMenuOpened(true); - }, []); - - const handlePropertyMenuClose = useCallback(() => { - setPropertyMenuOpened(false); - }, []); - - return ( -
- - {open && ( - - )} -
- ); - }, - areEqual -); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_field/GridFieldMenu.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_field/GridFieldMenu.tsx deleted file mode 100644 index 1407fe30c2..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_field/GridFieldMenu.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import React, { useRef } from 'react'; -import Popover, { PopoverProps } from '@mui/material/Popover'; -import { Field } from '$app/application/database'; -import PropertyNameInput from '$app/components/database/components/property/PropertyNameInput'; -import { MenuList } from '@mui/material'; -import PropertyActions, { FieldAction } from '$app/components/database/components/property/PropertyActions'; - -interface Props extends PopoverProps { - field: Field; - onOpenPropertyMenu?: () => void; - onOpenMenu?: (fieldId: string) => void; -} - -export function GridFieldMenu({ field, onOpenPropertyMenu, onOpenMenu, onClose, ...props }: Props) { - const inputRef = useRef(null); - - return ( - e.stopPropagation()} - {...props} - onClose={onClose} - keepMounted={false} - onMouseDown={(e) => { - const isInput = inputRef.current?.contains(e.target as Node); - - if (isInput) return; - - e.stopPropagation(); - e.preventDefault(); - }} - > - - - onClose?.({}, 'backdropClick')} - onMenuItemClick={(action, newFieldId?: string) => { - if (action === FieldAction.EditProperty) { - onOpenPropertyMenu?.(); - } else if (newFieldId && (action === FieldAction.InsertLeft || action === FieldAction.InsertRight)) { - onOpenMenu?.(newFieldId); - } - - onClose?.({}, 'backdropClick'); - }} - fieldId={field.id} - /> - - - ); -} - -export default GridFieldMenu; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_field/GridNewField.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_field/GridNewField.tsx deleted file mode 100644 index d0b739298a..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_field/GridNewField.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import React, { useCallback } from 'react'; -import { useViewId } from '$app/hooks'; -import { useTranslation } from 'react-i18next'; -import { fieldService } from '$app/application/database'; -import { FieldType } from '@/services/backend'; -import Button from '@mui/material/Button'; -import { ReactComponent as AddSvg } from '$app/assets/add.svg'; - -export function GridNewField({ onInserted }: { onInserted?: (id: string) => void }) { - const viewId = useViewId(); - const { t } = useTranslation(); - - const handleClick = useCallback(async () => { - try { - const field = await fieldService.createField({ - viewId, - fieldType: FieldType.RichText, - }); - - onInserted?.(field.id); - } catch (e) { - // toast.error(t('grid.field.newPropertyFail')); - } - }, [onInserted, viewId]); - - return ( - <> - - - ); -} - -export default GridNewField; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_field/GridResizer.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_field/GridResizer.tsx deleted file mode 100644 index 12aef74996..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_field/GridResizer.tsx +++ /dev/null @@ -1,84 +0,0 @@ -import React, { useCallback, useRef, useState } from 'react'; -import { Field, fieldService } from '$app/application/database'; -import { useViewId } from '$app/hooks'; - -interface GridResizerProps { - field: Field; - onWidthChange?: (width: number) => void; -} - -const minWidth = 150; - -export function GridResizer({ field, onWidthChange }: GridResizerProps) { - const viewId = useViewId(); - const fieldId = field.id; - const width = field.width || 0; - const [isResizing, setIsResizing] = useState(false); - const [hover, setHover] = useState(false); - const startX = useRef(0); - const newWidthRef = useRef(width); - const onResize = useCallback( - (e: MouseEvent) => { - const diff = e.clientX - startX.current; - const newWidth = width + diff; - - if (newWidth < minWidth) { - return; - } - - newWidthRef.current = newWidth; - onWidthChange?.(newWidth); - }, - [width, onWidthChange] - ); - - const onResizeEnd = useCallback(() => { - setIsResizing(false); - - void fieldService.updateFieldSetting(viewId, fieldId, { - width: newWidthRef.current, - }); - document.removeEventListener('mousemove', onResize); - document.removeEventListener('mouseup', onResizeEnd); - }, [fieldId, onResize, viewId]); - - const onResizeStart = useCallback( - (e: React.MouseEvent) => { - e.stopPropagation(); - e.preventDefault(); - startX.current = e.clientX; - setIsResizing(true); - document.addEventListener('mousemove', onResize); - document.addEventListener('mouseup', onResizeEnd); - }, - [onResize, onResizeEnd] - ); - - return ( -
{ - e.stopPropagation(); - }} - onMouseEnter={() => { - setHover(true); - }} - onMouseLeave={() => { - setHover(false); - }} - style={{ - right: `-3px`, - }} - className={'absolute top-0 z-10 h-full cursor-col-resize'} - > -
-
- ); -} - -export default GridResizer; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_field/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_field/index.ts deleted file mode 100644 index 384ee2af3b..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_field/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from './GridField'; -export * from './GridFieldMenu'; -export * from './GridNewField'; -export * from './GridResizer'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_new_row/GridNewRow.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_new_row/GridNewRow.tsx deleted file mode 100644 index 4dc70e21dc..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_new_row/GridNewRow.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import React, { useCallback } from 'react'; -import { rowService } from '$app/application/database'; -import { useViewId } from '$app/hooks'; -import { ReactComponent as AddSvg } from '$app/assets/add.svg'; -import { useTranslation } from 'react-i18next'; - -interface Props { - index: number; - groupId?: string; - getContainerRef?: () => React.RefObject; -} - -const CSS_HIGHLIGHT_PROPERTY = 'bg-content-blue-50'; - -function GridNewRow({ index, groupId, getContainerRef }: Props) { - const viewId = useViewId(); - - const { t } = useTranslation(); - const handleClick = useCallback(() => { - void rowService.createRow(viewId, { - groupId, - }); - }, [viewId, groupId]); - - const toggleCssProperty = useCallback( - (status: boolean) => { - const container = getContainerRef?.()?.current; - - if (!container) return; - - const newRowCells = container.querySelectorAll('.grid-new-row'); - - newRowCells.forEach((cell) => { - if (status) { - cell.classList.add(CSS_HIGHLIGHT_PROPERTY); - } else { - cell.classList.remove(CSS_HIGHLIGHT_PROPERTY); - } - }); - }, - [getContainerRef] - ); - - return ( -
{ - toggleCssProperty(true); - }} - onMouseLeave={() => { - toggleCssProperty(false); - }} - onClick={handleClick} - className={'grid-new-row flex grow cursor-pointer text-text-title'} - > - - - {t('grid.row.newRow')} - -
- ); -} - -export default GridNewRow; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_overlay/GridTableOverlay.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_overlay/GridTableOverlay.tsx deleted file mode 100644 index 07ece5dec2..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_overlay/GridTableOverlay.tsx +++ /dev/null @@ -1,75 +0,0 @@ -import React, { useCallback, useEffect, useState } from 'react'; -import { - GridRowContextMenu, - GridRowActions, - useGridTableHoverState, -} from '$app/components/database/grid/grid_row_actions'; -import DeleteConfirmDialog from '$app/components/_shared/confirm_dialog/DeleteConfirmDialog'; -import { useTranslation } from 'react-i18next'; - -function GridTableOverlay({ - containerRef, - getScrollElement, -}: { - containerRef: React.MutableRefObject; - getScrollElement: () => HTMLDivElement | null; -}) { - const [hoverRowTop, setHoverRowTop] = useState(); - - const { t } = useTranslation(); - const [openConfirm, setOpenConfirm] = useState(false); - const [confirmModalProps, setConfirmModalProps] = useState< - | { - onOk: () => Promise; - onCancel: () => void; - } - | undefined - >(undefined); - - const { hoverRowId } = useGridTableHoverState(containerRef); - - const handleOpenConfirm = useCallback((onOk: () => Promise, onCancel: () => void) => { - setOpenConfirm(true); - setConfirmModalProps({ onOk, onCancel }); - }, []); - - useEffect(() => { - const container = containerRef.current; - - if (!container) return; - - const cell = container.querySelector(`[data-key="row:${hoverRowId}"]`); - - if (!cell) return; - const top = (cell as HTMLDivElement).style.top; - - setHoverRowTop(top); - }, [containerRef, hoverRowId]); - - return ( -
- - - {openConfirm && ( - { - setOpenConfirm(false); - }} - {...confirmModalProps} - /> - )} -
- ); -} - -export default GridTableOverlay; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_row_actions/GridRowActions.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_row_actions/GridRowActions.hooks.ts deleted file mode 100644 index a4251c9ed5..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_row_actions/GridRowActions.hooks.ts +++ /dev/null @@ -1,244 +0,0 @@ -import React, { useCallback, useEffect, useRef, useState } from 'react'; -import { useViewId } from '$app/hooks'; -import { rowService } from '$app/application/database'; -import { autoScrollOnEdge, ScrollDirection } from '$app/components/database/_shared/dnd/utils'; -import { useSortsCount } from '$app/components/database'; -import { deleteAllSorts } from '$app/application/database/sort/sort_service'; - -export function getCellsWithRowId(rowId: string, container: HTMLDivElement) { - return Array.from(container.querySelectorAll(`[data-key^="row:${rowId}"]`)); -} - -const SELECTED_ROW_CSS_PROPERTY = 'bg-content-blue-50'; - -export function toggleProperty( - container: HTMLDivElement, - rowId: string, - status: boolean, - property = SELECTED_ROW_CSS_PROPERTY -) { - const rowColumns = getCellsWithRowId(rowId, container); - - rowColumns.forEach((column, index) => { - if (index === 0) return; - if (status) { - column.classList.add(property); - } else { - column.classList.remove(property); - } - }); -} - -function createVirtualDragElement(rowId: string, container: HTMLDivElement) { - const cells = getCellsWithRowId(rowId, container); - - const cell = cells[0] as HTMLDivElement; - - if (!cell) return null; - - const row = document.createElement('div'); - - row.style.display = 'flex'; - row.style.position = 'absolute'; - row.style.top = cell.style.top; - const left = Number(cell.style.left.split('px')[0]) + 64; - - row.style.left = `${left}px`; - row.style.background = 'var(--content-blue-50)'; - cells.forEach((cell) => { - const node = cell.cloneNode(true) as HTMLDivElement; - - if (!node.classList.contains('grid-cell')) return; - - node.style.top = ''; - node.style.position = ''; - node.style.left = ''; - node.style.width = (cell as HTMLDivElement).style.width; - node.style.height = (cell as HTMLDivElement).style.height; - node.className = 'flex items-center border-r border-b border-divider-line opacity-50'; - row.appendChild(node); - }); - - cell.parentElement?.appendChild(row); - return row; -} - -export function useDraggableGridRow( - rowId: string, - containerRef: React.RefObject, - getScrollElement: () => HTMLDivElement | null, - onOpenConfirm: (onOk: () => Promise, onCancel: () => void) => void -) { - const viewId = useViewId(); - const sortsCount = useSortsCount(); - - const [isDragging, setIsDragging] = useState(false); - const dropRowIdRef = useRef(undefined); - const previewRef = useRef(); - - const onDragStart = useCallback( - (e: React.DragEvent) => { - e.dataTransfer.effectAllowed = 'move'; - e.dataTransfer.dropEffect = 'move'; - const container = containerRef.current; - - if (container) { - const row = createVirtualDragElement(rowId, container); - - if (row) { - previewRef.current = row; - e.dataTransfer.setDragImage(row, 0, 0); - } - } - - const scrollParent = getScrollElement(); - - if (scrollParent) { - autoScrollOnEdge({ - element: scrollParent, - direction: ScrollDirection.Vertical, - edgeGap: 20, - }); - } - - setIsDragging(true); - }, - [containerRef, rowId, getScrollElement] - ); - - const moveRowTo = useCallback( - async (toRowId: string) => { - return rowService.moveRow(viewId, rowId, toRowId); - }, - [viewId, rowId] - ); - - useEffect(() => { - if (!isDragging) { - if (previewRef.current) { - const row = previewRef.current; - - previewRef.current = undefined; - row?.remove(); - } - - return; - } - - const container = containerRef.current; - - if (!container) { - return; - } - - const onDragOver = (e: DragEvent) => { - e.preventDefault(); - const target = e.target as HTMLElement; - const cell = target.closest('[data-key]'); - const rowId = cell?.getAttribute('data-key')?.split(':')[1]; - - const oldRowId = dropRowIdRef.current; - - if (oldRowId) { - toggleProperty(container, oldRowId, false); - } - - if (!rowId) return; - - const rowColumns = getCellsWithRowId(rowId, container); - - dropRowIdRef.current = rowId; - if (!rowColumns.length) return; - - toggleProperty(container, rowId, true); - }; - - const onDragEnd = () => { - const oldRowId = dropRowIdRef.current; - - if (oldRowId) { - toggleProperty(container, oldRowId, false); - } - - dropRowIdRef.current = undefined; - setIsDragging(false); - }; - - const onDrop = async (e: DragEvent) => { - e.preventDefault(); - e.stopPropagation(); - const dropRowId = dropRowIdRef.current; - - toggleProperty(container, rowId, false); - if (dropRowId) { - if (sortsCount > 0) { - onOpenConfirm( - async () => { - await deleteAllSorts(viewId); - await moveRowTo(dropRowId); - }, - () => { - void moveRowTo(dropRowId); - } - ); - } else { - void moveRowTo(dropRowId); - } - - toggleProperty(container, dropRowId, false); - } - - setIsDragging(false); - container.removeEventListener('dragover', onDragOver); - container.removeEventListener('dragend', onDragEnd); - container.removeEventListener('drop', onDrop); - }; - - container.addEventListener('dragover', onDragOver); - container.addEventListener('dragend', onDragEnd); - container.addEventListener('drop', onDrop); - }, [isDragging, containerRef, moveRowTo, onOpenConfirm, rowId, sortsCount, viewId]); - - return { - isDragging, - onDragStart, - }; -} - -export function useGridTableHoverState(containerRef?: React.RefObject) { - const [hoverRowId, setHoverRowId] = useState(undefined); - - useEffect(() => { - const container = containerRef?.current; - - if (!container) return; - const onMouseMove = (e: MouseEvent) => { - const target = e.target as HTMLElement; - const cell = target.closest('[data-key]'); - - if (!cell) { - return; - } - - const hoverRowId = cell.getAttribute('data-key')?.split(':')[1]; - - setHoverRowId(hoverRowId); - }; - - const onMouseLeave = () => { - setHoverRowId(undefined); - }; - - container.addEventListener('mousemove', onMouseMove); - container.addEventListener('mouseleave', onMouseLeave); - - return () => { - container.removeEventListener('mousemove', onMouseMove); - container.removeEventListener('mouseleave', onMouseLeave); - }; - }, [containerRef]); - - return { - hoverRowId, - }; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_row_actions/GridRowActions.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_row_actions/GridRowActions.tsx deleted file mode 100644 index f4b39e2561..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_row_actions/GridRowActions.tsx +++ /dev/null @@ -1,135 +0,0 @@ -import React, { useCallback, useState } from 'react'; -import { IconButton, Tooltip } from '@mui/material'; -import { ReactComponent as AddSvg } from '$app/assets/add.svg'; -import { GRID_ACTIONS_WIDTH } from '$app/components/database/grid/constants'; -import { rowService } from '$app/application/database'; -import { useViewId } from '$app/hooks'; -import { GridRowDragButton, GridRowMenu, toggleProperty } from '$app/components/database/grid/grid_row_actions'; -import { OrderObjectPositionTypePB } from '@/services/backend'; -import { useSortsCount } from '$app/components/database'; -import { useTranslation } from 'react-i18next'; -import { deleteAllSorts } from '$app/application/database/sort/sort_service'; - -export function GridRowActions({ - rowId, - rowTop, - containerRef, - getScrollElement, - onOpenConfirm, -}: { - onOpenConfirm: (onOk: () => Promise, onCancel: () => void) => void; - rowId?: string; - rowTop?: string; - containerRef: React.MutableRefObject; - getScrollElement: () => HTMLDivElement | null; -}) { - const { t } = useTranslation(); - const viewId = useViewId(); - const sortsCount = useSortsCount(); - const [menuRowId, setMenuRowId] = useState(undefined); - const [menuPosition, setMenuPosition] = useState< - | { - top: number; - left: number; - } - | undefined - >(undefined); - - const openMenu = Boolean(menuPosition); - - const handleCloseMenu = useCallback(() => { - setMenuPosition(undefined); - if (containerRef.current && menuRowId) { - toggleProperty(containerRef.current, menuRowId, false); - } - }, [containerRef, menuRowId]); - - const handleInsertRecordBelow = useCallback( - async (rowId: string) => { - await rowService.createRow(viewId, { - position: OrderObjectPositionTypePB.After, - rowId: rowId, - }); - handleCloseMenu(); - }, - [viewId, handleCloseMenu] - ); - - const handleOpenMenu = (e: React.MouseEvent) => { - const target = e.target as HTMLButtonElement; - const rect = target.getBoundingClientRect(); - - if (containerRef.current && rowId) { - toggleProperty(containerRef.current, rowId, true); - } - - setMenuRowId(rowId); - setMenuPosition({ - top: rect.top + rect.height / 2, - left: rect.left + rect.width, - }); - }; - - return ( - <> - {rowId && rowTop && ( -
- - { - if (sortsCount > 0) { - onOpenConfirm( - async () => { - await deleteAllSorts(viewId); - void handleInsertRecordBelow(rowId); - }, - () => { - void handleInsertRecordBelow(rowId); - } - ); - } else { - void handleInsertRecordBelow(rowId); - } - }} - > - - - - -
- )} - {menuRowId && ( - - )} - - ); -} - -export default GridRowActions; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_row_actions/GridRowContextMenu.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_row_actions/GridRowContextMenu.tsx deleted file mode 100644 index a93188ddc4..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_row_actions/GridRowContextMenu.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import React, { useCallback, useEffect, useMemo, useState } from 'react'; -import GridRowMenu from './GridRowMenu'; -import { toggleProperty } from './GridRowActions.hooks'; - -export function GridRowContextMenu({ - containerRef, - hoverRowId, - onOpenConfirm, -}: { - hoverRowId?: string; - onOpenConfirm: (onOk: () => Promise, onCancel: () => void) => void; - containerRef: React.MutableRefObject; -}) { - const [position, setPosition] = useState<{ left: number; top: number } | undefined>(); - - const [rowId, setRowId] = useState(); - - const isContextMenuOpen = useMemo(() => { - return !!position; - }, [position]); - - const closeContextMenu = useCallback(() => { - setPosition(undefined); - const container = containerRef.current; - - if (!container || !rowId) return; - toggleProperty(container, rowId, false); - // setRowId(undefined); - }, [rowId, containerRef]); - - const openContextMenu = useCallback( - (event: MouseEvent) => { - event.preventDefault(); - event.stopPropagation(); - const container = containerRef.current; - - if (!container || !hoverRowId) return; - toggleProperty(container, hoverRowId, true); - setRowId(hoverRowId); - setPosition({ - left: event.clientX, - top: event.clientY, - }); - }, - [containerRef, hoverRowId] - ); - - useEffect(() => { - const container = containerRef.current; - - if (!container) { - return; - } - - container.addEventListener('contextmenu', openContextMenu); - return () => { - container.removeEventListener('contextmenu', openContextMenu); - }; - }, [containerRef, openContextMenu]); - - return rowId ? ( - - ) : null; -} - -export default GridRowContextMenu; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_row_actions/GridRowDragButton.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_row_actions/GridRowDragButton.tsx deleted file mode 100644 index 0790e48183..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_row_actions/GridRowDragButton.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import { useDraggableGridRow } from './GridRowActions.hooks'; -import { IconButton, Tooltip } from '@mui/material'; -import { ReactComponent as DragSvg } from '$app/assets/drag.svg'; -import { useTranslation } from 'react-i18next'; - -export function GridRowDragButton({ - rowId, - containerRef, - onClick, - getScrollElement, - onOpenConfirm, -}: { - onOpenConfirm: (onOk: () => Promise, onCancel: () => void) => void; - rowId: string; - onClick?: (e: React.MouseEvent) => void; - containerRef: React.MutableRefObject; - getScrollElement: () => HTMLDivElement | null; -}) { - const { t } = useTranslation(); - - const [openTooltip, setOpenTooltip] = useState(false); - const { onDragStart, isDragging } = useDraggableGridRow(rowId, containerRef, getScrollElement, onOpenConfirm); - - useEffect(() => { - if (isDragging) { - setOpenTooltip(false); - } - }, [isDragging]); - - return ( - <> - { - setOpenTooltip(true); - }} - onClose={() => { - setOpenTooltip(false); - }} - placement='top' - disableInteractive={true} - title={t('grid.row.dragAndClick')} - > - - - - - - ); -} - -export default GridRowDragButton; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_row_actions/GridRowMenu.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_row_actions/GridRowMenu.tsx deleted file mode 100644 index 2190e8739b..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_row_actions/GridRowMenu.tsx +++ /dev/null @@ -1,160 +0,0 @@ -import React, { useCallback, useMemo } from 'react'; -import { ReactComponent as UpSvg } from '$app/assets/up.svg'; -import { ReactComponent as AddSvg } from '$app/assets/add.svg'; -import { ReactComponent as DelSvg } from '$app/assets/delete.svg'; -import { ReactComponent as CopySvg } from '$app/assets/copy.svg'; -import Popover, { PopoverProps } from '@mui/material/Popover'; -import { useViewId } from '$app/hooks'; -import { useTranslation } from 'react-i18next'; -import { rowService } from '$app/application/database'; -import { OrderObjectPositionTypePB } from '@/services/backend'; -import KeyboardNavigation, { - KeyboardNavigationOption, -} from '$app/components/_shared/keyboard_navigation/KeyboardNavigation'; -import { useSortsCount } from '$app/components/database'; -import { deleteAllSorts } from '$app/application/database/sort/sort_service'; - -enum RowAction { - InsertAbove, - InsertBelow, - Duplicate, - Delete, -} -interface Props extends PopoverProps { - rowId: string; - onOpenConfirm?: (onOk: () => Promise, onCancel: () => void) => void; -} - -export function GridRowMenu({ onOpenConfirm, rowId, onClose, ...props }: Props) { - const viewId = useViewId(); - const sortsCount = useSortsCount(); - - const { t } = useTranslation(); - - const handleInsertRecordBelow = useCallback(() => { - void rowService.createRow(viewId, { - position: OrderObjectPositionTypePB.After, - rowId: rowId, - }); - }, [viewId, rowId]); - - const handleInsertRecordAbove = useCallback(() => { - void rowService.createRow(viewId, { - position: OrderObjectPositionTypePB.Before, - rowId: rowId, - }); - }, [rowId, viewId]); - - const handleDelRow = useCallback(() => { - void rowService.deleteRow(viewId, rowId); - }, [viewId, rowId]); - - const handleDuplicateRow = useCallback(() => { - void rowService.duplicateRow(viewId, rowId); - }, [viewId, rowId]); - - const renderContent = useCallback((title: string, Icon: React.FC>) => { - return ( -
- -
{title}
-
- ); - }, []); - - const handleAction = useCallback( - (confirmKey?: RowAction) => { - switch (confirmKey) { - case RowAction.InsertAbove: - handleInsertRecordAbove(); - break; - case RowAction.InsertBelow: - handleInsertRecordBelow(); - break; - case RowAction.Duplicate: - handleDuplicateRow(); - break; - case RowAction.Delete: - handleDelRow(); - break; - default: - break; - } - }, - [handleDelRow, handleDuplicateRow, handleInsertRecordAbove, handleInsertRecordBelow] - ); - - const onConfirm = useCallback( - (key: RowAction) => { - if (sortsCount > 0) { - onOpenConfirm?.( - async () => { - await deleteAllSorts(viewId); - handleAction(key); - }, - () => { - handleAction(key); - } - ); - } else { - handleAction(key); - } - - onClose?.({}, 'backdropClick'); - }, - [handleAction, onClose, onOpenConfirm, sortsCount, viewId] - ); - - const options: KeyboardNavigationOption[] = useMemo( - () => [ - { - key: RowAction.InsertAbove, - content: renderContent(t('grid.row.insertRecordAbove'), UpSvg), - }, - { - key: RowAction.InsertBelow, - content: renderContent(t('grid.row.insertRecordBelow'), AddSvg), - }, - { - key: RowAction.Duplicate, - content: renderContent(t('grid.row.duplicate'), CopySvg), - }, - - { - key: 100, - content:
, - children: [], - }, - { - key: RowAction.Delete, - content: renderContent(t('grid.row.delete'), DelSvg), - }, - ], - [renderContent, t] - ); - - return ( - <> - -
- { - onClose?.({}, 'escapeKeyDown'); - }} - /> -
-
- - ); -} - -export default GridRowMenu; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_row_actions/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_row_actions/index.ts deleted file mode 100644 index fb50b6248c..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_row_actions/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * from './GridRowActions.hooks'; -export * from './GridRowActions'; -export * from './GridRowContextMenu'; -export * from './GridRowDragButton'; -export * from './GridRowMenu'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_sticky_header/GridStickyHeader.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_sticky_header/GridStickyHeader.hooks.ts deleted file mode 100644 index ac5c0688b9..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_sticky_header/GridStickyHeader.hooks.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { createContext, useContext } from 'react'; - -export const OpenMenuContext = createContext(null); - -export const useOpenMenu = (id: string) => { - const context = useContext(OpenMenuContext); - - return context === id; -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_sticky_header/GridStickyHeader.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_sticky_header/GridStickyHeader.tsx deleted file mode 100644 index e9d01508b1..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_sticky_header/GridStickyHeader.tsx +++ /dev/null @@ -1,112 +0,0 @@ -import React, { useCallback, useState } from 'react'; -import { GridChildComponentProps, GridOnScrollProps, VariableSizeGrid as Grid } from 'react-window'; -import AutoSizer from 'react-virtualized-auto-sizer'; -import { useGridColumn } from '$app/components/database/grid/grid_table'; -import { GridField } from 'src/appflowy_app/components/database/grid/grid_field'; -import NewProperty from '$app/components/database/components/property/NewProperty'; -import { GridColumn, GridColumnType, RenderRow } from '$app/components/database/grid/constants'; -import { OpenMenuContext } from '$app/components/database/grid/grid_sticky_header/GridStickyHeader.hooks'; - -const GridStickyHeader = React.forwardRef< - Grid | null, - { - columns: GridColumn[]; - getScrollElement?: () => HTMLDivElement | null; - onScroll?: (props: GridOnScrollProps) => void; - } ->(({ onScroll, columns, getScrollElement }, ref) => { - const { columnWidth, resizeColumnWidth } = useGridColumn( - columns, - ref as React.MutableRefObject | null> - ); - - const [openMenuId, setOpenMenuId] = useState(null); - - const handleOpenMenu = useCallback((id: string) => { - setOpenMenuId(id); - }, []); - - const handleCloseMenu = useCallback((id: string) => { - setOpenMenuId((prev) => { - if (prev === id) { - return null; - } - - return prev; - }); - }, []); - - const Cell = useCallback( - ({ columnIndex, style, data }: GridChildComponentProps) => { - const column = data[columnIndex]; - - if (!column || column.type === GridColumnType.Action) return
; - if (column.type === GridColumnType.NewProperty) { - const width = (style.width || 0) as number; - - return ( -
- -
- ); - } - - const field = column.field; - - if (!field) return
; - - return ( - resizeColumnWidth(columnIndex, width)} - field={field} - getScrollElement={getScrollElement} - /> - ); - }, - [handleCloseMenu, handleOpenMenu, resizeColumnWidth, getScrollElement] - ); - - return ( - - - {({ height, width }: { height: number; width: number }) => { - return ( - 36} - rowCount={1} - columnCount={columns.length} - columnWidth={columnWidth} - ref={ref} - onScroll={onScroll} - itemData={columns} - style={{ overscrollBehavior: 'none' }} - > - {Cell} - - ); - }} - - - ); -}); - -export default GridStickyHeader; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_table/GridTable.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_table/GridTable.hooks.ts deleted file mode 100644 index 0d676f3bb2..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_table/GridTable.hooks.ts +++ /dev/null @@ -1,67 +0,0 @@ -import React, { useCallback, useEffect, useState } from 'react'; -import { DEFAULT_FIELD_WIDTH, GRID_ACTIONS_WIDTH, GridColumn, RenderRow } from '$app/components/database/grid/constants'; -import { VariableSizeGrid as Grid } from 'react-window'; - -export function useGridRow() { - const rowHeight = useCallback(() => { - return 36; - }, []); - - return { - rowHeight, - }; -} - -export function useGridColumn( - columns: GridColumn[], - ref: React.RefObject | null> -) { - const [columnWidths, setColumnWidths] = useState([]); - - useEffect(() => { - setColumnWidths( - columns.map((field, index) => (index === 0 ? GRID_ACTIONS_WIDTH : field.width || DEFAULT_FIELD_WIDTH)) - ); - ref.current?.resetAfterColumnIndex(0); - }, [columns, ref]); - - const resizeColumnWidth = useCallback( - (index: number, width: number) => { - setColumnWidths((columnWidths) => { - if (columnWidths[index] === width) { - return columnWidths; - } - - const newColumnWidths = [...columnWidths]; - - newColumnWidths[index] = width; - - return newColumnWidths; - }); - - if (ref.current) { - ref.current.resetAfterColumnIndex(index); - } - }, - [ref] - ); - - const columnWidth = useCallback( - (index: number) => { - if (index === 0) return GRID_ACTIONS_WIDTH; - return columnWidths[index] || DEFAULT_FIELD_WIDTH; - }, - [columnWidths] - ); - - return { - columnWidth, - resizeColumnWidth, - }; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_table/GridTable.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_table/GridTable.tsx deleted file mode 100644 index 0cd17d6a05..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_table/GridTable.tsx +++ /dev/null @@ -1,161 +0,0 @@ -import React, { FC, useCallback, useMemo, useRef } from 'react'; -import { RowMeta } from '$app/application/database'; -import { useDatabaseRendered, useDatabaseVisibilityFields, useDatabaseVisibilityRows } from '../../Database.hooks'; -import { fieldsToColumns, GridColumn, RenderRow, RenderRowType, rowMetasToRenderRow } from '../constants'; -import { CircularProgress } from '@mui/material'; -import { GridChildComponentProps, GridOnScrollProps, VariableSizeGrid as Grid } from 'react-window'; -import AutoSizer from 'react-virtualized-auto-sizer'; -import { GridCell } from 'src/appflowy_app/components/database/grid/grid_cell'; -import { useGridColumn, useGridRow } from './GridTable.hooks'; -import GridStickyHeader from '$app/components/database/grid/grid_sticky_header/GridStickyHeader'; -import GridTableOverlay from '$app/components/database/grid/grid_overlay/GridTableOverlay'; -import ReactDOM from 'react-dom'; -import { useViewId } from '$app/hooks'; - -export interface GridTableProps { - onEditRecord: (rowId: string) => void; -} - -export const GridTable: FC = React.memo(({ onEditRecord }) => { - const rowMetas = useDatabaseVisibilityRows(); - const fields = useDatabaseVisibilityFields(); - const renderRows = useMemo(() => rowMetasToRenderRow(rowMetas as RowMeta[]), [rowMetas]); - const columns = useMemo(() => fieldsToColumns(fields), [fields]); - const ref = useRef< - Grid<{ - columns: GridColumn[]; - renderRows: RenderRow[]; - }> - >(null); - const { columnWidth } = useGridColumn( - columns, - ref as React.MutableRefObject | null> - ); - const viewId = useViewId(); - const { rowHeight } = useGridRow(); - const onRendered = useDatabaseRendered(); - - const getItemKey = useCallback( - ({ columnIndex, rowIndex }: { columnIndex: number; rowIndex: number }) => { - const row = renderRows[rowIndex]; - const column = columns[columnIndex]; - - const field = column.field; - - if (row.type === RenderRowType.Row) { - if (field) { - return `${row.data.meta.id}:${field.id}`; - } - - return `${row.data.meta.id}:${column.type}`; - } - - if (field) { - return `${row.type}:${field.id}`; - } - - return `${row.type}:${column.type}`; - }, - [columns, renderRows] - ); - - const getContainerRef = useCallback(() => { - return containerRef; - }, []); - - const Cell = useCallback( - ({ columnIndex, rowIndex, style, data }: GridChildComponentProps) => { - const row = data.renderRows[rowIndex]; - const column = data.columns[columnIndex]; - - return ( - - ); - }, - [getContainerRef, onEditRecord] - ); - - const staticGrid = useRef | null>(null); - - const onScroll = useCallback(({ scrollLeft, scrollUpdateWasRequested }: GridOnScrollProps) => { - if (!scrollUpdateWasRequested) { - staticGrid.current?.scrollTo({ scrollLeft, scrollTop: 0 }); - } - }, []); - - const onHeaderScroll = useCallback(({ scrollLeft }: GridOnScrollProps) => { - ref.current?.scrollTo({ scrollLeft }); - }, []); - - const containerRef = useRef(null); - const scrollElementRef = useRef(null); - - const getScrollElement = useCallback(() => { - return scrollElementRef.current; - }, []); - - return ( -
- {fields.length === 0 && ( -
- -
- )} -
- -
- -
- - {({ height, width }: { height: number; width: number }) => ( - { - scrollElementRef.current = el; - onRendered(viewId); - }} - innerRef={containerRef} - > - {Cell} - - )} - - {containerRef.current - ? ReactDOM.createPortal( - , - containerRef.current - ) - : null} -
-
- ); -}); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_table/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_table/index.ts deleted file mode 100644 index dfdb9b7949..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_table/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './GridTable'; -export * from './GridTable.hooks'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/index.ts deleted file mode 100644 index 762542e7cb..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './Grid'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/database/index.ts deleted file mode 100644 index 42a6f31592..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './Database.hooks'; -export * from './Database'; -export * from './DatabaseTitle'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/Document.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/document/Document.tsx deleted file mode 100644 index 079a6fd75f..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/Document.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import React, { useCallback, useEffect, useMemo, useState } from 'react'; -import Editor from '$app/components/editor/Editor'; -import { DocumentHeader } from 'src/appflowy_app/components/document/document_header'; -import { useAppDispatch, useAppSelector } from '$app/stores/store'; -import { updatePageName } from '$app_reducers/pages/async_actions'; -import { PageCover } from '$app_reducers/pages/slice'; - -export function Document({ id }: { id: string }) { - const page = useAppSelector((state) => state.pages.pageMap[id]); - - const [cover, setCover] = useState(undefined); - const dispatch = useAppDispatch(); - - const onTitleChange = useCallback( - (newTitle: string) => { - void dispatch( - updatePageName({ - id, - name: newTitle, - }) - ); - }, - [dispatch, id] - ); - - const view = useMemo(() => { - return { - ...page, - cover, - }; - }, [page, cover]); - - useEffect(() => { - return () => { - setCover(undefined); - }; - }, [id]); - - if (!page) return null; - - return ( -
- -
-
- -
-
-
- ); -} - -export default Document; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/document_header/DocumentHeader.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/document/document_header/DocumentHeader.tsx deleted file mode 100644 index f6e8736c54..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/document_header/DocumentHeader.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import React, { memo, useCallback, useEffect, useRef, useState } from 'react'; -import { Page, PageCover, PageIcon } from '$app_reducers/pages/slice'; -import ViewTitle from '$app/components/_shared/view_title/ViewTitle'; -import { updatePageIcon } from '$app/application/folder/page.service'; - -interface DocumentHeaderProps { - page: Page; - onUpdateCover: (cover?: PageCover) => void; -} - -export function DocumentHeader({ page, onUpdateCover }: DocumentHeaderProps) { - const pageId = page.id; - const ref = useRef(null); - - const [forceHover, setForceHover] = useState(false); - const onUpdateIcon = useCallback( - async (icon: PageIcon) => { - await updatePageIcon(pageId, icon.value ? icon : undefined); - }, - [pageId] - ); - - useEffect(() => { - const parent = ref.current?.parentElement; - - if (!parent) return; - - const documentDom = parent.querySelector('.appflowy-editor') as HTMLElement; - - if (!documentDom) return; - - const handleMouseMove = (e: MouseEvent) => { - const isMoveInTitle = Boolean(e.target instanceof HTMLElement && e.target.closest('.document-title')); - const isMoveInHeader = Boolean(e.target instanceof HTMLElement && e.target.closest('.document-header')); - - setForceHover(isMoveInTitle || isMoveInHeader); - }; - - documentDom.addEventListener('mousemove', handleMouseMove); - return () => { - documentDom.removeEventListener('mousemove', handleMouseMove); - }; - }, []); - - if (!page) return null; - return ( -
- -
- ); -} - -export default memo(DocumentHeader); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/document_header/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/document/document_header/index.ts deleted file mode 100644 index 00f48716bf..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/document_header/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './DocumentHeader'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/document/index.ts deleted file mode 100644 index a844aa51ad..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './Document'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/Editor.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/Editor.hooks.ts deleted file mode 100644 index 1fc25346d2..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/Editor.hooks.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { createContext, useContext } from 'react'; - -export const EditorIdContext = createContext(''); - -export const EditorIdProvider = EditorIdContext.Provider; - -export function useEditorId() { - return useContext(EditorIdContext); -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/Editor.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/Editor.tsx deleted file mode 100644 index 879dc5f9c0..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/Editor.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import React, { memo } from 'react'; -import { EditorProps } from '../../application/document/document.types'; - -import { CollaborativeEditor } from '$app/components/editor/components/editor'; -import { EditorIdProvider } from '$app/components/editor/Editor.hooks'; -import './editor.scss'; -import withErrorBoundary from '$app/components/_shared/error_boundary/withError'; - -export function Editor(props: EditorProps) { - return ( -
- - - -
- ); -} - -export default withErrorBoundary(memo(Editor)); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/command/formula.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/command/formula.ts deleted file mode 100644 index 04a2e7c0f1..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/command/formula.ts +++ /dev/null @@ -1,95 +0,0 @@ -import { ReactEditor } from 'slate-react'; -import { Editor, Element, Element as SlateElement, NodeEntry, Range, Transforms } from 'slate'; -import { EditorInlineNodeType, FormulaNode } from '$app/application/document/document.types'; - -export function insertFormula(editor: ReactEditor, formula?: string) { - if (editor.selection) { - wrapFormula(editor, formula); - } -} - -export function updateFormula(editor: ReactEditor, formula: string) { - if (isFormulaActive(editor)) { - Transforms.delete(editor); - wrapFormula(editor, formula); - } -} - -export function deleteFormula(editor: ReactEditor) { - if (isFormulaActive(editor)) { - Transforms.delete(editor); - } -} - -export function wrapFormula(editor: ReactEditor, formula?: string) { - if (isFormulaActive(editor)) { - unwrapFormula(editor); - } - - const { selection } = editor; - - if (!selection) return; - const isCollapsed = selection && Range.isCollapsed(selection); - - const data = formula || editor.string(selection); - const formulaElement = { - type: EditorInlineNodeType.Formula, - data, - children: [ - { - text: '$', - }, - ], - }; - - if (!isCollapsed) { - Transforms.delete(editor); - } - - Transforms.insertNodes(editor, formulaElement, { - select: true, - }); - - const path = editor.selection?.anchor.path; - - if (path) { - editor.select(path); - } -} - -export function unwrapFormula(editor: ReactEditor) { - const [match] = Editor.nodes(editor, { - match: (n) => !Editor.isEditor(n) && SlateElement.isElement(n) && n.type === EditorInlineNodeType.Formula, - }); - - if (!match) return; - - const [node, path] = match as NodeEntry; - const formula = node.data; - const range = Editor.range(editor, match[1]); - const beforePoint = Editor.before(editor, path, { unit: 'character' }); - - Transforms.select(editor, range); - Transforms.delete(editor); - - Transforms.insertText(editor, formula); - - if (!beforePoint) return; - Transforms.select(editor, { - anchor: beforePoint, - focus: { - ...beforePoint, - offset: beforePoint.offset + formula.length, - }, - }); -} - -export function isFormulaActive(editor: ReactEditor) { - const [match] = editor.nodes({ - match: (n) => { - return !Editor.isEditor(n) && Element.isElement(n) && n.type === EditorInlineNodeType.Formula; - }, - }); - - return Boolean(match); -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/command/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/command/index.ts deleted file mode 100644 index 557b91f936..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/command/index.ts +++ /dev/null @@ -1,715 +0,0 @@ -import { ReactEditor } from 'slate-react'; -import { - Editor, - Element, - Node, - NodeEntry, - Point, - Range, - Transforms, - Location, - Path, - EditorBeforeOptions, - Text, - addMark, -} from 'slate'; -import { LIST_TYPES, tabBackward, tabForward } from '$app/components/editor/command/tab'; -import { getAllMarks, isMarkActive, removeMarks, toggleMark } from '$app/components/editor/command/mark'; -import { - deleteFormula, - insertFormula, - isFormulaActive, - unwrapFormula, - updateFormula, -} from '$app/components/editor/command/formula'; -import { - EditorInlineNodeType, - EditorNodeType, - CalloutNode, - Mention, - TodoListNode, - ToggleListNode, - inlineNodeTypes, - FormulaNode, - ImageNode, - EditorMarkFormat, -} from '$app/application/document/document.types'; -import cloneDeep from 'lodash-es/cloneDeep'; -import { generateId } from '$app/components/editor/provider/utils/convert'; -import { YjsEditor } from '@slate-yjs/core'; - -export const EmbedTypes: string[] = [ - EditorNodeType.DividerBlock, - EditorNodeType.EquationBlock, - EditorNodeType.GridBlock, - EditorNodeType.ImageBlock, -]; - -export const CustomEditor = { - getBlock: (editor: ReactEditor, at?: Location): NodeEntry | undefined => { - return Editor.above(editor, { - at, - match: (n) => !Editor.isEditor(n) && Element.isElement(n) && n.blockId !== undefined, - }); - }, - - isInlineNode: (editor: ReactEditor, point: Point): boolean => { - return Boolean( - editor.above({ - at: point, - match: (n) => { - return !Editor.isEditor(n) && Element.isElement(n) && inlineNodeTypes.includes(n.type as EditorInlineNodeType); - }, - }) - ); - }, - - beforeIsInlineNode: (editor: ReactEditor, at: Location, opts?: EditorBeforeOptions): boolean => { - const beforePoint = Editor.before(editor, at, opts); - - if (!beforePoint) return false; - return CustomEditor.isInlineNode(editor, beforePoint); - }, - - afterIsInlineNode: (editor: ReactEditor, at: Location, opts?: EditorBeforeOptions): boolean => { - const afterPoint = Editor.after(editor, at, opts); - - if (!afterPoint) return false; - return CustomEditor.isInlineNode(editor, afterPoint); - }, - - /** - * judge if the selection is multiple block - * @param editor - * @param filterEmptyEndSelection if the filterEmptyEndSelection is true, the function will filter the empty end selection - */ - isMultipleBlockSelected: (editor: ReactEditor, filterEmptyEndSelection?: boolean): boolean => { - const { selection } = editor; - - if (!selection) return false; - - if (Range.isCollapsed(selection)) return false; - const start = Range.start(selection); - const end = Range.end(selection); - const isBackward = Range.isBackward(selection); - const startBlock = CustomEditor.getBlock(editor, start); - const endBlock = CustomEditor.getBlock(editor, end); - - if (!startBlock || !endBlock) return false; - - const [, startPath] = startBlock; - const [, endPath] = endBlock; - - const isSomePath = Path.equals(startPath, endPath); - - // if the start and end path is the same, return false - if (isSomePath) { - return false; - } - - if (!filterEmptyEndSelection) { - return true; - } - - // The end point is at the start of the end block - const focusEndStart = Point.equals(end, editor.start(endPath)); - - if (!focusEndStart) { - return true; - } - - // find the previous block - const previous = editor.previous({ - at: endPath, - match: (n) => Element.isElement(n) && n.blockId !== undefined, - }); - - if (!previous) { - return true; - } - - // backward selection - const newEnd = editor.end(editor.range(previous[1])); - - editor.select({ - anchor: isBackward ? newEnd : start, - focus: isBackward ? start : newEnd, - }); - - return false; - }, - - /** - * turn the current block to a new block - * 1. clone the current block to a new block - * 2. lift the children of the current block if the current block doesn't allow has children - * 3. remove the old block - * 4. insert the new block - * @param editor - * @param newProperties - */ - turnToBlock: (editor: ReactEditor, newProperties: Partial) => { - const selection = editor.selection; - - if (!selection) return; - const match = CustomEditor.getBlock(editor); - - if (!match) return; - - const [node, path] = match as NodeEntry; - - const cloneNode = CustomEditor.cloneBlock(editor, node); - - Object.assign(cloneNode, newProperties); - cloneNode.data = { - ...(node.data || {}), - ...(newProperties.data || {}), - }; - - const isEmbed = editor.isEmbed(cloneNode); - - if (isEmbed) { - editor.splitNodes({ - always: true, - }); - cloneNode.children = []; - - Transforms.removeNodes(editor, { - at: path, - }); - Transforms.insertNodes(editor, cloneNode, { at: path }); - return cloneNode; - } - - const isListType = LIST_TYPES.includes(cloneNode.type as EditorNodeType); - - // if node doesn't allow has children, lift the children before insert the new node and remove the old node - if (!isListType) { - const [textNode, ...children] = cloneNode.children; - - const length = children.length; - - for (let i = 0; i < length; i++) { - editor.liftNodes({ - at: [...path, length - i], - }); - } - - cloneNode.children = [textNode]; - } - - Transforms.removeNodes(editor, { - at: path, - }); - - Transforms.insertNodes(editor, cloneNode, { at: path }); - if (selection) { - editor.select(selection); - } - - return cloneNode; - }, - tabForward, - tabBackward, - toggleMark, - removeMarks, - isMarkActive, - isFormulaActive, - insertFormula, - updateFormula, - deleteFormula, - toggleFormula: (editor: ReactEditor) => { - if (isFormulaActive(editor)) { - unwrapFormula(editor); - } else { - insertFormula(editor); - } - }, - - isBlockActive(editor: ReactEditor, format?: string) { - const match = CustomEditor.getBlock(editor); - - if (match && format !== undefined) { - return match[0].type === format; - } - - return !!match; - }, - - toggleAlign(editor: ReactEditor, format: string) { - const isIncludeRoot = CustomEditor.selectionIncludeRoot(editor); - - if (isIncludeRoot) return; - - const matchNodes = Array.from( - Editor.nodes(editor, { - // Note: we need to select the text node instead of the element node, otherwise the parent node will be selected - match: (n) => Element.isElement(n) && n.type === EditorNodeType.Text, - }) - ); - - if (!matchNodes) return; - - matchNodes.forEach((match) => { - const [, textPath] = match as NodeEntry; - const [node] = editor.parent(textPath) as NodeEntry< - Element & { - data: { - align?: string; - }; - } - >; - const path = ReactEditor.findPath(editor, node); - - const data = (node.data as { align?: string }) || {}; - const newProperties = { - data: { - ...data, - align: data.align === format ? undefined : format, - }, - } as Partial; - - Transforms.setNodes(editor, newProperties, { at: path }); - }); - }, - - getAlign(editor: ReactEditor) { - const match = CustomEditor.getBlock(editor); - - if (!match) return undefined; - - const [node] = match as NodeEntry; - - return (node.data as { align?: string })?.align; - }, - - isInlineActive(editor: ReactEditor) { - const [match] = editor.nodes({ - match: (n) => { - return !Editor.isEditor(n) && Element.isElement(n) && inlineNodeTypes.includes(n.type as EditorInlineNodeType); - }, - }); - - return !!match; - }, - - formulaActiveNode(editor: ReactEditor) { - const [match] = editor.nodes({ - match: (n) => { - return !Editor.isEditor(n) && Element.isElement(n) && n.type === EditorInlineNodeType.Formula; - }, - }); - - return match ? (match as NodeEntry) : undefined; - }, - - isMentionActive(editor: ReactEditor) { - const [match] = editor.nodes({ - match: (n) => { - return !Editor.isEditor(n) && Element.isElement(n) && n.type === EditorInlineNodeType.Mention; - }, - }); - - return Boolean(match); - }, - - insertMention(editor: ReactEditor, mention: Mention) { - const mentionElement = [ - { - type: EditorInlineNodeType.Mention, - children: [{ text: '$' }], - data: { - ...mention, - }, - }, - ]; - - Transforms.insertNodes(editor, mentionElement, { - select: true, - }); - - editor.collapse({ - edge: 'end', - }); - }, - - toggleTodo(editor: ReactEditor, at?: Location) { - const selection = at || editor.selection; - - if (!selection) return; - - const nodes = Array.from( - editor.nodes({ - at: selection, - match: (n) => Element.isElement(n) && n.type === EditorNodeType.TodoListBlock, - }) - ); - - const matchUnChecked = nodes.some(([node]) => { - return !(node as TodoListNode).data.checked; - }); - - const checked = Boolean(matchUnChecked); - - nodes.forEach(([node, path]) => { - const data = (node as TodoListNode).data || {}; - const newProperties = { - data: { - ...data, - checked: checked, - }, - } as Partial; - - Transforms.setNodes(editor, newProperties, { at: path }); - }); - }, - - toggleToggleList(editor: ReactEditor, node: ToggleListNode) { - const collapsed = node.data.collapsed; - const path = ReactEditor.findPath(editor, node); - const data = node.data || {}; - const newProperties = { - data: { - ...data, - collapsed: !collapsed, - }, - } as Partial; - - const selectMatch = Editor.above(editor, { - match: (n) => Element.isElement(n) && n.blockId !== undefined, - }); - - Transforms.setNodes(editor, newProperties, { at: path }); - - if (selectMatch) { - const [selectNode] = selectMatch; - const selectNodePath = ReactEditor.findPath(editor, selectNode); - - if (Path.isAncestor(path, selectNodePath)) { - editor.select(path); - editor.collapse({ - edge: 'start', - }); - } - } - }, - - setCalloutIcon(editor: ReactEditor, node: CalloutNode, newIcon: string) { - const path = ReactEditor.findPath(editor, node); - const data = node.data || {}; - const newProperties = { - data: { - ...data, - icon: newIcon, - }, - } as Partial; - - Transforms.setNodes(editor, newProperties, { at: path }); - }, - - setMathEquationBlockFormula(editor: ReactEditor, node: Element, newFormula: string) { - const path = ReactEditor.findPath(editor, node); - const data = node.data || {}; - const newProperties = { - data: { - ...data, - formula: newFormula, - }, - } as Partial; - - Transforms.setNodes(editor, newProperties, { at: path }); - }, - - setGridBlockViewId(editor: ReactEditor, node: Element, newViewId: string) { - const path = ReactEditor.findPath(editor, node); - const data = node.data || {}; - const newProperties = { - data: { - ...data, - viewId: newViewId, - }, - } as Partial; - - Transforms.setNodes(editor, newProperties, { at: path }); - }, - - setImageBlockData(editor: ReactEditor, node: Element, newData: ImageNode['data']) { - const path = ReactEditor.findPath(editor, node); - const data = node.data || {}; - const newProperties = { - data: { - ...data, - ...newData, - }, - } as Partial; - - Transforms.setNodes(editor, newProperties, { at: path }); - }, - - cloneBlock(editor: ReactEditor, block: Element): Element { - const cloneNode: Element = { - ...cloneDeep(block), - blockId: generateId(), - type: block.type === EditorNodeType.Page ? EditorNodeType.Paragraph : block.type, - children: [], - }; - const isEmbed = editor.isEmbed(cloneNode); - - if (isEmbed) { - return cloneNode; - } - - const [firstTextNode, ...children] = block.children as Element[]; - - const textNode = - firstTextNode && firstTextNode.type === EditorNodeType.Text - ? { - textId: generateId(), - type: EditorNodeType.Text, - children: cloneDeep(firstTextNode.children), - } - : undefined; - - if (textNode) { - cloneNode.children.push(textNode); - } - - const cloneChildren = children.map((child) => { - return CustomEditor.cloneBlock(editor, child); - }); - - cloneNode.children.push(...cloneChildren); - - return cloneNode; - }, - - duplicateNode(editor: ReactEditor, node: Element) { - const cloneNode = CustomEditor.cloneBlock(editor, node); - - const path = ReactEditor.findPath(editor, node); - - const nextPath = Path.next(path); - - Transforms.insertNodes(editor, cloneNode, { at: nextPath }); - return cloneNode; - }, - - deleteNode(editor: ReactEditor, node: Node) { - const path = ReactEditor.findPath(editor, node); - - Transforms.removeNodes(editor, { - at: path, - }); - editor.collapse({ - edge: 'start', - }); - }, - - getBlockType: (editor: ReactEditor) => { - const match = CustomEditor.getBlock(editor); - - if (!match) return null; - - const [node] = match as NodeEntry; - - return node.type as EditorNodeType; - }, - - selectionIncludeRoot: (editor: ReactEditor) => { - const [match] = Editor.nodes(editor, { - match: (n) => Element.isElement(n) && n.blockId !== undefined && n.type === EditorNodeType.Page, - }); - - return Boolean(match); - }, - - isCodeBlock: (editor: ReactEditor) => { - return CustomEditor.getBlockType(editor) === EditorNodeType.CodeBlock; - }, - - insertEmptyLine: (editor: ReactEditor & YjsEditor, path: Path) => { - editor.insertNode( - { - type: EditorNodeType.Paragraph, - data: {}, - blockId: generateId(), - children: [ - { - type: EditorNodeType.Text, - textId: generateId(), - children: [ - { - text: '', - }, - ], - }, - ], - }, - { - select: true, - at: path, - } - ); - ReactEditor.focus(editor); - Transforms.move(editor); - }, - - insertEmptyLineAtEnd: (editor: ReactEditor & YjsEditor) => { - CustomEditor.insertEmptyLine(editor, [editor.children.length]); - }, - - focusAtStartOfBlock(editor: ReactEditor) { - const { selection } = editor; - - if (selection && Range.isCollapsed(selection)) { - const match = CustomEditor.getBlock(editor); - const [, path] = match as NodeEntry; - const start = Editor.start(editor, path); - - return match && Point.equals(selection.anchor, start); - } - - return false; - }, - - setBlockColor( - editor: ReactEditor, - node: Element, - data: { - font_color?: string; - bg_color?: string; - } - ) { - const path = ReactEditor.findPath(editor, node); - - const nodeData = node.data || {}; - const newProperties = { - data: { - ...nodeData, - ...data, - }, - } as Partial; - - Transforms.setNodes(editor, newProperties, { at: path }); - editor.select(path); - }, - - deleteAllText(editor: ReactEditor, node: Element) { - const [textNode] = (node.children || []) as Element[]; - const hasTextNode = textNode && textNode.type === EditorNodeType.Text; - - if (!hasTextNode) return; - const path = ReactEditor.findPath(editor, textNode); - const textLength = editor.string(path).length; - const start = Editor.start(editor, path); - - for (let i = 0; i < textLength; i++) { - editor.select(start); - editor.deleteForward('character'); - } - }, - - getNodeText: (editor: ReactEditor, node: Element) => { - const [textNode] = (node.children || []) as Element[]; - const hasTextNode = textNode && textNode.type === EditorNodeType.Text; - - if (!hasTextNode) return ''; - - const path = ReactEditor.findPath(editor, textNode); - - return editor.string(path); - }, - - isEmptyText: (editor: ReactEditor, node: Element) => { - const [textNode] = (node.children || []) as Element[]; - const hasTextNode = textNode && textNode.type === EditorNodeType.Text; - - if (!hasTextNode) return false; - - return editor.isEmpty(textNode); - }, - - includeInlineBlocks: (editor: ReactEditor) => { - const [match] = Editor.nodes(editor, { - match: (n) => Element.isElement(n) && editor.isInline(n), - }); - - return Boolean(match); - }, - - getNodeTextContent(node: Node): string { - if (Element.isElement(node) && node.type === EditorInlineNodeType.Formula) { - return (node as FormulaNode).data || ''; - } - - if (Text.isText(node)) { - return node.text || ''; - } - - return node.children.map((n) => CustomEditor.getNodeTextContent(n)).join(''); - }, - - isEmbedNode(node: Element): boolean { - return EmbedTypes.includes(node.type); - }, - - getListLevel(editor: ReactEditor, type: EditorNodeType, path: Path) { - let level = 0; - let currentPath = path; - - while (currentPath.length > 0) { - const parent = editor.parent(currentPath); - - if (!parent) { - break; - } - - const [parentNode, parentPath] = parent as NodeEntry; - - if (parentNode.type !== type) { - break; - } - - level += 1; - currentPath = parentPath; - } - - return level; - }, - - getLinks(editor: ReactEditor): string[] { - const marks = getAllMarks(editor); - - if (!marks) return []; - - return Object.entries(marks) - .filter(([key]) => key === 'href') - .map(([_, val]) => val as string); - }, - - extendLineBackward(editor: ReactEditor) { - Transforms.move(editor, { - unit: 'line', - edge: 'focus', - reverse: true, - }); - }, - - extendLineForward(editor: ReactEditor) { - Transforms.move(editor, { unit: 'line', edge: 'focus' }); - }, - - insertPlainText(editor: ReactEditor, text: string) { - const [appendText, ...lines] = text.split('\n'); - - editor.insertText(appendText); - lines.forEach((line) => { - editor.insertBreak(); - editor.insertText(line); - }); - }, - - highlight(editor: ReactEditor) { - addMark(editor, EditorMarkFormat.BgColor, 'appflowy_them_color_tint5'); - }, -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/command/mark.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/command/mark.ts deleted file mode 100644 index 649eaca564..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/command/mark.ts +++ /dev/null @@ -1,137 +0,0 @@ -import { ReactEditor } from 'slate-react'; -import { Editor, Text, Range, Element } from 'slate'; -import { EditorInlineNodeType, EditorMarkFormat } from '$app/application/document/document.types'; -import { CustomEditor } from '$app/components/editor/command/index'; - -export function toggleMark( - editor: ReactEditor, - mark: { - key: EditorMarkFormat; - value: string | boolean; - } -) { - if (CustomEditor.selectionIncludeRoot(editor)) { - return; - } - - const { key, value } = mark; - - const isActive = isMarkActive(editor, key); - - if (isActive || !value) { - Editor.removeMark(editor, key as string); - } else if (value) { - Editor.addMark(editor, key as string, value); - } -} - -/** - * Check if the every text in the selection has the mark. - * @param editor - * @param format - */ -export function isMarkActive(editor: ReactEditor, format: EditorMarkFormat | EditorInlineNodeType) { - const selection = editor.selection; - - if (!selection) return false; - - const isExpanded = Range.isExpanded(selection); - - if (isExpanded) { - const texts = getSelectionTexts(editor); - - return texts.every((node) => { - const { text, ...attributes } = node; - - if (!text) return true; - return Boolean((attributes as Record)[format]); - }); - } - - const marks = Editor.marks(editor) as Record | null; - - return marks ? !!marks[format] : false; -} - -export function getSelectionTexts(editor: ReactEditor) { - const selection = editor.selection; - - if (!selection) return []; - - const texts: Text[] = []; - - const isExpanded = Range.isExpanded(selection); - - if (isExpanded) { - let anchor = Range.start(selection); - const focus = Range.end(selection); - const isEnd = Editor.isEnd(editor, anchor, anchor.path); - - if (isEnd) { - const after = Editor.after(editor, anchor); - - if (after) { - anchor = after; - } - } - - Array.from( - Editor.nodes(editor, { - at: { - anchor, - focus, - }, - }) - ).forEach((match) => { - const node = match[0] as Element; - - if (Text.isText(node)) { - texts.push(node); - } else if (Editor.isInline(editor, node)) { - texts.push(...(node.children as Text[])); - } - }); - } - - return texts; -} - -/** - * Get all marks in the current selection. - * @param editor - */ -export function getAllMarks(editor: ReactEditor) { - const selection = editor.selection; - - if (!selection) return null; - - const isExpanded = Range.isExpanded(selection); - - if (isExpanded) { - const texts = getSelectionTexts(editor); - - const marks: Record = {}; - - texts.forEach((node) => { - Object.entries(node).forEach(([key, value]) => { - if (key !== 'text') { - marks[key] = value; - } - }); - }); - - return marks; - } - - return Editor.marks(editor) as Record | null; -} - -export function removeMarks(editor: ReactEditor) { - const marks = getAllMarks(editor); - - if (!marks) return; - - for (const key in marks) { - Editor.removeMark(editor, key); - } -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/command/tab.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/command/tab.ts deleted file mode 100644 index 819596f92f..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/command/tab.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { Path, Element, NodeEntry } from 'slate'; -import { ReactEditor } from 'slate-react'; -import { EditorNodeType } from '$app/application/document/document.types'; -import { CustomEditor } from '$app/components/editor/command/index'; - -export const LIST_TYPES = [ - EditorNodeType.NumberedListBlock, - EditorNodeType.BulletedListBlock, - EditorNodeType.TodoListBlock, - EditorNodeType.ToggleListBlock, - EditorNodeType.QuoteBlock, - EditorNodeType.Paragraph, -]; - -/** - * Indent the current list item - * Conditions: - * 1. The current node must be a list item - * 2. The previous node must be a list - * 3. The previous node must be the same level as the current node - * Result: - * 1. The current node will be the child of the previous node - * 2. The current node will be indented - * 3. The children of the current node will be moved to the children of the previous node - * @param editor - */ -export function tabForward(editor: ReactEditor) { - const match = CustomEditor.getBlock(editor); - - if (!match) return; - - const [node, path] = match as NodeEntry; - - const hasPrevious = Path.hasPrevious(path); - - if (!hasPrevious) return; - - const previousPath = Path.previous(path); - - const previous = editor.node(previousPath); - const [previousNode] = previous as NodeEntry; - - if (!previousNode) return; - - const type = previousNode.type as EditorNodeType; - - if (type === EditorNodeType.Page) return; - // the previous node is not a list - if (!LIST_TYPES.includes(type)) return; - - const toPath = [...previousPath, previousNode.children.length]; - - editor.moveNodes({ - at: path, - to: toPath, - }); - - const length = node.children.length; - - for (let i = length - 1; i > 0; i--) { - editor.liftNodes({ - at: [...toPath, i], - }); - } -} - -export function tabBackward(editor: ReactEditor) { - const match = CustomEditor.getBlock(editor); - - if (!match) return; - - const [node, path] = match as NodeEntry; - - const depth = path.length; - - if (node.type === EditorNodeType.Page) return; - - if (depth === 1) return; - editor.liftNodes({ - at: path, - }); -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/_shared/Placeholder.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/_shared/Placeholder.tsx deleted file mode 100644 index d9a60f09ad..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/_shared/Placeholder.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; -import { Element } from 'slate'; -import PlaceholderContent from '$app/components/editor/components/blocks/_shared/PlaceholderContent'; - -function Placeholder({ node, isEmpty }: { node: Element; isEmpty: boolean }) { - if (!isEmpty) { - return null; - } - - return ; -} - -export default React.memo(Placeholder); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/_shared/PlaceholderContent.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/_shared/PlaceholderContent.tsx deleted file mode 100644 index 91645e0051..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/_shared/PlaceholderContent.tsx +++ /dev/null @@ -1,130 +0,0 @@ -import React, { CSSProperties, useEffect, useMemo, useState } from 'react'; -import { ReactEditor, useSelected, useSlate } from 'slate-react'; -import { Editor, Element, Range } from 'slate'; -import { EditorNodeType, HeadingNode } from '$app/application/document/document.types'; -import { useTranslation } from 'react-i18next'; - -function PlaceholderContent({ node, ...attributes }: { node: Element; className?: string; style?: CSSProperties }) { - const { t } = useTranslation(); - const editor = useSlate(); - const selected = useSelected() && !!editor.selection && Range.isCollapsed(editor.selection); - const [isComposing, setIsComposing] = useState(false); - const block = useMemo(() => { - const path = ReactEditor.findPath(editor, node); - const match = Editor.above(editor, { - match: (n) => { - return !Editor.isEditor(n) && Element.isElement(n) && n.blockId !== undefined && n.type !== undefined; - }, - at: path, - }); - - if (!match) return null; - - return match[0] as Element; - }, [editor, node]); - - const className = useMemo(() => { - return `text-placeholder select-none ${attributes.className ?? ''}`; - }, [attributes.className]); - - const unSelectedPlaceholder = useMemo(() => { - switch (block?.type) { - case EditorNodeType.Paragraph: { - if (editor.children.length === 1) { - return t('editor.slashPlaceHolder'); - } - - return ''; - } - - case EditorNodeType.ToggleListBlock: - return t('blockPlaceholders.bulletList'); - case EditorNodeType.QuoteBlock: - return t('blockPlaceholders.quote'); - case EditorNodeType.TodoListBlock: - return t('blockPlaceholders.todoList'); - case EditorNodeType.NumberedListBlock: - return t('blockPlaceholders.numberList'); - case EditorNodeType.BulletedListBlock: - return t('blockPlaceholders.bulletList'); - case EditorNodeType.HeadingBlock: { - const level = (block as HeadingNode).data.level; - - switch (level) { - case 1: - return t('editor.mobileHeading1'); - case 2: - return t('editor.mobileHeading2'); - case 3: - return t('editor.mobileHeading3'); - default: - return ''; - } - } - - case EditorNodeType.Page: - return t('document.title.placeholder'); - case EditorNodeType.CalloutBlock: - case EditorNodeType.CodeBlock: - return t('editor.typeSomething'); - default: - return ''; - } - }, [block, t, editor.children.length]); - - const selectedPlaceholder = useMemo(() => { - switch (block?.type) { - case EditorNodeType.HeadingBlock: - return unSelectedPlaceholder; - case EditorNodeType.Page: - return t('document.title.placeholder'); - case EditorNodeType.GridBlock: - case EditorNodeType.EquationBlock: - case EditorNodeType.CodeBlock: - case EditorNodeType.DividerBlock: - return ''; - - default: - return t('editor.slashPlaceHolder'); - } - }, [block?.type, t, unSelectedPlaceholder]); - - useEffect(() => { - if (!selected) return; - - const handleCompositionStart = () => { - setIsComposing(true); - }; - - const handleCompositionEnd = () => { - setIsComposing(false); - }; - - const editorDom = ReactEditor.toDOMNode(editor, editor); - - // placeholder should be hidden when composing - editorDom.addEventListener('compositionstart', handleCompositionStart); - editorDom.addEventListener('compositionend', handleCompositionEnd); - editorDom.addEventListener('compositionupdate', handleCompositionStart); - return () => { - editorDom.removeEventListener('compositionstart', handleCompositionStart); - editorDom.removeEventListener('compositionend', handleCompositionEnd); - editorDom.removeEventListener('compositionupdate', handleCompositionStart); - }; - }, [editor, selected]); - - if (isComposing) { - return null; - } - - return ( - - ); -} - -export default PlaceholderContent; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/_shared/unSupportBlock.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/_shared/unSupportBlock.tsx deleted file mode 100644 index 9e9e4fcb38..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/_shared/unSupportBlock.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import React, { forwardRef } from 'react'; -import { Alert } from '@mui/material'; - -export const UnSupportBlock = forwardRef((_, ref) => { - return ( -
- -
- ); -}); - -export default UnSupportBlock; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/bulleted_list/BulletedList.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/bulleted_list/BulletedList.tsx deleted file mode 100644 index 41fce1c9dc..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/bulleted_list/BulletedList.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import React, { forwardRef, memo } from 'react'; -import { EditorElementProps, BulletedListNode } from '$app/application/document/document.types'; - -export const BulletedList = memo( - forwardRef>( - ({ node: _, children, className, ...attributes }, ref) => { - return ( -
- {children} -
- ); - } - ) -); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/bulleted_list/BulletedListIcon.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/bulleted_list/BulletedListIcon.tsx deleted file mode 100644 index ea0de80f55..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/bulleted_list/BulletedListIcon.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import React, { useMemo } from 'react'; -import { BulletedListNode } from '$app/application/document/document.types'; -import { ReactEditor, useSlateStatic } from 'slate-react'; -import { CustomEditor } from '$app/components/editor/command'; - -enum Letter { - Disc, - Circle, - Square, -} - -function BulletedListIcon({ block, className }: { block: BulletedListNode; className: string }) { - const staticEditor = useSlateStatic(); - const path = ReactEditor.findPath(staticEditor, block); - - const letter = useMemo(() => { - const level = CustomEditor.getListLevel(staticEditor, block.type, path); - - if (level % 3 === 0) { - return Letter.Disc; - } else if (level % 3 === 1) { - return Letter.Circle; - } else { - return Letter.Square; - } - }, [block.type, staticEditor, path]); - - const dataLetter = useMemo(() => { - switch (letter) { - case Letter.Disc: - return '•'; - case Letter.Circle: - return '◦'; - case Letter.Square: - return '▪'; - } - }, [letter]); - - return ( - { - e.preventDefault(); - }} - data-letter={dataLetter} - contentEditable={false} - className={`${className} bulleted-icon flex min-w-[24px] justify-center pr-1 font-medium`} - /> - ); -} - -export default BulletedListIcon; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/bulleted_list/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/bulleted_list/index.ts deleted file mode 100644 index 2095dff308..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/bulleted_list/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './BulletedList'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/callout/Callout.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/callout/Callout.tsx deleted file mode 100644 index a20300bbc2..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/callout/Callout.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import React, { forwardRef, memo } from 'react'; -import { EditorElementProps, CalloutNode } from '$app/application/document/document.types'; -import CalloutIcon from '$app/components/editor/components/blocks/callout/CalloutIcon'; - -export const Callout = memo( - forwardRef>(({ node, children, ...attributes }, ref) => { - return ( - <> -
- -
-
-
- {children} -
-
- - ); - }) -); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/callout/CalloutIcon.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/callout/CalloutIcon.tsx deleted file mode 100644 index e9bba448a7..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/callout/CalloutIcon.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import React, { useCallback, useRef, useState } from 'react'; -import { IconButton } from '@mui/material'; -import { CalloutNode } from '$app/application/document/document.types'; -import EmojiPicker from '$app/components/_shared/emoji_picker/EmojiPicker'; -import Popover from '@mui/material/Popover'; -import { PopoverCommonProps } from '$app/components/editor/components/tools/popover'; -import { ReactEditor, useSlateStatic } from 'slate-react'; -import { CustomEditor } from '$app/components/editor/command'; - -function CalloutIcon({ node }: { node: CalloutNode }) { - const ref = useRef(null); - const [open, setOpen] = useState(false); - const editor = useSlateStatic(); - - const handleClose = useCallback(() => { - setOpen(false); - const path = ReactEditor.findPath(editor, node); - - ReactEditor.focus(editor); - editor.select(path); - editor.collapse({ - edge: 'start', - }); - }, [editor, node]); - const handleEmojiSelect = useCallback( - (emoji: string) => { - CustomEditor.setCalloutIcon(editor, node, emoji); - handleClose(); - }, - [editor, node, handleClose] - ); - - return ( - <> - { - setOpen(true); - }} - className={`h-8 w-8 p-1`} - > - {node.data.icon} - - {open && ( - - - - )} - - ); -} - -export default React.memo(CalloutIcon); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/callout/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/callout/index.ts deleted file mode 100644 index 4ca74e4be8..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/callout/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './Callout'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/code/Code.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/code/Code.hooks.ts deleted file mode 100644 index 0b043f4579..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/code/Code.hooks.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { useCallback } from 'react'; -import { ReactEditor, useSlateStatic } from 'slate-react'; -import { Element as SlateElement, Transforms } from 'slate'; -import { CodeNode } from '$app/application/document/document.types'; - -export function useCodeBlock(node: CodeNode) { - const language = node.data.language; - const editor = useSlateStatic() as ReactEditor; - const handleChangeLanguage = useCallback( - (newLang: string) => { - const path = ReactEditor.findPath(editor, node); - const newProperties = { - data: { - language: newLang, - }, - } as Partial; - - Transforms.setNodes(editor, newProperties, { at: path }); - }, - [editor, node] - ); - - return { - language, - handleChangeLanguage, - }; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/code/Code.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/code/Code.tsx deleted file mode 100644 index 7fe7b205f4..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/code/Code.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { forwardRef, memo, useCallback } from 'react'; -import { EditorElementProps, CodeNode } from '$app/application/document/document.types'; -import LanguageSelect from './SelectLanguage'; - -import { useCodeBlock } from '$app/components/editor/components/blocks/code/Code.hooks'; -import { ReactEditor, useSlateStatic } from 'slate-react'; - -export const Code = memo( - forwardRef>(({ node, children, ...attributes }, ref) => { - const { language, handleChangeLanguage } = useCodeBlock(node); - - const editor = useSlateStatic(); - const onBlur = useCallback(() => { - const path = ReactEditor.findPath(editor, node); - - ReactEditor.focus(editor); - editor.select(path); - editor.collapse({ - edge: 'start', - }); - }, [editor, node]); - - return ( - <> -
- -
-
-
-            {children}
-          
-
- - ); - }) -); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/code/SelectLanguage.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/code/SelectLanguage.tsx deleted file mode 100644 index 4805233e1d..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/code/SelectLanguage.tsx +++ /dev/null @@ -1,168 +0,0 @@ -import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; -import { TextField, Popover } from '@mui/material'; -import { useTranslation } from 'react-i18next'; -import { supportLanguage } from './constants'; -import KeyboardNavigation, { - KeyboardNavigationOption, -} from '$app/components/_shared/keyboard_navigation/KeyboardNavigation'; -import usePopoverAutoPosition from '$app/components/_shared/popover/Popover.hooks'; -import { PopoverOrigin } from '@mui/material/Popover/Popover'; - -const initialOrigin: { - transformOrigin: PopoverOrigin; - anchorOrigin: PopoverOrigin; -} = { - transformOrigin: { - vertical: 'top', - horizontal: 'left', - }, - anchorOrigin: { - vertical: 'bottom', - horizontal: 'left', - }, -}; - -function SelectLanguage({ - language = 'json', - onChangeLanguage, - onBlur, -}: { - language: string; - onChangeLanguage: (language: string) => void; - onBlur?: () => void; -}) { - const { t } = useTranslation(); - const ref = useRef(null); - const [open, setOpen] = useState(false); - const [search, setSearch] = useState(''); - - const searchRef = useRef(null); - const scrollRef = useRef(null); - const options: KeyboardNavigationOption[] = useMemo(() => { - return supportLanguage - .map((item) => ({ - key: item.id, - content: item.title, - })) - .filter((item) => { - return item.content?.toLowerCase().includes(search.toLowerCase()); - }); - }, [search]); - - const handleClose = useCallback(() => { - setOpen(false); - setSearch(''); - }, []); - - const handleConfirm = useCallback( - (key: string) => { - onChangeLanguage(key); - handleClose(); - }, - [onChangeLanguage, handleClose] - ); - - useEffect(() => { - const element = ref.current; - - if (!element) return; - const handleKeyDown = (e: KeyboardEvent) => { - e.stopPropagation(); - e.preventDefault(); - - if (e.key === 'Enter') { - setOpen(true); - return; - } - - onBlur?.(); - }; - - element.addEventListener('keydown', handleKeyDown); - - return () => { - element.removeEventListener('keydown', handleKeyDown); - }; - }, [onBlur]); - - const { paperHeight, transformOrigin, anchorOrigin, isEntered } = usePopoverAutoPosition({ - initialPaperWidth: 200, - initialPaperHeight: 220, - anchorEl: ref.current, - initialAnchorOrigin: initialOrigin.anchorOrigin, - initialTransformOrigin: initialOrigin.transformOrigin, - open, - }); - - return ( - <> - { - setOpen(true); - }} - InputProps={{ - readOnly: true, - }} - placeholder={t('document.codeBlock.language.placeholder')} - label={t('document.codeBlock.language.label')} - /> - - {open && ( - -
- setSearch(e.target.value)} - size={'small'} - autoFocus={true} - variant={'standard'} - className={'px-2 text-xs'} - placeholder={t('search.label')} - /> -
- -
-
-
- )} - - ); -} - -export default SelectLanguage; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/code/constants.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/code/constants.ts deleted file mode 100644 index dee71624db..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/code/constants.ts +++ /dev/null @@ -1,154 +0,0 @@ -export const supportLanguage = [ - { - id: 'bash', - title: 'Bash', - }, - { - id: 'basic', - title: 'Basic', - }, - { - id: 'c', - title: 'C', - }, - { - id: 'clojure', - title: 'Clojure', - }, - { - id: 'cpp', - title: 'C++', - }, - { - id: 'cs', - title: 'CS', - }, - { - id: 'css', - title: 'CSS', - }, - { - id: 'dart', - title: 'Dart', - }, - { - id: 'elixir', - title: 'Elixir', - }, - { - id: 'elm', - title: 'Elm', - }, - { - id: 'erlang', - title: 'Erlang', - }, - { - id: 'fortran', - title: 'Fortran', - }, - { - id: 'go', - title: 'Go', - }, - { - id: 'graphql', - title: 'GraphQL', - }, - { - id: 'haskell', - title: 'Haskell', - }, - { - id: 'java', - title: 'Java', - }, - { - id: 'javascript', - title: 'JavaScript', - }, - { - id: 'json', - title: 'JSON', - }, - { - id: 'kotlin', - title: 'Kotlin', - }, - { - id: 'lisp', - title: 'Lisp', - }, - { - id: 'lua', - title: 'Lua', - }, - { - id: 'markdown', - title: 'Markdown', - }, - { - id: 'matlab', - title: 'Matlab', - }, - { - id: 'ocaml', - title: 'OCaml', - }, - { - id: 'perl', - title: 'Perl', - }, - { - id: 'php', - title: 'PHP', - }, - { - id: 'powershell', - title: 'Powershell', - }, - { - id: 'python', - title: 'Python', - }, - { - id: 'r', - title: 'R', - }, - { - id: 'ruby', - title: 'Ruby', - }, - { - id: 'rust', - title: 'Rust', - }, - { - id: 'scala', - title: 'Scala', - }, - { - id: 'shell', - title: 'Shell', - }, - { - id: 'sql', - title: 'SQL', - }, - { - id: 'swift', - title: 'Swift', - }, - { - id: 'typescript', - title: 'TypeScript', - }, - { - id: 'xml', - title: 'XML', - }, - { - id: 'yaml', - title: 'YAML', - }, -]; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/code/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/code/index.ts deleted file mode 100644 index c3aa9443d1..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/code/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './Code'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/code/utils.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/code/utils.ts deleted file mode 100644 index 52eeebc8c4..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/code/utils.ts +++ /dev/null @@ -1,132 +0,0 @@ -import Prism from 'prismjs'; - -import 'prismjs/components/prism-bash'; -import 'prismjs/components/prism-basic'; -import 'prismjs/components/prism-c'; -import 'prismjs/components/prism-clojure'; -import 'prismjs/components/prism-cpp'; -import 'prismjs/components/prism-csp'; -import 'prismjs/components/prism-css'; -import 'prismjs/components/prism-dart'; -import 'prismjs/components/prism-elixir'; -import 'prismjs/components/prism-elm'; -import 'prismjs/components/prism-erlang'; -import 'prismjs/components/prism-fortran'; -import 'prismjs/components/prism-go'; -import 'prismjs/components/prism-graphql'; -import 'prismjs/components/prism-haskell'; -import 'prismjs/components/prism-java'; -import 'prismjs/components/prism-javascript'; -import 'prismjs/components/prism-json'; -import 'prismjs/components/prism-kotlin'; -import 'prismjs/components/prism-lisp'; -import 'prismjs/components/prism-lua'; -import 'prismjs/components/prism-markdown'; -import 'prismjs/components/prism-matlab'; -import 'prismjs/components/prism-ocaml'; -import 'prismjs/components/prism-perl'; -import 'prismjs/components/prism-php'; -import 'prismjs/components/prism-powershell'; -import 'prismjs/components/prism-python'; -import 'prismjs/components/prism-r'; -import 'prismjs/components/prism-ruby'; -import 'prismjs/components/prism-rust'; -import 'prismjs/components/prism-scala'; -import 'prismjs/components/prism-shell-session'; -import 'prismjs/components/prism-sql'; -import 'prismjs/components/prism-swift'; -import 'prismjs/components/prism-typescript'; -import 'prismjs/components/prism-xml-doc'; -import 'prismjs/components/prism-yaml'; - -import { BaseRange, NodeEntry, Text, Path } from 'slate'; - -const push_string = ( - token: string | Prism.Token, - path: Path, - start: number, - ranges: BaseRange[], - token_type = 'text' -) => { - let newStart = start; - - ranges.push({ - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - prism_token: token_type, - anchor: { path, offset: newStart }, - focus: { path, offset: newStart + token.length }, - }); - newStart += token.length; - return newStart; -}; - -// This recurses through the Prism.tokenizes result and creates stylized ranges based on the token type -const recurseTokenize = ( - token: string | Prism.Token, - path: Path, - ranges: BaseRange[], - start: number, - parent_tag?: string -) => { - // Uses the parent's token type if a Token only has a string as its content - if (typeof token === 'string') { - return push_string(token, path, start, ranges, parent_tag); - } - - if ('content' in token) { - if (token.content instanceof Array) { - // Calls recurseTokenize on nested Tokens in content - let newStart = start; - - for (const subToken of token.content) { - newStart = recurseTokenize(subToken, path, ranges, newStart, token.type) || 0; - } - - return newStart; - } - - return push_string(token.content, path, start, ranges, token.type); - } -}; - -function switchCodeTheme(isDark: boolean) { - const link = document.getElementById('prism-css'); - - if (link) { - document.head.removeChild(link); - } - - const newLink = document.createElement('link'); - - newLink.rel = 'stylesheet'; - newLink.href = isDark - ? 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.24.1/themes/prism-dark.min.css' - : 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.24.1/themes/prism.min.css'; - newLink.id = 'prism-css'; - document.head.appendChild(newLink); -} - -export const decorateCode = ([node, path]: NodeEntry, language: string, isDark: boolean) => { - switchCodeTheme(isDark); - - const ranges: BaseRange[] = []; - - if (!Text.isText(node)) { - return ranges; - } - - try { - const tokens = Prism.tokenize(node.text, Prism.languages[language]); - - let start = 0; - - for (const token of tokens) { - start = recurseTokenize(token, path, ranges, start) || 0; - } - - return ranges; - } catch { - return ranges; - } -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/database/DatabaseEmpty.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/database/DatabaseEmpty.tsx deleted file mode 100644 index a0f50016e1..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/database/DatabaseEmpty.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import React, { useCallback, useRef } from 'react'; -import CreateNewFolderIcon from '@mui/icons-material/CreateNewFolder'; - -import { GridNode } from '$app/application/document/document.types'; -import { useTranslation } from 'react-i18next'; - -import Drawer from '$app/components/editor/components/blocks/database/Drawer'; - -function DatabaseEmpty({ node }: { node: GridNode }) { - const { t } = useTranslation(); - const ref = useRef(null); - - const [open, setOpen] = React.useState(false); - - const toggleDrawer = useCallback((open: boolean) => { - return (e: React.MouseEvent | KeyboardEvent | React.FocusEvent) => { - e.stopPropagation(); - setOpen(open); - }; - }, []); - - return ( -
- -
{t('document.plugins.database.noDataSource')}
-
- - {t('document.plugins.database.selectADataSource')} - - {t('document.plugins.database.toContinue')} -
- - -
- ); -} - -export default React.memo(DatabaseEmpty); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/database/DatabaseList.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/database/DatabaseList.hooks.ts deleted file mode 100644 index 543b9900ca..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/database/DatabaseList.hooks.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { useAppSelector } from '$app/stores/store'; -import { ViewLayoutPB } from '@/services/backend'; - -export function useLoadDatabaseList({ searchText, layout }: { searchText: string; layout: ViewLayoutPB }) { - const list = useAppSelector((state) => { - const workspaces = state.workspace.workspaces.map((item) => item.id) ?? []; - - return Object.values(state.pages.pageMap).filter((page) => { - if (page.layout !== layout) return false; - const parentId = page.parentId; - - if (!parentId) return false; - - const parent = state.pages.pageMap[parentId]; - const parentLayout = parent?.layout; - - if (!workspaces.includes(parentId) && parentLayout !== ViewLayoutPB.Document) return false; - - return page.name.toLowerCase().includes(searchText.toLowerCase()); - }); - }); - - return { - list, - }; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/database/DatabaseList.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/database/DatabaseList.tsx deleted file mode 100644 index 5d06a13c06..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/database/DatabaseList.tsx +++ /dev/null @@ -1,104 +0,0 @@ -import React, { useCallback, useMemo } from 'react'; -import { useTranslation } from 'react-i18next'; -import { TextField } from '@mui/material'; -import { useLoadDatabaseList } from '$app/components/editor/components/blocks/database/DatabaseList.hooks'; -import { ViewLayoutPB } from '@/services/backend'; -import { ReactComponent as GridSvg } from '$app/assets/grid.svg'; -import { useSlateStatic } from 'slate-react'; -import { CustomEditor } from '$app/components/editor/command'; -import { GridNode } from '$app/application/document/document.types'; -import KeyboardNavigation, { - KeyboardNavigationOption, -} from '$app/components/_shared/keyboard_navigation/KeyboardNavigation'; -import { Page } from '$app_reducers/pages/slice'; - -function DatabaseList({ - node, - toggleDrawer, -}: { - node: GridNode; - toggleDrawer: (open: boolean) => (e: React.MouseEvent | KeyboardEvent | React.FocusEvent) => void; -}) { - const scrollRef = React.useRef(null); - - const inputRef = React.useRef(null); - const editor = useSlateStatic(); - const { t } = useTranslation(); - const [searchText, setSearchText] = React.useState(''); - const { list } = useLoadDatabaseList({ - searchText: searchText || '', - layout: ViewLayoutPB.Grid, - }); - - const renderItem = useCallback( - (item: Page) => { - return ( -
- -
{item.name.trim() || t('menuAppHeader.defaultNewPageName')}
-
- ); - }, - [t] - ); - - const options: KeyboardNavigationOption[] = useMemo(() => { - return list.map((item) => { - return { - key: item.id, - content: renderItem(item), - }; - }); - }, [list, renderItem]); - - const handleSelected = useCallback( - (id: string) => { - CustomEditor.setGridBlockViewId(editor, node, id); - }, - [editor, node] - ); - - const handleKeyDown = useCallback( - (e: KeyboardEvent) => { - if (e.key === 'Escape') { - e.stopPropagation(); - e.preventDefault(); - toggleDrawer(false)(e); - } - }, - [toggleDrawer] - ); - - return ( -
- { - setSearchText((e.currentTarget as HTMLInputElement).value); - }} - inputProps={{ - className: 'py-2 text-sm', - }} - placeholder={t('document.plugins.database.linkToDatabase')} - /> -
- -
-
- ); -} - -export default DatabaseList; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/database/Drawer.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/database/Drawer.tsx deleted file mode 100644 index 54c0005027..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/database/Drawer.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import React, { useCallback } from 'react'; -import { Button, IconButton } from '@mui/material'; -import { useTranslation } from 'react-i18next'; -import { createGrid } from '$app/components/editor/components/blocks/database/utils'; -import { CustomEditor } from '$app/components/editor/command'; -import { useSlateStatic } from 'slate-react'; -import { useEditorId } from '$app/components/editor/Editor.hooks'; -import { GridNode } from '$app/application/document/document.types'; -import { ReactComponent as CloseSvg } from '$app/assets/close.svg'; -import { ReactComponent as AddSvg } from '$app/assets/add.svg'; -import DatabaseList from '$app/components/editor/components/blocks/database/DatabaseList'; - -function Drawer({ - open, - toggleDrawer, - node, -}: { - open: boolean; - toggleDrawer: (open: boolean) => (e: React.MouseEvent | KeyboardEvent | React.FocusEvent) => void; - node: GridNode; -}) { - const editor = useSlateStatic(); - const id = useEditorId(); - const { t } = useTranslation(); - const handleCreateGrid = useCallback(async () => { - const gridId = await createGrid(id); - - CustomEditor.setGridBlockViewId(editor, node, gridId); - }, [id, editor, node]); - - return ( -
{ - e.stopPropagation(); - }} - className={'absolute right-0 top-0 h-full transform overflow-hidden'} - style={{ - width: open ? '250px' : '0px', - transition: 'width 0.3s ease-in-out', - }} - onMouseDown={(e) => { - const isInput = (e.target as HTMLElement).closest('input'); - - if (isInput) return; - e.stopPropagation(); - e.preventDefault(); - }} - > -
-
-
{t('document.plugins.database.selectDataSource')}
- - - -
-
- {open && } -
- -
- -
-
-
- ); -} - -export default Drawer; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/database/GridBlock.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/database/GridBlock.tsx deleted file mode 100644 index 936da9c2c8..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/database/GridBlock.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import React, { forwardRef, memo } from 'react'; -import { EditorElementProps, GridNode } from '$app/application/document/document.types'; - -import GridView from '$app/components/editor/components/blocks/database/GridView'; -import DatabaseEmpty from '$app/components/editor/components/blocks/database/DatabaseEmpty'; -import { useSelected } from 'slate-react'; - -export const GridBlock = memo( - forwardRef>(({ node, children, className = '', ...attributes }, ref) => { - const viewId = node.data.viewId; - const selected = useSelected(); - - return ( -
-
- {children} -
-
- {viewId ? : } -
-
- ); - }) -); - -export default GridBlock; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/database/GridView.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/database/GridView.tsx deleted file mode 100644 index 695482bbd8..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/database/GridView.tsx +++ /dev/null @@ -1,71 +0,0 @@ -import React, { useCallback, useEffect, useRef, useState } from 'react'; -import { Database, DatabaseRenderedProvider } from '$app/components/database'; -import { ViewIdProvider } from '$app/hooks'; - -function GridView({ viewId }: { viewId: string }) { - const [selectedViewId, onChangeSelectedViewId] = useState(viewId); - - const ref = useRef(null); - - const [rendered, setRendered] = useState<{ viewId: string; rendered: boolean } | undefined>(undefined); - - // delegate wheel event to layout when grid is scrolled to top or bottom - useEffect(() => { - const element = ref.current; - - const viewId = rendered?.viewId; - - if (!viewId || !element) { - return; - } - - const gridScroller = element.querySelector(`[data-view-id="${viewId}"] .grid-scroll-container`) as HTMLDivElement; - - const scrollLayout = gridScroller?.closest('.appflowy-scroll-container') as HTMLDivElement; - - if (!gridScroller || !scrollLayout) { - return; - } - - const onWheel = (event: WheelEvent) => { - const deltaY = event.deltaY; - const deltaX = event.deltaX; - - if (Math.abs(deltaX) > 8) { - return; - } - - const { scrollTop, scrollHeight, clientHeight } = gridScroller; - - const atTop = deltaY < 0 && scrollTop === 0; - const atBottom = deltaY > 0 && scrollTop + clientHeight >= scrollHeight; - - // if at top or bottom, prevent default to allow layout to scroll - if (atTop || atBottom) { - scrollLayout.scrollTop += deltaY; - } - }; - - gridScroller.addEventListener('wheel', onWheel, { passive: false }); - return () => { - gridScroller.removeEventListener('wheel', onWheel); - }; - }, [rendered]); - - const onRendered = useCallback((viewId: string) => { - setRendered({ - viewId, - rendered: true, - }); - }, []); - - return ( - - - - - - ); -} - -export default React.memo(GridView); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/database/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/database/index.ts deleted file mode 100644 index 986343f9df..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/database/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './GridBlock'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/database/utils.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/database/utils.ts deleted file mode 100644 index 032502b415..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/database/utils.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { ViewLayoutPB } from '@/services/backend'; -import { createPage } from '$app/application/folder/page.service'; - -export async function createGrid(pageId: string) { - const newViewId = await createPage({ - layout: ViewLayoutPB.Grid, - name: '', - parent_view_id: pageId, - }); - - return newViewId; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/divider/DividerNode.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/divider/DividerNode.tsx deleted file mode 100644 index d7d475199b..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/divider/DividerNode.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import React, { forwardRef, memo, useMemo } from 'react'; -import { EditorElementProps, DividerNode as DividerNodeType } from '$app/application/document/document.types'; -import { useSelected } from 'slate-react'; - -export const DividerNode = memo( - forwardRef>( - ({ node: _node, children: children, ...attributes }, ref) => { - const selected = useSelected(); - - const className = useMemo(() => { - return `${attributes.className ?? ''} divider-node relative w-full rounded ${ - selected ? 'bg-content-blue-100' : '' - }`; - }, [attributes.className, selected]); - - return ( -
-
-
-
-
- {children} -
-
- ); - } - ) -); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/divider/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/divider/index.ts deleted file mode 100644 index 8f6141749a..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/divider/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './DividerNode'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/heading/Heading.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/heading/Heading.tsx deleted file mode 100644 index 4d23069c46..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/heading/Heading.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import React, { forwardRef, memo } from 'react'; -import { EditorElementProps, HeadingNode } from '$app/application/document/document.types'; -import { getHeadingCssProperty } from '$app/components/editor/plugins/utils'; - -export const Heading = memo( - forwardRef>(({ node, children, ...attributes }, ref) => { - const level = node.data.level; - const fontSizeCssProperty = getHeadingCssProperty(level); - - const className = `${attributes.className ?? ''} ${fontSizeCssProperty}`; - - return ( -
- {children} -
- ); - }) -); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/heading/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/heading/index.ts deleted file mode 100644 index 6406e7b07f..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/heading/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './Heading'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/image/ImageActions.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/image/ImageActions.tsx deleted file mode 100644 index b3d3575af2..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/image/ImageActions.tsx +++ /dev/null @@ -1,163 +0,0 @@ -import React, { useMemo, useState } from 'react'; -import { ImageNode } from '$app/application/document/document.types'; -import { ReactComponent as CopyIcon } from '$app/assets/copy.svg'; -import { ReactComponent as AlignLeftIcon } from '$app/assets/align-left.svg'; -import { ReactComponent as AlignCenterIcon } from '$app/assets/align-center.svg'; -import { ReactComponent as AlignRightIcon } from '$app/assets/align-right.svg'; -import { ReactComponent as DeleteIcon } from '$app/assets/delete.svg'; -import { useTranslation } from 'react-i18next'; -import { IconButton } from '@mui/material'; -import { notify } from '$app/components/_shared/notify'; -import { CustomEditor } from '$app/components/editor/command'; -import { useSlateStatic } from 'slate-react'; -import Popover from '@mui/material/Popover'; -import Tooltip from '@mui/material/Tooltip'; - -enum ImageAction { - Copy = 'copy', - AlignLeft = 'left', - AlignCenter = 'center', - AlignRight = 'right', - Delete = 'delete', -} - -function ImageActions({ node }: { node: ImageNode }) { - const { t } = useTranslation(); - const align = node.data.align; - const editor = useSlateStatic(); - const [alignAnchorEl, setAlignAnchorEl] = useState(null); - const alignOptions = useMemo(() => { - return [ - { - key: ImageAction.AlignLeft, - Icon: AlignLeftIcon, - onClick: () => { - CustomEditor.setImageBlockData(editor, node, { align: 'left' }); - setAlignAnchorEl(null); - }, - }, - { - key: ImageAction.AlignCenter, - Icon: AlignCenterIcon, - onClick: () => { - CustomEditor.setImageBlockData(editor, node, { align: 'center' }); - setAlignAnchorEl(null); - }, - }, - { - key: ImageAction.AlignRight, - Icon: AlignRightIcon, - onClick: () => { - CustomEditor.setImageBlockData(editor, node, { align: 'right' }); - setAlignAnchorEl(null); - }, - }, - ]; - }, [editor, node]); - const options = useMemo(() => { - return [ - { - key: ImageAction.Copy, - Icon: CopyIcon, - tooltip: t('button.copyLink'), - onClick: () => { - if (!node.data.url) return; - void navigator.clipboard.writeText(node.data.url); - notify.success(t('message.copy.success')); - }, - }, - (!align || align === 'left') && { - key: ImageAction.AlignLeft, - Icon: AlignLeftIcon, - tooltip: t('button.align'), - onClick: (e: React.MouseEvent) => { - setAlignAnchorEl(e.currentTarget); - }, - }, - align === 'center' && { - key: ImageAction.AlignCenter, - Icon: AlignCenterIcon, - tooltip: t('button.align'), - onClick: (e: React.MouseEvent) => { - setAlignAnchorEl(e.currentTarget); - }, - }, - align === 'right' && { - key: ImageAction.AlignRight, - Icon: AlignRightIcon, - tooltip: t('button.align'), - onClick: (e: React.MouseEvent) => { - setAlignAnchorEl(e.currentTarget); - }, - }, - { - key: ImageAction.Delete, - Icon: DeleteIcon, - tooltip: t('button.delete'), - onClick: () => { - CustomEditor.deleteNode(editor, node); - }, - }, - ].filter(Boolean) as { - key: ImageAction; - Icon: React.FC>; - tooltip: string; - onClick: (e: React.MouseEvent) => void; - }[]; - }, [align, node, t, editor]); - - return ( -
- {options.map((option) => { - const { key, Icon, tooltip, onClick } = option; - - return ( - - - - - - ); - })} - {!!alignAnchorEl && ( - setAlignAnchorEl(null)} - > - {alignOptions.map((option) => { - const { key, Icon, onClick } = option; - - return ( - - - - ); - })} - - )} -
- ); -} - -export default ImageActions; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/image/ImageBlock.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/image/ImageBlock.tsx deleted file mode 100644 index 661eb3e3de..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/image/ImageBlock.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import React, { forwardRef, memo, useCallback, useMemo, useRef } from 'react'; -import { EditorElementProps, ImageNode } from '$app/application/document/document.types'; -import { ReactEditor, useSelected, useSlateStatic } from 'slate-react'; -import ImageRender from '$app/components/editor/components/blocks/image/ImageRender'; -import ImageEmpty from '$app/components/editor/components/blocks/image/ImageEmpty'; - -export const ImageBlock = memo( - forwardRef>(({ node, children, className, ...attributes }, ref) => { - const selected = useSelected(); - const { url, align } = useMemo(() => node.data || {}, [node.data]); - const containerRef = useRef(null); - const editor = useSlateStatic(); - const onFocusNode = useCallback(() => { - ReactEditor.focus(editor); - const path = ReactEditor.findPath(editor, node); - - editor.select(path); - }, [editor, node]); - - return ( -
{ - if (!selected) onFocusNode(); - }} - className={`${className} image-block relative w-full cursor-pointer py-1`} - > -
- {children} -
-
- {url ? ( - - ) : ( - - )} -
-
- ); - }) -); - -export default ImageBlock; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/image/ImageEmpty.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/image/ImageEmpty.tsx deleted file mode 100644 index e0b649939e..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/image/ImageEmpty.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import React, { useEffect } from 'react'; -import { ReactComponent as ImageIcon } from '$app/assets/image.svg'; -import { useTranslation } from 'react-i18next'; -import UploadPopover from '$app/components/editor/components/blocks/image/UploadPopover'; -import { EditorNodeType, ImageNode } from '$app/application/document/document.types'; -import { useEditorBlockDispatch, useEditorBlockState } from '$app/components/editor/stores/block'; - -function ImageEmpty({ - containerRef, - onEscape, - node, -}: { - containerRef: React.RefObject; - onEscape: () => void; - node: ImageNode; -}) { - const { t } = useTranslation(); - const state = useEditorBlockState(EditorNodeType.ImageBlock); - const open = Boolean(state?.popoverOpen && state?.blockId === node.blockId && containerRef.current); - const { openPopover, closePopover } = useEditorBlockDispatch(); - - useEffect(() => { - const container = containerRef.current; - - if (!container) { - return; - } - - const handleClick = () => { - openPopover(EditorNodeType.ImageBlock, node.blockId); - }; - - container.addEventListener('click', handleClick); - return () => { - container.removeEventListener('click', handleClick); - }; - }, [containerRef, node.blockId, openPopover]); - return ( - <> -
- - {t('document.plugins.image.addAnImage')} -
- {open && ( - { - closePopover(EditorNodeType.ImageBlock); - onEscape(); - }} - /> - )} - - ); -} - -export default ImageEmpty; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/image/ImageRender.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/image/ImageRender.tsx deleted file mode 100644 index 07310b05be..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/image/ImageRender.tsx +++ /dev/null @@ -1,136 +0,0 @@ -import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; -import { ImageNode, ImageType } from '$app/application/document/document.types'; -import { useTranslation } from 'react-i18next'; -import { CircularProgress } from '@mui/material'; -import { ErrorOutline } from '@mui/icons-material'; -import ImageResizer from '$app/components/editor/components/blocks/image/ImageResizer'; -import { CustomEditor } from '$app/components/editor/command'; -import { useSlateStatic } from 'slate-react'; -import ImageActions from '$app/components/editor/components/blocks/image/ImageActions'; -import { LocalImage } from '$app/components/_shared/image_upload'; -import debounce from 'lodash-es/debounce'; - -const MIN_WIDTH = 100; - -const DELAY = 300; - -function ImageRender({ selected, node }: { selected: boolean; node: ImageNode }) { - const [loading, setLoading] = useState(true); - const [hasError, setHasError] = useState(false); - - const imgRef = useRef(null); - const editor = useSlateStatic(); - const { url = '', width: imageWidth, image_type: source } = useMemo(() => node.data || {}, [node.data]); - const { t } = useTranslation(); - const blockId = node.blockId; - - const [showActions, setShowActions] = useState(false); - const [initialWidth, setInitialWidth] = useState(null); - const [newWidth, setNewWidth] = useState(imageWidth ?? null); - - const debounceSubmitWidth = useMemo(() => { - return debounce((newWidth: number) => { - CustomEditor.setImageBlockData(editor, node, { - width: newWidth, - }); - }, DELAY); - }, [editor, node]); - - const handleWidthChange = useCallback( - (newWidth: number) => { - setNewWidth(newWidth); - debounceSubmitWidth(newWidth); - }, - [debounceSubmitWidth] - ); - - useEffect(() => { - if (!loading && !hasError && initialWidth === null && imgRef.current) { - setInitialWidth(imgRef.current.offsetWidth); - } - }, [hasError, initialWidth, loading]); - const imageProps: React.ImgHTMLAttributes = useMemo(() => { - return { - style: { width: loading || hasError ? '0' : newWidth ?? '100%', opacity: selected ? 0.8 : 1 }, - className: 'object-cover', - ref: imgRef, - src: url, - draggable: false, - onLoad: () => { - setHasError(false); - setLoading(false); - }, - onError: () => { - setHasError(true); - setLoading(false); - }, - }; - }, [url, newWidth, loading, hasError, selected]); - - const renderErrorNode = useCallback(() => { - return ( -
- -
{t('editor.imageLoadFailed')}
-
- ); - }, [t]); - - if (!url) return null; - - return ( -
{ - setShowActions(true); - }} - onMouseLeave={() => { - setShowActions(false); - }} - style={{ - minWidth: MIN_WIDTH, - width: 'fit-content', - }} - className={`image-render relative min-h-[48px] ${ - hasError || (loading && source !== ImageType.Local) ? 'w-full' : '' - }`} - > - {source === ImageType.Local ? ( - { - setHasError(true); - return null; - }} - loading={'lazy'} - /> - ) : ( - {`image-${blockId}`} - )} - - {initialWidth && ( - <> - - - - )} - {showActions && } - {hasError ? ( - renderErrorNode() - ) : loading && source !== ImageType.Local ? ( -
- -
{t('editor.loading')}
-
- ) : null} -
- ); -} - -export default ImageRender; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/image/ImageResizer.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/image/ImageResizer.tsx deleted file mode 100644 index e0d272acf3..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/image/ImageResizer.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import React, { useCallback, useRef } from 'react'; - -function ImageResizer({ - minWidth, - width, - onWidthChange, - isLeft, -}: { - isLeft?: boolean; - minWidth: number; - width: number; - onWidthChange: (newWidth: number) => void; -}) { - const originalWidth = useRef(width); - const startX = useRef(0); - - const onResize = useCallback( - (e: MouseEvent) => { - e.preventDefault(); - const diff = isLeft ? startX.current - e.clientX : e.clientX - startX.current; - const newWidth = originalWidth.current + diff; - - if (newWidth < minWidth) { - return; - } - - onWidthChange(newWidth); - }, - [isLeft, minWidth, onWidthChange] - ); - - const onResizeEnd = useCallback(() => { - document.removeEventListener('mousemove', onResize); - document.removeEventListener('mouseup', onResizeEnd); - }, [onResize]); - - const onResizeStart = useCallback( - (e: React.MouseEvent) => { - startX.current = e.clientX; - originalWidth.current = width; - document.addEventListener('mousemove', onResize); - document.addEventListener('mouseup', onResizeEnd); - }, - [onResize, onResizeEnd, width] - ); - - return ( -
-
-
- ); -} - -export default ImageResizer; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/image/UploadPopover.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/image/UploadPopover.tsx deleted file mode 100644 index 0aff9fb0cc..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/image/UploadPopover.tsx +++ /dev/null @@ -1,112 +0,0 @@ -import React, { useMemo } from 'react'; -import { PopoverOrigin } from '@mui/material/Popover/Popover'; -import usePopoverAutoPosition from '$app/components/_shared/popover/Popover.hooks'; - -import { useTranslation } from 'react-i18next'; -import { EmbedLink, Unsplash, UploadTabs, TabOption, TAB_KEY, UploadImage } from '$app/components/_shared/image_upload'; -import { CustomEditor } from '$app/components/editor/command'; -import { useSlateStatic } from 'slate-react'; -import { ImageNode, ImageType } from '$app/application/document/document.types'; - -const initialOrigin: { - transformOrigin: PopoverOrigin; - anchorOrigin: PopoverOrigin; -} = { - transformOrigin: { - vertical: 'top', - horizontal: 'center', - }, - anchorOrigin: { - vertical: 'bottom', - horizontal: 'center', - }, -}; - -function UploadPopover({ - open, - anchorEl, - onClose, - node, -}: { - open: boolean; - anchorEl: HTMLDivElement | null; - onClose: () => void; - node: ImageNode; -}) { - const editor = useSlateStatic(); - - const { t } = useTranslation(); - - const { transformOrigin, anchorOrigin, isEntered, paperHeight, paperWidth } = usePopoverAutoPosition({ - initialPaperWidth: 433, - initialPaperHeight: 300, - anchorEl, - initialAnchorOrigin: initialOrigin.anchorOrigin, - initialTransformOrigin: initialOrigin.transformOrigin, - open, - }); - - const tabOptions: TabOption[] = useMemo(() => { - return [ - { - label: t('button.upload'), - key: TAB_KEY.UPLOAD, - Component: UploadImage, - onDone: (link: string) => { - CustomEditor.setImageBlockData(editor, node, { - url: link, - image_type: ImageType.Local, - }); - onClose(); - }, - }, - { - label: t('document.imageBlock.embedLink.label'), - key: TAB_KEY.EMBED_LINK, - Component: EmbedLink, - onDone: (link: string) => { - CustomEditor.setImageBlockData(editor, node, { - url: link, - image_type: ImageType.External, - }); - onClose(); - }, - }, - { - key: TAB_KEY.UNSPLASH, - label: t('document.imageBlock.unsplash.label'), - Component: Unsplash, - onDone: (link: string) => { - CustomEditor.setImageBlockData(editor, node, { - url: link, - image_type: ImageType.External, - }); - onClose(); - }, - }, - ]; - }, [editor, node, onClose, t]); - - return ( - { - e.stopPropagation(); - }, - }} - containerStyle={{ - maxWidth: paperWidth, - maxHeight: paperHeight, - overflow: 'hidden', - }} - tabOptions={tabOptions} - /> - ); -} - -export default UploadPopover; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/image/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/image/index.ts deleted file mode 100644 index 73c3003a92..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/image/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './ImageBlock'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/math_equation/EditPopover.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/math_equation/EditPopover.tsx deleted file mode 100644 index f44158bdf2..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/math_equation/EditPopover.tsx +++ /dev/null @@ -1,165 +0,0 @@ -import React, { useCallback, useEffect, useState } from 'react'; -import Popover from '@mui/material/Popover'; -import { PopoverCommonProps } from '$app/components/editor/components/tools/popover'; -import { TextareaAutosize } from '@mui/material'; -import Button from '@mui/material/Button'; -import { useTranslation } from 'react-i18next'; -import { CustomEditor } from '$app/components/editor/command'; -import { ReactEditor, useSlateStatic } from 'slate-react'; -import { MathEquationNode } from '$app/application/document/document.types'; -import katex from 'katex'; -import { PopoverOrigin } from '@mui/material/Popover/Popover'; -import usePopoverAutoPosition from '$app/components/_shared/popover/Popover.hooks'; - -const initialOrigin: { - transformOrigin: PopoverOrigin; - anchorOrigin: PopoverOrigin; -} = { - transformOrigin: { - vertical: 'top', - horizontal: 'center', - }, - anchorOrigin: { - vertical: 'bottom', - horizontal: 'center', - }, -}; - -function EditPopover({ - open, - anchorEl, - onClose, - node, -}: { - open: boolean; - node: MathEquationNode; - anchorEl: HTMLDivElement | null; - onClose: () => void; -}) { - const editor = useSlateStatic(); - - const [error, setError] = useState<{ - name: string; - message: string; - } | null>(null); - const { t } = useTranslation(); - const [value, setValue] = useState(node.data.formula || ''); - const onInput = (event: React.FormEvent) => { - setValue(event.currentTarget.value); - }; - - const handleClose = useCallback(() => { - onClose(); - if (!node) return; - ReactEditor.focus(editor); - const path = ReactEditor.findPath(editor, node); - - editor.select(path); - }, [onClose, editor, node]); - - const handleDone = () => { - if (!node || error) return; - if (value !== node.data.formula) { - CustomEditor.setMathEquationBlockFormula(editor, node, value); - } - - handleClose(); - }; - - const onKeyDown = (e: React.KeyboardEvent) => { - e.stopPropagation(); - const shift = e.shiftKey; - - // If shift is pressed, allow the user to enter a new line, otherwise close the popover - if (!shift && e.key === 'Enter') { - e.preventDefault(); - e.stopPropagation(); - handleDone(); - } - - if (e.key === 'Escape') { - e.preventDefault(); - e.stopPropagation(); - handleClose(); - } - }; - - useEffect(() => { - try { - katex.render(value, document.createElement('div')); - setError(null); - } catch (e) { - setError( - e as { - name: string; - message: string; - } - ); - } - }, [value]); - - const { transformOrigin, anchorOrigin, isEntered } = usePopoverAutoPosition({ - initialPaperWidth: 300, - initialPaperHeight: 170, - anchorEl, - initialAnchorOrigin: initialOrigin.anchorOrigin, - initialTransformOrigin: initialOrigin.transformOrigin, - open, - }); - - return ( - { - e.stopPropagation(); - }} - onKeyDown={onKeyDown} - > -
- - - {error && ( -
- {error.name}: {error.message} -
- )} - -
- - -
-
-
- ); -} - -export default EditPopover; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/math_equation/MathEquation.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/math_equation/MathEquation.tsx deleted file mode 100644 index ee441be624..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/math_equation/MathEquation.tsx +++ /dev/null @@ -1,88 +0,0 @@ -import { forwardRef, memo, useEffect, useRef } from 'react'; -import { EditorElementProps, EditorNodeType, MathEquationNode } from '$app/application/document/document.types'; -import KatexMath from '$app/components/_shared/katex_math/KatexMath'; -import { useTranslation } from 'react-i18next'; -import { FunctionsOutlined } from '@mui/icons-material'; -import EditPopover from '$app/components/editor/components/blocks/math_equation/EditPopover'; -import { ReactEditor, useSelected, useSlateStatic } from 'slate-react'; -import { useEditorBlockDispatch, useEditorBlockState } from '$app/components/editor/stores/block'; - -export const MathEquation = memo( - forwardRef>( - ({ node, children, className, ...attributes }, ref) => { - const formula = node.data.formula; - const { t } = useTranslation(); - const containerRef = useRef(null); - const { openPopover, closePopover } = useEditorBlockDispatch(); - const state = useEditorBlockState(EditorNodeType.EquationBlock); - const open = Boolean(state?.popoverOpen && state?.blockId === node.blockId && containerRef.current); - - const selected = useSelected(); - - const editor = useSlateStatic(); - - useEffect(() => { - const slateDom = ReactEditor.toDOMNode(editor, editor); - - if (!slateDom) return; - const handleKeyDown = (e: KeyboardEvent) => { - if (e.key === 'Enter') { - e.preventDefault(); - e.stopPropagation(); - openPopover(EditorNodeType.EquationBlock, node.blockId); - } - }; - - if (selected) { - slateDom.addEventListener('keydown', handleKeyDown); - } - - return () => { - slateDom.removeEventListener('keydown', handleKeyDown); - }; - }, [editor, node.blockId, openPopover, selected]); - - return ( - <> -
{ - openPopover(EditorNodeType.EquationBlock, node.blockId); - }} - className={`${className} math-equation-block relative w-full cursor-pointer py-2`} - > -
- {formula ? ( - - ) : ( -
- - {t('document.plugins.mathEquation.addMathEquation')} -
- )} -
-
- {children} -
-
- {open && ( - { - closePopover(EditorNodeType.EquationBlock); - }} - node={node} - open={open} - anchorEl={containerRef.current} - /> - )} - - ); - } - ) -); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/math_equation/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/math_equation/index.ts deleted file mode 100644 index ae6eb70209..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/math_equation/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './MathEquation'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/numbered_list/NumberListIcon.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/numbered_list/NumberListIcon.tsx deleted file mode 100644 index 888b46c980..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/numbered_list/NumberListIcon.tsx +++ /dev/null @@ -1,87 +0,0 @@ -import React, { useMemo } from 'react'; -import { ReactEditor, useSlate, useSlateStatic } from 'slate-react'; -import { Element, Path } from 'slate'; -import { NumberedListNode } from '$app/application/document/document.types'; -import { letterize, romanize } from '$app/utils/list'; -import { CustomEditor } from '$app/components/editor/command'; - -enum Letter { - Number = 'number', - Letter = 'letter', - Roman = 'roman', -} - -function getLetterNumber(index: number, letter: Letter) { - if (letter === Letter.Number) { - return index; - } else if (letter === Letter.Letter) { - return letterize(index); - } else { - return romanize(index); - } -} - -function NumberListIcon({ block, className }: { block: NumberedListNode; className: string }) { - const editor = useSlate(); - const staticEditor = useSlateStatic(); - - const path = ReactEditor.findPath(editor, block); - const index = useMemo(() => { - let index = 1; - - let topNode; - let prevPath = Path.previous(path); - - while (prevPath) { - const prev = editor.node(prevPath); - - const prevNode = prev[0] as Element; - - if (prevNode.type === block.type) { - index += 1; - topNode = prevNode; - } else { - break; - } - - prevPath = Path.previous(prevPath); - } - - if (!topNode) { - return Number(block.data?.number ?? 1); - } - - const startIndex = (topNode as NumberedListNode).data?.number ?? 1; - - return index + Number(startIndex) - 1; - }, [editor, block, path]); - - const letter = useMemo(() => { - const level = CustomEditor.getListLevel(staticEditor, block.type, path); - - if (level % 3 === 0) { - return Letter.Number; - } else if (level % 3 === 1) { - return Letter.Letter; - } else { - return Letter.Roman; - } - }, [block.type, staticEditor, path]); - - const dataNumber = useMemo(() => { - return getLetterNumber(index, letter); - }, [index, letter]); - - return ( - { - e.preventDefault(); - }} - contentEditable={false} - data-number={dataNumber} - className={`${className} numbered-icon flex w-[24px] min-w-[24px] justify-center pr-1 font-medium`} - /> - ); -} - -export default NumberListIcon; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/numbered_list/NumberedList.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/numbered_list/NumberedList.tsx deleted file mode 100644 index f3e34e1571..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/numbered_list/NumberedList.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import React, { forwardRef, memo } from 'react'; -import { EditorElementProps, NumberedListNode } from '$app/application/document/document.types'; - -export const NumberedList = memo( - forwardRef>( - ({ node: _, children, className, ...attributes }, ref) => { - return ( -
- {children} -
- ); - } - ) -); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/numbered_list/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/numbered_list/index.ts deleted file mode 100644 index 6e985ae25b..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/numbered_list/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './NumberedList'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/page/Page.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/page/Page.tsx deleted file mode 100644 index f93cb897ba..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/page/Page.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import React, { forwardRef, memo, useMemo } from 'react'; -import { EditorElementProps, PageNode } from '$app/application/document/document.types'; - -export const Page = memo( - forwardRef>(({ node: _, children, ...attributes }, ref) => { - const className = useMemo(() => { - return `${attributes.className ?? ''} document-title pb-3 text-5xl font-bold`; - }, [attributes.className]); - - return ( -
- {children} -
- ); - }) -); - -export default Page; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/page/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/page/index.ts deleted file mode 100644 index d9925d7520..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/page/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './Page'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/paragraph/Paragraph.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/paragraph/Paragraph.tsx deleted file mode 100644 index 96524db239..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/paragraph/Paragraph.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import React, { forwardRef, memo } from 'react'; -import { EditorElementProps, ParagraphNode } from '$app/application/document/document.types'; - -export const Paragraph = memo( - forwardRef>(({ node: _, children, ...attributes }, ref) => { - { - return ( -
- {children} -
- ); - } - }) -); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/paragraph/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/paragraph/index.ts deleted file mode 100644 index 01752c914c..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/paragraph/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './Paragraph'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/quote/Quote.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/quote/Quote.tsx deleted file mode 100644 index 5afc35289b..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/quote/Quote.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import React, { forwardRef, memo, useMemo } from 'react'; -import { EditorElementProps, QuoteNode } from '$app/application/document/document.types'; - -export const QuoteList = memo( - forwardRef>(({ node: _, children, ...attributes }, ref) => { - const className = useMemo(() => { - return `flex w-full flex-col ml-3 border-l-[4px] border-fill-default pl-2 ${attributes.className ?? ''}`; - }, [attributes.className]); - - return ( -
- {children} -
- ); - }) -); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/quote/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/quote/index.ts deleted file mode 100644 index c88e677a53..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/quote/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './Quote'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/text/StartIcon.hooks.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/text/StartIcon.hooks.tsx deleted file mode 100644 index acf16581f4..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/text/StartIcon.hooks.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import React, { FC, useCallback, useMemo } from 'react'; -import { EditorNodeType, TextNode } from '$app/application/document/document.types'; -import { ReactEditor, useSlate } from 'slate-react'; -import { Editor, Element } from 'slate'; -import CheckboxIcon from '$app/components/editor/components/blocks/todo_list/CheckboxIcon'; -import ToggleIcon from '$app/components/editor/components/blocks/toggle_list/ToggleIcon'; -import NumberListIcon from '$app/components/editor/components/blocks/numbered_list/NumberListIcon'; -import BulletedListIcon from '$app/components/editor/components/blocks/bulleted_list/BulletedListIcon'; - -export function useStartIcon(node: TextNode) { - const editor = useSlate(); - const path = ReactEditor.findPath(editor, node); - const block = Editor.parent(editor, path)?.[0] as Element | null; - - const Component = useMemo(() => { - if (!Element.isElement(block)) { - return null; - } - - switch (block.type) { - case EditorNodeType.TodoListBlock: - return CheckboxIcon; - case EditorNodeType.ToggleListBlock: - return ToggleIcon; - case EditorNodeType.NumberedListBlock: - return NumberListIcon; - case EditorNodeType.BulletedListBlock: - return BulletedListIcon; - default: - return null; - } - }, [block]) as FC<{ block: Element; className: string }> | null; - - const renderIcon = useCallback(() => { - if (!Component || !block) { - return null; - } - - return ; - }, [Component, block]); - - return { - hasStartIcon: !!Component, - renderIcon, - }; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/text/Text.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/text/Text.tsx deleted file mode 100644 index 768524394e..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/text/Text.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import React, { forwardRef, memo } from 'react'; -import Placeholder from '$app/components/editor/components/blocks/_shared/Placeholder'; -import { EditorElementProps, TextNode } from '$app/application/document/document.types'; -import { useSlateStatic } from 'slate-react'; -import { useStartIcon } from '$app/components/editor/components/blocks/text/StartIcon.hooks'; - -export const Text = memo( - forwardRef>(({ node, children, className, ...attributes }, ref) => { - const editor = useSlateStatic(); - const { hasStartIcon, renderIcon } = useStartIcon(node); - const isEmpty = editor.isEmpty(node); - - return ( - - {renderIcon()} - - {children} - - ); - }) -); - -export default Text; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/text/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/text/index.ts deleted file mode 100644 index b0c76af0b0..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/text/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './Text'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/todo_list/CheckboxIcon.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/todo_list/CheckboxIcon.tsx deleted file mode 100644 index d98990c886..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/todo_list/CheckboxIcon.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import React, { useCallback } from 'react'; -import { TodoListNode } from '$app/application/document/document.types'; -import { CustomEditor } from '$app/components/editor/command'; -import { ReactEditor, useSlateStatic } from 'slate-react'; -import { Location } from 'slate'; -import { ReactComponent as CheckboxCheckSvg } from '$app/assets/database/checkbox-check.svg'; -import { ReactComponent as CheckboxUncheckSvg } from '$app/assets/database/checkbox-uncheck.svg'; - -function CheckboxIcon({ block, className }: { block: TodoListNode; className: string }) { - const editor = useSlateStatic(); - const { checked } = block.data; - - const toggleTodo = useCallback( - (e: React.MouseEvent) => { - const path = ReactEditor.findPath(editor, block); - const start = editor.start(path); - let at: Location = start; - - if (e.shiftKey) { - const end = editor.end(path); - - at = { - anchor: start, - focus: end, - }; - } - - CustomEditor.toggleTodo(editor, at); - }, - [editor, block] - ); - - return ( - { - e.preventDefault(); - }} - className={`${className} cursor-pointer pr-1 text-xl text-fill-default`} - > - {checked ? : } - - ); -} - -export default CheckboxIcon; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/todo_list/TodoList.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/todo_list/TodoList.tsx deleted file mode 100644 index c662c48153..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/todo_list/TodoList.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import React, { forwardRef, memo, useMemo } from 'react'; -import { EditorElementProps, TodoListNode } from '$app/application/document/document.types'; - -export const TodoList = memo( - forwardRef>(({ node, children, ...attributes }, ref) => { - const { checked = false } = useMemo(() => node.data || {}, [node.data]); - const className = useMemo(() => { - return `flex w-full flex-col ${checked ? 'checked' : ''} ${attributes.className ?? ''}`; - }, [attributes.className, checked]); - - return ( - <> -
- {children} -
- - ); - }) -); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/todo_list/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/todo_list/index.ts deleted file mode 100644 index f239f43459..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/todo_list/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './TodoList'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/toggle_list/ToggleIcon.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/toggle_list/ToggleIcon.tsx deleted file mode 100644 index ad27822cb5..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/toggle_list/ToggleIcon.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import React, { useCallback } from 'react'; -import { CustomEditor } from '$app/components/editor/command'; -import { useSlateStatic } from 'slate-react'; -import { ToggleListNode } from '$app/application/document/document.types'; -import { ReactComponent as RightSvg } from '$app/assets/more.svg'; - -function ToggleIcon({ block, className }: { block: ToggleListNode; className: string }) { - const editor = useSlateStatic(); - const { collapsed } = block.data; - - const toggleToggleList = useCallback(() => { - CustomEditor.toggleToggleList(editor, block); - }, [editor, block]); - - return ( - { - e.preventDefault(); - }} - className={`${className} cursor-pointer pr-1 text-xl hover:text-fill-default`} - > - {collapsed ? : } - - ); -} - -export default ToggleIcon; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/toggle_list/ToggleList.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/toggle_list/ToggleList.tsx deleted file mode 100644 index 809f3b750d..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/toggle_list/ToggleList.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import React, { forwardRef, memo, useMemo } from 'react'; -import { EditorElementProps, ToggleListNode } from '$app/application/document/document.types'; - -export const ToggleList = memo( - forwardRef>(({ node, children, ...attributes }, ref) => { - const { collapsed } = useMemo(() => node.data || {}, [node.data]); - const className = `${attributes.className ?? ''} flex w-full flex-col ${collapsed ? 'collapsed' : ''}`; - - return ( - <> -
- {children} -
- - ); - }) -); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/toggle_list/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/toggle_list/index.ts deleted file mode 100644 index 833bdb5210..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/toggle_list/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './ToggleList'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/editor/CollaborativeEditor.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/editor/CollaborativeEditor.tsx deleted file mode 100644 index 2526df895e..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/editor/CollaborativeEditor.tsx +++ /dev/null @@ -1,93 +0,0 @@ -import { memo, useEffect, useMemo, useState } from 'react'; - -import Editor from '$app/components/editor/components/editor/Editor'; -import { EditorProps } from '$app/application/document/document.types'; -import { Provider } from '$app/components/editor/provider'; -import { YXmlText } from 'yjs/dist/src/types/YXmlText'; -import { getInsertTarget, getYTarget } from '$app/components/editor/provider/utils/relation'; -import isEqual from 'lodash-es/isEqual'; - -export const CollaborativeEditor = memo( - ({ id, title, cover, showTitle = true, onTitleChange, onCoverChange, ...props }: EditorProps) => { - const [sharedType, setSharedType] = useState(null); - const provider = useMemo(() => { - setSharedType(null); - - return new Provider(id); - }, [id]); - - const root = useMemo(() => { - if (!showTitle || !sharedType || !sharedType.doc) return null; - - return getYTarget(sharedType?.doc, [0]); - }, [sharedType, showTitle]); - - const rootText = useMemo(() => { - if (!root) return null; - return getInsertTarget(root, [0]); - }, [root]); - - useEffect(() => { - if (!rootText || rootText.toString() === title) return; - - if (rootText.length > 0) { - rootText.delete(0, rootText.length); - } - - rootText.insert(0, title || ''); - }, [title, rootText]); - - useEffect(() => { - if (!root) return; - - const originalCover = root.getAttribute('data')?.cover; - - if (cover === undefined) return; - if (isEqual(originalCover, cover)) return; - root.setAttribute('data', { cover: cover ? cover : undefined }); - }, [cover, root]); - - useEffect(() => { - if (!root) return; - const rootId = root.getAttribute('blockId'); - - if (!rootId) return; - - const getCover = () => { - const data = root.getAttribute('data'); - - onCoverChange?.(data?.cover); - }; - - getCover(); - const onChange = () => { - onTitleChange?.(root.toString()); - getCover(); - }; - - root.observeDeep(onChange); - return () => root.unobserveDeep(onChange); - }, [onTitleChange, root, onCoverChange]); - - useEffect(() => { - provider.connect(); - - const handleConnected = () => { - setSharedType(provider.sharedType); - }; - - provider.on('ready', handleConnected); - void provider.initialDocument(showTitle); - return () => { - provider.off('ready', handleConnected); - provider.disconnect(); - }; - }, [provider, showTitle]); - - if (!sharedType || id !== provider.id) { - return null; - } - - return ; - } -); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/editor/CustomEditable.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/editor/CustomEditable.tsx deleted file mode 100644 index b0bbe0eb28..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/editor/CustomEditable.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import React, { ComponentProps, useCallback } from 'react'; -import { Editable, useSlate } from 'slate-react'; -import Element from './Element'; -import { Leaf } from './Leaf'; -import { useShortcuts } from '$app/components/editor/plugins/shortcuts'; -import { useInlineKeyDown } from '$app/components/editor/components/editor/Editor.hooks'; - -type CustomEditableProps = Omit, 'renderElement' | 'renderLeaf'> & - Partial, 'renderElement' | 'renderLeaf'>> & { - disableFocus?: boolean; - }; - -export function CustomEditable({ - renderElement = Element, - disableFocus = false, - renderLeaf = Leaf, - ...props -}: CustomEditableProps) { - const editor = useSlate(); - const { onKeyDown: onShortcutsKeyDown } = useShortcuts(editor); - const withInlineKeyDown = useInlineKeyDown(editor); - const onKeyDown = useCallback( - (event: React.KeyboardEvent) => { - withInlineKeyDown(event); - onShortcutsKeyDown(event); - }, - [onShortcutsKeyDown, withInlineKeyDown] - ); - - return ( - - ); -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/editor/Editor.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/editor/Editor.hooks.ts deleted file mode 100644 index f2443ba44b..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/editor/Editor.hooks.ts +++ /dev/null @@ -1,137 +0,0 @@ -import { KeyboardEvent, useCallback, useEffect, useMemo } from 'react'; - -import { BaseRange, createEditor, Editor, Element, NodeEntry, Range, Transforms } from 'slate'; -import { ReactEditor, withReact } from 'slate-react'; -import { withBlockPlugins } from '$app/components/editor/plugins/withBlockPlugins'; -import { withInlines } from '$app/components/editor/components/inline_nodes'; -import { withYHistory, withYjs, YjsEditor } from '@slate-yjs/core'; -import * as Y from 'yjs'; -import { CustomEditor } from '$app/components/editor/command'; -import { CodeNode, EditorNodeType } from '$app/application/document/document.types'; -import { decorateCode } from '$app/components/editor/components/blocks/code/utils'; -import { withMarkdown } from '$app/components/editor/plugins/shortcuts'; -import { createHotkey, HOT_KEY_NAME } from '$app/utils/hotkeys'; - -export function useEditor(sharedType: Y.XmlText) { - const editor = useMemo(() => { - if (!sharedType) return null; - const e = withMarkdown(withBlockPlugins(withInlines(withReact(withYHistory(withYjs(createEditor(), sharedType)))))); - - // Ensure editor always has at least 1 valid child - const { normalizeNode } = e; - - e.normalizeNode = (entry) => { - const [node] = entry; - - if (!Editor.isEditor(node) || node.children.length > 0) { - return normalizeNode(entry); - } - - // Ensure editor always has at least 1 valid child - CustomEditor.insertEmptyLineAtEnd(e as ReactEditor & YjsEditor); - }; - - return e; - }, [sharedType]) as ReactEditor & YjsEditor; - - const initialValue = useMemo(() => { - return []; - }, []); - - // Connect editor in useEffect to comply with concurrent mode requirements. - useEffect(() => { - YjsEditor.connect(editor); - return () => { - YjsEditor.disconnect(editor); - }; - }, [editor]); - - const handleOnClickEnd = useCallback(() => { - const path = [editor.children.length - 1]; - const node = Editor.node(editor, path) as NodeEntry; - const latestNodeIsEmpty = CustomEditor.isEmptyText(editor, node[0]); - - if (latestNodeIsEmpty) { - ReactEditor.focus(editor); - editor.select(path); - editor.collapse({ - edge: 'end', - }); - - return; - } - - CustomEditor.insertEmptyLineAtEnd(editor); - }, [editor]); - - return { - editor, - initialValue, - handleOnClickEnd, - }; -} - -export function useDecorateCodeHighlight(editor: ReactEditor) { - return useCallback( - (entry: NodeEntry): BaseRange[] => { - const path = entry[1]; - - const blockEntry = editor.above({ - at: path, - match: (n) => !Editor.isEditor(n) && Element.isElement(n) && n.blockId !== undefined, - }); - - if (!blockEntry) return []; - - const block = blockEntry[0] as CodeNode; - - if (block.type === EditorNodeType.CodeBlock) { - const language = block.data.language; - - return decorateCode(entry, language, false); - } - - return []; - }, - [editor] - ); -} - -export function useInlineKeyDown(editor: ReactEditor) { - return useCallback( - (e: KeyboardEvent) => { - const selection = editor.selection; - - // Default left/right behavior is unit:'character'. - // This fails to distinguish between two cursor positions, such as - // foo vs foo. - // Here we modify the behavior to unit:'offset'. - // This lets the user step into and out of the inline without stepping over characters. - // You may wish to customize this further to only use unit:'offset' in specific cases. - if (selection && Range.isCollapsed(selection)) { - const { nativeEvent } = e; - - if ( - createHotkey(HOT_KEY_NAME.LEFT)(nativeEvent) && - CustomEditor.beforeIsInlineNode(editor, selection, { - unit: 'offset', - }) - ) { - e.preventDefault(); - Transforms.move(editor, { unit: 'offset', reverse: true }); - return; - } - - if ( - createHotkey(HOT_KEY_NAME.RIGHT)(nativeEvent) && - CustomEditor.afterIsInlineNode(editor, selection, { unit: 'offset' }) - ) { - e.preventDefault(); - Transforms.move(editor, { unit: 'offset' }); - return; - } - } - }, - [editor] - ); -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/editor/Editor.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/editor/Editor.tsx deleted file mode 100644 index d87dbe3f35..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/editor/Editor.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import React, { useCallback } from 'react'; -import { useDecorateCodeHighlight, useEditor } from '$app/components/editor/components/editor/Editor.hooks'; -import { Slate } from 'slate-react'; -import { CustomEditable } from '$app/components/editor/components/editor/CustomEditable'; -import { SelectionToolbar } from '$app/components/editor/components/tools/selection_toolbar'; -import { BlockActionsToolbar } from '$app/components/editor/components/tools/block_actions'; - -import { CircularProgress } from '@mui/material'; -import { NodeEntry } from 'slate'; -import { - DecorateStateProvider, - EditorSelectedBlockProvider, - useInitialEditorState, - SlashStateProvider, - EditorInlineBlockStateProvider, -} from '$app/components/editor/stores'; -import CommandPanel from '../tools/command_panel/CommandPanel'; -import { EditorBlockStateProvider } from '$app/components/editor/stores/block'; -import { LocalEditorProps } from '$app/application/document/document.types'; - -function Editor({ sharedType, disableFocus, caretColor = 'var(--text-title)' }: LocalEditorProps) { - const { editor, initialValue, handleOnClickEnd, ...props } = useEditor(sharedType); - const decorateCodeHighlight = useDecorateCodeHighlight(editor); - - const { - selectedBlocks, - decorate: decorateCustomRange, - decorateState, - slashState, - inlineBlockState, - blockState, - } = useInitialEditorState(editor); - - const decorate = useCallback( - (entry: NodeEntry) => { - const codeRanges = decorateCodeHighlight(entry); - const customRanges = decorateCustomRange(entry); - - return [...codeRanges, ...customRanges]; - }, - [decorateCodeHighlight, decorateCustomRange] - ); - - if (editor.sharedRoot.length === 0) { - return ; - } - - return ( - - - - - - - - - - - -
- - - - - - - ); -} - -export default Editor; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/editor/Element.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/editor/Element.hooks.ts deleted file mode 100644 index bf7045705d..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/editor/Element.hooks.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { Element } from 'slate'; -import { useContext, useEffect, useMemo } from 'react'; -import { useSnapshot } from 'valtio'; -import { useSelected } from 'slate-react'; - -import { EditorSelectedBlockContext } from '$app/components/editor/stores/selected'; - -export function useElementState(element: Element) { - const blockId = element.blockId; - const selectedBlockContext = useContext(EditorSelectedBlockContext); - const selected = useSelected(); - - useEffect(() => { - if (!blockId) return; - - if (!selected) { - selectedBlockContext.delete(blockId); - } - }, [blockId, selected, selectedBlockContext]); - - const selectedBlockIds = useSnapshot(selectedBlockContext); - const blockSelected = useMemo(() => { - if (!blockId) return false; - return selectedBlockIds.has(blockId); - }, [blockId, selectedBlockIds]); - - return { - blockSelected, - }; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/editor/Element.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/editor/Element.tsx deleted file mode 100644 index 1824d8a590..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/editor/Element.tsx +++ /dev/null @@ -1,132 +0,0 @@ -import React, { FC, HTMLAttributes, useMemo } from 'react'; -import { RenderElementProps, useSlateStatic } from 'slate-react'; -import { - BlockData, - EditorElementProps, - EditorInlineNodeType, - EditorNodeType, - TextNode, -} from '$app/application/document/document.types'; -import { Paragraph } from '$app/components/editor/components/blocks/paragraph'; -import { Heading } from '$app/components/editor/components/blocks/heading'; -import { TodoList } from '$app/components/editor/components/blocks/todo_list'; -import { Code } from '$app/components/editor/components/blocks/code'; -import { QuoteList } from '$app/components/editor/components/blocks/quote'; -import { NumberedList } from '$app/components/editor/components/blocks/numbered_list'; -import { BulletedList } from '$app/components/editor/components/blocks/bulleted_list'; -import { DividerNode } from '$app/components/editor/components/blocks/divider'; -import { InlineFormula } from '$app/components/editor/components/inline_nodes/inline_formula'; -import { ToggleList } from '$app/components/editor/components/blocks/toggle_list'; -import { Callout } from '$app/components/editor/components/blocks/callout'; -import { Mention } from '$app/components/editor/components/inline_nodes/mention'; -import { GridBlock } from '$app/components/editor/components/blocks/database'; -import { MathEquation } from '$app/components/editor/components/blocks/math_equation'; -import { ImageBlock } from '$app/components/editor/components/blocks/image'; - -import { Text as TextComponent } from '../blocks/text'; -import { Page } from '../blocks/page'; -import { useElementState } from '$app/components/editor/components/editor/Element.hooks'; -import UnSupportBlock from '$app/components/editor/components/blocks/_shared/unSupportBlock'; -import { renderColor } from '$app/utils/color'; - -function Element({ element, attributes, children }: RenderElementProps) { - const node = element; - - const InlineComponent = useMemo(() => { - switch (node.type) { - case EditorInlineNodeType.Formula: - return InlineFormula; - case EditorInlineNodeType.Mention: - return Mention; - default: - return null; - } - }, [node.type]) as FC; - - const Component = useMemo(() => { - switch (node.type) { - case EditorNodeType.Page: - return Page; - case EditorNodeType.HeadingBlock: - return Heading; - case EditorNodeType.TodoListBlock: - return TodoList; - case EditorNodeType.Paragraph: - return Paragraph; - case EditorNodeType.CodeBlock: - return Code; - case EditorNodeType.QuoteBlock: - return QuoteList; - case EditorNodeType.NumberedListBlock: - return NumberedList; - case EditorNodeType.BulletedListBlock: - return BulletedList; - case EditorNodeType.DividerBlock: - return DividerNode; - case EditorNodeType.ToggleListBlock: - return ToggleList; - case EditorNodeType.CalloutBlock: - return Callout; - case EditorNodeType.GridBlock: - return GridBlock; - case EditorNodeType.EquationBlock: - return MathEquation; - case EditorNodeType.ImageBlock: - return ImageBlock; - default: - return UnSupportBlock; - } - }, [node.type]) as FC>; - - const editor = useSlateStatic(); - const { blockSelected } = useElementState(node); - const isEmbed = editor.isEmbed(node); - - const className = useMemo(() => { - const align = - ( - node.data as { - align: 'left' | 'center' | 'right'; - } - )?.align || 'left'; - - return `block-element flex rounded ${align ? `block-align-${align}` : ''} ${ - blockSelected && !isEmbed ? 'bg-content-blue-100' : '' - }`; - }, [node.data, blockSelected, isEmbed]); - - const style = useMemo(() => { - const data = (node.data as BlockData) || {}; - - return { - backgroundColor: data.bg_color ? renderColor(data.bg_color) : undefined, - color: data.font_color ? renderColor(data.font_color) : undefined, - }; - }, [node.data]); - - if (InlineComponent) { - return ( - - {children} - - ); - } - - if (node.type === EditorNodeType.Text) { - return ( - - {children} - - ); - } - - return ( -
- - {children} - -
- ); -} - -export default Element; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/editor/Leaf.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/editor/Leaf.tsx deleted file mode 100644 index 188ac33361..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/editor/Leaf.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import React, { CSSProperties } from 'react'; -import { RenderLeafProps } from 'slate-react'; -import { Link } from '$app/components/editor/components/inline_nodes/link'; -import { renderColor } from '$app/utils/color'; - -export function Leaf({ attributes, children, leaf }: RenderLeafProps) { - let newChildren = children; - - const classList = [leaf.prism_token, leaf.prism_token && 'token', leaf.class_name].filter(Boolean); - - if (leaf.code) { - newChildren = ( - - {newChildren} - - ); - } - - if (leaf.underline) { - newChildren = {newChildren}; - } - - if (leaf.strikethrough) { - newChildren = {newChildren}; - } - - if (leaf.italic) { - newChildren = {newChildren}; - } - - if (leaf.bold) { - newChildren = {newChildren}; - } - - const style: CSSProperties = {}; - - if (leaf.font_color) { - style['color'] = renderColor(leaf.font_color); - } - - if (leaf.bg_color) { - style['backgroundColor'] = renderColor(leaf.bg_color); - } - - if (leaf.href) { - newChildren = {newChildren}; - } - - return ( - - {newChildren} - - ); -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/editor/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/editor/index.ts deleted file mode 100644 index c0c3c728d1..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/editor/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './CollaborativeEditor'; -export * from './Editor'; -export { useDecorateCodeHighlight } from '$app/components/editor/components/editor/Editor.hooks'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/editor/utils.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/editor/utils.ts deleted file mode 100644 index cc6960cdf4..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/editor/utils.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { BasePoint, Editor, Transforms } from 'slate'; -import { ReactEditor } from 'slate-react'; - -export function getNodePath(editor: ReactEditor, target: HTMLElement) { - const slateNode = ReactEditor.toSlateNode(editor, target); - const path = ReactEditor.findPath(editor, slateNode); - - return path; -} - -export function moveCursorToNodeEnd(editor: ReactEditor, target: HTMLElement) { - const path = getNodePath(editor, target); - const afterPath = Editor.after(editor, path); - - ReactEditor.focus(editor); - - if (afterPath) { - const afterStart = Editor.start(editor, afterPath); - - moveCursorToPoint(editor, afterStart); - } else { - const beforeEnd = Editor.end(editor, path); - - moveCursorToPoint(editor, beforeEnd); - } -} - -export function moveCursorToPoint(editor: ReactEditor, point: BasePoint) { - ReactEditor.focus(editor); - Transforms.select(editor, point); -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/InlineChromiumBugfix.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/InlineChromiumBugfix.tsx deleted file mode 100644 index fb32eb18a9..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/InlineChromiumBugfix.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import React from 'react'; - -// Put this at the start and end of an inline component to work around this Chromium bug: -// https://bugs.chromium.org/p/chromium/issues/detail?id=1249405 - -export const InlineChromiumBugfix = ({ className }: { className?: string }) => ( - - {String.fromCodePoint(160) /* Non-breaking space */} - -); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/index.ts deleted file mode 100644 index 29f27984f7..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './withInline'; -export * from './inline_formula'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/inline_formula/FormulaEditPopover.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/inline_formula/FormulaEditPopover.tsx deleted file mode 100644 index c60d7af40e..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/inline_formula/FormulaEditPopover.tsx +++ /dev/null @@ -1,87 +0,0 @@ -import React, { useState } from 'react'; - -import Popover from '@mui/material/Popover'; -import { PopoverCommonProps } from '$app/components/editor/components/tools/popover'; -import { useTranslation } from 'react-i18next'; -import TextField from '@mui/material/TextField'; -import { IconButton } from '@mui/material'; -import { ReactComponent as SelectCheck } from '$app/assets/select-check.svg'; -import { ReactComponent as Clear } from '$app/assets/delete.svg'; -import Tooltip from '@mui/material/Tooltip'; - -function FormulaEditPopover({ - defaultText, - open, - anchorEl, - onClose, - onDone, - onClear, -}: { - defaultText: string; - open: boolean; - anchorEl: HTMLElement | null; - onClose: () => void; - onClear: () => void; - onDone: (formula: string) => void; -}) { - const [text, setText] = useState(defaultText); - const { t } = useTranslation(); - - return ( - -
- setText(e.target.value)} - fullWidth={true} - onKeyDown={(e) => { - e.stopPropagation(); - if (e.key === 'Enter') { - e.preventDefault(); - onDone(text); - } - - if (e.key === 'Escape') { - e.preventDefault(); - onClose(); - } - - if (e.key === 'Tab') { - e.preventDefault(); - } - }} - /> - - onDone(text)}> - - - - - - - - -
-
- ); -} - -export default FormulaEditPopover; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/inline_formula/FormulaLeaf.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/inline_formula/FormulaLeaf.tsx deleted file mode 100644 index 324d273ab3..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/inline_formula/FormulaLeaf.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import React from 'react'; -import KatexMath from '$app/components/_shared/katex_math/KatexMath'; - -function FormulaLeaf({ formula, children }: { formula: string; children: React.ReactNode }) { - return ( - - - - - - {children} - - ); -} - -export default FormulaLeaf; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/inline_formula/InlineFormula.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/inline_formula/InlineFormula.tsx deleted file mode 100644 index 204047304f..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/inline_formula/InlineFormula.tsx +++ /dev/null @@ -1,133 +0,0 @@ -import React, { forwardRef, memo, useCallback, MouseEvent, useRef, useEffect } from 'react'; -import { ReactEditor, useSelected, useSlate } from 'slate-react'; -import { Editor, Range, Transforms } from 'slate'; -import { EditorElementProps, FormulaNode } from '$app/application/document/document.types'; -import FormulaLeaf from '$app/components/editor/components/inline_nodes/inline_formula/FormulaLeaf'; -import FormulaEditPopover from '$app/components/editor/components/inline_nodes/inline_formula/FormulaEditPopover'; -import { getNodePath, moveCursorToNodeEnd } from '$app/components/editor/components/editor/utils'; -import { CustomEditor } from '$app/components/editor/command'; -import { useEditorInlineBlockState } from '$app/components/editor/stores'; -import { InlineChromiumBugfix } from '$app/components/editor/components/inline_nodes/InlineChromiumBugfix'; - -export const InlineFormula = memo( - forwardRef>(({ node, children, ...attributes }, ref) => { - const editor = useSlate(); - const formula = node.data; - const { popoverOpen = false, setRange, openPopover, closePopover } = useEditorInlineBlockState('formula'); - const anchor = useRef(null); - const selected = useSelected(); - const open = Boolean(popoverOpen && selected); - - const isCollapsed = editor.selection && Range.isCollapsed(editor.selection); - - useEffect(() => { - if (selected && isCollapsed && !open) { - const afterPoint = editor.selection ? editor.after(editor.selection) : undefined; - - const afterStart = afterPoint ? Editor.start(editor, afterPoint) : undefined; - - if (afterStart) { - editor.select(afterStart); - } - } - }, [editor, isCollapsed, selected, open]); - - const handleClick = useCallback( - (e: MouseEvent) => { - const target = e.currentTarget; - const path = getNodePath(editor, target); - - setRange(path); - openPopover(); - }, - [editor, openPopover, setRange] - ); - - const handleEditPopoverClose = useCallback(() => { - closePopover(); - if (anchor.current === null) { - return; - } - - moveCursorToNodeEnd(editor, anchor.current); - }, [closePopover, editor]); - - const selectNode = useCallback(() => { - if (anchor.current === null) { - return; - } - - const path = getNodePath(editor, anchor.current); - - ReactEditor.focus(editor); - Transforms.select(editor, path); - }, [editor]); - - const onClear = useCallback(() => { - selectNode(); - CustomEditor.toggleFormula(editor); - closePopover(); - }, [selectNode, closePopover, editor]); - - const onDone = useCallback( - (newFormula: string) => { - selectNode(); - if (newFormula === '' && anchor.current) { - const path = getNodePath(editor, anchor.current); - const point = editor.before(path); - - CustomEditor.deleteFormula(editor); - closePopover(); - if (point) { - ReactEditor.focus(editor); - editor.select(point); - } - - return; - } else { - CustomEditor.updateFormula(editor, newFormula); - handleEditPopoverClose(); - } - }, - [closePopover, editor, handleEditPopoverClose, selectNode] - ); - - return ( - <> - { - anchor.current = el; - if (ref) { - if (typeof ref === 'function') { - ref(el); - } else { - ref.current = el; - } - } - }} - contentEditable={false} - onDoubleClick={handleClick} - onClick={handleClick} - className={`${attributes.className ?? ''} formula-inline relative cursor-pointer rounded px-1 py-0.5 ${ - selected ? 'selected' : '' - }`} - > - - {children} - - - {open && ( - - )} - - ); - }) -); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/inline_formula/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/inline_formula/index.ts deleted file mode 100644 index 5643ae8943..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/inline_formula/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './InlineFormula'; -export * from './FormulaLeaf'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/link/Link.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/link/Link.tsx deleted file mode 100644 index 09095480dc..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/link/Link.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import { memo, useCallback, useRef } from 'react'; -import { ReactEditor, useSlate } from 'slate-react'; -import { getNodePath } from '$app/components/editor/components/editor/utils'; -import { Transforms, Text } from 'slate'; -import { useDecorateDispatch } from '$app/components/editor/stores'; - -export const Link = memo(({ children }: { leaf: Text; children: React.ReactNode }) => { - const { add: addDecorate } = useDecorateDispatch(); - - const editor = useSlate(); - - const ref = useRef(null); - - const handleClick = useCallback( - (e: React.MouseEvent) => { - e.stopPropagation(); - e.preventDefault(); - if (ref.current === null) { - return; - } - - const path = getNodePath(editor, ref.current); - - ReactEditor.focus(editor); - Transforms.select(editor, path); - - if (!editor.selection) return; - addDecorate({ - range: editor.selection, - class_name: 'bg-content-blue-100 rounded', - type: 'link', - }); - }, - [addDecorate, editor] - ); - - return ( - <> - - {children} - - - ); -}); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/link/LinkEditContent.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/link/LinkEditContent.tsx deleted file mode 100644 index af62a7b28f..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/link/LinkEditContent.tsx +++ /dev/null @@ -1,179 +0,0 @@ -import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; -import Typography from '@mui/material/Typography'; -import { addMark, removeMark } from 'slate'; -import { EditorMarkFormat } from '$app/application/document/document.types'; -import { notify } from 'src/appflowy_app/components/_shared/notify'; -import { CustomEditor } from '$app/components/editor/command'; -import { useTranslation } from 'react-i18next'; -import { useSlateStatic } from 'slate-react'; -import { ReactComponent as RemoveSvg } from '$app/assets/delete.svg'; -import { ReactComponent as LinkSvg } from '$app/assets/link.svg'; -import { ReactComponent as CopySvg } from '$app/assets/copy.svg'; -import KeyboardNavigation, { - KeyboardNavigationOption, -} from '$app/components/_shared/keyboard_navigation/KeyboardNavigation'; -import isHotkey from 'is-hotkey'; -import LinkEditInput from '$app/components/editor/components/inline_nodes/link/LinkEditInput'; -import { openUrl, isUrl } from '$app/utils/open_url'; - -function LinkEditContent({ onClose, defaultHref }: { onClose: () => void; defaultHref: string }) { - const editor = useSlateStatic(); - const { t } = useTranslation(); - const isActivated = CustomEditor.isMarkActive(editor, EditorMarkFormat.Href); - - const [focusMenu, setFocusMenu] = useState(false); - const scrollRef = useRef(null); - const inputRef = useRef(null); - const [link, setLink] = useState(defaultHref); - - const setNodeMark = useCallback(() => { - if (link === '') { - removeMark(editor, EditorMarkFormat.Href); - } else { - addMark(editor, EditorMarkFormat.Href, link); - } - }, [editor, link]); - - const removeNodeMark = useCallback(() => { - onClose(); - editor.removeMark(EditorMarkFormat.Href); - }, [editor, onClose]); - - useEffect(() => { - const input = inputRef.current; - - if (!input) return; - - let isComposing = false; - - const handleCompositionUpdate = () => { - isComposing = true; - }; - - const handleCompositionEnd = () => { - isComposing = false; - }; - - const handleKeyDown = (e: KeyboardEvent) => { - e.stopPropagation(); - - if (e.key === 'Enter') { - e.preventDefault(); - if (isUrl(link)) { - onClose(); - setNodeMark(); - } - - return; - } - - if (e.key === 'Escape') { - e.preventDefault(); - onClose(); - return; - } - - if (e.key === 'Tab') { - e.preventDefault(); - setFocusMenu(true); - return; - } - - if (!isComposing && (e.key === 'ArrowDown' || e.key === 'ArrowUp')) { - notify.clear(); - notify.info(`Press Tab to focus on the menu`); - return; - } - }; - - input.addEventListener('compositionstart', handleCompositionUpdate); - input.addEventListener('compositionend', handleCompositionEnd); - input.addEventListener('compositionupdate', handleCompositionUpdate); - input.addEventListener('keydown', handleKeyDown); - return () => { - input.removeEventListener('keydown', handleKeyDown); - input.removeEventListener('compositionstart', handleCompositionUpdate); - input.removeEventListener('compositionend', handleCompositionEnd); - input.removeEventListener('compositionupdate', handleCompositionUpdate); - }; - }, [link, onClose, setNodeMark]); - - const onConfirm = useCallback( - (key: string) => { - if (key === 'open') { - openUrl(link); - } else if (key === 'copy') { - void navigator.clipboard.writeText(link); - notify.success(t('message.copy.success')); - } else if (key === 'remove') { - removeNodeMark(); - } - }, - [link, removeNodeMark, t] - ); - - const renderOption = useCallback((icon: React.ReactNode, label: string) => { - return ( -
- {icon} -
{label}
-
- ); - }, []); - - const editOptions: KeyboardNavigationOption[] = useMemo(() => { - return [ - { - key: 'open', - disabled: !isUrl(link), - content: renderOption(, t('editor.openLink')), - }, - { - key: 'copy', - content: renderOption(, t('editor.copyLink')), - }, - { - key: 'remove', - content: renderOption(, t('editor.removeLink')), - }, - ]; - }, [link, renderOption, t]); - - return ( - <> - {!isActivated && ( - {t('editor.addYourLink')} - )} - - -
- {isActivated && ( - { - setFocusMenu(true); - }} - onBlur={() => { - setFocusMenu(false); - }} - disableSelect={!focusMenu} - onEscape={onClose} - onKeyDown={(e) => { - e.stopPropagation(); - if (isHotkey('Tab', e)) { - e.preventDefault(); - setFocusMenu(false); - inputRef.current?.focus(); - } - }} - /> - )} -
- - ); -} - -export default LinkEditContent; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/link/LinkEditInput.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/link/LinkEditInput.tsx deleted file mode 100644 index 6e9a0bb497..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/link/LinkEditInput.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import { TextField } from '@mui/material'; -import { useTranslation } from 'react-i18next'; -import { isUrl } from '$app/utils/open_url'; - -function LinkEditInput({ - link, - setLink, - inputRef, -}: { - link: string; - setLink: (link: string) => void; - inputRef: React.RefObject; -}) { - const { t } = useTranslation(); - const [error, setError] = useState(null); - - useEffect(() => { - if (isUrl(link)) { - setError(null); - return; - } - - setError(t('editor.incorrectLink')); - }, [link, t]); - - return ( -
- setLink(e.target.value)} - spellCheck={false} - inputRef={inputRef} - className={'my-1 p-0'} - placeholder={'https://example.com'} - fullWidth={true} - /> - - ); -} - -export default LinkEditInput; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/link/LinkEditPopover.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/link/LinkEditPopover.tsx deleted file mode 100644 index 2a5e3630da..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/link/LinkEditPopover.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import React from 'react'; -import Popover from '@mui/material/Popover'; -import { PopoverCommonProps } from '$app/components/editor/components/tools/popover'; - -import usePopoverAutoPosition from '$app/components/_shared/popover/Popover.hooks'; -import { PopoverOrigin } from '@mui/material/Popover/Popover'; -import LinkEditContent from '$app/components/editor/components/inline_nodes/link/LinkEditContent'; - -const initialAnchorOrigin: PopoverOrigin = { - vertical: 'bottom', - horizontal: 'center', -}; - -const initialTransformOrigin: PopoverOrigin = { - vertical: 'top', - horizontal: 'center', -}; - -export function LinkEditPopover({ - defaultHref, - open, - onClose, - anchorPosition, - anchorReference, -}: { - defaultHref: string; - open: boolean; - onClose: () => void; - anchorPosition?: { top: number; left: number; height: number }; - anchorReference?: 'anchorPosition' | 'anchorEl'; -}) { - const { - paperHeight, - anchorPosition: newAnchorPosition, - transformOrigin, - anchorOrigin, - } = usePopoverAutoPosition({ - anchorPosition, - open, - initialAnchorOrigin, - initialTransformOrigin, - initialPaperWidth: 340, - initialPaperHeight: 200, - }); - - return ( - { - onClose(); - }} - transformOrigin={transformOrigin} - anchorOrigin={anchorOrigin} - onMouseDown={(e) => e.stopPropagation()} - > -
- -
-
- ); -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/link/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/link/index.ts deleted file mode 100644 index 295683a3bc..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/link/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './Link'; - -export * from './LinkEditPopover'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/mention/Mention.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/mention/Mention.tsx deleted file mode 100644 index 7511147ad0..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/mention/Mention.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import React, { forwardRef, memo } from 'react'; -import { EditorElementProps, MentionNode } from '$app/application/document/document.types'; - -import MentionLeaf from '$app/components/editor/components/inline_nodes/mention/MentionLeaf'; -import { InlineChromiumBugfix } from '$app/components/editor/components/inline_nodes/InlineChromiumBugfix'; - -export const Mention = memo( - forwardRef>(({ node, children, ...attributes }, ref) => { - return ( - - - {children} - - - - ); - }) -); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/mention/MentionLeaf.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/mention/MentionLeaf.tsx deleted file mode 100644 index 10def395c5..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/mention/MentionLeaf.tsx +++ /dev/null @@ -1,141 +0,0 @@ -import React, { useCallback, useEffect, useState } from 'react'; -import { Mention, MentionPage } from '$app/application/document/document.types'; -import { ReactComponent as DocumentSvg } from '$app/assets/document.svg'; -import { useTranslation } from 'react-i18next'; -import { getPage } from '$app/application/folder/page.service'; -import { useSelected, useSlate } from 'slate-react'; -import { ReactComponent as EyeClose } from '$app/assets/eye_close.svg'; -import { notify } from 'src/appflowy_app/components/_shared/notify'; -import { subscribeNotifications } from '$app/application/notification'; -import { FolderNotification } from '@/services/backend'; -import { Editor, Range } from 'slate'; -import { useAppDispatch } from '$app/stores/store'; -import { openPage } from '$app_reducers/pages/async_actions'; - -export function MentionLeaf({ mention }: { mention: Mention }) { - const { t } = useTranslation(); - const [page, setPage] = useState(null); - const [error, setError] = useState(false); - const editor = useSlate(); - const selected = useSelected(); - const isCollapsed = editor.selection && Range.isCollapsed(editor.selection); - const dispatch = useAppDispatch(); - - useEffect(() => { - if (selected && isCollapsed && page) { - const afterPoint = editor.selection ? editor.after(editor.selection) : undefined; - - const afterStart = afterPoint ? Editor.start(editor, afterPoint) : undefined; - - if (afterStart) { - editor.select(afterStart); - } - } - }, [editor, isCollapsed, selected, page]); - - const loadPage = useCallback(async () => { - setError(true); - // keep old field for backward compatibility - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-expect-error - const pageId = mention.page_id ?? mention.page; - - if (!pageId) return; - try { - const page = await getPage(pageId); - - setPage(page); - setError(false); - } catch { - setPage(null); - setError(true); - } - }, [mention]); - - useEffect(() => { - void loadPage(); - }, [loadPage]); - - const handleOpenPage = useCallback(() => { - if (!page) { - notify.error(t('document.mention.deletedContent')); - return; - } - - void dispatch(openPage(page.id)); - }, [page, dispatch, t]); - - useEffect(() => { - if (!page) return; - const unsubscribePromise = subscribeNotifications( - { - [FolderNotification.DidUpdateView]: (changeset) => { - setPage((prev) => { - if (!prev) { - return prev; - } - - return { - ...prev, - name: changeset.name, - }; - }); - }, - }, - { - id: page.id, - } - ); - - return () => void unsubscribePromise.then((unsubscribe) => unsubscribe()); - }, [page]); - - useEffect(() => { - const parentId = page?.parentId; - - if (!parentId) return; - - const unsubscribePromise = subscribeNotifications( - { - [FolderNotification.DidUpdateChildViews]: (changeset) => { - if (changeset.delete_child_views.includes(page.id)) { - setPage(null); - setError(true); - } - }, - }, - { - id: parentId, - } - ); - - return () => void unsubscribePromise.then((unsubscribe) => unsubscribe()); - }, [page]); - - return ( - - {error ? ( - <> - - {t('document.mention.deleted')} - - ) : ( - page && ( - <> - {page.icon?.value || } - {page.name.trim() || t('menuAppHeader.defaultNewPageName')} - - ) - )} - - ); -} - -export default MentionLeaf; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/mention/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/mention/index.ts deleted file mode 100644 index d3ee18034d..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/mention/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './Mention'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/withInline.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/withInline.ts deleted file mode 100644 index 2859c1f0a8..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/withInline.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { ReactEditor } from 'slate-react'; -import { EditorInlineNodeType, inlineNodeTypes } from '$app/application/document/document.types'; -import { Element } from 'slate'; - -export function withInlines(editor: ReactEditor) { - const { isInline, isElementReadOnly, isSelectable, isVoid, markableVoid } = editor; - - const matchInlineType = (element: Element) => { - return inlineNodeTypes.includes(element.type as EditorInlineNodeType); - }; - - editor.isInline = (element) => { - return matchInlineType(element) || isInline(element); - }; - - editor.isVoid = (element) => { - return matchInlineType(element) || isVoid(element); - }; - - editor.markableVoid = (element) => { - return matchInlineType(element) || markableVoid(element); - }; - - editor.isElementReadOnly = (element) => - inlineNodeTypes.includes(element.type as EditorInlineNodeType) || isElementReadOnly(element); - - editor.isSelectable = (element) => - !inlineNodeTypes.includes(element.type as EditorInlineNodeType) && isSelectable(element); - - return editor; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/_shared/ColorPicker.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/_shared/ColorPicker.tsx deleted file mode 100644 index 41dea96f1e..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/_shared/ColorPicker.tsx +++ /dev/null @@ -1,174 +0,0 @@ -import React, { useCallback, useRef, useMemo } from 'react'; -import Typography from '@mui/material/Typography'; -import KeyboardNavigation, { - KeyboardNavigationOption, -} from '$app/components/_shared/keyboard_navigation/KeyboardNavigation'; -import { useTranslation } from 'react-i18next'; -import { TitleOutlined } from '@mui/icons-material'; -import { EditorMarkFormat } from '$app/application/document/document.types'; -import { ColorEnum, renderColor } from '$app/utils/color'; - -export interface ColorPickerProps { - onChange?: (format: EditorMarkFormat.FontColor | EditorMarkFormat.BgColor, color: string) => void; - onEscape?: () => void; - disableFocus?: boolean; -} -export function ColorPicker({ onEscape, onChange, disableFocus }: ColorPickerProps) { - const { t } = useTranslation(); - - const ref = useRef(null); - - const handleColorChange = useCallback( - (key: string) => { - const [format, , color = ''] = key.split('-'); - const formatKey = format === 'font' ? EditorMarkFormat.FontColor : EditorMarkFormat.BgColor; - - onChange?.(formatKey, color); - }, - [onChange] - ); - - const renderColorItem = useCallback( - (name: string, color: string, backgroundColor?: string) => { - return ( -
{ - handleColorChange(backgroundColor ? backgroundColor : color); - }} - className={'flex w-full cursor-pointer items-center justify-center gap-2'} - > -
- -
-
{name}
-
- ); - }, - [handleColorChange] - ); - - const colors: KeyboardNavigationOption[] = useMemo(() => { - return [ - { - key: 'font_color', - content: ( - - {t('editor.textColor')} - - ), - children: [ - { - key: 'font-default', - content: renderColorItem(t('editor.fontColorDefault'), ''), - }, - { - key: `font-gray-rgb(120, 119, 116)`, - content: renderColorItem(t('editor.fontColorGray'), 'rgb(120, 119, 116)'), - }, - { - key: 'font-brown-rgb(159, 107, 83)', - content: renderColorItem(t('editor.fontColorBrown'), 'rgb(159, 107, 83)'), - }, - { - key: 'font-orange-rgb(217, 115, 13)', - content: renderColorItem(t('editor.fontColorOrange'), 'rgb(217, 115, 13)'), - }, - { - key: 'font-yellow-rgb(203, 145, 47)', - content: renderColorItem(t('editor.fontColorYellow'), 'rgb(203, 145, 47)'), - }, - { - key: 'font-green-rgb(68, 131, 97)', - content: renderColorItem(t('editor.fontColorGreen'), 'rgb(68, 131, 97)'), - }, - { - key: 'font-blue-rgb(51, 126, 169)', - content: renderColorItem(t('editor.fontColorBlue'), 'rgb(51, 126, 169)'), - }, - { - key: 'font-purple-rgb(144, 101, 176)', - content: renderColorItem(t('editor.fontColorPurple'), 'rgb(144, 101, 176)'), - }, - { - key: 'font-pink-rgb(193, 76, 138)', - content: renderColorItem(t('editor.fontColorPink'), 'rgb(193, 76, 138)'), - }, - { - key: 'font-red-rgb(212, 76, 71)', - content: renderColorItem(t('editor.fontColorRed'), 'rgb(212, 76, 71)'), - }, - ], - }, - { - key: 'bg_color', - content: ( - - {t('editor.backgroundColor')} - - ), - children: [ - { - key: 'bg-default', - content: renderColorItem(t('editor.backgroundColorDefault'), '', ''), - }, - { - key: `bg-lime-${ColorEnum.Lime}`, - content: renderColorItem(t('editor.backgroundColorLime'), '', ColorEnum.Lime), - }, - { - key: `bg-aqua-${ColorEnum.Aqua}`, - content: renderColorItem(t('editor.backgroundColorAqua'), '', ColorEnum.Aqua), - }, - { - key: `bg-orange-${ColorEnum.Orange}`, - content: renderColorItem(t('editor.backgroundColorOrange'), '', ColorEnum.Orange), - }, - { - key: `bg-yellow-${ColorEnum.Yellow}`, - content: renderColorItem(t('editor.backgroundColorYellow'), '', ColorEnum.Yellow), - }, - { - key: `bg-green-${ColorEnum.Green}`, - content: renderColorItem(t('editor.backgroundColorGreen'), '', ColorEnum.Green), - }, - { - key: `bg-blue-${ColorEnum.Blue}`, - content: renderColorItem(t('editor.backgroundColorBlue'), '', ColorEnum.Blue), - }, - { - key: `bg-purple-${ColorEnum.Purple}`, - content: renderColorItem(t('editor.backgroundColorPurple'), '', ColorEnum.Purple), - }, - { - key: `bg-pink-${ColorEnum.Pink}`, - content: renderColorItem(t('editor.backgroundColorPink'), '', ColorEnum.Pink), - }, - { - key: `bg-red-${ColorEnum.LightPink}`, - content: renderColorItem(t('editor.backgroundColorRed'), '', ColorEnum.LightPink), - }, - ], - }, - ]; - }, [renderColorItem, t]); - - return ( -
- -
- ); -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/_shared/CustomColorPicker.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/_shared/CustomColorPicker.tsx deleted file mode 100644 index ae463a2ff3..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/_shared/CustomColorPicker.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import React, { useState } from 'react'; -import Popover, { PopoverProps } from '@mui/material/Popover'; -import { Color, SketchPicker } from 'react-color'; - -import { Divider } from '@mui/material'; - -export function CustomColorPicker({ - onColorChange, - ...props -}: { - onColorChange?: (color: string) => void; -} & PopoverProps) { - const [color, setColor] = useState(); - - return ( - - { - setColor(color.rgb); - onColorChange?.(color.hex); - }} - color={color} - /> - - - ); -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/_shared/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/_shared/index.ts deleted file mode 100644 index 00e212aa7f..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/_shared/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './CustomColorPicker'; -export * from './ColorPicker'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/block_actions/AddBlockBelow.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/block_actions/AddBlockBelow.tsx deleted file mode 100644 index eb2675bc71..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/block_actions/AddBlockBelow.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import React from 'react'; -import { ReactEditor, useSlate } from 'slate-react'; -import { IconButton, Tooltip } from '@mui/material'; -import { ReactComponent as AddSvg } from '$app/assets/add.svg'; -import { useTranslation } from 'react-i18next'; -import { Element, Path } from 'slate'; -import { CustomEditor } from '$app/components/editor/command'; -import { EditorNodeType } from '$app/application/document/document.types'; -import { YjsEditor } from '@slate-yjs/core'; -import { useSlashState } from '$app/components/editor/stores'; - -function AddBlockBelow({ node }: { node?: Element }) { - const { t } = useTranslation(); - const editor = useSlate(); - const { setOpen: setSlashOpen } = useSlashState(); - - const handleAddBelow = () => { - if (!node) return; - ReactEditor.focus(editor); - - const nodePath = ReactEditor.findPath(editor, node); - const nextPath = Path.next(nodePath); - - editor.select(nodePath); - - if (editor.isSelectable(node)) { - editor.collapse({ - edge: 'start', - }); - } - - const isEmptyNode = CustomEditor.isEmptyText(editor, node); - - // if the node is not a paragraph, or it is not empty, insert a new empty line - if (node.type !== EditorNodeType.Paragraph || !isEmptyNode) { - CustomEditor.insertEmptyLine(editor as ReactEditor & YjsEditor, nextPath); - editor.select(nextPath); - } - - requestAnimationFrame(() => { - setSlashOpen(true); - }); - }; - - return ( - <> - - - - - - - ); -} - -export default AddBlockBelow; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/block_actions/BlockActions.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/block_actions/BlockActions.tsx deleted file mode 100644 index f5833e538b..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/block_actions/BlockActions.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import React from 'react'; - -import { Element } from 'slate'; -import AddBlockBelow from '$app/components/editor/components/tools/block_actions/AddBlockBelow'; -import { ReactComponent as DragSvg } from '$app/assets/drag.svg'; -import { IconButton, Tooltip } from '@mui/material'; -import { useTranslation } from 'react-i18next'; - -export function BlockActions({ - node, - onClickDrag, -}: { - node?: Element; - onClickDrag: (e: React.MouseEvent) => void; -}) { - const { t } = useTranslation(); - - return ( - <> - - - - - - - - ); -} - -export default BlockActions; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/block_actions/BlockActionsToolbar.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/block_actions/BlockActionsToolbar.hooks.ts deleted file mode 100644 index bc1086dde9..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/block_actions/BlockActionsToolbar.hooks.ts +++ /dev/null @@ -1,146 +0,0 @@ -import { RefObject, useCallback, useEffect, useState } from 'react'; -import { ReactEditor, useSlate } from 'slate-react'; -import { findEventNode, getBlockActionsPosition } from '$app/components/editor/components/tools/block_actions/utils'; -import { Element, Editor, Range } from 'slate'; -import { EditorNodeType } from '$app/application/document/document.types'; -import { Log } from '$app/utils/log'; - -export function useBlockActionsToolbar(ref: RefObject, contextMenuVisible: boolean) { - const editor = useSlate(); - const [node, setNode] = useState(null); - - const recalculatePosition = useCallback( - (blockElement: HTMLElement) => { - const { top, left } = getBlockActionsPosition(editor, blockElement); - - const slateEditorDom = ReactEditor.toDOMNode(editor, editor); - - if (!ref.current) return; - - ref.current.style.top = `${top + slateEditorDom.offsetTop}px`; - ref.current.style.left = `${left + slateEditorDom.offsetLeft - 64}px`; - }, - [editor, ref] - ); - - const close = useCallback(() => { - const el = ref.current; - - if (!el) return; - - el.style.opacity = '0'; - el.style.pointerEvents = 'none'; - setNode(null); - }, [ref]); - - useEffect(() => { - const handleMouseMove = (e: MouseEvent) => { - const el = ref.current; - - if (!el) return; - - const target = e.target as HTMLElement; - - if (target.closest(`[contenteditable="false"]`)) { - return; - } - - let range: Range | null = null; - let node; - - try { - range = ReactEditor.findEventRange(editor, e); - } catch { - const editorDom = ReactEditor.toDOMNode(editor, editor); - const rect = editorDom.getBoundingClientRect(); - const isOverLeftBoundary = e.clientX < rect.left + 64; - const isOverRightBoundary = e.clientX > rect.right - 64; - let newX = e.clientX; - - if (isOverLeftBoundary) { - newX = rect.left + 64; - } - - if (isOverRightBoundary) { - newX = rect.right - 64; - } - - node = findEventNode(editor, { - x: newX, - y: e.clientY, - }); - } - - if (!range && !node) { - Log.warn('No range and node found'); - return; - } else if (range) { - const match = editor.above({ - match: (n) => { - return !Editor.isEditor(n) && Element.isElement(n) && n.blockId !== undefined; - }, - at: range, - }); - - if (!match) { - close(); - return; - } - - node = match[0] as Element; - } - - if (!node) { - close(); - return; - } - - if (node.type === EditorNodeType.Page) return; - const blockElement = ReactEditor.toDOMNode(editor, node); - - if (!blockElement) return; - recalculatePosition(blockElement); - el.style.opacity = '1'; - el.style.pointerEvents = 'auto'; - const slateNode = ReactEditor.toSlateNode(editor, blockElement) as Element; - - setNode(slateNode); - }; - - const dom = ReactEditor.toDOMNode(editor, editor); - - if (!contextMenuVisible) { - dom.addEventListener('mousemove', handleMouseMove); - dom.parentElement?.addEventListener('mouseleave', close); - } - - return () => { - dom.removeEventListener('mousemove', handleMouseMove); - dom.parentElement?.removeEventListener('mouseleave', close); - }; - }, [close, editor, contextMenuVisible, ref, recalculatePosition]); - - useEffect(() => { - let observer: MutationObserver | null = null; - - if (node) { - const dom = ReactEditor.toDOMNode(editor, node); - - if (dom.parentElement) { - observer = new MutationObserver(close); - - observer.observe(dom.parentElement, { - childList: true, - }); - } - } - - return () => { - observer?.disconnect(); - }; - }, [close, editor, node]); - - return { - node: node?.type === EditorNodeType.Page ? null : node, - }; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/block_actions/BlockActionsToolbar.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/block_actions/BlockActionsToolbar.tsx deleted file mode 100644 index 729b4df144..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/block_actions/BlockActionsToolbar.tsx +++ /dev/null @@ -1,141 +0,0 @@ -import React, { useCallback, useContext, useEffect, useRef, useState } from 'react'; -import { useBlockActionsToolbar } from './BlockActionsToolbar.hooks'; -import BlockActions from '$app/components/editor/components/tools/block_actions/BlockActions'; - -import { getBlockCssProperty } from '$app/components/editor/components/tools/block_actions/utils'; -import BlockOperationMenu from '$app/components/editor/components/tools/block_actions/BlockOperationMenu'; -import { ReactEditor, useSlateStatic } from 'slate-react'; -import { PopoverProps } from '@mui/material/Popover'; - -import { EditorSelectedBlockContext } from '$app/components/editor/stores/selected'; -import withErrorBoundary from '$app/components/_shared/error_boundary/withError'; -import { CustomEditor } from '$app/components/editor/command'; -import isEqual from 'lodash-es/isEqual'; -import { Range } from 'slate'; - -const Toolbar = () => { - const ref = useRef(null); - const [openContextMenu, setOpenContextMenu] = useState(false); - const { node } = useBlockActionsToolbar(ref, openContextMenu); - const cssProperty = node && getBlockCssProperty(node); - const selectedBlockContext = useContext(EditorSelectedBlockContext); - const popoverPropsRef = useRef | undefined>(undefined); - const editor = useSlateStatic(); - - const handleOpen = useCallback(() => { - if (!node || !node.blockId) return; - setOpenContextMenu(true); - const path = ReactEditor.findPath(editor, node); - - editor.select(path); - selectedBlockContext.clear(); - selectedBlockContext.add(node.blockId); - }, [editor, node, selectedBlockContext]); - - const handleClose = useCallback(() => { - setOpenContextMenu(false); - selectedBlockContext.clear(); - }, [selectedBlockContext]); - - useEffect(() => { - if (!node) return; - const nodeDom = ReactEditor.toDOMNode(editor, node); - const onContextMenu = (e: MouseEvent) => { - const { clientX, clientY } = e; - - e.stopPropagation(); - - const { selection } = editor; - - const editorRange = ReactEditor.findEventRange(editor, e); - - if (!editorRange || !selection) return; - - const rangeBlock = CustomEditor.getBlock(editor, editorRange); - const selectedBlock = CustomEditor.getBlock(editor, selection); - - if ( - Range.intersection(selection, editorRange) || - (rangeBlock && selectedBlock && isEqual(rangeBlock[1], selectedBlock[1])) - ) { - const windowSelection = window.getSelection(); - const range = windowSelection?.rangeCount ? windowSelection?.getRangeAt(0) : null; - const isCollapsed = windowSelection?.isCollapsed; - - if (windowSelection && !isCollapsed) { - if (range && range.endOffset === 0 && range.startContainer !== range.endContainer) { - const newRange = range.cloneRange(); - - newRange.setEnd(range.startContainer, range.startOffset); - windowSelection.removeAllRanges(); - windowSelection.addRange(newRange); - } - } - - return; - } - - e.preventDefault(); - - popoverPropsRef.current = { - transformOrigin: { - vertical: 'top', - horizontal: 'left', - }, - anchorReference: 'anchorPosition', - anchorPosition: { - top: clientY, - left: clientX, - }, - }; - - handleOpen(); - }; - - nodeDom.addEventListener('contextmenu', onContextMenu); - - return () => { - nodeDom.removeEventListener('contextmenu', onContextMenu); - }; - }, [editor, handleOpen, node]); - return ( - <> -
- {/* Ensure the toolbar in middle */} -
$
- { - ) => { - const target = e.currentTarget; - const rect = target.getBoundingClientRect(); - - popoverPropsRef.current = { - transformOrigin: { - vertical: 'center', - horizontal: 'right', - }, - anchorReference: 'anchorPosition', - anchorPosition: { - top: rect.top + rect.height / 2, - left: rect.left, - }, - }; - - handleOpen(); - }} - /> - } -
- {node && openContextMenu && ( - - )} - - ); -}; - -export const BlockActionsToolbar = withErrorBoundary(Toolbar); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/block_actions/BlockOperationMenu.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/block_actions/BlockOperationMenu.tsx deleted file mode 100644 index ade9817503..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/block_actions/BlockOperationMenu.tsx +++ /dev/null @@ -1,209 +0,0 @@ -import React, { useCallback, useMemo } from 'react'; -import Popover, { PopoverProps } from '@mui/material/Popover'; -import { ReactComponent as DeleteSvg } from '$app/assets/delete.svg'; -import { ReactComponent as CopySvg } from '$app/assets/copy.svg'; -import { useTranslation } from 'react-i18next'; -import { Divider } from '@mui/material'; -import { PopoverCommonProps } from '$app/components/editor/components/tools/popover'; -import { Element, Path } from 'slate'; -import { ReactEditor, useSlateStatic } from 'slate-react'; -import { CustomEditor } from '$app/components/editor/command'; -import KeyboardNavigation, { - KeyboardNavigationOption, -} from '$app/components/_shared/keyboard_navigation/KeyboardNavigation'; -import { Color } from '$app/components/editor/components/tools/block_actions/color'; -import { getModifier } from '$app/utils/hotkeys'; - -import isHotkey from 'is-hotkey'; -import { EditorNodeType } from '$app/application/document/document.types'; -import { EditorSelectedBlockContext } from '$app/components/editor/stores/selected'; - -export const canSetColorBlocks: EditorNodeType[] = [ - EditorNodeType.Paragraph, - EditorNodeType.HeadingBlock, - EditorNodeType.TodoListBlock, - EditorNodeType.BulletedListBlock, - EditorNodeType.NumberedListBlock, - EditorNodeType.ToggleListBlock, - EditorNodeType.QuoteBlock, - EditorNodeType.CalloutBlock, -]; - -export function BlockOperationMenu({ - node, - ...props -}: { - node: Element; -} & PopoverProps) { - const editor = useSlateStatic(); - const { t } = useTranslation(); - - const canSetColor = useMemo(() => { - return canSetColorBlocks.includes(node.type as EditorNodeType); - }, [node]); - const selectedBlockContext = React.useContext(EditorSelectedBlockContext); - const [openColorMenu, setOpenColorMenu] = React.useState(false); - const ref = React.useRef(null); - const handleClose = useCallback(() => { - props.onClose?.({}, 'backdropClick'); - ReactEditor.focus(editor); - try { - const path = ReactEditor.findPath(editor, node); - - editor.select(path); - } catch (e) { - // do nothing - } - - editor.collapse({ - edge: 'start', - }); - }, [editor, node, props]); - - const onConfirm = useCallback( - (optionKey: string) => { - switch (optionKey) { - case 'delete': { - CustomEditor.deleteNode(editor, node); - break; - } - - case 'duplicate': { - const path = ReactEditor.findPath(editor, node); - const newNode = CustomEditor.duplicateNode(editor, node); - - handleClose(); - - const newBlockId = newNode.blockId; - - if (!newBlockId) return; - requestAnimationFrame(() => { - selectedBlockContext.clear(); - selectedBlockContext.add(newBlockId); - const nextPath = Path.next(path); - - editor.select(nextPath); - editor.collapse({ - edge: 'start', - }); - }); - return; - } - - case 'color': { - setOpenColorMenu(true); - return; - } - } - - handleClose(); - }, - [editor, handleClose, node, selectedBlockContext] - ); - - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - const options: KeyboardNavigationOption[] = useMemo( - () => - [ - { - key: 'block-operation', - children: [ - { - key: 'delete', - content: ( -
- -
{t('button.delete')}
-
{'Del'}
-
- ), - }, - { - key: 'duplicate', - content: ( -
- -
{t('button.duplicate')}
-
{`${getModifier()} + D`}
-
- ), - }, - ], - }, - canSetColor && { - key: 'color', - content: , - children: [ - { - key: 'color', - content: ( - { - setOpenColorMenu(false); - }} - openPicker={openColorMenu} - onOpenPicker={() => setOpenColorMenu(true)} - /> - ), - }, - ], - }, - ].filter(Boolean), - [node, canSetColor, openColorMenu, t] - ); - - const handleKeyDown = useCallback( - (e: KeyboardEvent) => { - e.stopPropagation(); - if (isHotkey('mod+d', e)) { - e.preventDefault(); - onConfirm('duplicate'); - } - - if (isHotkey('del', e) || isHotkey('backspace', e)) { - e.preventDefault(); - onConfirm('delete'); - } - }, - [onConfirm] - ); - - return ( - e.stopPropagation()} - {...props} - onClose={handleClose} - > -
- { - if (key === 'color') { - onConfirm(key); - } else { - handleClose(); - } - }} - options={options} - scrollRef={ref} - onEscape={handleClose} - onConfirm={onConfirm} - /> -
-
- ); -} - -export default BlockOperationMenu; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/block_actions/color/Color.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/block_actions/color/Color.tsx deleted file mode 100644 index 499ab95c76..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/block_actions/color/Color.tsx +++ /dev/null @@ -1,90 +0,0 @@ -import React, { useCallback, useRef } from 'react'; -import { Element } from 'slate'; -import { Popover } from '@mui/material'; -import ColorLensOutlinedIcon from '@mui/icons-material/ColorLensOutlined'; -import { useTranslation } from 'react-i18next'; -import { ColorPicker } from '$app/components/editor/components/tools/_shared'; -import { CustomEditor } from '$app/components/editor/command'; -import { ReactComponent as MoreSvg } from '$app/assets/more.svg'; -import { useSlateStatic } from 'slate-react'; -import { PopoverOrigin } from '@mui/material/Popover/Popover'; - -const initialOrigin: { - transformOrigin?: PopoverOrigin; - anchorOrigin?: PopoverOrigin; -} = { - anchorOrigin: { - vertical: 'center', - horizontal: 'right', - }, - transformOrigin: { - vertical: 'center', - horizontal: 'left', - }, -}; - -export function Color({ - node, - openPicker, - onOpenPicker, - onClosePicker, -}: { - node: Element & { - data?: { - font_color?: string; - bg_color?: string; - }; - }; - openPicker?: boolean; - onOpenPicker?: () => void; - onClosePicker?: () => void; -}) { - const { t } = useTranslation(); - - const editor = useSlateStatic(); - - const ref = useRef(null); - - const onColorChange = useCallback( - (format: 'font_color' | 'bg_color', color: string) => { - CustomEditor.setBlockColor(editor, node, { - [format]: color, - }); - onClosePicker?.(); - }, - [editor, node, onClosePicker] - ); - - return ( - <> -
- -
{t('editor.color')}
- -
- {openPicker && ( - { - e.stopPropagation(); - e.nativeEvent.stopImmediatePropagation(); - if (e.key === 'Escape' || e.key === 'ArrowLeft') { - e.preventDefault(); - onClosePicker?.(); - } - }} - onClick={(e) => e.stopPropagation()} - anchorEl={ref.current} - onClose={onClosePicker} - > -
- -
-
- )} - - ); -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/block_actions/color/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/block_actions/color/index.ts deleted file mode 100644 index 0fd619bc86..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/block_actions/color/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './Color'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/block_actions/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/block_actions/index.ts deleted file mode 100644 index e8b87721c2..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/block_actions/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './BlockActions'; -export * from './BlockActionsToolbar'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/block_actions/utils.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/block_actions/utils.ts deleted file mode 100644 index b63afe9dc1..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/block_actions/utils.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { ReactEditor } from 'slate-react'; -import { getEditorDomNode, getHeadingCssProperty } from '$app/components/editor/plugins/utils'; -import { Element } from 'slate'; -import { EditorNodeType, HeadingNode } from '$app/application/document/document.types'; - -export function getBlockActionsPosition(editor: ReactEditor, blockElement: HTMLElement) { - const editorDom = getEditorDomNode(editor); - const editorDomRect = editorDom.getBoundingClientRect(); - const blockDomRect = blockElement.getBoundingClientRect(); - - const relativeTop = blockDomRect.top - editorDomRect.top; - const relativeLeft = blockDomRect.left - editorDomRect.left; - - return { - top: relativeTop, - left: relativeLeft, - }; -} - -export function getBlockCssProperty(node: Element) { - switch (node.type) { - case EditorNodeType.HeadingBlock: - return `${getHeadingCssProperty((node as HeadingNode).data.level)} mt-1`; - case EditorNodeType.CodeBlock: - case EditorNodeType.CalloutBlock: - case EditorNodeType.EquationBlock: - case EditorNodeType.GridBlock: - return 'my-3'; - case EditorNodeType.DividerBlock: - return 'my-0'; - default: - return 'mt-1'; - } -} - -/** - * @param editor - * @param e - */ -export function findEventNode( - editor: ReactEditor, - { - x, - y, - }: { - x: number; - y: number; - } -) { - const element = document.elementFromPoint(x, y); - const nodeDom = element?.closest('[data-block-type]'); - - if (nodeDom) { - return ReactEditor.toSlateNode(editor, nodeDom) as Element; - } - - return null; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/command_panel/Command.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/command_panel/Command.hooks.ts deleted file mode 100644 index 633d09349d..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/command_panel/Command.hooks.ts +++ /dev/null @@ -1,259 +0,0 @@ -import { useEffect, useState, useCallback, useRef } from 'react'; -import { getPanelPosition } from '$app/components/editor/components/tools/command_panel/utils'; -import { useSlate } from 'slate-react'; -import { PopoverPreventBlurProps } from '$app/components/editor/components/tools/popover'; -import { PopoverProps } from '@mui/material/Popover'; - -import { Editor, Point, Range, Transforms } from 'slate'; -import { CustomEditor } from '$app/components/editor/command'; -import { PopoverOrigin } from '@mui/material/Popover/Popover'; -import { useSlashState } from '$app/components/editor/stores'; - -export enum EditorCommand { - Mention = '@', - SlashCommand = '/', -} - -export const PanelPopoverProps: Partial = { - ...PopoverPreventBlurProps, - anchorReference: 'anchorPosition', -}; - -const commands = Object.values(EditorCommand); - -export interface PanelProps { - anchorPosition?: { left: number; top: number; height: number }; - closePanel: (deleteText?: boolean) => void; - searchText: string; - openPanel: () => void; -} - -export function useCommandPanel() { - const editor = useSlate(); - const { open: slashOpen, setOpen: setSlashOpen } = useSlashState(); - const [command, setCommand] = useState(undefined); - const [anchorPosition, setAnchorPosition] = useState< - | { - top: number; - left: number; - height: number; - } - | undefined - >(undefined); - const startPoint = useRef(); - const endPoint = useRef(); - const open = Boolean(anchorPosition); - const [searchText, setSearchText] = useState(''); - - const closePanel = useCallback( - (deleteText?: boolean) => { - if (deleteText && startPoint.current && endPoint.current) { - const anchor = { - path: startPoint.current.path, - offset: startPoint.current.offset > 0 ? startPoint.current.offset - 1 : 0, - }; - const focus = { - path: endPoint.current.path, - offset: endPoint.current.offset, - }; - - if (!Point.equals(anchor, focus)) { - Transforms.delete(editor, { - at: { - anchor, - focus, - }, - }); - } - } - - setSlashOpen(false); - setCommand(undefined); - setAnchorPosition(undefined); - setSearchText(''); - }, - [editor, setSlashOpen] - ); - - const setPosition = useCallback( - (position?: { left: number; top: number; height: number }) => { - if (!position) { - closePanel(false); - return; - } - - const nodeEntry = CustomEditor.getBlock(editor); - - if (!nodeEntry) return; - - setAnchorPosition(position); - }, - [closePanel, editor] - ); - - const openPanel = useCallback(() => { - const position = getPanelPosition(editor); - - if (position && editor.selection) { - startPoint.current = Editor.start(editor, editor.selection); - endPoint.current = Editor.end(editor, editor.selection); - setPosition(position); - } else { - setPosition(undefined); - } - }, [editor, setPosition]); - - useEffect(() => { - if (!slashOpen && command === EditorCommand.SlashCommand) { - closePanel(); - return; - } - - if (slashOpen && !open) { - setCommand(EditorCommand.SlashCommand); - openPanel(); - return; - } - }, [slashOpen, closePanel, command, open, openPanel]); - /** - * listen to editor insertText and deleteBackward event - */ - useEffect(() => { - const { insertText } = editor; - - /** - * insertText: when insert char at after space or at start of element, show the panel - * open condition: - * 1. open is false - * 2. current block is not code block - * 3. current selection is not include root - * 4. current selection is collapsed - * 5. insert char is command char - * 6. before text is empty or end with space - * --------- start ----------------- - * | - selection point - * @ - panel char - * _ - space - * - - other text - * -------- open panel ---------------- - * ---_@|--- => insert text is panel char and before text is end with space, open the panel - * @|--- => insert text is panel char and before text is empty, open the panel - */ - editor.insertText = (text, opts) => { - if (open || CustomEditor.isCodeBlock(editor) || CustomEditor.selectionIncludeRoot(editor)) { - insertText(text, opts); - return; - } - - const { selection } = editor; - - const command = commands.find((c) => text.endsWith(c)); - const endOfPanelChar = !!command; - - if (command === EditorCommand.SlashCommand) { - setSlashOpen(true); - } - - setCommand(command); - if (!selection || !endOfPanelChar || !Range.isCollapsed(selection)) { - insertText(text, opts); - return; - } - - const block = CustomEditor.getBlock(editor); - const path = block ? block[1] : []; - const { anchor } = selection; - const beforeText = Editor.string(editor, { anchor, focus: Editor.start(editor, path) }) + text.slice(0, -1); - // show the panel when insert char at after space or at start of element - const showPanel = !beforeText || beforeText.endsWith(' '); - - insertText(text, opts); - - if (!showPanel) return; - openPanel(); - }; - - return () => { - editor.insertText = insertText; - }; - }, [open, editor, openPanel, setSlashOpen]); - - /** - * listen to editor onChange event - */ - useEffect(() => { - const { onChange } = editor; - - if (!open) return; - - /** - * onChange: when selection change, update the search text or close the panel - * --------- start ----------------- - * | - selection point - * @ - panel char - * __ - search text - * - - other text - * -------- close panel ---------------- - * --|@--- => selection is backward to start point, close the panel - * ---@__-|--- => selection is forward to end point, close the panel - * -------- update search text ---------------- - * ---@__|--- - * ---@_|_--- => selection is forward to start point and backward to end point, update the search text - * ---@|__--- - * --------- end ----------------- - */ - editor.onChange = (...args) => { - if (!editor.selection || !startPoint.current || !endPoint.current) return; - onChange(...args); - const isSelectionChange = editor.operations.every((op) => op.type === 'set_selection'); - const currentPoint = Editor.end(editor, editor.selection); - const isBackward = currentPoint.offset < startPoint.current.offset; - - if (isBackward) { - closePanel(false); - return; - } - - if (!isSelectionChange) { - if (currentPoint.offset > endPoint.current?.offset) { - endPoint.current = currentPoint; - } - - const text = Editor.string(editor, { - anchor: startPoint.current, - focus: endPoint.current, - }); - - setSearchText(text); - } else { - const isForward = currentPoint.offset > endPoint.current.offset; - - if (isForward) { - closePanel(false); - } - } - }; - - return () => { - editor.onChange = onChange; - }; - }, [open, editor, closePanel]); - - return { - anchorPosition, - closePanel, - searchText, - openPanel, - command, - }; -} - -export const initialTransformOrigin: PopoverOrigin = { - vertical: 'top', - horizontal: 'left', -}; - -export const initialAnchorOrigin: PopoverOrigin = { - vertical: 'bottom', - horizontal: 'right', -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/command_panel/CommandPanel.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/command_panel/CommandPanel.tsx deleted file mode 100644 index db58e2deca..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/command_panel/CommandPanel.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import React from 'react'; -import { SlashCommandPanel } from '$app/components/editor/components/tools/command_panel/slash_command_panel'; -import { MentionPanel } from '$app/components/editor/components/tools/command_panel/mention_panel'; -import { EditorCommand, useCommandPanel } from '$app/components/editor/components/tools/command_panel/Command.hooks'; -import withErrorBoundary from '$app/components/_shared/error_boundary/withError'; - -function CommandPanel() { - const { anchorPosition, searchText, openPanel, closePanel, command } = useCommandPanel(); - - const Component = command === EditorCommand.SlashCommand ? SlashCommandPanel : MentionPanel; - - return ( - - ); -} - -export default withErrorBoundary(CommandPanel); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/command_panel/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/command_panel/index.ts deleted file mode 100644 index cf07c7d996..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/command_panel/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './mention_panel'; -export * from './slash_command_panel'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/command_panel/mention_panel/MentionPanel.hooks.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/command_panel/mention_panel/MentionPanel.hooks.tsx deleted file mode 100644 index 5d83870719..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/command_panel/mention_panel/MentionPanel.hooks.tsx +++ /dev/null @@ -1,114 +0,0 @@ -import React, { useCallback, useMemo } from 'react'; -import { useTranslation } from 'react-i18next'; -import { useSlate } from 'slate-react'; -import { MentionPage, MentionType } from '$app/application/document/document.types'; -import { CustomEditor } from '$app/components/editor/command'; -import { KeyboardNavigationOption } from '$app/components/_shared/keyboard_navigation/KeyboardNavigation'; -import { ReactComponent as DocumentSvg } from '$app/assets/document.svg'; -// import dayjs from 'dayjs'; - -// enum DateKey { -// Today = 'today', -// Tomorrow = 'tomorrow', -// } -export function useMentionPanel({ - closePanel, - pages, -}: { - pages: MentionPage[]; - closePanel: (deleteText?: boolean) => void; -}) { - const { t } = useTranslation(); - const editor = useSlate(); - - const onConfirm = useCallback( - (key: string) => { - const [, id] = key.split(','); - - closePanel(true); - CustomEditor.insertMention(editor, { - page_id: id, - type: MentionType.PageRef, - }); - }, - [closePanel, editor] - ); - - const renderPage = useCallback( - (page: MentionPage) => { - return { - key: `${MentionType.PageRef},${page.id}`, - content: ( -
-
{page.icon?.value || }
- -
{page.name.trim() || t('menuAppHeader.defaultNewPageName')}
-
- ), - }; - }, - [t] - ); - - // const renderDate = useCallback(() => { - // return [ - // { - // key: DateKey.Today, - // content: ( - //
- // {t('relativeDates.today')} -{' '} - // {dayjs().format('MMM D, YYYY')} - //
- // ), - // - // children: [], - // }, - // { - // key: DateKey.Tomorrow, - // content: ( - //
- // {t('relativeDates.tomorrow')} - //
- // ), - // children: [], - // }, - // ]; - // }, [t]); - - const options: KeyboardNavigationOption[] = useMemo(() => { - return [ - // { - // key: MentionType.Date, - // content:
{t('editor.date')}
, - // children: renderDate(), - // }, - { - key: 'divider', - content:
, - children: [], - }, - - { - key: MentionType.PageRef, - content:
{t('document.mention.page.label')}
, - children: - pages.length > 0 - ? pages.map(renderPage) - : [ - { - key: 'noPage', - content: ( -
{t('findAndReplace.noResult')}
- ), - children: [], - }, - ], - }, - ]; - }, [pages, renderPage, t]); - - return { - options, - onConfirm, - }; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/command_panel/mention_panel/MentionPanel.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/command_panel/mention_panel/MentionPanel.tsx deleted file mode 100644 index 6ca0225579..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/command_panel/mention_panel/MentionPanel.tsx +++ /dev/null @@ -1,81 +0,0 @@ -import React, { useCallback, useEffect, useRef, useState } from 'react'; -import { - initialAnchorOrigin, - initialTransformOrigin, - PanelPopoverProps, - PanelProps, -} from '$app/components/editor/components/tools/command_panel/Command.hooks'; -import Popover from '@mui/material/Popover'; - -import MentionPanelContent from '$app/components/editor/components/tools/command_panel/mention_panel/MentionPanelContent'; -import usePopoverAutoPosition from '$app/components/_shared/popover/Popover.hooks'; -import { useAppSelector } from '$app/stores/store'; -import { MentionPage } from '$app/application/document/document.types'; - -export function MentionPanel({ anchorPosition, closePanel, searchText }: PanelProps) { - const ref = useRef(null); - const pagesMap = useAppSelector((state) => state.pages.pageMap); - - const pagesRef = useRef([]); - const [recentPages, setPages] = useState([]); - - const loadPages = useCallback(async () => { - const pages = Object.values(pagesMap); - - pagesRef.current = pages; - setPages(pages); - }, [pagesMap]); - - useEffect(() => { - void loadPages(); - }, [loadPages]); - - useEffect(() => { - if (!searchText) { - setPages(pagesRef.current); - return; - } - - const filteredPages = pagesRef.current.filter((page) => { - return page.name.toLowerCase().includes(searchText.toLowerCase()); - }); - - setPages(filteredPages); - }, [searchText]); - const open = Boolean(anchorPosition); - - const { - paperHeight, - anchorPosition: newAnchorPosition, - paperWidth, - transformOrigin, - anchorOrigin, - isEntered, - } = usePopoverAutoPosition({ - initialPaperWidth: 300, - initialPaperHeight: 360, - anchorPosition, - initialTransformOrigin, - initialAnchorOrigin, - open, - }); - - return ( -
- {open && ( - closePanel(false)} - > - - - )} -
- ); -} - -export default MentionPanel; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/command_panel/mention_panel/MentionPanelContent.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/command_panel/mention_panel/MentionPanelContent.tsx deleted file mode 100644 index 36b00ca2b6..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/command_panel/mention_panel/MentionPanelContent.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import React, { useRef } from 'react'; -import { useMentionPanel } from '$app/components/editor/components/tools/command_panel/mention_panel/MentionPanel.hooks'; - -import KeyboardNavigation from '$app/components/_shared/keyboard_navigation/KeyboardNavigation'; -import { MentionPage } from '$app/application/document/document.types'; - -function MentionPanelContent({ - closePanel, - pages, - maxHeight, - width, -}: { - closePanel: (deleteText?: boolean) => void; - pages: MentionPage[]; - maxHeight: number; - width: number; -}) { - const scrollRef = useRef(null); - - const { options, onConfirm } = useMentionPanel({ - closePanel, - pages, - }); - - return ( -
- -
- ); -} - -export default MentionPanelContent; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/command_panel/mention_panel/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/command_panel/mention_panel/index.ts deleted file mode 100644 index bfca34ef9a..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/command_panel/mention_panel/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './MentionPanel'; -export * from './MentionPanelContent'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/command_panel/slash_command_panel/SlashCommandPanel.hooks.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/command_panel/slash_command_panel/SlashCommandPanel.hooks.tsx deleted file mode 100644 index c2d9445b56..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/command_panel/slash_command_panel/SlashCommandPanel.hooks.tsx +++ /dev/null @@ -1,245 +0,0 @@ -import { EditorNodeType } from '$app/application/document/document.types'; -import { useCallback, useMemo } from 'react'; -import { useTranslation } from 'react-i18next'; -import { ReactEditor, useSlate } from 'slate-react'; -import { Path } from 'slate'; -import { getBlock } from '$app/components/editor/plugins/utils'; -import { ReactComponent as TextIcon } from '$app/assets/text.svg'; -import { ReactComponent as TodoListIcon } from '$app/assets/todo-list.svg'; -import { ReactComponent as Heading1Icon } from '$app/assets/h1.svg'; -import { ReactComponent as Heading2Icon } from '$app/assets/h2.svg'; -import { ReactComponent as Heading3Icon } from '$app/assets/h3.svg'; -import { ReactComponent as BulletedListIcon } from '$app/assets/list.svg'; -import { ReactComponent as NumberedListIcon } from '$app/assets/numbers.svg'; -import { ReactComponent as QuoteIcon } from '$app/assets/quote.svg'; -import { ReactComponent as ToggleListIcon } from '$app/assets/show-menu.svg'; -import { ReactComponent as GridIcon } from '$app/assets/grid.svg'; -import { ReactComponent as ImageIcon } from '$app/assets/image.svg'; -import { DataObjectOutlined, FunctionsOutlined, HorizontalRuleOutlined, MenuBookOutlined } from '@mui/icons-material'; -import { CustomEditor } from '$app/components/editor/command'; -import { KeyboardNavigationOption } from '$app/components/_shared/keyboard_navigation/KeyboardNavigation'; -import { YjsEditor } from '@slate-yjs/core'; -import { useEditorBlockDispatch } from '$app/components/editor/stores/block'; -import { - headingTypes, - headingTypeToLevelMap, - reorderSlashOptions, - SlashAliases, - SlashCommandPanelTab, - slashOptionGroup, - slashOptionMapToEditorNodeType, - SlashOptionType, -} from '$app/components/editor/components/tools/command_panel/slash_command_panel/const'; - -export function useSlashCommandPanel({ - searchText, - closePanel, -}: { - searchText: string; - closePanel: (deleteText?: boolean) => void; -}) { - const { openPopover } = useEditorBlockDispatch(); - const { t } = useTranslation(); - const editor = useSlate(); - const onConfirm = useCallback( - (type: SlashOptionType) => { - const node = getBlock(editor); - - if (!node) return; - - const nodeType = slashOptionMapToEditorNodeType[type]; - - if (!nodeType) return; - - const data = {}; - - if (headingTypes.includes(type)) { - Object.assign(data, { - level: headingTypeToLevelMap[type], - }); - } - - if (nodeType === EditorNodeType.CalloutBlock) { - Object.assign(data, { - icon: '📌', - }); - } - - if (nodeType === EditorNodeType.CodeBlock) { - Object.assign(data, { - language: 'json', - }); - } - - if (nodeType === EditorNodeType.ImageBlock) { - Object.assign(data, { - url: '', - }); - } - - closePanel(true); - - const newNode = getBlock(editor); - const block = CustomEditor.getBlock(editor); - - const path = block ? block[1] : null; - - if (!newNode || !path) return; - - const isEmpty = CustomEditor.isEmptyText(editor, newNode); - - if (!isEmpty) { - const nextPath = Path.next(path); - - CustomEditor.insertEmptyLine(editor as ReactEditor & YjsEditor, nextPath); - editor.select(nextPath); - } - - const turnIntoBlock = CustomEditor.turnToBlock(editor, { - type: nodeType, - data, - }); - - setTimeout(() => { - if (turnIntoBlock && turnIntoBlock.blockId) { - if (turnIntoBlock.type === EditorNodeType.ImageBlock || turnIntoBlock.type === EditorNodeType.EquationBlock) { - openPopover(turnIntoBlock.type, turnIntoBlock.blockId); - } - } - }, 0); - }, - [editor, closePanel, openPopover] - ); - - const typeToLabelIconMap = useMemo(() => { - return { - [SlashOptionType.Paragraph]: { - label: t('editor.text'), - Icon: TextIcon, - }, - [SlashOptionType.TodoList]: { - label: t('editor.checkbox'), - Icon: TodoListIcon, - }, - [SlashOptionType.Heading1]: { - label: t('editor.heading1'), - Icon: Heading1Icon, - }, - [SlashOptionType.Heading2]: { - label: t('editor.heading2'), - Icon: Heading2Icon, - }, - [SlashOptionType.Heading3]: { - label: t('editor.heading3'), - Icon: Heading3Icon, - }, - [SlashOptionType.BulletedList]: { - label: t('editor.bulletedList'), - Icon: BulletedListIcon, - }, - [SlashOptionType.NumberedList]: { - label: t('editor.numberedList'), - Icon: NumberedListIcon, - }, - [SlashOptionType.Quote]: { - label: t('editor.quote'), - Icon: QuoteIcon, - }, - [SlashOptionType.ToggleList]: { - label: t('document.plugins.toggleList'), - Icon: ToggleListIcon, - }, - [SlashOptionType.Divider]: { - label: t('editor.divider'), - Icon: HorizontalRuleOutlined, - }, - [SlashOptionType.Callout]: { - label: t('document.plugins.callout'), - Icon: MenuBookOutlined, - }, - [SlashOptionType.Code]: { - label: t('document.selectionMenu.codeBlock'), - Icon: DataObjectOutlined, - }, - [SlashOptionType.Grid]: { - label: t('grid.menuName'), - Icon: GridIcon, - }, - - [SlashOptionType.MathEquation]: { - label: t('document.plugins.mathEquation.name'), - Icon: FunctionsOutlined, - }, - [SlashOptionType.Image]: { - label: t('editor.image'), - Icon: ImageIcon, - }, - }; - }, [t]); - - const groupTypeToLabelMap = useMemo(() => { - return { - [SlashCommandPanelTab.BASIC]: 'Basic', - [SlashCommandPanelTab.ADVANCED]: 'Advanced', - [SlashCommandPanelTab.MEDIA]: 'Media', - [SlashCommandPanelTab.DATABASE]: 'Database', - }; - }, []); - - const renderOptionContent = useCallback( - (type: SlashOptionType) => { - const Icon = typeToLabelIconMap[type].Icon; - - return ( -
-
- -
- -
{typeToLabelIconMap[type].label}
-
- ); - }, - [typeToLabelIconMap] - ); - - const options: KeyboardNavigationOption[] = useMemo(() => { - return slashOptionGroup - .map((group) => { - return { - key: group.key, - content:
{groupTypeToLabelMap[group.key]}
, - children: group.options - - .map((type) => { - return { - key: type, - content: renderOptionContent(type), - }; - }) - .filter((option) => { - if (!searchText) return true; - const label = typeToLabelIconMap[option.key].label; - - let newSearchText = searchText; - - if (searchText.startsWith('/')) { - newSearchText = searchText.slice(1); - } - - return ( - label.toLowerCase().includes(newSearchText.toLowerCase()) || - SlashAliases[option.key].some((alias) => alias.startsWith(newSearchText.toLowerCase())) - ); - }) - .sort(reorderSlashOptions(searchText)), - }; - }) - .filter((group) => group.children.length > 0); - }, [searchText, groupTypeToLabelMap, typeToLabelIconMap, renderOptionContent]); - - return { - options, - onConfirm, - }; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/command_panel/slash_command_panel/SlashCommandPanel.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/command_panel/slash_command_panel/SlashCommandPanel.tsx deleted file mode 100644 index b09af97b39..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/command_panel/slash_command_panel/SlashCommandPanel.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import React, { useCallback, useRef } from 'react'; -import { - initialAnchorOrigin, - initialTransformOrigin, - PanelPopoverProps, - PanelProps, -} from '$app/components/editor/components/tools/command_panel/Command.hooks'; -import Popover from '@mui/material/Popover'; -import SlashCommandPanelContent from '$app/components/editor/components/tools/command_panel/slash_command_panel/SlashCommandPanelContent'; -import { useSlate } from 'slate-react'; -import usePopoverAutoPosition from '$app/components/_shared/popover/Popover.hooks'; - -export function SlashCommandPanel({ anchorPosition, closePanel, searchText }: PanelProps) { - const ref = useRef(null); - const editor = useSlate(); - - const open = Boolean(anchorPosition); - - const handleClose = useCallback( - (deleteText?: boolean) => { - closePanel(deleteText); - }, - [closePanel] - ); - - const { - paperHeight, - paperWidth, - anchorPosition: newAnchorPosition, - transformOrigin, - anchorOrigin, - isEntered, - } = usePopoverAutoPosition({ - initialPaperWidth: 220, - initialPaperHeight: 360, - anchorPosition, - initialTransformOrigin, - initialAnchorOrigin, - open, - }); - - return ( -
- {open && ( - { - const selection = editor.selection; - - handleClose(false); - - if (selection) { - editor.select(selection); - } - }} - > - - - )} -
- ); -} - -export default SlashCommandPanel; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/command_panel/slash_command_panel/SlashCommandPanelContent.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/command_panel/slash_command_panel/SlashCommandPanelContent.tsx deleted file mode 100644 index 256e82f811..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/command_panel/slash_command_panel/SlashCommandPanelContent.tsx +++ /dev/null @@ -1,89 +0,0 @@ -import React, { useEffect, useRef } from 'react'; -import KeyboardNavigation from '$app/components/_shared/keyboard_navigation/KeyboardNavigation'; -import { useSlashCommandPanel } from '$app/components/editor/components/tools/command_panel/slash_command_panel/SlashCommandPanel.hooks'; -import { useSlateStatic } from 'slate-react'; -import { SlashOptionType } from '$app/components/editor/components/tools/command_panel/slash_command_panel/const'; - -const noResultBuffer = 2; - -function SlashCommandPanelContent({ - closePanel, - searchText, - maxHeight, - width, -}: { - closePanel: (deleteText?: boolean) => void; - searchText: string; - maxHeight: number; - width: number; -}) { - const scrollRef = useRef(null); - - const { options, onConfirm } = useSlashCommandPanel({ - closePanel, - searchText, - }); - - // Used to keep track of how many times the user has typed and not found any result - const noResultCount = useRef(0); - - const editor = useSlateStatic(); - - useEffect(() => { - const { insertText, deleteBackward } = editor; - - editor.insertText = (text, opts) => { - // close panel if track of no result is greater than buffer - if (noResultCount.current >= noResultBuffer) { - closePanel(false); - } - - if (options.length === 0) { - noResultCount.current += 1; - } - - insertText(text, opts); - }; - - editor.deleteBackward = (unit) => { - // reset no result count - if (noResultCount.current > 0) { - noResultCount.current -= 1; - } - - // close panel if no text - if (!searchText) { - closePanel(true); - return; - } - - deleteBackward(unit); - }; - - return () => { - editor.insertText = insertText; - editor.deleteBackward = deleteBackward; - }; - }, [closePanel, editor, searchText, options.length]); - - return ( -
- onConfirm(key as SlashOptionType)} - options={options} - disableFocus={true} - /> -
- ); -} - -export default SlashCommandPanelContent; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/command_panel/slash_command_panel/const.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/command_panel/slash_command_panel/const.ts deleted file mode 100644 index 7dfaa2b4a0..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/command_panel/slash_command_panel/const.ts +++ /dev/null @@ -1,174 +0,0 @@ -import { EditorNodeType } from '$app/application/document/document.types'; - -export enum SlashCommandPanelTab { - BASIC = 'basic', - MEDIA = 'media', - DATABASE = 'database', - ADVANCED = 'advanced', -} - -export enum SlashOptionType { - Paragraph, - TodoList, - Heading1, - Heading2, - Heading3, - BulletedList, - NumberedList, - Quote, - ToggleList, - Divider, - Callout, - Code, - Grid, - MathEquation, - Image, -} - -export const slashOptionGroup = [ - { - key: SlashCommandPanelTab.BASIC, - options: [ - SlashOptionType.Paragraph, - SlashOptionType.TodoList, - SlashOptionType.Heading1, - SlashOptionType.Heading2, - SlashOptionType.Heading3, - SlashOptionType.BulletedList, - SlashOptionType.NumberedList, - SlashOptionType.Quote, - SlashOptionType.ToggleList, - SlashOptionType.Divider, - SlashOptionType.Callout, - ], - }, - { - key: SlashCommandPanelTab.MEDIA, - options: [SlashOptionType.Code, SlashOptionType.Image], - }, - { - key: SlashCommandPanelTab.DATABASE, - options: [SlashOptionType.Grid], - }, - { - key: SlashCommandPanelTab.ADVANCED, - options: [SlashOptionType.MathEquation], - }, -]; -export const slashOptionMapToEditorNodeType = { - [SlashOptionType.Paragraph]: EditorNodeType.Paragraph, - [SlashOptionType.TodoList]: EditorNodeType.TodoListBlock, - [SlashOptionType.Heading1]: EditorNodeType.HeadingBlock, - [SlashOptionType.Heading2]: EditorNodeType.HeadingBlock, - [SlashOptionType.Heading3]: EditorNodeType.HeadingBlock, - [SlashOptionType.BulletedList]: EditorNodeType.BulletedListBlock, - [SlashOptionType.NumberedList]: EditorNodeType.NumberedListBlock, - [SlashOptionType.Quote]: EditorNodeType.QuoteBlock, - [SlashOptionType.ToggleList]: EditorNodeType.ToggleListBlock, - [SlashOptionType.Divider]: EditorNodeType.DividerBlock, - [SlashOptionType.Callout]: EditorNodeType.CalloutBlock, - [SlashOptionType.Code]: EditorNodeType.CodeBlock, - [SlashOptionType.Grid]: EditorNodeType.GridBlock, - [SlashOptionType.MathEquation]: EditorNodeType.EquationBlock, - [SlashOptionType.Image]: EditorNodeType.ImageBlock, -}; -export const headingTypeToLevelMap: Record = { - [SlashOptionType.Heading1]: 1, - [SlashOptionType.Heading2]: 2, - [SlashOptionType.Heading3]: 3, -}; -export const headingTypes = [SlashOptionType.Heading1, SlashOptionType.Heading2, SlashOptionType.Heading3]; - -export const SlashAliases = { - [SlashOptionType.Paragraph]: ['paragraph', 'text', 'block', 'textblock'], - [SlashOptionType.TodoList]: [ - 'list', - 'todo', - 'todolist', - 'checkbox', - 'block', - 'todoblock', - 'checkboxblock', - 'todolistblock', - ], - [SlashOptionType.Heading1]: ['h1', 'heading1', 'block', 'headingblock', 'h1block'], - [SlashOptionType.Heading2]: ['h2', 'heading2', 'block', 'headingblock', 'h2block'], - [SlashOptionType.Heading3]: ['h3', 'heading3', 'block', 'headingblock', 'h3block'], - [SlashOptionType.BulletedList]: [ - 'list', - 'bulleted', - 'block', - 'bulletedlist', - 'bulletedblock', - 'listblock', - 'bulletedlistblock', - 'bulletelist', - ], - [SlashOptionType.NumberedList]: [ - 'list', - 'numbered', - 'block', - 'numberedlist', - 'numberedblock', - 'listblock', - 'numberedlistblock', - 'numberlist', - ], - [SlashOptionType.Quote]: ['quote', 'block', 'quoteblock'], - [SlashOptionType.ToggleList]: ['list', 'toggle', 'block', 'togglelist', 'toggleblock', 'listblock', 'togglelistblock'], - [SlashOptionType.Divider]: ['divider', 'hr', 'block', 'dividerblock', 'line', 'lineblock'], - [SlashOptionType.Callout]: ['callout', 'info', 'block', 'calloutblock'], - [SlashOptionType.Code]: ['code', 'code', 'block', 'codeblock', 'media'], - [SlashOptionType.Grid]: ['grid', 'table', 'block', 'gridblock', 'database'], - [SlashOptionType.MathEquation]: [ - 'math', - 'equation', - 'block', - 'mathblock', - 'mathequation', - 'mathequationblock', - 'advanced', - ], - [SlashOptionType.Image]: ['img', 'image', 'block', 'imageblock', 'media'], -}; - -export const reorderSlashOptions = (searchText: string) => { - return ( - a: { - key: SlashOptionType; - }, - b: { - key: SlashOptionType; - } - ) => { - const compareIndex = (option: SlashOptionType) => { - const aliases = SlashAliases[option]; - - if (aliases) { - for (const alias of aliases) { - if (alias.startsWith(searchText)) { - return -1; - } - } - } - - return 0; - }; - - const compareLength = (option: SlashOptionType) => { - const aliases = SlashAliases[option]; - - if (aliases) { - for (const alias of aliases) { - if (alias.length < searchText.length) { - return -1; - } - } - } - - return 0; - }; - - return compareIndex(a.key) - compareIndex(b.key) || compareLength(a.key) - compareLength(b.key); - }; -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/command_panel/slash_command_panel/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/command_panel/slash_command_panel/index.ts deleted file mode 100644 index 688a6ffb7d..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/command_panel/slash_command_panel/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './SlashCommandPanel'; -export * from './SlashCommandPanelContent'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/command_panel/utils.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/command_panel/utils.ts deleted file mode 100644 index 65a095dc58..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/command_panel/utils.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { ReactEditor } from 'slate-react'; - -export function getPanelPosition(editor: ReactEditor) { - const { selection } = editor; - - const isFocused = ReactEditor.isFocused(editor); - - if (!selection || !isFocused) { - return null; - } - - const domSelection = window.getSelection(); - const rangeCount = domSelection?.rangeCount; - - if (!rangeCount) return null; - - const domRange = rangeCount > 0 ? domSelection.getRangeAt(0) : undefined; - - const rect = domRange?.getBoundingClientRect(); - - if (!rect) return null; - const nodeDom = domSelection.anchorNode?.parentElement?.closest('.text-element'); - const height = (nodeDom?.getBoundingClientRect().height ?? 0) + 8; - - return { - ...rect, - height, - top: rect.top, - left: rect.left, - }; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/popover.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/popover.ts deleted file mode 100644 index 2b6a715baa..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/popover.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { PopoverProps } from '@mui/material/Popover'; - -export const PopoverCommonProps: Partial = { - keepMounted: false, - disableAutoFocus: true, - disableEnforceFocus: true, - disableRestoreFocus: true, -}; - -export const PopoverPreventBlurProps: Partial = { - ...PopoverCommonProps, - - onMouseDown: (e) => { - // prevent editor blur - e.preventDefault(); - e.stopPropagation(); - }, -}; - -export const PopoverNoBackdropProps: Partial = { - ...PopoverCommonProps, - sx: { - pointerEvents: 'none', - }, - PaperProps: { - style: { - pointerEvents: 'auto', - }, - }, - onMouseDown: (e) => { - // prevent editor blur - e.stopPropagation(); - }, -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/SelectionActions.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/SelectionActions.tsx deleted file mode 100644 index 9d7c19b999..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/SelectionActions.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import React from 'react'; - -import { Paragraph } from '$app/components/editor/components/tools/selection_toolbar/actions/paragraph'; -import { Heading } from '$app/components/editor/components/tools/selection_toolbar/actions/heading'; -import { Divider } from '@mui/material'; -import { Bold } from '$app/components/editor/components/tools/selection_toolbar/actions/bold'; -import { Italic } from '$app/components/editor/components/tools/selection_toolbar/actions/italic'; -import { Underline } from '$app/components/editor/components/tools/selection_toolbar/actions/underline'; -import { StrikeThrough } from '$app/components/editor/components/tools/selection_toolbar/actions/strikethrough'; -import { InlineCode } from '$app/components/editor/components/tools/selection_toolbar/actions/inline_code'; -import { Formula } from '$app/components/editor/components/tools/selection_toolbar/actions/formula'; -import { TodoList } from '$app/components/editor/components/tools/selection_toolbar/actions/todo_list'; -import { Quote } from '$app/components/editor/components/tools/selection_toolbar/actions/quote'; -import { ToggleList } from '$app/components/editor/components/tools/selection_toolbar/actions/toggle_list'; -import { BulletedList } from '$app/components/editor/components/tools/selection_toolbar/actions/bulleted_list'; -import { NumberedList } from '$app/components/editor/components/tools/selection_toolbar/actions/numbered_list'; -import { Href, LinkActions } from '$app/components/editor/components/tools/selection_toolbar/actions/href'; -import { Align } from '$app/components/editor/components/tools/selection_toolbar/actions/align'; -import { Color } from '$app/components/editor/components/tools/selection_toolbar/actions/color'; - -function SelectionActions({ - isAcrossBlocks, - storeSelection, - restoreSelection, - isIncludeRoot, -}: { - storeSelection: () => void; - restoreSelection: () => void; - isAcrossBlocks: boolean; - visible: boolean; - isIncludeRoot: boolean; -}) { - if (isIncludeRoot) return null; - return ( -
- {!isAcrossBlocks && ( - <> - - - - - )} - - - - - - {!isAcrossBlocks && ( - <> - - - - )} - - {!isAcrossBlocks && ( - <> - - - - - - - - )} - {!isAcrossBlocks && } - - - -
- ); -} - -export default SelectionActions; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/SelectionToolbar.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/SelectionToolbar.hooks.ts deleted file mode 100644 index 58834db6d5..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/SelectionToolbar.hooks.ts +++ /dev/null @@ -1,239 +0,0 @@ -import { ReactEditor, useFocused, useSlate } from 'slate-react'; -import { MutableRefObject, useCallback, useEffect, useMemo, useRef, useState } from 'react'; -import { getSelectionPosition } from '$app/components/editor/components/tools/selection_toolbar/utils'; -import debounce from 'lodash-es/debounce'; -import { CustomEditor } from '$app/components/editor/command'; -import { BaseRange, Range as SlateRange } from 'slate'; -import { useDecorateDispatch } from '$app/components/editor/stores/decorate'; - -const DELAY = 300; - -export function useSelectionToolbar(ref: MutableRefObject) { - const editor = useSlate() as ReactEditor; - const isDraggingRef = useRef(false); - const [isAcrossBlocks, setIsAcrossBlocks] = useState(false); - const [visible, setVisible] = useState(false); - const isFocusedEditor = useFocused(); - const isIncludeRoot = CustomEditor.selectionIncludeRoot(editor); - - // paint the selection when the editor is blurred - const { add: addDecorate, clear: clearDecorate, getStaticState } = useDecorateDispatch(); - - // Restore selection after the editor is focused - const restoreSelection = useCallback(() => { - const decorateState = getStaticState(); - - if (!decorateState) return; - - editor.select({ - ...decorateState.range, - }); - - clearDecorate(); - ReactEditor.focus(editor); - }, [getStaticState, clearDecorate, editor]); - - // Store selection when the editor is blurred - const storeSelection = useCallback(() => { - addDecorate({ - range: editor.selection as BaseRange, - class_name: 'bg-content-blue-100', - }); - }, [addDecorate, editor]); - - const closeToolbar = useCallback(() => { - const el = ref.current; - - if (!el) { - return; - } - - restoreSelection(); - - setVisible(false); - el.style.opacity = '0'; - el.style.pointerEvents = 'none'; - }, [ref, restoreSelection]); - - const recalculatePosition = useCallback(() => { - const el = ref.current; - - if (!el) { - return; - } - - const position = getSelectionPosition(editor); - - if (!position) { - closeToolbar(); - return; - } - - const slateEditorDom = ReactEditor.toDOMNode(editor, editor); - - setVisible(true); - el.style.opacity = '1'; - - // if dragging, disable pointer events - if (isDraggingRef.current) { - el.style.pointerEvents = 'none'; - } else { - el.style.pointerEvents = 'auto'; - } - - // If toolbar is out of editor, move it to the top - el.style.top = `${position.top + slateEditorDom.offsetTop - el.offsetHeight}px`; - - const left = position.left + slateEditorDom.offsetLeft; - - // If toolbar is out of editor, move it to the left edge of the editor - if (left < 0) { - el.style.left = '0'; - return; - } - - const right = left + el.offsetWidth; - - // If toolbar is out of editor, move the right edge to the right edge of the editor - if (right > slateEditorDom.offsetWidth) { - el.style.left = `${slateEditorDom.offsetWidth - el.offsetWidth}px`; - return; - } - - el.style.left = `${left}px`; - }, [closeToolbar, editor, ref]); - - const debounceRecalculatePosition = useMemo(() => debounce(recalculatePosition, DELAY), [recalculatePosition]); - - // eslint-disable-next-line react-hooks/exhaustive-deps - useEffect(() => { - const decorateState = getStaticState(); - - if (decorateState) { - setIsAcrossBlocks(false); - return; - } - - const { selection } = editor; - - const close = () => { - debounceRecalculatePosition.cancel(); - closeToolbar(); - }; - - if (isIncludeRoot || !isFocusedEditor || !selection || SlateRange.isCollapsed(selection)) { - close(); - return; - } - - // There has a bug which the text of selection is empty when the selection include inline blocks - const isEmptyText = !CustomEditor.includeInlineBlocks(editor) && editor.string(selection) === ''; - - if (isEmptyText) { - close(); - return; - } - - setIsAcrossBlocks(CustomEditor.isMultipleBlockSelected(editor, true)); - debounceRecalculatePosition(); - }); - - // Update drag status - useEffect(() => { - const el = ReactEditor.toDOMNode(editor, editor); - - const toolbar = ref.current; - - if (!el || !toolbar) { - return; - } - - const onMouseDown = () => { - isDraggingRef.current = true; - }; - - const onMouseUp = () => { - if (visible) { - toolbar.style.pointerEvents = 'auto'; - } - - isDraggingRef.current = false; - }; - - el.addEventListener('mousedown', onMouseDown); - document.addEventListener('mouseup', onMouseUp); - - return () => { - el.removeEventListener('mousedown', onMouseDown); - document.removeEventListener('mouseup', onMouseUp); - }; - }, [visible, editor, ref]); - - // Close toolbar when press ESC - useEffect(() => { - const slateEditorDom = ReactEditor.toDOMNode(editor, editor); - const onKeyDown = (e: KeyboardEvent) => { - // Close toolbar when press ESC - if (e.key === 'Escape') { - e.preventDefault(); - e.stopPropagation(); - editor.collapse({ - edge: 'end', - }); - debounceRecalculatePosition.cancel(); - closeToolbar(); - } - }; - - if (visible) { - slateEditorDom.addEventListener('keydown', onKeyDown); - } else { - slateEditorDom.removeEventListener('keydown', onKeyDown); - } - - return () => { - slateEditorDom.removeEventListener('keydown', onKeyDown); - }; - }, [closeToolbar, debounceRecalculatePosition, editor, visible]); - - // Recalculate position when the scroll container is scrolled - useEffect(() => { - const slateEditorDom = ReactEditor.toDOMNode(editor, editor); - const scrollContainer = slateEditorDom.closest('.appflowy-scroll-container'); - - if (!visible) return; - if (!scrollContainer) return; - const handleScroll = () => { - if (isDraggingRef.current) return; - - const domSelection = window.getSelection(); - const rangeCount = domSelection?.rangeCount; - - if (!rangeCount) return null; - - const domRange = rangeCount > 0 ? domSelection.getRangeAt(0) : undefined; - - const rangeRect = domRange?.getBoundingClientRect(); - - // Stop calculating when the range is out of the window - if (!rangeRect?.bottom || rangeRect.bottom < 0) { - return; - } - - recalculatePosition(); - }; - - scrollContainer.addEventListener('scroll', handleScroll); - return () => { - scrollContainer.removeEventListener('scroll', handleScroll); - }; - }, [visible, editor, recalculatePosition]); - - return { - visible, - restoreSelection, - storeSelection, - isAcrossBlocks, - isIncludeRoot, - }; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/SelectionToolbar.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/SelectionToolbar.tsx deleted file mode 100644 index d4ca9c9de0..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/SelectionToolbar.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import React, { memo, useRef } from 'react'; -import { useSelectionToolbar } from '$app/components/editor/components/tools/selection_toolbar/SelectionToolbar.hooks'; -import SelectionActions from '$app/components/editor/components/tools/selection_toolbar/SelectionActions'; -import withErrorBoundary from '$app/components/_shared/error_boundary/withError'; - -const Toolbar = memo(() => { - const ref = useRef(null); - - const { visible, restoreSelection, storeSelection, isAcrossBlocks, isIncludeRoot } = useSelectionToolbar(ref); - - return ( -
{ - // prevent toolbar from taking focus away from editor - e.preventDefault(); - }} - > - -
- ); -}); - -export const SelectionToolbar = withErrorBoundary(Toolbar); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/_shared/ActionButton.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/_shared/ActionButton.tsx deleted file mode 100644 index 3f86d1eab9..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/_shared/ActionButton.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import React, { forwardRef } from 'react'; -import IconButton, { IconButtonProps } from '@mui/material/IconButton'; -import { Tooltip } from '@mui/material'; - -const ActionButton = forwardRef< - HTMLButtonElement, - { - tooltip: string | React.ReactNode; - onClick?: (e: React.MouseEvent) => void; - children: React.ReactNode; - active?: boolean; - } & IconButtonProps ->(({ tooltip, onClick, disabled, children, active, className, ...props }, ref) => { - return ( - - - {children} - - - ); -}); - -export default ActionButton; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/align/Align.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/align/Align.tsx deleted file mode 100644 index 23917e146b..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/align/Align.tsx +++ /dev/null @@ -1,98 +0,0 @@ -import React, { useCallback, useMemo, useState } from 'react'; -import Tooltip from '@mui/material/Tooltip'; -import { ReactComponent as AlignLeftSvg } from '$app/assets/align-left.svg'; -import { ReactComponent as AlignCenterSvg } from '$app/assets/align-center.svg'; -import { ReactComponent as AlignRightSvg } from '$app/assets/align-right.svg'; -import ActionButton from '$app/components/editor/components/tools/selection_toolbar/actions/_shared/ActionButton'; -import { useTranslation } from 'react-i18next'; -import { CustomEditor } from '$app/components/editor/command'; -import { useSlateStatic } from 'slate-react'; -import { IconButton } from '@mui/material'; -import { ReactComponent as MoreSvg } from '$app/assets/more.svg'; - -export function Align() { - const { t } = useTranslation(); - const editor = useSlateStatic(); - const align = CustomEditor.getAlign(editor); - const [open, setOpen] = useState(false); - - const handleClose = useCallback(() => { - setOpen(false); - }, []); - - const handleOpen = useCallback(() => { - setOpen(true); - }, []); - - const Icon = useMemo(() => { - switch (align) { - case 'left': - return AlignLeftSvg; - case 'center': - return AlignCenterSvg; - case 'right': - return AlignRightSvg; - default: - return AlignLeftSvg; - } - }, [align]); - - const toggleAlign = useCallback( - (align: string) => { - return () => { - CustomEditor.toggleAlign(editor, align); - handleClose(); - }; - }, - [editor, handleClose] - ); - - const getAlignIcon = useCallback((key: string) => { - switch (key) { - case 'left': - return ; - case 'center': - return ; - case 'right': - return ; - default: - return ; - } - }, []); - - return ( - - {['left', 'center', 'right'].map((key) => { - return ( - - {getAlignIcon(key)} - - ); - })} -
- } - > - -
- - -
-
- - ); -} - -export default Align; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/align/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/align/index.ts deleted file mode 100644 index 6cba19d7bd..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/align/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './Align'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/bold/Bold.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/bold/Bold.tsx deleted file mode 100644 index 22be9f970a..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/bold/Bold.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import React, { useCallback, useMemo } from 'react'; -import ActionButton from '$app/components/editor/components/tools/selection_toolbar/actions/_shared/ActionButton'; -import { useTranslation } from 'react-i18next'; -import { useSlateStatic } from 'slate-react'; -import { CustomEditor } from '$app/components/editor/command'; -import { ReactComponent as BoldSvg } from '$app/assets/bold.svg'; -import { EditorMarkFormat } from '$app/application/document/document.types'; -import { createHotKeyLabel, HOT_KEY_NAME } from '$app/utils/hotkeys'; - -export function Bold() { - const { t } = useTranslation(); - const editor = useSlateStatic(); - const isActivated = CustomEditor.isMarkActive(editor, EditorMarkFormat.Bold); - - const modifier = useMemo(() => createHotKeyLabel(HOT_KEY_NAME.BOLD), []); - const onClick = useCallback(() => { - CustomEditor.toggleMark(editor, { - key: EditorMarkFormat.Bold, - value: true, - }); - }, [editor]); - - return ( - -
{t('toolbar.bold')}
-
{modifier}
- - } - > - -
- ); -} - -export default Bold; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/bold/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/bold/index.ts deleted file mode 100644 index 6ef457faaa..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/bold/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './Bold'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/bulleted_list/BulletedList.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/bulleted_list/BulletedList.tsx deleted file mode 100644 index f35f2aeeea..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/bulleted_list/BulletedList.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import React, { useCallback } from 'react'; -import { useTranslation } from 'react-i18next'; -import { useSlateStatic } from 'slate-react'; -import { EditorNodeType } from '$app/application/document/document.types'; -import { CustomEditor } from '$app/components/editor/command'; -import ActionButton from '$app/components/editor/components/tools/selection_toolbar/actions/_shared/ActionButton'; -import { ReactComponent as BulletedListSvg } from '$app/assets/list.svg'; - -export function BulletedList() { - const { t } = useTranslation(); - const editor = useSlateStatic(); - const isActivated = CustomEditor.isBlockActive(editor, EditorNodeType.BulletedListBlock); - - const onClick = useCallback(() => { - CustomEditor.turnToBlock(editor, { - type: EditorNodeType.BulletedListBlock, - }); - }, [editor]); - - return ( - - - - ); -} - -export default BulletedList; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/bulleted_list/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/bulleted_list/index.ts deleted file mode 100644 index 2095dff308..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/bulleted_list/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './BulletedList'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/color/Color.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/color/Color.tsx deleted file mode 100644 index 60c44423bd..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/color/Color.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import React, { useCallback, useMemo, useRef, useState } from 'react'; -import ActionButton from '$app/components/editor/components/tools/selection_toolbar/actions/_shared/ActionButton'; -import { CustomEditor } from '$app/components/editor/command'; -import { useTranslation } from 'react-i18next'; -import { useSlateStatic } from 'slate-react'; -import ColorLensOutlinedIcon from '@mui/icons-material/ColorLensOutlined'; -import { ReactComponent as MoreSvg } from '$app/assets/more.svg'; -import { EditorMarkFormat } from '$app/application/document/document.types'; -import debounce from 'lodash-es/debounce'; -import ColorPopover from './ColorPopover'; - -export function Color(_: { onOpen?: () => void; onClose?: () => void }) { - const { t } = useTranslation(); - const editor = useSlateStatic(); - const [open, setOpen] = useState(false); - const ref = useRef(null); - - const isActivated = - CustomEditor.isMarkActive(editor, EditorMarkFormat.FontColor) || - CustomEditor.isMarkActive(editor, EditorMarkFormat.BgColor); - const debouncedClose = useMemo( - () => - debounce(() => { - setOpen(false); - }, 200), - [] - ); - - const handleOpen = useCallback(() => { - debouncedClose.cancel(); - setOpen(true); - }, [debouncedClose]); - - return ( - <> - -
- - -
-
- {open && ref.current && ( - - )} - - ); -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/color/ColorPopover.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/color/ColorPopover.tsx deleted file mode 100644 index 9f007dc7b5..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/color/ColorPopover.tsx +++ /dev/null @@ -1,92 +0,0 @@ -import React, { useCallback } from 'react'; -import { PopoverNoBackdropProps } from '$app/components/editor/components/tools/popover'; -import { ColorPicker } from '$app/components/editor/components/tools/_shared'; -import { Popover } from '@mui/material'; -import { PopoverOrigin } from '@mui/material/Popover/Popover'; -import { EditorMarkFormat } from '$app/application/document/document.types'; -import { addMark, removeMark } from 'slate'; -import { useSlateStatic } from 'slate-react'; -import { DebouncedFunc } from 'lodash-es/debounce'; -import usePopoverAutoPosition from '$app/components/_shared/popover/Popover.hooks'; - -const initialOrigin: { - transformOrigin?: PopoverOrigin; - anchorOrigin?: PopoverOrigin; -} = { - anchorOrigin: { - vertical: 'bottom', - horizontal: 'center', - }, - transformOrigin: { - vertical: 'top', - horizontal: 'center', - }, -}; - -function ColorPopover({ - open, - anchorEl, - debounceClose, -}: { - open: boolean; - onOpen: () => void; - anchorEl: HTMLButtonElement | null; - debounceClose: DebouncedFunc<() => void>; -}) { - const editor = useSlateStatic(); - const handleChange = useCallback( - (format: EditorMarkFormat.FontColor | EditorMarkFormat.BgColor, color: string) => { - if (color) { - addMark(editor, format, color); - } else { - removeMark(editor, format); - } - }, - [editor] - ); - - const { paperHeight, transformOrigin, anchorOrigin, isEntered } = usePopoverAutoPosition({ - initialPaperWidth: 200, - initialPaperHeight: 420, - anchorEl, - initialAnchorOrigin: initialOrigin.anchorOrigin, - initialTransformOrigin: initialOrigin.transformOrigin, - open, - }); - - return ( - { - e.stopPropagation(); - if (e.key === 'Escape') { - debounceClose(); - } - }} - onMouseDown={(e) => { - e.preventDefault(); - }} - onMouseEnter={() => { - debounceClose.cancel(); - }} - onMouseLeave={debounceClose} - > - - - ); -} - -export default ColorPopover; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/color/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/color/index.ts deleted file mode 100644 index 0fd619bc86..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/color/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './Color'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/formula/Formula.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/formula/Formula.tsx deleted file mode 100644 index c7bfc11352..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/formula/Formula.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import React, { useCallback } from 'react'; -import ActionButton from '$app/components/editor/components/tools/selection_toolbar/actions/_shared/ActionButton'; -import { useTranslation } from 'react-i18next'; -import { useSlateStatic } from 'slate-react'; -import { CustomEditor } from '$app/components/editor/command'; -import Functions from '@mui/icons-material/Functions'; -import { useEditorInlineBlockState } from '$app/components/editor/stores'; - -export function Formula() { - const { t } = useTranslation(); - const editor = useSlateStatic(); - const isActivatedMention = CustomEditor.isMentionActive(editor); - - const formulaMatch = CustomEditor.formulaActiveNode(editor); - const isActivated = !isActivatedMention && CustomEditor.isFormulaActive(editor); - - const { setRange, openPopover } = useEditorInlineBlockState('formula'); - const onClick = useCallback(() => { - let selection = editor.selection; - - if (!selection) return; - if (formulaMatch) { - selection = editor.range(formulaMatch[1]); - editor.select(selection); - } else { - CustomEditor.toggleFormula(editor); - } - - requestAnimationFrame(() => { - const selection = editor.selection; - - if (!selection) return; - - setRange(selection); - openPopover(); - }); - }, [editor, formulaMatch, setRange, openPopover]); - - return ( - - - - ); -} - -export default Formula; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/formula/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/formula/index.ts deleted file mode 100644 index dc4ad2cd03..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/formula/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './Formula'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/heading/Heading.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/heading/Heading.tsx deleted file mode 100644 index 1fc639c41f..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/heading/Heading.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import React, { useCallback } from 'react'; -import ActionButton from '$app/components/editor/components/tools/selection_toolbar/actions/_shared/ActionButton'; -import { ReactComponent as Heading1Svg } from '$app/assets/h1.svg'; -import { ReactComponent as Heading2Svg } from '$app/assets/h2.svg'; -import { ReactComponent as Heading3Svg } from '$app/assets/h3.svg'; -import { useTranslation } from 'react-i18next'; -import { CustomEditor } from '$app/components/editor/command'; -import { EditorNodeType, HeadingNode } from '$app/application/document/document.types'; -import { useSlateStatic } from 'slate-react'; -import { getBlock } from '$app/components/editor/plugins/utils'; - -export function Heading() { - const { t } = useTranslation(); - const editor = useSlateStatic(); - const toHeading = useCallback( - (level: number) => { - return () => { - CustomEditor.turnToBlock(editor, { - type: EditorNodeType.HeadingBlock, - data: { - level, - }, - }); - }; - }, - [editor] - ); - - const isActivated = useCallback( - (level: number) => { - const node = getBlock(editor) as HeadingNode; - - if (!node) return false; - const isBlock = CustomEditor.isBlockActive(editor, EditorNodeType.HeadingBlock); - - return isBlock && node.data.level === level; - }, - [editor] - ); - - return ( -
- - - - - - - - - -
- ); -} - -export default Heading; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/heading/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/heading/index.ts deleted file mode 100644 index 6406e7b07f..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/heading/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './Heading'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/href/Href.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/href/Href.tsx deleted file mode 100644 index e7412a909e..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/href/Href.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import React, { useCallback, useMemo } from 'react'; -import ActionButton from '$app/components/editor/components/tools/selection_toolbar/actions/_shared/ActionButton'; -import { useTranslation } from 'react-i18next'; -import { useSlateStatic } from 'slate-react'; -import { CustomEditor } from '$app/components/editor/command'; -import { ReactComponent as LinkSvg } from '$app/assets/link.svg'; -import { EditorMarkFormat } from '$app/application/document/document.types'; -import { useDecorateDispatch } from '$app/components/editor/stores'; -import { getModifier } from '$app/utils/hotkeys'; - -export function Href() { - const { t } = useTranslation(); - const editor = useSlateStatic(); - const isActivatedInline = CustomEditor.isInlineActive(editor); - const isActivated = !isActivatedInline && CustomEditor.isMarkActive(editor, EditorMarkFormat.Href); - - const { add: addDecorate } = useDecorateDispatch(); - const onClick = useCallback(() => { - if (!editor.selection) return; - addDecorate({ - range: editor.selection, - class_name: 'bg-content-blue-100 rounded', - type: 'link', - }); - }, [addDecorate, editor]); - - const tooltip = useMemo(() => { - const modifier = getModifier(); - - return ( - <> -
{t('editor.link')}
-
{`${modifier} + K`}
- - ); - }, [t]); - - return ( - <> - - - - - ); -} - -export default Href; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/href/LinkActions.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/href/LinkActions.tsx deleted file mode 100644 index b77a249051..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/href/LinkActions.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import React, { useCallback, useMemo } from 'react'; -import { useDecorateDispatch, useDecorateState } from '$app/components/editor/stores'; -import { ReactEditor, useSlateStatic } from 'slate-react'; -import { Editor } from 'slate'; -import { LinkEditPopover } from '$app/components/editor/components/inline_nodes/link'; - -export function LinkActions() { - const editor = useSlateStatic(); - const decorateState = useDecorateState('link'); - const openEditPopover = !!decorateState; - const { clear: clearDecorate } = useDecorateDispatch(); - - const anchorPosition = useMemo(() => { - const range = decorateState?.range; - - if (!range) return; - - const domRange = ReactEditor.toDOMRange(editor, range); - - const rect = domRange.getBoundingClientRect(); - - return { - top: rect.top, - left: rect.left, - height: rect.height, - }; - }, [decorateState?.range, editor]); - - const defaultHref = useMemo(() => { - const range = decorateState?.range; - - if (!range) return ''; - - const marks = Editor.marks(editor); - - return marks?.href || Editor.string(editor, range); - }, [decorateState?.range, editor]); - - const handleEditPopoverClose = useCallback(() => { - const range = decorateState?.range; - - clearDecorate(); - if (range) { - ReactEditor.focus(editor); - editor.select(range); - } - }, [clearDecorate, decorateState?.range, editor]); - - if (!openEditPopover) return null; - return ( - - ); -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/href/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/href/index.ts deleted file mode 100644 index 9a7210c140..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/href/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './Href'; -export * from './LinkActions'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/inline_code/InlineCode.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/inline_code/InlineCode.tsx deleted file mode 100644 index 3cf9c7ed85..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/inline_code/InlineCode.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import React, { useCallback, useMemo } from 'react'; -import ActionButton from '$app/components/editor/components/tools/selection_toolbar/actions/_shared/ActionButton'; -import { useTranslation } from 'react-i18next'; -import { useSlateStatic } from 'slate-react'; -import { CustomEditor } from '$app/components/editor/command'; -import { ReactComponent as CodeSvg } from '$app/assets/inline-code.svg'; -import { EditorMarkFormat } from '$app/application/document/document.types'; -import { createHotKeyLabel, HOT_KEY_NAME } from '$app/utils/hotkeys'; - -export function InlineCode() { - const { t } = useTranslation(); - const editor = useSlateStatic(); - const isActivated = CustomEditor.isMarkActive(editor, EditorMarkFormat.Code); - const modifier = useMemo(() => createHotKeyLabel(HOT_KEY_NAME.CODE), []); - - const onClick = useCallback(() => { - CustomEditor.toggleMark(editor, { - key: EditorMarkFormat.Code, - value: true, - }); - }, [editor]); - - return ( - -
{t('editor.embedCode')}
-
{modifier}
- - } - > - -
- ); -} - -export default InlineCode; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/inline_code/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/inline_code/index.ts deleted file mode 100644 index 9a4c4930c7..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/inline_code/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './InlineCode'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/italic/Italic.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/italic/Italic.tsx deleted file mode 100644 index 89fff40e6f..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/italic/Italic.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import React, { useCallback, useMemo } from 'react'; -import ActionButton from '$app/components/editor/components/tools/selection_toolbar/actions/_shared/ActionButton'; -import { useTranslation } from 'react-i18next'; -import { useSlateStatic } from 'slate-react'; -import { CustomEditor } from '$app/components/editor/command'; -import { ReactComponent as ItalicSvg } from '$app/assets/italic.svg'; -import { EditorMarkFormat } from '$app/application/document/document.types'; -import { createHotKeyLabel, HOT_KEY_NAME } from '$app/utils/hotkeys'; - -export function Italic() { - const { t } = useTranslation(); - const editor = useSlateStatic(); - const isActivated = CustomEditor.isMarkActive(editor, EditorMarkFormat.Italic); - const modifier = useMemo(() => createHotKeyLabel(HOT_KEY_NAME.ITALIC), []); - - const onClick = useCallback(() => { - CustomEditor.toggleMark(editor, { - key: EditorMarkFormat.Italic, - value: true, - }); - }, [editor]); - - return ( - -
{t('toolbar.italic')}
-
{modifier}
- - } - > - -
- ); -} - -export default Italic; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/italic/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/italic/index.ts deleted file mode 100644 index 70bb069b60..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/italic/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './Italic'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/numbered_list/NumberedList.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/numbered_list/NumberedList.tsx deleted file mode 100644 index 006247ca8b..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/numbered_list/NumberedList.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import React, { useCallback } from 'react'; -import { useTranslation } from 'react-i18next'; -import { useSlateStatic } from 'slate-react'; -import { EditorNodeType } from '$app/application/document/document.types'; -import { CustomEditor } from '$app/components/editor/command'; -import ActionButton from '$app/components/editor/components/tools/selection_toolbar/actions/_shared/ActionButton'; -import { ReactComponent as NumberedListSvg } from '$app/assets/numbers.svg'; - -export function NumberedList() { - const { t } = useTranslation(); - const editor = useSlateStatic(); - const isActivated = CustomEditor.isBlockActive(editor, EditorNodeType.NumberedListBlock); - - const onClick = useCallback(() => { - let type = EditorNodeType.NumberedListBlock; - - if (isActivated) { - type = EditorNodeType.Paragraph; - } - - CustomEditor.turnToBlock(editor, { - type, - }); - }, [editor, isActivated]); - - return ( - - - - ); -} - -export default NumberedList; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/numbered_list/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/numbered_list/index.ts deleted file mode 100644 index 6e985ae25b..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/numbered_list/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './NumberedList'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/paragraph/Paragraph.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/paragraph/Paragraph.tsx deleted file mode 100644 index 1ac5610787..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/paragraph/Paragraph.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import React, { useCallback } from 'react'; -import ActionButton from '$app/components/editor/components/tools/selection_toolbar/actions/_shared/ActionButton'; -import { useTranslation } from 'react-i18next'; -import { getBlock } from '$app/components/editor/plugins/utils'; -import { CustomEditor } from '$app/components/editor/command'; -import { EditorNodeType } from '$app/application/document/document.types'; -import { useSlateStatic } from 'slate-react'; -import { ReactComponent as ParagraphSvg } from '$app/assets/text.svg'; - -export function Paragraph() { - const { t } = useTranslation(); - const editor = useSlateStatic(); - - const onClick = useCallback(() => { - const node = getBlock(editor); - - if (!node) return; - - CustomEditor.turnToBlock(editor, { - type: EditorNodeType.Paragraph, - }); - }, [editor]); - - const isActive = CustomEditor.isBlockActive(editor, EditorNodeType.Paragraph); - - return ( - - - - ); -} - -export default Paragraph; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/paragraph/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/paragraph/index.ts deleted file mode 100644 index 01752c914c..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/paragraph/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './Paragraph'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/quote/Quote.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/quote/Quote.tsx deleted file mode 100644 index 29ad0de104..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/quote/Quote.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import React, { useCallback } from 'react'; -import { useTranslation } from 'react-i18next'; -import { useSlateStatic } from 'slate-react'; -import { EditorNodeType } from '$app/application/document/document.types'; -import { CustomEditor } from '$app/components/editor/command'; -import ActionButton from '$app/components/editor/components/tools/selection_toolbar/actions/_shared/ActionButton'; -import { ReactComponent as QuoteSvg } from '$app/assets/quote.svg'; - -export function Quote() { - const { t } = useTranslation(); - const editor = useSlateStatic(); - const isActivated = CustomEditor.isBlockActive(editor, EditorNodeType.QuoteBlock); - - const onClick = useCallback(() => { - let type = EditorNodeType.QuoteBlock; - - if (isActivated) { - type = EditorNodeType.Paragraph; - } - - CustomEditor.turnToBlock(editor, { - type, - }); - }, [editor, isActivated]); - - return ( - - - - ); -} - -export default Quote; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/quote/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/quote/index.ts deleted file mode 100644 index c88e677a53..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/quote/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './Quote'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/strikethrough/StrikeThrough.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/strikethrough/StrikeThrough.tsx deleted file mode 100644 index 325f6ac55a..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/strikethrough/StrikeThrough.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import React, { useCallback, useMemo } from 'react'; -import ActionButton from '$app/components/editor/components/tools/selection_toolbar/actions/_shared/ActionButton'; -import { useTranslation } from 'react-i18next'; -import { useSlateStatic } from 'slate-react'; -import { CustomEditor } from '$app/components/editor/command'; -import { ReactComponent as StrikeThroughSvg } from '$app/assets/strikethrough.svg'; -import { EditorMarkFormat } from '$app/application/document/document.types'; -import { createHotKeyLabel, HOT_KEY_NAME } from '$app/utils/hotkeys'; - -export function StrikeThrough() { - const { t } = useTranslation(); - const editor = useSlateStatic(); - const isActivated = CustomEditor.isMarkActive(editor, EditorMarkFormat.StrikeThrough); - const modifier = useMemo(() => createHotKeyLabel(HOT_KEY_NAME.STRIKETHROUGH), []); - - const onClick = useCallback(() => { - CustomEditor.toggleMark(editor, { - key: EditorMarkFormat.StrikeThrough, - value: true, - }); - }, [editor]); - - return ( - -
{t('editor.strikethrough')}
-
{modifier}
- - } - > - -
- ); -} - -export default StrikeThrough; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/strikethrough/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/strikethrough/index.ts deleted file mode 100644 index f8314d16e3..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/strikethrough/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './StrikeThrough'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/todo_list/TodoList.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/todo_list/TodoList.tsx deleted file mode 100644 index cd576edafa..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/todo_list/TodoList.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import React, { useCallback } from 'react'; -import { useSlateStatic } from 'slate-react'; -import { CustomEditor } from '$app/components/editor/command'; -import { EditorNodeType } from '$app/application/document/document.types'; -import { ReactComponent as TodoListSvg } from '$app/assets/todo-list.svg'; -import ActionButton from '$app/components/editor/components/tools/selection_toolbar/actions/_shared/ActionButton'; -import { useTranslation } from 'react-i18next'; - -export function TodoList() { - const { t } = useTranslation(); - const editor = useSlateStatic(); - - const isActivated = CustomEditor.isBlockActive(editor, EditorNodeType.TodoListBlock); - - const onClick = useCallback(() => { - let type = EditorNodeType.TodoListBlock; - - if (isActivated) { - type = EditorNodeType.Paragraph; - } - - CustomEditor.turnToBlock(editor, { - type, - data: { - checked: false, - }, - }); - }, [editor, isActivated]); - - return ( - - - - ); -} - -export default TodoList; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/todo_list/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/todo_list/index.ts deleted file mode 100644 index f239f43459..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/todo_list/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './TodoList'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/toggle_list/ToggleList.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/toggle_list/ToggleList.tsx deleted file mode 100644 index 4d82652988..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/toggle_list/ToggleList.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import React, { useCallback } from 'react'; -import { useTranslation } from 'react-i18next'; -import { useSlateStatic } from 'slate-react'; -import { CustomEditor } from '$app/components/editor/command'; -import { EditorNodeType } from '$app/application/document/document.types'; -import ActionButton from '$app/components/editor/components/tools/selection_toolbar/actions/_shared/ActionButton'; -import { ReactComponent as ToggleListSvg } from '$app/assets/show-menu.svg'; - -export function ToggleList() { - const { t } = useTranslation(); - const editor = useSlateStatic(); - const isActivated = CustomEditor.isBlockActive(editor, EditorNodeType.ToggleListBlock); - - const onClick = useCallback(() => { - let type = EditorNodeType.ToggleListBlock; - - if (isActivated) { - type = EditorNodeType.Paragraph; - } - - CustomEditor.turnToBlock(editor, { - type, - data: { - collapsed: false, - }, - }); - }, [editor, isActivated]); - - return ( - - - - ); -} - -export default ToggleList; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/toggle_list/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/toggle_list/index.ts deleted file mode 100644 index 833bdb5210..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/toggle_list/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './ToggleList'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/underline/Underline.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/underline/Underline.tsx deleted file mode 100644 index b0df70e30e..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/underline/Underline.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import React, { useCallback, useMemo } from 'react'; -import ActionButton from '$app/components/editor/components/tools/selection_toolbar/actions/_shared/ActionButton'; -import { useTranslation } from 'react-i18next'; -import { useSlateStatic } from 'slate-react'; -import { CustomEditor } from '$app/components/editor/command'; -import { ReactComponent as UnderlineSvg } from '$app/assets/underline.svg'; -import { EditorMarkFormat } from '$app/application/document/document.types'; -import { createHotKeyLabel, HOT_KEY_NAME } from '$app/utils/hotkeys'; - -export function Underline() { - const { t } = useTranslation(); - const editor = useSlateStatic(); - const isActivated = CustomEditor.isMarkActive(editor, EditorMarkFormat.Underline); - const modifier = useMemo(() => createHotKeyLabel(HOT_KEY_NAME.UNDERLINE), []); - - const onClick = useCallback(() => { - CustomEditor.toggleMark(editor, { - key: EditorMarkFormat.Underline, - value: true, - }); - }, [editor]); - - return ( - -
{t('editor.underline')}
-
{modifier}
- - } - > - -
- ); -} - -export default Underline; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/underline/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/underline/index.ts deleted file mode 100644 index a1d53a4384..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/actions/underline/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './Underline'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/index.ts deleted file mode 100644 index a6ced3f248..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './SelectionToolbar'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/utils.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/utils.ts deleted file mode 100644 index 178da73df4..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/tools/selection_toolbar/utils.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { ReactEditor } from 'slate-react'; -import { getEditorDomNode } from '$app/components/editor/plugins/utils'; - -export function getSelectionPosition(editor: ReactEditor) { - const domSelection = window.getSelection(); - const rangeCount = domSelection?.rangeCount; - - if (!rangeCount) return null; - - const domRange = rangeCount > 0 ? domSelection.getRangeAt(0) : undefined; - - const rect = domRange?.getBoundingClientRect(); - - let newRect; - - const domNode = getEditorDomNode(editor); - const domNodeRect = domNode.getBoundingClientRect(); - - // the default height of the toolbar is 30px - const gap = 106; - - if (rect) { - let relativeDomTop = rect.top - domNodeRect.top; - const relativeDomLeft = rect.left - domNodeRect.left; - - // if the range is above the window, move the toolbar to the bottom of range - if (rect.top < gap) { - relativeDomTop = -domNodeRect.top + gap; - } - - newRect = { - top: relativeDomTop, - left: relativeDomLeft, - width: rect.width, - height: rect.height, - }; - } - - return newRect; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/editor.scss b/frontend/appflowy_tauri/src/appflowy_app/components/editor/editor.scss deleted file mode 100644 index 271dd36cda..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/editor.scss +++ /dev/null @@ -1,231 +0,0 @@ - -.block-element { - @apply my-[4px]; - -} - -.block-element .block-element { - @apply mb-0; - margin-left: 24px; -} - -.block-element.block-align-left { - > div > .text-element { - text-align: left; - justify-content: flex-start; - } -} -.block-element.block-align-right { - > div > .text-element { - text-align: right; - justify-content: flex-end; - } -} -.block-element.block-align-center { - > div > .text-element { - text-align: center; - justify-content: center; - } - -} - - -.block-element[data-block-type="todo_list"] .checked > .text-element { - text-decoration: line-through; - color: var(--text-caption); -} - -.block-element .collapsed .block-element { - display: none !important; -} - -[role=textbox] { - .text-element { - &::selection { - @apply bg-transparent; - } - } -} - - - -span[data-slate-placeholder="true"]:not(.inline-block-content) { - @apply text-text-placeholder; - opacity: 1 !important; -} - - -[role="textbox"] { - ::selection { - @apply bg-content-blue-100; - } - .text-content { - &::selection { - @apply bg-transparent; - } - &.selected { - @apply bg-content-blue-100; - } - span { - &::selection { - @apply bg-content-blue-100; - } - } - } -} - - -[data-dark-mode="true"] [role="textbox"]{ - ::selection { - background-color: #1e79a2; - } - - .text-content { - &::selection { - @apply bg-transparent; - } - &.selected { - background-color: #1e79a2; - } - span { - &::selection { - background-color: #1e79a2; - } - } - } -} - - -.text-content, [data-dark-mode="true"] .text-content { - @apply min-w-[1px]; - &.empty-text { - span { - &::selection { - @apply bg-transparent; - } - } - } -} - -.text-element:has(.text-placeholder), .divider-node, [data-dark-mode="true"] .text-element:has(.text-placeholder), [data-dark-mode="true"] .divider-node { - ::selection { - @apply bg-transparent; - } -} - -.text-placeholder { - @apply absolute left-[5px] transform -translate-y-1/2 pointer-events-none select-none whitespace-nowrap; - &:after { - @apply text-text-placeholder absolute top-0; - content: (attr(placeholder)); - } -} - -.block-align-center { - .text-placeholder { - @apply left-[calc(50%+1px)]; - &:after { - @apply left-0; - } - } - .has-start-icon .text-placeholder { - @apply left-[calc(50%+13px)]; - &:after { - @apply left-0; - } - } - -} - -.block-align-left { - .text-placeholder { - &:after { - @apply left-0; - } - } - .has-start-icon .text-placeholder { - &:after { - @apply left-[24px]; - } - } -} - -.block-align-right { - - .text-placeholder { - - @apply relative w-fit h-0 order-2; - &:after { - @apply relative w-fit top-1/2 left-[-6px]; - } - } - .text-content { - @apply order-1; - } - - .has-start-icon .text-placeholder { - &:after { - @apply left-[-6px]; - } - } -} - - -.formula-inline { - &.selected { - @apply rounded bg-content-blue-100; - } -} - -.bulleted-icon { - &:after { - content: attr(data-letter); - } -} - -.numbered-icon { - &:after { - content: attr(data-number) "."; - } -} - - -.grid-block .grid-scroll-container::-webkit-scrollbar { - width: 0; - height: 0; -} - -.image-render { - .image-resizer { - @apply absolute w-[10px] top-0 z-10 flex h-full cursor-col-resize items-center justify-end; - .resize-handle { - @apply h-1/4 w-1/2 transform transition-all duration-500 select-none rounded-full border border-white opacity-0; - background: var(--fill-toolbar); - } - } - &:hover { - .image-resizer{ - .resize-handle { - @apply opacity-90; - } - } - } -} - - -.image-block, .math-equation-block, [data-dark-mode="true"] .image-block, [data-dark-mode="true"] .math-equation-block { - ::selection { - @apply bg-transparent; - } - &:hover { - .container-bg { - background: var(--content-blue-100) !important; - } - } -} - -.mention-inline { - &:hover { - @apply bg-fill-list-active rounded; - } -} \ No newline at end of file diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/index.ts deleted file mode 100644 index 8b7c4c267a..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './Editor'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/plugins/constants.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/plugins/constants.ts deleted file mode 100644 index 03e441b1c3..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/plugins/constants.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { EditorNodeType } from '$app/application/document/document.types'; - -export const SOFT_BREAK_TYPES = [EditorNodeType.CalloutBlock, EditorNodeType.CodeBlock]; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/plugins/copyPasted/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/plugins/copyPasted/index.ts deleted file mode 100644 index bf2b09a1c3..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/plugins/copyPasted/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './withCopy'; -export * from './withPasted'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/plugins/copyPasted/utils.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/plugins/copyPasted/utils.ts deleted file mode 100644 index cb377fece4..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/plugins/copyPasted/utils.ts +++ /dev/null @@ -1,311 +0,0 @@ -import { ReactEditor } from 'slate-react'; -import { Editor, Node, Location, Range, Path, Element, Text, Transforms, NodeEntry } from 'slate'; -import { EditorNodeType } from '$app/application/document/document.types'; -import { CustomEditor } from '$app/components/editor/command'; -import { LIST_TYPES } from '$app/components/editor/command/tab'; - -/** - * Rewrite the insertFragment function to avoid the empty node(doesn't have text node) in the fragment - - * @param editor - * @param fragment - * @param options - */ -export function insertFragment( - editor: ReactEditor, - fragment: (Text | Element)[], - options: { - at?: Location; - hanging?: boolean; - voids?: boolean; - } = {} -) { - Editor.withoutNormalizing(editor, () => { - const { hanging = false, voids = false } = options; - let { at = getDefaultInsertLocation(editor) } = options; - - if (!fragment.length) { - return; - } - - if (Range.isRange(at)) { - if (!hanging) { - at = Editor.unhangRange(editor, at, { voids }); - } - - if (Range.isCollapsed(at)) { - at = at.anchor; - } else { - const [, end] = Range.edges(at); - - if (!voids && Editor.void(editor, { at: end })) { - return; - } - - const pointRef = Editor.pointRef(editor, end); - - Transforms.delete(editor, { at }); - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - at = pointRef.unref()!; - } - } else if (Path.isPath(at)) { - at = Editor.start(editor, at); - } - - if (!voids && Editor.void(editor, { at })) { - return; - } - - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const blockMatch = Editor.above(editor, { - match: (n) => Element.isElement(n) && Editor.isBlock(editor, n) && n.blockId !== undefined, - at, - voids, - })!; - const [block, blockPath] = blockMatch as NodeEntry; - - const isEmbedBlock = Element.isElement(block) && editor.isEmbed(block); - const isPageBlock = Element.isElement(block) && block.type === EditorNodeType.Page; - const isBlockStart = Editor.isStart(editor, at, blockPath); - const isBlockEnd = Editor.isEnd(editor, at, blockPath); - const isBlockEmpty = isBlockStart && isBlockEnd; - - if (isEmbedBlock) { - insertOnEmbedBlock(editor, fragment, blockPath); - return; - } - - if (isBlockEmpty && !isPageBlock) { - const node = fragment[0] as Element; - - if (block.type !== EditorNodeType.Paragraph) { - node.type = block.type; - node.data = { - ...(node.data || {}), - ...(block.data || {}), - }; - } - - insertOnEmptyBlock(editor, fragment, blockPath); - return; - } - - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - const fragmentRoot: Node = { - children: fragment, - }; - const [, firstPath] = Node.first(fragmentRoot, []); - const [, lastPath] = Node.last(fragmentRoot, []); - const sameBlock = Path.equals(firstPath.slice(0, -1), lastPath.slice(0, -1)); - - if (sameBlock) { - insertTexts( - editor, - isPageBlock - ? ({ - children: [ - { - text: CustomEditor.getNodeTextContent(fragmentRoot), - }, - ], - } as Node) - : fragmentRoot, - at - ); - return; - } - - const isListTypeBlock = LIST_TYPES.includes(block.type as EditorNodeType); - const [, ...blockChildren] = block.children; - - const blockEnd = editor.end([...blockPath, 0]); - const afterRange: Range = { anchor: at, focus: blockEnd }; - - const afterTexts = getTexts(editor, { - children: editor.fragment(afterRange), - } as Node) as (Text | Element)[]; - - Transforms.delete(editor, { at: afterRange }); - - const { startTexts, startChildren, middles } = getFragmentGroup(editor, fragment); - - insertNodes( - editor, - isPageBlock - ? [ - { - text: CustomEditor.getNodeTextContent({ - children: startTexts, - } as Node), - }, - ] - : startTexts, - { - at, - } - ); - - if (isPageBlock) { - insertNodes(editor, [...startChildren, ...middles], { - at: Path.next(blockPath), - select: true, - }); - } else { - if (blockChildren.length > 0) { - const path = [...blockPath, 1]; - - insertNodes(editor, [...startChildren, ...middles], { - at: path, - select: true, - }); - } else { - const newMiddle = [...middles]; - - if (isListTypeBlock) { - const path = [...blockPath, 1]; - - insertNodes(editor, startChildren, { - at: path, - select: newMiddle.length === 0, - }); - } else { - newMiddle.unshift(...startChildren); - } - - insertNodes(editor, newMiddle, { - at: Path.next(blockPath), - select: true, - }); - } - } - - const { selection } = editor; - - if (!selection) return; - - insertNodes(editor, afterTexts, { - at: selection, - }); - }); -} - -function getFragmentGroup(editor: ReactEditor, fragment: Node[]) { - const startTexts = []; - const startChildren = []; - const middles = []; - - const [firstNode, ...otherNodes] = fragment; - const [firstNodeText, ...firstNodeChildren] = (firstNode as Element).children as Element[]; - - startTexts.push(...firstNodeText.children); - startChildren.push(...firstNodeChildren); - - for (const node of otherNodes) { - if (Element.isElement(node) && node.blockId !== undefined) { - middles.push(node); - } - } - - return { - startTexts, - startChildren, - middles, - }; -} - -function getTexts(editor: ReactEditor, fragment: Node) { - const matches = []; - const matcher = ([n]: NodeEntry) => Text.isText(n) || (Element.isElement(n) && editor.isInline(n)); - - for (const entry of Node.nodes(fragment, { pass: matcher })) { - if (matcher(entry)) { - matches.push(entry[0]); - } - } - - return matches; -} - -function insertTexts(editor: ReactEditor, fragmentRoot: Node, at: Location) { - const matches = getTexts(editor, fragmentRoot); - - insertNodes(editor, matches, { - at, - select: true, - }); -} - -function insertOnEmptyBlock(editor: ReactEditor, fragment: Node[], blockPath: Path) { - editor.removeNodes({ - at: blockPath, - }); - - insertNodes(editor, fragment, { - at: blockPath, - select: true, - }); -} - -function insertOnEmbedBlock(editor: ReactEditor, fragment: Node[], blockPath: Path) { - insertNodes(editor, fragment, { - at: Path.next(blockPath), - select: true, - }); -} - -function insertNodes(editor: ReactEditor, nodes: Node[], options: { at?: Location; select?: boolean } = {}) { - try { - Transforms.insertNodes(editor, nodes, options); - } catch (e) { - try { - editor.move({ - distance: 1, - unit: 'line', - }); - } catch (e) { - // do nothing - } - } -} - -/** - * Copy Code from slate/src/utils/get-default-insert-location.ts - * Get the default location to insert content into the editor. - * By default, use the selection as the target location. But if there is - * no selection, insert at the end of the document since that is such a - * common use case when inserting from a non-selected state. - */ -export const getDefaultInsertLocation = (editor: Editor): Location => { - if (editor.selection) { - return editor.selection; - } else if (editor.children.length > 0) { - return Editor.end(editor, []); - } else { - return [0]; - } -}; - -export function transFragment(editor: ReactEditor, fragment: Node[]) { - // flatten the fragment to avoid the empty node(doesn't have text node) in the fragment - const flatMap = (node: Node): Node[] => { - const isInputElement = - !Editor.isEditor(node) && Element.isElement(node) && node.blockId !== undefined && !editor.isEmbed(node); - - if ( - isInputElement && - node.children?.length > 0 && - Element.isElement(node.children[0]) && - node.children[0].type !== EditorNodeType.Text - ) { - return node.children.flatMap((child) => flatMap(child)); - } - - return [node]; - }; - - const fragmentFlatMap = fragment?.flatMap(flatMap); - - // clone the node to avoid the duplicated block id - return fragmentFlatMap.map((item) => CustomEditor.cloneBlock(editor, item as Element)); -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/plugins/copyPasted/withCopy.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/plugins/copyPasted/withCopy.ts deleted file mode 100644 index c0daab0a8f..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/plugins/copyPasted/withCopy.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { ReactEditor } from 'slate-react'; -import { Editor, Element, Range } from 'slate'; - -export function withCopy(editor: ReactEditor) { - const { setFragmentData } = editor; - - editor.setFragmentData = (...args) => { - if (!editor.selection) { - setFragmentData(...args); - return; - } - - // selection is collapsed and the node is an embed, we need to set the data manually - if (Range.isCollapsed(editor.selection)) { - const match = Editor.above(editor, { - match: (n) => !Editor.isEditor(n) && Element.isElement(n) && n.blockId !== undefined, - }); - const node = match ? (match[0] as Element) : undefined; - - if (node && editor.isEmbed(node)) { - const fragment = editor.getFragment(); - - if (fragment.length > 0) { - const data = args[0]; - const string = JSON.stringify(fragment); - const encoded = window.btoa(encodeURIComponent(string)); - - const dom = ReactEditor.toDOMNode(editor, node); - - data.setData(`application/x-slate-fragment`, encoded); - data.setData(`text/html`, dom.innerHTML); - } - } - } - - setFragmentData(...args); - }; - - return editor; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/plugins/copyPasted/withPasted.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/plugins/copyPasted/withPasted.ts deleted file mode 100644 index 2266ff41c7..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/plugins/copyPasted/withPasted.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { ReactEditor } from 'slate-react'; -import { insertFragment, transFragment } from './utils'; -import { convertBlockToJson } from '$app/application/document/document.service'; -import { InputType } from '@/services/backend'; -import { CustomEditor } from '$app/components/editor/command'; -import { Log } from '$app/utils/log'; - -export function withPasted(editor: ReactEditor) { - const { insertData } = editor; - - editor.insertData = (data) => { - const fragment = data.getData('application/x-slate-fragment'); - - if (fragment) { - insertData(data); - return; - } - - const html = data.getData('text/html'); - const text = data.getData('text/plain'); - - if (!html && !text) { - insertData(data); - return; - } - - void (async () => { - try { - const nodes = await convertBlockToJson(html, InputType.Html); - - const htmlTransNoText = nodes.every((node) => { - return CustomEditor.getNodeTextContent(node).length === 0; - }); - - if (!htmlTransNoText) { - return editor.insertFragment(nodes); - } - } catch (e) { - Log.warn('pasted html error', e); - // ignore - } - - if (text) { - const nodes = await convertBlockToJson(text, InputType.PlainText); - - editor.insertFragment(nodes); - return; - } - })(); - }; - - editor.insertFragment = (fragment, options = {}) => { - const clonedFragment = transFragment(editor, fragment); - - insertFragment(editor, clonedFragment, options); - }; - - return editor; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/plugins/shortcuts/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/plugins/shortcuts/index.ts deleted file mode 100644 index 0292784ba5..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/plugins/shortcuts/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './shortcuts.hooks'; -export * from './withMarkdown'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/plugins/shortcuts/markdown.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/plugins/shortcuts/markdown.ts deleted file mode 100644 index 59ff0a8593..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/plugins/shortcuts/markdown.ts +++ /dev/null @@ -1,172 +0,0 @@ -export type MarkdownRegex = { - [key in MarkdownShortcuts]: { - pattern: RegExp; - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - data?: Record; - }[]; -}; - -export type TriggerHotKey = { - [key in MarkdownShortcuts]: string[]; -}; - -export enum MarkdownShortcuts { - Bold, - Italic, - StrikeThrough, - Code, - Equation, - /** block */ - Heading, - BlockQuote, - CodeBlock, - Divider, - /** list */ - BulletedList, - NumberedList, - TodoList, - ToggleList, -} - -const defaultMarkdownRegex: MarkdownRegex = { - [MarkdownShortcuts.Heading]: [ - { - pattern: /^#{1,6}$/, - }, - ], - [MarkdownShortcuts.Bold]: [ - { - pattern: /(\*\*|__)(.*?)(\*\*|__)$/, - }, - ], - [MarkdownShortcuts.Italic]: [ - { - pattern: /([*_])(.*?)([*_])$/, - }, - ], - [MarkdownShortcuts.StrikeThrough]: [ - { - pattern: /(~~)(.*?)(~~)$/, - }, - { - pattern: /(~)(.*?)(~)$/, - }, - ], - [MarkdownShortcuts.Code]: [ - { - pattern: /(`)(.*?)(`)$/, - }, - ], - [MarkdownShortcuts.Equation]: [ - { - pattern: /(\$)(.*?)(\$)$/, - data: { - formula: '', - }, - }, - ], - [MarkdownShortcuts.BlockQuote]: [ - { - pattern: /^([”“"])$/, - }, - ], - [MarkdownShortcuts.CodeBlock]: [ - { - pattern: /^(`{2,})$/, - data: { - language: 'json', - }, - }, - ], - [MarkdownShortcuts.Divider]: [ - { - pattern: /^(([-*]){2,})$/, - }, - ], - - [MarkdownShortcuts.BulletedList]: [ - { - pattern: /^([*\-+])$/, - }, - ], - [MarkdownShortcuts.NumberedList]: [ - { - pattern: /^(\d+)\.$/, - }, - ], - [MarkdownShortcuts.TodoList]: [ - { - pattern: /^(-)?\[ ]$/, - data: { - checked: false, - }, - }, - { - pattern: /^(-)?\[x]$/, - data: { - checked: true, - }, - }, - { - pattern: /^(-)?\[]$/, - data: { - checked: false, - }, - }, - ], - [MarkdownShortcuts.ToggleList]: [ - { - pattern: /^>$/, - data: { - collapsed: false, - }, - }, - ], -}; - -export const defaultTriggerChar: TriggerHotKey = { - [MarkdownShortcuts.Heading]: [' '], - [MarkdownShortcuts.Bold]: ['*', '_'], - [MarkdownShortcuts.Italic]: ['*', '_'], - [MarkdownShortcuts.StrikeThrough]: ['~'], - [MarkdownShortcuts.Code]: ['`'], - [MarkdownShortcuts.BlockQuote]: [' '], - [MarkdownShortcuts.CodeBlock]: ['`'], - [MarkdownShortcuts.Divider]: ['-', '*'], - [MarkdownShortcuts.Equation]: ['$'], - [MarkdownShortcuts.BulletedList]: [' '], - [MarkdownShortcuts.NumberedList]: [' '], - [MarkdownShortcuts.TodoList]: [' '], - [MarkdownShortcuts.ToggleList]: [' '], -}; - -export function isTriggerChar(char: string) { - return Object.values(defaultTriggerChar).some((trigger) => trigger.includes(char)); -} - -export function whatShortcutTrigger(char: string): MarkdownShortcuts[] | null { - const isTrigger = isTriggerChar(char); - - if (!isTrigger) { - return null; - } - - const shortcuts = Object.keys(defaultTriggerChar).map((key) => Number(key) as MarkdownShortcuts); - - return shortcuts.filter((shortcut) => defaultTriggerChar[shortcut].includes(char)); -} - -export function getRegex(shortcut: MarkdownShortcuts) { - return defaultMarkdownRegex[shortcut]; -} - -export function whatShortcutsMatch(text: string) { - const shortcuts = Object.keys(defaultMarkdownRegex).map((key) => Number(key) as MarkdownShortcuts); - - return shortcuts.filter((shortcut) => { - const regexes = defaultMarkdownRegex[shortcut]; - - return regexes.some((regex) => regex.pattern.test(text)); - }); -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/plugins/shortcuts/shortcuts.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/plugins/shortcuts/shortcuts.hooks.ts deleted file mode 100644 index 45d61f847c..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/plugins/shortcuts/shortcuts.hooks.ts +++ /dev/null @@ -1,349 +0,0 @@ -import { ReactEditor } from 'slate-react'; -import { useCallback, KeyboardEvent } from 'react'; -import { EditorMarkFormat, EditorNodeType, ToggleListNode } from '$app/application/document/document.types'; -import { getBlock } from '$app/components/editor/plugins/utils'; -import { SOFT_BREAK_TYPES } from '$app/components/editor/plugins/constants'; -import { CustomEditor } from '$app/components/editor/command'; -import { createHotkey, HOT_KEY_NAME } from '$app/utils/hotkeys'; -import { openUrl } from '$app/utils/open_url'; -import { Range } from 'slate'; -import { readText } from '@tauri-apps/api/clipboard'; -import { useDecorateDispatch } from '$app/components/editor/stores'; - -function getScrollContainer(editor: ReactEditor) { - const editorDom = ReactEditor.toDOMNode(editor, editor); - - return editorDom.closest('.appflowy-scroll-container') as HTMLDivElement; -} - -export function useShortcuts(editor: ReactEditor) { - const { add: addDecorate } = useDecorateDispatch(); - - const formatLink = useCallback(() => { - const { selection } = editor; - - if (!selection || Range.isCollapsed(selection)) return; - - const isIncludeRoot = CustomEditor.selectionIncludeRoot(editor); - - if (isIncludeRoot) return; - - const isActivatedInline = CustomEditor.isInlineActive(editor); - - if (isActivatedInline) return; - - addDecorate({ - range: selection, - class_name: 'bg-content-blue-100 rounded', - type: 'link', - }); - }, [addDecorate, editor]); - - const onKeyDown = useCallback( - (e: KeyboardEvent) => { - const event = e.nativeEvent; - const hasEditableTarget = ReactEditor.hasEditableTarget(editor, event.target); - - if (!hasEditableTarget) return; - - const node = getBlock(editor); - - const { selection } = editor; - const isExpanded = selection && Range.isExpanded(selection); - - switch (true) { - /** - * Select all: Mod+A - * Default behavior: Select all text in the editor - * Special case for select all in code block: Only select all text in code block - */ - case createHotkey(HOT_KEY_NAME.SELECT_ALL)(event): - if (node && node.type === EditorNodeType.CodeBlock) { - e.preventDefault(); - const path = ReactEditor.findPath(editor, node); - - editor.select(path); - } - - break; - /** - * Escape: Esc - * Default behavior: Deselect editor - */ - case createHotkey(HOT_KEY_NAME.ESCAPE)(event): - editor.deselect(); - break; - /** - * Indent block: Tab - * Default behavior: Indent block - */ - case createHotkey(HOT_KEY_NAME.INDENT_BLOCK)(event): - e.preventDefault(); - if (SOFT_BREAK_TYPES.includes(node?.type as EditorNodeType)) { - editor.insertText('\t'); - break; - } - - CustomEditor.tabForward(editor); - break; - /** - * Outdent block: Shift+Tab - * Default behavior: Outdent block - */ - case createHotkey(HOT_KEY_NAME.OUTDENT_BLOCK)(event): - e.preventDefault(); - CustomEditor.tabBackward(editor); - break; - /** - * Split block: Enter - * Default behavior: Split block - * Special case for soft break types: Insert \n - */ - case createHotkey(HOT_KEY_NAME.SPLIT_BLOCK)(event): - if (SOFT_BREAK_TYPES.includes(node?.type as EditorNodeType)) { - e.preventDefault(); - editor.insertText('\n'); - } - - break; - /** - * Insert soft break: Shift+Enter - * Default behavior: Insert \n - * Special case for soft break types: Split block - */ - case createHotkey(HOT_KEY_NAME.INSERT_SOFT_BREAK)(event): - e.preventDefault(); - if (node && SOFT_BREAK_TYPES.includes(node.type as EditorNodeType)) { - editor.splitNodes({ - always: true, - }); - } else { - editor.insertText('\n'); - } - - break; - /** - * Toggle todo: Shift+Enter - * Default behavior: Toggle todo - * Special case for toggle list block: Toggle collapse - */ - case createHotkey(HOT_KEY_NAME.TOGGLE_TODO)(event): - case createHotkey(HOT_KEY_NAME.TOGGLE_COLLAPSE)(event): - e.preventDefault(); - if (node && node.type === EditorNodeType.ToggleListBlock) { - CustomEditor.toggleToggleList(editor, node as ToggleListNode); - } else { - CustomEditor.toggleTodo(editor); - } - - break; - /** - * Backspace: Backspace / Shift+Backspace - * Default behavior: Delete backward - */ - case createHotkey(HOT_KEY_NAME.BACKSPACE)(event): - e.stopPropagation(); - break; - /** - * Open link: Alt + enter - * Default behavior: Open one link in selection - */ - case createHotkey(HOT_KEY_NAME.OPEN_LINK)(event): { - if (!isExpanded) break; - e.preventDefault(); - const links = CustomEditor.getLinks(editor); - - if (links.length === 0) break; - openUrl(links[0]); - break; - } - - /** - * Open links: Alt + Shift + enter - * Default behavior: Open all links in selection - */ - case createHotkey(HOT_KEY_NAME.OPEN_LINKS)(event): { - if (!isExpanded) break; - e.preventDefault(); - const links = CustomEditor.getLinks(editor); - - if (links.length === 0) break; - links.forEach((link) => openUrl(link)); - break; - } - - /** - * Extend line backward: Opt + Shift + right - * Default behavior: Extend line backward - */ - case createHotkey(HOT_KEY_NAME.EXTEND_LINE_BACKWARD)(event): - e.preventDefault(); - CustomEditor.extendLineBackward(editor); - break; - /** - * Extend line forward: Opt + Shift + left - */ - case createHotkey(HOT_KEY_NAME.EXTEND_LINE_FORWARD)(event): - e.preventDefault(); - CustomEditor.extendLineForward(editor); - break; - - /** - * Paste: Mod + Shift + V - * Default behavior: Paste plain text - */ - case createHotkey(HOT_KEY_NAME.PASTE_PLAIN_TEXT)(event): - e.preventDefault(); - void (async () => { - const text = await readText(); - - if (!text) return; - CustomEditor.insertPlainText(editor, text); - })(); - - break; - /** - * Highlight: Mod + Shift + H - * Default behavior: Highlight selected text - */ - case createHotkey(HOT_KEY_NAME.HIGH_LIGHT)(event): - e.preventDefault(); - CustomEditor.highlight(editor); - break; - /** - * Extend document backward: Mod + Shift + Up - * Don't prevent default behavior - * Default behavior: Extend document backward - */ - case createHotkey(HOT_KEY_NAME.EXTEND_DOCUMENT_BACKWARD)(event): - editor.collapse({ edge: 'start' }); - break; - /** - * Extend document forward: Mod + Shift + Down - * Don't prevent default behavior - * Default behavior: Extend document forward - */ - case createHotkey(HOT_KEY_NAME.EXTEND_DOCUMENT_FORWARD)(event): - editor.collapse({ edge: 'end' }); - break; - - /** - * Scroll to top: Home - * Default behavior: Scroll to top - */ - case createHotkey(HOT_KEY_NAME.SCROLL_TO_TOP)(event): { - const scrollContainer = getScrollContainer(editor); - - scrollContainer.scrollTo({ - top: 0, - }); - break; - } - - /** - * Scroll to bottom: End - * Default behavior: Scroll to bottom - */ - case createHotkey(HOT_KEY_NAME.SCROLL_TO_BOTTOM)(event): { - const scrollContainer = getScrollContainer(editor); - - scrollContainer.scrollTo({ - top: scrollContainer.scrollHeight, - }); - break; - } - - /** - * Align left: Control + Shift + L - * Default behavior: Align left - */ - case createHotkey(HOT_KEY_NAME.ALIGN_LEFT)(event): - e.preventDefault(); - CustomEditor.toggleAlign(editor, 'left'); - break; - /** - * Align center: Control + Shift + E - */ - case createHotkey(HOT_KEY_NAME.ALIGN_CENTER)(event): - e.preventDefault(); - CustomEditor.toggleAlign(editor, 'center'); - break; - /** - * Align right: Control + Shift + R - */ - case createHotkey(HOT_KEY_NAME.ALIGN_RIGHT)(event): - e.preventDefault(); - CustomEditor.toggleAlign(editor, 'right'); - break; - /** - * Bold: Mod + B - */ - case createHotkey(HOT_KEY_NAME.BOLD)(event): - e.preventDefault(); - CustomEditor.toggleMark(editor, { - key: EditorMarkFormat.Bold, - value: true, - }); - break; - /** - * Italic: Mod + I - */ - case createHotkey(HOT_KEY_NAME.ITALIC)(event): - e.preventDefault(); - CustomEditor.toggleMark(editor, { - key: EditorMarkFormat.Italic, - value: true, - }); - break; - /** - * Underline: Mod + U - */ - case createHotkey(HOT_KEY_NAME.UNDERLINE)(event): - e.preventDefault(); - CustomEditor.toggleMark(editor, { - key: EditorMarkFormat.Underline, - value: true, - }); - break; - /** - * Strikethrough: Mod + Shift + S / Mod + Shift + X - */ - case createHotkey(HOT_KEY_NAME.STRIKETHROUGH)(event): - e.preventDefault(); - CustomEditor.toggleMark(editor, { - key: EditorMarkFormat.StrikeThrough, - value: true, - }); - break; - /** - * Code: Mod + E - */ - case createHotkey(HOT_KEY_NAME.CODE)(event): - e.preventDefault(); - CustomEditor.toggleMark(editor, { - key: EditorMarkFormat.Code, - value: true, - }); - break; - /** - * Format link: Mod + K - */ - case createHotkey(HOT_KEY_NAME.FORMAT_LINK)(event): - formatLink(); - break; - - case createHotkey(HOT_KEY_NAME.FIND_REPLACE)(event): - console.log('find replace'); - break; - - default: - break; - } - }, - [formatLink, editor] - ); - - return { - onKeyDown, - }; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/plugins/shortcuts/withMarkdown.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/plugins/shortcuts/withMarkdown.ts deleted file mode 100644 index fd7801204c..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/plugins/shortcuts/withMarkdown.ts +++ /dev/null @@ -1,239 +0,0 @@ -import { Range, Element, Editor, NodeEntry, Path } from 'slate'; -import { ReactEditor } from 'slate-react'; -import { - defaultTriggerChar, - getRegex, - MarkdownShortcuts, - whatShortcutsMatch, - whatShortcutTrigger, -} from '$app/components/editor/plugins/shortcuts/markdown'; -import { CustomEditor } from '$app/components/editor/command'; -import { EditorMarkFormat, EditorNodeType } from '$app/application/document/document.types'; -import isEqual from 'lodash-es/isEqual'; - -export const withMarkdown = (editor: ReactEditor) => { - const { insertText } = editor; - - editor.insertText = (char) => { - const { selection } = editor; - - insertText(char); - if (!selection || !Range.isCollapsed(selection)) { - return; - } - - const triggerShortcuts = whatShortcutTrigger(char); - - if (!triggerShortcuts) { - return; - } - - const match = CustomEditor.getBlock(editor); - const [node, path] = match as NodeEntry; - - let prevIsNumberedList = false; - - try { - const prevPath = Path.previous(path); - const prev = editor.node(prevPath) as NodeEntry; - - prevIsNumberedList = prev && prev[0].type === EditorNodeType.NumberedListBlock; - } catch (e) { - // do nothing - } - - const start = Editor.start(editor, path); - const beforeRange = { anchor: start, focus: selection.anchor }; - const beforeText = Editor.string(editor, beforeRange); - - const removeBeforeText = (beforeRange: Range) => { - editor.deleteBackward('character'); - editor.delete({ - at: beforeRange, - }); - }; - - const matchBlockShortcuts = whatShortcutsMatch(beforeText); - - for (const shortcut of matchBlockShortcuts) { - const block = whichBlock(shortcut, beforeText); - - // if the block shortcut is matched, remove the before text and turn to the block - // then return - if (block && defaultTriggerChar[shortcut].includes(char)) { - // Don't turn to the block condition - // 1. Heading should be able to co-exist with number list - if (block.type === EditorNodeType.NumberedListBlock && node.type === EditorNodeType.HeadingBlock) { - return; - } - - // 2. If the block is the same type, and data is the same - if (block.type === node.type && isEqual(block.data || {}, node.data || {})) { - return; - } - - // 3. If the block is number list, and the previous block is also number list - if (block.type === EditorNodeType.NumberedListBlock && prevIsNumberedList) { - return; - } - - removeBeforeText(beforeRange); - CustomEditor.turnToBlock(editor, block); - - return; - } - } - - // get the range that matches the mark shortcuts - const markRange = { - anchor: Editor.start(editor, selection.anchor.path), - focus: selection.focus, - }; - const rangeText = Editor.string(editor, markRange) + char; - - if (!rangeText) return; - - // inputting a character that is start of a mark - const isStartTyping = rangeText.indexOf(char) === rangeText.lastIndexOf(char); - - if (isStartTyping) return; - - // if the range text includes a double character mark, and the last one is not finished - const doubleCharNotFinish = - ['*', '_', '~'].includes(char) && - rangeText.indexOf(`${char}${char}`) > -1 && - rangeText.indexOf(`${char}${char}`) === rangeText.lastIndexOf(`${char}${char}`); - - if (doubleCharNotFinish) return; - - const matchMarkShortcuts = whatShortcutsMatch(rangeText); - - for (const shortcut of matchMarkShortcuts) { - const item = getRegex(shortcut).find((p) => p.pattern.test(rangeText)); - const execArr = item?.pattern?.exec(rangeText); - - const removeText = execArr ? execArr[0] : ''; - - const text = execArr ? execArr[2]?.replaceAll(char, '') : ''; - - if (text) { - const index = rangeText.indexOf(removeText); - const removeRange = { - anchor: { - path: markRange.anchor.path, - offset: markRange.anchor.offset + index, - }, - focus: { - path: markRange.anchor.path, - offset: markRange.anchor.offset + index + removeText.length, - }, - }; - - removeBeforeText(removeRange); - insertMark(editor, shortcut, text); - return; - } - } - }; - - return editor; -}; - -function whichBlock(shortcut: MarkdownShortcuts, beforeText: string) { - switch (shortcut) { - case MarkdownShortcuts.Heading: - return { - type: EditorNodeType.HeadingBlock, - data: { - level: beforeText.length, - }, - }; - case MarkdownShortcuts.CodeBlock: - return { - type: EditorNodeType.CodeBlock, - data: { - language: 'json', - }, - }; - case MarkdownShortcuts.BulletedList: - return { - type: EditorNodeType.BulletedListBlock, - data: {}, - }; - case MarkdownShortcuts.NumberedList: - return { - type: EditorNodeType.NumberedListBlock, - data: { - number: Number(beforeText.split('.')[0]) ?? 1, - }, - }; - case MarkdownShortcuts.TodoList: - return { - type: EditorNodeType.TodoListBlock, - data: { - checked: beforeText.includes('[x]'), - }, - }; - case MarkdownShortcuts.BlockQuote: - return { - type: EditorNodeType.QuoteBlock, - data: {}, - }; - case MarkdownShortcuts.Divider: - return { - type: EditorNodeType.DividerBlock, - data: {}, - }; - - case MarkdownShortcuts.ToggleList: - return { - type: EditorNodeType.ToggleListBlock, - data: { - collapsed: false, - }, - }; - - default: - return null; - } -} - -function insertMark(editor: ReactEditor, shortcut: MarkdownShortcuts, text: string) { - switch (shortcut) { - case MarkdownShortcuts.Bold: - case MarkdownShortcuts.Italic: - case MarkdownShortcuts.StrikeThrough: - case MarkdownShortcuts.Code: { - const textNode = { - text, - }; - const attributes = { - [MarkdownShortcuts.Bold]: { - [EditorMarkFormat.Bold]: true, - }, - [MarkdownShortcuts.Italic]: { - [EditorMarkFormat.Italic]: true, - }, - [MarkdownShortcuts.StrikeThrough]: { - [EditorMarkFormat.StrikeThrough]: true, - }, - [MarkdownShortcuts.Code]: { - [EditorMarkFormat.Code]: true, - }, - }; - - Object.assign(textNode, attributes[shortcut]); - - editor.insertNodes(textNode); - return; - } - - case MarkdownShortcuts.Equation: { - CustomEditor.insertFormula(editor, text); - return; - } - - default: - return null; - } -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/plugins/utils.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/plugins/utils.ts deleted file mode 100644 index 62e3ad945a..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/plugins/utils.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { Element, NodeEntry } from 'slate'; -import { ReactEditor } from 'slate-react'; -import { CustomEditor } from '$app/components/editor/command'; - -export function getHeadingCssProperty(level: number) { - switch (level) { - case 1: - return 'text-3xl pt-[10px] pb-[8px] font-bold'; - case 2: - return 'text-2xl pt-[8px] pb-[6px] font-bold'; - case 3: - return 'text-xl pt-[4px] font-bold'; - case 4: - return 'text-lg pt-[4px] font-bold'; - case 5: - return 'text-base pt-[4px] font-bold'; - case 6: - return 'text-sm pt-[4px] font-bold'; - default: - return ''; - } -} - -export function getBlock(editor: ReactEditor) { - const match = CustomEditor.getBlock(editor); - - if (match) { - const [node] = match as NodeEntry; - - return node; - } - - return; -} - -export function getEditorDomNode(editor: ReactEditor) { - return ReactEditor.toDOMNode(editor, editor); -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/plugins/withBlockDelete.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/plugins/withBlockDelete.ts deleted file mode 100644 index 0bcd0965a9..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/plugins/withBlockDelete.ts +++ /dev/null @@ -1,228 +0,0 @@ -import { ReactEditor } from 'slate-react'; -import { Editor, Element, NodeEntry, Path, Range } from 'slate'; -import { EditorNodeType } from '$app/application/document/document.types'; -import { CustomEditor } from '$app/components/editor/command'; - -/** - * Delete backward. - * | -> cursor - * - * ------------------- delete backward to its previous sibling lift all children - * 1 1|2 - * |2 3 - * 3 => delete backward => 4 - * 4 5 - * 5 - * ------------------- delete backward to its parent and lift all children - * 1 1|2 - * |2 3 - * 3 => delete backward => 4 - * 4 5 - * 5 - * ------------------- outdent the node if the node has no children - * 1 1 - * 2 2 - * |3 |3 - * 4 => delete backward => 4 - * @param editor - */ -export function withBlockDelete(editor: ReactEditor) { - const { deleteBackward, deleteFragment, mergeNodes } = editor; - - editor.deleteBackward = (unit) => { - const match = CustomEditor.getBlock(editor); - - if (!match || !CustomEditor.focusAtStartOfBlock(editor)) { - deleteBackward(unit); - return; - } - - const [node, path] = match; - - const isEmbed = editor.isEmbed(node); - - if (isEmbed) { - CustomEditor.deleteNode(editor, node); - return; - } - - const previous = editor.previous({ - at: path, - match: (n) => !Editor.isEditor(n) && Element.isElement(n) && n.blockId !== undefined, - }); - const [previousNode] = previous || [undefined, undefined]; - - const previousIsPage = previousNode && Element.isElement(previousNode) && previousNode.type === EditorNodeType.Page; - - // merge to document title - if (previousIsPage) { - const textNodePath = [...path, 0]; - const [textNode] = editor.node(textNodePath); - const text = CustomEditor.getNodeTextContent(textNode); - - // clear all attributes - editor.select(textNodePath); - CustomEditor.removeMarks(editor); - editor.insertText(text); - - editor.move({ - distance: text.length, - reverse: true, - }); - } - - // if the current node is not a paragraph, convert it to a paragraph(except code block and callout block) - if ( - ![EditorNodeType.Paragraph, EditorNodeType.CalloutBlock, EditorNodeType.CodeBlock].includes( - node.type as EditorNodeType - ) && - node.type !== EditorNodeType.Page - ) { - CustomEditor.turnToBlock(editor, { type: EditorNodeType.Paragraph }); - return; - } - - const next = editor.next({ - at: path, - }); - - if (!next && path.length > 1) { - CustomEditor.tabBackward(editor); - return; - } - - const length = node.children.length; - - for (let i = length - 1; i > 0; i--) { - editor.liftNodes({ - at: [...path, i], - }); - } - - // if previous node is an embed, merge the current node to another node which is not an embed - if (Element.isElement(previousNode) && editor.isEmbed(previousNode)) { - const previousTextMatch = editor.previous({ - at: path, - match: (n) => !Editor.isEditor(n) && Element.isElement(n) && n.textId !== undefined, - }); - - if (!previousTextMatch) { - deleteBackward(unit); - return; - } - - const previousTextPath = previousTextMatch[1]; - const textNode = node.children[0] as Element; - - const at = Editor.end(editor, previousTextPath); - - editor.select(at); - editor.insertNodes(textNode.children, { - at, - }); - - editor.removeNodes({ - at: path, - }); - return; - } - - deleteBackward(unit); - }; - - editor.deleteFragment = (...args) => { - beforeDeleteToDocumentTitle(editor); - - deleteFragment(...args); - }; - - editor.mergeNodes = (options) => { - mergeNodes(options); - if (!editor.selection || !options?.at) return; - const nextPath = findNextPath(editor, editor.selection.anchor.path); - - const [nextNode] = editor.node(nextPath); - - if (Element.isElement(nextNode) && nextNode.blockId !== undefined && nextNode.children.length === 0) { - editor.removeNodes({ - at: nextPath, - }); - } - - return; - }; - - return editor; -} - -function beforeDeleteToDocumentTitle(editor: ReactEditor) { - if (!editor.selection) return; - if (Range.isCollapsed(editor.selection)) return; - const start = Range.start(editor.selection); - const end = Range.end(editor.selection); - const startNode = editor.above({ - at: start, - match: (n) => !Editor.isEditor(n) && Element.isElement(n) && n.type === EditorNodeType.Page, - }); - - const endNode = editor.above({ - at: end, - match: (n) => !Editor.isEditor(n) && Element.isElement(n) && n.blockId !== undefined, - }); - - const startNodeIsPage = !!startNode; - - if (!startNodeIsPage || !endNode) return; - const [node, path] = endNode as NodeEntry; - const selectedText = editor.string({ - anchor: { - path, - offset: 0, - }, - focus: end, - }); - - const nodeChildren = node.children; - const nodeChildrenLength = nodeChildren.length; - - for (let i = nodeChildrenLength - 1; i > 0; i--) { - editor.liftNodes({ - at: [...path, i], - }); - } - - const textNodePath = [...path, 0]; - const [textNode] = editor.node(textNodePath); - const text = CustomEditor.getNodeTextContent(textNode); - - // clear all attributes - editor.select([...path, 0]); - CustomEditor.removeMarks(editor); - editor.insertText(text); - editor.move({ - distance: text.length - selectedText.length, - reverse: true, - }); - editor.select({ - anchor: start, - focus: editor.selection.focus, - }); -} - -function findNextPath(editor: ReactEditor, path: Path): Path { - if (path.length === 0) return path; - const parentPath = Path.parent(path); - - try { - const nextPath = Path.next(path); - const [nextNode] = Editor.node(editor, nextPath); - - if (Element.isElement(nextNode) && nextNode.blockId !== undefined) { - return nextPath; - } - } catch (e) { - // ignore - } - - return findNextPath(editor, parentPath); -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/plugins/withBlockInsertBreak.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/plugins/withBlockInsertBreak.ts deleted file mode 100644 index b6f8da0e56..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/plugins/withBlockInsertBreak.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { ReactEditor } from 'slate-react'; -import { EditorNodeType } from '$app/application/document/document.types'; -import { CustomEditor } from '$app/components/editor/command'; -import { Path, Transforms } from 'slate'; -import { YjsEditor } from '@slate-yjs/core'; -import { generateId } from '$app/components/editor/provider/utils/convert'; - -export function withBlockInsertBreak(editor: ReactEditor) { - const { insertBreak } = editor; - - editor.insertBreak = (...args) => { - const block = CustomEditor.getBlock(editor); - - if (!block) return insertBreak(...args); - - const [node, path] = block; - - const isEmbed = editor.isEmbed(node); - - const nextPath = Path.next(path); - - if (isEmbed) { - CustomEditor.insertEmptyLine(editor as ReactEditor & YjsEditor, nextPath); - editor.select(nextPath); - return; - } - - const type = node.type as EditorNodeType; - - const isBeginning = CustomEditor.focusAtStartOfBlock(editor); - - const isEmpty = CustomEditor.isEmptyText(editor, node); - - if (isEmpty) { - const depth = path.length; - let hasNextNode = false; - - try { - hasNextNode = Boolean(editor.node(nextPath)); - } catch (e) { - // do nothing - } - - // if the node is empty and the depth is greater than 1, tab backward - if (depth > 1 && !hasNextNode) { - CustomEditor.tabBackward(editor); - return; - } - - // if the node is empty, convert it to a paragraph - if (type !== EditorNodeType.Paragraph && type !== EditorNodeType.Page) { - CustomEditor.turnToBlock(editor, { type: EditorNodeType.Paragraph }); - return; - } - } else if (isBeginning) { - // insert line below the current block - const newNodeType = [ - EditorNodeType.TodoListBlock, - EditorNodeType.BulletedListBlock, - EditorNodeType.NumberedListBlock, - ].includes(type) - ? type - : EditorNodeType.Paragraph; - - Transforms.insertNodes( - editor, - { - type: newNodeType, - data: node.data ?? {}, - blockId: generateId(), - children: [ - { - type: EditorNodeType.Text, - textId: generateId(), - children: [ - { - text: '', - }, - ], - }, - ], - }, - { - at: path, - } - ); - return; - } - - insertBreak(...args); - }; - - return editor; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/plugins/withBlockMove.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/plugins/withBlockMove.ts deleted file mode 100644 index 814c6e7333..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/plugins/withBlockMove.ts +++ /dev/null @@ -1,134 +0,0 @@ -import { ReactEditor } from 'slate-react'; -import { generateId } from '$app/components/editor/provider/utils/convert'; -import { Editor, Element, Location, NodeEntry, Path, Node, Transforms } from 'slate'; -import { EditorNodeType } from '$app/application/document/document.types'; - -const matchPath = (editor: Editor, path: Path): ((node: Node) => boolean) => { - const [node] = Editor.node(editor, path); - - return (n) => { - return n === node; - }; -}; - -export function withBlockMove(editor: ReactEditor) { - const { moveNodes } = editor; - - editor.moveNodes = (args) => { - const { to } = args; - - moveNodes(args); - - replaceId(editor, to); - }; - - editor.liftNodes = (args = {}) => { - Editor.withoutNormalizing(editor, () => { - const { at = editor.selection, mode = 'lowest', voids = false } = args; - let { match } = args; - - if (!match) { - match = Path.isPath(at) - ? matchPath(editor, at) - : (n) => Element.isElement(n) && Editor.isBlock(editor, n) && n.blockId !== undefined; - } - - if (!at) { - return; - } - - const matches = Editor.nodes(editor, { at, match, mode, voids }); - - const pathRefs = Array.from(matches, ([, p]) => { - return Editor.pathRef(editor, p); - }); - - for (const pathRef of pathRefs) { - const path = pathRef.unref(); - - if (!path) return; - if (path.length < 2) { - throw new Error(`Cannot lift node at a path [${path}] because it has a depth of less than \`2\`.`); - } - - const parentNodeEntry = Editor.node(editor, Path.parent(path)); - const [parent, parentPath] = parentNodeEntry as NodeEntry; - const index = path[path.length - 1]; - const { length } = parent.children; - - if (length === 1) { - const toPath = Path.next(parentPath); - - Transforms.moveNodes(editor, { at: path, to: toPath, voids }); - Transforms.removeNodes(editor, { at: parentPath, voids }); - } else if (index === 0) { - Transforms.moveNodes(editor, { at: path, to: parentPath, voids }); - } else if (index === length - 1) { - const toPath = Path.next(parentPath); - - Transforms.moveNodes(editor, { at: path, to: toPath, voids }); - } else { - const toPath = Path.next(parentPath); - - const node = parent.children[index] as Element; - const nodeChildrenLength = node.children.length; - - for (let i = length - 1; i > index; i--) { - Transforms.moveNodes(editor, { - at: [...parentPath, i], - to: [...path, nodeChildrenLength], - mode: 'all', - }); - } - - Transforms.moveNodes(editor, { at: path, to: toPath, voids }); - } - } - }); - }; - - return editor; -} - -function replaceId(editor: Editor, at?: Location) { - const newBlockId = generateId(); - const newTextId = generateId(); - - const selection = editor.selection; - - const location = at || selection; - - if (!location) return; - - const [node, path] = editor.node(location) as NodeEntry; - - if (node.blockId === undefined) { - return; - } - - const [textNode, ...children] = node.children as Element[]; - - editor.setNodes( - { - blockId: newBlockId, - }, - { - at, - } - ); - - if (textNode && textNode.type === EditorNodeType.Text) { - editor.setNodes( - { - textId: newTextId, - }, - { - at: [...path, 0], - } - ); - } - - children.forEach((_, index) => { - replaceId(editor, [...path, index + 1]); - }); -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/plugins/withBlockPlugins.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/plugins/withBlockPlugins.ts deleted file mode 100644 index 1e9fc7f105..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/plugins/withBlockPlugins.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { ReactEditor } from 'slate-react'; - -import { withBlockDelete } from '$app/components/editor/plugins/withBlockDelete'; -import { withBlockInsertBreak } from '$app/components/editor/plugins/withBlockInsertBreak'; -import { withSplitNodes } from '$app/components/editor/plugins/withSplitNodes'; -import { withPasted, withCopy } from '$app/components/editor/plugins/copyPasted'; -import { withBlockMove } from '$app/components/editor/plugins/withBlockMove'; -import { CustomEditor } from '$app/components/editor/command'; - -export function withBlockPlugins(editor: ReactEditor) { - const { isElementReadOnly, isEmpty, isSelectable } = editor; - - editor.isElementReadOnly = (element) => { - return CustomEditor.isEmbedNode(element) || isElementReadOnly(element); - }; - - editor.isEmbed = (element) => { - return CustomEditor.isEmbedNode(element); - }; - - editor.isSelectable = (element) => { - return !CustomEditor.isEmbedNode(element) && isSelectable(element); - }; - - editor.isEmpty = (element) => { - return !CustomEditor.isEmbedNode(element) && isEmpty(element); - }; - - return withPasted(withBlockMove(withSplitNodes(withBlockInsertBreak(withBlockDelete(withCopy(editor)))))); -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/plugins/withSplitNodes.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/plugins/withSplitNodes.ts deleted file mode 100644 index eee7dd92d0..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/plugins/withSplitNodes.ts +++ /dev/null @@ -1,139 +0,0 @@ -import { ReactEditor } from 'slate-react'; -import { Transforms, Editor, Element, NodeEntry, Path, Range } from 'slate'; -import { EditorNodeType, ToggleListNode } from '$app/application/document/document.types'; -import { CustomEditor } from '$app/components/editor/command'; -import { generateId } from '$app/components/editor/provider/utils/convert'; -import cloneDeep from 'lodash-es/cloneDeep'; -import { SOFT_BREAK_TYPES } from '$app/components/editor/plugins/constants'; - -/** - * Split nodes. - * split text node into two text nodes, and wrap the second text node with a new block node. - * - * Split to the first child condition: - * 1. block type is toggle list block, and the block is not collapsed. - * - * Split to the next sibling condition: - * 1. block type is toggle list block, and the block is collapsed. - * 2. block type is other block type. - * - * Split to a paragraph node: (otherwise split to the same block type) - * 1. block type is heading block. - * 2. block type is quote block. - * 3. block type is page. - * 4. block type is code block and callout block. - * 5. block type is paragraph. - * - * @param editor - */ -export function withSplitNodes(editor: ReactEditor) { - const { splitNodes } = editor; - - editor.splitNodes = (...args) => { - const isInsertBreak = args.length === 1 && JSON.stringify(args[0]) === JSON.stringify({ always: true }); - - if (!isInsertBreak) { - splitNodes(...args); - return; - } - - const selection = editor.selection; - - const isCollapsed = selection && Range.isCollapsed(selection); - - if (!isCollapsed) { - editor.deleteFragment({ direction: 'backward' }); - } - - const match = CustomEditor.getBlock(editor); - - if (!match) { - splitNodes(...args); - return; - } - - const [node, path] = match; - const nodeType = node.type as EditorNodeType; - - const newBlockId = generateId(); - const newTextId = generateId(); - - splitNodes(...args); - - const matchTextNode = editor.above({ - match: (n) => !Editor.isEditor(n) && Element.isElement(n) && n.type === EditorNodeType.Text, - }); - - if (!matchTextNode) return; - const [textNode, textNodePath] = matchTextNode as NodeEntry; - - editor.removeNodes({ - at: textNodePath, - }); - - const newNodeType = [ - EditorNodeType.HeadingBlock, - EditorNodeType.QuoteBlock, - EditorNodeType.Page, - ...SOFT_BREAK_TYPES, - ].includes(node.type as EditorNodeType) - ? EditorNodeType.Paragraph - : node.type; - - const newNode: Element = { - type: newNodeType, - data: {}, - blockId: newBlockId, - children: [ - { - ...cloneDeep(textNode), - textId: newTextId, - }, - ], - }; - let newNodePath; - - if (nodeType === EditorNodeType.ToggleListBlock) { - const collapsed = (node as ToggleListNode).data.collapsed; - - if (!collapsed) { - newNode.type = EditorNodeType.Paragraph; - newNodePath = textNodePath; - } else { - newNode.type = EditorNodeType.ToggleListBlock; - newNodePath = Path.next(path); - } - - Transforms.insertNodes(editor, newNode, { - at: newNodePath, - }); - - editor.select(newNodePath); - - CustomEditor.removeMarks(editor); - editor.collapse({ - edge: 'start', - }); - return; - } - - newNodePath = textNodePath; - - Transforms.insertNodes(editor, newNode, { - at: newNodePath, - }); - - editor.select(newNodePath); - editor.collapse({ - edge: 'start', - }); - - editor.liftNodes({ - at: newNodePath, - }); - - CustomEditor.removeMarks(editor); - }; - - return editor; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/provider/__tests__/action.test.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/provider/__tests__/action.test.ts deleted file mode 100644 index 026ee57222..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/provider/__tests__/action.test.ts +++ /dev/null @@ -1,124 +0,0 @@ -import { applyActions } from './utils/mockBackendService'; -import { generateId } from '$app/components/editor/provider/utils/convert'; -import { Provider } from '$app/components/editor/provider'; -import * as Y from 'yjs'; -import { BlockActionTypePB } from '@/services/backend'; -import { - generateFormulaInsertTextOp, - generateInsertTextOp, - genersteMentionInsertTextOp, -} from '$app/components/editor/provider/__tests__/utils/convert'; - -describe('Transform events to actions', () => { - let provider: Provider; - beforeEach(() => { - provider = new Provider(generateId()); - provider.initialDocument(true); - provider.connect(); - applyActions.mockClear(); - }); - - afterEach(() => { - provider.disconnect(); - }); - - test('should transform insert event to insert action', () => { - const sharedType = provider.sharedType; - - const insertTextOp = generateInsertTextOp('insert text'); - - sharedType?.applyDelta([{ retain: 2 }, insertTextOp]); - - const actions = applyActions.mock.calls[0][1]; - expect(actions).toHaveLength(2); - const textId = actions[0].payload.text_id; - expect(actions[0].action).toBe(BlockActionTypePB.InsertText); - expect(actions[0].payload.delta).toBe('[{"insert":"insert text"}]'); - expect(actions[1].action).toBe(BlockActionTypePB.Insert); - expect(actions[1].payload.block.ty).toBe('paragraph'); - expect(actions[1].payload.block.parent_id).toBe('3EzeCrtxlh'); - expect(actions[1].payload.block.children_id).not.toBeNull(); - expect(actions[1].payload.block.external_id).toBe(textId); - expect(actions[1].payload.parent_id).toBe('3EzeCrtxlh'); - expect(actions[1].payload.prev_id).toBe('2qonPRrNTO'); - }); - - test('should transform delete event to delete action', () => { - const sharedType = provider.sharedType; - - sharedType?.doc?.transact(() => { - sharedType?.applyDelta([{ retain: 4 }, { delete: 1 }]); - }); - - const actions = applyActions.mock.calls[0][1]; - expect(actions).toHaveLength(1); - expect(actions[0].action).toBe(BlockActionTypePB.Delete); - expect(actions[0].payload.block.id).toBe('Fn4KACkt1i'); - }); - - test('should transform update event to update action', () => { - const sharedType = provider.sharedType; - - const yText = sharedType?.toDelta()[4].insert as Y.XmlText; - sharedType?.doc?.transact(() => { - yText.setAttribute('data', { - checked: true, - }); - }); - - const actions = applyActions.mock.calls[0][1]; - expect(actions).toHaveLength(1); - expect(actions[0].action).toBe(BlockActionTypePB.Update); - expect(actions[0].payload.block.id).toBe('Fn4KACkt1i'); - expect(actions[0].payload.block.data).toBe('{"checked":true}'); - }); - - test('should transform apply delta event to apply delta action (insert text)', () => { - const sharedType = provider.sharedType; - - const blockYText = sharedType?.toDelta()[4].insert as Y.XmlText; - const textYText = blockYText.toDelta()[0].insert as Y.XmlText; - sharedType?.doc?.transact(() => { - textYText.applyDelta([{ retain: 1 }, { insert: 'apply delta' }]); - }); - const textId = textYText.getAttribute('textId'); - - const actions = applyActions.mock.calls[0][1]; - expect(actions).toHaveLength(1); - expect(actions[0].action).toBe(BlockActionTypePB.ApplyTextDelta); - expect(actions[0].payload.text_id).toBe(textId); - expect(actions[0].payload.delta).toBe('[{"retain":1},{"insert":"apply delta"}]'); - }); - - test('should transform apply delta event to apply delta action: insert mention', () => { - const sharedType = provider.sharedType; - - const blockYText = sharedType?.toDelta()[4].insert as Y.XmlText; - const yText = blockYText.toDelta()[0].insert as Y.XmlText; - sharedType?.doc?.transact(() => { - yText.applyDelta([{ retain: 1 }, genersteMentionInsertTextOp()]); - }); - - const actions = applyActions.mock.calls[0][1]; - expect(actions).toHaveLength(1); - expect(actions[0].action).toBe(BlockActionTypePB.ApplyTextDelta); - expect(actions[0].payload.delta).toBe('[{"retain":1},{"insert":"@","attributes":{"mention":{"page":"page_id"}}}]'); - }); - - test('should transform apply delta event to apply delta action: insert formula', () => { - const sharedType = provider.sharedType; - - const blockYText = sharedType?.toDelta()[4].insert as Y.XmlText; - const yText = blockYText.toDelta()[0].insert as Y.XmlText; - sharedType?.doc?.transact(() => { - yText.applyDelta([{ retain: 1 }, generateFormulaInsertTextOp()]); - }); - - const actions = applyActions.mock.calls[0][1]; - expect(actions).toHaveLength(1); - expect(actions[0].action).toBe(BlockActionTypePB.ApplyTextDelta); - expect(actions[0].payload.delta).toBe('[{"retain":1},{"insert":"= 1 + 1","attributes":{"formula":true}}]'); - }); -}); - -export {}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/provider/__tests__/observe.test.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/provider/__tests__/observe.test.ts deleted file mode 100644 index 0937d265ed..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/provider/__tests__/observe.test.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { applyActions } from './utils/mockBackendService'; - -import { Provider } from '$app/components/editor/provider'; -import { generateId } from '$app/components/editor/provider/utils/convert'; -import { generateInsertTextOp } from '$app/components/editor/provider/__tests__/utils/convert'; - -export {}; - -describe('Provider connected', () => { - let provider: Provider; - - beforeEach(() => { - provider = new Provider(generateId()); - provider.initialDocument(true); - provider.connect(); - applyActions.mockClear(); - }); - - afterEach(() => { - provider.disconnect(); - }); - - test('should initial document', () => { - const sharedType = provider.sharedType; - expect(sharedType).not.toBeNull(); - expect(sharedType?.length).toBe(25); - expect(sharedType?.getAttribute('blockId')).toBe('3EzeCrtxlh'); - }); - - test('should send actions when the local changed', () => { - const sharedType = provider.sharedType; - - const parentId = sharedType?.getAttribute('blockId') as string; - const insertTextOp = generateInsertTextOp(''); - - sharedType?.applyDelta([{ retain: 2 }, insertTextOp]); - - expect(sharedType?.length).toBe(26); - expect(applyActions).toBeCalledTimes(1); - }); -}); - -export {}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/provider/__tests__/read_me.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/provider/__tests__/read_me.ts deleted file mode 100644 index adb85f2bfd..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/provider/__tests__/read_me.ts +++ /dev/null @@ -1,437 +0,0 @@ -export default { - viewId: '04acbc17-2265-4e4d-ac80-392bdc81379f', - rootId: '3EzeCrtxlh', - nodeMap: { - '692ooXzoV-': { - id: '692ooXzoV-', - type: 'todo_list', - parent: '3EzeCrtxlh', - children: '4yIcpjxFTQ', - data: { checked: false }, - externalId: '9L10h3UZ7J', - externalType: 'text', - }, - gCjs671FiD: { - id: 'gCjs671FiD', - type: 'paragraph', - parent: '3EzeCrtxlh', - children: 'Bj4M6midh3', - data: {}, - externalId: 'uGR_eATq2B', - externalType: 'text', - }, - whGVOpFJzA: { - id: 'whGVOpFJzA', - type: 'code', - parent: '3EzeCrtxlh', - children: 'eYqZSaUSrF', - data: { language: 'rust' }, - externalId: '2H8dnFhOsJ', - externalType: 'text', - }, - PeUTr8lpaW: { - id: 'PeUTr8lpaW', - type: 'divider', - parent: '3EzeCrtxlh', - children: '7gXZ4anHxc', - data: {}, - externalId: '', - externalType: '', - }, - aRUJ8rTJR9: { - id: 'aRUJ8rTJR9', - type: 'quote', - parent: '3EzeCrtxlh', - children: '877leNxAdX', - data: {}, - externalId: 'Qdn9CIuCJb', - externalType: 'text', - }, - '5OZNiernqA': { - id: '5OZNiernqA', - type: 'paragraph', - parent: '3EzeCrtxlh', - children: 'rnWU0OMG2D', - data: {}, - externalId: '7szeShePbX', - externalType: 'text', - }, - '6okS7LcJz6': { - id: '6okS7LcJz6', - type: 'paragraph', - parent: '3EzeCrtxlh', - children: 'o2VwjuyMqD', - data: {}, - externalId: 'T7KYfpQzkC', - externalType: 'text', - }, - '2qonPRrNTO': { - id: '2qonPRrNTO', - type: 'heading', - parent: '3EzeCrtxlh', - children: 'EwyNm_pVG3', - data: { level: 1 }, - externalId: 'zatA8Lta9U', - externalType: 'text', - }, - G6SPYNOXyd: { - id: 'G6SPYNOXyd', - type: 'heading', - parent: '3EzeCrtxlh', - children: 'dMAT_mdB3t', - data: { level: 2 }, - externalId: 'EZJU9Ks-XL', - externalType: 'text', - }, - 'i-7TRjZWn4': { - id: 'i-7TRjZWn4', - type: 'todo_list', - parent: '3EzeCrtxlh', - children: '3NsAmPWBLT', - data: { checked: true }, - externalId: 'rG9KOmfyQc', - externalType: 'text', - }, - mAce5pJ5iN: { - id: 'mAce5pJ5iN', - type: 'paragraph', - parent: '3EzeCrtxlh', - children: 'PavtRGeb_I', - data: {}, - externalId: 'UUBk3lnHDj', - externalType: 'text', - }, - ViWXVLgaux: { - id: 'ViWXVLgaux', - type: 'numbered_list', - parent: '3EzeCrtxlh', - children: 'osPbZZroOQ', - data: {}, - externalId: 'OkO9CIoWYX', - externalType: 'text', - }, - VrW0GWtvmq: { - id: 'VrW0GWtvmq', - type: 'todo_list', - parent: '3EzeCrtxlh', - children: 'hFX8bE3MQ6', - data: { checked: false }, - externalId: 'wBzhBx7bcM', - externalType: 'text', - }, - YzM4q9vJgy: { - id: 'YzM4q9vJgy', - type: 'paragraph', - parent: '3EzeCrtxlh', - children: 'mlDk334eJ5', - data: {}, - externalId: 'wIXo3cMKpn', - externalType: 'text', - }, - okBQecghDx: { - id: 'okBQecghDx', - type: 'todo_list', - parent: '3EzeCrtxlh', - children: 'r7U-ocUQEj', - data: { checked: false }, - externalId: '8T-1vemF9G', - externalType: 'text', - }, - qJcR2SnePa: { - id: 'qJcR2SnePa', - type: 'callout', - parent: '3EzeCrtxlh', - children: 'X8-C5rdFkI', - data: { icon: '🥰' }, - externalId: 'o3mqcqjEvX', - externalType: 'text', - }, - q8trFTc21J: { - id: 'q8trFTc21J', - type: 'numbered_list', - parent: '3EzeCrtxlh', - children: 'Ye_KrA1Zqb', - data: {}, - externalId: 'E-XQYK1KGP', - externalType: 'text', - }, - '-7trMtJMEt': { - id: '-7trMtJMEt', - type: 'numbered_list', - parent: '3EzeCrtxlh', - children: '5-RQuN9654', - data: {}, - externalId: 'meKXZh3E_1', - externalType: 'text', - }, - Fn4KACkt1i: { - id: 'Fn4KACkt1i', - type: 'todo_list', - parent: '3EzeCrtxlh', - children: 'MM6vCgc7RC', - data: { checked: false }, - externalId: 'v0XWYu0w3F', - externalType: 'text', - }, - TTU0eUzM4G: { - id: 'TTU0eUzM4G', - type: 'paragraph', - parent: '3EzeCrtxlh', - children: 'Jo1ix-lCgZ', - data: {}, - externalId: 'IRpzcwVaU4', - externalType: 'text', - }, - '6QZZccBfnT': { - id: '6QZZccBfnT', - type: 'heading', - parent: '3EzeCrtxlh', - children: 'bdI2gAQB-G', - data: { level: 2 }, - externalId: 'J-oMw2g2_D', - externalType: 'text', - }, - sZT5qbJvLX: { - id: 'sZT5qbJvLX', - type: 'paragraph', - parent: '3EzeCrtxlh', - children: 'yR1_d6lPtR', - data: {}, - externalId: 'anA255zYMV', - externalType: 'text', - }, - '3EzeCrtxlh': { - id: '3EzeCrtxlh', - type: 'page', - parent: '', - children: 'ChrjyUcqp5', - data: {}, - externalId: 'bGaty5Tv88', - externalType: 'text', - }, - 'b3G76VM-nh': { - id: 'b3G76VM-nh', - type: 'heading', - parent: '3EzeCrtxlh', - children: '7PDV2Ev8pz', - data: { level: 2 }, - externalId: 'baM4S6ohnQ', - externalType: 'text', - }, - CxPil0324P: { - id: 'CxPil0324P', - type: 'todo_list', - parent: '3EzeCrtxlh', - children: 'qJkq_FYLux', - data: { checked: false }, - externalId: 'LGUrob79hg', - externalType: 'text', - }, - }, - childrenMap: { - '-7trMtJMEt': [], - okBQecghDx: [], - PeUTr8lpaW: [], - '6QZZccBfnT': [], - aRUJ8rTJR9: [], - G6SPYNOXyd: [], - VrW0GWtvmq: [], - 'i-7TRjZWn4': [], - '6okS7LcJz6': [], - Fn4KACkt1i: [], - qJcR2SnePa: [], - sZT5qbJvLX: [], - '2qonPRrNTO': [], - CxPil0324P: [], - YzM4q9vJgy: [], - mAce5pJ5iN: [], - gCjs671FiD: [], - q8trFTc21J: [], - whGVOpFJzA: [], - '5OZNiernqA': [], - 'b3G76VM-nh': [], - TTU0eUzM4G: [], - '3EzeCrtxlh': [ - '2qonPRrNTO', - 'b3G76VM-nh', - 'CxPil0324P', - 'Fn4KACkt1i', - 'okBQecghDx', - 'VrW0GWtvmq', - 'i-7TRjZWn4', - '692ooXzoV-', - 'sZT5qbJvLX', - 'PeUTr8lpaW', - 'YzM4q9vJgy', - 'G6SPYNOXyd', - '-7trMtJMEt', - 'q8trFTc21J', - 'ViWXVLgaux', - 'whGVOpFJzA', - 'mAce5pJ5iN', - '6QZZccBfnT', - 'aRUJ8rTJR9', - 'gCjs671FiD', - 'qJcR2SnePa', - '5OZNiernqA', - 'TTU0eUzM4G', - '6okS7LcJz6', - ], - ViWXVLgaux: [], - '692ooXzoV-': [], - }, - relativeMap: { - '4yIcpjxFTQ': '692ooXzoV-', - Bj4M6midh3: 'gCjs671FiD', - eYqZSaUSrF: 'whGVOpFJzA', - '7gXZ4anHxc': 'PeUTr8lpaW', - '877leNxAdX': 'aRUJ8rTJR9', - rnWU0OMG2D: '5OZNiernqA', - o2VwjuyMqD: '6okS7LcJz6', - EwyNm_pVG3: '2qonPRrNTO', - dMAT_mdB3t: 'G6SPYNOXyd', - '3NsAmPWBLT': 'i-7TRjZWn4', - PavtRGeb_I: 'mAce5pJ5iN', - osPbZZroOQ: 'ViWXVLgaux', - hFX8bE3MQ6: 'VrW0GWtvmq', - mlDk334eJ5: 'YzM4q9vJgy', - 'r7U-ocUQEj': 'okBQecghDx', - 'X8-C5rdFkI': 'qJcR2SnePa', - Ye_KrA1Zqb: 'q8trFTc21J', - '5-RQuN9654': '-7trMtJMEt', - MM6vCgc7RC: 'Fn4KACkt1i', - 'Jo1ix-lCgZ': 'TTU0eUzM4G', - 'bdI2gAQB-G': '6QZZccBfnT', - yR1_d6lPtR: 'sZT5qbJvLX', - ChrjyUcqp5: '3EzeCrtxlh', - '7PDV2Ev8pz': 'b3G76VM-nh', - qJkq_FYLux: 'CxPil0324P', - }, - deltaMap: { - gCjs671FiD: [], - G6SPYNOXyd: [{ insert: 'Keyboard shortcuts, markdown, and code block' }], - VrW0GWtvmq: [ - { insert: 'Type ' }, - { insert: '/', attributes: { code: true } }, - { insert: ' followed by ' }, - { insert: '/bullet', attributes: { code: true } }, - { insert: ' or ' }, - { insert: '/num', attributes: { code: true } }, - { insert: ' to create a list.', attributes: { code: false } }, - ], - '-7trMtJMEt': [ - { insert: 'Keyboard shortcuts ' }, - { - insert: 'guide', - attributes: { href: 'https://appflowy.gitbook.io/docs/essential-documentation/shortcuts' }, - }, - ], - okBQecghDx: [ - { insert: 'As soon as you type ' }, - { insert: '/', attributes: { font_color: '0xff00b5ff', code: true } }, - { insert: ' a menu will pop up. Select ' }, - { insert: 'different types', attributes: { bg_color: '0x4d9c27b0' } }, - { insert: ' of content blocks you can add.' }, - ], - qJcR2SnePa: [ - { insert: '\nLike AppFlowy? Follow us:\n' }, - { insert: 'GitHub', attributes: { href: 'https://github.com/AppFlowy-IO/AppFlowy' } }, - { insert: '\n' }, - { insert: 'Twitter', attributes: { href: 'https://twitter.com/appflowy' } }, - { insert: ': @appflowy\n' }, - { insert: 'Newsletter', attributes: { href: 'https://blog-appflowy.ghost.io/' } }, - { insert: '\n' }, - ], - whGVOpFJzA: [ - { - insert: - '// This is the main function.\nfn main() {\n // Print text to the console.\n println!("Hello World!");\n}', - }, - ], - YzM4q9vJgy: [], - '692ooXzoV-': [ - { insert: 'Click ' }, - { insert: '+', attributes: { code: true } }, - { insert: ' next to any page title in the sidebar to ' }, - { insert: 'quickly', attributes: { font_color: '0xff8427e0' } }, - { insert: ' add a new subpage, ' }, - { insert: 'Document', attributes: { code: true } }, - { insert: ', ', attributes: { code: false } }, - { insert: 'Grid', attributes: { code: true } }, - { insert: ', or ', attributes: { code: false } }, - { insert: 'Kanban Board', attributes: { code: true } }, - { insert: '.', attributes: { code: false } }, - ], - q8trFTc21J: [ - { insert: 'Markdown ' }, - { - insert: 'reference', - attributes: { href: 'https://appflowy.gitbook.io/docs/essential-documentation/markdown' }, - }, - ], - ViWXVLgaux: [ - { insert: 'Type ' }, - { insert: '/code', attributes: { code: true } }, - { insert: ' to insert a code block', attributes: { code: false } }, - ], - sZT5qbJvLX: [], - '6QZZccBfnT': [{ insert: 'Have a question❓' }], - '5OZNiernqA': [], - '6okS7LcJz6': [], - mAce5pJ5iN: [], - aRUJ8rTJR9: [ - { insert: 'Click ' }, - { insert: '?', attributes: { code: true } }, - { insert: ' at the bottom right for help and support.' }, - ], - Fn4KACkt1i: [ - { insert: 'Highlight ', attributes: { bg_color: '0x4dffeb3b' } }, - { insert: 'any text, and use the editing menu to ' }, - { insert: 'style', attributes: { italic: true } }, - { insert: ' ' }, - { insert: 'your', attributes: { bold: true } }, - { insert: ' ' }, - { insert: 'writing', attributes: { underline: true } }, - { insert: ' ' }, - { insert: 'however', attributes: { code: true } }, - { insert: ' you ' }, - { insert: 'like.', attributes: { strikethrough: true } }, - ], - '3EzeCrtxlh': [], - '2qonPRrNTO': [{ insert: 'Welcome to AppFlowy!' }], - 'b3G76VM-nh': [{ insert: 'Here are the basics' }], - TTU0eUzM4G: [], - CxPil0324P: [{ insert: 'Click anywhere and just start typing.' }], - 'i-7TRjZWn4': [ - { insert: 'Click ' }, - { insert: '+ New Page ', attributes: { code: true } }, - { insert: 'button at the bottom of your sidebar to add a new page.' }, - ], - }, - externalIdMap: { - '9L10h3UZ7J': '692ooXzoV-', - uGR_eATq2B: 'gCjs671FiD', - '2H8dnFhOsJ': 'whGVOpFJzA', - Qdn9CIuCJb: 'aRUJ8rTJR9', - '7szeShePbX': '5OZNiernqA', - T7KYfpQzkC: '6okS7LcJz6', - zatA8Lta9U: '2qonPRrNTO', - 'EZJU9Ks-XL': 'G6SPYNOXyd', - rG9KOmfyQc: 'i-7TRjZWn4', - UUBk3lnHDj: 'mAce5pJ5iN', - OkO9CIoWYX: 'ViWXVLgaux', - wBzhBx7bcM: 'VrW0GWtvmq', - wIXo3cMKpn: 'YzM4q9vJgy', - '8T-1vemF9G': 'okBQecghDx', - o3mqcqjEvX: 'qJcR2SnePa', - 'E-XQYK1KGP': 'q8trFTc21J', - meKXZh3E_1: '-7trMtJMEt', - v0XWYu0w3F: 'Fn4KACkt1i', - IRpzcwVaU4: 'TTU0eUzM4G', - 'J-oMw2g2_D': '6QZZccBfnT', - anA255zYMV: 'sZT5qbJvLX', - bGaty5Tv88: '3EzeCrtxlh', - baM4S6ohnQ: 'b3G76VM-nh', - LGUrob79hg: 'CxPil0324P', - }, -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/provider/__tests__/utils/convert.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/provider/__tests__/utils/convert.ts deleted file mode 100644 index 028fff7419..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/provider/__tests__/utils/convert.ts +++ /dev/null @@ -1,76 +0,0 @@ -/** - * @jest-environment jsdom - */ -import { slateNodesToInsertDelta } from '@slate-yjs/core'; -import * as Y from 'yjs'; -import { generateId } from '$app/components/editor/provider/utils/convert'; - -export function slateElementToYText({ - children, - ...attributes -}: { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - children: any; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - [key: string]: any; -}) { - const yElement = new Y.XmlText(); - - Object.entries(attributes).forEach(([key, value]) => { - yElement.setAttribute(key, value); - }); - yElement.applyDelta(slateNodesToInsertDelta(children), { - sanitize: false, - }); - return yElement; -} - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export function generateInsertTextOp(text: string) { - const insertYText = slateElementToYText({ - children: [ - { - type: 'text', - textId: generateId(), - children: [ - { - text, - }, - ], - }, - ], - type: 'paragraph', - data: {}, - blockId: generateId(), - }); - - return { - insert: insertYText, - }; -} - -export function genersteMentionInsertTextOp() { - const mentionYText = slateElementToYText({ - children: [{ text: '@' }], - type: 'mention', - data: { - page: 'page_id', - }, - }); - - return { - insert: mentionYText, - }; -} - -export function generateFormulaInsertTextOp() { - const formulaYText = slateElementToYText({ - children: [{ text: '= 1 + 1' }], - type: 'formula', - data: true, - }); - - return { - insert: formulaYText, - }; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/provider/__tests__/utils/mockBackendService.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/provider/__tests__/utils/mockBackendService.ts deleted file mode 100644 index 3bd7646268..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/provider/__tests__/utils/mockBackendService.ts +++ /dev/null @@ -1,21 +0,0 @@ -import read_me from '$app/components/editor/provider/__tests__/read_me'; - -const applyActions = jest.fn().mockReturnValue(Promise.resolve()); - -jest.mock('$app/application/notification', () => { - return { - subscribeNotification: jest.fn().mockReturnValue(Promise.resolve(() => ({}))), - }; -}); - -jest.mock('nanoid', () => ({ nanoid: jest.fn().mockReturnValue(String(Math.random())) })); - -jest.mock('$app/application/document/document.service', () => { - return { - openDocument: jest.fn().mockReturnValue(Promise.resolve(read_me)), - applyActions, - closeDocument: jest.fn().mockReturnValue(Promise.resolve()), - }; -}); - -export { applyActions }; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/provider/data_client.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/provider/data_client.ts deleted file mode 100644 index bf0ea2c2a7..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/provider/data_client.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { applyActions, closeDocument, openDocument } from '$app/application/document/document.service'; -import { slateNodesToInsertDelta } from '@slate-yjs/core'; -import { convertToSlateValue } from '$app/components/editor/provider/utils/convert'; -import { EventEmitter } from 'events'; -import { BlockActionPB, DocEventPB, DocumentNotification } from '@/services/backend'; -import { AsyncQueue } from '$app/utils/async_queue'; -import { subscribeNotification } from '$app/application/notification'; -import { YDelta } from '$app/components/editor/provider/types/y_event'; -import { DocEvent2YDelta } from '$app/components/editor/provider/utils/delta'; - -export class DataClient extends EventEmitter { - private queue: AsyncQueue[]>; - private unsubscribe: Promise<() => void>; - public rootId?: string; - - constructor(private id: string) { - super(); - this.queue = new AsyncQueue(this.sendActions); - this.unsubscribe = subscribeNotification(DocumentNotification.DidReceiveUpdate, this.sendMessage); - - this.on('update', this.handleReceiveMessage); - } - - public disconnect() { - this.off('update', this.handleReceiveMessage); - void closeDocument(this.id); - void this.unsubscribe.then((unsubscribe) => unsubscribe()); - } - - public async getInsertDelta(includeRoot = true) { - const data = await openDocument(this.id); - - this.rootId = data.rootId; - - const slateValue = convertToSlateValue(data, includeRoot); - - return slateNodesToInsertDelta(slateValue); - } - - public on(event: 'change', listener: (events: YDelta) => void): this; - public on(event: 'update', listener: (actions: ReturnType[]) => void): this; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - public on(event: string, listener: (...args: any[]) => void): this { - return super.on(event, listener); - } - - public off(event: 'change', listener: (events: YDelta) => void): this; - public off(event: 'update', listener: (actions: ReturnType[]) => void): this; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - public off(event: string, listener: (...args: any[]) => void): this { - return super.off(event, listener); - } - - public emit(event: 'change', events: YDelta): boolean; - public emit(event: 'update', actions: ReturnType[]): boolean; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - public emit(event: string, ...args: any[]): boolean { - return super.emit(event, ...args); - } - - private sendMessage = (docEvent: DocEventPB) => { - // transform events to ops - this.emit('change', DocEvent2YDelta(docEvent)); - }; - - private handleReceiveMessage = (actions: ReturnType[]) => { - this.queue.enqueue(actions); - }; - - private sendActions = async (actions: ReturnType[]) => { - if (!actions.length) return; - await applyActions(this.id, actions); - }; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/provider/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/provider/index.ts deleted file mode 100644 index 03be03e588..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/provider/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './provider'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/provider/provider.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/provider/provider.ts deleted file mode 100644 index 727b33ec69..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/provider/provider.ts +++ /dev/null @@ -1,74 +0,0 @@ -import * as Y from 'yjs'; - -import { DataClient } from '$app/components/editor/provider/data_client'; -import { YDelta } from '$app/components/editor/provider/types/y_event'; -import { YEvents2BlockActions } from '$app/components/editor/provider/utils/action'; -import { EventEmitter } from 'events'; - -const REMOTE_ORIGIN = 'remote'; - -export class Provider extends EventEmitter { - document: Y.Doc = new Y.Doc(); - sharedType: Y.XmlText | null = null; - dataClient: DataClient; - // get origin data after document updated - backupDoc: Y.Doc = new Y.Doc(); - constructor(public id: string) { - super(); - this.dataClient = new DataClient(id); - this.document.on('update', this.documentUpdate); - } - - initialDocument = async (includeRoot = true) => { - const sharedType = this.document.get('sharedType', Y.XmlText) as Y.XmlText; - // Load the initial value into the yjs document - const delta = await this.dataClient.getInsertDelta(includeRoot); - - sharedType.applyDelta(delta); - - const rootId = this.dataClient.rootId as string; - const root = delta[0].insert as Y.XmlText; - const data = root.getAttribute('data'); - - sharedType.setAttribute('blockId', rootId); - sharedType.setAttribute('data', data); - - this.sharedType = sharedType; - this.sharedType?.observeDeep(this.onChange); - this.emit('ready'); - }; - - connect() { - this.dataClient.on('change', this.onRemoteChange); - return; - } - - disconnect() { - this.dataClient.off('change', this.onRemoteChange); - this.dataClient.disconnect(); - this.sharedType?.unobserveDeep(this.onChange); - this.sharedType = null; - } - - onChange = (events: Y.YEvent[], transaction: Y.Transaction) => { - if (transaction.origin === REMOTE_ORIGIN) { - return; - } - - if (!this.sharedType || !events.length) return; - // transform events to actions - this.dataClient.emit('update', YEvents2BlockActions(this.backupDoc, events)); - }; - - onRemoteChange = (delta: YDelta) => { - if (!delta.length) return; - - this.document.transact(() => { - this.sharedType?.applyDelta(delta); - }, REMOTE_ORIGIN); - }; - - documentUpdate = (update: Uint8Array) => { - Y.applyUpdate(this.backupDoc, update); - }; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/provider/types/y_event.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/provider/types/y_event.ts deleted file mode 100644 index 36ec97aa39..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/provider/types/y_event.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { YXmlText } from 'yjs/dist/src/types/YXmlText'; - -export interface YOp { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - insert?: string | object | any[] | YXmlText | undefined; - retain?: number | undefined; - delete?: number | undefined; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - attributes?: { [p: string]: any } | undefined; -} - -export type YDelta = YOp[]; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/provider/utils/action.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/provider/utils/action.ts deleted file mode 100644 index 447a8f95f9..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/provider/utils/action.ts +++ /dev/null @@ -1,301 +0,0 @@ -import * as Y from 'yjs'; -import { BlockActionPB, BlockActionTypePB } from '@/services/backend'; -import { generateId } from '$app/components/editor/provider/utils/convert'; -import { YDelta2Delta } from '$app/components/editor/provider/utils/delta'; -import { YDelta } from '$app/components/editor/provider/types/y_event'; -import { getInsertTarget, getYTarget } from '$app/components/editor/provider/utils/relation'; -import { EditorInlineNodeType, EditorNodeType } from '$app/application/document/document.types'; -import { Log } from '$app/utils/log'; - -export function YEvents2BlockActions( - backupDoc: Readonly, - events: Y.YEvent[] -): ReturnType[] { - const actions: ReturnType[] = []; - - events.forEach((event) => { - const eventActions = YEvent2BlockActions(backupDoc, event); - - if (eventActions.length === 0) return; - - actions.push(...eventActions); - }); - - return actions; -} - -export function YEvent2BlockActions( - backupDoc: Readonly, - event: Y.YEvent -): ReturnType[] { - const { target: yXmlText, keys, delta, path } = event; - const isBlockEvent = !!yXmlText.getAttribute('blockId'); - const sharedType = backupDoc.get('sharedType', Y.XmlText) as Readonly; - const rootId = sharedType.getAttribute('blockId'); - - const backupTarget = getYTarget(backupDoc, path) as Readonly; - const actions = []; - - if ([EditorInlineNodeType.Formula, EditorInlineNodeType.Mention].includes(yXmlText.getAttribute('type'))) { - const parentYXmlText = yXmlText.parent as Y.XmlText; - const parentDelta = parentYXmlText.toDelta() as YDelta; - const index = parentDelta.findIndex((op) => op.insert === yXmlText); - const ops = YDelta2Delta(parentDelta); - - const retainIndex = ops.reduce((acc, op, currentIndex) => { - if (currentIndex < index) { - return acc + (op.insert as string).length ?? 0; - } - - return acc; - }, 0); - - const newDelta = [ - { - retain: retainIndex, - }, - ...delta, - ]; - - actions.push(...generateApplyTextActions(parentYXmlText, newDelta)); - } - - if (yXmlText.getAttribute('type') === 'text') { - actions.push(...textOps2BlockActions(rootId, yXmlText, delta)); - } - - if (keys.size > 0) { - actions.push(...dataOps2BlockActions(yXmlText, keys)); - } - - if (isBlockEvent) { - actions.push(...blockOps2BlockActions(backupTarget, delta)); - } - - return actions; -} - -function textOps2BlockActions( - rootId: string, - yXmlText: Y.XmlText, - ops: YDelta -): ReturnType[] { - if (ops.length === 0) return []; - const blockYXmlText = yXmlText.parent as Y.XmlText; - const blockId = blockYXmlText.getAttribute('blockId'); - - if (blockId === rootId) { - return []; - } - - return generateApplyTextActions(yXmlText, ops); -} - -function dataOps2BlockActions( - yXmlText: Y.XmlText, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - keys: Map -) { - const dataUpdated = keys.has('data'); - - if (!dataUpdated) return []; - const data = yXmlText.getAttribute('data'); - - return generateUpdateActions(yXmlText, { - data, - }); -} - -function blockOps2BlockActions( - blockYXmlText: Readonly, - ops: YDelta -): ReturnType[] { - const actions: ReturnType[] = []; - - let index = 0; - - ops.forEach((op) => { - if (op.insert) { - if (op.insert instanceof Y.XmlText) { - const insertYXmlText = op.insert; - const blockId = insertYXmlText.getAttribute('blockId'); - const textId = insertYXmlText.getAttribute('textId'); - - if (!blockId && !textId) { - throw new Error('blockId and textId is not exist'); - } - - if (blockId) { - actions.push(...generateInsertBlockActions(insertYXmlText)); - index += 1; - } - - if (textId) { - const target = getInsertTarget(blockYXmlText, [0]); - - if (target) { - const length = target.length; - - const delta = [{ delete: length }, ...insertYXmlText.toDelta()]; - - // restore textId - insertYXmlText.setAttribute('textId', target.getAttribute('textId')); - actions.push(...generateApplyTextActions(target, delta)); - } - } - } - } else if (op.retain) { - index += op.retain; - } else if (op.delete) { - let i = 0; - - for (; i < op.delete; i++) { - const target = getInsertTarget(blockYXmlText, [i + index]); - - if (target && target !== blockYXmlText) { - const deletedId = target.getAttribute('blockId') as string; - - if (deletedId) { - actions.push( - ...generateDeleteBlockActions({ - ids: [deletedId], - }) - ); - } else { - Log.error('blockOps2BlockActions', 'deletedId is not exist'); - } - } - } - - index += i; - } - }); - - return actions; -} - -export function generateUpdateActions( - yXmlText: Y.XmlText, - { - data, - }: { - data?: Record; - external_id?: string; - } -) { - const id = yXmlText.getAttribute('blockId'); - const parentId = yXmlText.getAttribute('parentId'); - - return [ - { - action: BlockActionTypePB.Update, - payload: { - block: { - id, - data: JSON.stringify(data), - }, - parent_id: parentId, - }, - }, - ]; -} - -export function generateApplyTextActions(yXmlText: Y.XmlText, delta: YDelta) { - const externalId = yXmlText.getAttribute('textId'); - - if (!externalId) return []; - - const deltaString = JSON.stringify(YDelta2Delta(delta)); - - return [ - { - action: BlockActionTypePB.ApplyTextDelta, - payload: { - text_id: externalId, - delta: deltaString, - }, - }, - ]; -} - -export function generateDeleteBlockActions({ ids }: { ids: string[] }) { - return ids.map((id) => ({ - action: BlockActionTypePB.Delete, - payload: { - block: { - id, - }, - parent_id: '', - }, - })); -} - -export function generateInsertTextActions(insertYXmlText: Y.XmlText) { - const textId = insertYXmlText.getAttribute('textId'); - const delta = YDelta2Delta(insertYXmlText.toDelta()); - - return [ - { - action: BlockActionTypePB.InsertText, - payload: { - text_id: textId, - delta: JSON.stringify(delta), - }, - }, - ]; -} - -export function generateInsertBlockActions( - insertYXmlText: Y.XmlText -): ReturnType[] { - const childrenId = generateId(); - - const [textInsert, ...childrenInserts] = (insertYXmlText.toDelta() as YDelta).map((op) => op.insert); - const textInsertActions = textInsert instanceof Y.XmlText ? generateInsertTextActions(textInsert) : []; - const externalId = textInsertActions[0]?.payload.text_id; - const prev = insertYXmlText.prevSibling; - const prevId = prev ? prev.getAttribute('blockId') : null; - const parentId = (insertYXmlText.parent as Y.XmlText).getAttribute('blockId'); - - const data = insertYXmlText.getAttribute('data'); - const type = insertYXmlText.getAttribute('type'); - const id = insertYXmlText.getAttribute('blockId'); - - if (!id) { - Log.error('generateInsertBlockActions', 'id is not exist'); - return []; - } - - if (!type || type === 'text' || Object.values(EditorNodeType).indexOf(type) === -1) { - Log.error('generateInsertBlockActions', 'type is error: ' + type); - return []; - } - - const actions: ReturnType[] = [ - ...textInsertActions, - { - action: BlockActionTypePB.Insert, - payload: { - block: { - id, - data: JSON.stringify(data), - ty: type, - parent_id: parentId, - children_id: childrenId, - external_id: externalId, - external_type: externalId ? 'text' : undefined, - }, - prev_id: prevId, - parent_id: parentId, - }, - }, - ]; - - childrenInserts.forEach((insert) => { - if (insert instanceof Y.XmlText) { - actions.push(...generateInsertBlockActions(insert)); - } - }); - - return actions; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/provider/utils/convert.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/provider/utils/convert.ts deleted file mode 100644 index b4da4b3ca7..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/provider/utils/convert.ts +++ /dev/null @@ -1,154 +0,0 @@ -import { nanoid } from 'nanoid'; -import { EditorData, EditorInlineNodeType, Mention } from '$app/application/document/document.types'; -import { Element, Text } from 'slate'; -import { Op } from 'quill-delta'; - -export function generateId() { - return nanoid(10); -} - -export function transformToInlineElement(op: Op): Element[] { - const attributes = op.attributes; - - if (!attributes) return []; - const { formula, mention, ...attrs } = attributes; - - if (formula) { - const texts = (op.insert as string).split(''); - - return texts.map((text) => { - return { - type: EditorInlineNodeType.Formula, - data: formula, - children: [ - { - text, - ...attrs, - }, - ], - }; - }); - } - - if (mention) { - const texts = (op.insert as string).split(''); - - return texts.map((text) => { - return { - type: EditorInlineNodeType.Mention, - children: [ - { - text, - ...attrs, - }, - ], - data: { - ...(mention as Mention), - }, - }; - }); - } - - return []; -} - -export function getInlinesWithDelta(delta?: Op[]): (Text | Element)[] { - const newDelta: (Text | Element)[] = []; - - if (!delta || !delta.length) - return [ - { - text: '', - }, - ]; - - delta.forEach((op) => { - const matchInlines = transformToInlineElement(op); - - if (matchInlines.length > 0) { - newDelta.push(...matchInlines); - return; - } - - if (op.attributes) { - if ('font_color' in op.attributes && op.attributes['font_color'] === '') { - delete op.attributes['font_color']; - } - - if ('bg_color' in op.attributes && op.attributes['bg_color'] === '') { - delete op.attributes['bg_color']; - } - - if ('code' in op.attributes && !op.attributes['code']) { - delete op.attributes['code']; - } - } - - newDelta.push({ - text: op.insert as string, - ...op.attributes, - }); - }); - - return newDelta; -} - -export function convertToSlateValue(data: EditorData, includeRoot: boolean): Element[] { - const traverse = (id: string, isRoot = false) => { - const node = data.nodeMap[id]; - const delta = data.deltaMap[id]; - - const slateNode: Element = { - type: node.type, - data: node.data, - children: [], - blockId: id, - }; - - const textNode: Element | null = - !isRoot && node.externalId - ? { - type: 'text', - children: [], - textId: node.externalId, - } - : null; - - const inlineNodes = getInlinesWithDelta(delta); - - textNode?.children.push(...inlineNodes); - - const children = data.childrenMap[id]; - - slateNode.children = children.map((childId) => traverse(childId)); - if (textNode) { - slateNode.children.unshift(textNode); - } - - return slateNode; - }; - - const rootId = data.rootId; - - const root = traverse(rootId, true); - - const nodes = root.children as Element[]; - - if (includeRoot) { - nodes.unshift({ - ...root, - children: [ - { - type: 'text', - children: [ - { - text: '', - }, - ], - }, - ], - }); - } - - return nodes; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/provider/utils/delta.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/provider/utils/delta.ts deleted file mode 100644 index 630b6fbdf5..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/provider/utils/delta.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { YDelta, YOp } from '$app/components/editor/provider/types/y_event'; -import { Op } from 'quill-delta'; -import * as Y from 'yjs'; -import { inlineNodeTypes } from '$app/application/document/document.types'; -import { DocEventPB } from '@/services/backend'; - -export function YDelta2Delta(yDelta: YDelta): Op[] { - const ops: Op[] = []; - - yDelta.forEach((op) => { - if (op.insert instanceof Y.XmlText) { - const type = op.insert.getAttribute('type'); - - if (inlineNodeTypes.includes(type)) { - ops.push(...YInlineOp2Op(op)); - return; - } - } - - ops.push(op as Op); - }); - return ops; -} - -export function YInlineOp2Op(yOp: YOp): Op[] { - if (!(yOp.insert instanceof Y.XmlText)) { - return [ - { - insert: yOp.insert as string, - attributes: yOp.attributes, - }, - ]; - } - - const type = yOp.insert.getAttribute('type'); - const data = yOp.insert.getAttribute('data'); - - const delta = yOp.insert.toDelta() as Op[]; - - return delta.map((op) => ({ - insert: op.insert, - - attributes: { - [type]: data, - ...op.attributes, - }, - })); -} - -export function DocEvent2YDelta(events: DocEventPB): YDelta { - if (!events.is_remote) return []; - - return []; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/provider/utils/relation.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/provider/utils/relation.ts deleted file mode 100644 index 72b2e126df..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/provider/utils/relation.ts +++ /dev/null @@ -1,24 +0,0 @@ -import * as Y from 'yjs'; - -export function getInsertTarget(root: Y.XmlText, path: (string | number)[]): Y.XmlText { - const delta = root.toDelta(); - const index = path[0]; - - const current = delta[index]; - - if (current && current.insert instanceof Y.XmlText) { - if (path.length === 1) { - return current.insert; - } - - return getInsertTarget(current.insert, path.slice(1)); - } - - return root; -} - -export function getYTarget(doc: Y.Doc, path: (string | number)[]) { - const sharedType = doc.get('sharedType', Y.XmlText) as Y.XmlText; - - return getInsertTarget(sharedType, path); -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/stores/block.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/stores/block.ts deleted file mode 100644 index 00992964fb..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/stores/block.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { createContext, useCallback, useContext, useMemo } from 'react'; -import { proxy, useSnapshot } from 'valtio'; -import { EditorNodeType } from '$app/application/document/document.types'; - -export interface EditorBlockState { - [EditorNodeType.ImageBlock]: { - popoverOpen: boolean; - blockId?: string; - }; - [EditorNodeType.EquationBlock]: { - popoverOpen: boolean; - blockId?: string; - }; -} - -const initialState = { - [EditorNodeType.ImageBlock]: { - popoverOpen: false, - blockId: undefined, - }, - [EditorNodeType.EquationBlock]: { - popoverOpen: false, - blockId: undefined, - }, -}; - -export const EditorBlockStateContext = createContext(initialState); - -export const EditorBlockStateProvider = EditorBlockStateContext.Provider; - -export function useEditorInitialBlockState() { - const state = useMemo(() => { - return proxy({ - ...initialState, - }); - }, []); - - return state; -} - -export function useEditorBlockState(key: EditorNodeType.ImageBlock | EditorNodeType.EquationBlock) { - const context = useContext(EditorBlockStateContext); - - return useSnapshot(context[key]); -} - -export function useEditorBlockDispatch() { - const context = useContext(EditorBlockStateContext); - - const openPopover = useCallback( - (key: EditorNodeType.ImageBlock | EditorNodeType.EquationBlock, blockId: string) => { - context[key].popoverOpen = true; - context[key].blockId = blockId; - }, - [context] - ); - - const closePopover = useCallback( - (key: EditorNodeType.ImageBlock | EditorNodeType.EquationBlock) => { - context[key].popoverOpen = false; - context[key].blockId = undefined; - }, - [context] - ); - - return { - openPopover, - closePopover, - }; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/stores/decorate.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/stores/decorate.ts deleted file mode 100644 index 078296aade..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/stores/decorate.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { createContext, useCallback, useContext, useMemo } from 'react'; -import { BaseRange, Editor, NodeEntry, Range } from 'slate'; -import { proxySet } from 'valtio/utils'; -import { useSnapshot } from 'valtio'; -import { ReactEditor } from 'slate-react'; - -export const DecorateStateContext = createContext< - Set<{ - range: BaseRange; - class_name: string; - type?: 'link'; - }> ->(new Set()); -export const DecorateStateProvider = DecorateStateContext.Provider; - -export function useInitialDecorateState(editor: ReactEditor) { - const decorateState = useMemo( - () => - proxySet<{ - range: BaseRange; - class_name: string; - }>([]), - [] - ); - - const ranges = useSnapshot(decorateState); - - const decorate = useCallback( - ([, path]: NodeEntry): BaseRange[] => { - const highlightRanges: (Range & { - class_name: string; - })[] = []; - - ranges.forEach((state) => { - const intersection = Range.intersection(state.range, Editor.range(editor, path)); - - if (intersection) { - highlightRanges.push({ - ...intersection, - class_name: state.class_name, - }); - } - }); - - return highlightRanges; - }, - [editor, ranges] - ); - - return { - decorate, - decorateState, - }; -} - -export function useDecorateState(type?: 'link') { - const context = useContext(DecorateStateContext); - - const state = useSnapshot(context); - - return useMemo(() => { - return Array.from(state).find((s) => !type || s.type === type); - }, [state, type]); -} - -export function useDecorateDispatch() { - const context = useContext(DecorateStateContext); - - const getStaticState = useCallback(() => { - return Array.from(context)[0]; - }, [context]); - - const add = useCallback( - (state: { range: BaseRange; class_name: string; type?: 'link' }) => { - context.add(state); - }, - [context] - ); - - const clear = useCallback(() => { - context.clear(); - }, [context]); - - return { - add, - clear, - getStaticState, - }; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/stores/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/stores/index.ts deleted file mode 100644 index 22f0bb81be..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/stores/index.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { ReactEditor } from 'slate-react'; -import { useInitialDecorateState } from '$app/components/editor/stores/decorate'; -import { useInitialSelectedBlocks } from '$app/components/editor/stores/selected'; -import { useInitialSlashState } from '$app/components/editor/stores/slash'; -import { useInitialEditorInlineBlockState } from '$app/components/editor/stores/inline_node'; -import { useEditorInitialBlockState } from '$app/components/editor/stores/block'; - -export * from './decorate'; -export * from './selected'; -export * from './slash'; -export * from './inline_node'; - -export function useInitialEditorState(editor: ReactEditor) { - const { decorate, decorateState } = useInitialDecorateState(editor); - const selectedBlocks = useInitialSelectedBlocks(editor); - const slashState = useInitialSlashState(); - const inlineBlockState = useInitialEditorInlineBlockState(); - const blockState = useEditorInitialBlockState(); - - return { - selectedBlocks, - decorate, - decorateState, - slashState, - inlineBlockState, - blockState, - }; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/stores/inline_node.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/stores/inline_node.ts deleted file mode 100644 index 6607a546d8..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/stores/inline_node.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { createContext, useCallback, useContext, useMemo } from 'react'; -import { BaseRange, Path } from 'slate'; -import { proxy, useSnapshot } from 'valtio'; - -export interface EditorInlineBlockState { - formula: { - popoverOpen: boolean; - range?: BaseRange; - }; -} -const initialState = { - formula: { - popoverOpen: false, - range: undefined, - }, -}; - -export const EditorInlineBlockStateContext = createContext(initialState); - -export const EditorInlineBlockStateProvider = EditorInlineBlockStateContext.Provider; - -export function useInitialEditorInlineBlockState() { - const state = useMemo(() => { - return proxy({ - ...initialState, - }); - }, []); - - return state; -} - -export function useEditorInlineBlockState(key: 'formula') { - const context = useContext(EditorInlineBlockStateContext); - - const state = useSnapshot(context[key]); - - const openPopover = useCallback(() => { - context[key].popoverOpen = true; - }, [context, key]); - - const closePopover = useCallback(() => { - context[key].popoverOpen = false; - }, [context, key]); - - const setRange = useCallback( - (at: BaseRange | Path) => { - const range = Path.isPath(at) ? { anchor: at, focus: at } : at; - - context[key].range = range as BaseRange; - }, - [context, key] - ); - - return { - ...state, - openPopover, - closePopover, - setRange, - }; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/stores/selected.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/stores/selected.ts deleted file mode 100644 index 803f474723..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/stores/selected.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { createContext, useEffect, useMemo, useState } from 'react'; -import { proxySet, subscribeKey } from 'valtio/utils'; -import { ReactEditor } from 'slate-react'; -import { Element } from 'slate'; - -export function useInitialSelectedBlocks(editor: ReactEditor) { - const selectedBlocks = useMemo(() => proxySet([]), []); - const [selectedLength, setSelectedLength] = useState(0); - - subscribeKey(selectedBlocks, 'size', (v) => setSelectedLength(v)); - - useEffect(() => { - const { onChange } = editor; - - const onKeydown = (e: KeyboardEvent) => { - if (!ReactEditor.isFocused(editor) && selectedLength > 0) { - e.preventDefault(); - e.stopPropagation(); - const selectedBlockId = selectedBlocks.values().next().value; - const [selectedBlock] = editor.nodes({ - at: [], - match: (n) => Element.isElement(n) && n.blockId === selectedBlockId, - }); - const [, path] = selectedBlock; - - editor.select(path); - ReactEditor.focus(editor); - } - }; - - if (selectedLength > 0) { - editor.onChange = (...args) => { - const isSelectionChange = editor.operations.every((arg) => arg.type === 'set_selection'); - - if (isSelectionChange) { - selectedBlocks.clear(); - } - - onChange(...args); - }; - - document.addEventListener('keydown', onKeydown); - } else { - editor.onChange = onChange; - document.removeEventListener('keydown', onKeydown); - } - - return () => { - editor.onChange = onChange; - document.removeEventListener('keydown', onKeydown); - }; - }, [editor, selectedBlocks, selectedLength]); - - return selectedBlocks; -} - -export const EditorSelectedBlockContext = createContext>(new Set()); -export const EditorSelectedBlockProvider = EditorSelectedBlockContext.Provider; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/stores/slash.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/stores/slash.ts deleted file mode 100644 index 13e9447d0c..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/stores/slash.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { createContext, useCallback, useContext, useMemo } from 'react'; -import { proxyMap } from 'valtio/utils'; -import { useSnapshot } from 'valtio'; - -export const SlashStateContext = createContext>(new Map()); -export const SlashStateProvider = SlashStateContext.Provider; - -export function useInitialSlashState() { - const state = useMemo(() => proxyMap([['open', false]]), []); - - return state; -} - -export function useSlashState() { - const context = useContext(SlashStateContext); - const state = useSnapshot(context); - const open = state.get('open'); - - const setOpen = useCallback( - (open: boolean) => { - context.set('open', open); - }, - [context] - ); - - return { - open, - setOpen, - }; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/error/Error.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/error/Error.hooks.ts deleted file mode 100644 index ceaa5a51a0..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/error/Error.hooks.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { useAppDispatch, useAppSelector } from '$app/stores/store'; -import { errorActions } from '$app_reducers/error/slice'; -import { useCallback, useEffect, useState } from 'react'; - -export const useError = (e: Error) => { - const dispatch = useAppDispatch(); - const error = useAppSelector((state) => state.error); - const [errorMessage, setErrorMessage] = useState(''); - const [displayError, setDisplayError] = useState(false); - - useEffect(() => { - setDisplayError(error.display); - setErrorMessage(error.message); - }, [error]); - - const showError = useCallback( - (msg: string) => { - dispatch(errorActions.showError(msg)); - }, - [dispatch] - ); - - useEffect(() => { - if (e) { - showError(e.message); - } - }, [e, showError]); - - const hideError = () => { - dispatch(errorActions.hideError()); - }; - - return { - showError, - hideError, - errorMessage, - displayError, - }; -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/error/ErrorHandlerPage.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/error/ErrorHandlerPage.tsx deleted file mode 100644 index 1bb15f2ca3..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/error/ErrorHandlerPage.tsx +++ /dev/null @@ -1,8 +0,0 @@ -import { useError } from './Error.hooks'; -import { ErrorModal } from './ErrorModal'; - -export const ErrorHandlerPage = ({ error }: { error: Error }) => { - const { hideError, errorMessage, displayError } = useError(error); - - return displayError ? : <>; -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/error/ErrorModal.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/error/ErrorModal.tsx deleted file mode 100644 index 6da2ee96d0..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/error/ErrorModal.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { ReactComponent as InformationSvg } from '$app/assets/information.svg'; -import { ReactComponent as CloseSvg } from '$app/assets/close.svg'; - -export const ErrorModal = ({ message, onClose }: { message: string; onClose: () => void }) => { - return ( -
-
- -
- -
-

Oops.. something went wrong

-

{message}

-
-
- ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/index.ts deleted file mode 100644 index cb0ff5c3b5..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/index.ts +++ /dev/null @@ -1 +0,0 @@ -export {}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/FooterPanel.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/FooterPanel.tsx deleted file mode 100644 index 4f468f5461..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/FooterPanel.tsx +++ /dev/null @@ -1,12 +0,0 @@ -export const FooterPanel = () => { - return ( -
- ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/Layout.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/layout/Layout.hooks.ts deleted file mode 100644 index 807c1e6811..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/Layout.hooks.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { useCallback } from 'react'; -import { createHotkey, HOT_KEY_NAME } from '$app/utils/hotkeys'; -import { useAppDispatch, useAppSelector } from '$app/stores/store'; -import { currentUserActions, ThemeMode } from '$app_reducers/current-user/slice'; -import { UserService } from '$app/application/user/user.service'; -import { sidebarActions } from '$app_reducers/sidebar/slice'; - -export function useShortcuts() { - const dispatch = useAppDispatch(); - const userSettingState = useAppSelector((state) => state.currentUser.userSetting); - const { isDark } = userSettingState; - - const switchThemeMode = useCallback(() => { - const newSetting = { - themeMode: isDark ? ThemeMode.Light : ThemeMode.Dark, - isDark: !isDark, - }; - - dispatch(currentUserActions.setUserSetting(newSetting)); - void UserService.setAppearanceSetting({ - theme_mode: newSetting.themeMode, - }); - }, [dispatch, isDark]); - - const toggleSidebar = useCallback(() => { - dispatch(sidebarActions.toggleCollapse()); - }, [dispatch]); - - return useCallback( - (e: KeyboardEvent) => { - switch (true) { - /** - * Toggle theme: Mod+L - * Switch between light and dark theme - */ - case createHotkey(HOT_KEY_NAME.TOGGLE_THEME)(e): - switchThemeMode(); - break; - /** - * Toggle sidebar: Mod+. (period) - * Prevent the default behavior of the browser (Exit full screen) - * Collapse or expand the sidebar - */ - case createHotkey(HOT_KEY_NAME.TOGGLE_SIDEBAR)(e): - e.preventDefault(); - toggleSidebar(); - break; - default: - break; - } - }, - [toggleSidebar, switchThemeMode] - ); -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/Layout.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/Layout.tsx deleted file mode 100644 index 509aa388cf..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/Layout.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import React, { ReactNode, useEffect, useMemo } from 'react'; -import SideBar from '$app/components/layout/side_bar/SideBar'; -import TopBar from '$app/components/layout/top_bar/TopBar'; -import { useAppSelector } from '$app/stores/store'; -import './layout.scss'; -import { AFScroller } from '../_shared/scroller'; -import { useNavigate } from 'react-router-dom'; -import { pageTypeMap } from '$app_reducers/pages/slice'; -import { useShortcuts } from '$app/components/layout/Layout.hooks'; - -function Layout({ children }: { children: ReactNode }) { - const { isCollapsed, width } = useAppSelector((state) => state.sidebar); - const currentUser = useAppSelector((state) => state.currentUser); - const navigate = useNavigate(); - const { id: latestOpenViewId, layout } = useMemo( - () => - currentUser?.workspaceSetting?.latestView || { - id: undefined, - layout: undefined, - }, - [currentUser?.workspaceSetting?.latestView] - ); - - const onKeyDown = useShortcuts(); - - useEffect(() => { - window.addEventListener('keydown', onKeyDown); - return () => { - window.removeEventListener('keydown', onKeyDown); - }; - }, [onKeyDown]); - - useEffect(() => { - if (latestOpenViewId) { - const pageType = pageTypeMap[layout]; - - navigate(`/page/${pageType}/${latestOpenViewId}`); - } - }, [latestOpenViewId, navigate, layout]); - return ( - <> -
- -
- - - {children} - -
-
- - ); -} - -export default Layout; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/bread_crumb/BreadCrumb.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/bread_crumb/BreadCrumb.tsx deleted file mode 100644 index ec9e990cdb..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/bread_crumb/BreadCrumb.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import React, { useCallback } from 'react'; -import { useLoadExpandedPages } from '$app/components/layout/bread_crumb/Breadcrumb.hooks'; -import Breadcrumbs from '@mui/material/Breadcrumbs'; -import Link from '@mui/material/Link'; -import Typography from '@mui/material/Typography'; -import { Page } from '$app_reducers/pages/slice'; -import { useTranslation } from 'react-i18next'; -import { getPageIcon } from '$app/hooks/page.hooks'; -import { useAppDispatch } from '$app/stores/store'; -import { openPage } from '$app_reducers/pages/async_actions'; - -function Breadcrumb() { - const { t } = useTranslation(); - const { isTrash, pagePath, currentPage } = useLoadExpandedPages(); - const dispatch = useAppDispatch(); - - const navigateToPage = useCallback( - (page: Page) => { - void dispatch(openPage(page.id)); - }, - [dispatch] - ); - - if (!currentPage) { - if (isTrash) { - return {t('trash.text')}; - } - - return null; - } - - return ( - - {pagePath?.map((page: Page, index) => { - if (index === pagePath.length - 1) { - return ( -
-
{getPageIcon(page)}
- {page.name.trim() || t('menuAppHeader.defaultNewPageName')} -
- ); - } - - return ( - { - navigateToPage(page); - }} - > -
{getPageIcon(page)}
- - {page.name.trim() || t('menuAppHeader.defaultNewPageName')} - - ); - })} -
- ); -} - -export default Breadcrumb; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/bread_crumb/Breadcrumb.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/layout/bread_crumb/Breadcrumb.hooks.ts deleted file mode 100644 index f2bec915d9..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/bread_crumb/Breadcrumb.hooks.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { useAppSelector } from '$app/stores/store'; -import { useMemo } from 'react'; -import { useParams, useLocation } from 'react-router-dom'; -import { Page } from '$app_reducers/pages/slice'; - -export function useLoadExpandedPages() { - const params = useParams(); - const location = useLocation(); - const isTrash = useMemo(() => location.pathname.includes('trash'), [location.pathname]); - const currentPageId = params.id; - const currentPage = useAppSelector((state) => (currentPageId ? state.pages.pageMap[currentPageId] : undefined)); - - const pagePath = useAppSelector((state) => { - const result: Page[] = []; - - if (!currentPage) return result; - - const findParent = (page: Page) => { - if (!page.parentId) return; - const parent = state.pages.pageMap[page.parentId]; - - if (parent) { - result.unshift(parent); - findParent(parent); - } - }; - - findParent(currentPage); - result.push(currentPage); - return result; - }); - - return { - pagePath, - currentPage, - isTrash, - }; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/collapse_menu_button/CollapseMenuButton.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/collapse_menu_button/CollapseMenuButton.tsx deleted file mode 100644 index 87662a99bb..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/collapse_menu_button/CollapseMenuButton.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import React, { useCallback, useMemo } from 'react'; -import { IconButton, Tooltip } from '@mui/material'; - -import { useAppDispatch, useAppSelector } from '$app/stores/store'; -import { sidebarActions } from '$app_reducers/sidebar/slice'; -import { ReactComponent as ShowMenuIcon } from '$app/assets/show-menu.svg'; -import { useTranslation } from 'react-i18next'; -import { createHotKeyLabel, HOT_KEY_NAME } from '$app/utils/hotkeys'; - -function CollapseMenuButton() { - const isCollapsed = useAppSelector((state) => state.sidebar.isCollapsed); - const dispatch = useAppDispatch(); - const handleClick = useCallback(() => { - dispatch(sidebarActions.toggleCollapse()); - }, [dispatch]); - - const { t } = useTranslation(); - - const title = useMemo(() => { - return ( -
-
{isCollapsed ? t('sideBar.openSidebar') : t('sideBar.closeSidebar')}
-
{createHotKeyLabel(HOT_KEY_NAME.TOGGLE_SIDEBAR)}
-
- ); - }, [isCollapsed, t]); - - return ( - - - - - - ); -} - -export default CollapseMenuButton; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/layout.scss b/frontend/appflowy_tauri/src/appflowy_app/components/layout/layout.scss deleted file mode 100644 index 43f4f55892..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/layout.scss +++ /dev/null @@ -1,81 +0,0 @@ - - -.sketch-picker { - background-color: var(--bg-body) !important; - border-color: transparent !important; - box-shadow: none !important; -} -.sketch-picker .flexbox-fix { - border-color: var(--line-divider) !important; -} -.sketch-picker [id^='rc-editable-input'] { - background-color: var(--bg-body) !important; - border-color: var(--line-divider) !important; - color: var(--text-title) !important; - box-shadow: var(--line-border) 0px 0px 0px 1px inset !important; -} - -.appflowy-date-picker-calendar { - width: 100%; - -} - -.grid-sticky-header::-webkit-scrollbar { - width: 0; - height: 0; -} -.grid-scroll-container::-webkit-scrollbar { - width: 0; - height: 0; -} - - -.appflowy-scroll-container { - &::-webkit-scrollbar { - width: 0; - } -} - -.appflowy-scrollbar-thumb-horizontal, .appflowy-scrollbar-thumb-vertical { - background-color: var(--scrollbar-thumb); - border-radius: 4px; - opacity: 60%; -} - -.workspaces { - ::-webkit-scrollbar { - width: 0px; - } -} - - - -.MuiPopover-root, .MuiPaper-root { - ::-webkit-scrollbar { - width: 0; - height: 0; - } -} - -.view-icon { - &:hover { - background-color: rgba(156, 156, 156, 0.20); - } -} - -.theme-mode-item { - @apply relative flex h-[72px] w-[88px] cursor-pointer items-end justify-end rounded border hover:shadow; - background: linear-gradient(150.74deg, rgba(231, 231, 231, 0) 17.95%, #C5C5C5 95.51%); -} - -[data-dark-mode="true"] { - .theme-mode-item { - background: linear-gradient(150.74deg, rgba(128, 125, 125, 0) 17.95%, #4d4d4d 95.51%); - } -} - -.document-header { - .view-banner { - @apply items-center; - } -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/nested_page/AddButton.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/nested_page/AddButton.tsx deleted file mode 100644 index 1387f16f4d..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/nested_page/AddButton.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import React, { useCallback, useMemo } from 'react'; -import { ReactComponent as AddSvg } from '$app/assets/add.svg'; -import { useTranslation } from 'react-i18next'; -import { ReactComponent as DocumentSvg } from '$app/assets/document.svg'; -import { ReactComponent as GridSvg } from '$app/assets/grid.svg'; -import { ViewLayoutPB } from '@/services/backend'; -import OperationMenu from '$app/components/layout/nested_page/OperationMenu'; - -function AddButton({ - isHovering, - setHovering, - onAddPage, -}: { - isHovering: boolean; - setHovering: (hovering: boolean) => void; - onAddPage: (layout: ViewLayoutPB) => void; -}) { - const { t } = useTranslation(); - - const onConfirm = useCallback( - (key: string) => { - switch (key) { - case 'document': - onAddPage(ViewLayoutPB.Document); - break; - case 'grid': - onAddPage(ViewLayoutPB.Grid); - break; - default: - break; - } - }, - [onAddPage] - ); - - const options = useMemo( - () => [ - { - key: 'document', - title: t('document.menuName'), - icon: , - }, - { - key: 'grid', - title: t('grid.menuName'), - icon: , - }, - ], - [t] - ); - - return ( - - - - ); -} - -export default AddButton; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/nested_page/DeleteDialog.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/nested_page/DeleteDialog.tsx deleted file mode 100644 index 4af8a2f2f1..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/nested_page/DeleteDialog.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import React from 'react'; -import { useTranslation } from 'react-i18next'; -import { ViewLayoutPB } from '@/services/backend'; -import DeleteConfirmDialog from '$app/components/_shared/confirm_dialog/DeleteConfirmDialog'; - -function DeleteDialog({ - layout, - open, - onClose, - onOk, -}: { - layout: ViewLayoutPB; - open: boolean; - onClose: () => void; - onOk: () => Promise; -}) { - const { t } = useTranslation(); - - const pageType = { - [ViewLayoutPB.Document]: t('document.menuName'), - [ViewLayoutPB.Grid]: t('grid.menuName'), - [ViewLayoutPB.Board]: t('board.menuName'), - [ViewLayoutPB.Calendar]: t('calendar.menuName'), - }[layout]; - - return ( - - ); -} - -export default DeleteDialog; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/nested_page/MoreButton.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/nested_page/MoreButton.tsx deleted file mode 100644 index 94a86655ac..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/nested_page/MoreButton.tsx +++ /dev/null @@ -1,130 +0,0 @@ -import React, { useCallback, useMemo, useState } from 'react'; -import { useTranslation } from 'react-i18next'; - -import { ReactComponent as DetailsSvg } from '$app/assets/details.svg'; -import { ReactComponent as EditSvg } from '$app/assets/edit.svg'; -import { ReactComponent as CopySvg } from '$app/assets/copy.svg'; -import { ReactComponent as TrashSvg } from '$app/assets/delete.svg'; - -import RenameDialog from '../../_shared/confirm_dialog/RenameDialog'; -import { Page } from '$app_reducers/pages/slice'; -import DeleteDialog from '$app/components/layout/nested_page/DeleteDialog'; -import OperationMenu from '$app/components/layout/nested_page/OperationMenu'; -import { getModifier } from '$app/utils/hotkeys'; -import isHotkey from 'is-hotkey'; - -function MoreButton({ - onDelete, - onDuplicate, - onRename, - page, - isHovering, - setHovering, -}: { - isHovering: boolean; - setHovering: (hovering: boolean) => void; - onDelete: () => Promise; - onDuplicate: () => Promise; - onRename: (newName: string) => Promise; - page: Page; -}) { - const [renameDialogOpen, setRenameDialogOpen] = useState(false); - const [deleteDialogOpen, setDeleteDialogOpen] = useState(false); - - const { t } = useTranslation(); - - const onConfirm = useCallback( - (key: string) => { - switch (key) { - case 'rename': - setRenameDialogOpen(true); - break; - case 'delete': - setDeleteDialogOpen(true); - break; - case 'duplicate': - void onDuplicate(); - - break; - default: - break; - } - }, - [onDuplicate] - ); - - const options = useMemo( - () => [ - { - title: t('button.rename'), - icon: , - key: 'rename', - }, - { - key: 'delete', - title: t('button.delete'), - icon: , - caption: 'Del', - }, - { - key: 'duplicate', - title: t('button.duplicate'), - icon: , - caption: `${getModifier()}+D`, - }, - ], - [t] - ); - - const onKeyDown = useCallback( - (e: KeyboardEvent) => { - if (isHotkey('del', e) || isHotkey('backspace', e)) { - e.preventDefault(); - e.stopPropagation(); - onConfirm('delete'); - return; - } - - if (isHotkey('mod+d', e)) { - e.stopPropagation(); - onConfirm('duplicate'); - return; - } - }, - [onConfirm] - ); - - return ( - <> - - - - - { - setDeleteDialogOpen(false); - }} - onOk={onDelete} - /> - {renameDialogOpen && ( - setRenameDialogOpen(false)} - onOk={onRename} - /> - )} - - ); -} - -export default MoreButton; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/nested_page/NestedPage.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/layout/nested_page/NestedPage.hooks.ts deleted file mode 100644 index d43499e801..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/nested_page/NestedPage.hooks.ts +++ /dev/null @@ -1,147 +0,0 @@ -import { useCallback, useEffect } from 'react'; -import { pagesActions, parserViewPBToPage } from '$app_reducers/pages/slice'; -import { useAppDispatch, useAppSelector } from '$app/stores/store'; -import { FolderNotification, ViewLayoutPB } from '@/services/backend'; -import { useParams } from 'react-router-dom'; -import { openPage, updatePageName } from '$app_reducers/pages/async_actions'; -import { createPage, deletePage, duplicatePage, getChildPages } from '$app/application/folder/page.service'; -import { subscribeNotifications } from '$app/application/notification'; - -export function useLoadChildPages(pageId: string) { - const dispatch = useAppDispatch(); - const childPages = useAppSelector((state) => state.pages.relationMap[pageId]); - const collapsed = useAppSelector((state) => !state.pages.expandedIdMap[pageId]); - const toggleCollapsed = useCallback(() => { - if (collapsed) { - dispatch(pagesActions.expandPage(pageId)); - } else { - dispatch(pagesActions.collapsePage(pageId)); - } - }, [dispatch, pageId, collapsed]); - - const loadPageChildren = useCallback( - async (pageId: string) => { - const childPages = await getChildPages(pageId); - - dispatch( - pagesActions.addChildPages({ - id: pageId, - childPages, - }) - ); - }, - [dispatch] - ); - - useEffect(() => { - void loadPageChildren(pageId); - }, [loadPageChildren, pageId]); - - useEffect(() => { - const unsubscribePromise = subscribeNotifications( - { - [FolderNotification.DidUpdateView]: async (payload) => { - const childViews = payload.child_views; - - if (childViews.length === 0) { - return; - } - - dispatch( - pagesActions.addChildPages({ - id: pageId, - childPages: childViews.map(parserViewPBToPage), - }) - ); - }, - [FolderNotification.DidUpdateChildViews]: async (payload) => { - if (payload.delete_child_views.length === 0 && payload.create_child_views.length === 0) { - return; - } - - void loadPageChildren(pageId); - }, - }, - { - id: pageId, - } - ); - - return () => void unsubscribePromise.then((unsubscribe) => unsubscribe()); - }, [pageId, loadPageChildren, dispatch]); - - return { - toggleCollapsed, - collapsed, - childPages, - }; -} - -export function usePageActions(pageId: string) { - const page = useAppSelector((state) => state.pages.pageMap[pageId]); - const dispatch = useAppDispatch(); - const params = useParams(); - const currentPageId = params.id; - - const onPageClick = useCallback(() => { - void dispatch(openPage(pageId)); - }, [dispatch, pageId]); - - const onAddPage = useCallback( - async (layout: ViewLayoutPB) => { - const newViewId = await createPage({ - layout, - name: '', - parent_view_id: pageId, - }); - - dispatch( - pagesActions.addPage({ - page: { - id: newViewId, - parentId: pageId, - layout, - name: '', - }, - isLast: true, - }) - ); - - dispatch(pagesActions.expandPage(pageId)); - await dispatch(openPage(newViewId)); - }, - [dispatch, pageId] - ); - - const onDeletePage = useCallback(async () => { - if (currentPageId === pageId) { - dispatch(pagesActions.setTrashSnackbar(true)); - } - - await deletePage(pageId); - dispatch(pagesActions.deletePages([pageId])); - }, [dispatch, pageId, currentPageId]); - - const onDuplicatePage = useCallback(async () => { - await duplicatePage(page); - }, [page]); - - const onRenamePage = useCallback( - async (name: string) => { - await dispatch(updatePageName({ id: pageId, name })); - }, - [dispatch, pageId] - ); - - return { - onAddPage, - onPageClick, - onRenamePage, - onDeletePage, - onDuplicatePage, - }; -} - -export function useSelectedPage(pageId: string) { - return useParams().id === pageId; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/nested_page/NestedPage.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/nested_page/NestedPage.tsx deleted file mode 100644 index e423f05517..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/nested_page/NestedPage.tsx +++ /dev/null @@ -1,126 +0,0 @@ -import React, { useCallback, useMemo } from 'react'; -import Collapse from '@mui/material/Collapse'; -import { TransitionGroup } from 'react-transition-group'; -import NestedPageTitle from '$app/components/layout/nested_page/NestedPageTitle'; -import { useLoadChildPages, usePageActions } from '$app/components/layout/nested_page/NestedPage.hooks'; -import { useDrag } from 'src/appflowy_app/components/_shared/drag_block'; -import { useAppDispatch, useAppSelector } from '$app/stores/store'; -import { movePageThunk } from '$app_reducers/pages/async_actions'; -import { ViewLayoutPB } from '@/services/backend'; - -function NestedPage({ pageId }: { pageId: string }) { - const { toggleCollapsed, collapsed, childPages } = useLoadChildPages(pageId); - const { onAddPage, onPageClick, onDeletePage, onDuplicatePage, onRenamePage } = usePageActions(pageId); - const dispatch = useAppDispatch(); - const { page, parentLayout } = useAppSelector((state) => { - const page = state.pages.pageMap[pageId]; - const parent = state.pages.pageMap[page?.parentId || '']; - - return { - page, - parentLayout: parent?.layout, - }; - }); - - const disableChildren = useAppSelector((state) => { - if (!page) return true; - const layout = state.pages.pageMap[page.parentId]?.layout; - - return !(layout === undefined || layout === ViewLayoutPB.Document); - }); - const children = useMemo(() => { - if (disableChildren) { - return []; - } - - return collapsed ? [] : childPages; - }, [collapsed, childPages, disableChildren]); - - const onDragFinished = useCallback( - (result: { dragId: string; position: 'before' | 'after' | 'inside' }) => { - const { dragId, position } = result; - - if (dragId === pageId) return; - if (position === 'inside' && page?.layout !== ViewLayoutPB.Document) return; - void dispatch( - movePageThunk({ - sourceId: dragId, - targetId: pageId, - insertType: position, - }) - ); - }, - [dispatch, page?.layout, pageId] - ); - - const { onDrop, dropPosition, onDragOver, onDragLeave, onDragStart, onDragEnd, isDraggingOver, isDragging } = useDrag({ - onEnd: onDragFinished, - dragId: pageId, - }); - - const className = useMemo(() => { - const defaultClassName = 'relative flex-1 select-none flex flex-col w-full'; - - if (isDragging) { - return `${defaultClassName} opacity-40`; - } - - if (isDraggingOver && dropPosition === 'inside' && page?.layout === ViewLayoutPB.Document) { - if (dropPosition === 'inside') { - return `${defaultClassName} bg-content-blue-100`; - } - } else { - return defaultClassName; - } - }, [dropPosition, isDragging, isDraggingOver, page?.layout]); - - // Only allow dragging if the parent layout is undefined or a document - const draggable = parentLayout === undefined || parentLayout === ViewLayoutPB.Document; - - return ( -
-
- { - onPageClick(); - }} - onAddPage={onAddPage} - onDuplicate={onDuplicatePage} - onDelete={onDeletePage} - onRename={onRenamePage} - collapsed={collapsed} - toggleCollapsed={toggleCollapsed} - pageId={pageId} - /> -
- - {children?.map((pageId) => ( - - - - ))} - -
-
- ); -} - -export default React.memo(NestedPage); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/nested_page/NestedPageTitle.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/nested_page/NestedPageTitle.tsx deleted file mode 100644 index 948aedcae2..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/nested_page/NestedPageTitle.tsx +++ /dev/null @@ -1,102 +0,0 @@ -import React, { useMemo, useState } from 'react'; -import { useAppSelector } from '$app/stores/store'; -import AddButton from './AddButton'; -import MoreButton from './MoreButton'; -import { ViewLayoutPB } from '@/services/backend'; -import { useSelectedPage } from '$app/components/layout/nested_page/NestedPage.hooks'; -import { useTranslation } from 'react-i18next'; -import { ReactComponent as MoreIcon } from '$app/assets/more.svg'; -import { IconButton } from '@mui/material'; -import { Page } from '$app_reducers/pages/slice'; -import { getPageIcon } from '$app/hooks/page.hooks'; - -function NestedPageTitle({ - pageId, - collapsed, - toggleCollapsed, - onAddPage, - onClick, - onDelete, - onDuplicate, - onRename, -}: { - pageId: string; - collapsed: boolean; - toggleCollapsed: () => void; - onAddPage: (layout: ViewLayoutPB) => void; - onClick: () => void; - onDelete: () => Promise; - onDuplicate: () => Promise; - onRename: (newName: string) => Promise; -}) { - const { t } = useTranslation(); - const page = useAppSelector((state) => { - return state.pages.pageMap[pageId] as Page | undefined; - }); - const disableChildren = useAppSelector((state) => { - if (!page) return true; - const layout = state.pages.pageMap[page.parentId]?.layout; - - return !(layout === undefined || layout === ViewLayoutPB.Document); - }); - - const [isHovering, setIsHovering] = useState(false); - const isSelected = useSelectedPage(pageId); - - const pageIcon = useMemo(() => (page ? getPageIcon(page) : null), [page]); - - return ( -
setIsHovering(true)} - onMouseLeave={() => setIsHovering(false)} - > -
-
- {disableChildren ? ( -
- ) : ( - { - e.stopPropagation(); - toggleCollapsed(); - }} - style={{ - transform: collapsed ? 'rotate(0deg)' : 'rotate(90deg)', - }} - > - - - )} - - {pageIcon} - -
- {page?.name.trim() || t('menuAppHeader.defaultNewPageName')} -
-
-
e.stopPropagation()} className={'min:w-14 flex items-center justify-end px-2'}> - {page?.layout === ViewLayoutPB.Document && ( - - )} - {page && ( - - )} -
-
-
- ); -} - -export default NestedPageTitle; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/nested_page/OperationMenu.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/nested_page/OperationMenu.tsx deleted file mode 100644 index cef1d3307c..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/nested_page/OperationMenu.tsx +++ /dev/null @@ -1,103 +0,0 @@ -import React, { ReactNode, useCallback, useMemo, useState } from 'react'; -import { IconButton } from '@mui/material'; -import Popover from '@mui/material/Popover'; -import KeyboardNavigation from '$app/components/_shared/keyboard_navigation/KeyboardNavigation'; -import Tooltip from '@mui/material/Tooltip'; - -function OperationMenu({ - options, - onConfirm, - isHovering, - setHovering, - children, - tooltip, - onKeyDown, -}: { - isHovering: boolean; - setHovering: (hovering: boolean) => void; - options: { - key: string; - title: string; - icon: React.ReactNode; - caption?: string; - }[]; - children: React.ReactNode; - onConfirm: (key: string) => void; - tooltip: string; - onKeyDown?: (e: KeyboardEvent) => void; -}) { - const [anchorEl, setAnchorEl] = useState(null); - const renderItem = useCallback((title: string, icon: ReactNode, caption?: string) => { - return ( -
- {icon} -
{title}
-
{caption || ''}
-
- ); - }, []); - - const handleClose = useCallback(() => { - setAnchorEl(null); - setHovering(false); - }, [setHovering]); - - const optionList = useMemo(() => { - return options.map((option) => { - return { - key: option.key, - content: renderItem(option.title, option.icon, option.caption), - }; - }); - }, [options, renderItem]); - - const open = Boolean(anchorEl); - - const handleConfirm = useCallback( - (key: string) => { - onConfirm(key); - handleClose(); - }, - [handleClose, onConfirm] - ); - - return ( - <> - - { - setAnchorEl(e.currentTarget); - }} - className={`${!isHovering ? 'invisible' : ''} text-icon-primary`} - > - {children} - - - - - - - - ); -} - -export default OperationMenu; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/share/Share.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/layout/share/Share.hooks.ts deleted file mode 100644 index b281706848..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/share/Share.hooks.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { useParams } from 'react-router-dom'; - -export function useShareConfig() { - const params = useParams(); - const id = params.id; - - const showShareButton = !!id; - - return { - showShareButton, - }; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/share/Share.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/share/Share.tsx deleted file mode 100644 index 1a10cb08e6..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/share/Share.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import React from 'react'; -import { useTranslation } from 'react-i18next'; -import Button from '@mui/material/Button'; -import { useShareConfig } from '$app/components/layout/share/Share.hooks'; - -function ShareButton() { - const { showShareButton } = useShareConfig(); - const { t } = useTranslation(); - - if (!showShareButton) return null; - return ; -} - -export default ShareButton; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/side_bar/Resizer.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/side_bar/Resizer.tsx deleted file mode 100644 index 639d5283e0..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/side_bar/Resizer.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import React, { useCallback, useRef } from 'react'; -import { useAppDispatch, useAppSelector } from '$app/stores/store'; -import { sidebarActions } from '$app_reducers/sidebar/slice'; - -const minSidebarWidth = 200; - -function Resizer() { - const dispatch = useAppDispatch(); - const width = useAppSelector((state) => state.sidebar.width); - const startX = useRef(0); - const onResize = useCallback( - (e: MouseEvent) => { - e.preventDefault(); - const diff = e.clientX - startX.current; - const newWidth = width + diff; - - if (newWidth < minSidebarWidth) { - return; - } - - dispatch(sidebarActions.changeWidth(newWidth)); - }, - [dispatch, width] - ); - - const onResizeEnd = useCallback(() => { - dispatch(sidebarActions.stopResizing()); - document.removeEventListener('mousemove', onResize); - document.removeEventListener('mouseup', onResizeEnd); - }, [onResize, dispatch]); - - const onResizeStart = useCallback( - (e: React.MouseEvent) => { - startX.current = e.clientX; - dispatch(sidebarActions.startResizing()); - document.addEventListener('mousemove', onResize); - document.addEventListener('mouseup', onResizeEnd); - }, - [onResize, onResizeEnd, dispatch] - ); - - return ( -
-
-
- ); -} - -export default React.memo(Resizer); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/side_bar/SideBar.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/side_bar/SideBar.tsx deleted file mode 100644 index 5cdbfb125b..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/side_bar/SideBar.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import React, { useEffect, useRef } from 'react'; -import { useAppDispatch, useAppSelector } from '$app/stores/store'; -import { ReactComponent as AppflowyLogoDark } from '$app/assets/dark-logo.svg'; -import { ReactComponent as AppflowyLogoLight } from '$app/assets/light-logo.svg'; -import CollapseMenuButton from '$app/components/layout/collapse_menu_button/CollapseMenuButton'; -import Resizer from '$app/components/layout/side_bar/Resizer'; -import UserInfo from '$app/components/layout/side_bar/UserInfo'; -import WorkspaceManager from '$app/components/layout/workspace_manager/WorkspaceManager'; -import { ThemeMode } from '$app_reducers/current-user/slice'; -import { sidebarActions } from '$app_reducers/sidebar/slice'; - -function SideBar() { - const { isCollapsed, width, isResizing } = useAppSelector((state) => state.sidebar); - const dispatch = useAppDispatch(); - - const themeMode = useAppSelector((state) => state.currentUser?.userSetting?.themeMode); - const isDark = - themeMode === ThemeMode.Dark || - (themeMode === ThemeMode.System && window.matchMedia('(prefers-color-scheme: dark)').matches); - - const lastCollapsedRef = useRef(isCollapsed); - - useEffect(() => { - const handleResize = () => { - const width = window.innerWidth; - - if (width <= 800 && !isCollapsed) { - lastCollapsedRef.current = false; - dispatch(sidebarActions.setCollapse(true)); - } else if (width > 800 && !lastCollapsedRef.current) { - lastCollapsedRef.current = true; - dispatch(sidebarActions.setCollapse(false)); - } - }; - - window.addEventListener('resize', handleResize); - - return () => { - window.removeEventListener('resize', handleResize); - }; - }, [dispatch, isCollapsed]); - return ( - <> -
-
-
- {isDark ? ( - - ) : ( - - )} - -
-
- -
-
- -
-
-
- - - ); -} - -export default SideBar; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/side_bar/UserInfo.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/side_bar/UserInfo.tsx deleted file mode 100644 index 62763c670e..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/side_bar/UserInfo.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import React, { useState } from 'react'; -import { useAppSelector } from '$app/stores/store'; -import { IconButton } from '@mui/material'; -import { ReactComponent as SettingIcon } from '$app/assets/settings.svg'; -import Tooltip from '@mui/material/Tooltip'; -import { useTranslation } from 'react-i18next'; -import { SettingsDialog } from '$app/components/settings/SettingsDialog'; -import { ProfileAvatar } from '$app/components/_shared/avatar'; - -function UserInfo() { - const currentUser = useAppSelector((state) => state.currentUser); - const [showUserSetting, setShowUserSetting] = useState(false); - - const { t } = useTranslation(); - - return ( - <> -
-
- - {currentUser.displayName} -
- - - { - setShowUserSetting(!showUserSetting); - }} - > - - - -
- - {showUserSetting && setShowUserSetting(false)} />} - - ); -} - -export default UserInfo; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/top_bar/DeletePageSnackbar.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/top_bar/DeletePageSnackbar.tsx deleted file mode 100644 index f5638362b9..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/top_bar/DeletePageSnackbar.tsx +++ /dev/null @@ -1,104 +0,0 @@ -import React, { useEffect } from 'react'; -import { Alert, Snackbar } from '@mui/material'; -import { useAppDispatch, useAppSelector } from '$app/stores/store'; -import { useParams } from 'react-router-dom'; -import { pagesActions } from '$app_reducers/pages/slice'; -import Slide, { SlideProps } from '@mui/material/Slide'; -import { useTranslation } from 'react-i18next'; -import Button from '@mui/material/Button'; -import { useTrashActions } from '$app/components/trash/Trash.hooks'; -import { openPage } from '$app_reducers/pages/async_actions'; - -function SlideTransition(props: SlideProps) { - return ; -} - -function DeletePageSnackbar() { - const firstViewId = useAppSelector((state) => { - const workspaceId = state.workspace.currentWorkspaceId; - const children = workspaceId ? state.pages.relationMap[workspaceId] : undefined; - - if (!children) return null; - - return children[0]; - }); - - const showTrashSnackbar = useAppSelector((state) => state.pages.showTrashSnackbar); - const dispatch = useAppDispatch(); - const { onPutback, onDelete } = useTrashActions(); - const { id } = useParams(); - - const { t } = useTranslation(); - - useEffect(() => { - dispatch(pagesActions.setTrashSnackbar(false)); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [id]); - - const handleBack = () => { - if (firstViewId) { - void dispatch(openPage(firstViewId)); - } - }; - - const handleClose = (toBack = true) => { - dispatch(pagesActions.setTrashSnackbar(false)); - if (toBack) { - handleBack(); - } - }; - - const handleRestore = () => { - if (!id) return; - void onPutback(id); - handleClose(false); - }; - - const handleDelete = () => { - if (!id) return; - void onDelete([id]); - - if (!firstViewId) { - handleClose(false); - return; - } - - handleBack(); - }; - - return ( - - handleClose()} - severity='info' - variant='standard' - sx={{ - width: '100%', - '.MuiAlert-action': { - padding: 0, - }, - }} - > -
- {t('deletePagePrompt.text')} - - -
-
-
- ); -} - -export default DeletePageSnackbar; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/top_bar/FontSizeConfig.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/top_bar/FontSizeConfig.tsx deleted file mode 100644 index 4b439cc3fb..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/top_bar/FontSizeConfig.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import React from 'react'; -import { useTranslation } from 'react-i18next'; -import { ButtonGroup, Divider } from '@mui/material'; -import Button from '@mui/material/Button'; - -function FontSizeConfig() { - const { t } = useTranslation(); - - return ( - <> -
-
{t('moreAction.fontSize')}
-
- - - - - -
-
- - - ); -} - -export default FontSizeConfig; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/top_bar/MoreButton.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/top_bar/MoreButton.tsx deleted file mode 100644 index d37d1bf060..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/top_bar/MoreButton.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import React, { useCallback } from 'react'; -import { useTranslation } from 'react-i18next'; -import { Drawer, IconButton } from '@mui/material'; -import { ReactComponent as Details2Svg } from '$app/assets/details.svg'; -import Tooltip from '@mui/material/Tooltip'; -import MoreOptions from '$app/components/layout/top_bar/MoreOptions'; -import { useMoreOptionsConfig } from '$app/components/layout/top_bar/MoreOptions.hooks'; - -function MoreButton() { - const { t } = useTranslation(); - const [open, setOpen] = React.useState(false); - const toggleDrawer = useCallback((open: boolean) => { - setOpen(open); - }, []); - const { showMoreButton } = useMoreOptionsConfig(); - - if (!showMoreButton) return null; - return ( - <> - - toggleDrawer(true)} className={'text-icon-primary'}> - - - - toggleDrawer(false)}> - - - - ); -} - -export default MoreButton; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/top_bar/MoreOptions.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/layout/top_bar/MoreOptions.hooks.ts deleted file mode 100644 index 63f1173885..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/top_bar/MoreOptions.hooks.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { useLocation } from 'react-router-dom'; -import { useMemo } from 'react'; - -export function useMoreOptionsConfig() { - const location = useLocation(); - - const { type, pageType } = useMemo(() => { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const [_, type, pageType, id] = location.pathname.split('/'); - - return { - type, - pageType, - id, - }; - }, [location.pathname]); - - const showMoreButton = useMemo(() => { - return type === 'page'; - }, [type]); - - const showStyleOptions = useMemo(() => { - return type === 'page' && pageType === 'document'; - }, [pageType, type]); - - return { - showMoreButton, - showStyleOptions, - }; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/top_bar/MoreOptions.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/top_bar/MoreOptions.tsx deleted file mode 100644 index 7f77259212..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/top_bar/MoreOptions.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import React from 'react'; -import FontSizeConfig from '$app/components/layout/top_bar/FontSizeConfig'; -import { useMoreOptionsConfig } from '$app/components/layout/top_bar/MoreOptions.hooks'; - -function MoreOptions() { - const { showStyleOptions } = useMoreOptionsConfig(); - - return
{showStyleOptions && }
; -} - -export default MoreOptions; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/top_bar/TopBar.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/top_bar/TopBar.tsx deleted file mode 100644 index 173bf86cab..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/top_bar/TopBar.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import React from 'react'; -import CollapseMenuButton from '$app/components/layout/collapse_menu_button/CollapseMenuButton'; -import { useAppSelector } from '$app/stores/store'; -import Breadcrumb from '$app/components/layout/bread_crumb/BreadCrumb'; -import DeletePageSnackbar from '$app/components/layout/top_bar/DeletePageSnackbar'; - -function TopBar() { - const sidebarIsCollapsed = useAppSelector((state) => state.sidebar.isCollapsed); - - return ( -
- {sidebarIsCollapsed && ( -
- -
- )} -
-
- -
-
- -
- ); -} - -export default TopBar; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/workspace_manager/NestedPages.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/workspace_manager/NestedPages.tsx deleted file mode 100644 index ec3335b6b3..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/workspace_manager/NestedPages.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import React from 'react'; -import { useAppSelector } from '$app/stores/store'; -import NestedPage from '$app/components/layout/nested_page/NestedPage'; - -function WorkspaceNestedPages({ workspaceId }: { workspaceId: string }) { - const pageIds = useAppSelector((state) => { - return state.pages.relationMap[workspaceId]; - }); - - return ( -
- {pageIds?.map((pageId) => ( - - ))} -
- ); -} - -export default WorkspaceNestedPages; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/workspace_manager/NewPageButton.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/workspace_manager/NewPageButton.tsx deleted file mode 100644 index 537b7d2d9a..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/workspace_manager/NewPageButton.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import React from 'react'; -import { useTranslation } from 'react-i18next'; -import { useWorkspaceActions } from '$app/components/layout/workspace_manager/Workspace.hooks'; -import Button from '@mui/material/Button'; -import { ReactComponent as AddSvg } from '$app/assets/add.svg'; - -function NewPageButton({ workspaceId }: { workspaceId: string }) { - const { t } = useTranslation(); - const { newPage } = useWorkspaceActions(workspaceId); - - return ( -
-
- } - className={'flex w-full items-center justify-start text-xs hover:bg-transparent hover:text-fill-default'} - > - {t('newPageText')} - -
- ); -} - -export default NewPageButton; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/workspace_manager/TrashButton.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/workspace_manager/TrashButton.tsx deleted file mode 100644 index 984ed6f67f..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/workspace_manager/TrashButton.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import React, { useCallback } from 'react'; -import { ReactComponent as TrashSvg } from '$app/assets/delete.svg'; -import { useTranslation } from 'react-i18next'; -import { useLocation, useNavigate } from 'react-router-dom'; -import { useDrag } from 'src/appflowy_app/components/_shared/drag_block'; -import { deletePage } from '$app/application/folder/page.service'; - -function TrashButton() { - const { t } = useTranslation(); - const navigate = useNavigate(); - const currentPathType = useLocation().pathname.split('/')[1]; - const navigateToTrash = () => { - navigate('/trash'); - }; - - const selected = currentPathType === 'trash'; - - const onEnd = useCallback((result: { dragId: string; position: 'before' | 'after' | 'inside' }) => { - void deletePage(result.dragId); - }, []); - - const { onDrop, onDragOver, onDragLeave, isDraggingOver } = useDrag({ - onEnd, - }); - - return ( -
- - {t('trash.text')} -
- ); -} - -export default TrashButton; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/workspace_manager/Workspace.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/layout/workspace_manager/Workspace.hooks.ts deleted file mode 100644 index 86bca45ada..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/workspace_manager/Workspace.hooks.ts +++ /dev/null @@ -1,136 +0,0 @@ -import { useCallback, useEffect, useMemo } from 'react'; -import { useAppDispatch, useAppSelector } from '$app/stores/store'; -import { workspaceActions, WorkspaceItem } from '$app_reducers/workspace/slice'; -import { Page, pagesActions, parserViewPBToPage } from '$app_reducers/pages/slice'; -import { subscribeNotifications } from '$app/application/notification'; -import { FolderNotification, ViewLayoutPB } from '@/services/backend'; -import * as workspaceService from '$app/application/folder/workspace.service'; -import { createCurrentWorkspaceChildView } from '$app/application/folder/workspace.service'; -import { openPage } from '$app_reducers/pages/async_actions'; - -export function useLoadWorkspaces() { - const dispatch = useAppDispatch(); - const { workspaces, currentWorkspaceId } = useAppSelector((state) => state.workspace); - - const currentWorkspace = useMemo(() => { - return workspaces.find((workspace) => workspace.id === currentWorkspaceId); - }, [workspaces, currentWorkspaceId]); - - const initializeWorkspaces = useCallback(async () => { - const workspaces = await workspaceService.getWorkspaces(); - - const currentWorkspaceId = await workspaceService.getCurrentWorkspace(); - - dispatch( - workspaceActions.initWorkspaces({ - workspaces, - currentWorkspaceId, - }) - ); - }, [dispatch]); - - return { - workspaces, - currentWorkspace, - initializeWorkspaces, - }; -} - -export function useLoadWorkspace(workspace: WorkspaceItem) { - const { id } = workspace; - const dispatch = useAppDispatch(); - - const openWorkspace = useCallback(async () => { - await workspaceService.openWorkspace(id); - }, [id]); - - const deleteWorkspace = useCallback(async () => { - await workspaceService.deleteWorkspace(id); - }, [id]); - - const onChildPagesChanged = useCallback( - (childPages: Page[]) => { - dispatch( - pagesActions.addChildPages({ - id, - childPages, - }) - ); - }, - [dispatch, id] - ); - - const initializeWorkspace = useCallback(async () => { - const childPages = await workspaceService.getWorkspaceChildViews(id); - - dispatch( - pagesActions.addChildPages({ - id, - childPages, - }) - ); - }, [dispatch, id]); - - useEffect(() => { - void (async () => { - await initializeWorkspace(); - })(); - }, [initializeWorkspace]); - - useEffect(() => { - const unsubscribePromise = subscribeNotifications( - { - [FolderNotification.DidUpdateWorkspace]: async (changeset) => { - dispatch( - workspaceActions.updateWorkspace({ - id: String(changeset.id), - name: changeset.name, - icon: changeset.icon_url, - }) - ); - }, - [FolderNotification.DidUpdateWorkspaceViews]: async (changeset) => { - const res = changeset.items; - - onChildPagesChanged(res.map(parserViewPBToPage)); - }, - }, - { id } - ); - - return () => void unsubscribePromise.then((unsubscribe) => unsubscribe()); - }, [dispatch, id, onChildPagesChanged]); - - return { - openWorkspace, - deleteWorkspace, - }; -} - -export function useWorkspaceActions(workspaceId: string) { - const dispatch = useAppDispatch(); - const newPage = useCallback(async () => { - const { id } = await createCurrentWorkspaceChildView({ - name: '', - layout: ViewLayoutPB.Document, - parent_view_id: workspaceId, - }); - - dispatch( - pagesActions.addPage({ - page: { - id: id, - parentId: workspaceId, - layout: ViewLayoutPB.Document, - name: '', - }, - isLast: true, - }) - ); - void dispatch(openPage(id)); - }, [dispatch, workspaceId]); - - return { - newPage, - }; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/workspace_manager/Workspace.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/workspace_manager/Workspace.tsx deleted file mode 100644 index 24fc7be91e..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/workspace_manager/Workspace.tsx +++ /dev/null @@ -1,85 +0,0 @@ -import React, { useState } from 'react'; -import { WorkspaceItem } from '$app_reducers/workspace/slice'; -import NestedViews from '$app/components/layout/workspace_manager/NestedPages'; -import { useLoadWorkspace, useWorkspaceActions } from '$app/components/layout/workspace_manager/Workspace.hooks'; -import { useTranslation } from 'react-i18next'; -import { ReactComponent as AddIcon } from '$app/assets/add.svg'; -import { IconButton } from '@mui/material'; -import Tooltip from '@mui/material/Tooltip'; -import { WorkplaceAvatar } from '$app/components/_shared/avatar'; - -function Workspace({ workspace, opened }: { workspace: WorkspaceItem; opened: boolean }) { - useLoadWorkspace(workspace); - const { t } = useTranslation(); - const { newPage } = useWorkspaceActions(workspace.id); - const [showPages, setShowPages] = useState(true); - const [showAdd, setShowAdd] = useState(false); - - return ( - <> -
-
{ - e.stopPropagation(); - e.preventDefault(); - setShowPages((prev) => { - return !prev; - }); - }} - onMouseEnter={() => { - setShowAdd(true); - }} - onMouseLeave={() => { - setShowAdd(false); - }} - className={'mt-2 flex h-[22px] w-full cursor-pointer select-none items-center justify-between px-4'} - > - -
- {!workspace.name ? ( - t('sideBar.personal') - ) : ( - <> - - {workspace.name} - - )} -
-
- {showAdd && ( - - { - e.stopPropagation(); - void newPage(); - }} - size={'small'} - > - - - - )} -
- -
- -
-
- - ); -} - -export default Workspace; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/workspace_manager/WorkspaceManager.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/workspace_manager/WorkspaceManager.tsx deleted file mode 100644 index 083dd61ec3..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/workspace_manager/WorkspaceManager.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import React, { useEffect } from 'react'; -import NewPageButton from '$app/components/layout/workspace_manager/NewPageButton'; -import { useLoadWorkspaces } from '$app/components/layout/workspace_manager/Workspace.hooks'; -import Workspace from './Workspace'; -import TrashButton from '$app/components/layout/workspace_manager/TrashButton'; -import { useAppSelector } from '@/appflowy_app/stores/store'; -import { LoginState } from '$app_reducers/current-user/slice'; -import { AFScroller } from '$app/components/_shared/scroller'; - -function WorkspaceManager() { - const { workspaces, currentWorkspace, initializeWorkspaces } = useLoadWorkspaces(); - - const loginState = useAppSelector((state) => state.currentUser.loginState); - - useEffect(() => { - if (loginState === LoginState.Success || loginState === undefined) { - void initializeWorkspaces(); - } - }, [initializeWorkspaces, loginState]); - - return ( -
- -
- {workspaces.map((workspace) => ( - - ))} -
-
-
- -
- {currentWorkspace && } -
- ); -} - -export default WorkspaceManager; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/settings/Login.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/settings/Login.tsx deleted file mode 100644 index d5ecc4bc0c..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/settings/Login.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import Typography from '@mui/material/Typography'; -import { useTranslation } from 'react-i18next'; -import Button from '@mui/material/Button'; -import { LoginButtonGroup } from '$app/components/auth/LoginButtonGroup'; - -export const Login = ({ onBack }: { onBack?: () => void }) => { - const { t } = useTranslation(); - - return ( -
- - {t('button.login')} - -
- - -
-
- ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/settings/Settings.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/settings/Settings.tsx deleted file mode 100644 index 1d9f3c0cd9..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/settings/Settings.tsx +++ /dev/null @@ -1,92 +0,0 @@ -import { useMemo, useState } from 'react'; -import { Box, Tab, Tabs } from '@mui/material'; -import { useTranslation } from 'react-i18next'; -import { MyAccount } from '$app/components/settings/my_account'; -import { ReactComponent as AccountIcon } from '$app/assets/settings/account.svg'; -import { ReactComponent as WorkplaceIcon } from '$app/assets/settings/workplace.svg'; -import { Workplace } from '$app/components/settings/workplace'; -import { SettingsRoutes } from '$app/components/settings/workplace/const'; - -interface TabPanelProps { - children?: React.ReactNode; - index: number; - value: number; -} - -function TabPanel(props: TabPanelProps) { - const { children, value, index, ...other } = props; - - return ( - - ); -} - -export const Settings = ({ onForward }: { onForward: (route: SettingsRoutes) => void }) => { - const { t } = useTranslation(); - const [activeTab, setActiveTab] = useState(0); - - const tabOptions = useMemo(() => { - return [ - { - label: t('newSettings.myAccount.title'), - Icon: AccountIcon, - Component: MyAccount, - }, - { - label: t('newSettings.workplace.name'), - Icon: WorkplaceIcon, - Component: Workplace, - }, - ]; - }, [t]); - - const handleChangeTab = (event: React.SyntheticEvent, newValue: number) => { - setActiveTab(newValue); - }; - - return ( - - - {tabOptions.map((tab, index) => ( - - - {tab.label} -
- } - onClick={() => setActiveTab(index)} - sx={{ '&.Mui-selected': { borderColor: 'transparent', backgroundColor: 'var(--fill-list-active)' } }} - /> - ))} - - {tabOptions.map((tab, index) => ( - - - - ))} - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/settings/SettingsDialog.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/settings/SettingsDialog.tsx deleted file mode 100644 index b53f8a6002..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/settings/SettingsDialog.tsx +++ /dev/null @@ -1,108 +0,0 @@ -/** - * @figmaUrl https://www.figma.com/file/MF5CWlOzBRuGHp45zAXyUH/Appflowy%3A-Desktop-Settings?type=design&node-id=100%3A2119&mode=design&t=4Wb0Zg5NOFO36kOf-1 - */ - -import Dialog, { DialogProps } from '@mui/material/Dialog'; -import { Settings } from '$app/components/settings/Settings'; -import { useCallback, useEffect, useRef, useState } from 'react'; -import DialogTitle from '@mui/material/DialogTitle'; -import { IconButton } from '@mui/material'; -import { ReactComponent as CloseIcon } from '$app/assets/close.svg'; -import { useTranslation } from 'react-i18next'; -import { ReactComponent as UpIcon } from '$app/assets/up.svg'; -import { SettingsRoutes } from '$app/components/settings/workplace/const'; -import DialogContent from '@mui/material/DialogContent'; -import { Login } from '$app/components/settings/Login'; -import SwipeableViews from 'react-swipeable-views'; -import { useAppDispatch, useAppSelector } from '$app/stores/store'; -import { currentUserActions, LoginState } from '$app_reducers/current-user/slice'; - -export const SettingsDialog = (props: DialogProps) => { - const dispatch = useAppDispatch(); - const [routes, setRoutes] = useState([]); - const loginState = useAppSelector((state) => state.currentUser.loginState); - const lastLoginStateRef = useRef(loginState); - const { t } = useTranslation(); - const handleForward = useCallback((route: SettingsRoutes) => { - setRoutes((prev) => { - return [...prev, route]; - }); - }, []); - - const handleBack = useCallback(() => { - setRoutes((prevState) => { - return prevState.slice(0, -1); - }); - dispatch(currentUserActions.resetLoginState()); - }, [dispatch]); - - const handleClose = useCallback(() => { - dispatch(currentUserActions.resetLoginState()); - props?.onClose?.({}, 'backdropClick'); - }, [dispatch, props]); - - const currentRoute = routes[routes.length - 1]; - - useEffect(() => { - if (lastLoginStateRef.current === LoginState.Loading && loginState === LoginState.Success) { - handleClose(); - return; - } - - lastLoginStateRef.current = loginState; - }, [loginState, handleClose]); - - return ( - { - if (e.key === 'Escape') { - e.preventDefault(); - } - }} - scroll={'paper'} - > - - - - - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/settings/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/settings/index.ts deleted file mode 100644 index 0f0a2c23f4..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/settings/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './my_account'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/settings/my_account/AccountLogin.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/settings/my_account/AccountLogin.tsx deleted file mode 100644 index 05b375c920..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/settings/my_account/AccountLogin.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { useTranslation } from 'react-i18next'; -import Typography from '@mui/material/Typography'; -import Button from '@mui/material/Button'; -import { Divider } from '@mui/material'; -import { DeleteAccount } from '$app/components/settings/my_account/DeleteAccount'; -import { SettingsRoutes } from '$app/components/settings/workplace/const'; -import { useAuth } from '$app/components/auth/auth.hooks'; - -export const AccountLogin = ({ onForward }: { onForward?: (route: SettingsRoutes) => void }) => { - const { t } = useTranslation(); - const { currentUser, logout } = useAuth(); - - const isLocal = currentUser.isLocal; - - return ( - <> -
- - {t('newSettings.myAccount.accountLogin')} - - - - -
- - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/settings/my_account/DeleteAccount.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/settings/my_account/DeleteAccount.tsx deleted file mode 100644 index 82a909180e..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/settings/my_account/DeleteAccount.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import { useTranslation } from 'react-i18next'; -import { useState } from 'react'; -import Typography from '@mui/material/Typography'; -import Button from '@mui/material/Button'; -import { DeleteAccountDialog } from '$app/components/settings/my_account/DeleteAccountDialog'; - -export const DeleteAccount = () => { - const { t } = useTranslation(); - - const [openDialog, setOpenDialog] = useState(false); - - return ( -
-
- - {t('newSettings.myAccount.deleteAccount.title')} - - - {t('newSettings.myAccount.deleteAccount.subtitle')} - -
-
- -
- { - setOpenDialog(false); - }} - /> -
- ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/settings/my_account/DeleteAccountDialog.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/settings/my_account/DeleteAccountDialog.tsx deleted file mode 100644 index 2f8cc37258..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/settings/my_account/DeleteAccountDialog.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import Dialog, { DialogProps } from '@mui/material/Dialog'; -import { useTranslation } from 'react-i18next'; -import DialogTitle from '@mui/material/DialogTitle'; -import { DialogActions, DialogContentText, IconButton } from '@mui/material'; -import Button from '@mui/material/Button'; -import DialogContent from '@mui/material/DialogContent'; -import { ReactComponent as CloseIcon } from '$app/assets/close.svg'; - -export const DeleteAccountDialog = (props: DialogProps) => { - const { t } = useTranslation(); - - const handleClose = () => { - props?.onClose?.({}, 'backdropClick'); - }; - - const handleOk = () => { - //123 - }; - - return ( - - {t('newSettings.myAccount.deleteAccount.dialogTitle')} - - {t('newSettings.myAccount.deleteAccount.dialogContent1')} - {t('newSettings.myAccount.deleteAccount.dialogContent2')} - - -
- -
-
- -
-
- - - -
- ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/settings/my_account/MyAccount.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/settings/my_account/MyAccount.tsx deleted file mode 100644 index b3a315994b..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/settings/my_account/MyAccount.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { useTranslation } from 'react-i18next'; -import Typography from '@mui/material/Typography'; -import { Profile } from './Profile'; -import { AccountLogin } from './AccountLogin'; -import { Divider } from '@mui/material'; -import { SettingsRoutes } from '$app/components/settings/workplace/const'; - -export const MyAccount = ({ onForward }: { onForward?: (route: SettingsRoutes) => void }) => { - const { t } = useTranslation(); - - return ( -
- - {t('newSettings.myAccount.title')} - - - {t('newSettings.myAccount.subtitle')} - - - - -
- ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/settings/my_account/Profile.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/settings/my_account/Profile.tsx deleted file mode 100644 index 2ac672b0e5..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/settings/my_account/Profile.tsx +++ /dev/null @@ -1,180 +0,0 @@ -import Typography from '@mui/material/Typography'; -import { useTranslation } from 'react-i18next'; -import { IconButton, InputAdornment, OutlinedInput } from '@mui/material'; -import { useAppSelector } from '$app/stores/store'; -import React, { useState } from 'react'; -import { ReactComponent as CheckIcon } from '$app/assets/select-check.svg'; -import { ReactComponent as CloseIcon } from '$app/assets/close.svg'; -import { ReactComponent as EditIcon } from '$app/assets/edit.svg'; - -import Tooltip from '@mui/material/Tooltip'; -import { UserService } from '$app/application/user/user.service'; -import { notify } from '$app/components/_shared/notify'; -import { ProfileAvatar } from '$app/components/_shared/avatar'; -import Popover from '@mui/material/Popover'; -import { PopoverCommonProps } from '$app/components/editor/components/tools/popover'; -import EmojiPicker from '$app/components/_shared/emoji_picker/EmojiPicker'; -import Button from '@mui/material/Button'; - -export const Profile = () => { - const { displayName, id } = useAppSelector((state) => state.currentUser); - const { t } = useTranslation(); - const [isEditing, setIsEditing] = useState(false); - const [newName, setNewName] = useState(displayName ?? 'Me'); - const [error, setError] = useState(false); - const [emojiPickerAnchor, setEmojiPickerAnchor] = useState(null); - const openEmojiPicker = Boolean(emojiPickerAnchor); - const handleSave = async () => { - setError(false); - if (!newName) { - setError(true); - return; - } - - if (newName === displayName) { - setIsEditing(false); - return; - } - - try { - await UserService.updateUserProfile({ - id, - name: newName, - }); - setIsEditing(false); - } catch { - setError(true); - notify.error(t('newSettings.myAccount.updateNameError')); - } - }; - - const handleEmojiSelect = async (emoji: string) => { - try { - await UserService.updateUserProfile({ - id, - icon_url: emoji, - }); - } catch { - notify.error(t('newSettings.myAccount.updateIconError')); - } - }; - - const handleCancel = () => { - setNewName(displayName ?? 'Me'); - setIsEditing(false); - }; - - const handleKeyDown = (e: React.KeyboardEvent) => { - if (e.key === 'Enter') { - e.stopPropagation(); - e.preventDefault(); - void handleSave(); - } - - if (e.key === 'Escape') { - e.stopPropagation(); - e.preventDefault(); - handleCancel(); - } - }; - - return ( -
- - {t('newSettings.myAccount.profileLabel')} - -
- - -
- {isEditing ? ( - setNewName(e.target.value)} - spellCheck={false} - autoFocus={true} - autoCorrect={'off'} - autoCapitalize={'off'} - fullWidth - endAdornment={ - -
- - - - - - - - - - -
-
- } - sx={{ - '&.MuiOutlinedInput-root': { - borderRadius: '8px', - }, - }} - placeholder={t('newSettings.myAccount.profileNamePlaceholder')} - value={newName} - /> - ) : ( - - {newName} - - setIsEditing(true)} size={'small'} className={'ml-1'}> - - - - - )} -
-
- {openEmojiPicker && ( - { - setEmojiPickerAnchor(null); - }} - anchorOrigin={{ - vertical: 'bottom', - horizontal: 'left', - }} - transformOrigin={{ - vertical: 'top', - horizontal: 'left', - }} - > - { - setEmojiPickerAnchor(null); - }} - onEmojiSelect={handleEmojiSelect} - /> - - )} -
- ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/settings/my_account/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/settings/my_account/index.ts deleted file mode 100644 index d923fcefce..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/settings/my_account/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './MyAccount'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/settings/workplace/Appearance.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/settings/workplace/Appearance.tsx deleted file mode 100644 index 1dc8581dae..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/settings/workplace/Appearance.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { useTranslation } from 'react-i18next'; -import { ThemeModeSwitch } from '$app/components/settings/workplace/appearance/ThemeModeSwitch'; -import Typography from '@mui/material/Typography'; -import { Divider } from '@mui/material'; -import { LanguageSetting } from '$app/components/settings/workplace/appearance/LanguageSetting'; - -export const Appearance = () => { - const { t } = useTranslation(); - - return ( - <> - - {t('newSettings.workplace.appearance.name')} - - - - - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/settings/workplace/Workplace.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/settings/workplace/Workplace.tsx deleted file mode 100644 index 8af69eec51..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/settings/workplace/Workplace.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { useTranslation } from 'react-i18next'; -import Typography from '@mui/material/Typography'; -import { WorkplaceDisplay } from '$app/components/settings/workplace/WorkplaceDisplay'; -import { Divider } from '@mui/material'; -import { Appearance } from '$app/components/settings/workplace/Appearance'; - -export const Workplace = () => { - const { t } = useTranslation(); - - return ( -
- - {t('newSettings.workplace.title')} - - - {t('newSettings.workplace.subtitle')} - - - - -
- ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/settings/workplace/WorkplaceDisplay.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/settings/workplace/WorkplaceDisplay.tsx deleted file mode 100644 index 3a71c5f070..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/settings/workplace/WorkplaceDisplay.tsx +++ /dev/null @@ -1,155 +0,0 @@ -import { useTranslation } from 'react-i18next'; -import Typography from '@mui/material/Typography'; -import { Divider, OutlinedInput } from '@mui/material'; -import React, { useMemo, useState } from 'react'; -import Button from '@mui/material/Button'; -import { useAppDispatch, useAppSelector } from '$app/stores/store'; -import { changeWorkspaceIcon, renameWorkspace } from '$app/application/folder/workspace.service'; -import { notify } from '$app/components/_shared/notify'; -import { WorkplaceAvatar } from '$app/components/_shared/avatar'; -import Popover from '@mui/material/Popover'; -import { PopoverCommonProps } from '$app/components/editor/components/tools/popover'; -import EmojiPicker from '$app/components/_shared/emoji_picker/EmojiPicker'; -import { workspaceActions } from '$app_reducers/workspace/slice'; -import debounce from 'lodash-es/debounce'; - -export const WorkplaceDisplay = () => { - const { t } = useTranslation(); - const isLocal = useAppSelector((state) => state.currentUser.isLocal); - const { workspaces, currentWorkspaceId } = useAppSelector((state) => state.workspace); - const workspace = useMemo( - () => workspaces.find((workspace) => workspace.id === currentWorkspaceId), - [workspaces, currentWorkspaceId] - ); - const [name, setName] = useState(workspace?.name ?? ''); - const [emojiPickerAnchor, setEmojiPickerAnchor] = useState(null); - const openEmojiPicker = Boolean(emojiPickerAnchor); - const dispatch = useAppDispatch(); - - const debounceUpdateWorkspace = useMemo(() => { - return debounce(async ({ id, name, icon }: { id: string; name?: string; icon?: string }) => { - if (!id || !name) return; - - if (icon) { - try { - await changeWorkspaceIcon(id, icon); - } catch { - notify.error(t('newSettings.workplace.updateIconError')); - } - } - - if (name) { - try { - await renameWorkspace(id, name); - } catch { - notify.error(t('newSettings.workplace.renameError')); - } - } - }, 500); - }, [t]); - - const handleSave = async () => { - if (!workspace || !name) return; - dispatch(workspaceActions.updateWorkspace({ id: workspace.id, name })); - - await debounceUpdateWorkspace({ id: workspace.id, name }); - }; - - const handleEmojiSelect = async (icon: string) => { - if (!workspace) return; - dispatch(workspaceActions.updateWorkspace({ id: workspace.id, icon })); - - await debounceUpdateWorkspace({ id: workspace.id, icon }); - }; - - const handleKeyDown = (e: React.KeyboardEvent) => { - if (e.key === 'Enter') { - e.stopPropagation(); - e.preventDefault(); - void handleSave(); - } - }; - - return ( -
- - {t('newSettings.workplace.workplaceName')} - -
-
- setName(e.target.value)} - sx={{ - '&.MuiOutlinedInput-root': { - borderRadius: '8px', - }, - }} - placeholder={t('newSettings.workplace.workplaceNamePlaceholder')} - value={name} - /> -
- -
- - - {t('newSettings.workplace.workplaceIcon')} - - - {t('newSettings.workplace.workplaceIconSubtitle')} - - - {openEmojiPicker && ( - { - setEmojiPickerAnchor(null); - }} - anchorOrigin={{ - vertical: 'top', - horizontal: 'right', - }} - transformOrigin={{ - vertical: 'top', - horizontal: 'left', - }} - > - { - setEmojiPickerAnchor(null); - }} - onEmojiSelect={handleEmojiSelect} - /> - - )} -
- ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/settings/workplace/appearance/LanguageSetting.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/settings/workplace/appearance/LanguageSetting.tsx deleted file mode 100644 index 41a42bd011..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/settings/workplace/appearance/LanguageSetting.tsx +++ /dev/null @@ -1,115 +0,0 @@ -import Typography from '@mui/material/Typography'; -import { useTranslation } from 'react-i18next'; -import MenuItem from '@mui/material/MenuItem'; -import Select from '@mui/material/Select'; -import React, { useCallback } from 'react'; -import { useAppDispatch, useAppSelector } from '$app/stores/store'; -import { currentUserActions } from '$app_reducers/current-user/slice'; -import { UserService } from '$app/application/user/user.service'; - -const languages = [ - { - key: 'ar-SA', - title: 'العربية', - }, - { key: 'ca-ES', title: 'Català' }, - { key: 'de-DE', title: 'Deutsch' }, - { key: 'en', title: 'English' }, - { key: 'es-VE', title: 'Español (Venezuela)' }, - { key: 'eu-ES', title: 'Español' }, - { key: 'fr-FR', title: 'Français' }, - { key: 'hu-HU', title: 'Magyar' }, - { key: 'id-ID', title: 'Bahasa Indonesia' }, - { key: 'it-IT', title: 'Italiano' }, - { key: 'ja-JP', title: '日本語' }, - { key: 'ko-KR', title: '한국어' }, - { key: 'pl-PL', title: 'Polski' }, - { key: 'pt-BR', title: 'Português' }, - { key: 'pt-PT', title: 'Português' }, - { key: 'ru-RU', title: 'Русский' }, - { key: 'sv', title: 'Svenska' }, - { key: 'th-TH', title: 'ไทย' }, - { key: 'tr-TR', title: 'Türkçe' }, - { key: 'zh-CN', title: '简体中文' }, - { key: 'zh-TW', title: '繁體中文' }, -]; - -export const LanguageSetting = () => { - const { t, i18n } = useTranslation(); - const userSettingState = useAppSelector((state) => state.currentUser.userSetting); - const dispatch = useAppDispatch(); - const selectedLanguage = userSettingState.language; - - const [hoverKey, setHoverKey] = React.useState(null); - - const handleChange = useCallback( - (language: string) => { - const newSetting = { ...userSettingState, language }; - - dispatch(currentUserActions.setUserSetting(newSetting)); - const newLanguage = newSetting.language || 'en'; - - void UserService.setAppearanceSetting({ - theme: newSetting.theme, - theme_mode: newSetting.themeMode, - locale: { - language_code: newLanguage.split('-')[0], - country_code: newLanguage.split('-')[1], - }, - }); - }, - [dispatch, userSettingState] - ); - - const handleKeyDown = useCallback((e: React.KeyboardEvent) => { - if (e.key === 'Escape') { - e.preventDefault(); - } - }, []); - - return ( - <> - - {t('newSettings.workplace.appearance.language')} - - - - - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/settings/workplace/appearance/ThemeModeSwitch.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/settings/workplace/appearance/ThemeModeSwitch.tsx deleted file mode 100644 index 34fdb8e598..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/settings/workplace/appearance/ThemeModeSwitch.tsx +++ /dev/null @@ -1,93 +0,0 @@ -import { useTranslation } from 'react-i18next'; -import { useAppDispatch, useAppSelector } from '$app/stores/store'; -import { useCallback, useMemo } from 'react'; -import { ThemeModePB } from '@/services/backend'; -import darkSrc from '$app/assets/settings/dark.png'; -import lightSrc from '$app/assets/settings/light.png'; -import { currentUserActions, ThemeMode } from '$app_reducers/current-user/slice'; -import { UserService } from '$app/application/user/user.service'; -import { ReactComponent as CheckCircle } from '$app/assets/settings/check_circle.svg'; - -export const ThemeModeSwitch = () => { - const { t } = useTranslation(); - const userSettingState = useAppSelector((state) => state.currentUser.userSetting); - const dispatch = useAppDispatch(); - - const selectedMode = userSettingState.themeMode; - const themeModes = useMemo(() => { - return [ - { - name: t('newSettings.workplace.appearance.themeMode.auto'), - value: ThemeModePB.System, - img: ( -
- - -
- ), - }, - { - name: t('newSettings.workplace.appearance.themeMode.light'), - value: ThemeModePB.Light, - img: , - }, - { - name: t('newSettings.workplace.appearance.themeMode.dark'), - value: ThemeModePB.Dark, - img: , - }, - ]; - }, [t]); - - const handleChange = useCallback( - (newValue: ThemeModePB) => { - const newSetting = { - ...userSettingState, - ...{ - themeMode: newValue, - isDark: - newValue === ThemeMode.Dark || - (newValue === ThemeMode.System && window.matchMedia('(prefers-color-scheme: dark)').matches), - }, - }; - - dispatch(currentUserActions.setUserSetting(newSetting)); - - void UserService.setAppearanceSetting({ - theme_mode: newSetting.themeMode, - }); - }, - [dispatch, userSettingState] - ); - - const renderThemeModeItem = useCallback( - (option: { name: string; value: ThemeModePB; img: JSX.Element }) => { - return ( -
handleChange(option.value)} - className={'flex cursor-pointer flex-col items-center gap-2'} - > -
- {option.img} - -
-
{option.name}
-
- ); - }, - [handleChange, selectedMode] - ); - - return
{themeModes.map((mode) => renderThemeModeItem(mode))}
; -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/settings/workplace/const.ts b/frontend/appflowy_tauri/src/appflowy_app/components/settings/workplace/const.ts deleted file mode 100644 index 075e2744a5..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/settings/workplace/const.ts +++ /dev/null @@ -1,3 +0,0 @@ -export enum SettingsRoutes { - LOGIN = 'login', -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/settings/workplace/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/settings/workplace/index.ts deleted file mode 100644 index a64592ac8b..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/settings/workplace/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './Workplace'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/trash/Trash.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/trash/Trash.hooks.ts deleted file mode 100644 index b6748614b8..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/trash/Trash.hooks.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { useCallback, useEffect, useState } from 'react'; -import { useAppDispatch, useAppSelector } from '@/appflowy_app/stores/store'; -import { trashActions, trashPBToTrash } from '$app_reducers/trash/slice'; -import { subscribeNotifications } from '$app/application/notification'; -import { FolderNotification } from '@/services/backend'; -import { deleteTrashItem, getTrash, putback, deleteAll, restoreAll } from '$app/application/folder/trash.service'; - -export function useLoadTrash() { - const trash = useAppSelector((state) => state.trash.list); - const dispatch = useAppDispatch(); - - const initializeTrash = useCallback(async () => { - const trash = await getTrash(); - - dispatch(trashActions.initTrash(trash.map(trashPBToTrash))); - }, [dispatch]); - - useEffect(() => { - void initializeTrash(); - }, [initializeTrash]); - - useEffect(() => { - const unsubscribePromise = subscribeNotifications({ - [FolderNotification.DidUpdateTrash]: async (changeset) => { - dispatch(trashActions.onTrashChanged(changeset.items.map(trashPBToTrash))); - }, - }); - - return () => { - void unsubscribePromise.then((fn) => fn()); - }; - }, [dispatch]); - - return { - trash, - }; -} - -export function useTrashActions() { - const [restoreAllDialogOpen, setRestoreAllDialogOpen] = useState(false); - const [deleteAllDialogOpen, setDeleteAllDialogOpen] = useState(false); - const [deleteDialogOpen, setDeleteDialogOpen] = useState(false); - - const [deleteId, setDeleteId] = useState(''); - - const onClickRestoreAll = () => { - setRestoreAllDialogOpen(true); - }; - - const onClickDeleteAll = () => { - setDeleteAllDialogOpen(true); - }; - - const closeDialog = () => { - setRestoreAllDialogOpen(false); - setDeleteAllDialogOpen(false); - setDeleteDialogOpen(false); - }; - - const onClickDelete = (id: string) => { - setDeleteId(id); - setDeleteDialogOpen(true); - }; - - return { - onClickDelete, - deleteDialogOpen, - deleteId, - onPutback: putback, - onDelete: deleteTrashItem, - onDeleteAll: deleteAll, - onRestoreAll: restoreAll, - onClickRestoreAll, - onClickDeleteAll, - restoreAllDialogOpen, - deleteAllDialogOpen, - closeDialog, - }; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/trash/Trash.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/trash/Trash.tsx deleted file mode 100644 index f10848dc9b..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/trash/Trash.tsx +++ /dev/null @@ -1,87 +0,0 @@ -import React, { useState } from 'react'; -import { useTranslation } from 'react-i18next'; -import Button from '@mui/material/Button'; -import { DeleteOutline, RestoreOutlined } from '@mui/icons-material'; -import { useLoadTrash, useTrashActions } from '$app/components/trash/Trash.hooks'; -import { List } from '@mui/material'; -import TrashItem from '$app/components/trash/TrashItem'; -import DeleteConfirmDialog from '$app/components/_shared/confirm_dialog/DeleteConfirmDialog'; - -function Trash() { - const { t } = useTranslation(); - const { trash } = useLoadTrash(); - const { - onPutback, - onDelete, - onClickRestoreAll, - onClickDeleteAll, - restoreAllDialogOpen, - deleteAllDialogOpen, - onRestoreAll, - onDeleteAll, - closeDialog, - deleteDialogOpen, - deleteId, - onClickDelete, - } = useTrashActions(); - const [hoverId, setHoverId] = useState(''); - - return ( -
-
-
{t('trash.text')}
-
- - -
-
-
-
{t('trash.pageHeader.fileName')}
-
{t('trash.pageHeader.lastModified')}
-
{t('trash.pageHeader.created')}
-
-
- - {trash.map((item) => ( - - ))} - - - - onDelete([deleteId])} - onClose={closeDialog} - /> -
- ); -} - -export default Trash; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/trash/TrashItem.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/trash/TrashItem.tsx deleted file mode 100644 index d266005612..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/trash/TrashItem.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import React from 'react'; -import dayjs from 'dayjs'; -import { IconButton, ListItem } from '@mui/material'; -import { DeleteOutline, RestoreOutlined } from '@mui/icons-material'; -import Tooltip from '@mui/material/Tooltip'; -import { useTranslation } from 'react-i18next'; -import { Trash } from '$app_reducers/trash/slice'; - -function TrashItem({ - item, - hoverId, - setHoverId, - onDelete, - onPutback, -}: { - setHoverId: (id: string) => void; - item: Trash; - hoverId: string; - onPutback: (id: string) => void; - onDelete: (id: string) => void; -}) { - const { t } = useTranslation(); - - return ( - { - setHoverId(item.id); - }} - onMouseLeave={() => { - setHoverId(''); - }} - key={item.id} - style={{ - paddingInline: 0, - }} - > -
-
- {item.name.trim() || t('menuAppHeader.defaultNewPageName')} -
-
{dayjs.unix(item.modifiedTime).format('MM/DD/YYYY hh:mm A')}
-
{dayjs.unix(item.createTime).format('MM/DD/YYYY hh:mm A')}
-
- - onPutback(item.id)} className={'mr-2'}> - - - - - onDelete(item.id)}> - - - -
-
-
- ); -} - -export default TrashItem; diff --git a/frontend/appflowy_tauri/src/appflowy_app/hooks/ViewId.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/hooks/ViewId.hooks.ts deleted file mode 100644 index 6711ece8c8..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/hooks/ViewId.hooks.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { createContext, useContext } from 'react'; - -const ViewIdContext = createContext(''); - -export const ViewIdProvider = ViewIdContext.Provider; -export const useViewId = () => useContext(ViewIdContext); diff --git a/frontend/appflowy_tauri/src/appflowy_app/hooks/index.ts b/frontend/appflowy_tauri/src/appflowy_app/hooks/index.ts deleted file mode 100644 index c29ddd04aa..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/hooks/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './notification.hooks'; -export * from './ViewId.hooks'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/hooks/notification.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/hooks/notification.hooks.ts deleted file mode 100644 index f8669852d3..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/hooks/notification.hooks.ts +++ /dev/null @@ -1,20 +0,0 @@ -/* eslint-disable no-redeclare */ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import { useEffect } from 'react'; -import { NotificationEnum, NotificationHandler, subscribeNotification } from '$app/application/notification'; - -export function useNotification( - notification: K, - callback: NotificationHandler, - options: { id?: string } -): void { - const { id } = options; - - useEffect(() => { - const unsubscribePromise = subscribeNotification(notification, callback, { id }); - - return () => { - void unsubscribePromise.then((fn) => fn()); - }; - }, [callback, id, notification]); -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/hooks/page.hooks.tsx b/frontend/appflowy_tauri/src/appflowy_app/hooks/page.hooks.tsx deleted file mode 100644 index 49e01e75c0..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/hooks/page.hooks.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { ViewLayoutPB } from '@/services/backend'; -import React from 'react'; -import { Page } from '$app_reducers/pages/slice'; -import { ReactComponent as DocumentIcon } from '$app/assets/document.svg'; -import { ReactComponent as GridIcon } from '$app/assets/grid.svg'; -import { ReactComponent as BoardIcon } from '$app/assets/board.svg'; -import { ReactComponent as CalendarIcon } from '$app/assets/date.svg'; - -export function getPageIcon(page: Page) { - if (page.icon) { - return page.icon.value; - } - - switch (page.layout) { - case ViewLayoutPB.Document: - return ; - case ViewLayoutPB.Grid: - return ; - case ViewLayoutPB.Board: - return ; - case ViewLayoutPB.Calendar: - return ; - default: - return null; - } -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/i18n/config.ts b/frontend/appflowy_tauri/src/appflowy_app/i18n/config.ts deleted file mode 100644 index d36dba3bb2..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/i18n/config.ts +++ /dev/null @@ -1,15 +0,0 @@ -import i18next from 'i18next'; -import LanguageDetector from 'i18next-browser-languagedetector'; -import { initReactI18next } from 'react-i18next'; -import resourcesToBackend from 'i18next-resources-to-backend'; - -void i18next - .use(resourcesToBackend((language: string) => import(`./translations/${language}.json`))) - .use(LanguageDetector) - .use(initReactI18next) - .init({ - lng: 'en', - defaultNS: 'translation', - debug: false, - fallbackLng: 'en', - }); diff --git a/frontend/appflowy_tauri/src/appflowy_app/slate-editor.d.ts b/frontend/appflowy_tauri/src/appflowy_app/slate-editor.d.ts deleted file mode 100644 index 26b713e333..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/slate-editor.d.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { ReactEditor } from 'slate-react'; - -interface EditorInlineAttributes { - bold?: boolean; - italic?: boolean; - underline?: boolean; - strikethrough?: boolean; - font_color?: string; - bg_color?: string; - href?: string; - code?: boolean; - formula?: string; - prism_token?: string; - class_name?: string; - mention?: { - type: string; - // inline page ref id - page?: string; - // reminder date ref id - date?: string; - }; -} - -type CustomElement = { - children: (CustomText | CustomElement)[]; - type: string; - data?: unknown; - blockId?: string; - textId?: string; -}; - -type CustomText = { text: string } & EditorInlineAttributes; - -declare module 'slate' { - interface CustomTypes { - Editor: BaseEditor & ReactEditor; - Element: CustomElement; - Text: CustomText; - } - - interface BaseEditor { - isEmbed: (element: CustomElement) => boolean; - } -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/current-user/slice.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/current-user/slice.ts deleted file mode 100644 index 464b7428a3..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/current-user/slice.ts +++ /dev/null @@ -1,99 +0,0 @@ -import { createSlice, PayloadAction } from '@reduxjs/toolkit'; -import { WorkspaceSettingPB } from '@/services/backend/models/flowy-folder/workspace'; -import { ThemeModePB as ThemeMode } from '@/services/backend'; -import { Page, parserViewPBToPage } from '$app_reducers/pages/slice'; - -export { ThemeMode }; - -export interface UserSetting { - theme?: Theme; - themeMode?: ThemeMode; - language?: string; - isDark?: boolean; -} - -export enum Theme { - Default = 'default', - Dandelion = 'dandelion', - Lavender = 'lavender', -} - -export enum LoginState { - Loading = 'loading', - Success = 'success', - Error = 'error', -} - -export interface UserWorkspaceSetting { - workspaceId: string; - latestView?: Page; - hasLatestView: boolean; -} - -export function parseWorkspaceSettingPBToSetting(workspaceSetting: WorkspaceSettingPB): UserWorkspaceSetting { - return { - workspaceId: workspaceSetting.workspace_id, - latestView: workspaceSetting.latest_view ? parserViewPBToPage(workspaceSetting.latest_view) : undefined, - hasLatestView: !!workspaceSetting.latest_view, - }; -} - -export interface ICurrentUser { - id?: number; - deviceId?: string; - displayName?: string; - email?: string; - token?: string; - iconUrl?: string; - isAuthenticated: boolean; - workspaceSetting?: UserWorkspaceSetting; - userSetting: UserSetting; - isLocal: boolean; - loginState?: LoginState; -} - -const initialState: ICurrentUser | null = { - isAuthenticated: false, - userSetting: {}, - isLocal: true, -}; - -export const currentUserSlice = createSlice({ - name: 'currentUser', - initialState: initialState, - reducers: { - updateUser: (state, action: PayloadAction>) => { - return { - ...state, - ...action.payload, - loginState: LoginState.Success, - }; - }, - logout: () => { - return initialState; - }, - setUserSetting: (state, action: PayloadAction>) => { - state.userSetting = { - ...state.userSetting, - ...action.payload, - }; - }, - - setLoginState: (state, action: PayloadAction) => { - state.loginState = action.payload; - }, - - resetLoginState: (state) => { - state.loginState = undefined; - }, - - setLatestView: (state, action: PayloadAction) => { - if (state.workspaceSetting) { - state.workspaceSetting.latestView = action.payload; - state.workspaceSetting.hasLatestView = true; - } - }, - }, -}); - -export const currentUserActions = currentUserSlice.actions; diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/error/slice.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/error/slice.ts deleted file mode 100644 index 9b47df7777..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/error/slice.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { createSlice, PayloadAction } from '@reduxjs/toolkit'; - -export interface IErrorOptions { - display: boolean; - message: string; -} - -const initialState: IErrorOptions = { - display: false, - message: '', -}; - -export const errorSlice = createSlice({ - name: 'error', - initialState: initialState, - reducers: { - showError(state, action: PayloadAction) { - return { - display: true, - message: action.payload, - }; - }, - hideError() { - return { - display: false, - message: '', - }; - }, - }, -}); - -export const errorActions = errorSlice.actions; diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/pages/async_actions.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/pages/async_actions.ts deleted file mode 100644 index 90014c1e7f..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/pages/async_actions.ts +++ /dev/null @@ -1,106 +0,0 @@ -import { createAsyncThunk } from '@reduxjs/toolkit'; -import { RootState } from '$app/stores/store'; -import { pagesActions } from '$app_reducers/pages/slice'; -import { movePage, setLatestOpenedPage, updatePage } from '$app/application/folder/page.service'; -import debounce from 'lodash-es/debounce'; -import { currentUserActions } from '$app_reducers/current-user/slice'; - -export const movePageThunk = createAsyncThunk( - 'pages/movePage', - async ( - payload: { - sourceId: string; - targetId: string; - insertType: 'before' | 'after' | 'inside'; - }, - thunkAPI - ) => { - const { sourceId, targetId, insertType } = payload; - const { getState, dispatch } = thunkAPI; - const { pageMap, relationMap } = (getState() as RootState).pages; - const sourcePage = pageMap[sourceId]; - const targetPage = pageMap[targetId]; - - if (!sourcePage || !targetPage) return; - const sourceParentId = sourcePage.parentId; - const targetParentId = targetPage.parentId; - - if (!sourceParentId || !targetParentId) return; - - const targetParentChildren = relationMap[targetParentId] || []; - const targetIndex = targetParentChildren.indexOf(targetId); - - if (targetIndex < 0) return; - - let prevId, parentId; - - if (insertType === 'before') { - const prevIndex = targetIndex - 1; - - parentId = targetParentId; - if (prevIndex >= 0) { - prevId = targetParentChildren[prevIndex]; - } - } else if (insertType === 'after') { - prevId = targetId; - parentId = targetParentId; - } else { - const targetChildren = relationMap[targetId] || []; - - parentId = targetId; - if (targetChildren.length > 0) { - prevId = targetChildren[targetChildren.length - 1]; - } - } - - dispatch(pagesActions.movePage({ id: sourceId, newParentId: parentId, prevId })); - - await movePage({ - view_id: sourceId, - new_parent_id: parentId, - prev_view_id: prevId, - }); - } -); - -const debounceUpdateName = debounce(updatePage, 1000); - -export const updatePageName = createAsyncThunk( - 'pages/updateName', - async (payload: { id: string; name: string; immediate?: boolean }, thunkAPI) => { - const { dispatch, getState } = thunkAPI; - const { pageMap } = (getState() as RootState).pages; - const { id, name, immediate } = payload; - const page = pageMap[id]; - - if (name === page.name) return; - - dispatch( - pagesActions.onPageChanged({ - ...page, - name, - }) - ); - - if (immediate) { - await updatePage({ id, name }); - } else { - await debounceUpdateName({ - id, - name, - }); - } - } -); - -export const openPage = createAsyncThunk('pages/openPage', async (id: string, thunkAPI) => { - const { dispatch, getState } = thunkAPI; - const { pageMap } = (getState() as RootState).pages; - - const page = pageMap[id]; - - if (!page) return; - - dispatch(currentUserActions.setLatestView(page)); - await setLatestOpenedPage(id); -}); diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/pages/slice.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/pages/slice.ts deleted file mode 100644 index dbf313ecc1..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/pages/slice.ts +++ /dev/null @@ -1,223 +0,0 @@ -import { ViewIconTypePB, ViewLayoutPB, ViewPB } from '@/services/backend'; -import { createSlice, PayloadAction } from '@reduxjs/toolkit'; -import isEqual from 'lodash-es/isEqual'; -import { ImageType } from '$app/application/document/document.types'; -import { Nullable } from 'unsplash-js/dist/helpers/typescript'; - -export const pageTypeMap = { - [ViewLayoutPB.Document]: 'document', - [ViewLayoutPB.Board]: 'board', - [ViewLayoutPB.Grid]: 'grid', - [ViewLayoutPB.Calendar]: 'calendar', -}; -export interface Page { - id: string; - parentId: string; - name: string; - layout: ViewLayoutPB; - icon?: PageIcon; - cover?: PageCover; -} - -export interface PageIcon { - ty: ViewIconTypePB; - value: string; -} - -export enum CoverType { - Color = 'CoverType.color', - Image = 'CoverType.file', - Asset = 'CoverType.asset', -} -export type PageCover = Nullable<{ - image_type?: ImageType; - cover_selection_type?: CoverType; - cover_selection?: string; -}>; - -export function parserViewPBToPage(view: ViewPB): Page { - const icon = view.icon; - - return { - id: view.id, - name: view.name, - parentId: view.parent_view_id, - layout: view.layout, - icon: icon - ? { - ty: icon.ty, - value: icon.value, - } - : undefined, - }; -} - -export interface PageState { - pageMap: Record; - relationMap: Record; - expandedIdMap: Record; - showTrashSnackbar: boolean; -} - -export const initialState: PageState = { - pageMap: {}, - relationMap: {}, - expandedIdMap: getExpandedPageIds().reduce((acc, id) => { - acc[id] = true; - return acc; - }, {} as Record), - showTrashSnackbar: false, -}; - -export const pagesSlice = createSlice({ - name: 'pages', - initialState, - reducers: { - addChildPages( - state, - action: PayloadAction<{ - childPages: Page[]; - id: string; - }> - ) { - const { childPages, id } = action.payload; - const pageMap: Record = {}; - - const children: string[] = []; - - childPages.forEach((page) => { - pageMap[page.id] = page; - children.push(page.id); - }); - - state.pageMap = { - ...state.pageMap, - ...pageMap, - }; - state.relationMap[id] = children; - }, - - onPageChanged(state, action: PayloadAction) { - const page = action.payload; - - if (!isEqual(state.pageMap[page.id], page)) { - state.pageMap[page.id] = page; - } - }, - - addPage( - state, - action: PayloadAction<{ - page: Page; - isLast?: boolean; - prevId?: string; - }> - ) { - const { page, prevId, isLast } = action.payload; - - state.pageMap[page.id] = page; - state.relationMap[page.id] = []; - - const parentId = page.parentId; - - if (isLast) { - state.relationMap[parentId]?.push(page.id); - } else { - const index = prevId ? state.relationMap[parentId]?.indexOf(prevId) ?? -1 : -1; - - state.relationMap[parentId]?.splice(index + 1, 0, page.id); - } - }, - - deletePages(state, action: PayloadAction) { - const ids = action.payload; - - ids.forEach((id) => { - const parentId = state.pageMap[id].parentId; - const parentChildren = state.relationMap[parentId]; - - state.relationMap[parentId] = parentChildren && parentChildren.filter((childId) => childId !== id); - delete state.relationMap[id]; - delete state.expandedIdMap[id]; - delete state.pageMap[id]; - }); - }, - - duplicatePage( - state, - action: PayloadAction<{ - id: string; - newId: string; - }> - ) { - const { id, newId } = action.payload; - const page = state.pageMap[id]; - const newPage = { ...page, id: newId }; - - state.pageMap[newPage.id] = newPage; - - const index = state.relationMap[page.parentId]?.indexOf(id); - - state.relationMap[page.parentId]?.splice(index ?? 0, 0, newId); - }, - - movePage( - state, - action: PayloadAction<{ - id: string; - newParentId: string; - prevId?: string; - }> - ) { - const { id, newParentId, prevId } = action.payload; - const parentId = state.pageMap[id].parentId; - const parentChildren = state.relationMap[parentId]; - - const index = parentChildren?.indexOf(id) ?? -1; - - if (index > -1) { - state.relationMap[parentId]?.splice(index, 1); - } - - state.pageMap[id].parentId = newParentId; - const newParentChildren = state.relationMap[newParentId] || []; - const prevIndex = prevId ? newParentChildren.indexOf(prevId) : -1; - - state.relationMap[newParentId]?.splice(prevIndex + 1, 0, id); - }, - - expandPage(state, action: PayloadAction) { - const id = action.payload; - - state.expandedIdMap[id] = true; - const ids = Object.keys(state.expandedIdMap).filter((id) => state.expandedIdMap[id]); - - storeExpandedPageIds(ids); - }, - - collapsePage(state, action: PayloadAction) { - const id = action.payload; - - state.expandedIdMap[id] = false; - const ids = Object.keys(state.expandedIdMap).filter((id) => state.expandedIdMap[id]); - - storeExpandedPageIds(ids); - }, - - setTrashSnackbar(state, action: PayloadAction) { - state.showTrashSnackbar = action.payload; - }, - }, -}); - -export const pagesActions = pagesSlice.actions; - -function storeExpandedPageIds(expandedPageIds: string[]) { - localStorage.setItem('expandedPageIds', JSON.stringify(expandedPageIds)); -} - -function getExpandedPageIds(): string[] { - const expandedPageIds = localStorage.getItem('expandedPageIds'); - - return expandedPageIds ? JSON.parse(expandedPageIds) : []; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/sidebar/slice.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/sidebar/slice.ts deleted file mode 100644 index fae1d59214..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/sidebar/slice.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { createSlice, PayloadAction } from '@reduxjs/toolkit'; - -interface SidebarState { - isCollapsed: boolean; - width: number; - isResizing: boolean; -} - -const initialState: SidebarState = { - isCollapsed: false, - width: 250, - isResizing: false, -}; - -export const sidebarSlice = createSlice({ - name: 'sidebar', - initialState: initialState, - reducers: { - toggleCollapse(state) { - state.isCollapsed = !state.isCollapsed; - }, - setCollapse(state, action: PayloadAction) { - state.isCollapsed = action.payload; - }, - changeWidth(state, action: PayloadAction) { - state.width = action.payload; - }, - startResizing(state) { - state.isResizing = true; - }, - stopResizing(state) { - state.isResizing = false; - }, - }, -}); - -export const sidebarActions = sidebarSlice.actions; diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/trash/slice.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/trash/slice.ts deleted file mode 100644 index 98d850f6fe..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/trash/slice.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { createSlice, PayloadAction } from '@reduxjs/toolkit'; -import { TrashPB } from '@/services/backend'; - -export interface Trash { - id: string; - name: string; - modifiedTime: number; - createTime: number; -} - -export function trashPBToTrash(trash: TrashPB) { - return { - id: trash.id, - name: trash.name, - modifiedTime: trash.modified_time, - createTime: trash.create_time, - }; -} - -interface TrashState { - list: Trash[]; -} - -const initialState: TrashState = { - list: [], -}; - -export const trashSlice = createSlice({ - name: 'trash', - initialState, - reducers: { - initTrash: (state, action: PayloadAction) => { - state.list = action.payload; - }, - onTrashChanged: (state, action: PayloadAction) => { - state.list = action.payload; - }, - }, -}); - -export const trashActions = trashSlice.actions; diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/workspace/slice.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/workspace/slice.ts deleted file mode 100644 index d071de846e..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/workspace/slice.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { createSlice, PayloadAction } from '@reduxjs/toolkit'; - -export interface WorkspaceItem { - id: string; - name: string; - icon?: string; -} - -interface WorkspaceState { - workspaces: WorkspaceItem[]; - currentWorkspaceId: string | null; -} - -const initialState: WorkspaceState = { - workspaces: [], - currentWorkspaceId: null, -}; - -export const workspaceSlice = createSlice({ - name: 'workspace', - initialState, - reducers: { - initWorkspaces: ( - state, - action: PayloadAction<{ - workspaces: WorkspaceItem[]; - currentWorkspaceId: string | null; - }> - ) => { - return action.payload; - }, - - updateWorkspace: (state, action: PayloadAction>) => { - const index = state.workspaces.findIndex((workspace) => workspace.id === action.payload.id); - - if (index !== -1) { - state.workspaces[index] = { - ...state.workspaces[index], - ...action.payload, - }; - } - }, - }, -}); - -export const workspaceActions = workspaceSlice.actions; diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/store.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/store.ts deleted file mode 100644 index 269f46884c..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/store.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux'; -import { - configureStore, - createListenerMiddleware, - TypedStartListening, - TypedAddListener, - ListenerEffectAPI, - addListener, -} from '@reduxjs/toolkit'; -import { pagesSlice } from './reducers/pages/slice'; -import { currentUserSlice } from './reducers/current-user/slice'; -import { workspaceSlice } from './reducers/workspace/slice'; -import { errorSlice } from './reducers/error/slice'; -import { sidebarSlice } from '$app_reducers/sidebar/slice'; -import { trashSlice } from '$app_reducers/trash/slice'; - -const listenerMiddlewareInstance = createListenerMiddleware({ - onError: () => console.error, -}); - -const store = configureStore({ - reducer: { - [pagesSlice.name]: pagesSlice.reducer, - [currentUserSlice.name]: currentUserSlice.reducer, - [workspaceSlice.name]: workspaceSlice.reducer, - [errorSlice.name]: errorSlice.reducer, - [sidebarSlice.name]: sidebarSlice.reducer, - [trashSlice.name]: trashSlice.reducer, - }, - middleware: (gDM) => gDM({ serializableCheck: false }).prepend(listenerMiddlewareInstance.middleware), -}); - -export { store }; - -// Infer the `RootState` and `AppDispatch` types from the store itself -export type RootState = ReturnType; -// @see https://redux-toolkit.js.org/usage/usage-with-typescript#getting-the-dispatch-type -export type AppDispatch = typeof store.dispatch; - -export type AppListenerEffectAPI = ListenerEffectAPI; - -// @see https://redux-toolkit.js.org/api/createListenerMiddleware#typescript-usage -export type AppStartListening = TypedStartListening; -export type AppAddListener = TypedAddListener; - -export const startAppListening = listenerMiddlewareInstance.startListening as AppStartListening; -export const addAppListener = addListener as AppAddListener; - -// Use throughout your app instead of plain `useDispatch` and `useSelector` -export const useAppDispatch = () => useDispatch(); -export const useAppSelector: TypedUseSelectorHook = useSelector; diff --git a/frontend/appflowy_tauri/src/appflowy_app/utils/async_queue.ts b/frontend/appflowy_tauri/src/appflowy_app/utils/async_queue.ts deleted file mode 100644 index 7e673506de..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/utils/async_queue.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { Log } from '$app/utils/log'; - -export class AsyncQueue { - private queue: T[] = []; - private isProcessing = false; - private executeFunction: (item: T) => Promise; - - constructor(executeFunction: (item: T) => Promise) { - this.executeFunction = executeFunction; - } - - enqueue(item: T): void { - this.queue.push(item); - this.processQueue(); - } - - private processQueue(): void { - if (this.isProcessing || this.queue.length === 0) { - return; - } - - const item = this.queue.shift(); - - if (!item) { - return; - } - - this.isProcessing = true; - - const executeFn = async (item: T) => { - try { - await this.processItem(item); - } catch (error) { - Log.error('queue processing error:', error); - } finally { - this.isProcessing = false; - this.processQueue(); - } - }; - - void executeFn(item); - } - - private async processItem(item: T): Promise { - try { - await this.executeFunction(item); - } catch (error) { - Log.error('queue processing error:', error); - } - } -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/utils/avatar.ts b/frontend/appflowy_tauri/src/appflowy_app/utils/avatar.ts deleted file mode 100644 index a9a752c579..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/utils/avatar.ts +++ /dev/null @@ -1,26 +0,0 @@ -export function stringToColor(string: string) { - let hash = 0; - let i; - - /* eslint-disable no-bitwise */ - for (i = 0; i < string.length; i += 1) { - hash = string.charCodeAt(i) + ((hash << 5) - hash); - } - - let color = '#'; - - for (i = 0; i < 3; i += 1) { - const value = (hash >> (i * 8)) & 0xff; - - color += `00${value.toString(16)}`.slice(-2); - } - /* eslint-enable no-bitwise */ - - return color; -} - -export function stringToShortName(string: string) { - const [firstName, lastName = ''] = string.split(' '); - - return `${firstName.charAt(0)}${lastName.charAt(0)}`; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/utils/change_notifier.ts b/frontend/appflowy_tauri/src/appflowy_app/utils/change_notifier.ts deleted file mode 100644 index 57d9f2a370..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/utils/change_notifier.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { Observable, Subject } from 'rxjs'; - -export class ChangeNotifier { - private isUnsubscribe = false; - private subject = new Subject(); - - notify(value: T) { - this.subject.next(value); - } - - get observer(): Observable | null { - if (this.isUnsubscribe) { - return null; - } - - return this.subject.asObservable(); - } - - // Unsubscribe the subject might cause [UnsubscribedError] error if there is - // ongoing Observable execution. - // - // Maybe you should use the [Subscription] that returned when call subscribe on - // [Observable] to unsubscribe. - unsubscribe = () => { - if (!this.isUnsubscribe) { - this.isUnsubscribe = true; - this.subject.unsubscribe(); - } - }; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/utils/color.ts b/frontend/appflowy_tauri/src/appflowy_app/utils/color.ts deleted file mode 100644 index 025c8c45ed..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/utils/color.ts +++ /dev/null @@ -1,50 +0,0 @@ -export enum ColorEnum { - Purple = 'appflowy_them_color_tint1', - Pink = 'appflowy_them_color_tint2', - LightPink = 'appflowy_them_color_tint3', - Orange = 'appflowy_them_color_tint4', - Yellow = 'appflowy_them_color_tint5', - Lime = 'appflowy_them_color_tint6', - Green = 'appflowy_them_color_tint7', - Aqua = 'appflowy_them_color_tint8', - Blue = 'appflowy_them_color_tint9', -} - -export const colorMap = { - [ColorEnum.Purple]: 'var(--tint-purple)', - [ColorEnum.Pink]: 'var(--tint-pink)', - [ColorEnum.LightPink]: 'var(--tint-red)', - [ColorEnum.Orange]: 'var(--tint-orange)', - [ColorEnum.Yellow]: 'var(--tint-yellow)', - [ColorEnum.Lime]: 'var(--tint-lime)', - [ColorEnum.Green]: 'var(--tint-green)', - [ColorEnum.Aqua]: 'var(--tint-aqua)', - [ColorEnum.Blue]: 'var(--tint-blue)', -}; - -// Convert ARGB to RGBA -// Flutter uses ARGB, but CSS uses RGBA -function argbToRgba(color: string): string { - const hex = color.replace(/^#|0x/, ''); - - const hasAlpha = hex.length === 8; - - if (!hasAlpha) { - return color.replace('0x', '#'); - } - - const r = parseInt(hex.slice(2, 4), 16); - const g = parseInt(hex.slice(4, 6), 16); - const b = parseInt(hex.slice(6, 8), 16); - const a = hasAlpha ? parseInt(hex.slice(0, 2), 16) / 255 : 1; - - return `rgba(${r}, ${g}, ${b}, ${a})`; -} - -export function renderColor(color: string) { - if (colorMap[color as ColorEnum]) { - return colorMap[color as ColorEnum]; - } - - return argbToRgba(color); -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/utils/emoji.ts b/frontend/appflowy_tauri/src/appflowy_app/utils/emoji.ts deleted file mode 100644 index 8d5adb5df6..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/utils/emoji.ts +++ /dev/null @@ -1,9 +0,0 @@ -import emojiData, { EmojiMartData } from '@emoji-mart/data'; - -export const randomEmoji = (skin = 0) => { - const emojis = (emojiData as EmojiMartData).emojis; - const keys = Object.keys(emojis); - const randomKey = keys[Math.floor(Math.random() * keys.length)]; - - return emojis[randomKey].skins[skin].native; -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/utils/env.ts b/frontend/appflowy_tauri/src/appflowy_app/utils/env.ts deleted file mode 100644 index 064dc042aa..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/utils/env.ts +++ /dev/null @@ -1,11 +0,0 @@ -export function isApple() { - return typeof navigator !== 'undefined' && /Mac OS X/.test(navigator.userAgent); -} - -export function isTauri() { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - const isTauri = window.__TAURI__; - - return isTauri; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/utils/hotkeys.ts b/frontend/appflowy_tauri/src/appflowy_app/utils/hotkeys.ts deleted file mode 100644 index 20aa05db27..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/utils/hotkeys.ts +++ /dev/null @@ -1,134 +0,0 @@ -import isHotkey from 'is-hotkey'; - -export const isMac = () => { - return navigator.userAgent.includes('Mac OS X'); -}; - -const MODIFIERS = { - control: 'Ctrl', - meta: '⌘', -}; - -export const getModifier = () => { - return isMac() ? MODIFIERS.meta : MODIFIERS.control; -}; - -export enum HOT_KEY_NAME { - LEFT = 'left', - RIGHT = 'right', - SELECT_ALL = 'select-all', - ESCAPE = 'escape', - ALIGN_LEFT = 'align-left', - ALIGN_CENTER = 'align-center', - ALIGN_RIGHT = 'align-right', - BOLD = 'bold', - ITALIC = 'italic', - UNDERLINE = 'underline', - STRIKETHROUGH = 'strikethrough', - CODE = 'code', - TOGGLE_TODO = 'toggle-todo', - TOGGLE_COLLAPSE = 'toggle-collapse', - INDENT_BLOCK = 'indent-block', - OUTDENT_BLOCK = 'outdent-block', - INSERT_SOFT_BREAK = 'insert-soft-break', - SPLIT_BLOCK = 'split-block', - BACKSPACE = 'backspace', - OPEN_LINK = 'open-link', - OPEN_LINKS = 'open-links', - EXTEND_LINE_BACKWARD = 'extend-line-backward', - EXTEND_LINE_FORWARD = 'extend-line-forward', - PASTE = 'paste', - PASTE_PLAIN_TEXT = 'paste-plain-text', - HIGH_LIGHT = 'high-light', - EXTEND_DOCUMENT_BACKWARD = 'extend-document-backward', - EXTEND_DOCUMENT_FORWARD = 'extend-document-forward', - SCROLL_TO_TOP = 'scroll-to-top', - SCROLL_TO_BOTTOM = 'scroll-to-bottom', - FORMAT_LINK = 'format-link', - FIND_REPLACE = 'find-replace', - /** - * Navigation - */ - TOGGLE_THEME = 'toggle-theme', - TOGGLE_SIDEBAR = 'toggle-sidebar', -} - -const defaultHotKeys = { - [HOT_KEY_NAME.ALIGN_LEFT]: ['control+shift+l'], - [HOT_KEY_NAME.ALIGN_CENTER]: ['control+shift+e'], - [HOT_KEY_NAME.ALIGN_RIGHT]: ['control+shift+r'], - [HOT_KEY_NAME.BOLD]: ['mod+b'], - [HOT_KEY_NAME.ITALIC]: ['mod+i'], - [HOT_KEY_NAME.UNDERLINE]: ['mod+u'], - [HOT_KEY_NAME.STRIKETHROUGH]: ['mod+shift+s', 'mod+shift+x'], - [HOT_KEY_NAME.CODE]: ['mod+e'], - [HOT_KEY_NAME.TOGGLE_TODO]: ['mod+enter'], - [HOT_KEY_NAME.TOGGLE_COLLAPSE]: ['mod+enter'], - [HOT_KEY_NAME.SELECT_ALL]: ['mod+a'], - [HOT_KEY_NAME.ESCAPE]: ['esc'], - [HOT_KEY_NAME.INDENT_BLOCK]: ['tab'], - [HOT_KEY_NAME.OUTDENT_BLOCK]: ['shift+tab'], - [HOT_KEY_NAME.SPLIT_BLOCK]: ['enter'], - [HOT_KEY_NAME.INSERT_SOFT_BREAK]: ['shift+enter'], - [HOT_KEY_NAME.BACKSPACE]: ['backspace', 'shift+backspace'], - [HOT_KEY_NAME.OPEN_LINK]: ['opt+enter'], - [HOT_KEY_NAME.OPEN_LINKS]: ['opt+shift+enter'], - [HOT_KEY_NAME.EXTEND_LINE_BACKWARD]: ['opt+shift+left'], - [HOT_KEY_NAME.EXTEND_LINE_FORWARD]: ['opt+shift+right'], - [HOT_KEY_NAME.PASTE]: ['mod+v'], - [HOT_KEY_NAME.PASTE_PLAIN_TEXT]: ['mod+shift+v'], - [HOT_KEY_NAME.HIGH_LIGHT]: ['mod+shift+h'], - [HOT_KEY_NAME.EXTEND_DOCUMENT_BACKWARD]: ['mod+shift+up'], - [HOT_KEY_NAME.EXTEND_DOCUMENT_FORWARD]: ['mod+shift+down'], - [HOT_KEY_NAME.SCROLL_TO_TOP]: ['home'], - [HOT_KEY_NAME.SCROLL_TO_BOTTOM]: ['end'], - [HOT_KEY_NAME.TOGGLE_THEME]: ['mod+shift+l'], - [HOT_KEY_NAME.TOGGLE_SIDEBAR]: ['mod+.'], - [HOT_KEY_NAME.FORMAT_LINK]: ['mod+k'], - [HOT_KEY_NAME.LEFT]: ['left'], - [HOT_KEY_NAME.RIGHT]: ['right'], - [HOT_KEY_NAME.FIND_REPLACE]: ['mod+f'], -}; - -const replaceModifier = (hotkey: string) => { - return hotkey.replace('mod', getModifier()).replace('control', 'ctrl'); -}; - -/** - * Create a hotkey checker. - * @example trigger strike through when user press "Cmd + Shift + S" or "Cmd + Shift + X" - * @param hotkeyName - * @param customHotKeys - */ -export const createHotkey = (hotkeyName: HOT_KEY_NAME, customHotKeys?: Record) => { - const keys = customHotKeys || defaultHotKeys; - const hotkeys = keys[hotkeyName]; - - return (event: KeyboardEvent) => { - return hotkeys.some((hotkey) => { - return isHotkey(hotkey, event); - }); - }; -}; - -/** - * Create a hotkey label. - * eg. "Ctrl + B / ⌘ + B" - * @param hotkeyName - * @param customHotKeys - */ -export const createHotKeyLabel = (hotkeyName: HOT_KEY_NAME, customHotKeys?: Record) => { - const keys = customHotKeys || defaultHotKeys; - const hotkeys = keys[hotkeyName].map((key) => replaceModifier(key)); - - return hotkeys - .map((hotkey) => - hotkey - .split('+') - .map((key) => { - return key === ' ' ? 'Space' : key.charAt(0).toUpperCase() + key.slice(1); - }) - .join(' + ') - ) - .join(' / '); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/utils/list.ts b/frontend/appflowy_tauri/src/appflowy_app/utils/list.ts deleted file mode 100644 index 6e5d22ccda..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/utils/list.ts +++ /dev/null @@ -1,45 +0,0 @@ -const romanMap: [number, string][] = [ - [1000, 'M'], - [900, 'CM'], - [500, 'D'], - [400, 'CD'], - [100, 'C'], - [90, 'XC'], - [50, 'L'], - [40, 'XL'], - [10, 'X'], - [9, 'IX'], - [5, 'V'], - [4, 'IV'], - [1, 'I'], -]; - -export function romanize(num: number): string { - let result = ''; - let nextNum = num; - - for (const [value, symbol] of romanMap) { - const count = Math.floor(nextNum / value); - - nextNum -= value * count; - result += symbol.repeat(count); - if (nextNum === 0) break; - } - - return result; -} - -export function letterize(num: number): string { - let nextNum = num; - let letters = ''; - - while (nextNum > 0) { - nextNum--; - const letter = String.fromCharCode((nextNum % 26) + 'a'.charCodeAt(0)); - - letters = letter + letters; - nextNum = Math.floor(nextNum / 26); - } - - return letters; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/utils/log.ts b/frontend/appflowy_tauri/src/appflowy_app/utils/log.ts deleted file mode 100644 index daccf21d0a..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/utils/log.ts +++ /dev/null @@ -1,20 +0,0 @@ -export class Log { - static error(...msg: unknown[]) { - console.error(...msg); - } - static info(...msg: unknown[]) { - console.info(...msg); - } - - static debug(...msg: unknown[]) { - console.debug(...msg); - } - - static trace(...msg: unknown[]) { - console.trace(...msg); - } - - static warn(...msg: unknown[]) { - console.warn(...msg); - } -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/utils/mui.ts b/frontend/appflowy_tauri/src/appflowy_app/utils/mui.ts deleted file mode 100644 index 94e2cf94d5..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/utils/mui.ts +++ /dev/null @@ -1,168 +0,0 @@ -import { ThemeOptions } from '@mui/material'; - -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-ignore -export const getDesignTokens = (isDark: boolean): ThemeOptions => { - return { - typography: { - fontFamily: ['Poppins'].join(','), - fontSize: 12, - button: { - textTransform: 'none', - }, - }, - components: { - MuiMenuItem: { - defaultProps: { - sx: { - '&.Mui-selected.Mui-focusVisible': { - backgroundColor: 'var(--fill-list-hover)', - }, - '&.Mui-focusVisible': { - backgroundColor: 'unset', - }, - }, - }, - }, - MuiIconButton: { - styleOverrides: { - root: { - '&:hover': { - backgroundColor: 'var(--fill-list-hover)', - }, - borderRadius: '4px', - padding: '2px', - }, - }, - }, - MuiButton: { - styleOverrides: { - contained: { - color: 'var(--content-on-fill)', - boxShadow: 'var(--shadow)', - }, - containedPrimary: { - '&:hover': { - backgroundColor: 'var(--fill-default)', - }, - }, - containedInherit: { - color: 'var(--text-title)', - backgroundColor: isDark ? 'rgba(0, 0, 0, 0.4)' : 'rgba(255, 255, 255, 0.4)', - '&:hover': { - backgroundColor: 'var(--bg-body)', - boxShadow: 'var(--shadow)', - }, - }, - outlinedInherit: { - color: 'var(--text-title)', - borderColor: 'var(--line-border)', - '&:hover': { - boxShadow: 'var(--shadow)', - }, - }, - }, - }, - MuiButtonBase: { - defaultProps: { - sx: { - '&.Mui-selected:hover': { - backgroundColor: 'var(--fill-list-hover)', - }, - }, - }, - styleOverrides: { - root: { - '&:hover': { - backgroundColor: 'var(--fill-list-hover)', - }, - '&:active': { - backgroundColor: 'var(--fill-list-hover)', - }, - borderRadius: '4px', - padding: '2px', - boxShadow: 'none', - }, - }, - }, - MuiPaper: { - styleOverrides: { - root: { - backgroundImage: 'none', - }, - }, - }, - MuiDialog: { - defaultProps: { - sx: { - '& .MuiBackdrop-root': { - backgroundColor: 'var(--bg-mask)', - }, - }, - }, - }, - - MuiTooltip: { - styleOverrides: { - arrow: { - color: 'var(--bg-tips)', - }, - tooltip: { - backgroundColor: 'var(--bg-tips)', - color: 'var(--text-title)', - fontSize: '0.85rem', - borderRadius: '8px', - fontWeight: 400, - }, - }, - }, - MuiInputBase: { - styleOverrides: { - input: { - backgroundColor: 'transparent !important', - }, - }, - }, - MuiDivider: { - styleOverrides: { - root: { - borderColor: 'var(--line-divider)', - }, - }, - }, - }, - palette: { - mode: isDark ? 'dark' : 'light', - primary: { - main: '#00BCF0', - dark: '#00BCF0', - }, - error: { - main: '#FB006D', - dark: '#D32772', - }, - warning: { - main: '#FFC107', - dark: '#E9B320', - }, - info: { - main: '#00BCF0', - dark: '#2E9DBB', - }, - success: { - main: '#66CF80', - dark: '#3BA856', - }, - text: { - primary: isDark ? '#E2E9F2' : '#333333', - secondary: isDark ? '#7B8A9D' : '#828282', - disabled: isDark ? '#363D49' : '#F2F2F2', - }, - divider: isDark ? '#59647A' : '#BDBDBD', - background: { - default: isDark ? '#1A202C' : '#FFFFFF', - paper: isDark ? '#1A202C' : '#FFFFFF', - }, - }, - }; -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/utils/open_url.ts b/frontend/appflowy_tauri/src/appflowy_app/utils/open_url.ts deleted file mode 100644 index d854be5211..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/utils/open_url.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { open as openWindow } from '@tauri-apps/api/shell'; - -const urlPattern = /^(https?:\/\/)?([\da-z.-]+)\.([a-z.]{2,6})(\S*)*\/?(\?[=&\w.%-]*)?(#[\w.\-!~*'()]*)?$/; -const ipPattern = /^(https?:\/\/)?(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})(:\d{1,5})?$/; - -export function isUrl(str: string) { - return urlPattern.test(str) || ipPattern.test(str); -} - -export function openUrl(str: string) { - if (isUrl(str)) { - const linkPrefix = ['http://', 'https://', 'file://', 'ftp://', 'ftps://', 'mailto:']; - - if (linkPrefix.some((prefix) => str.startsWith(prefix))) { - void openWindow(str); - } else { - void openWindow('https://' + str); - } - } else { - // open google search - void openWindow('https://www.google.com/search?q=' + encodeURIComponent(str)); - } -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/utils/tool.ts b/frontend/appflowy_tauri/src/appflowy_app/utils/tool.ts deleted file mode 100644 index afcd7a32b4..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/utils/tool.ts +++ /dev/null @@ -1,57 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ - -/** - * Creates an interval that repeatedly calls the given function with a specified delay. - * - * @param {Function} fn - The function to be called repeatedly. - * @param {number} [delay] - The delay between function calls in milliseconds. - * @param {Object} [options] - Additional options for the interval. - * @param {boolean} [options.immediate] - Whether to immediately call the function when the interval is created. Default is true. - * - * @return {Function} - The function that runs the interval. - * @return {Function.cancel} - A method to cancel the interval. - * - * @example - * const log = interval((message) => console.log(message), 1000); - * - * log('foo'); // prints 'foo' every second. - * - * log('bar'); // change to prints 'bar' every second. - * - * log.cancel(); // stops the interval. - */ -export function interval any = (...args: any[]) => any>( - fn: T, - delay?: number, - options?: { immediate?: boolean } -): T & { cancel: () => void } { - const { immediate = true } = options || {}; - let intervalId: NodeJS.Timer | null = null; - let parameters: any[] = []; - - function run(...args: Parameters) { - parameters = args; - - if (intervalId !== null) { - return; - } - - immediate && fn.apply(undefined, parameters); - intervalId = setInterval(() => { - fn.apply(undefined, parameters); - }, delay); - } - - function cancel() { - if (intervalId === null) { - return; - } - - clearInterval(intervalId); - intervalId = null; - parameters = []; - } - - run.cancel = cancel; - return run as T & { cancel: () => void }; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/utils/upload_image.ts b/frontend/appflowy_tauri/src/appflowy_app/utils/upload_image.ts deleted file mode 100644 index 22213ac8b3..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/utils/upload_image.ts +++ /dev/null @@ -1,9 +0,0 @@ -export const MAX_IMAGE_SIZE = 10 * 1024 * 1024; // 10MB -export const ALLOWED_IMAGE_EXTENSIONS = ['jpg', 'jpeg', 'png', 'gif', 'svg', 'webp']; -export const IMAGE_DIR = 'images'; - -export function getFileName(url: string) { - const [...parts] = url.split('/'); - - return parts.pop() ?? url; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/views/DatabasePage.tsx b/frontend/appflowy_tauri/src/appflowy_app/views/DatabasePage.tsx deleted file mode 100644 index 004fc4355b..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/views/DatabasePage.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { useParams } from 'react-router-dom'; -import { ViewIdProvider } from '$app/hooks'; -import { Database, DatabaseTitle, useSelectDatabaseView } from '../components/database'; - -export const DatabasePage = () => { - const viewId = useParams().id; - - const { selectedViewId, onChange } = useSelectDatabaseView({ - viewId, - }); - - if (!viewId) { - return null; - } - - return ( -
- - - - -
- ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/views/DocumentPage.tsx b/frontend/appflowy_tauri/src/appflowy_app/views/DocumentPage.tsx deleted file mode 100644 index 03ba493c10..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/views/DocumentPage.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import React from 'react'; -import { useParams } from 'react-router-dom'; -import { Document } from '$app/components/document'; - -function DocumentPage() { - const params = useParams(); - - const documentId = params.id; - - if (!documentId) return null; - return ; -} - -export default DocumentPage; diff --git a/frontend/appflowy_tauri/src/appflowy_app/views/TrashPage.tsx b/frontend/appflowy_tauri/src/appflowy_app/views/TrashPage.tsx deleted file mode 100644 index 78baa9872d..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/views/TrashPage.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import React from 'react'; -import Trash from '$app/components/trash/Trash'; - -function TrashPage() { - return ( -
- -
- ); -} - -export default TrashPage; diff --git a/frontend/appflowy_tauri/src/appflowy_app/vite-env.d.ts b/frontend/appflowy_tauri/src/appflowy_app/vite-env.d.ts deleted file mode 100644 index b1f45c7866..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/vite-env.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -/// -/// diff --git a/frontend/appflowy_tauri/src/main.tsx b/frontend/appflowy_tauri/src/main.tsx deleted file mode 100644 index e53dc96c43..0000000000 --- a/frontend/appflowy_tauri/src/main.tsx +++ /dev/null @@ -1,8 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom/client'; -import App from './appflowy_app/App'; -import './styles/tailwind.css'; -import './styles/font.css'; -import './styles/template.css'; - -ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(); diff --git a/frontend/appflowy_tauri/src/services/backend/index.ts b/frontend/appflowy_tauri/src/services/backend/index.ts deleted file mode 100644 index 3e02ff7183..0000000000 --- a/frontend/appflowy_tauri/src/services/backend/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -export * from "./models/flowy-user"; -export * from "./models/flowy-database2"; -export * from "./models/flowy-folder"; -export * from "./models/flowy-document"; -export * from "./models/flowy-error"; -export * from "./models/flowy-config"; -export * from "./models/flowy-date"; -export * from "./models/flowy-search"; -export * from "./models/flowy-storage"; diff --git a/frontend/appflowy_tauri/src/styles/font.css b/frontend/appflowy_tauri/src/styles/font.css deleted file mode 100644 index 514b5a6e38..0000000000 --- a/frontend/appflowy_tauri/src/styles/font.css +++ /dev/null @@ -1,125 +0,0 @@ -@font-face { - font-family: 'Poppins'; - src: url('/google_fonts/Poppins/Poppins-Thin.ttf') format('truetype'); - font-weight: 100; - font-style: normal; -} - -@font-face { - font-family: 'Poppins'; - src: url('/google_fonts/Poppins/Poppins-ThinItalic.ttf') format('truetype'); - font-weight: 100; - font-style: italic; -} - -@font-face { - font-family: 'Poppins'; - src: url('/google_fonts/Poppins/Poppins-ExtraLight.ttf') format('truetype'); - font-weight: 200; - font-style: normal; -} - -@font-face { - font-family: 'Poppins'; - src: url('/google_fonts/Poppins/Poppins-ExtraLightItalic.ttf') format('truetype'); - font-weight: 200; - font-style: italic; -} - -@font-face { - font-family: 'Poppins'; - src: url('/google_fonts/Poppins/Poppins-Light.ttf') format('truetype'); - font-weight: 300; - font-style: normal; -} - -@font-face { - font-family: 'Poppins'; - src: url('/google_fonts/Poppins/Poppins-LightItalic.ttf') format('truetype'); - font-weight: 300; - font-style: italic; -} - -@font-face { - font-family: 'Poppins'; - src: url('/google_fonts/Poppins/Poppins-Regular.ttf') format('truetype'); - font-weight: 400; - font-style: normal; -} - -@font-face { - font-family: 'Poppins'; - src: url('/google_fonts/Poppins/Poppins-Italic.ttf') format('truetype'); - font-weight: 400; - font-style: italic; -} - -@font-face { - font-family: 'Poppins'; - src: url('/google_fonts/Poppins/Poppins-Medium.ttf') format('truetype'); - font-weight: 500; - font-style: normal; -} - -@font-face { - font-family: 'Poppins'; - src: url('/google_fonts/Poppins/Poppins-MediumItalic.ttf') format('truetype'); - font-weight: 500; - font-style: italic; -} - -@font-face { - font-family: 'Poppins'; - src: url('/google_fonts/Poppins/Poppins-SemiBold.ttf') format('truetype'); - font-weight: 600; - font-style: normal; -} - -@font-face { - font-family: 'Poppins'; - src: url('/google_fonts/Poppins/Poppins-SemiBoldItalic.ttf') format('truetype'); - font-weight: 600; - font-style: italic; -} - -@font-face { - font-family: 'Poppins'; - src: url('/google_fonts/Poppins/Poppins-Bold.ttf') format('truetype'); - font-weight: 700; - font-style: normal; -} - -@font-face { - font-family: 'Poppins'; - src: url('/google_fonts/Poppins/Poppins-BoldItalic.ttf') format('truetype'); - font-weight: 700; - font-style: italic; -} - -@font-face { - font-family: 'Poppins'; - src: url('/google_fonts/Poppins/Poppins-ExtraBold.ttf') format('truetype'); - font-weight: 800; - font-style: normal; -} - -@font-face { - font-family: 'Poppins'; - src: url('/google_fonts/Poppins/Poppins-ExtraBoldItalic.ttf') format('truetype'); - font-weight: 800; - font-style: italic; -} - -@font-face { - font-family: 'Poppins'; - src: url('/google_fonts/Poppins/Poppins-Black.ttf') format('truetype'); - font-weight: 900; - font-style: normal; -} - -@font-face { - font-family: 'Poppins'; - src: url('/google_fonts/Poppins/Poppins-BlackItalic.ttf') format('truetype'); - font-weight: 900; - font-style: italic; -} diff --git a/frontend/appflowy_tauri/src/styles/tailwind.css b/frontend/appflowy_tauri/src/styles/tailwind.css deleted file mode 100644 index b5c61c9567..0000000000 --- a/frontend/appflowy_tauri/src/styles/tailwind.css +++ /dev/null @@ -1,3 +0,0 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; diff --git a/frontend/appflowy_tauri/src/styles/template.css b/frontend/appflowy_tauri/src/styles/template.css deleted file mode 100644 index 1bff6bdc76..0000000000 --- a/frontend/appflowy_tauri/src/styles/template.css +++ /dev/null @@ -1,60 +0,0 @@ -@import './variables/index.css'; - -* { - margin: 0; - padding: 0; -} - -/* stop body from scrolling */ -html, -body { - margin: 0; - height: 100%; - overflow: hidden; -} -[contenteditable] { - -webkit-tap-highlight-color: transparent; -} -input, -textarea { - outline: 0; - background: transparent; -} - -body { - font-family: Poppins, serif; -} - -::-webkit-scrollbar { - width: 8px; -} - - - -:root[data-dark-mode=true] body { - scrollbar-color: #fff var(--bg-body); -} - -body { - scrollbar-track-color: var(--bg-body); - scrollbar-shadow-color: var(--bg-body); -} - - -.btn { - @apply rounded-xl border border-line-divider px-4 py-3; -} - -.btn-primary { - @apply bg-fill-default text-text-title hover:bg-fill-list-hover; -} - -.input { - @apply rounded-xl border border-line-divider px-[18px] py-[14px] text-sm; -} - - -th { - @apply text-left font-normal; -} - diff --git a/frontend/appflowy_tauri/src/styles/variables/dark.variables.css b/frontend/appflowy_tauri/src/styles/variables/dark.variables.css deleted file mode 100644 index ca7544687b..0000000000 --- a/frontend/appflowy_tauri/src/styles/variables/dark.variables.css +++ /dev/null @@ -1,121 +0,0 @@ -/** -* Do not edit directly -* Generated on Tue, 19 Mar 2024 03:48:58 GMT -* Generated from $pnpm css:variables -*/ - -:root[data-dark-mode=true] { - --base-light-neutral-50: #f9fafd; - --base-light-neutral-100: #edeef2; - --base-light-neutral-200: #e2e4eb; - --base-light-neutral-300: #f2f2f2; - --base-light-neutral-400: #e0e0e0; - --base-light-neutral-500: #bdbdbd; - --base-light-neutral-600: #828282; - --base-light-neutral-700: #4f4f4f; - --base-light-neutral-800: #333333; - --base-light-neutral-900: #1f2329; - --base-light-neutral-1000: #000000; - --base-light-neutral-00: #ffffff; - --base-light-blue-50: #f2fcff; - --base-light-blue-100: #e0f8ff; - --base-light-blue-200: #a6ecff; - --base-light-blue-300: #52d1f4; - --base-light-blue-400: #00bcf0; - --base-light-blue-500: #05ade2; - --base-light-blue-600: #009fd1; - --base-light-color-deep-red: #fb006d; - --base-light-color-deep-yellow: #ffd667; - --base-light-color-deep-green: #66cf80; - --base-light-color-deep-blue: #00bcf0; - --base-light-color-light-purple: #e8e0ff; - --base-light-color-light-pink: #ffe7ee; - --base-light-color-light-orange: #ffefe3; - --base-light-color-light-yellow: #fff2cd; - --base-light-color-light-lime: #f5ffdc; - --base-light-color-light-green: #ddffd6; - --base-light-color-light-aqua: #defff1; - --base-light-color-light-blue: #e1fbff; - --base-light-color-light-red: #ffdddd; - --base-black-neutral-100: #252F41; - --base-black-neutral-200: #313c51; - --base-black-neutral-300: #3c4557; - --base-black-neutral-400: #525A69; - --base-black-neutral-500: #59647a; - --base-black-neutral-600: #87A0BF; - --base-black-neutral-700: #99a6b8; - --base-black-neutral-800: #e2e9f2; - --base-black-neutral-900: #eff4fb; - --base-black-neutral-1000: #ffffff; - --base-black-neutral-n50: #232b38; - --base-black-neutral-n00: #1a202c; - --base-black-blue-50: #232b38; - --base-black-blue-100: #005174; - --base-black-blue-200: #a6ecff; - --base-black-blue-300: #52d1f4; - --base-black-blue-400: #00bcf0; - --base-black-blue-500: #05ade2; - --base-black-blue-600: #009fd1; - --base-black-color-deep-red: #d32772; - --base-black-color-deep-yellow: #e9b320; - --base-black-color-deep-green: #3ba856; - --base-black-color-deep-blue: #2e9dbb; - --base-black-color-light-purple: #4D4078; - --base-black-color-light-blue: #2C3B58; - --base-black-color-light-green: #3C5133; - --base-black-color-light-yellow: #695E3E; - --base-black-color-light-pink: #5E3C5E; - --base-black-color-light-red: #56363F; - --base-black-color-light-aqua: #1B3849; - --base-black-color-light-lime: #394027; - --base-black-color-light-orange: #5E3C3C; - --base-else-brand: #2c144b; - --text-title: #e2e9f2; - --text-caption: #87A0BF; - --text-placeholder: #3c4557; - --text-link-default: #00bcf0; - --text-link-hover: #52d1f4; - --text-link-pressed: #009fd1; - --text-link-disabled: #005174; - --icon-primary: #e2e9f2; - --icon-secondary: #59647a; - --icon-disabled: #525A69; - --icon-on-toolbar: white; - --line-border: #59647a; - --line-divider: #252F41; - --line-on-toolbar: #99a6b8; - --fill-default: #00bcf0; - --fill-hover: #005174; - --fill-toolbar: #0F111C; - --fill-selector: #232b38; - --fill-list-active: #3c4557; - --fill-list-hover: #005174; - --content-blue-400: #00bcf0; - --content-blue-300: #52d1f4; - --content-blue-600: #009fd1; - --content-blue-100: #005174; - --content-on-fill: #1a202c; - --content-on-tag: #99a6b8; - --content-blue-50: #232b38; - --bg-body: #1a202c; - --bg-base: #232b38; - --bg-mask: rgba(0,0,0,0.7); - --bg-tips: #005174; - --bg-brand: #2c144b; - --function-error: #d32772; - --function-warning: #e9b320; - --function-success: #3ba856; - --function-info: #2e9dbb; - --tint-red: #56363F; - --tint-green: #3C5133; - --tint-purple: #4D4078; - --tint-blue: #2C3B58; - --tint-yellow: #695E3E; - --tint-pink: #5E3C5E; - --tint-lime: #394027; - --tint-aqua: #1B3849; - --tint-orange: #5E3C3C; - --shadow: 0px 0px 25px 0px rgba(0,0,0,0.3); - --scrollbar-track: #252F41; - --scrollbar-thumb: #3c4557; -} \ No newline at end of file diff --git a/frontend/appflowy_tauri/src/styles/variables/index.css b/frontend/appflowy_tauri/src/styles/variables/index.css deleted file mode 100644 index 08d6a948f1..0000000000 --- a/frontend/appflowy_tauri/src/styles/variables/index.css +++ /dev/null @@ -1,7 +0,0 @@ -@import "./light.variables.css"; -@import "./dark.variables.css"; - -:root { - /* resize popover shadow */ - --shadow-resize-popover: 0px 5px 5px -3px rgba(0,0,0,0.2),0px 8px 10px 1px rgba(0,0,0,0.14),0px 3px 14px 2px rgba(0,0,0,0.12); -} \ No newline at end of file diff --git a/frontend/appflowy_tauri/src/styles/variables/light.variables.css b/frontend/appflowy_tauri/src/styles/variables/light.variables.css deleted file mode 100644 index 26acc76f0a..0000000000 --- a/frontend/appflowy_tauri/src/styles/variables/light.variables.css +++ /dev/null @@ -1,124 +0,0 @@ -/** -* Do not edit directly -* Generated on Tue, 19 Mar 2024 03:48:58 GMT -* Generated from $pnpm css:variables -*/ - -:root { - --base-light-neutral-50: #f9fafd; - --base-light-neutral-100: #edeef2; - --base-light-neutral-200: #e2e4eb; - --base-light-neutral-300: #f2f2f2; - --base-light-neutral-400: #e0e0e0; - --base-light-neutral-500: #bdbdbd; - --base-light-neutral-600: #828282; - --base-light-neutral-700: #4f4f4f; - --base-light-neutral-800: #333333; - --base-light-neutral-900: #1f2329; - --base-light-neutral-1000: #000000; - --base-light-neutral-00: #ffffff; - --base-light-blue-50: #f2fcff; - --base-light-blue-100: #e0f8ff; - --base-light-blue-200: #a6ecff; - --base-light-blue-300: #52d1f4; - --base-light-blue-400: #00bcf0; - --base-light-blue-500: #05ade2; - --base-light-blue-600: #009fd1; - --base-light-color-deep-red: #fb006d; - --base-light-color-deep-yellow: #ffd667; - --base-light-color-deep-green: #66cf80; - --base-light-color-deep-blue: #00bcf0; - --base-light-color-light-purple: #e8e0ff; - --base-light-color-light-pink: #ffe7ee; - --base-light-color-light-orange: #ffefe3; - --base-light-color-light-yellow: #fff2cd; - --base-light-color-light-lime: #f5ffdc; - --base-light-color-light-green: #ddffd6; - --base-light-color-light-aqua: #defff1; - --base-light-color-light-blue: #e1fbff; - --base-light-color-light-red: #ffdddd; - --base-black-neutral-100: #252F41; - --base-black-neutral-200: #313c51; - --base-black-neutral-300: #3c4557; - --base-black-neutral-400: #525A69; - --base-black-neutral-500: #59647a; - --base-black-neutral-600: #87A0BF; - --base-black-neutral-700: #99a6b8; - --base-black-neutral-800: #e2e9f2; - --base-black-neutral-900: #eff4fb; - --base-black-neutral-1000: #ffffff; - --base-black-neutral-n50: #232b38; - --base-black-neutral-n00: #1a202c; - --base-black-blue-50: #232b38; - --base-black-blue-100: #005174; - --base-black-blue-200: #a6ecff; - --base-black-blue-300: #52d1f4; - --base-black-blue-400: #00bcf0; - --base-black-blue-500: #05ade2; - --base-black-blue-600: #009fd1; - --base-black-color-deep-red: #d32772; - --base-black-color-deep-yellow: #e9b320; - --base-black-color-deep-green: #3ba856; - --base-black-color-deep-blue: #2e9dbb; - --base-black-color-light-purple: #4D4078; - --base-black-color-light-blue: #2C3B58; - --base-black-color-light-green: #3C5133; - --base-black-color-light-yellow: #695E3E; - --base-black-color-light-pink: #5E3C5E; - --base-black-color-light-red: #56363F; - --base-black-color-light-aqua: #1B3849; - --base-black-color-light-lime: #394027; - --base-black-color-light-orange: #5E3C3C; - --base-else-brand: #2c144b; - --text-title: #333333; - --text-caption: #828282; - --text-placeholder: #bdbdbd; - --text-disabled: #e0e0e0; - --text-link-default: #00bcf0; - --text-link-hover: #52d1f4; - --text-link-pressed: #009fd1; - --text-link-disabled: #e0f8ff; - --icon-primary: #333333; - --icon-secondary: #59647a; - --icon-disabled: #e0e0e0; - --icon-on-toolbar: #ffffff; - --line-border: #bdbdbd; - --line-divider: #edeef2; - --line-on-toolbar: #4f4f4f; - --fill-toolbar: #333333; - --fill-default: #00bcf0; - --fill-hover: #52d1f4; - --fill-pressed: #009fd1; - --fill-active: #e0f8ff; - --fill-list-hover: #e0f8ff; - --fill-list-active: #edeef2; - --content-blue-400: #00bcf0; - --content-blue-300: #52d1f4; - --content-blue-600: #009fd1; - --content-blue-100: #e0f8ff; - --content-blue-50: #f2fcff; - --content-on-fill-hover: #00bcf0; - --content-on-fill: #ffffff; - --content-on-tag: #4f4f4f; - --bg-body: #ffffff; - --bg-base: #f9fafd; - --bg-mask: rgba(0,0,0,0.55); - --bg-tips: #e0f8ff; - --bg-brand: #2c144b; - --function-error: #fb006d; - --function-waring: #ffd667; - --function-success: #66cf80; - --function-info: #00bcf0; - --tint-purple: #e8e0ff; - --tint-pink: #ffe7ee; - --tint-red: #ffdddd; - --tint-lime: #f5ffdc; - --tint-green: #ddffd6; - --tint-aqua: #defff1; - --tint-blue: #e1fbff; - --tint-orange: #ffefe3; - --tint-yellow: #fff2cd; - --shadow: 0px 0px 10px 0px rgba(0,0,0,0.1); - --scrollbar-thumb: #bdbdbd; - --scrollbar-track: #edeef2; -} \ No newline at end of file diff --git a/frontend/appflowy_tauri/src/tests/helpers/init.ts b/frontend/appflowy_tauri/src/tests/helpers/init.ts deleted file mode 100644 index cb0ff5c3b5..0000000000 --- a/frontend/appflowy_tauri/src/tests/helpers/init.ts +++ /dev/null @@ -1 +0,0 @@ -export {}; diff --git a/frontend/appflowy_tauri/style-dictionary/config.cjs b/frontend/appflowy_tauri/style-dictionary/config.cjs deleted file mode 100644 index 10d7084060..0000000000 --- a/frontend/appflowy_tauri/style-dictionary/config.cjs +++ /dev/null @@ -1,114 +0,0 @@ -const StyleDictionary = require('style-dictionary'); -const fs = require('fs'); -const path = require('path'); - -// Add comment header to generated files -StyleDictionary.registerFormat({ - name: 'css/variables', - formatter: function(dictionary, config) { - const header = `/**\n` + '* Do not edit directly\n' + `* Generated on ${new Date().toUTCString()}\n` + `* Generated from $pnpm css:variables \n` + `*/\n\n`; - const allProperties = dictionary.allProperties; - const properties = allProperties.map(prop => { - const { name, value } = prop; - return ` --${name}: ${value};` - }).join('\n'); - // generate tailwind config - generateTailwindConfig(allProperties); - return header + `:root${this.selector} {\n${properties}\n}` - } -}); - -// expand shadow tokens into a single string -StyleDictionary.registerTransform({ - name: 'shadow/spreadShadow', - type: 'value', - matcher: function (prop) { - return prop.type === 'boxShadow'; - }, - transformer: function (prop) { - // destructure shadow values from original token value - const { x, y, blur, spread, color } = prop.original.value; - - return `${x}px ${y}px ${blur}px ${spread}px ${color}`; - }, -}); - -const transforms = ['attribute/cti', 'name/cti/kebab', 'shadow/spreadShadow']; - -// Generate Light CSS variables -StyleDictionary.extend({ - source: ['./style-dictionary/tokens/base.json', './style-dictionary/tokens/light.json'], - platforms: { - css: { - transformGroup: 'css', - buildPath: './src/styles/variables/', - files: [ - { - format: 'css/variables', - destination: 'light.variables.css', - selector: '', - options: { - outputReferences: true - } - }, - ], - transforms, - }, - }, -}).buildAllPlatforms(); - -// Generate Dark CSS variables -StyleDictionary.extend({ - source: ['./style-dictionary/tokens/base.json', './style-dictionary/tokens/dark.json'], - platforms: { - css: { - transformGroup: 'css', - buildPath: './src/styles/variables/', - files: [ - { - format: 'css/variables', - destination: 'dark.variables.css', - selector: '[data-dark-mode=true]', - }, - ], - transforms, - }, - }, -}).buildAllPlatforms(); - - -function set(obj, path, value) { - const lastKey = path.pop(); - const lastObj = path.reduce((obj, key) => - obj[key] = obj[key] || {}, - obj); - lastObj[lastKey] = value; -} - -function writeFile (file, data) { - const header = `/**\n` + '* Do not edit directly\n' + `* Generated on ${new Date().toUTCString()}\n` + `* Generated from $pnpm css:variables \n` + `*/\n\n`; - const exportString = `module.exports = ${JSON.stringify(data, null, 2)}`; - fs.writeFileSync(path.join(__dirname, file), header + exportString); -} - -function generateTailwindConfig(allProperties) { - const tailwindColors = {}; - const tailwindBoxShadow = {}; - allProperties.forEach(prop => { - const { path, type, name, value } = prop; - if (path[0] === 'Base') { - return; - } - if (type === 'color') { - if (name.includes('fill')) { - console.log(prop); - } - set(tailwindColors, path, `var(--${name})`); - } - if (type === 'boxShadow') { - set(tailwindBoxShadow, ['md'], `var(--${name})`); - } - }); - writeFile('./tailwind/colors.cjs', tailwindColors); - writeFile('./tailwind/box-shadow.cjs', tailwindBoxShadow); -} \ No newline at end of file diff --git a/frontend/appflowy_tauri/style-dictionary/tailwind/box-shadow.cjs b/frontend/appflowy_tauri/style-dictionary/tailwind/box-shadow.cjs deleted file mode 100644 index e9d8024320..0000000000 --- a/frontend/appflowy_tauri/style-dictionary/tailwind/box-shadow.cjs +++ /dev/null @@ -1,9 +0,0 @@ -/** -* Do not edit directly -* Generated on Tue, 19 Mar 2024 03:48:58 GMT -* Generated from $pnpm css:variables -*/ - -module.exports = { - "md": "var(--shadow)" -} \ No newline at end of file diff --git a/frontend/appflowy_tauri/style-dictionary/tailwind/colors.cjs b/frontend/appflowy_tauri/style-dictionary/tailwind/colors.cjs deleted file mode 100644 index bfa25fa56f..0000000000 --- a/frontend/appflowy_tauri/style-dictionary/tailwind/colors.cjs +++ /dev/null @@ -1,75 +0,0 @@ -/** -* Do not edit directly -* Generated on Tue, 19 Mar 2024 03:48:58 GMT -* Generated from $pnpm css:variables -*/ - -module.exports = { - "text": { - "title": "var(--text-title)", - "caption": "var(--text-caption)", - "placeholder": "var(--text-placeholder)", - "link-default": "var(--text-link-default)", - "link-hover": "var(--text-link-hover)", - "link-pressed": "var(--text-link-pressed)", - "link-disabled": "var(--text-link-disabled)" - }, - "icon": { - "primary": "var(--icon-primary)", - "secondary": "var(--icon-secondary)", - "disabled": "var(--icon-disabled)", - "on-toolbar": "var(--icon-on-toolbar)" - }, - "line": { - "border": "var(--line-border)", - "divider": "var(--line-divider)", - "on-toolbar": "var(--line-on-toolbar)" - }, - "fill": { - "default": "var(--fill-default)", - "hover": "var(--fill-hover)", - "toolbar": "var(--fill-toolbar)", - "selector": "var(--fill-selector)", - "list": { - "active": "var(--fill-list-active)", - "hover": "var(--fill-list-hover)" - } - }, - "content": { - "blue-400": "var(--content-blue-400)", - "blue-300": "var(--content-blue-300)", - "blue-600": "var(--content-blue-600)", - "blue-100": "var(--content-blue-100)", - "on-fill": "var(--content-on-fill)", - "on-tag": "var(--content-on-tag)", - "blue-50": "var(--content-blue-50)" - }, - "bg": { - "body": "var(--bg-body)", - "base": "var(--bg-base)", - "mask": "var(--bg-mask)", - "tips": "var(--bg-tips)", - "brand": "var(--bg-brand)" - }, - "function": { - "error": "var(--function-error)", - "warning": "var(--function-warning)", - "success": "var(--function-success)", - "info": "var(--function-info)" - }, - "tint": { - "red": "var(--tint-red)", - "green": "var(--tint-green)", - "purple": "var(--tint-purple)", - "blue": "var(--tint-blue)", - "yellow": "var(--tint-yellow)", - "pink": "var(--tint-pink)", - "lime": "var(--tint-lime)", - "aqua": "var(--tint-aqua)", - "orange": "var(--tint-orange)" - }, - "scrollbar": { - "track": "var(--scrollbar-track)", - "thumb": "var(--scrollbar-thumb)" - } -} \ No newline at end of file diff --git a/frontend/appflowy_tauri/style-dictionary/tokens/base.json b/frontend/appflowy_tauri/style-dictionary/tokens/base.json deleted file mode 100644 index fb58a867b1..0000000000 --- a/frontend/appflowy_tauri/style-dictionary/tokens/base.json +++ /dev/null @@ -1,290 +0,0 @@ -{ - "Base": { - "Light": { - "neutral": { - "50": { - "value": "#f9fafd", - "type": "color" - }, - "100": { - "value": "#dadbdd", - "type": "color" - }, - "200": { - "value": "#e2e4eb", - "type": "color" - }, - "300": { - "value": "#f2f2f2", - "type": "color" - }, - "400": { - "value": "#e0e0e0", - "type": "color" - }, - "500": { - "value": "#bdbdbd", - "type": "color" - }, - "600": { - "value": "#828282", - "type": "color" - }, - "700": { - "value": "#4f4f4f", - "type": "color" - }, - "800": { - "value": "#333333", - "type": "color" - }, - "900": { - "value": "#1f2329", - "type": "color" - }, - "1000": { - "value": "#000000", - "type": "color" - }, - "00": { - "value": "#ffffff", - "type": "color" - } - }, - "blue": { - "50": { - "value": "#f2fcff", - "type": "color" - }, - "100": { - "value": "#e0f8ff", - "type": "color" - }, - "200": { - "value": "#a6ecff", - "type": "color" - }, - "300": { - "value": "#52d1f4", - "type": "color" - }, - "400": { - "value": "#00bcf0", - "type": "color" - }, - "500": { - "value": "#05ade2", - "type": "color" - }, - "600": { - "value": "#009fd1", - "type": "color" - } - }, - "color": { - "deep": { - "red": { - "value": "#fb006d", - "type": "color" - }, - "yellow": { - "value": "#ffd667", - "type": "color" - }, - "green": { - "value": "#66cf80", - "type": "color" - }, - "blue": { - "value": "#00bcf0", - "type": "color" - } - }, - "light": { - "purple": { - "value": "#e8e0ff", - "type": "color" - }, - "pink": { - "value": "#ffe7ee", - "type": "color" - }, - "orange": { - "value": "#ffefe3", - "type": "color" - }, - "yellow": { - "value": "#fff2cd", - "type": "color" - }, - "lime": { - "value": "#f5ffdc", - "type": "color" - }, - "green": { - "value": "#ddffd6", - "type": "color" - }, - "aqua": { - "value": "#defff1", - "type": "color" - }, - "blue": { - "value": "#e1fbff", - "type": "color" - }, - "red": { - "value": "#ffdddd", - "type": "color" - } - } - } - }, - "black": { - "neutral": { - "100": { - "value": "#252F41", - "type": "color" - }, - "200": { - "value": "#313c51", - "type": "color" - }, - "300": { - "value": "#3c4557", - "type": "color" - }, - "400": { - "value": "#525A69", - "type": "color" - }, - "500": { - "value": "#59647a", - "type": "color" - }, - "600": { - "value": "#87A0BF", - "type": "color" - }, - "700": { - "value": "#99a6b8", - "type": "color" - }, - "800": { - "value": "#e2e9f2", - "type": "color" - }, - "900": { - "value": "#eff4fb", - "type": "color" - }, - "1000": { - "value": "#ffffff", - "type": "color" - }, - "N50": { - "value": "#232b38", - "type": "color" - }, - "N00": { - "value": "#1a202c", - "type": "color" - } - }, - "blue": { - "50": { - "value": "#232b38", - "type": "color" - }, - "100": { - "value": "#005174", - "type": "color" - }, - "200": { - "value": "#a6ecff", - "type": "color" - }, - "300": { - "value": "#52d1f4", - "type": "color" - }, - "400": { - "value": "#00bcf0", - "type": "color" - }, - "500": { - "value": "#05ade2", - "type": "color" - }, - "600": { - "value": "#009fd1", - "type": "color" - } - }, - "color": { - "deep": { - "red": { - "value": "#d32772", - "type": "color" - }, - "yellow": { - "value": "#e9b320", - "type": "color" - }, - "green": { - "value": "#3ba856", - "type": "color" - }, - "blue": { - "value": "#2e9dbb", - "type": "color" - } - }, - "light": { - "purple": { - "value": "#4D4078", - "type": "color" - }, - "blue": { - "value": "#2C3B58", - "type": "color" - }, - "green": { - "value": "#3C5133", - "type": "color" - }, - "yellow": { - "value": "#695E3E", - "type": "color" - }, - "pink": { - "value": "#5E3C5E", - "type": "color" - }, - "red": { - "value": "#56363F", - "type": "color" - }, - "aqua": { - "value": "#1B3849", - "type": "color" - }, - "lime": { - "value": "#394027", - "type": "color" - }, - "orange": { - "value": "#5E3C3C", - "type": "color" - } - } - } - }, - "else": { - "brand": { - "value": "#2c144b", - "type": "color" - } - } - } -} \ No newline at end of file diff --git a/frontend/appflowy_tauri/style-dictionary/tokens/dark.json b/frontend/appflowy_tauri/style-dictionary/tokens/dark.json deleted file mode 100644 index c67af7c9ec..0000000000 --- a/frontend/appflowy_tauri/style-dictionary/tokens/dark.json +++ /dev/null @@ -1,221 +0,0 @@ -{ - "text": { - "title": { - "value": "{Base.black.neutral.800}", - "type": "color" - }, - "caption": { - "value": "{Base.black.neutral.600}", - "type": "color" - }, - "placeholder": { - "value": "{Base.black.neutral.300}", - "type": "color" - }, - "link-default": { - "value": "{Base.black.blue.400}", - "type": "color" - }, - "link-hover": { - "value": "{Base.black.blue.300}", - "type": "color" - }, - "link-pressed": { - "value": "{Base.black.blue.600}", - "type": "color" - }, - "link-disabled": { - "value": "{Base.black.blue.100}", - "type": "color" - } - }, - "icon": { - "primary": { - "value": "{Base.black.neutral.800}", - "type": "color" - }, - "secondary": { - "value": "{Base.black.neutral.500}", - "type": "color" - }, - "disabled": { - "value": "{Base.black.neutral.400}", - "type": "color" - }, - "on-toolbar": { - "value": "white", - "type": "color" - } - }, - "line": { - "border": { - "value": "{Base.black.neutral.500}", - "type": "color" - }, - "divider": { - "value": "{Base.black.neutral.100}", - "type": "color" - }, - "on-toolbar": { - "value": "{Base.black.neutral.700}", - "type": "color" - } - }, - "fill": { - "default": { - "value": "{Base.black.blue.400}", - "type": "color" - }, - "hover": { - "value": "{Base.black.blue.100}", - "type": "color" - }, - "toolbar": { - "value": "#0F111C", - "type": "color" - }, - "selector": { - "value": "{Base.black.blue.50}", - "type": "color" - }, - "list": { - "active": { - "value": "{Base.black.neutral.300}", - "type": "color" - }, - "hover": { - "value": "{Base.black.blue.100}", - "type": "color" - } - } - }, - "content": { - "blue-400": { - "value": "{Base.black.blue.400}", - "type": "color" - }, - "blue-300": { - "value": "{Base.black.blue.300}", - "type": "color" - }, - "blue-600": { - "value": "{Base.black.blue.600}", - "type": "color" - }, - "blue-100": { - "value": "{Base.black.blue.100}", - "type": "color" - }, - "on-fill": { - "value": "{Base.black.neutral.N00}", - "type": "color" - }, - "on-tag": { - "value": "{Base.black.neutral.700}", - "type": "color" - }, - "blue-50": { - "value": "{Base.black.blue.50}", - "type": "color" - } - }, - "bg": { - "body": { - "value": "{Base.black.neutral.N00}", - "type": "color" - }, - "base": { - "value": "{Base.black.blue.50}", - "type": "color" - }, - "mask": { - "value": "rgba(0,0,0,0.7)", - "type": "color" - }, - "tips": { - "value": "{Base.black.blue.100}", - "type": "color" - }, - "brand": { - "value": "{Base.else.brand}", - "type": "color" - } - }, - "function": { - "error": { - "value": "{Base.black.color.deep.red}", - "type": "color" - }, - "warning": { - "value": "{Base.black.color.deep.yellow}", - "type": "color" - }, - "success": { - "value": "#3ba856", - "type": "color" - }, - "info": { - "value": "#2e9dbb", - "type": "color" - } - }, - "tint": { - "red": { - "value": "{Base.black.color.light.red}", - "type": "color" - }, - "green": { - "value": "{Base.black.color.light.green}", - "type": "color" - }, - "purple": { - "value": "{Base.black.color.light.purple}", - "type": "color" - }, - "blue": { - "value": "{Base.black.color.light.blue}", - "type": "color" - }, - "yellow": { - "value": "{Base.black.color.light.yellow}", - "type": "color" - }, - "pink": { - "value": "{Base.black.color.light.pink}", - "type": "color" - }, - "lime": { - "value": "{Base.black.color.light.lime}", - "type": "color" - }, - "aqua": { - "value": "{Base.black.color.light.aqua}", - "type": "color" - }, - "orange": { - "value": "{Base.black.color.light.orange}", - "type": "color" - } - }, - "shadow": { - "value": { - "x": "0", - "y": "0", - "blur": "25", - "spread": "0", - "color": "rgba(0,0,0,0.3)", - "type": "innerShadow" - }, - "type": "boxShadow" - }, - "scrollbar": { - "track": { - "value": "{Base.black.neutral.100}", - "type": "color" - }, - "thumb": { - "value": "{Base.black.neutral.300}", - "type": "color" - } - } -} \ No newline at end of file diff --git a/frontend/appflowy_tauri/style-dictionary/tokens/light.json b/frontend/appflowy_tauri/style-dictionary/tokens/light.json deleted file mode 100644 index 173f3d35aa..0000000000 --- a/frontend/appflowy_tauri/style-dictionary/tokens/light.json +++ /dev/null @@ -1,233 +0,0 @@ -{ - "text": { - "title": { - "value": "{Base.Light.neutral.800}", - "type": "color" - }, - "caption": { - "value": "{Base.Light.neutral.600}", - "type": "color" - }, - "placeholder": { - "value": "{Base.Light.neutral.500}", - "type": "color" - }, - "disabled": { - "value": "{Base.Light.neutral.400}", - "type": "color" - }, - "link-default": { - "value": "{Base.Light.blue.400}", - "type": "color" - }, - "link-hover": { - "value": "{Base.Light.blue.300}", - "type": "color" - }, - "link-pressed": { - "value": "{Base.Light.blue.600}", - "type": "color" - }, - "link-disabled": { - "value": "{Base.Light.blue.100}", - "type": "color" - } - }, - "icon": { - "primary": { - "value": "{Base.Light.neutral.800}", - "type": "color" - }, - "secondary": { - "value": "{Base.black.neutral.500}", - "type": "color" - }, - "disabled": { - "value": "{Base.Light.neutral.400}", - "type": "color" - }, - "on-toolbar": { - "value": "{Base.Light.neutral.00}", - "type": "color" - } - }, - "line": { - "border": { - "value": "{Base.Light.neutral.500}", - "type": "color" - }, - "divider": { - "value": "{Base.Light.neutral.100}", - "type": "color" - }, - "on-toolbar": { - "value": "{Base.Light.neutral.700}", - "type": "color" - } - }, - "fill": { - "toolbar": { - "value": "{Base.Light.neutral.800}", - "type": "color" - }, - "default": { - "value": "{Base.Light.blue.400}", - "type": "color" - }, - "hover": { - "value": "{Base.Light.blue.300}", - "type": "color" - }, - "pressed": { - "value": "{Base.Light.blue.600}", - "type": "color" - }, - "active": { - "value": "{Base.Light.blue.100}", - "type": "color" - }, - "list": { - "hover": { - "value": "{Base.Light.blue.100}", - "type": "color" - }, - "active": { - "value": "{Base.Light.neutral.100}", - "type": "color" - } - } - }, - "content": { - "blue-400": { - "value": "{Base.Light.blue.400}", - "type": "color" - }, - "blue-300": { - "value": "{Base.Light.blue.300}", - "type": "color" - }, - "blue-600": { - "value": "{Base.Light.blue.600}", - "type": "color" - }, - "blue-100": { - "value": "{Base.Light.blue.100}", - "type": "color" - }, - "blue-50": { - "value": "{Base.Light.blue.50}", - "type": "color" - }, - "on-fill-hover": { - "value": "{Base.Light.blue.400}", - "type": "color" - }, - "on-fill": { - "value": "{Base.Light.neutral.00}", - "type": "color" - }, - "on-tag": { - "value": "{Base.Light.neutral.700}", - "type": "color" - } - }, - "bg": { - "body": { - "value": "{Base.Light.neutral.00}", - "type": "color" - }, - "base": { - "value": "{Base.Light.neutral.50}", - "type": "color" - }, - "mask": { - "value": "rgba(0,0,0,0.55)", - "type": "color" - }, - "tips": { - "value": "{Base.Light.blue.100}", - "type": "color" - }, - "brand": { - "value": "{Base.else.brand}", - "type": "color" - } - }, - "function": { - "error": { - "value": "{Base.Light.color.deep.red}", - "type": "color" - }, - "waring": { - "value": "{Base.Light.color.deep.yellow}", - "type": "color" - }, - "success": { - "value": "{Base.Light.color.deep.green}", - "type": "color" - }, - "info": { - "value": "{Base.Light.color.deep.blue}", - "type": "color" - } - }, - "tint": { - "purple": { - "value": "{Base.Light.color.light.purple}", - "type": "color" - }, - "pink": { - "value": "{Base.Light.color.light.pink}", - "type": "color" - }, - "red": { - "value": "{Base.Light.color.light.red}", - "type": "color" - }, - "lime": { - "value": "{Base.Light.color.light.lime}", - "type": "color" - }, - "green": { - "value": "{Base.Light.color.light.green}", - "type": "color" - }, - "aqua": { - "value": "{Base.Light.color.light.aqua}", - "type": "color" - }, - "blue": { - "value": "{Base.Light.color.light.blue}", - "type": "color" - }, - "orange": { - "value": "{Base.Light.color.light.orange}", - "type": "color" - }, - "yellow": { - "value": "{Base.Light.color.light.yellow}", - "type": "color" - } - }, - "shadow": { - "value": { - "x": "0", - "y": "0", - "blur": "10", - "spread": "0", - "color": "rgba(0,0,0,0.1)", - "type": "dropShadow" - }, - "type": "boxShadow" - }, - "scrollbar": { - "thumb": { - "value": "{Base.Light.neutral.500}", - "type": "color" - }, - "track": { - "value": "{Base.Light.neutral.100}", - "type": "color" - } - } -} \ No newline at end of file diff --git a/frontend/appflowy_tauri/tailwind.config.cjs b/frontend/appflowy_tauri/tailwind.config.cjs deleted file mode 100644 index 06390d938f..0000000000 --- a/frontend/appflowy_tauri/tailwind.config.cjs +++ /dev/null @@ -1,20 +0,0 @@ -const colors = require('./style-dictionary/tailwind/colors.cjs'); -const boxShadow = require('./style-dictionary/tailwind/box-shadow.cjs'); - -/** @type {import('tailwindcss').Config} */ -module.exports = { - content: [ - './index.html', - './src/**/*.{js,ts,jsx,tsx}', - './node_modules/react-tailwindcss-datepicker/dist/index.esm.js', - ], - important: '#body', - darkMode: 'class', - theme: { - extend: { - colors, - boxShadow, - }, - }, - plugins: [], -}; diff --git a/frontend/appflowy_tauri/tsconfig.json b/frontend/appflowy_tauri/tsconfig.json deleted file mode 100644 index 63b15b6039..0000000000 --- a/frontend/appflowy_tauri/tsconfig.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "compilerOptions": { - "target": "ESNext", - "useDefineForClassFields": true, - "lib": ["DOM", "DOM.Iterable", "ESNext"], - "allowJs": false, - "skipLibCheck": true, - "esModuleInterop": false, - "allowSyntheticDefaultImports": true, - "strict": true, - "forceConsistentCasingInFileNames": true, - "module": "ESNext", - "moduleResolution": "Node", - "resolveJsonModule": true, - "isolatedModules": true, - "noEmit": true, - "jsx": "react-jsx", - "types": ["node", "jest"], - "baseUrl": "./", - "paths": { - "@/*": ["src/*"], - "$app/*": ["src/appflowy_app/*"], - "$app_reducers/*": ["src/appflowy_app/stores/reducers/*"], - "src/*": ["src/*"] - } - }, - "include": ["src", "vite.config.ts"], - "exclude": ["node_modules"], - "references": [{ "path": "./tsconfig.node.json" }] -} diff --git a/frontend/appflowy_tauri/tsconfig.node.json b/frontend/appflowy_tauri/tsconfig.node.json deleted file mode 100644 index 9d31e2aed9..0000000000 --- a/frontend/appflowy_tauri/tsconfig.node.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "compilerOptions": { - "composite": true, - "module": "ESNext", - "moduleResolution": "Node", - "allowSyntheticDefaultImports": true - }, - "include": ["vite.config.ts"] -} diff --git a/frontend/appflowy_tauri/vite.config.ts b/frontend/appflowy_tauri/vite.config.ts deleted file mode 100644 index b571cc40de..0000000000 --- a/frontend/appflowy_tauri/vite.config.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { defineConfig } from 'vite'; -import react from '@vitejs/plugin-react'; -import svgr from 'vite-plugin-svgr'; - -// https://vitejs.dev/config/ -export default defineConfig({ - plugins: [ - react(), - svgr({ - svgrOptions: { - prettier: false, - plugins: ['@svgr/plugin-svgo', '@svgr/plugin-jsx'], - icon: true, - svgoConfig: { - multipass: true, - plugins: [ - { - name: 'preset-default', - params: { - overrides: { - removeViewBox: false, - }, - }, - }, - ], - }, - svgProps: { - role: 'img', - }, - replaceAttrValues: { - '#333': 'currentColor', - }, - }, - }), - ], - - // Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build` - // prevent vite from obscuring rust errors - clearScreen: false, - // tauri expects a fixed port, fail if that port is not available - server: { - port: 1420, - strictPort: true, - watch: { - ignored: ['**/__tests__/**'], - }, - }, - // to make use of `TAURI_DEBUG` and other env variables - // https://tauri.studio/v1/api/config#buildconfig.beforedevcommand - envPrefix: ['VITE_', 'TAURI_'], - build: { - // Tauri supports es2021 - target: process.env.TAURI_PLATFORM === 'windows' ? 'chrome105' : 'safari13', - // don't minify for debug builds - minify: !process.env.TAURI_DEBUG ? 'esbuild' : false, - // produce sourcemaps for debug builds - sourcemap: !!process.env.TAURI_DEBUG, - }, - resolve: { - alias: [ - { find: 'src/', replacement: `${__dirname}/src/` }, - { find: '@/', replacement: `${__dirname}/src/` }, - { find: '$app/', replacement: `${__dirname}/src/appflowy_app/` }, - { find: '$app_reducers/', replacement: `${__dirname}/src/appflowy_app/stores/reducers/` }, - ], - }, - optimizeDeps: { - include: ['@mui/material/Tooltip'], - }, -}); diff --git a/frontend/appflowy_tauri/webdriver/selenium/package.json b/frontend/appflowy_tauri/webdriver/selenium/package.json deleted file mode 100644 index 78bbd20aad..0000000000 --- a/frontend/appflowy_tauri/webdriver/selenium/package.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "name": "selenium", - "version": "1.0.0", - "private": true, - "scripts": { - "test": "mocha" - }, - "dependencies": { - "chai": "^4.3.4", - "mocha": "^9.0.3", - "selenium-webdriver": "^4.0.0-beta.4" - } -} diff --git a/frontend/appflowy_tauri/webdriver/selenium/test/test.cjs b/frontend/appflowy_tauri/webdriver/selenium/test/test.cjs deleted file mode 100644 index 7a57bdbbaf..0000000000 --- a/frontend/appflowy_tauri/webdriver/selenium/test/test.cjs +++ /dev/null @@ -1,76 +0,0 @@ -const os = require("os"); -const path = require("path"); -const { expect } = require("chai"); -const { spawn, spawnSync } = require("child_process"); -const { Builder, By, Capabilities, until } = require("selenium-webdriver"); -const { elementIsVisible, elementLocated } = require("selenium-webdriver/lib/until.js"); - -// create the path to the expected application binary -const application = path.resolve( - __dirname, - "..", - "..", - "..", - "src-tauri", - "target", - "release", - "appflowy_tauri" -); - -// keep track of the webdriver instance we create -let driver; - -// keep track of the tauri-driver process we start -let tauriDriver; - -before(async function() { - // set timeout to 2 minutes to allow the program to build if it needs to - this.timeout(120000); - - // ensure the program has been built - spawnSync("cargo", ["build", "--release"]); - - // start tauri-driver - tauriDriver = spawn( - path.resolve(os.homedir(), ".cargo", "bin", "tauri-driver"), - [], - { stdio: [null, process.stdout, process.stderr] } - ); - - const capabilities = new Capabilities(); - capabilities.set("tauri:options", { application }); - capabilities.setBrowserName("wry"); - - // start the webdriver client - driver = await new Builder() - .withCapabilities(capabilities) - .usingServer("http://localhost:4444/") - .build(); -}); - -after(async function() { - // stop the webdriver session - await driver.quit(); - - // kill the tauri-driver process - tauriDriver.kill(); -}); - -describe("AppFlowy Unit Test", () => { - it("should find get started button", async () => { - // should sign out if already sign in - const getStartedButton = await driver.wait(until.elementLocated(By.xpath("//*[@id=\"root\"]/form/div/div[3]"))); - getStartedButton.click(); - }); - - it("should get sign out button", async (done) => { - // const optionButton = await driver.wait(until.elementLocated(By.css('*[test-id=option-button]'))); - // const optionButton = await driver.wait(until.elementLocated(By.id('option-button'))); - // const optionButton = await driver.wait(until.elementLocated(By.css('[aria-label=option]'))); - - // Currently, only the find className is work - const optionButton = await driver.wait(until.elementLocated(By.className("relative h-8 w-8"))); - optionButton.click(); - await new Promise((resolve) => setTimeout(resolve, 4000)); - }); -}); diff --git a/frontend/scripts/makefile/env.toml b/frontend/scripts/makefile/env.toml index 1bb61f94f7..96a632f8ab 100644 --- a/frontend/scripts/makefile/env.toml +++ b/frontend/scripts/makefile/env.toml @@ -1,15 +1,9 @@ [tasks.appflowy-flutter-deps-tools] run_task = { name = ["install_flutter_prerequests"] } -[tasks.appflowy-tauri-deps-tools] -run_task = { name = ["install_tauri_prerequests"] } - [tasks.appflowy-flutter-dev-tools] run_task = { name = ["appflowy-flutter-deps-tools", "install_diesel"] } -[tasks.appflowy-tauri-dev-tools] -run_task = { name = ["appflowy-tauri-deps-tools", "install_diesel"] } - [tasks.install_windows_deps.windows] dependencies = ["check_duckscript_installation", "check_vcpkg", "install_vcpkg_sqlite", "install_rust_vcpkg_cli"] @@ -100,9 +94,6 @@ script = """ rustup target add x86_64-unknown-linux-gnu """ -[tasks.install_tauri_prerequests] -dependencies = ["install_targets", "install_web_protobuf"] - [tasks.install_flutter_prerequests] dependencies = ["install_targets", "install_flutter_protobuf"] diff --git a/frontend/scripts/makefile/tauri.toml b/frontend/scripts/makefile/tauri.toml index 4fe6b19fd7..549c7e250e 100644 --- a/frontend/scripts/makefile/tauri.toml +++ b/frontend/scripts/makefile/tauri.toml @@ -1,10 +1,3 @@ -[tasks.tauri_build] -description = "Build the Tauri backend" -script = [""" - cd appflowy_tauri/src-tauri - cargo build - """] -script_runner = "@shell" [tasks.tauri_dev] env = { RUST_LOG = "debug" } diff --git a/frontend/scripts/tool/update_client_api_rev.sh b/frontend/scripts/tool/update_client_api_rev.sh index ea16622f6f..9146e6e2d8 100755 --- a/frontend/scripts/tool/update_client_api_rev.sh +++ b/frontend/scripts/tool/update_client_api_rev.sh @@ -8,7 +8,7 @@ fi NEW_REV="$1" echo "New revision: $NEW_REV" -directories=("rust-lib" "appflowy_tauri/src-tauri" "appflowy_web_app/src-tauri") +directories=("rust-lib") for dir in "${directories[@]}"; do echo "Updating $dir" diff --git a/frontend/scripts/tool/update_collab_rev.sh b/frontend/scripts/tool/update_collab_rev.sh index edff885cc0..aa228a0eef 100755 --- a/frontend/scripts/tool/update_collab_rev.sh +++ b/frontend/scripts/tool/update_collab_rev.sh @@ -8,7 +8,7 @@ fi NEW_REV="$1" echo "New revision: $NEW_REV" -directories=("rust-lib" "appflowy_tauri/src-tauri" "appflowy_web_app/src-tauri") +directories=("rust-lib") for dir in "${directories[@]}"; do echo "Updating $dir" diff --git a/frontend/scripts/tool/update_collab_source.sh b/frontend/scripts/tool/update_collab_source.sh index d73c62c033..697d293e31 100755 --- a/frontend/scripts/tool/update_collab_source.sh +++ b/frontend/scripts/tool/update_collab_source.sh @@ -5,9 +5,6 @@ REPO_PATH="./AppFlowy-Collab" CARGO_TOML_1="./rust-lib/Cargo.toml" REPO_RELATIVE_PATH_1="../AppFlowy-Collab" -CARGO_TOML_2="./appflowy_tauri/src-tauri/Cargo.toml" -REPO_RELATIVE_PATH_2="../../AppFlowy-Collab" - # Function to switch dependencies in a given Cargo.toml switch_deps() { local cargo_toml="$1" @@ -38,4 +35,3 @@ fi # Switch dependencies in both Cargo.toml files switch_deps "$CARGO_TOML_1" "$REPO_RELATIVE_PATH_1" -switch_deps "$CARGO_TOML_2" "$REPO_RELATIVE_PATH_2" diff --git a/frontend/scripts/tool/update_local_ai_rev.sh b/frontend/scripts/tool/update_local_ai_rev.sh index f234ae2920..83c5e67d80 100755 --- a/frontend/scripts/tool/update_local_ai_rev.sh +++ b/frontend/scripts/tool/update_local_ai_rev.sh @@ -8,7 +8,7 @@ fi NEW_REV="$1" echo "New revision: $NEW_REV" -directories=("rust-lib" "appflowy_tauri/src-tauri" "appflowy_web_app/src-tauri") +directories=("rust-lib") for dir in "${directories[@]}"; do echo "Updating $dir" From 256d0159672b53d12e5af08ef0fc98b5fc9f127b Mon Sep 17 00:00:00 2001 From: "Kilu.He" <108015703+qinluhe@users.noreply.github.com> Date: Mon, 30 Dec 2024 15:46:39 +0800 Subject: [PATCH 092/576] fix: add tip for database page (#7090) --- .../_shared/notify/InfoSnackbar.tsx | 24 ++++--- .../blocks/database/DatabaseBlock.tsx | 10 +-- .../appflowy_web_app/src/pages/AppPage.tsx | 72 +++++++++++++++---- 3 files changed, 77 insertions(+), 29 deletions(-) diff --git a/frontend/appflowy_web_app/src/components/_shared/notify/InfoSnackbar.tsx b/frontend/appflowy_web_app/src/components/_shared/notify/InfoSnackbar.tsx index bc43e60c45..ee9dbe45f2 100644 --- a/frontend/appflowy_web_app/src/components/_shared/notify/InfoSnackbar.tsx +++ b/frontend/appflowy_web_app/src/components/_shared/notify/InfoSnackbar.tsx @@ -31,7 +31,10 @@ const InfoSnackbar = forwardRef( }; return ( - +
@@ -39,13 +42,18 @@ const InfoSnackbar = forwardRef(
{title}
- +
-
{message}
+
{message}
{showActions && (
-
- - Read-only: Use the AppFlowy app to create or edit database pages. This feature will be available soon. -
{t('publish.hasNotBeenPublished')}
) : ( - + )}
)} diff --git a/frontend/appflowy_web_app/src/pages/AppPage.tsx b/frontend/appflowy_web_app/src/pages/AppPage.tsx index b3916c3205..d715c67a52 100644 --- a/frontend/appflowy_web_app/src/pages/AppPage.tsx +++ b/frontend/appflowy_web_app/src/pages/AppPage.tsx @@ -1,25 +1,24 @@ import { UIVariant, ViewComponentProps, ViewLayout, ViewMetaProps, YDoc } from '@/application/types'; import Help from '@/components/_shared/help/Help'; +import { notify } from '@/components/_shared/notify'; import { findView } from '@/components/_shared/outline/utils'; - -import { - AppContext, - useAppHandlers, - useAppOutline, - useAppViewId, -} from '@/components/app/app.hooks'; +import { ReactComponent as TipIcon } from '@/assets/warning.svg'; +import { AppContext, useAppHandlers, useAppOutline, useAppViewId } from '@/components/app/app.hooks'; import DatabaseView from '@/components/app/DatabaseView'; import { Document } from '@/components/document'; import RecordNotFound from '@/components/error/RecordNotFound'; import { getPlatform } from '@/utils/platform'; +import { desktopDownloadLink, openAppFlowySchema } from '@/utils/url'; +import { Button, Checkbox, FormControlLabel } from '@mui/material'; import React, { lazy, memo, Suspense, useCallback, useContext, useEffect, useMemo } from 'react'; const ViewHelmet = lazy(() => import('@/components/_shared/helmet/ViewHelmet')); -function AppPage() { +function AppPage () { const viewId = useAppViewId(); const outline = useAppOutline(); const ref = React.useRef(null); + const { toView, loadViewMeta, @@ -135,20 +134,69 @@ function AppPage() { localStorage.setItem('last_view_id', viewId); }, [View, viewId, doc]); + const layout = view?.layout; + + useEffect(() => { + if (layout !== undefined && layout !== ViewLayout.Document && !localStorage.getItem('open_edit_tip')) { + notify.clear(); + notify.info({ + autoHideDuration: null, + type: 'info', + title: 'Edit in app', + message:
+
{`Editing databases is supported in AppFlowy's desktop and mobile apps`} +
+
+
+ { + if (value) { + localStorage.setItem('open_edit_tip', 'true'); + } else { + localStorage.removeItem('open_edit_tip'); + } + }} + control={} + label="Don't remind me again" + /> + +
+
, + showActions: false, + }); + } + }, [layout]); + if (!viewId) return null; return ( -
+
{helmet} {notFound ? ( - + ) : (
{viewDom}
)} - {view && doc && } - + {view && doc && }
); } From 5e581e912f026829054b6933cb2588a3af31e072 Mon Sep 17 00:00:00 2001 From: "Kilu.He" <108015703+qinluhe@users.noreply.github.com> Date: Mon, 30 Dec 2024 15:51:58 +0800 Subject: [PATCH 093/576] fix: hover controls position (#7093) --- .../editor/components/table-container/TableContainer.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/appflowy_web_app/src/components/editor/components/table-container/TableContainer.tsx b/frontend/appflowy_web_app/src/components/editor/components/table-container/TableContainer.tsx index 79f3e18627..2779e676db 100644 --- a/frontend/appflowy_web_app/src/components/editor/components/table-container/TableContainer.tsx +++ b/frontend/appflowy_web_app/src/components/editor/components/table-container/TableContainer.tsx @@ -3,7 +3,7 @@ import { getScrollParent } from '@/components/global-comment/utils'; import React, { useCallback, useEffect, useRef } from 'react'; import { ReactEditor, useReadOnly, useSlateStatic } from 'slate-react'; -function TableContainer({ blockId, readSummary, children, paddingLeft = 0 }: { +function TableContainer ({ blockId, readSummary, children, paddingLeft = 0 }: { blockId: string; readSummary?: boolean; children?: React.ReactNode; @@ -99,12 +99,12 @@ function TableContainer({ blockId, readSummary, children, paddingLeft = 0 }: {
From dffb865a6625d44e98575557f7108f96accfe3f6 Mon Sep 17 00:00:00 2001 From: Richard Shiue <71320345+richardshiue@users.noreply.github.com> Date: Mon, 30 Dec 2024 16:35:41 +0800 Subject: [PATCH 094/576] chore(flutter_desktop): adjust toast style (#7083) --- .../message/error_text_message.dart | 2 +- .../presentation/widgets/dialogs.dart | 159 +++++++++++++----- .../flowy_icons/16x/toast_checked_filled.svg | 3 + .../16x/{ai_close.svg => toast_close.svg} | 0 .../flowy_icons/16x/toast_error_filled.svg | 3 + .../flowy_icons/16x/toast_warning_filled.svg | 3 + .../flowy_icons/16x/warning_filled.svg | 5 - 7 files changed, 126 insertions(+), 49 deletions(-) create mode 100644 frontend/resources/flowy_icons/16x/toast_checked_filled.svg rename frontend/resources/flowy_icons/16x/{ai_close.svg => toast_close.svg} (100%) create mode 100644 frontend/resources/flowy_icons/16x/toast_error_filled.svg create mode 100644 frontend/resources/flowy_icons/16x/toast_warning_filled.svg delete mode 100644 frontend/resources/flowy_icons/16x/warning_filled.svg diff --git a/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/message/error_text_message.dart b/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/message/error_text_message.dart index 66b39fe308..6056ffffa6 100644 --- a/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/message/error_text_message.dart +++ b/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/message/error_text_message.dart @@ -58,7 +58,7 @@ class _ChatErrorMessageWidgetState extends State { mainAxisSize: MainAxisSize.min, children: [ const FlowySvg( - FlowySvgs.warning_filled_s, + FlowySvgs.toast_error_filled_s, blendMode: null, ), const HSpace(8.0), diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/widgets/dialogs.dart b/frontend/appflowy_flutter/lib/workspace/presentation/widgets/dialogs.dart index 7686b1d891..42b687937e 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/widgets/dialogs.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/widgets/dialogs.dart @@ -1,6 +1,7 @@ import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/startup/tasks/app_widget.dart'; +import 'package:appflowy/util/theme_extension.dart'; import 'package:appflowy/workspace/presentation/home/menu/sidebar/space/shared_widget.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flowy_infra/size.dart'; @@ -364,71 +365,59 @@ class OkCancelButton extends StatelessWidget { void showToastNotification( BuildContext context, { - required String message, + String? message, + TextSpan? richMessage, String? description, ToastificationType type = ToastificationType.success, ToastificationCallbacks? callbacks, double bottomPadding = 100, }) { - if (UniversalPlatform.isMobile) { - toastification.showCustom( - alignment: Alignment.bottomCenter, - autoCloseDuration: const Duration(milliseconds: 3000), - callbacks: callbacks ?? const ToastificationCallbacks(), - builder: (_, __) => _MToast( - message: message, - type: type, - bottomPadding: bottomPadding, - description: description, - ), - ); - return; - } - - toastification.show( - context: context, - type: type, - style: ToastificationStyle.flat, - closeButtonShowType: CloseButtonShowType.onHover, + assert( + (message == null) != (richMessage == null), + "Exactly one of message or richMessage must be non-null.", + ); + toastification.showCustom( alignment: Alignment.bottomCenter, autoCloseDuration: const Duration(milliseconds: 3000), - showProgressBar: false, - backgroundColor: Theme.of(context).colorScheme.surface, - borderSide: BorderSide( - color: Colors.grey.withOpacity(0.4), - ), - title: FlowyText( - message, - maxLines: 3, - ), - description: description != null - ? FlowyText.regular( - description, - fontSize: 12, - lineHeight: 1.2, - maxLines: 3, - ) - : null, + callbacks: callbacks ?? const ToastificationCallbacks(), + builder: (_, item) { + return UniversalPlatform.isMobile + ? _MobileToast( + message: message, + type: type, + bottomPadding: bottomPadding, + description: description, + ) + : _DesktopToast( + message: message, + richMessage: richMessage, + type: type, + onDismiss: () => toastification.dismiss(item), + ); + }, ); } -class _MToast extends StatelessWidget { - const _MToast({ - required this.message, +class _MobileToast extends StatelessWidget { + const _MobileToast({ + this.message, this.type = ToastificationType.success, this.bottomPadding = 100, this.description, }); - final String message; + final String? message; final ToastificationType type; final double bottomPadding; final String? description; @override Widget build(BuildContext context) { + if (message == null) { + return const SizedBox.shrink(); + } final hintText = FlowyText.regular( - message, + message!, fontSize: 16.0, figmaLineHeight: 18.0, color: Colors.white, @@ -498,6 +487,90 @@ class _MToast extends StatelessWidget { } } +class _DesktopToast extends StatelessWidget { + const _DesktopToast({ + this.message, + this.richMessage, + required this.type, + this.onDismiss, + }); + + final String? message; + final TextSpan? richMessage; + final ToastificationType type; + final void Function()? onDismiss; + + @override + Widget build(BuildContext context) { + return Center( + child: Container( + constraints: const BoxConstraints(maxWidth: 360.0), + padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 8.0), + margin: const EdgeInsets.only(bottom: 32.0), + decoration: BoxDecoration( + color: Theme.of(context).isLightMode + ? const Color(0xFF333333) + : const Color(0xFF363D49), + borderRadius: const BorderRadius.all(Radius.circular(8.0)), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + // icon + FlowySvg( + switch (type) { + ToastificationType.warning => FlowySvgs.toast_warning_filled_s, + ToastificationType.success => FlowySvgs.toast_checked_filled_s, + ToastificationType.error => FlowySvgs.toast_error_filled_s, + _ => throw UnimplementedError(), + }, + size: const Size.square(20.0), + blendMode: null, + ), + const HSpace(8.0), + // text + Flexible( + child: message != null + ? FlowyText( + message!, + maxLines: 2, + figmaLineHeight: 20.0, + overflow: TextOverflow.ellipsis, + color: const Color(0xFFFFFFFF), + ) + : RichText( + text: richMessage!, + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + ), + const HSpace(16.0), + // close + MouseRegion( + cursor: SystemMouseCursors.click, + child: GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: onDismiss, + child: const SizedBox.square( + dimension: 24.0, + child: Center( + child: FlowySvg( + FlowySvgs.toast_close_s, + size: Size.square(16.0), + color: Color(0xFFBDBDBD), + ), + ), + ), + ), + ), + ], + ), + ), + ); + } +} + Future showConfirmDeletionDialog({ required BuildContext context, required String name, diff --git a/frontend/resources/flowy_icons/16x/toast_checked_filled.svg b/frontend/resources/flowy_icons/16x/toast_checked_filled.svg new file mode 100644 index 0000000000..6d43cf16c3 --- /dev/null +++ b/frontend/resources/flowy_icons/16x/toast_checked_filled.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/resources/flowy_icons/16x/ai_close.svg b/frontend/resources/flowy_icons/16x/toast_close.svg similarity index 100% rename from frontend/resources/flowy_icons/16x/ai_close.svg rename to frontend/resources/flowy_icons/16x/toast_close.svg diff --git a/frontend/resources/flowy_icons/16x/toast_error_filled.svg b/frontend/resources/flowy_icons/16x/toast_error_filled.svg new file mode 100644 index 0000000000..bdf63223e2 --- /dev/null +++ b/frontend/resources/flowy_icons/16x/toast_error_filled.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/resources/flowy_icons/16x/toast_warning_filled.svg b/frontend/resources/flowy_icons/16x/toast_warning_filled.svg new file mode 100644 index 0000000000..5c60f1e009 --- /dev/null +++ b/frontend/resources/flowy_icons/16x/toast_warning_filled.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/resources/flowy_icons/16x/warning_filled.svg b/frontend/resources/flowy_icons/16x/warning_filled.svg deleted file mode 100644 index b9b65cc1d7..0000000000 --- a/frontend/resources/flowy_icons/16x/warning_filled.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - From 00cdbe5a1c26869d4e0041f4a8bfa2591cb96298 Mon Sep 17 00:00:00 2001 From: Morn Date: Mon, 30 Dec 2024 16:42:14 +0800 Subject: [PATCH 095/576] fix: issues related to the emoji icon picker (#7063) * fix: remove the scrolling conflict of the icon picker on macOS * fix: the icon is not supported in sites tab * feat: keep the icon panel open after click ramdom * feat: the type of selector opened depends on the already set icon or emoji * feat: the skin tone of the random emoji follows the selected skin ton * fix: unit testing error --- .../database/database_calendar_test.dart | 10 ++- .../desktop/database/database_field_test.dart | 13 +++- .../sidebar/sidebar_view_item_test.dart | 10 ++- .../lib/plugins/base/emoji/emoji_picker.dart | 25 +++++++- .../base/emoji/emoji_picker_screen.dart | 6 +- .../presentation/database_document_title.dart | 6 +- .../base/emoji_picker_button.dart | 26 +++++--- .../cover/document_immersive_cover.dart | 5 +- .../header/document_cover_widget.dart | 24 ++++--- .../header/emoji_icon_widget.dart | 30 ++++++--- .../page_style/_page_style_icon.dart | 9 +-- .../icon_emoji_picker/emoji_search_bar.dart | 23 ++++++- .../flowy_icon_emoji_picker.dart | 64 +++++++++++++++---- .../shared/icon_emoji_picker/icon_picker.dart | 25 ++++++-- .../icon_emoji_picker/icon_search_bar.dart | 19 ++++++ .../lib/shared/icon_emoji_picker/tab.dart | 10 +++ .../lib/startup/tasks/generate_router.dart | 11 +++- .../workspace/_sidebar_workspace_icon.dart | 6 +- .../home/menu/view/view_item.dart | 12 ++-- .../menu/view/view_more_action_button.dart | 7 +- .../pages/sites/publish_info_view_item.dart | 10 ++- .../widgets/emoji_picker/emoji_menu_item.dart | 7 +- .../widgets/rename_view_popover.dart | 8 ++- frontend/appflowy_flutter/pubspec.lock | 4 +- frontend/appflowy_flutter/pubspec.yaml | 2 +- 25 files changed, 281 insertions(+), 91 deletions(-) diff --git a/frontend/appflowy_flutter/integration_test/desktop/database/database_calendar_test.dart b/frontend/appflowy_flutter/integration_test/desktop/database/database_calendar_test.dart index d6df648bb3..3a565cbee9 100644 --- a/frontend/appflowy_flutter/integration_test/desktop/database/database_calendar_test.dart +++ b/frontend/appflowy_flutter/integration_test/desktop/database/database_calendar_test.dart @@ -1,4 +1,5 @@ import 'package:appflowy/plugins/database/calendar/presentation/calendar_event_editor.dart'; +import 'package:appflowy/shared/icon_emoji_picker/recent_icons.dart'; import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart'; import 'package:appflowy_backend/protobuf/flowy-folder/view.pbenum.dart'; import 'package:flowy_infra_ui/style_widget/icon_button.dart'; @@ -9,7 +10,14 @@ import '../../shared/database_test_op.dart'; import '../../shared/util.dart'; void main() { - IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + setUpAll(() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + RecentIcons.enable = false; + }); + + tearDownAll(() { + RecentIcons.enable = true; + }); group('calendar', () { testWidgets('update calendar layout', (tester) async { diff --git a/frontend/appflowy_flutter/integration_test/desktop/database/database_field_test.dart b/frontend/appflowy_flutter/integration_test/desktop/database/database_field_test.dart index 1422aa8aee..6ce248a8a1 100644 --- a/frontend/appflowy_flutter/integration_test/desktop/database/database_field_test.dart +++ b/frontend/appflowy_flutter/integration_test/desktop/database/database_field_test.dart @@ -1,12 +1,12 @@ -import 'package:flutter/material.dart'; - import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/plugins/database/grid/presentation/grid_page.dart'; import 'package:appflowy/plugins/database/widgets/field/type_option_editor/select/select_option.dart'; +import 'package:appflowy/shared/icon_emoji_picker/recent_icons.dart'; import 'package:appflowy/util/field_type_extension.dart'; import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart'; import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart'; import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; @@ -14,7 +14,14 @@ import '../../shared/database_test_op.dart'; import '../../shared/util.dart'; void main() { - IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + setUpAll(() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + RecentIcons.enable = false; + }); + + tearDownAll(() { + RecentIcons.enable = true; + }); group('grid edit field test:', () { testWidgets('rename existing field', (tester) async { diff --git a/frontend/appflowy_flutter/integration_test/desktop/sidebar/sidebar_view_item_test.dart b/frontend/appflowy_flutter/integration_test/desktop/sidebar/sidebar_view_item_test.dart index 1400ccebe3..f2b721e686 100644 --- a/frontend/appflowy_flutter/integration_test/desktop/sidebar/sidebar_view_item_test.dart +++ b/frontend/appflowy_flutter/integration_test/desktop/sidebar/sidebar_view_item_test.dart @@ -1,6 +1,7 @@ import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/plugins/base/emoji/emoji_picker.dart'; import 'package:appflowy/shared/icon_emoji_picker/flowy_icon_emoji_picker.dart'; +import 'package:appflowy/shared/icon_emoji_picker/recent_icons.dart'; import 'package:appflowy/workspace/presentation/home/menu/view/view_item.dart'; import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart'; import 'package:easy_localization/easy_localization.dart'; @@ -12,7 +13,14 @@ import '../../shared/emoji.dart'; import '../../shared/util.dart'; void main() { - IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + setUpAll(() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + RecentIcons.enable = false; + }); + + tearDownAll(() { + RecentIcons.enable = true; + }); group('Sidebar view item tests', () { testWidgets('Access view item context menu by right click', (tester) async { diff --git a/frontend/appflowy_flutter/lib/plugins/base/emoji/emoji_picker.dart b/frontend/appflowy_flutter/lib/plugins/base/emoji/emoji_picker.dart index 517a0d68fa..0f218641da 100644 --- a/frontend/appflowy_flutter/lib/plugins/base/emoji/emoji_picker.dart +++ b/frontend/appflowy_flutter/lib/plugins/base/emoji/emoji_picker.dart @@ -13,6 +13,18 @@ import 'package:flutter_emoji_mart/flutter_emoji_mart.dart'; EmojiData? kCachedEmojiData; const _kRecentEmojiCategoryId = 'Recent'; +class EmojiPickerResult { + EmojiPickerResult({ + required this.emojiId, + required this.emoji, + this.isRandom = false, + }); + + final String emojiId; + final String emoji; + final bool isRandom; +} + class FlowyEmojiPicker extends StatefulWidget { const FlowyEmojiPicker({ super.key, @@ -21,7 +33,7 @@ class FlowyEmojiPicker extends StatefulWidget { this.ensureFocus = false, }); - final EmojiSelectedCallback onEmojiSelected; + final ValueChanged onEmojiSelected; final int emojiPerLine; final bool ensureFocus; @@ -70,7 +82,9 @@ class _FlowyEmojiPickerState extends State { defaultSkinTone: lastSelectedEmojiSkinTone ?? EmojiSkinTone.none, ), onEmojiSelected: (id, emoji) { - widget.onEmojiSelected.call(id, emoji); + widget.onEmojiSelected.call( + EmojiPickerResult(emojiId: id, emoji: emoji), + ); RecentIcons.putEmoji(id); }, padding: const EdgeInsets.symmetric(horizontal: 16.0), @@ -106,7 +120,12 @@ class _FlowyEmojiPickerState extends State { onSkinToneChanged: (value) { skinTone.value = value; }, - onRandomEmojiSelected: widget.onEmojiSelected, + onRandomEmojiSelected: (id, emoji) { + widget.onEmojiSelected.call( + EmojiPickerResult(emojiId: id, emoji: emoji, isRandom: true), + ); + RecentIcons.putEmoji(id); + }, ), ); }, diff --git a/frontend/appflowy_flutter/lib/plugins/base/emoji/emoji_picker_screen.dart b/frontend/appflowy_flutter/lib/plugins/base/emoji/emoji_picker_screen.dart index 513b0fd224..cf32cad611 100644 --- a/frontend/appflowy_flutter/lib/plugins/base/emoji/emoji_picker_screen.dart +++ b/frontend/appflowy_flutter/lib/plugins/base/emoji/emoji_picker_screen.dart @@ -11,14 +11,17 @@ class MobileEmojiPickerScreen extends StatelessWidget { const MobileEmojiPickerScreen({ super.key, this.title, + this.selectedType, this.tabs = const [PickerTabType.emoji, PickerTabType.icon], }); + final PickerTabType? selectedType; final String? title; final List tabs; static const routeName = '/emoji_picker'; static const pageTitle = 'title'; + static const iconSelectedType = 'iconSelectedType'; static const selectTabs = 'tabs'; @override @@ -30,8 +33,9 @@ class MobileEmojiPickerScreen extends StatelessWidget { body: SafeArea( child: FlowyIconEmojiPicker( tabs: tabs, + initialType: selectedType, onSelectedEmoji: (r) { - context.pop(r); + context.pop(r.data); }, ), ), diff --git a/frontend/appflowy_flutter/lib/plugins/database_document/presentation/database_document_title.dart b/frontend/appflowy_flutter/lib/plugins/database_document/presentation/database_document_title.dart index 04b8a30905..7c7a408b17 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_document/presentation/database_document_title.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_document/presentation/database_document_title.dart @@ -244,9 +244,9 @@ class _RenameRowPopoverState extends State { direction: PopoverDirection.bottomWithCenterAligned, offset: const Offset(0, 18), defaultIcon: const FlowySvg(FlowySvgs.document_s), - onSubmitted: (emoji, _) { - widget.onUpdateIcon(emoji); - PopoverContainer.of(context).close(); + onSubmitted: (r, _) { + widget.onUpdateIcon(r.data); + if (!r.keepOpen) PopoverContainer.of(context).close(); }, ), const HSpace(6), diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/base/emoji_picker_button.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/base/emoji_picker_button.dart index e2113cc1fb..894536dbcc 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/base/emoji_picker_button.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/base/emoji_picker_button.dart @@ -26,8 +26,10 @@ class EmojiPickerButton extends StatelessWidget { final EmojiIconData emoji; final double emojiSize; final Size emojiPickerSize; - final void Function(EmojiIconData emoji, PopoverController? controller) - onSubmitted; + final void Function( + SelectedEmojiIconResult result, + PopoverController? controller, + ) onSubmitted; final PopoverController popoverController = PopoverController(); final Widget? defaultIcon; final Offset? offset; @@ -85,8 +87,10 @@ class _DesktopEmojiPickerButton extends StatelessWidget { final EmojiIconData emoji; final double emojiSize; final Size emojiPickerSize; - final void Function(EmojiIconData emoji, PopoverController? controller) - onSubmitted; + final void Function( + SelectedEmojiIconResult result, + PopoverController? controller, + ) onSubmitted; final PopoverController popoverController = PopoverController(); final Widget? defaultIcon; final Offset? offset; @@ -113,6 +117,7 @@ class _DesktopEmojiPickerButton extends StatelessWidget { height: emojiPickerSize.height, padding: const EdgeInsets.all(4.0), child: FlowyIconEmojiPicker( + initialType: emoji.type.toPickerTabType(), onSelectedEmoji: (r) { onSubmitted(r, popoverController); }, @@ -156,8 +161,10 @@ class _MobileEmojiPickerButton extends StatelessWidget { final EmojiIconData emoji; final double emojiSize; - final void Function(EmojiIconData emoji, PopoverController? controller) - onSubmitted; + final void Function( + SelectedEmojiIconResult result, + PopoverController? controller, + ) onSubmitted; final String? title; final bool enable; final EdgeInsets? margin; @@ -177,11 +184,14 @@ class _MobileEmojiPickerButton extends StatelessWidget { final result = await context.push( Uri( path: MobileEmojiPickerScreen.routeName, - queryParameters: {MobileEmojiPickerScreen.pageTitle: title}, + queryParameters: { + MobileEmojiPickerScreen.pageTitle: title, + MobileEmojiPickerScreen.iconSelectedType: emoji.type.name, + }, ).toString(), ); if (result != null) { - onSubmitted(result, null); + onSubmitted(result.toSelectedResult(), null); } } : null, diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/cover/document_immersive_cover.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/cover/document_immersive_cover.dart index 05c891bd1e..7485bfe018 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/cover/document_immersive_cover.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/cover/document_immersive_cover.dart @@ -227,11 +227,12 @@ class _DocumentImmersiveCoverState extends State { value: pageStyleIconBloc, child: Expanded( child: FlowyIconEmojiPicker( + initialType: icon.type.toPickerTabType(), onSelectedEmoji: (r) { pageStyleIconBloc.add( - PageStyleIconEvent.updateIcon(r, true), + PageStyleIconEvent.updateIcon(r.data, true), ); - Navigator.pop(context); + if (!r.keepOpen) Navigator.pop(context); }, ), ), diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/header/document_cover_widget.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/header/document_cover_widget.dart index 54feee3d90..d28020d0fe 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/header/document_cover_widget.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/header/document_cover_widget.dart @@ -468,9 +468,9 @@ class _DocumentHeaderToolbarState extends State { popupBuilder: (BuildContext popoverContext) { isPopoverOpen = true; return FlowyIconEmojiPicker( - onSelectedEmoji: (result) { - widget.onIconOrCoverChanged(icon: result); - _popoverController.close(); + onSelectedEmoji: (r) { + widget.onIconOrCoverChanged(icon: r.data); + if (!r.keepOpen) _popoverController.close(); }, ); }, @@ -838,9 +838,7 @@ class _DocumentIconState extends State { @override Widget build(BuildContext context) { - Widget child = EmojiIconWidget( - emoji: widget.icon, - ); + Widget child = EmojiIconWidget(emoji: widget.icon); if (UniversalPlatform.isDesktopOrWeb) { child = AppFlowyPopover( @@ -852,9 +850,10 @@ class _DocumentIconState extends State { child: child, popupBuilder: (BuildContext popoverContext) { return FlowyIconEmojiPicker( - onSelectedEmoji: (result) { - widget.onChangeIcon(result); - _popoverController.close(); + initialType: widget.icon.type.toPickerTabType(), + onSelectedEmoji: (r) { + widget.onChangeIcon(r.data); + if (!r.keepOpen) _popoverController.close(); }, ); }, @@ -864,7 +863,12 @@ class _DocumentIconState extends State { child: child, onTap: () async { final result = await context.push( - MobileEmojiPickerScreen.routeName, + Uri( + path: MobileEmojiPickerScreen.routeName, + queryParameters: { + MobileEmojiPickerScreen.iconSelectedType: widget.icon.type.name, + }, + ).toString(), ); if (result != null) { widget.onChangeIcon(result); diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/header/emoji_icon_widget.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/header/emoji_icon_widget.dart index 77b0c158f5..a2f3d2aeeb 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/header/emoji_icon_widget.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/header/emoji_icon_widget.dart @@ -1,4 +1,5 @@ import 'dart:convert'; +import 'dart:io'; import 'package:appflowy/plugins/base/emoji/emoji_text.dart'; import 'package:appflowy/shared/icon_emoji_picker/icon_picker.dart'; @@ -67,24 +68,35 @@ class RawEmojiIconWidget extends StatelessWidget { @override Widget build(BuildContext context) { - final defaultEmoji = EmojiText( - emoji: '❓', - fontSize: emojiSize, - textAlign: TextAlign.center, + final defaultEmoji = SizedBox( + width: emojiSize, + child: EmojiText( + emoji: '❓', + fontSize: emojiSize, + textAlign: TextAlign.center, + ), ); try { switch (emoji.type) { case FlowyIconType.emoji: - return EmojiText( - emoji: emoji.emoji, - fontSize: emojiSize, - textAlign: TextAlign.center, + return SizedBox( + width: emojiSize, + child: EmojiText( + emoji: emoji.emoji, + fontSize: emojiSize, + textAlign: TextAlign.center, + ), ); case FlowyIconType.icon: final iconData = IconsData.fromJson(jsonDecode(emoji.emoji)); + + /// Under the same width conditions, icons on macOS seem to appear + /// larger than emojis, so 0.9 is used here to slightly reduce the + /// size of the icons + final iconSize = Platform.isMacOS ? emojiSize * 0.9 : emojiSize; return IconWidget( data: iconData, - size: emojiSize, + size: iconSize, ); default: return defaultEmoji; diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/page_style/_page_style_icon.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/page_style/_page_style_icon.dart index 6b640be1c4..6647796833 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/page_style/_page_style_icon.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/page_style/_page_style_icon.dart @@ -35,7 +35,7 @@ class _PageStyleIconState extends State { builder: (context, state) { final icon = state.icon ?? EmojiIconData.none(); return GestureDetector( - onTap: () => _showIconSelector(context), + onTap: () => _showIconSelector(context, icon), behavior: HitTestBehavior.opaque, child: Container( height: 52, @@ -66,7 +66,7 @@ class _PageStyleIconState extends State { ); } - void _showIconSelector(BuildContext context) { + void _showIconSelector(BuildContext context, EmojiIconData icon) { Navigator.pop(context); final pageStyleIconBloc = PageStyleIconBloc(view: widget.view) ..add(const PageStyleIconEvent.initial()); @@ -85,11 +85,12 @@ class _PageStyleIconState extends State { value: pageStyleIconBloc, child: Expanded( child: FlowyIconEmojiPicker( + initialType: icon.type.toPickerTabType(), onSelectedEmoji: (r) { pageStyleIconBloc.add( - PageStyleIconEvent.updateIcon(r, true), + PageStyleIconEvent.updateIcon(r.data, true), ); - Navigator.pop(ctx); + if (!r.keepOpen) Navigator.pop(ctx); }, ), ), diff --git a/frontend/appflowy_flutter/lib/shared/icon_emoji_picker/emoji_search_bar.dart b/frontend/appflowy_flutter/lib/shared/icon_emoji_picker/emoji_search_bar.dart index 5604da1b33..4520a2b118 100644 --- a/frontend/appflowy_flutter/lib/shared/icon_emoji_picker/emoji_search_bar.dart +++ b/frontend/appflowy_flutter/lib/shared/icon_emoji_picker/emoji_search_bar.dart @@ -34,6 +34,7 @@ class FlowyEmojiSearchBar extends StatefulWidget { class _FlowyEmojiSearchBarState extends State { final TextEditingController controller = TextEditingController(); + EmojiSkinTone skinTone = lastSelectedEmojiSkinTone ?? EmojiSkinTone.none; @override void dispose() { @@ -58,12 +59,18 @@ class _FlowyEmojiSearchBarState extends State { ), const HSpace(8.0), _RandomEmojiButton( + skinTone: skinTone, emojiData: widget.emojiData, onRandomEmojiSelected: widget.onRandomEmojiSelected, ), const HSpace(8.0), FlowyEmojiSkinToneSelector( - onEmojiSkinToneChanged: widget.onSkinToneChanged, + onEmojiSkinToneChanged: (v) { + setState(() { + skinTone = v; + }); + widget.onSkinToneChanged.call(v); + }, ), ], ), @@ -73,10 +80,12 @@ class _FlowyEmojiSearchBarState extends State { class _RandomEmojiButton extends StatelessWidget { const _RandomEmojiButton({ + required this.skinTone, required this.emojiData, required this.onRandomEmojiSelected, }); + final EmojiSkinTone skinTone; final EmojiData emojiData; final EmojiSelectedCallback onRandomEmojiSelected; @@ -100,9 +109,14 @@ class _RandomEmojiButton extends StatelessWidget { ), onTap: () { final random = emojiData.random; + final emojiId = random.$1; + final emoji = emojiData.getEmojiById( + emojiId, + skinTone: skinTone, + ); onRandomEmojiSelected( - random.$1, - random.$2, + emojiId, + emoji, ); }, ), @@ -131,6 +145,9 @@ class _SearchTextFieldState extends State<_SearchTextField> { @override void initState() { super.initState(); + + /// Sometimes focus is lost due to the [SelectionGestureInterceptor] in [KeyboardServiceWidgetState] + /// this is to ensure that focus can be regained within a short period of time if (widget.ensureFocus) { Future.delayed(const Duration(milliseconds: 200), () { if (!mounted || focusNode.hasFocus) return; diff --git a/frontend/appflowy_flutter/lib/shared/icon_emoji_picker/flowy_icon_emoji_picker.dart b/frontend/appflowy_flutter/lib/shared/icon_emoji_picker/flowy_icon_emoji_picker.dart index 727d0b8fba..132e79e8e8 100644 --- a/frontend/appflowy_flutter/lib/shared/icon_emoji_picker/flowy_icon_emoji_picker.dart +++ b/frontend/appflowy_flutter/lib/shared/icon_emoji_picker/flowy_icon_emoji_picker.dart @@ -6,6 +6,7 @@ import 'package:appflowy_backend/protobuf/flowy-folder/icon.pb.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flutter/material.dart' hide Icon; +import 'package:flutter/services.dart'; import 'package:universal_platform/universal_platform.dart'; extension ToProto on FlowyIconType { @@ -46,6 +47,10 @@ enum FlowyIconType { custom; } +extension FlowyIconTypeToPickerTabType on FlowyIconType { + PickerTabType? toPickerTabType() => name.toPickerTabType(); +} + class EmojiIconData { factory EmojiIconData.none() => const EmojiIconData(FlowyIconType.icon, ''); @@ -78,17 +83,35 @@ class EmojiIconData { bool get isNotEmpty => emoji.isNotEmpty; } +class SelectedEmojiIconResult { + SelectedEmojiIconResult(this.data, this.keepOpen); + + final EmojiIconData data; + final bool keepOpen; + + FlowyIconType get type => data.type; + + String get emoji => data.emoji; +} + +extension EmojiIconDataToSelectedResultExtension on EmojiIconData { + SelectedEmojiIconResult toSelectedResult({bool keepOpen = false}) => + SelectedEmojiIconResult(this, keepOpen); +} + class FlowyIconEmojiPicker extends StatefulWidget { const FlowyIconEmojiPicker({ super.key, this.onSelectedEmoji, + this.initialType, this.enableBackgroundColorSelection = true, this.tabs = const [PickerTabType.emoji, PickerTabType.icon], }); - final ValueChanged? onSelectedEmoji; + final ValueChanged? onSelectedEmoji; final bool enableBackgroundColorSelection; final List tabs; + final PickerTabType? initialType; @override State createState() => _FlowyIconEmojiPickerState(); @@ -96,12 +119,23 @@ class FlowyIconEmojiPicker extends StatefulWidget { class _FlowyIconEmojiPickerState extends State with SingleTickerProviderStateMixin { - late final controller = TabController( - length: widget.tabs.length, - vsync: this, - ); + late TabController controller; int currentIndex = 0; + @override + void initState() { + super.initState(); + final initialType = widget.initialType; + if (initialType != null) { + currentIndex = widget.tabs.indexOf(initialType); + } + controller = TabController( + initialIndex: currentIndex, + length: widget.tabs.length, + vsync: this, + ); + } + @override void dispose() { controller.dispose(); @@ -127,7 +161,8 @@ class _FlowyIconEmojiPickerState extends State ), _RemoveIconButton( onTap: () { - widget.onSelectedEmoji?.call(EmojiIconData.none()); + widget.onSelectedEmoji + ?.call(EmojiIconData.none().toSelectedResult()); }, ), ], @@ -155,9 +190,12 @@ class _FlowyIconEmojiPickerState extends State return FlowyEmojiPicker( ensureFocus: true, emojiPerLine: _getEmojiPerLine(context), - onEmojiSelected: (_, emoji) => widget.onSelectedEmoji?.call( - EmojiIconData.emoji(emoji), - ), + onEmojiSelected: (r) { + widget.onSelectedEmoji?.call( + EmojiIconData.emoji(r.emoji).toSelectedResult(keepOpen: r.isRandom), + ); + SystemChannels.textInput.invokeMethod('TextInput.hide'); + }, ); } @@ -171,9 +209,13 @@ class _FlowyIconEmojiPickerState extends State Widget _buildIconPicker() { return FlowyIconPicker( + ensureFocus: true, enableBackgroundColorSelection: widget.enableBackgroundColorSelection, - onSelectedIcon: (result) { - widget.onSelectedEmoji?.call(result.toEmojiIconData()); + onSelectedIcon: (r) { + widget.onSelectedEmoji?.call( + r.data.toEmojiIconData().toSelectedResult(keepOpen: r.isRandom), + ); + SystemChannels.textInput.invokeMethod('TextInput.hide'); }, ); } diff --git a/frontend/appflowy_flutter/lib/shared/icon_emoji_picker/icon_picker.dart b/frontend/appflowy_flutter/lib/shared/icon_emoji_picker/icon_picker.dart index 75f5633ee5..4e34badacd 100644 --- a/frontend/appflowy_flutter/lib/shared/icon_emoji_picker/icon_picker.dart +++ b/frontend/appflowy_flutter/lib/shared/icon_emoji_picker/icon_picker.dart @@ -75,17 +75,31 @@ Future> loadIconGroups() async { } } +class IconPickerResult { + IconPickerResult(this.data, this.isRandom); + + final IconsData data; + final bool isRandom; +} + +extension IconsDataToIconPickerResultExtension on IconsData { + IconPickerResult toResult({bool isRandom = false}) => + IconPickerResult(this, isRandom); +} + class FlowyIconPicker extends StatefulWidget { const FlowyIconPicker({ super.key, required this.onSelectedIcon, required this.enableBackgroundColorSelection, this.iconPerLine = 9, + this.ensureFocus = false, }); final bool enableBackgroundColorSelection; - final ValueChanged onSelectedIcon; + final ValueChanged onSelectedIcon; final int iconPerLine; + final bool ensureFocus; @override State createState() => _FlowyIconPickerState(); @@ -142,6 +156,7 @@ class _FlowyIconPickerState extends State { Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0), child: IconSearchBar( + ensureFocus: widget.ensureFocus, onRandomTap: () { final value = kIconGroups?.randomIcon(); if (value == null) { @@ -154,8 +169,9 @@ class _FlowyIconPickerState extends State { value.$2.content, value.$2.name, color, - ), + ).toResult(isRandom: true), ); + RecentIcons.putIcon(value.$2); }, onKeywordChanged: (keyword) => { debounce.call(() { @@ -193,14 +209,14 @@ class _FlowyIconPickerState extends State { iconGroups: filteredIconGroups, enableBackgroundColorSelection: widget.enableBackgroundColorSelection, - onSelectedIcon: widget.onSelectedIcon, + onSelectedIcon: (r) => widget.onSelectedIcon.call(r.toResult()), iconPerLine: widget.iconPerLine, ); } return IconPicker( iconGroups: iconGroups, enableBackgroundColorSelection: widget.enableBackgroundColorSelection, - onSelectedIcon: widget.onSelectedIcon, + onSelectedIcon: (r) => widget.onSelectedIcon.call(r.toResult()), iconPerLine: widget.iconPerLine, ); }, @@ -278,6 +294,7 @@ class _IconPickerState extends State { crossAxisCount: widget.iconPerLine, ), itemCount: iconGroup.icons.length, + physics: const NeverScrollableScrollPhysics(), shrinkWrap: true, itemBuilder: (context, index) { final icon = iconGroup.icons[index]; diff --git a/frontend/appflowy_flutter/lib/shared/icon_emoji_picker/icon_search_bar.dart b/frontend/appflowy_flutter/lib/shared/icon_emoji_picker/icon_search_bar.dart index dc079bbc4e..a12be47684 100644 --- a/frontend/appflowy_flutter/lib/shared/icon_emoji_picker/icon_search_bar.dart +++ b/frontend/appflowy_flutter/lib/shared/icon_emoji_picker/icon_search_bar.dart @@ -16,9 +16,11 @@ class IconSearchBar extends StatefulWidget { super.key, required this.onRandomTap, required this.onKeywordChanged, + this.ensureFocus = false, }); final VoidCallback onRandomTap; + final bool ensureFocus; final IconKeywordChangedCallback onKeywordChanged; @override @@ -46,6 +48,7 @@ class _IconSearchBarState extends State { Expanded( child: _SearchTextField( onKeywordChanged: widget.onKeywordChanged, + ensureFocus: widget.ensureFocus, ), ), const HSpace(8.0), @@ -93,9 +96,11 @@ class _RandomIconButton extends StatelessWidget { class _SearchTextField extends StatefulWidget { const _SearchTextField({ required this.onKeywordChanged, + this.ensureFocus = false, }); final IconKeywordChangedCallback onKeywordChanged; + final bool ensureFocus; @override State<_SearchTextField> createState() => _SearchTextFieldState(); @@ -105,6 +110,20 @@ class _SearchTextFieldState extends State<_SearchTextField> { final TextEditingController controller = TextEditingController(); final FocusNode focusNode = FocusNode(); + @override + void initState() { + super.initState(); + + /// Sometimes focus is lost due to the [SelectionGestureInterceptor] in [KeyboardServiceWidgetState] + /// this is to ensure that focus can be regained within a short period of time + if (widget.ensureFocus) { + Future.delayed(const Duration(milliseconds: 200), () { + if (!mounted || focusNode.hasFocus) return; + focusNode.requestFocus(); + }); + } + } + @override void dispose() { controller.dispose(); diff --git a/frontend/appflowy_flutter/lib/shared/icon_emoji_picker/tab.dart b/frontend/appflowy_flutter/lib/shared/icon_emoji_picker/tab.dart index 56a363132c..b74d1145c6 100644 --- a/frontend/appflowy_flutter/lib/shared/icon_emoji_picker/tab.dart +++ b/frontend/appflowy_flutter/lib/shared/icon_emoji_picker/tab.dart @@ -15,6 +15,16 @@ enum PickerTabType { } } +extension StringToPickerTabType on String { + PickerTabType? toPickerTabType() { + try { + return PickerTabType.values.byName(this); + } on ArgumentError { + return null; + } + } +} + class PickerTab extends StatelessWidget { const PickerTab({ super.key, diff --git a/frontend/appflowy_flutter/lib/startup/tasks/generate_router.dart b/frontend/appflowy_flutter/lib/startup/tasks/generate_router.dart index 27bfdcee5b..bcca2d0a69 100644 --- a/frontend/appflowy_flutter/lib/startup/tasks/generate_router.dart +++ b/frontend/appflowy_flutter/lib/startup/tasks/generate_router.dart @@ -285,14 +285,21 @@ GoRoute _mobileEmojiPickerPageRoute() { state.uri.queryParameters[MobileEmojiPickerScreen.pageTitle]; final selectTabs = state.uri.queryParameters[MobileEmojiPickerScreen.selectTabs] ?? ''; + final selectedType = state + .uri.queryParameters[MobileEmojiPickerScreen.iconSelectedType] + ?.toPickerTabType(); final tabs = selectTabs .split('-') .map((e) => PickerTabType.values.byName(e)) .toList(); return MaterialExtendedPage( child: tabs.isEmpty - ? MobileEmojiPickerScreen(title: title) - : MobileEmojiPickerScreen(title: title, tabs: tabs), + ? MobileEmojiPickerScreen(title: title, selectedType: selectedType) + : MobileEmojiPickerScreen( + title: title, + selectedType: selectedType, + tabs: tabs, + ), ); }, ); diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/workspace/_sidebar_workspace_icon.dart b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/workspace/_sidebar_workspace_icon.dart index 12dec42026..1f9f4b03b8 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/workspace/_sidebar_workspace_icon.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/workspace/_sidebar_workspace_icon.dart @@ -96,9 +96,9 @@ class _WorkspaceIconState extends State { margin: const EdgeInsets.all(0), popupBuilder: (_) => FlowyIconEmojiPicker( tabs: const [PickerTabType.emoji], - onSelectedEmoji: (result) { - widget.onSelected(result); - controller.close(); + onSelectedEmoji: (r) { + widget.onSelected(r.data); + if (!r.keepOpen) controller.close(); }, ), child: MouseRegion( diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/view/view_item.dart b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/view/view_item.dart index d6b5dfe297..3f39b976d2 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/view/view_item.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/view/view_item.dart @@ -641,12 +641,13 @@ class _SingleInnerViewItemState extends State { popupBuilder: (context) { isIconPickerOpened = true; return FlowyIconEmojiPicker( - onSelectedEmoji: (result) { + initialType: iconData.type.toPickerTabType(), + onSelectedEmoji: (r) { ViewBackendService.updateViewIcon( viewId: widget.view.id, - viewIcon: result, + viewIcon: r.data, ); - controller.close(); + if (!r.keepOpen) controller.close(); }, ); }, @@ -770,13 +771,12 @@ class _SingleInnerViewItemState extends State { context.read().add(const ViewEvent.collapseAllPages()); break; case ViewMoreActionType.changeIcon: - if (data is! EmojiIconData) { + if (data is! SelectedEmojiIconResult) { return; } - final result = data; await ViewBackendService.updateViewIcon( viewId: widget.view.id, - viewIcon: result, + viewIcon: data.data, ); break; case ViewMoreActionType.moveTo: diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/view/view_more_action_button.dart b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/view/view_more_action_button.dart index ba9a946cc2..186c593572 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/view/view_more_action_button.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/view/view_more_action_button.dart @@ -58,7 +58,11 @@ class ViewMoreActionPopover extends StatelessWidget { (e) => ViewMoreActionTypeWrapper(e, view, (controller, data) { onEditing(false); onAction(e, data); - controller.close(); + bool enableClose = true; + if (data is SelectedEmojiIconResult) { + if (data.keepOpen) enableClose = false; + } + if (enableClose) controller.close(); }), ) .toList(); @@ -172,6 +176,7 @@ class ViewMoreActionTypeWrapper extends CustomActionCell { margin: const EdgeInsets.all(0), clickHandler: PopoverClickHandler.gestureDetector, popupBuilder: (_) => FlowyIconEmojiPicker( + initialType: sourceView.icon.toEmojiIconData().type.toPickerTabType(), onSelectedEmoji: (result) => onTap(controller, result), ), child: child, diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/settings/pages/sites/publish_info_view_item.dart b/frontend/appflowy_flutter/lib/workspace/presentation/settings/pages/sites/publish_info_view_item.dart index f2a3980bf6..3ba2c7e75e 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/settings/pages/sites/publish_info_view_item.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/settings/pages/sites/publish_info_view_item.dart @@ -1,4 +1,6 @@ import 'package:appflowy/generated/locale_keys.g.dart'; +import 'package:appflowy/plugins/document/presentation/editor_plugins/header/emoji_icon_widget.dart'; +import 'package:appflowy/shared/icon_emoji_picker/flowy_icon_emoji_picker.dart'; import 'package:appflowy/util/string_extension.dart'; import 'package:appflowy/workspace/application/view/view_ext.dart'; import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart'; @@ -51,13 +53,9 @@ class PublishInfoViewItem extends StatelessWidget { } Widget _buildIcon() { - final icon = publishInfoView.view.icon.value; + final icon = publishInfoView.view.icon.toEmojiIconData(); return icon.isNotEmpty - ? FlowyText.emoji( - icon, - fontSize: 16.0, - figmaLineHeight: 18.0, - ) + ? RawEmojiIconWidget(emoji: icon, emojiSize: 16.0) : publishInfoView.view.defaultIcon(); } } diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/emoji_picker/emoji_menu_item.dart b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/emoji_picker/emoji_menu_item.dart index 6cdccb3b3b..ab952386bd 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/emoji_picker/emoji_menu_item.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/emoji_picker/emoji_menu_item.dart @@ -1,12 +1,11 @@ -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; - import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/plugins/base/emoji/emoji_picker.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/base/selectable_svg_widget.dart'; import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flowy_infra_ui/style_widget/decoration.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; SelectionMenuItem emojiMenuItem = SelectionMenuItem( getName: LocaleKeys.document_plugins_emoji.tr, @@ -109,7 +108,7 @@ class _EmojiSelectionMenuState extends State { @override Widget build(BuildContext context) { return FlowyEmojiPicker( - onEmojiSelected: (_, emoji) => widget.onSubmitted(emoji), + onEmojiSelected: (r) => widget.onSubmitted(r.emoji), ); } } diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/widgets/rename_view_popover.dart b/frontend/appflowy_flutter/lib/workspace/presentation/widgets/rename_view_popover.dart index ccea2f895c..cae65cafca 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/widgets/rename_view_popover.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/widgets/rename_view_popover.dart @@ -91,13 +91,15 @@ class _RenameViewPopoverState extends State { } Future _updateViewIcon( - EmojiIconData emoji, + SelectedEmojiIconResult r, PopoverController? _, ) async { await ViewBackendService.updateViewIcon( viewId: widget.viewId, - viewIcon: emoji, + viewIcon: r.data, ); - widget.popoverController.close(); + if (!r.keepOpen) { + widget.popoverController.close(); + } } } diff --git a/frontend/appflowy_flutter/pubspec.lock b/frontend/appflowy_flutter/pubspec.lock index c0df44a207..b108a0c23a 100644 --- a/frontend/appflowy_flutter/pubspec.lock +++ b/frontend/appflowy_flutter/pubspec.lock @@ -757,8 +757,8 @@ packages: dependency: "direct main" description: path: "." - ref: "8a9fa49" - resolved-ref: "8a9fa491cb3b86baf78b0a33c2c37a29d1cae028" + ref: "355aa56" + resolved-ref: "355aa56e9c74a91e00370a882739e0bb98c30bd8" url: "https://github.com/LucasXu0/emoji_mart.git" source: git version: "1.0.2" diff --git a/frontend/appflowy_flutter/pubspec.yaml b/frontend/appflowy_flutter/pubspec.yaml index 60ab502b2b..9433511050 100644 --- a/frontend/appflowy_flutter/pubspec.yaml +++ b/frontend/appflowy_flutter/pubspec.yaml @@ -73,7 +73,7 @@ dependencies: flutter_emoji_mart: git: url: https://github.com/LucasXu0/emoji_mart.git - ref: "8a9fa49" + ref: "355aa56" flutter_math_fork: ^0.7.3 flutter_slidable: ^3.0.0 From 5ffa27f54560fead262ea28b8fd06e821ba70008 Mon Sep 17 00:00:00 2001 From: Morn Date: Mon, 30 Dec 2024 16:57:59 +0800 Subject: [PATCH 096/576] fix: toolbar menu not showing beacase keyboard height is not updated in time (#7060) --- .../mobile_toolbar_v3/appflowy_mobile_toolbar.dart | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mobile_toolbar_v3/appflowy_mobile_toolbar.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mobile_toolbar_v3/appflowy_mobile_toolbar.dart index cd806f6c56..787ccfda9f 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mobile_toolbar_v3/appflowy_mobile_toolbar.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mobile_toolbar_v3/appflowy_mobile_toolbar.dart @@ -20,6 +20,7 @@ import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; abstract class AppFlowyMobileToolbarWidgetService { void closeItemMenu(); + void closeKeyboard(); PropertyValueNotifier get showMenuNotifier; @@ -179,7 +180,13 @@ class _MobileToolbarState extends State<_MobileToolbar> // but in this case, we don't want to update the cached keyboard height. // This is because we want to keep the same height when the menu is shown. bool canUpdateCachedKeyboardHeight = true; - ValueNotifier cachedKeyboardHeight = ValueNotifier(0.0); + + /// when the [_MobileToolbar] disposed before the keyboard height can be updated in time, + /// there will be an issue with the height being 0 + /// this is used to globally record the height. + static double _globalCachedKeyboardHeight = 0.0; + ValueNotifier cachedKeyboardHeight = + ValueNotifier(_globalCachedKeyboardHeight); // used to check if click the same item again int? selectedMenuIndex; @@ -408,6 +415,9 @@ class _MobileToolbarState extends State<_MobileToolbar> ); } } + if (keyboardHeight > 0) { + _globalCachedKeyboardHeight = keyboardHeight; + } return SizedBox( height: keyboardHeight, child: (showingMenu && selectedMenuIndex != null) From 20b16cf174a0772f8f103c45c013867a0b18414d Mon Sep 17 00:00:00 2001 From: Richard Shiue <71320345+richardshiue@users.noreply.github.com> Date: Mon, 30 Dec 2024 17:17:36 +0800 Subject: [PATCH 097/576] feat: add toast messages for ai chat interactions (#7086) --- .../message/ai_message_action_bar.dart | 48 +++++++++++++++++-- .../presentation/message/ai_metadata.dart | 1 + .../presentation/message/message_util.dart | 8 ++++ frontend/resources/translations/en.json | 4 +- 4 files changed, 55 insertions(+), 6 deletions(-) diff --git a/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/message/ai_message_action_bar.dart b/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/message/ai_message_action_bar.dart index 64183d0346..fe61398caa 100644 --- a/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/message/ai_message_action_bar.dart +++ b/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/message/ai_message_action_bar.dart @@ -274,7 +274,7 @@ class _SaveToPageButtonState extends State { onPressed: () async { final documentId = getOpenedDocumentId(); if (documentId != null) { - await onAddToExistingPage(documentId); + await onAddToExistingPage(context, documentId); await forceReloadAndUpdateSelection(documentId); } else { widget.onOverrideVisibility?.call(true); @@ -298,9 +298,8 @@ class _SaveToPageButtonState extends State { }, onAddToExistingPage: (documentId) async { popoverController.close(); - await onAddToExistingPage(documentId); - final view = - await ViewBackendService.getView(documentId).toNullable(); + final view = await onAddToExistingPage(context, documentId); + if (context.mounted) { openPageFromMessage(context, view); } @@ -309,12 +308,20 @@ class _SaveToPageButtonState extends State { ); } - Future onAddToExistingPage(String documentId) async { + Future onAddToExistingPage( + BuildContext context, + String documentId, + ) async { await ChatEditDocumentService.addMessageToPage( documentId, widget.textMessage, ); await Future.delayed(const Duration(milliseconds: 500)); + final view = await ViewBackendService.getView(documentId).toNullable(); + if (context.mounted) { + showSaveMessageSuccessToast(context, view); + } + return view; } void addMessageToNewPage(BuildContext context) async { @@ -327,12 +334,43 @@ class _SaveToPageButtonState extends State { chatView.parentViewId, [widget.textMessage], ); + if (context.mounted) { + showSaveMessageSuccessToast(context, newView); openPageFromMessage(context, newView); } } } + void showSaveMessageSuccessToast(BuildContext context, ViewPB? view) { + if (view == null) { + return; + } + showToastNotification( + context, + richMessage: TextSpan( + children: [ + TextSpan( + text: LocaleKeys.chat_addToNewPageSuccessToast.tr(), + style: Theme.of(context).textTheme.bodyMedium?.copyWith( + color: const Color(0xFFFFFFFF), + ), + ), + const TextSpan( + text: ' ', + ), + TextSpan( + text: view.nameOrDefault, + style: Theme.of(context).textTheme.bodyMedium?.copyWith( + color: const Color(0xFFFFFFFF), + fontWeight: FontWeight.w700, + ), + ), + ], + ), + ); + } + Future forceReloadAndUpdateSelection(String documentId) async { final bloc = DocumentBloc.findOpen(documentId); if (bloc == null) { diff --git a/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/message/ai_metadata.dart b/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/message/ai_metadata.dart index 1e06ff5f46..cc97610e8d 100644 --- a/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/message/ai_metadata.dart +++ b/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/message/ai_metadata.dart @@ -90,6 +90,7 @@ class _AIMessageMetadataState extends State { data == null) { return _MetadataButton( name: m.name, + onTap: () => widget.onSelectedMetadata?.call(m), ); } return BlocProvider( diff --git a/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/message/message_util.dart b/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/message/message_util.dart index 4673b9def1..97e5e84b54 100644 --- a/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/message/message_util.dart +++ b/frontend/appflowy_flutter/lib/plugins/ai_chat/presentation/message/message_util.dart @@ -1,8 +1,11 @@ +import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/mobile/application/mobile_router.dart'; import 'package:appflowy/startup/startup.dart'; import 'package:appflowy/workspace/application/tabs/tabs_bloc.dart'; import 'package:appflowy/workspace/application/view/view_ext.dart'; +import 'package:appflowy/workspace/presentation/widgets/dialogs.dart'; import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart'; +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/widgets.dart'; import 'package:universal_platform/universal_platform.dart'; @@ -10,6 +13,11 @@ import 'package:universal_platform/universal_platform.dart'; /// on mobile void openPageFromMessage(BuildContext context, ViewPB? view) { if (view == null) { + showToastNotification( + context, + message: LocaleKeys.chat_openPagePreviewFailedToast.tr(), + type: ToastificationType.error, + ); return; } if (UniversalPlatform.isDesktop) { diff --git a/frontend/resources/translations/en.json b/frontend/resources/translations/en.json index 284d912d6f..ef8078a2a8 100644 --- a/frontend/resources/translations/en.json +++ b/frontend/resources/translations/en.json @@ -218,7 +218,9 @@ "addToPageButton": "Add to page", "addToPageTitle": "Add message to...", "addToNewPage": "Add to a new page", - "addToNewPageName": "Messages extracted from \"{}\"" + "addToNewPageName": "Messages extracted from \"{}\"", + "addToNewPageSuccessToast": "Message added to", + "openPagePreviewFailedToast": "Failed to open page" }, "trash": { "text": "Trash", From e04c1bdaec3a19f641f0687bfa038ced6f43b3c5 Mon Sep 17 00:00:00 2001 From: Lucas Date: Mon, 30 Dec 2024 17:53:55 +0800 Subject: [PATCH 098/576] chore: update changelog (#7095) --- CHANGELOG.md | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e4f1e5931..188feb596f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,24 @@ # Release Notes -## Version 0.7.9 - 25/12/2024 +## Version 0.7.9 - 30/12/2024 ### New Features - +- Meet AppFlowy Web (Lite): Use AppFlowy directly in your browser. + - Create beautiful documents with 22 content types and markdown support + - Use Quick Note to save anything you want to remember—like meeting notes, a grocery list, or to-dos + - Invite members to your workspace for seamless collaboration + - Create multiple public/private spaces to better organize your content +- Simple Table is now available on Mobile, designed specifically for mobile devices. + - Create and manage Simple Table blocks on Mobile with easy-to-use action menus. + - Use the '+' button in the fixed toolbar to easily add a content block into a table cell on Mobile + - Use '/' to insert a content block into a table cell on Desktop +- Add pages as AI sources in AI chat, enabling you to ask questions about the selected sources +- Add messages to an editable document while chatting with AI side by side +- The new Emoji menu now includes Icons with a Recent section for quickly reusing emojis/icons +- Drag a page from the sidebar into a document to easily mention the page without typing its title +- Paste as plain text, a new option in the right-click paste menu ### Bug Fixes +- Fixed misalignment in numbered lists +- Resolved several bugs in the emoji menu +- Fixed a bug with checklist items ## Version 0.7.8 - 18/12/2024 ### New Features From 92722d0922745b11c32364a6627cf2bac8a2bd1c Mon Sep 17 00:00:00 2001 From: Richard Shiue <71320345+richardshiue@users.noreply.github.com> Date: Mon, 30 Dec 2024 17:55:40 +0800 Subject: [PATCH 099/576] fix(flutter_desktop): workspace menu ui issues (#7091) * fix(flutter_desktop): remove log out and workspace option popovers conflict * test: add integration test * fix(flutter_desktop): workspace list scrollbar overlaps with list * chore(flutter_desktop): fix padding around import from notion button * chore(flutter_desktop): adjust popover conflict rules for workspace * test: add integration tests * chore(flutter_desktop): make the popoovers as barriers * fix: regression from making the workspace item menu as barrier * chore: update frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/workspace/_sidebar_workspace_actions.dart Co-authored-by: Lucas --------- Co-authored-by: Lucas --- .../collaborative_workspace_test.dart | 72 +++++++++++- .../workspace/_sidebar_workspace_actions.dart | 41 +++++-- .../workspace/_sidebar_workspace_menu.dart | 110 +++++++++--------- .../sidebar/workspace/sidebar_workspace.dart | 1 + 4 files changed, 155 insertions(+), 69 deletions(-) diff --git a/frontend/appflowy_flutter/integration_test/desktop/cloud/workspace/collaborative_workspace_test.dart b/frontend/appflowy_flutter/integration_test/desktop/cloud/workspace/collaborative_workspace_test.dart index 2de6fb8fa7..a473e45fb7 100644 --- a/frontend/appflowy_flutter/integration_test/desktop/cloud/workspace/collaborative_workspace_test.dart +++ b/frontend/appflowy_flutter/integration_test/desktop/cloud/workspace/collaborative_workspace_test.dart @@ -5,6 +5,7 @@ import 'package:appflowy/shared/feature_flags.dart'; import 'package:appflowy/workspace/presentation/home/menu/sidebar/workspace/_sidebar_workspace_actions.dart'; import 'package:appflowy/workspace/presentation/home/menu/sidebar/workspace/_sidebar_workspace_menu.dart'; import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; @@ -102,8 +103,7 @@ void main() { expect(memberCount, findsNWidgets(2)); }); - testWidgets('only display one menu item in the workspace menu', - (tester) async { + testWidgets('workspace menu popover behavior test', (tester) async { // only run the test when the feature flag is on if (!FeatureFlag.collaborativeWorkspace.isOn) { return; @@ -128,6 +128,8 @@ void main() { final workspaceItem = find.byWidgetPredicate( (w) => w is WorkspaceMenuItem && w.workspace.name == name, ); + + // the workspace menu shouldn't conflict with logout await tester.hoverOnWidget( workspaceItem, onHover: () async { @@ -136,15 +138,73 @@ void main() { ); expect(moreButton, findsOneWidget); await tester.tapButton(moreButton); + expect(find.text(LocaleKeys.button_rename.tr()), findsOneWidget); + + final logoutButton = find.byType(WorkspaceMoreButton); + await tester.tapButton(logoutButton); + expect(find.text(LocaleKeys.button_logout.tr()), findsOneWidget); + expect(moreButton, findsNothing); + + await tester.tapButton(moreButton); + expect(find.text(LocaleKeys.button_logout.tr()), findsNothing); + expect(moreButton, findsOneWidget); + }, + ); + await tester.sendKeyEvent(LogicalKeyboardKey.escape); + await tester.pumpAndSettle(); + + // clicking on the more action button for the same workspace shouldn't do + // anything + await tester.openCollaborativeWorkspaceMenu(); + await tester.hoverOnWidget( + workspaceItem, + onHover: () async { + final moreButton = find.byWidgetPredicate( + (w) => w is WorkspaceMoreActionList && w.workspace.name == name, + ); + expect(moreButton, findsOneWidget); + await tester.tapButton(moreButton); + expect(find.text(LocaleKeys.button_rename.tr()), findsOneWidget); // click it again await tester.tapButton(moreButton); // nothing should happen - expect( - find.text(LocaleKeys.button_rename.tr()), - findsOneWidget, - ); + expect(find.text(LocaleKeys.button_rename.tr()), findsOneWidget); + }, + ); + await tester.sendKeyEvent(LogicalKeyboardKey.escape); + await tester.pumpAndSettle(); + + // clicking on the more button of another workspace should close the menu + // for this one + await tester.openCollaborativeWorkspaceMenu(); + final moreButton = find.byWidgetPredicate( + (w) => w is WorkspaceMoreActionList && w.workspace.name == name, + ); + await tester.hoverOnWidget( + workspaceItem, + onHover: () async { + expect(moreButton, findsOneWidget); + await tester.tapButton(moreButton); + expect(find.text(LocaleKeys.button_rename.tr()), findsOneWidget); + }, + ); + + final otherWorspaceItem = find.byWidgetPredicate( + (w) => w is WorkspaceMenuItem && w.workspace.name != name, + ); + final otherMoreButton = find.byWidgetPredicate( + (w) => w is WorkspaceMoreActionList && w.workspace.name != name, + ); + await tester.hoverOnWidget( + otherWorspaceItem, + onHover: () async { + expect(otherMoreButton, findsOneWidget); + await tester.tapButton(otherMoreButton); + expect(find.text(LocaleKeys.button_rename.tr()), findsOneWidget); + + expect(moreButton, findsNothing); }, ); }); diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/workspace/_sidebar_workspace_actions.dart b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/workspace/_sidebar_workspace_actions.dart index ee5bc3fab3..44f558fc17 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/workspace/_sidebar_workspace_actions.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/workspace/_sidebar_workspace_actions.dart @@ -18,15 +18,23 @@ enum WorkspaceMoreAction { divider, } -class WorkspaceMoreActionList extends StatelessWidget { +class WorkspaceMoreActionList extends StatefulWidget { const WorkspaceMoreActionList({ super.key, required this.workspace, - required this.isShowingMoreActions, + required this.popoverMutex, }); final UserWorkspacePB workspace; - final ValueNotifier isShowingMoreActions; + final PopoverMutex popoverMutex; + + @override + State createState() => + _WorkspaceMoreActionListState(); +} + +class _WorkspaceMoreActionListState extends State { + bool isPopoverOpen = false; @override Widget build(BuildContext context) { @@ -45,16 +53,22 @@ class WorkspaceMoreActionList extends StatelessWidget { return PopoverActionList<_WorkspaceMoreActionWrapper>( direction: PopoverDirection.bottomWithLeftAligned, actions: actions - .map((e) => _WorkspaceMoreActionWrapper(e, workspace)) + .map( + (action) => _WorkspaceMoreActionWrapper( + action, + widget.workspace, + () => PopoverContainer.of(context).closeAll(), + ), + ) .toList(), + mutex: widget.popoverMutex, constraints: const BoxConstraints(minWidth: 220), animationDuration: Durations.short3, slideDistance: 2, beginScaleFactor: 1.0, beginOpacity: 0.8, - onClosed: () { - isShowingMoreActions.value = false; - }, + onClosed: () => isPopoverOpen = false, + asBarrier: true, buildChild: (controller) { return SizedBox.square( dimension: 24.0, @@ -64,11 +78,10 @@ class WorkspaceMoreActionList extends StatelessWidget { FlowySvgs.workspace_three_dots_s, ), onTap: () { - if (!isShowingMoreActions.value) { + if (!isPopoverOpen) { controller.show(); + isPopoverOpen = true; } - - isShowingMoreActions.value = true; }, ), ); @@ -79,10 +92,15 @@ class WorkspaceMoreActionList extends StatelessWidget { } class _WorkspaceMoreActionWrapper extends CustomActionCell { - _WorkspaceMoreActionWrapper(this.inner, this.workspace); + _WorkspaceMoreActionWrapper( + this.inner, + this.workspace, + this.closeWorkspaceMenu, + ); final WorkspaceMoreAction inner; final UserWorkspacePB workspace; + final VoidCallback closeWorkspaceMenu; @override Widget buildWithContext( @@ -117,6 +135,7 @@ class _WorkspaceMoreActionWrapper extends CustomActionCell { margin: const EdgeInsets.all(6), onTap: () async { PopoverContainer.of(context).closeAll(); + closeWorkspaceMenu(); final workspaceBloc = context.read(); switch (inner) { diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/workspace/_sidebar_workspace_menu.dart b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/workspace/_sidebar_workspace_menu.dart index cfb86b8832..f3038c0bec 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/workspace/_sidebar_workspace_menu.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/workspace/_sidebar_workspace_menu.dart @@ -43,13 +43,7 @@ class WorkspacesMenu extends StatefulWidget { } class _WorkspacesMenuState extends State { - final ValueNotifier isShowingMoreActions = ValueNotifier(false); - - @override - void dispose() { - isShowingMoreActions.dispose(); - super.dispose(); - } + final popoverMutex = PopoverMutex(); @override Widget build(BuildContext context) { @@ -59,7 +53,7 @@ class _WorkspacesMenuState extends State { children: [ // user email Padding( - padding: const EdgeInsets.symmetric(horizontal: 4.0), + padding: const EdgeInsets.only(left: 10.0, top: 6.0, right: 10.0), child: Row( children: [ Expanded( @@ -71,18 +65,21 @@ class _WorkspacesMenuState extends State { ), ), const HSpace(4.0), - const _WorkspaceMoreButton(), + WorkspaceMoreButton( + popoverMutex: popoverMutex, + ), const HSpace(8.0), ], ), ), const Padding( - padding: EdgeInsets.symmetric(vertical: 8.0), + padding: EdgeInsets.symmetric(vertical: 8.0, horizontal: 6.0), child: Divider(height: 1.0), ), // workspace list Flexible( child: SingleChildScrollView( + padding: const EdgeInsets.symmetric(horizontal: 6.0), child: Column( mainAxisSize: MainAxisSize.min, children: [ @@ -93,7 +90,7 @@ class _WorkspacesMenuState extends State { userProfile: widget.userProfile, isSelected: workspace.workspaceId == widget.currentWorkspace.workspaceId, - isShowingMoreActions: isShowingMoreActions, + popoverMutex: popoverMutex, ), const VSpace(6.0), ], @@ -102,13 +99,19 @@ class _WorkspacesMenuState extends State { ), ), // add new workspace - const _CreateWorkspaceButton(), - const VSpace(6.0), + const Padding( + padding: EdgeInsets.symmetric(horizontal: 6.0), + child: _CreateWorkspaceButton(), + ), if (UniversalPlatform.isDesktop) ...[ - const _ImportNotionButton(), - const VSpace(6.0), + const Padding( + padding: EdgeInsets.only(left: 6.0, top: 6.0, right: 6.0), + child: _ImportNotionButton(), + ), ], + + const VSpace(6.0), ], ); } @@ -132,13 +135,13 @@ class WorkspaceMenuItem extends StatefulWidget { required this.workspace, required this.userProfile, required this.isSelected, - required this.isShowingMoreActions, + required this.popoverMutex, }); final UserProfilePB userProfile; final UserWorkspacePB workspace; final bool isSelected; - final ValueNotifier isShowingMoreActions; + final PopoverMutex popoverMutex; @override State createState() => _WorkspaceMenuItemState(); @@ -230,7 +233,7 @@ class _WorkspaceMenuItemState extends State { }, child: WorkspaceMoreActionList( workspace: widget.workspace, - isShowingMoreActions: widget.isShowingMoreActions, + popoverMutex: widget.popoverMutex, ), ), const HSpace(8.0), @@ -394,40 +397,35 @@ class _ImportNotionButton extends StatelessWidget { Widget build(BuildContext context) { return SizedBox( height: 40, - child: Stack( - alignment: Alignment.centerRight, - children: [ - FlowyButton( - key: importNotionButtonKey, - onTap: () { - _showImportNotinoDialog(context); + child: FlowyButton( + key: importNotionButtonKey, + onTap: () { + _showImportNotinoDialog(context); + }, + margin: const EdgeInsets.symmetric(horizontal: 4.0), + text: Row( + children: [ + _buildLeftIcon(context), + const HSpace(8.0), + FlowyText.regular( + LocaleKeys.workspace_importFromNotion.tr(), + ), + ], + ), + rightIcon: FlowyTooltip( + message: LocaleKeys.workspace_learnMore.tr(), + preferBelow: true, + child: FlowyIconButton( + icon: const FlowySvg( + FlowySvgs.information_s, + ), + onPressed: () { + afLaunchUrlString( + 'https://docs.appflowy.io/docs/guides/import-from-notion', + ); }, - margin: const EdgeInsets.symmetric(horizontal: 4.0), - text: Row( - children: [ - _buildLeftIcon(context), - const HSpace(8.0), - FlowyText.regular( - LocaleKeys.workspace_importFromNotion.tr(), - ), - ], - ), ), - FlowyTooltip( - message: LocaleKeys.workspace_learnMore.tr(), - preferBelow: true, - child: FlowyIconButton( - icon: const FlowySvg( - FlowySvgs.information_s, - ), - onPressed: () { - afLaunchUrlString( - 'https://docs.appflowy.io/docs/guides/import-from-notion', - ); - }, - ), - ), - ], + ), ), ); } @@ -478,14 +476,22 @@ class _ImportNotionButton extends StatelessWidget { } } -class _WorkspaceMoreButton extends StatelessWidget { - const _WorkspaceMoreButton(); +@visibleForTesting +class WorkspaceMoreButton extends StatelessWidget { + const WorkspaceMoreButton({ + super.key, + required this.popoverMutex, + }); + + final PopoverMutex popoverMutex; @override Widget build(BuildContext context) { return AppFlowyPopover( direction: PopoverDirection.bottomWithLeftAligned, offset: const Offset(0, 6), + mutex: popoverMutex, + asBarrier: true, popupBuilder: (_) => FlowyButton( margin: const EdgeInsets.symmetric(horizontal: 6.0, vertical: 7.0), leftIcon: const FlowySvg(FlowySvgs.workspace_logout_s), diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/workspace/sidebar_workspace.dart b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/workspace/sidebar_workspace.dart index c3480a94bc..aee527d1a8 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/workspace/sidebar_workspace.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/workspace/sidebar_workspace.dart @@ -207,6 +207,7 @@ class _SidebarSwitchWorkspaceButtonState direction: PopoverDirection.bottomWithCenterAligned, offset: const Offset(0, 5), constraints: const BoxConstraints(maxWidth: 300, maxHeight: 600), + margin: EdgeInsets.zero, animationDuration: Durations.short3, beginScaleFactor: 1.0, beginOpacity: 0.8, From dfe994b3419e57a878b1f1d8935e0af5c471c0ce Mon Sep 17 00:00:00 2001 From: Lucas Date: Mon, 30 Dec 2024 17:56:43 +0800 Subject: [PATCH 100/576] feat: auto-dismiss collapsed handle on Android if no interaction occurs (#7088) * feat: support auto-dismiss collapsed handle on Android * fix: hit test area of collasepd handle is too big * chore: upgrade appflowy_editor * fix: simple table issues on mobile * feat: highlight cell after insertion * test: text color and cell background color test * fix: sign_in_page_settings_test --- .../settings/sign_in_page_settings_test.dart | 1 + .../document/presentation/editor_page.dart | 4 + .../simple_table_map_operation.dart | 241 ++++++++++++++++++ .../simple_table_more_action_popup.dart | 54 ++++ .../document/presentation/editor_style.dart | 3 +- frontend/appflowy_flutter/pubspec.lock | 4 +- frontend/appflowy_flutter/pubspec.yaml | 2 +- .../simple_table_delete_operation_test.dart | 62 +++++ ...simple_table_duplicate_operation_test.dart | 64 +++++ .../simple_table_insert_operation_test.dart | 62 +++++ 10 files changed, 493 insertions(+), 4 deletions(-) diff --git a/frontend/appflowy_flutter/integration_test/desktop/settings/sign_in_page_settings_test.dart b/frontend/appflowy_flutter/integration_test/desktop/settings/sign_in_page_settings_test.dart index b7074be357..b0b751a52f 100644 --- a/frontend/appflowy_flutter/integration_test/desktop/settings/sign_in_page_settings_test.dart +++ b/frontend/appflowy_flutter/integration_test/desktop/settings/sign_in_page_settings_test.dart @@ -67,6 +67,7 @@ void main() { // open settings page to check the result await tester.tapButton(settingsButton); + await tester.pumpAndSettle(const Duration(milliseconds: 250)); // check the server type expect( diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_page.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_page.dart index 0cf39e8bcc..d83bd055ce 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_page.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_page.dart @@ -505,6 +505,10 @@ class _AppFlowyEditorPageState extends State Position(path: lastNode.path), ); } + + transaction.customSelectionType = SelectionType.inline; + transaction.reason = SelectionUpdateReason.uiEvent; + await editorState.apply(transaction); } diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_map_operation.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_map_operation.dart index cb42571704..875da5fffe 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_map_operation.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_map_operation.dart @@ -104,6 +104,18 @@ extension TableMapOperation on Node { comparator: (iKey, index) => iKey >= index, ); + final rowBoldAttributes = _remapSource( + this.rowBoldAttributes, + index, + comparator: (iKey, index) => iKey >= index, + ); + + final rowTextColors = _remapSource( + this.rowTextColors, + index, + comparator: (iKey, index) => iKey >= index, + ); + return attributes .mergeValues( SimpleTableBlockKeys.rowColors, @@ -112,6 +124,14 @@ extension TableMapOperation on Node { .mergeValues( SimpleTableBlockKeys.rowAligns, rowAligns, + ) + .mergeValues( + SimpleTableBlockKeys.rowBoldAttributes, + rowBoldAttributes, + ) + .mergeValues( + SimpleTableBlockKeys.rowTextColors, + rowTextColors, ); } catch (e) { Log.warn('Failed to map row insertion attributes: $e'); @@ -167,6 +187,18 @@ extension TableMapOperation on Node { comparator: (iKey, index) => iKey >= index, ); + final columnBoldAttributes = _remapSource( + this.columnBoldAttributes, + index, + comparator: (iKey, index) => iKey >= index, + ); + + final columnTextColors = _remapSource( + this.columnTextColors, + index, + comparator: (iKey, index) => iKey >= index, + ); + final bool distributeColumnWidthsEvenly = attributes[SimpleTableBlockKeys.distributeColumnWidthsEvenly] ?? false; @@ -189,6 +221,14 @@ extension TableMapOperation on Node { .mergeValues( SimpleTableBlockKeys.columnWidths, columnWidths, + ) + .mergeValues( + SimpleTableBlockKeys.columnBoldAttributes, + columnBoldAttributes, + ) + .mergeValues( + SimpleTableBlockKeys.columnTextColors, + columnTextColors, ); } catch (e) { Log.warn('Failed to map row insertion attributes: $e'); @@ -238,6 +278,18 @@ extension TableMapOperation on Node { index, ); + final (rowBoldAttributes, duplicatedRowBoldAttribute) = + _findDuplicatedEntryAndRemap( + this.rowBoldAttributes, + index, + ); + + final (rowTextColors, duplicatedRowTextColor) = + _findDuplicatedEntryAndRemap( + this.rowTextColors, + index, + ); + return attributes .mergeValues( SimpleTableBlockKeys.rowColors, @@ -248,6 +300,16 @@ extension TableMapOperation on Node { SimpleTableBlockKeys.rowAligns, rowAligns, duplicatedEntry: duplicatedRowAlign, + ) + .mergeValues( + SimpleTableBlockKeys.rowBoldAttributes, + rowBoldAttributes, + duplicatedEntry: duplicatedRowBoldAttribute, + ) + .mergeValues( + SimpleTableBlockKeys.rowTextColors, + rowTextColors, + duplicatedEntry: duplicatedRowTextColor, ); } catch (e) { Log.warn('Failed to map row insertion attributes: $e'); @@ -304,6 +366,18 @@ extension TableMapOperation on Node { index, ); + final (columnBoldAttributes, duplicatedColumnBoldAttribute) = + _findDuplicatedEntryAndRemap( + this.columnBoldAttributes, + index, + ); + + final (columnTextColors, duplicatedColumnTextColor) = + _findDuplicatedEntryAndRemap( + this.columnTextColors, + index, + ); + return attributes .mergeValues( SimpleTableBlockKeys.columnColors, @@ -319,6 +393,16 @@ extension TableMapOperation on Node { SimpleTableBlockKeys.columnWidths, columnWidths, duplicatedEntry: duplicatedColumnWidth, + ) + .mergeValues( + SimpleTableBlockKeys.columnBoldAttributes, + columnBoldAttributes, + duplicatedEntry: duplicatedColumnBoldAttribute, + ) + .mergeValues( + SimpleTableBlockKeys.columnTextColors, + columnTextColors, + duplicatedEntry: duplicatedColumnTextColor, ); } catch (e) { Log.warn('Failed to map column duplication attributes: $e'); @@ -364,6 +448,7 @@ extension TableMapOperation on Node { comparator: (iKey, index) => iKey > index, filterIndex: index, ); + final columnAligns = _remapSource( this.columnAligns, index, @@ -371,6 +456,7 @@ extension TableMapOperation on Node { comparator: (iKey, index) => iKey > index, filterIndex: index, ); + final columnWidths = _remapSource( this.columnWidths, index, @@ -379,6 +465,22 @@ extension TableMapOperation on Node { filterIndex: index, ); + final columnBoldAttributes = _remapSource( + this.columnBoldAttributes, + index, + increment: false, + comparator: (iKey, index) => iKey > index, + filterIndex: index, + ); + + final columnTextColors = _remapSource( + this.columnTextColors, + index, + increment: false, + comparator: (iKey, index) => iKey > index, + filterIndex: index, + ); + return attributes .mergeValues( SimpleTableBlockKeys.columnColors, @@ -391,6 +493,14 @@ extension TableMapOperation on Node { .mergeValues( SimpleTableBlockKeys.columnWidths, columnWidths, + ) + .mergeValues( + SimpleTableBlockKeys.columnBoldAttributes, + columnBoldAttributes, + ) + .mergeValues( + SimpleTableBlockKeys.columnTextColors, + columnTextColors, ); } catch (e) { Log.warn('Failed to map column deletion attributes: $e'); @@ -443,6 +553,22 @@ extension TableMapOperation on Node { filterIndex: index, ); + final rowBoldAttributes = _remapSource( + this.rowBoldAttributes, + index, + increment: false, + comparator: (iKey, index) => iKey > index, + filterIndex: index, + ); + + final rowTextColors = _remapSource( + this.rowTextColors, + index, + increment: false, + comparator: (iKey, index) => iKey > index, + filterIndex: index, + ); + return attributes .mergeValues( SimpleTableBlockKeys.rowColors, @@ -451,6 +577,14 @@ extension TableMapOperation on Node { .mergeValues( SimpleTableBlockKeys.rowAligns, rowAligns, + ) + .mergeValues( + SimpleTableBlockKeys.rowBoldAttributes, + rowBoldAttributes, + ) + .mergeValues( + SimpleTableBlockKeys.rowTextColors, + rowTextColors, ); } catch (e) { Log.warn('Failed to map row deletion attributes: $e'); @@ -531,6 +665,10 @@ extension TableMapOperation on Node { final duplicatedColumnColor = this.columnColors[fromIndex.toString()]; final duplicatedColumnAlign = this.columnAligns[fromIndex.toString()]; final duplicatedColumnWidth = this.columnWidths[fromIndex.toString()]; + final duplicatedColumnBoldAttribute = + this.columnBoldAttributes[fromIndex.toString()]; + final duplicatedColumnTextColor = + this.columnTextColors[fromIndex.toString()]; /// Case 1: fromIndex > toIndex /// Before: @@ -619,6 +757,34 @@ extension TableMapOperation on Node { filterIndex: fromIndex, ); + final columnBoldAttributes = _remapSource( + this.columnBoldAttributes, + fromIndex, + increment: fromIndex > toIndex, + comparator: (iKey, index) { + if (fromIndex > toIndex) { + return iKey < fromIndex && iKey >= toIndex; + } else { + return iKey > fromIndex && iKey <= toIndex; + } + }, + filterIndex: fromIndex, + ); + + final columnTextColors = _remapSource( + this.columnTextColors, + fromIndex, + increment: fromIndex > toIndex, + comparator: (iKey, index) { + if (fromIndex > toIndex) { + return iKey < fromIndex && iKey >= toIndex; + } else { + return iKey > fromIndex && iKey <= toIndex; + } + }, + filterIndex: fromIndex, + ); + return attributes .mergeValues( SimpleTableBlockKeys.columnColors, @@ -652,6 +818,28 @@ extension TableMapOperation on Node { ) : null, removeNullValue: true, + ) + .mergeValues( + SimpleTableBlockKeys.columnBoldAttributes, + columnBoldAttributes, + duplicatedEntry: duplicatedColumnBoldAttribute != null + ? MapEntry( + toIndex.toString(), + duplicatedColumnBoldAttribute, + ) + : null, + removeNullValue: true, + ) + .mergeValues( + SimpleTableBlockKeys.columnTextColors, + columnTextColors, + duplicatedEntry: duplicatedColumnTextColor != null + ? MapEntry( + toIndex.toString(), + duplicatedColumnTextColor, + ) + : null, + removeNullValue: true, ); } catch (e) { Log.warn('Failed to map column deletion attributes: $e'); @@ -667,6 +855,9 @@ extension TableMapOperation on Node { try { final duplicatedRowColor = this.rowColors[fromIndex.toString()]; final duplicatedRowAlign = this.rowAligns[fromIndex.toString()]; + final duplicatedRowBoldAttribute = + this.rowBoldAttributes[fromIndex.toString()]; + final duplicatedRowTextColor = this.rowTextColors[fromIndex.toString()]; /// Example: /// Case 1: fromIndex > toIndex @@ -742,6 +933,34 @@ extension TableMapOperation on Node { filterIndex: fromIndex, ); + final rowBoldAttributes = _remapSource( + this.rowBoldAttributes, + fromIndex, + increment: fromIndex > toIndex, + comparator: (iKey, index) { + if (fromIndex > toIndex) { + return iKey < fromIndex && iKey >= toIndex; + } else { + return iKey > fromIndex && iKey <= toIndex; + } + }, + filterIndex: fromIndex, + ); + + final rowTextColors = _remapSource( + this.rowTextColors, + fromIndex, + increment: fromIndex > toIndex, + comparator: (iKey, index) { + if (fromIndex > toIndex) { + return iKey < fromIndex && iKey >= toIndex; + } else { + return iKey > fromIndex && iKey <= toIndex; + } + }, + filterIndex: fromIndex, + ); + return attributes .mergeValues( SimpleTableBlockKeys.rowColors, @@ -764,6 +983,28 @@ extension TableMapOperation on Node { ) : null, removeNullValue: true, + ) + .mergeValues( + SimpleTableBlockKeys.rowBoldAttributes, + rowBoldAttributes, + duplicatedEntry: duplicatedRowBoldAttribute != null + ? MapEntry( + toIndex.toString(), + duplicatedRowBoldAttribute, + ) + : null, + removeNullValue: true, + ) + .mergeValues( + SimpleTableBlockKeys.rowTextColors, + rowTextColors, + duplicatedEntry: duplicatedRowTextColor != null + ? MapEntry( + toIndex.toString(), + duplicatedRowTextColor, + ) + : null, + removeNullValue: true, ); } catch (e) { Log.warn('Failed to map row reordering attributes: $e'); diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_more_action_popup.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_more_action_popup.dart index d2e7340790..b7b5880b38 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_more_action_popup.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/simple_table_more_action_popup.dart @@ -455,6 +455,21 @@ class _SimpleTableMoreActionItemState extends State { final columnIndex = node.columnIndex; final editorState = context.read(); editorState.insertColumnInTable(table, columnIndex); + + final cell = table.getTableCellNode( + rowIndex: 0, + columnIndex: columnIndex, + ); + if (cell == null) { + return; + } + + // update selection + editorState.selection = Selection.collapsed( + Position( + path: cell.path.child(0), + ), + ); } void _insertColumnRight() { @@ -466,6 +481,21 @@ class _SimpleTableMoreActionItemState extends State { final columnIndex = node.columnIndex; final editorState = context.read(); editorState.insertColumnInTable(table, columnIndex + 1); + + final cell = table.getTableCellNode( + rowIndex: 0, + columnIndex: columnIndex + 1, + ); + if (cell == null) { + return; + } + + // update selection + editorState.selection = Selection.collapsed( + Position( + path: cell.path.child(0), + ), + ); } void _insertRowAbove() { @@ -477,6 +507,18 @@ class _SimpleTableMoreActionItemState extends State { final rowIndex = node.rowIndex; final editorState = context.read(); editorState.insertRowInTable(table, rowIndex); + + final cell = table.getTableCellNode(rowIndex: rowIndex, columnIndex: 0); + if (cell == null) { + return; + } + + // update selection + editorState.selection = Selection.collapsed( + Position( + path: cell.path.child(0), + ), + ); } void _insertRowBelow() { @@ -488,6 +530,18 @@ class _SimpleTableMoreActionItemState extends State { final rowIndex = node.rowIndex; final editorState = context.read(); editorState.insertRowInTable(table, rowIndex + 1); + + final cell = table.getTableCellNode(rowIndex: rowIndex + 1, columnIndex: 0); + if (cell == null) { + return; + } + + // update selection + editorState.selection = Selection.collapsed( + Position( + path: cell.path.child(0), + ), + ); } void _deleteRow() { diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_style.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_style.dart index de4d431a08..31377a93b7 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_style.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_style.dart @@ -166,9 +166,10 @@ class EditorStyleCustomizer { applyHeightToLastDescent: true, ), textSpanDecorator: customizeAttributeDecorator, - mobileDragHandleBallSize: const Size.square(12.0), magnifierSize: const Size(144, 96), textScaleFactor: textScaleFactor, + mobileDragHandleLeftExtend: 12.0, + mobileDragHandleWidthExtend: 24.0, ); } diff --git a/frontend/appflowy_flutter/pubspec.lock b/frontend/appflowy_flutter/pubspec.lock index b108a0c23a..8936824930 100644 --- a/frontend/appflowy_flutter/pubspec.lock +++ b/frontend/appflowy_flutter/pubspec.lock @@ -61,8 +61,8 @@ packages: dependency: "direct main" description: path: "." - ref: c68e5f6 - resolved-ref: c68e5f6c585205083e27e875b822656425b2853f + ref: cfb8b1b + resolved-ref: cfb8b1b6eb06f73a4fb297b6fd1d54b0ccec2922 url: "https://github.com/AppFlowy-IO/appflowy-editor.git" source: git version: "4.0.0" diff --git a/frontend/appflowy_flutter/pubspec.yaml b/frontend/appflowy_flutter/pubspec.yaml index 9433511050..f40d61b4b0 100644 --- a/frontend/appflowy_flutter/pubspec.yaml +++ b/frontend/appflowy_flutter/pubspec.yaml @@ -174,7 +174,7 @@ dependency_overrides: appflowy_editor: git: url: https://github.com/AppFlowy-IO/appflowy-editor.git - ref: "c68e5f6" + ref: "cfb8b1b" appflowy_editor_plugins: git: diff --git a/frontend/appflowy_flutter/test/unit_test/simple_table/simple_table_delete_operation_test.dart b/frontend/appflowy_flutter/test/unit_test/simple_table/simple_table_delete_operation_test.dart index c9c1be8379..cb28f955da 100644 --- a/frontend/appflowy_flutter/test/unit_test/simple_table/simple_table_delete_operation_test.dart +++ b/frontend/appflowy_flutter/test/unit_test/simple_table/simple_table_delete_operation_test.dart @@ -170,5 +170,67 @@ void main() { '0': TableAlign.center.key, }); }); + + test('delete a column with text color & bold style (1)', () async { + final (editorState, tableNode) = createEditorStateAndTable( + rowCount: 3, + columnCount: 4, + ); + // delete the column 1 + final tableCellNode = + tableNode.getTableCellNode(rowIndex: 0, columnIndex: 1); + await editorState.updateColumnTextColor( + tableCellNode: tableCellNode!, + color: '0xFF0000FF', + ); + await editorState.toggleColumnBoldAttribute( + tableCellNode: tableCellNode, + isBold: true, + ); + expect(tableNode.columnTextColors, { + '1': '0xFF0000FF', + }); + expect(tableNode.columnBoldAttributes, { + '1': true, + }); + await editorState.deleteColumnInTable(tableNode, 0); + expect(tableNode.columnTextColors, { + '0': '0xFF0000FF', + }); + expect(tableNode.columnBoldAttributes, { + '0': true, + }); + expect(tableNode.rowLength, 3); + expect(tableNode.columnLength, 3); + }); + + test('delete a column with text color & bold style (2)', () async { + final (editorState, tableNode) = createEditorStateAndTable( + rowCount: 3, + columnCount: 4, + ); + // delete the column 1 + final tableCellNode = + tableNode.getTableCellNode(rowIndex: 0, columnIndex: 1); + await editorState.updateColumnTextColor( + tableCellNode: tableCellNode!, + color: '0xFF0000FF', + ); + await editorState.toggleColumnBoldAttribute( + tableCellNode: tableCellNode, + isBold: true, + ); + expect(tableNode.columnTextColors, { + '1': '0xFF0000FF', + }); + expect(tableNode.columnBoldAttributes, { + '1': true, + }); + await editorState.deleteColumnInTable(tableNode, 1); + expect(tableNode.columnTextColors, {}); + expect(tableNode.columnBoldAttributes, {}); + expect(tableNode.rowLength, 3); + expect(tableNode.columnLength, 3); + }); }); } diff --git a/frontend/appflowy_flutter/test/unit_test/simple_table/simple_table_duplicate_operation_test.dart b/frontend/appflowy_flutter/test/unit_test/simple_table/simple_table_duplicate_operation_test.dart index 123310538a..85a1c252c7 100644 --- a/frontend/appflowy_flutter/test/unit_test/simple_table/simple_table_duplicate_operation_test.dart +++ b/frontend/appflowy_flutter/test/unit_test/simple_table/simple_table_duplicate_operation_test.dart @@ -161,5 +161,69 @@ void main() { '1': TableAlign.center.key, }); }); + + test('duplicate a column with text color & bold style (1)', () async { + final (editorState, tableNode) = createEditorStateAndTable( + rowCount: 3, + columnCount: 4, + ); + // duplicate the column 1 + final tableCellNode = + tableNode.getTableCellNode(rowIndex: 0, columnIndex: 1); + await editorState.updateColumnTextColor( + tableCellNode: tableCellNode!, + color: '0xFF0000FF', + ); + await editorState.toggleColumnBoldAttribute( + tableCellNode: tableCellNode, + isBold: true, + ); + expect(tableNode.columnTextColors, { + '1': '0xFF0000FF', + }); + expect(tableNode.columnBoldAttributes, { + '1': true, + }); + await editorState.duplicateColumnInTable(tableNode, 1); + expect(tableNode.columnTextColors, { + '1': '0xFF0000FF', + '2': '0xFF0000FF', + }); + expect(tableNode.columnBoldAttributes, { + '1': true, + '2': true, + }); + }); + + test('duplicate a column with text color & bold style (2)', () async { + final (editorState, tableNode) = createEditorStateAndTable( + rowCount: 3, + columnCount: 4, + ); + // duplicate the column 1 + final tableCellNode = + tableNode.getTableCellNode(rowIndex: 0, columnIndex: 1); + await editorState.updateColumnTextColor( + tableCellNode: tableCellNode!, + color: '0xFF0000FF', + ); + await editorState.toggleColumnBoldAttribute( + tableCellNode: tableCellNode, + isBold: true, + ); + expect(tableNode.columnTextColors, { + '1': '0xFF0000FF', + }); + expect(tableNode.columnBoldAttributes, { + '1': true, + }); + await editorState.duplicateColumnInTable(tableNode, 0); + expect(tableNode.columnTextColors, { + '2': '0xFF0000FF', + }); + expect(tableNode.columnBoldAttributes, { + '2': true, + }); + }); }); } diff --git a/frontend/appflowy_flutter/test/unit_test/simple_table/simple_table_insert_operation_test.dart b/frontend/appflowy_flutter/test/unit_test/simple_table/simple_table_insert_operation_test.dart index c84615ac42..86c4236a03 100644 --- a/frontend/appflowy_flutter/test/unit_test/simple_table/simple_table_insert_operation_test.dart +++ b/frontend/appflowy_flutter/test/unit_test/simple_table/simple_table_insert_operation_test.dart @@ -190,5 +190,67 @@ void main() { '0': TableAlign.center.key, }); }); + + test('insert a column with text color & bold style (1)', () async { + final (editorState, tableNode) = createEditorStateAndTable( + rowCount: 2, + columnCount: 3, + ); + // insert the column at the first position + final tableCellNode = + tableNode.getTableCellNode(rowIndex: 0, columnIndex: 0); + await editorState.updateColumnTextColor( + tableCellNode: tableCellNode!, + color: '0xFF0000FF', + ); + await editorState.toggleColumnBoldAttribute( + tableCellNode: tableCellNode, + isBold: true, + ); + expect(tableNode.columnTextColors, { + '0': '0xFF0000FF', + }); + expect(tableNode.columnBoldAttributes, { + '0': true, + }); + await editorState.insertColumnInTable(tableNode, 0); + expect(tableNode.columnTextColors, { + '1': '0xFF0000FF', + }); + expect(tableNode.columnBoldAttributes, { + '1': true, + }); + }); + + test('insert a column with text color & bold style (2)', () async { + final (editorState, tableNode) = createEditorStateAndTable( + rowCount: 2, + columnCount: 3, + ); + // insert the column at the first position + final tableCellNode = + tableNode.getTableCellNode(rowIndex: 0, columnIndex: 0); + await editorState.updateColumnTextColor( + tableCellNode: tableCellNode!, + color: '0xFF0000FF', + ); + await editorState.toggleColumnBoldAttribute( + tableCellNode: tableCellNode, + isBold: true, + ); + expect(tableNode.columnTextColors, { + '0': '0xFF0000FF', + }); + expect(tableNode.columnBoldAttributes, { + '0': true, + }); + await editorState.insertColumnInTable(tableNode, 1); + expect(tableNode.columnTextColors, { + '0': '0xFF0000FF', + }); + expect(tableNode.columnBoldAttributes, { + '0': true, + }); + }); }); } From 3190eebf6e4310598d8fd3547e70a79530662b59 Mon Sep 17 00:00:00 2001 From: "Kilu.He" <108015703+qinluhe@users.noreply.github.com> Date: Mon, 30 Dec 2024 18:10:36 +0800 Subject: [PATCH 101/576] fix: modified universal link (#7094) --- .../.well-known/apple-app-site-association | 26 +------ .../public/.well-known/assetlinks.json | 3 +- .../__tests__/shortcuts/Markdown.cy.tsx | 1 + .../src/components/login/Login.tsx | 14 +++- .../src/components/main/App.tsx | 29 +++---- .../src/components/quick-note/AddNote.tsx | 7 +- .../src/components/quick-note/QuickNote.tsx | 76 +++++++++++++------ .../appflowy_web_app/src/utils/open_schema.ts | 22 ++++-- frontend/resources/translations/en.json | 12 +-- 9 files changed, 104 insertions(+), 86 deletions(-) diff --git a/frontend/appflowy_web_app/public/.well-known/apple-app-site-association b/frontend/appflowy_web_app/public/.well-known/apple-app-site-association index ae5a32410b..7a7f3e89ea 100644 --- a/frontend/appflowy_web_app/public/.well-known/apple-app-site-association +++ b/frontend/appflowy_web_app/public/.well-known/apple-app-site-association @@ -1,25 +1 @@ -{ - "applinks": { - "apps": [], - "details": [ - { - "appIDs": [ - "VHB67HRSZG.com.appflowy.appflowy.flutter" - ], - "paths": [ - "*" - ], - "components": [ - { - "/": "/*" - } - ] - } - ] - }, - "webcredentials": { - "apps": [ - "VHB67HRSZG.com.appflowy.appflowy.flutter" - ] - } -} \ No newline at end of file +{"applinks":{"apps":[],"details":[{"appIDs":["VHB67HRSZG.com.appflowy.appflowy.flutter"],"paths":["/download","/download/*"],"components":[{"/":"/download","comment":"Matches any URL whose path starts with /download"},{"/":"/download/*","comment":"Matches any URL whose path starts with /download/"}]}]},"webcredentials":{"apps":["VHB67HRSZG.com.appflowy.appflowy.flutter"]}} \ No newline at end of file diff --git a/frontend/appflowy_web_app/public/.well-known/assetlinks.json b/frontend/appflowy_web_app/public/.well-known/assetlinks.json index 9161081308..c1dd118199 100644 --- a/frontend/appflowy_web_app/public/.well-known/assetlinks.json +++ b/frontend/appflowy_web_app/public/.well-known/assetlinks.json @@ -8,7 +8,8 @@ "package_name": "io.appflowy.appflowy", "sha256_cert_fingerprints": [ "19:13:85:33:DB:B3:A2:FD:65:2F:61:D7:F2:35:95:79:FE:6E:CC:B5:AC:94:AA:02:9E:BE:E7:0E:02:6B:45:FF" - ] + ], + "path_prefix": "/download" } } ] \ No newline at end of file diff --git a/frontend/appflowy_web_app/src/components/editor/__tests__/shortcuts/Markdown.cy.tsx b/frontend/appflowy_web_app/src/components/editor/__tests__/shortcuts/Markdown.cy.tsx index f5dc98a856..9a644d7047 100644 --- a/frontend/appflowy_web_app/src/components/editor/__tests__/shortcuts/Markdown.cy.tsx +++ b/frontend/appflowy_web_app/src/components/editor/__tests__/shortcuts/Markdown.cy.tsx @@ -27,6 +27,7 @@ describe('Markdown editing', () => { // Test `Bold` cy.get('@editor').type('**bold'); cy.get('@editor').realPress(['*', '*']); + cy.wait(50); expectedJson = [{ type: 'paragraph', data: {}, diff --git a/frontend/appflowy_web_app/src/components/login/Login.tsx b/frontend/appflowy_web_app/src/components/login/Login.tsx index ff565756c8..521af59bb6 100644 --- a/frontend/appflowy_web_app/src/components/login/Login.tsx +++ b/frontend/appflowy_web_app/src/components/login/Login.tsx @@ -5,7 +5,7 @@ import React from 'react'; import { ReactComponent as Logo } from '@/assets/logo.svg'; import { useTranslation } from 'react-i18next'; -export function Login({ redirectTo }: { redirectTo: string }) { +export function Login ({ redirectTo }: { redirectTo: string }) { const { t } = useTranslation(); return ( @@ -27,11 +27,19 @@ export function Login({ redirectTo }: { redirectTo: string }) { } > {t('web.signInAgreement')} - + {t('web.termOfUse')} {' '} {t('web.and')}{' '} - + {t('web.privacyPolicy')} . diff --git a/frontend/appflowy_web_app/src/components/main/App.tsx b/frontend/appflowy_web_app/src/components/main/App.tsx index 941db2d226..77764ba364 100644 --- a/frontend/appflowy_web_app/src/components/main/App.tsx +++ b/frontend/appflowy_web_app/src/components/main/App.tsx @@ -20,35 +20,35 @@ const AppMain = withAppWrapper(() => { } + element={} /> } + element={} /> } + element={} /> } + element={} /> } + element={} /> } + element={} /> } + element={} /> } + element={} /> { path="/app/*" element={ - + } /> } + element={} /> ); }); -function App() { - const path = window.location.pathname; - - if (path.startsWith('/.well-known')) { - return null; - } +function App () { return ( - + ); } diff --git a/frontend/appflowy_web_app/src/components/quick-note/AddNote.tsx b/frontend/appflowy_web_app/src/components/quick-note/AddNote.tsx index 7f8966014f..1ba3633fe4 100644 --- a/frontend/appflowy_web_app/src/components/quick-note/AddNote.tsx +++ b/frontend/appflowy_web_app/src/components/quick-note/AddNote.tsx @@ -5,7 +5,7 @@ import { QuickNote } from '@/application/types'; import { Button, CircularProgress } from '@mui/material'; import { useTranslation } from 'react-i18next'; -function AddNote({ +function AddNote ({ onEnterNote, onAdd, }: { @@ -27,9 +27,10 @@ function AddNote({ diff --git a/frontend/appflowy_web_app/src/components/quick-note/QuickNote.tsx b/frontend/appflowy_web_app/src/components/quick-note/QuickNote.tsx index bacb741525..876e56a48c 100644 --- a/frontend/appflowy_web_app/src/components/quick-note/QuickNote.tsx +++ b/frontend/appflowy_web_app/src/components/quick-note/QuickNote.tsx @@ -1,3 +1,4 @@ +import CircularProgress from '@mui/material/CircularProgress'; import React, { useCallback, useEffect, useMemo, useRef } from 'react'; import { IconButton, Tooltip, Zoom, Snackbar, Portal } from '@mui/material'; import { ReactComponent as EditIcon } from '@/assets/edit.svg'; @@ -16,7 +17,7 @@ import { getPopoverPosition, setPopoverPosition } from '@/components/quick-note/ import Note from '@/components/quick-note/Note'; const PAPER_SIZE = [480, 396]; -const Transition = React.forwardRef(function Transition( +const Transition = React.forwardRef(function Transition ( props: TransitionProps & { children: React.ReactElement; }, @@ -33,7 +34,7 @@ enum QuickNoteRoute { LIST = 'list', } -export function QuickNote() { +export function QuickNote () { const { t } = useTranslation(); const modifier = useMemo(() => createHotKeyLabel(HOT_KEY_NAME.QUICK_NOTE), []); const [open, setOpen] = React.useState(false); @@ -197,6 +198,7 @@ export function QuickNote() { useEffect(() => { resetPosition(); }, [resetPosition]); + const [loading, setLoading] = React.useState(false); const buttonRef = useRef(null); const handleOpen = useCallback(async (forceCreate?: boolean) => { @@ -217,7 +219,7 @@ export function QuickNote() { } : prev); } - await initNoteList(); + void initNoteList(); if (route === QuickNoteRoute.LIST || forceCreate) { await handleAdd(); @@ -232,7 +234,9 @@ export function QuickNote() { e.stopPropagation(); e.preventDefault(); + if (loading) return; void (async () => { + setLoading(true); try { await handleOpen(true); // eslint-disable-next-line @@ -240,6 +244,8 @@ export function QuickNote() { console.error(e); handleOpenToast(e.message); } + + setLoading(false); })(); } else if (createHotkey(HOT_KEY_NAME.ESCAPE)(e)) { handleClose(); @@ -251,7 +257,7 @@ export function QuickNote() { return () => { document.removeEventListener('keydown', handleKeyDown); }; - }, [handleOpen, handleOpenToast]); + }, [handleOpen, handleOpenToast, loading]); const handleMouseDown = (event: React.MouseEvent) => { if (!position) return; @@ -393,7 +399,8 @@ export function QuickNote() { expand={expand} onToggleExpand={handleToggleExpand} onClose={handleClose} - onBack={handleBackList}/> + onBack={handleBackList} + /> ); } @@ -423,26 +430,40 @@ export function QuickNote() { return ( <> - -
{t('quickNote.label')}
-
{modifier}
- - }> + +
{t('quickNote.label')}
+
{modifier}
+ + } + > { + onClick={async (e) => { e.currentTarget.blur(); if (open) { handleClose(); return; } - void handleOpen(); + try { + setLoading(true); + await handleOpen(); + // eslint-disable-next-line + } catch (e: any) { + console.error(e); + handleOpenToast(e.message); + } + + setLoading(false); + }} + disabled={loading} > - + {loading ? : + }
- { - setToastMessage(''); - setOpenToast(false); - }, - open: openToast, - }}> + { + setToastMessage(''); + setOpenToast(false); + }, + open: openToast, + }} + >
+ className={'bg-note-header py-2 px-5 flex items-center justify-between gap-5 h-[44px] w-full'} + >
{renderHeader()}
{ handleUpdateNodeData(currentNote.id, data); - }}/> + }} + /> : { }; }; -export function openOnly(schema?: string) { +export function openOnly (schema?: string) { return openAppOrDownload({ appScheme: schema || openAppFlowySchema, }); } -export function openOrDownload(schema?: string) { +export function openOrDownload (schema?: string) { const os = getOS(); - - const downloadUrl = os === 'ios' ? iosDownloadLink : os === 'android' ? androidDownloadLink : desktopDownloadLink; + + if (os === 'ios' || os === 'android') { + const universalLink = 'https://appflowy.io/download'; + const intentUrl = `intent://appflowy.io/download#Intent;` + + 'scheme=https;' + + 'package=io.appflowy.app;' + + `S.browser_fallback_url=${encodeURIComponent(androidDownloadLink)};` + + 'end'; + + window.location.href = os === 'ios' ? universalLink : intentUrl; + return; + } return openAppOrDownload({ appScheme: schema || openAppFlowySchema, - downloadUrl, + downloadUrl: desktopDownloadLink, }); } \ No newline at end of file diff --git a/frontend/resources/translations/en.json b/frontend/resources/translations/en.json index ef8078a2a8..bec922f153 100644 --- a/frontend/resources/translations/en.json +++ b/frontend/resources/translations/en.json @@ -2962,16 +2962,16 @@ "emails": "Email" }, "quickNote": { - "label": "Quick note", - "quickNotes": "Quick notes", - "search": "Search quick notes", + "label": "Quick Note", + "quickNotes": "Quick Notes", + "search": "Search Quick Notes", "collapseFullView": "Collapse full view", "expandFullView": "Expand full view", - "createFailed": "Failed to create quick note", - "quickNotesEmpty": "No quick notes", + "createFailed": "Failed to create Quick Note", + "quickNotesEmpty": "No Quick Notes", "emptyNote": "Empty note", "deleteNotePrompt": "The selected note will be deleted permanently. Are you sure you want to delete it?", - "addNote": "New note", + "addNote": "New Note", "noAdditionalText": "No additional text" }, "subscribe": { From 6158954d9769a42de71d4f1937ed903156404c7e Mon Sep 17 00:00:00 2001 From: Richard Shiue <71320345+richardshiue@users.noreply.github.com> Date: Mon, 30 Dec 2024 18:10:55 +0800 Subject: [PATCH 102/576] test: fix change server integeration test (#7096) --- .../settings/sign_in_page_settings_test.dart | 6 +-- .../presentation/widgets/dialogs.dart | 8 ++-- frontend/appflowy_flutter/macos/Podfile.lock | 42 +++++++++---------- 3 files changed, 29 insertions(+), 27 deletions(-) diff --git a/frontend/appflowy_flutter/integration_test/desktop/settings/sign_in_page_settings_test.dart b/frontend/appflowy_flutter/integration_test/desktop/settings/sign_in_page_settings_test.dart index b0b751a52f..1c952a129d 100644 --- a/frontend/appflowy_flutter/integration_test/desktop/settings/sign_in_page_settings_test.dart +++ b/frontend/appflowy_flutter/integration_test/desktop/settings/sign_in_page_settings_test.dart @@ -2,10 +2,10 @@ import 'package:appflowy/env/cloud_env.dart'; import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/user/presentation/screens/sign_in_screen/desktop_sign_in_screen.dart'; import 'package:appflowy/workspace/presentation/settings/settings_dialog.dart'; +import 'package:appflowy/workspace/presentation/widgets/dialogs.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; -import 'package:toastification/toastification.dart'; import '../../shared/util.dart'; @@ -62,7 +62,7 @@ void main() { ); // wait the app to restart, and the tooltip to disappear - await tester.pumpUntilNotFound(find.byType(BuiltInToastBuilder)); + await tester.pumpUntilNotFound(find.byType(DesktopToast)); await tester.pumpAndSettle(const Duration(milliseconds: 250)); // open settings page to check the result @@ -90,7 +90,7 @@ void main() { ); // wait the app to restart, and the tooltip to disappear - await tester.pumpUntilNotFound(find.byType(BuiltInToastBuilder)); + await tester.pumpUntilNotFound(find.byType(DesktopToast)); await tester.pumpAndSettle(const Duration(milliseconds: 250)); // check the server type diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/widgets/dialogs.dart b/frontend/appflowy_flutter/lib/workspace/presentation/widgets/dialogs.dart index 42b687937e..1a36ea2a66 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/widgets/dialogs.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/widgets/dialogs.dart @@ -388,7 +388,7 @@ void showToastNotification( bottomPadding: bottomPadding, description: description, ) - : _DesktopToast( + : DesktopToast( message: message, richMessage: richMessage, type: type, @@ -487,8 +487,10 @@ class _MobileToast extends StatelessWidget { } } -class _DesktopToast extends StatelessWidget { - const _DesktopToast({ +@visibleForTesting +class DesktopToast extends StatelessWidget { + const DesktopToast({ + super.key, this.message, this.richMessage, required this.type, diff --git a/frontend/appflowy_flutter/macos/Podfile.lock b/frontend/appflowy_flutter/macos/Podfile.lock index 2d9b24cdaa..d5ac7975b9 100644 --- a/frontend/appflowy_flutter/macos/Podfile.lock +++ b/frontend/appflowy_flutter/macos/Podfile.lock @@ -130,31 +130,31 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral/.symlinks/plugins/window_manager/macos SPEC CHECKSUMS: - app_links: 9028728e32c83a0831d9db8cf91c526d16cc5468 - appflowy_backend: 464aeb3e5c6966a41641a2111e5ead72ce2695f7 - bitsdojo_window_macos: 7959fb0ca65a3ccda30095c181ecb856fae48ea9 - connectivity_plus: e74b9f74717d2d99d45751750e266e55912baeb5 - desktop_drop: e0b672a7d84c0a6cbc378595e82cdb15f2970a43 - device_info_plus: a56e6e74dbbd2bb92f2da12c64ddd4f67a749041 - file_selector_macos: 585232b688707857504f9cb5f985a7c97fe4dd30 - flowy_infra_ui: 8760ff42a789de40bf5007a5f176b454722a341e + app_links: 10e0a0ab602ffaf34d142cd4862f29d34b303b2a + appflowy_backend: 865496343de667fc8c600e04b9fd05234e130cf9 + bitsdojo_window_macos: 44e3b8fe3dd463820e0321f6256c5b1c16bb6a00 + connectivity_plus: 18d3c32514c886e046de60e9c13895109866c747 + desktop_drop: 69eeff437544aa619c8db7f4481b3a65f7696898 + device_info_plus: ce1b7762849d3ec103d0e0517299f2db7ad60720 + file_selector_macos: 54fdab7caa3ac3fc43c9fac4d7d8d231277f8cf2 + flowy_infra_ui: 03301a39ad118771adbf051a664265c61c507f38 FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 HotKey: e96d8a2ddbf4591131e2bb3f54e69554d90cdca6 - hotkey_manager: b443f35f4d772162937aa73fd8995e579f8ac4e2 - irondash_engine_context: 893c7d96d20ce361d7e996f39d360c4c2f9869ba - local_notifier: ebf072651e35ae5e47280ad52e2707375cb2ae4e - package_info_plus: a8a591e70e87ce97ce5d21b2594f69cea9e0312f - path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564 + hotkey_manager: c32bf0bfe8f934b7bc17ab4ad5c4c142960b023c + irondash_engine_context: da62996ee25616d2f01bbeb85dc115d813359478 + local_notifier: e9506bc66fc70311e8bc7291fb70f743c081e4ff + package_info_plus: fa739dd842b393193c5ca93c26798dff6e3d0e0c + path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 ReachabilitySwift: 7f151ff156cea1481a8411701195ac6a984f4979 - screen_retriever: 4f97c103641aab8ce183fa5af3b87029df167936 + screen_retriever: 59634572a57080243dd1bf715e55b6c54f241a38 Sentry: 1fe34e9c2cbba1e347623610d26db121dcb569f1 - sentry_flutter: e24b397f9a61fa5bbefd8279c3b2242ca86faa90 - share_plus: 11c7b7fa7020465584eca3ff6392c5bc1e399d6e - shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7 - sqflite: c35dad70033b8862124f8337cc994a809fcd9fa3 - super_native_extensions: c2795d6d9aedf4a79fae25cb6160b71b50549189 - url_launcher_macos: de10e46d8d8b9e3a7b8a133e8de92b104379f05e - window_manager: 1d01fa7ac65a6e6f83b965471b1a7fdd3f06166c + sentry_flutter: a39c2a2d67d5e5b9cb0b94a4985c76dd5b3fc737 + share_plus: 36537c04ce0c3e3f5bd297ce4318b6d5ee5fd6cf + shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 + sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec + super_native_extensions: 85efee3a7495b46b04befcfc86ed12069264ebf3 + url_launcher_macos: 5f437abeda8c85500ceb03f5c1938a8c5a705399 + window_manager: 3a1844359a6295ab1e47659b1a777e36773cd6e8 PODFILE CHECKSUM: 0532f3f001ca3110b8be345d6491fff690e95823 From 73463cd7e338f6697a9a280653ba25bd115bb47c Mon Sep 17 00:00:00 2001 From: KD-MM2 <57068549+KD-MM2@users.noreply.github.com> Date: Mon, 30 Dec 2024 19:38:34 +0900 Subject: [PATCH 103/576] =?UTF-8?q?chore(i18n):=20update=20vi-VN=20transla?= =?UTF-8?q?tions=20Fink=20=F0=9F=90=A6=20(#6290)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Nathan.fooo <86001920+appflowy@users.noreply.github.com> --- frontend/resources/translations/ar-SA.json | 2 +- frontend/resources/translations/ca-ES.json | 2 +- frontend/resources/translations/ckb-KU.json | 2 +- frontend/resources/translations/cs-CZ.json | 2 +- frontend/resources/translations/de-DE.json | 2 +- frontend/resources/translations/es-VE.json | 2 +- frontend/resources/translations/eu-ES.json | 2 +- frontend/resources/translations/fa.json | 2 +- frontend/resources/translations/fr-CA.json | 2 +- frontend/resources/translations/fr-FR.json | 2 +- frontend/resources/translations/he.json | 2 +- frontend/resources/translations/hu-HU.json | 2 +- frontend/resources/translations/id-ID.json | 2 +- frontend/resources/translations/it-IT.json | 2 +- frontend/resources/translations/ko-KR.json | 2 +- frontend/resources/translations/pl-PL.json | 2 +- frontend/resources/translations/pt-BR.json | 2 +- frontend/resources/translations/pt-PT.json | 2 +- frontend/resources/translations/ru-RU.json | 2 +- frontend/resources/translations/sv-SE.json | 2 +- frontend/resources/translations/tr-TR.json | 2 +- frontend/resources/translations/uk-UA.json | 2 +- frontend/resources/translations/vi-VN.json | 78 ++++++++++++++++++--- frontend/resources/translations/vi.json | 2 +- frontend/resources/translations/zh-CN.json | 2 +- frontend/resources/translations/zh-TW.json | 2 +- 26 files changed, 94 insertions(+), 34 deletions(-) diff --git a/frontend/resources/translations/ar-SA.json b/frontend/resources/translations/ar-SA.json index 82b6871a80..ad60566f59 100644 --- a/frontend/resources/translations/ar-SA.json +++ b/frontend/resources/translations/ar-SA.json @@ -1634,4 +1634,4 @@ "gallery": { "resetZoom": "إعادة ضبط التكبير" } -} +} \ No newline at end of file diff --git a/frontend/resources/translations/ca-ES.json b/frontend/resources/translations/ca-ES.json index f388cf9bd5..1e427411aa 100644 --- a/frontend/resources/translations/ca-ES.json +++ b/frontend/resources/translations/ca-ES.json @@ -811,4 +811,4 @@ "deleteContentTitle": "Esteu segur que voleu suprimir {pageType}?", "deleteContentCaption": "si suprimiu aquest {pageType}, podeu restaurar-lo des de la paperera." } -} +} \ No newline at end of file diff --git a/frontend/resources/translations/ckb-KU.json b/frontend/resources/translations/ckb-KU.json index 4acb7a1765..841546f901 100644 --- a/frontend/resources/translations/ckb-KU.json +++ b/frontend/resources/translations/ckb-KU.json @@ -941,4 +941,4 @@ "frequentlyUsed": "زۆرجار بەکارت هێناوە" } } -} +} \ No newline at end of file diff --git a/frontend/resources/translations/cs-CZ.json b/frontend/resources/translations/cs-CZ.json index 07e5a01bea..de895531f9 100644 --- a/frontend/resources/translations/cs-CZ.json +++ b/frontend/resources/translations/cs-CZ.json @@ -1094,4 +1094,4 @@ "font": "Písmo", "actions": "Příkazy" } -} +} \ No newline at end of file diff --git a/frontend/resources/translations/de-DE.json b/frontend/resources/translations/de-DE.json index 34b7afde2a..34b02063c9 100644 --- a/frontend/resources/translations/de-DE.json +++ b/frontend/resources/translations/de-DE.json @@ -2866,4 +2866,4 @@ "permissionDenied": "Keine Berechtigung zum Öffnen dieser Datei", "unknownError": "Öffnen der Datei fehlgeschlagen" } -} +} \ No newline at end of file diff --git a/frontend/resources/translations/es-VE.json b/frontend/resources/translations/es-VE.json index 6b61b3c2a6..4936844535 100644 --- a/frontend/resources/translations/es-VE.json +++ b/frontend/resources/translations/es-VE.json @@ -1547,4 +1547,4 @@ "betaTooltip": "Actualmente solo admitimos la búsqueda de páginas.", "fromTrashHint": "De la papelera" } -} +} \ No newline at end of file diff --git a/frontend/resources/translations/eu-ES.json b/frontend/resources/translations/eu-ES.json index be987e7a53..fe153883c4 100644 --- a/frontend/resources/translations/eu-ES.json +++ b/frontend/resources/translations/eu-ES.json @@ -600,4 +600,4 @@ "deleteContentTitle": "Ziur {pageType} ezabatu nahi duzula?", "deleteContentCaption": "{pageType} hau ezabatzen baduzu, zaborrontzitik leheneratu dezakezu." } -} +} \ No newline at end of file diff --git a/frontend/resources/translations/fa.json b/frontend/resources/translations/fa.json index 21c8505c88..5baa50e8dd 100644 --- a/frontend/resources/translations/fa.json +++ b/frontend/resources/translations/fa.json @@ -674,4 +674,4 @@ "frequentlyUsed": "استفاده‌شده" } } -} +} \ No newline at end of file diff --git a/frontend/resources/translations/fr-CA.json b/frontend/resources/translations/fr-CA.json index a3396724aa..c3411ef3ef 100644 --- a/frontend/resources/translations/fr-CA.json +++ b/frontend/resources/translations/fr-CA.json @@ -1256,4 +1256,4 @@ "userIcon": "Icône utilisateur" }, "noLogFiles": "Il n'y a pas de log" -} +} \ No newline at end of file diff --git a/frontend/resources/translations/fr-FR.json b/frontend/resources/translations/fr-FR.json index 501995f5b1..b21f11d442 100644 --- a/frontend/resources/translations/fr-FR.json +++ b/frontend/resources/translations/fr-FR.json @@ -2945,4 +2945,4 @@ "permissionDenied": "Aucune autorisation d'ouvrir ce fichier", "unknownError": "Échec de l'ouverture du fichier" } -} +} \ No newline at end of file diff --git a/frontend/resources/translations/he.json b/frontend/resources/translations/he.json index d47c33c6da..9894e5efaa 100644 --- a/frontend/resources/translations/he.json +++ b/frontend/resources/translations/he.json @@ -2081,4 +2081,4 @@ "signInError": "שגיאת כניסה", "login": "הרשמה או כניסה" } -} +} \ No newline at end of file diff --git a/frontend/resources/translations/hu-HU.json b/frontend/resources/translations/hu-HU.json index 40f05cccc6..c747f7fbb9 100644 --- a/frontend/resources/translations/hu-HU.json +++ b/frontend/resources/translations/hu-HU.json @@ -598,4 +598,4 @@ "deleteContentTitle": "Biztosan törli a következőt: {pageType}?", "deleteContentCaption": "ha törli ezt a {pageType} oldalt, visszaállíthatja a kukából." } -} +} \ No newline at end of file diff --git a/frontend/resources/translations/id-ID.json b/frontend/resources/translations/id-ID.json index ede3571878..c4a3d324c8 100644 --- a/frontend/resources/translations/id-ID.json +++ b/frontend/resources/translations/id-ID.json @@ -1044,4 +1044,4 @@ "noFavorite": "Tidak ada halaman favorit", "noFavoriteHintText": "Geser halaman ke kiri untuk menambahkannya ke favorit Anda" } -} +} \ No newline at end of file diff --git a/frontend/resources/translations/it-IT.json b/frontend/resources/translations/it-IT.json index d6877ecd59..3707e71753 100644 --- a/frontend/resources/translations/it-IT.json +++ b/frontend/resources/translations/it-IT.json @@ -1366,4 +1366,4 @@ "userIcon": "Icona utente" }, "noLogFiles": "Non ci sono file di log" -} +} \ No newline at end of file diff --git a/frontend/resources/translations/ko-KR.json b/frontend/resources/translations/ko-KR.json index 8f145cbcbe..dbd9f81120 100644 --- a/frontend/resources/translations/ko-KR.json +++ b/frontend/resources/translations/ko-KR.json @@ -605,4 +605,4 @@ "deleteContentTitle": "{pageType}을(를) 삭제하시겠습니까?", "deleteContentCaption": "이 {pageType}을(를) 삭제하면 휴지통에서 복원할 수 있습니다." } -} +} \ No newline at end of file diff --git a/frontend/resources/translations/pl-PL.json b/frontend/resources/translations/pl-PL.json index e3ca580354..cc56e634ae 100644 --- a/frontend/resources/translations/pl-PL.json +++ b/frontend/resources/translations/pl-PL.json @@ -1145,4 +1145,4 @@ "language": "Język", "font": "Czcionka" } -} +} \ No newline at end of file diff --git a/frontend/resources/translations/pt-BR.json b/frontend/resources/translations/pt-BR.json index 51b585f14b..538d09d5ea 100644 --- a/frontend/resources/translations/pt-BR.json +++ b/frontend/resources/translations/pt-BR.json @@ -1544,4 +1544,4 @@ "addField": "Adicionar campo", "userIcon": "Ícone do usuário" } -} +} \ No newline at end of file diff --git a/frontend/resources/translations/pt-PT.json b/frontend/resources/translations/pt-PT.json index 617e097f6b..5fc806e07f 100644 --- a/frontend/resources/translations/pt-PT.json +++ b/frontend/resources/translations/pt-PT.json @@ -856,4 +856,4 @@ "noResult": "Nenhum resultado", "caseSensitive": "Maiúsculas e minúsculas" } -} +} \ No newline at end of file diff --git a/frontend/resources/translations/ru-RU.json b/frontend/resources/translations/ru-RU.json index 68a3fbfd9e..bcdf45a8d2 100644 --- a/frontend/resources/translations/ru-RU.json +++ b/frontend/resources/translations/ru-RU.json @@ -2184,4 +2184,4 @@ "signInError": "Ошибка входа", "login": "Зарегистрироваться или войти" } -} +} \ No newline at end of file diff --git a/frontend/resources/translations/sv-SE.json b/frontend/resources/translations/sv-SE.json index 42855011b2..4c2d625946 100644 --- a/frontend/resources/translations/sv-SE.json +++ b/frontend/resources/translations/sv-SE.json @@ -662,4 +662,4 @@ "deleteContentTitle": "Är du säker på att du vill ta bort {pageType}?", "deleteContentCaption": "om du tar bort denna {pageType} kan du återställa den från papperskorgen." } -} +} \ No newline at end of file diff --git a/frontend/resources/translations/tr-TR.json b/frontend/resources/translations/tr-TR.json index 0764181ab0..3984282364 100644 --- a/frontend/resources/translations/tr-TR.json +++ b/frontend/resources/translations/tr-TR.json @@ -2295,4 +2295,4 @@ "signInError": "Oturum açma hatası", "login": "Kaydolun veya giriş yapın" } -} +} \ No newline at end of file diff --git a/frontend/resources/translations/uk-UA.json b/frontend/resources/translations/uk-UA.json index 7f354bd892..c0c647f975 100644 --- a/frontend/resources/translations/uk-UA.json +++ b/frontend/resources/translations/uk-UA.json @@ -2587,4 +2587,4 @@ "uploadFailedDescription": "Не вдалося завантажити файл", "uploadingDescription": "Файл завантажується" } -} +} \ No newline at end of file diff --git a/frontend/resources/translations/vi-VN.json b/frontend/resources/translations/vi-VN.json index 21c0e6ede1..aa80386326 100644 --- a/frontend/resources/translations/vi-VN.json +++ b/frontend/resources/translations/vi-VN.json @@ -129,7 +129,7 @@ "medium": "trung bình", "large": "lớn", "fontSize": "Cỡ chữ", - "import": "Import", + "import": "Nhập", "moreOptions": "Lựa chọn khác", "wordCount": "Số từ: {}", "charCount": "Số ký tự: {}", @@ -306,11 +306,15 @@ "upgradeToPro": "Nâng cấp lên Pro", "upgradeToAIMax": "Mở khóa AI không giới hạn", "storageLimitDialogTitle": "Bạn đã hết dung lượng lưu trữ miễn phí. Nâng cấp để mở khóa dung lượng lưu trữ không giới hạn", + "storageLimitDialogTitleIOS": "Bạn đã hết dung lượng lưu trữ miễn phí.", "aiResponseLimitTitle": "Bạn đã hết phản hồi AI miễn phí. Nâng cấp lên Gói Pro hoặc mua tiện ích bổ sung AI để mở khóa phản hồi không giới hạn", + "aiResponseLimitTitleIOS": "Bạn đã hết lượt sử dụng AI miễn phí.", "aiResponseLimitDialogTitle": "Đã đạt đến giới hạn sử dụng AI", "aiResponseLimit": "Bạn đã hết lượt dùng AI miễn phí.\nVào Cài đặt -> Gói đăng ký -> Nhấp vào AI Max hoặc Gói Pro để có thêm lượt dùng AI", "askOwnerToUpgradeToPro": "Không gian làm việc của bạn sắp hết dung lượng lưu trữ miễn phí. Vui lòng yêu cầu chủ sở hữu không gian làm việc của bạn nâng cấp lên Gói Pro", + "askOwnerToUpgradeToProIOS": "Không gian làm việc của bạn sắp hết dung lượng lưu trữ miễn phí.", "askOwnerToUpgradeToAIMax": "Không gian làm việc của bạn sắp hết phản hồi AI miễn phí. Vui lòng yêu cầu chủ sở hữu không gian làm việc của bạn nâng cấp gói hoặc mua tiện ích bổ sung AI", + "askOwnerToUpgradeToAIMaxIOS": "Không gian làm việc của bạn sắp hết lượt sử dụng AI miễn phí.", "purchaseStorageSpace": "Mua không gian lưu trữ", "purchaseAIResponse": "Mua ", "askOwnerToUpgradeToLocalAI": "Yêu cầu chủ sở hữu không gian làm việc bật AI trên thiết bị", @@ -846,6 +850,7 @@ "itemFour": "Chỉnh sửa thời gian thực", "itemFive": "Ứng dụng di động", "itemSix": "Phản hồi của AI", + "itemFileUpload": "Tải tập tin lên", "tooltipSix": "Trọn đời có nghĩa là số lượng phản hồi không bao giờ được thiết lập lại", "intelligentSearch": "Tìm kiếm thông minh", "tooltipSeven": "Cho phép bạn tùy chỉnh một phần URL cho không gian làm việc của bạn" @@ -856,7 +861,9 @@ "itemThree": "5 GB", "itemFour": "Đúng", "itemFive": "Đúng", + "itemFileUpload": "Lên đến 7 MB", "itemSix": "20 trọn đời", + "intelligentSearch": "Tìm kiếm thông minh" }, "proLabels": { @@ -866,6 +873,7 @@ "itemFour": "Đúng", "itemFive": "Đúng", "itemSix": "không giới hạn", + "itemFileUpload": "Không giới hạn", "intelligentSearch": "Tìm kiếm thông minh" }, "paymentSuccess": { @@ -1221,6 +1229,7 @@ "typeAValue": "Nhập một giá trị...", "layout": "Bố cục", "databaseLayout": "Bố cục", + "viewList": "Database Views", "editView": "Chỉnh sửa chế độ xem", "boardSettings": "Cài đặt bảng", "calendarSettings": "Cài đặt lịch", @@ -1228,8 +1237,7 @@ "duplicateView": "Xem trùng lặp", "deleteView": "Xóa chế độ xem", "numberOfVisibleFields": "{} đã hiển thị", - "Properties": "Thuộc tính", - "viewList": "Database Views" + "Properties": "Thuộc tính" }, "textFilter": { "contains": "Chứa", @@ -1296,6 +1304,7 @@ "isNotEmpty": "Không trống" }, "field": { + "label": "Thuộc tính", "hide": "Ẩn", "show": "Hiện", "insertLeft": "Chèn trái", @@ -1317,6 +1326,7 @@ "relationFieldName": "Mối quan hệ", "summaryFieldName": "Tóm tắt AI", "timeFieldName": "Thời gian", + "mediaFieldName": "Tệp tin & phương tiện", "translateFieldName": "AI Dịch", "translateTo": "Dịch sang", "numberFormat": "Định dạng số", @@ -1459,6 +1469,24 @@ "countEmptyShort": "TRỐNG", "countNonEmpty": "Đếm không trống", "countNonEmptyShort": "ĐIỀN" + }, + "media": { + "rename": "Đổi tên", + "download": "Tải về", + "open": "Mở", + "delete": "Xóa bỏ", + "moreFilesHint": "+{}", + "addFileOrImage": "Thêm tệp, hình ảnh hoặc liên kết", + "attachmentsHint": "{}", + "addFileMobile": "Thêm tập tin", + "showFile": "Hiển thị 1 tập tin", + "showFiles": "Hiển thị {} tập tin", + "hideFile": "Ẩn 1 tập tin", + "hideFiles": "Ẩn {} tập tin", + "deleteFileDescription": "Bạn có chắc chắn muốn xóa tệp này không? Hành động này không thể hoàn tác.", + "hideFileNames": "Ẩn tên tập tin", + "downloadSuccess": "Đã lưu tập tin thành công", + "downloadFailedToken": "Không tải được tệp, mã thông báo người dùng không khả dụng" } }, "document": { @@ -1670,6 +1698,7 @@ "file": { "name": "Tài liệu", "uploadTab": "Tải lên", + "uploadMobile": "Chọn tệp", "networkTab": "Nhúng liên kết", "placeholderText": "Tải lên hoặc nhúng một tập tin", "placeholderDragging": "Thả tệp để tải lên", @@ -1685,7 +1714,8 @@ "nameEmptyError": "Tên tệp không được để trống." }, "uploadedAt": "Đã tải lên vào {}", - "linkedAt": "Liên kết đã được thêm vào {}" + "linkedAt": "Liên kết đã được thêm vào {}", + "failedToOpenMsg": "Không mở được, không tìm thấy tệp" } }, "outlineBlock": { @@ -1796,7 +1826,9 @@ "errorBlock": { "theBlockIsNotSupported": "Không thể phân tích nội dung khối", "clickToCopyTheBlockContent": "Nhấp để sao chép nội dung khối", - "blockContentHasBeenCopied": "Nội dung khối đã được sao chép." + "blockContentHasBeenCopied": "Nội dung khối đã được sao chép.", + "parseError": "Đã xảy ra lỗi khi phân tích khối {}.", + "copyBlockContent": "Sao chép nội dung khối" }, "mobilePageSelector": { "title": "Chọn trang", @@ -1806,6 +1838,7 @@ }, "board": { "column": { + "label": "Cột", "createNewCard": "Mới", "renameGroupTooltip": "Nhấn để đổi tên nhóm", "createNewColumn": "Thêm một nhóm mới", @@ -1855,7 +1888,11 @@ "nextThirtyDays": "30 ngày tiếp theo" }, "noGroup": "Không có nhóm theo tài sản", - "noGroupDesc": "Các chế độ xem bảng yêu cầu một thuộc tính để nhóm theo để hiển thị" + "noGroupDesc": "Các chế độ xem bảng yêu cầu một thuộc tính để nhóm theo để hiển thị", + "media": { + "cardText": "{} {}", + "fallbackName": "tập tin" + } }, "calendar": { "menuName": "Lịch", @@ -2230,10 +2267,18 @@ "deleteAccount": { "title": "Xóa tài khoản", "subtitle": "Xóa vĩnh viễn tài khoản và toàn bộ dữ liệu của bạn.", + "description": "Xóa vĩnh viễn tài khoản của bạn và xóa quyền truy cập khỏi mọi không gian làm việc.", "deleteMyAccount": "Xóa tài khoản của tôi", "dialogTitle": "Xóa tài khoản", "dialogContent1": "Bạn có chắc chắn muốn xóa vĩnh viễn tài khoản của mình không?", - "dialogContent2": "Không thể hoàn tác hành động này và sẽ xóa quyền truy cập khỏi mọi không gian làm việc nhóm, xóa toàn bộ tài khoản của bạn, bao gồm cả không gian làm việc riêng tư và xóa bạn khỏi mọi không gian làm việc được chia sẻ." + "dialogContent2": "Không thể hoàn tác hành động này và sẽ xóa quyền truy cập khỏi mọi không gian làm việc nhóm, xóa toàn bộ tài khoản của bạn, bao gồm cả không gian làm việc riêng tư và xóa bạn khỏi mọi không gian làm việc được chia sẻ.", + "confirmHint1": "Vui lòng nhập \"XÓA TÀI KHOẢN CỦA TÔI\" để xác nhận.", + "confirmHint2": "Tôi hiểu rằng hành động này là không thể đảo ngược và sẽ xóa vĩnh viễn tài khoản của tôi cùng mọi dữ liệu liên quan.", + "confirmHint3": "XÓA TÀI KHOẢN CỦA TÔI", + "checkToConfirmError": "Bạn phải đánh dấu vào ô để xác nhận việc xóa", + "failedToGetCurrentUser": "Không lấy được email người dùng hiện tại", + "confirmTextValidationFailed": "Văn bản xác nhận của bạn không khớp với \"XÓA TÀI KHOẢN CỦA TÔI\"", + "deleteAccountSuccess": "Tài khoản đã được xóa thành công" } }, "workplace": { @@ -2332,6 +2377,7 @@ }, "publish": { "hasNotBeenPublished": "Trang này chưa được xuất bản", + "spaceHasNotBeenPublished": "Chưa hỗ trợ xuất bản không gian", "reportPage": "Báo cáo trang", "databaseHasNotBeenPublished": "Việc xuất bản cơ sở dữ liệu hiện chưa được hỗ trợ.", "createdWith": "Được tạo ra với", @@ -2375,7 +2421,8 @@ "one": "1 thành viên", "many": "{đếm} thành viên", "other": "{đếm} thành viên" - } + }, + "useThisTemplate": "Sử dụng mẫu" }, "web": { "continue": "Tiếp tục", @@ -2383,6 +2430,9 @@ "continueWithGoogle": "Tiếp tục với Google", "continueWithGithub": "Tiếp tục với GitHub", "continueWithDiscord": "Tiếp tục với Discord", + "continueWithApple": "Tiếp tục với Apple ", + "moreOptions": "Thêm tùy chọn", + "collapse": "Thu gọn", "signInAgreement": "Bằng cách nhấp vào \"Tiếp tục\" ở trên, bạn đã đồng ý với AppFlowy", "and": "và", "termOfUse": "Điều khoản", @@ -2506,5 +2556,15 @@ "uploadSuccessDescription": "Tệp đã được tải lên thành công", "uploadFailedDescription": "Tải tệp lên không thành công", "uploadingDescription": "Tập tin đang được tải lên" + }, + "gallery": { + "preview": "Mở toàn màn hình", + "copy": "Sao chép", + "download": "Tải về", + "prev": "Trước", + "next": "Kế tiếp", + "resetZoom": "Đặt lại chế độ thu phóng", + "zoomIn": "Phóng to", + "zoomOut": "Thu nhỏ" } -} +} \ No newline at end of file diff --git a/frontend/resources/translations/vi.json b/frontend/resources/translations/vi.json index b921c1844e..4d1716447a 100644 --- a/frontend/resources/translations/vi.json +++ b/frontend/resources/translations/vi.json @@ -6,4 +6,4 @@ "failedToLoad": "Không tải được chế độ xem bảng" } } -} +} \ No newline at end of file diff --git a/frontend/resources/translations/zh-CN.json b/frontend/resources/translations/zh-CN.json index d7e9b70dac..d8cfc88e2a 100644 --- a/frontend/resources/translations/zh-CN.json +++ b/frontend/resources/translations/zh-CN.json @@ -1889,4 +1889,4 @@ "yesterday": "昨天", "today": "今天" } -} +} \ No newline at end of file diff --git a/frontend/resources/translations/zh-TW.json b/frontend/resources/translations/zh-TW.json index 34f86cd383..55da2ee917 100644 --- a/frontend/resources/translations/zh-TW.json +++ b/frontend/resources/translations/zh-TW.json @@ -1545,4 +1545,4 @@ "betaLabel": "BETA", "betaTooltip": "目前我們只支援搜尋頁面" } -} +} \ No newline at end of file From b965a5f3ae4e6af03145af2082ab4cf4417c5d4c Mon Sep 17 00:00:00 2001 From: Ahad Patel <69256193+Ahad-patel@users.noreply.github.com> Date: Tue, 31 Dec 2024 07:06:14 +0530 Subject: [PATCH 104/576] feat: delete the previous image when the cover changes in local mode (#6368) * remove unecessary images from localstorage * feat: Add handler for deleting previous cover image on cover image change * fix: add local image case for versions after 0.5.5 * fix: add try catch block and delete action to bottom of function * chore: add test case for uploading and deleting image in localmode --------- Co-authored-by: Lucas.Xu --- .../document_with_cover_image_test.dart | 61 +++++++++++++++++++ .../header/document_cover_widget.dart | 20 +++++- .../editor_plugins/image/image_util.dart | 13 ++++ .../migration/editor_migration.dart | 8 +++ 4 files changed, 99 insertions(+), 3 deletions(-) diff --git a/frontend/appflowy_flutter/integration_test/desktop/document/document_with_cover_image_test.dart b/frontend/appflowy_flutter/integration_test/desktop/document/document_with_cover_image_test.dart index c9f844f374..84b6790403 100644 --- a/frontend/appflowy_flutter/integration_test/desktop/document/document_with_cover_image_test.dart +++ b/frontend/appflowy_flutter/integration_test/desktop/document/document_with_cover_image_test.dart @@ -1,15 +1,23 @@ +import 'dart:io'; + import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/header/document_cover_widget.dart'; +import 'package:appflowy/plugins/document/presentation/editor_plugins/image/image_util.dart'; import 'package:appflowy/shared/icon_emoji_picker/flowy_icon_emoji_picker.dart'; import 'package:appflowy/shared/icon_emoji_picker/recent_icons.dart'; import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart'; import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_emoji_mart/flutter_emoji_mart.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; +import 'package:path/path.dart' as p; +import 'package:path_provider/path_provider.dart'; import '../../shared/emoji.dart'; +import '../../shared/mock/mock_file_picker.dart'; import '../../shared/util.dart'; void main() { @@ -60,6 +68,59 @@ void main() { tester.expectToSeeNoDocumentCover(); }); + testWidgets('document cover local image tests', (tester) async { + await tester.initializeAppFlowy(); + await tester.tapAnonymousSignInButton(); + + tester.expectToSeeNoDocumentCover(); + + // Hover over cover toolbar to show 'Add Cover' and 'Add Icon' buttons + await tester.editor.hoverOnCoverToolbar(); + + // Insert a document cover + await tester.editor.tapOnAddCover(); + tester.expectToSeeDocumentCover(CoverType.asset); + + // Hover over the cover to show the 'Change Cover' and delete buttons + await tester.editor.hoverOnCover(); + tester.expectChangeCoverAndDeleteButton(); + + // Change cover to a local image image + final imagePath = await rootBundle.load('assets/test/images/sample.jpeg'); + final tempDirectory = await getTemporaryDirectory(); + final localImagePath = p.join(tempDirectory.path, 'sample.jpeg'); + final imageFile = File(localImagePath) + ..writeAsBytesSync(imagePath.buffer.asUint8List()); + + await tester.editor.hoverOnCover(); + await tester.editor.tapOnChangeCover(); + + final uploadButton = find.findTextInFlowyText( + LocaleKeys.document_imageBlock_upload_label.tr(), + ); + await tester.tapButton(uploadButton); + + mockPickFilePaths(paths: [localImagePath]); + await tester.tapButtonWithName( + LocaleKeys.document_imageBlock_upload_placeholder.tr(), + ); + + await tester.pumpAndSettle(); + tester.expectToSeeDocumentCover(CoverType.file); + + // Remove the cover + await tester.editor.hoverOnCover(); + await tester.editor.tapOnRemoveCover(); + tester.expectToSeeNoDocumentCover(); + + // Test if deleteImageFromLocalStorage(localImagePath) function is called once + await tester.pump(kDoubleTapTimeout); + expect(deleteImageTestCounter, 1); + + // delete temp files + await imageFile.delete(); + }); + testWidgets('document icon tests', (tester) async { await tester.initializeAppFlowy(); await tester.tapAnonymousSignInButton(); diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/header/document_cover_widget.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/header/document_cover_widget.dart index d28020d0fe..3a827b7ce0 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/header/document_cover_widget.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/header/document_cover_widget.dart @@ -763,15 +763,29 @@ class DocumentCoverState extends State { } Future onCoverChanged(CoverType type, String? details) async { - if (type == CoverType.file && details != null && !isURL(details)) { + final previousType = CoverType.fromString( + widget.node.attributes[DocumentHeaderBlockKeys.coverType], + ); + final previousDetails = + widget.node.attributes[DocumentHeaderBlockKeys.coverDetails]; + + bool isFileType(CoverType type, String? details) => + type == CoverType.file && details != null && !isURL(details); + + if (isFileType(type, details)) { if (_isLocalMode()) { - details = await saveImageToLocalStorage(details); + details = await saveImageToLocalStorage(details!); } else { // else we should save the image to cloud storage - (details, _) = await saveImageToCloudStorage(details, widget.view.id); + (details, _) = await saveImageToCloudStorage(details!, widget.view.id); } } widget.onChangeCover(type, details); + + // After cover change,delete from localstorage if previous cover was image type + if (isFileType(previousType, previousDetails) && _isLocalMode()) { + await deleteImageFromLocalStorage(previousDetails); + } } void setOverlayButtonsHidden(bool value) { diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/image/image_util.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/image/image_util.dart index efa5721382..74d1955312 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/image/image_util.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/image/image_util.dart @@ -117,3 +117,16 @@ Future> extractAndUploadImages( return images; } + +@visibleForTesting +int deleteImageTestCounter = 0; + +Future deleteImageFromLocalStorage(String localImagePath) async { + try { + await File(localImagePath) + .delete() + .whenComplete(() => deleteImageTestCounter++); + } catch (e) { + Log.error('cannot delete image file', e); + } +} diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/migration/editor_migration.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/migration/editor_migration.dart index 8463030667..dcda2941ff 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/migration/editor_migration.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/migration/editor_migration.dart @@ -226,6 +226,14 @@ class EditorMigration { }, }; } + } else { + extra = { + ViewExtKeys.coverKey: { + ViewExtKeys.coverTypeKey: + PageStyleCoverImageType.localImage.toString(), + ViewExtKeys.coverValueKey: coverDetails, + }, + }; } break; default: From 3d4d4cf7097561af3290187e6e3d2088f8d4956d Mon Sep 17 00:00:00 2001 From: Morn Date: Tue, 31 Dec 2024 09:46:13 +0800 Subject: [PATCH 105/576] test: add edit link menu test (#7097) --- .../mobile/document/document_test_runner.dart | 2 + .../mobile/document/toolbar_test.dart | 117 ++++++++++++++++++ 2 files changed, 119 insertions(+) create mode 100644 frontend/appflowy_flutter/integration_test/mobile/document/toolbar_test.dart diff --git a/frontend/appflowy_flutter/integration_test/mobile/document/document_test_runner.dart b/frontend/appflowy_flutter/integration_test/mobile/document/document_test_runner.dart index 67fdaaa204..e19896b310 100644 --- a/frontend/appflowy_flutter/integration_test/mobile/document/document_test_runner.dart +++ b/frontend/appflowy_flutter/integration_test/mobile/document/document_test_runner.dart @@ -4,6 +4,7 @@ import 'page_style_test.dart' as page_style_test; import 'plus_menu_test.dart' as plus_menu_test; import 'simple_table_test.dart' as simple_table_test; import 'title_test.dart' as title_test; +import 'toolbar_test.dart' as toolbar_test; void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); @@ -13,4 +14,5 @@ void main() { page_style_test.main(); plus_menu_test.main(); simple_table_test.main(); + toolbar_test.main(); } diff --git a/frontend/appflowy_flutter/integration_test/mobile/document/toolbar_test.dart b/frontend/appflowy_flutter/integration_test/mobile/document/toolbar_test.dart new file mode 100644 index 0000000000..72da283cd6 --- /dev/null +++ b/frontend/appflowy_flutter/integration_test/mobile/document/toolbar_test.dart @@ -0,0 +1,117 @@ +import 'package:appflowy/generated/flowy_svgs.g.dart'; +import 'package:appflowy/generated/locale_keys.g.dart'; +import 'package:appflowy/mobile/presentation/editor/mobile_editor_screen.dart'; +import 'package:appflowy/mobile/presentation/mobile_bottom_navigation_bar.dart'; +import 'package:appflowy/plugins/document/presentation/editor_plugins/mobile_toolbar_v3/appflowy_mobile_toolbar_item.dart'; +import 'package:appflowy/plugins/document/presentation/editor_plugins/mobile_toolbar_v3/util.dart'; +import 'package:appflowy_editor/appflowy_editor.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flowy_infra_ui/style_widget/text_field.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; + +import '../../shared/util.dart'; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + group('toolbar menu:', () { + testWidgets('insert links', (tester) async { + await tester.launchInAnonymousMode(); + + final createPageButton = find.byKey( + BottomNavigationBarItemType.add.valueKey, + ); + await tester.tapButton(createPageButton); + expect(find.byType(MobileDocumentScreen), findsOneWidget); + + final editor = find.byType(AppFlowyEditor); + expect(editor, findsOneWidget); + final editorState = tester.editor.getCurrentEditorState(); + + /// move cursor to content + final root = editorState.document.root; + final lastNode = root.children.lastOrNull; + await editorState.updateSelectionWithReason( + Selection.collapsed(Position(path: lastNode!.path)), + ); + await tester.pumpAndSettle(); + + /// insert two lines of text + const strFirst = 'FirstLine', + strSecond = 'SecondLine', + link = 'google.com'; + await editorState.insertTextAtCurrentSelection(strFirst); + await tester.pumpAndSettle(); + await editorState.insertNewLine(); + await tester.pumpAndSettle(); + await editorState.insertTextAtCurrentSelection(strSecond); + await tester.pumpAndSettle(); + final firstLine = find.text(strFirst, findRichText: true), + secondLine = find.text(strSecond, findRichText: true); + expect(firstLine, findsOneWidget); + expect(secondLine, findsOneWidget); + + /// select the first line + await tester.longPress(firstLine); + await tester.pumpAndSettle(); + + /// find aa item and tap it + final aaItem = find.byWidgetPredicate( + (widget) => + widget is AppFlowyMobileToolbarIconItem && + widget.icon == FlowySvgs.m_toolbar_aa_m, + ); + expect(aaItem, findsOneWidget); + await tester.tapButton(aaItem); + + /// find link button and tap it + final linkButton = find.byWidgetPredicate( + (widget) => + widget is MobileToolbarMenuItemWrapper && + widget.icon == FlowySvgs.m_toolbar_link_m, + ); + expect(linkButton, findsOneWidget); + await tester.tapButton(linkButton); + + /// input the link + final linkField = find.byWidgetPredicate( + (w) => + w is FlowyTextField && + w.hintText == LocaleKeys.document_inlineLink_url_placeholder.tr(), + ); + await tester.enterText(linkField, link); + await tester.pumpAndSettle(); + + /// complete inputting + await tester.tapButton(find.text(LocaleKeys.button_done.tr())); + + /// do it again + /// select the second line + await tester.longPress(secondLine); + await tester.pumpAndSettle(); + await tester.tapButton(aaItem); + await tester.tapButton(linkButton); + await tester.enterText(linkField, link); + await tester.pumpAndSettle(); + await tester.tapButton(find.text(LocaleKeys.button_done.tr())); + + final firstNode = editorState.getNodeAtPath([0]); + final secondNode = editorState.getNodeAtPath([1]); + + Map commonDeltaJson(String insert) => { + "insert": insert, + "attributes": {"href": link}, + }; + + expect( + firstNode?.delta?.toJson(), + commonDeltaJson(strFirst), + ); + expect( + secondNode?.delta?.toJson(), + commonDeltaJson(strSecond), + ); + }); + }); +} From c2643bfb6ce3d796892dac32114bf051659bb9bc Mon Sep 17 00:00:00 2001 From: FakhriAzzouz Date: Tue, 31 Dec 2024 04:02:16 +0100 Subject: [PATCH 106/576] chore: update ar-SA translations (#7099) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 * chore: update translations with Fink 🐦 --- frontend/resources/translations/ar-SA.json | 216 +++++++++++++++++++- frontend/resources/translations/ca-ES.json | 2 +- frontend/resources/translations/ckb-KU.json | 2 +- frontend/resources/translations/cs-CZ.json | 2 +- frontend/resources/translations/de-DE.json | 2 +- frontend/resources/translations/es-VE.json | 2 +- frontend/resources/translations/eu-ES.json | 2 +- frontend/resources/translations/fa.json | 2 +- frontend/resources/translations/fr-CA.json | 2 +- frontend/resources/translations/fr-FR.json | 10 +- frontend/resources/translations/he.json | 2 +- frontend/resources/translations/hu-HU.json | 2 +- frontend/resources/translations/id-ID.json | 2 +- frontend/resources/translations/it-IT.json | 2 +- frontend/resources/translations/ko-KR.json | 2 +- frontend/resources/translations/pl-PL.json | 2 +- frontend/resources/translations/pt-BR.json | 2 +- frontend/resources/translations/pt-PT.json | 2 +- frontend/resources/translations/ru-RU.json | 2 +- frontend/resources/translations/sv-SE.json | 2 +- frontend/resources/translations/tr-TR.json | 2 +- frontend/resources/translations/uk-UA.json | 2 +- frontend/resources/translations/vi-VN.json | 21 +- frontend/resources/translations/vi.json | 2 +- frontend/resources/translations/zh-CN.json | 2 +- frontend/resources/translations/zh-TW.json | 2 +- 26 files changed, 243 insertions(+), 50 deletions(-) diff --git a/frontend/resources/translations/ar-SA.json b/frontend/resources/translations/ar-SA.json index ad60566f59..f8eaa3bc94 100644 --- a/frontend/resources/translations/ar-SA.json +++ b/frontend/resources/translations/ar-SA.json @@ -149,7 +149,11 @@ "charCount": "عدد الأحرف: {}", "createdAt": "منشأ: {}", "deleteView": "يمسح", - "duplicateView": "تكرار" + "duplicateView": "تكرار", + "wordCountLabel": "عدد الكلمات: ", + "charCountLabel": "عدد الأحرف: ", + "createdAtLabel": "تم إنشاؤه: ", + "syncedAtLabel": "تم المزامنة: " }, "importPanel": { "textAndMarkdown": "نص و Markdown", @@ -170,7 +174,9 @@ "addToFavorites": "اضافة الى المفضلة", "copyLink": "نسخ الرابط", "changeIcon": "تغيير الأيقونة", - "collapseAllPages": "طي جميع الصفحات الفرعية" + "collapseAllPages": "طي جميع الصفحات الفرعية", + "movePageTo": "تحريك الصفحة إلى", + "move": "تحريك" }, "blankPageTitle": "صفحة فارغة", "newPageText": "صفحة جديدة", @@ -182,7 +188,7 @@ "newChat": "الدردشة بالذكاء الاصطناعي", "inputMessageHint": "اسأل @:appName AI", "inputLocalAIMessageHint": "اسأل @:appName Local AI", - "unsupportedCloudPrompt": "هذه الميزة متاحة فقط عند استخدام @:appName Cloud", + "unsupportedCloudPrompt": "هذه الميزة متوفرة فقط عند استخدام @:appName Cloud", "relatedQuestion": "ذات صلة", "serverUnavailable": "الخدمة غير متاحة مؤقتًا. يرجى المحاولة مرة أخرى لاحقًا.", "aiServerUnavailable": "تم فقد الاتصال. يرجى التحقق من اتصالك بالإنترنت", @@ -208,7 +214,14 @@ "indexingFile": "الفهرسة {}", "generatingResponse": "توليد الاستجابة", "selectSources": "اختر المصادر", - "regenerate": "حاول ثانية" + "sourceUnsupported": "نحن لا ندعم الدردشة مع قواعد البيانات في الوقت الحالي", + "regenerate": "حاول ثانية", + "addToPageButton": "أضف إلى الصفحة", + "addToPageTitle": "أضف رسالة إلى...", + "addToNewPage": "أضف إلى صفحة جديدة", + "addToNewPageName": "الرسائل المستخرجة من \"{}\"", + "addToNewPageSuccessToast": "تمت إضافة الرسالة إلى", + "openPagePreviewFailedToast": "فشل في فتح الصفحة" }, "trash": { "text": "المهملات", @@ -300,6 +313,7 @@ "sideBar": { "closeSidebar": "إغلاق الشريط الجانبي", "openSidebar": "فتح الشريط الجانبي", + "expandSidebar": "توسيع كصفحة كاملة", "personal": "شخصي", "private": "خاص", "workspace": "مساحة العمل", @@ -396,6 +410,7 @@ "add": "اضافة", "yes": "نعم", "no": "لا", + "clear": "مسح", "remove": "حذف", "dontRemove": "لا تقم بالإزالة", "copyLink": "نسخ الرابط", @@ -418,6 +433,9 @@ "viewing": "عرض", "editing": "تحرير", "gotIt": "فهمتها", + "retry": "إعادة المحاولة", + "uploadFailed": "فشل الرفع.", + "copyLinkOriginal": "نسخ الرابط إلى الأصل", "tryAGain": "حاول ثانية" }, "label": { @@ -445,6 +463,7 @@ "popupMenuItem": { "settings": "إعدادات", "members": "الأعضاء", + "trash": "سلة المحذوفات", "helpAndSupport": "المساعدة والدعم" }, "sites": { @@ -708,10 +727,177 @@ "deleteLeftSentence": "حذف الجملة اليسرى", "delete": "حذف الحرف الأيمن", "deleteMacOS": "حذف الحرف الأيسر", - "deleteRightWord": "حذف الكلمة اليمنى" + "deleteRightWord": "حذف الكلمة اليمنى", + "moveCursorLeft": "حرك المؤشر إلى اليسار", + "moveCursorBeginning": "حرك المؤشر إلى البداية", + "moveCursorLeftWord": "حرك المؤشر إلى اليسار كلمة واحدة", + "moveCursorLeftSelect": "حدد وحرك المؤشر إلى اليسار", + "moveCursorBeginSelect": "حدد وحرك المؤشر إلى البداية", + "moveCursorLeftWordSelect": "حدد وحرك المؤشر إلى اليسار كلمة واحدة", + "moveCursorRight": "حرك المؤشر إلى اليمين", + "moveCursorEnd": "حرك المؤشر إلى النهاية", + "moveCursorRightWord": "حرك المؤشر إلى اليمين كلمة واحدة", + "moveCursorRightSelect": "حدد المؤشر وحركه إلى اليمين", + "moveCursorEndSelect": "حدد وحرك المؤشر إلى النهاية", + "moveCursorRightWordSelect": "حدد وحرك المؤشر إلى اليمين كلمة واحدة", + "moveCursorUp": "حرك المؤشر لأعلى", + "moveCursorTopSelect": "حدد وحرك المؤشر إلى الأعلى", + "moveCursorTop": "حرك المؤشر إلى الأعلى", + "moveCursorUpSelect": "حدد وحرك المؤشر لأعلى", + "moveCursorBottomSelect": "حدد وحرك المؤشر إلى الأسفل", + "moveCursorBottom": "حرك المؤشر إلى الأسفل", + "moveCursorDown": "حرك المؤشر إلى الأسفل", + "moveCursorDownSelect": "حدد وحرك المؤشر لأسفل", + "home": "تمرير إلى الأعلى", + "end": "تمرير إلى الأسفل", + "toggleBold": "تبديل الخط الغامق", + "toggleItalic": "تبديل الخط المائل", + "toggleUnderline": "تبديل التسطير", + "toggleStrikethrough": "تبديل الشطب", + "toggleCode": "تبديل الكود المضمن", + "toggleHighlight": "تبديل التمييز", + "showLinkMenu": "عرض قائمة الروابط", + "openInlineLink": "افتح الرابط المضمن", + "openLinks": "فتح جميع الروابط المحددة", + "indent": "المسافة البادئة", + "outdent": "مسافة بادئة سلبية", + "exit": "الخروج من التحرير", + "pageUp": "قم بالتمرير صفحة واحدة لأعلى", + "pageDown": "قم بالتمرير صفحة واحدة لأسفل", + "selectAll": "حدد الكل", + "pasteWithoutFormatting": "لصق المحتوى بدون تنسيق", + "showEmojiPicker": "عرض أداة اختيار الرموز التعبيرية", + "enterInTableCell": "إضافة فاصل الأسطر في الجدول", + "leftInTableCell": "حرك خلية واحدة إلى اليسار في الجدول", + "rightInTableCell": "حرك خلية واحدة إلى اليمين في الجدول", + "upInTableCell": "التحرك لأعلى خلية واحدة في الجدول", + "downInTableCell": "التحرك لأسفل خلية واحدة في الجدول", + "tabInTableCell": "انتقل إلى الخلية التالية المتوفرة في الجدول", + "shiftTabInTableCell": "انتقل إلى الخلية المتوفرة سابقًا في الجدول", + "backSpaceInTableCell": "توقف في بداية الخلية" }, "commands": { - "codeBlockNewParagraph": "إدراج فقرة جديدة بجوار كتلة التعليمات البرمجية" + "codeBlockNewParagraph": "إدراج فقرة جديدة بجوار كتلة التعليمات البرمجية", + "textAlignLeft": "محاذاة النص إلى اليسار", + "textAlignCenter": "محاذاة النص إلى الوسط", + "textAlignRight": "محاذاة النص إلى اليمين" + }, + "couldNotLoadErrorMsg": "لم نتمكن من تحميل الاختصارات، حاول مرة أخرى", + "couldNotSaveErrorMsg": "لم نتمكن من حفظ الاختصارات، حاول مرة أخرى" + }, + "aiPage": { + "title": "إعدادات الذكاء الاصطناعي", + "menuLabel": "إعدادات الذكاء الاصطناعي", + "keys": { + "enableAISearchTitle": "بحث الذكاء الاصطناعي", + "aiSettingsDescription": "اختر النموذج المفضل لديك لتشغيل AppFlowy AI. يتضمن الآن GPT 4-o وClaude 3,5 وLlama 3.1 وMistral 7B", + "loginToEnableAIFeature": "لا يتم تمكين ميزات الذكاء الاصطناعي إلا بعد تسجيل الدخول باستخدام @:appName Cloud. إذا لم يكن لديك حساب @:appName ، فانتقل إلى \"حسابي\" للتسجيل", + "llmModel": "نموذج اللغة", + "llmModelType": "نوع نموذج اللغة", + "downloadLLMPrompt": "تنزيل {}", + "downloadAppFlowyOfflineAI": "سيؤدي تنزيل حزمة AI دون اتصال بالإنترنت إلى تفعيل تشغيل AI على جهازك. هل تريد الاستمرار؟", + "downloadLLMPromptDetail": " تنزيل النموذج المحلي {} سيستخدم ما يصل إلى {} من مساحة التخزين. هل تريد الاستمرار؟", + "downloadBigFilePrompt": "قد يستغرق الأمر حوالي 10 دقائق لإكمال التنزيل", + "downloadAIModelButton": "تنزيل", + "downloadingModel": "جاري التنزيل", + "localAILoaded": "تمت إضافة نموذج الذكاء الاصطناعي المحلي بنجاح وهو جاهز للاستخدام", + "localAIStart": "بدأت الدردشة المحلية بالذكاء الاصطناعي...", + "localAILoading": "جاري تحميل نموذج الدردشة المحلية للذكاء الاصطناعي...", + "localAIStopped": "تم إيقاف الذكاء الاصطناعي المحلي", + "failToLoadLocalAI": "فشل في بدء تشغيل الذكاء الاصطناعي المحلي", + "restartLocalAI": "إعادة تشغيل الذكاء الاصطناعي المحلي", + "disableLocalAITitle": "تعطيل الذكاء الاصطناعي المحلي", + "disableLocalAIDescription": "هل تريد تعطيل الذكاء الاصطناعي المحلي؟", + "localAIToggleTitle": "التبديل لتفعيل أو تعطيل الذكاء الاصطناعي المحلي", + "offlineAIInstruction1": "اتبع", + "offlineAIInstruction2": "تعليمات", + "offlineAIInstruction3": "لتفعيل الذكاء الاصطناعي دون اتصال بالإنترنت.", + "offlineAIDownload1": "إذا لم تقم بتنزيل AppFlowy AI، فيرجى", + "offlineAIDownload2": "التنزيل", + "activeOfflineAI": "نشط", + "downloadOfflineAI": "التنزيل", + "openModelDirectory": "افتح المجلد" + } + }, + "planPage": { + "menuLabel": "الخطة", + "title": "خطة التسعير", + "planUsage": { + "title": "ملخص استخدام الخطة", + "storageLabel": "تخزين", + "storageUsage": "{} من {} جيجا بايت", + "unlimitedStorageLabel": "تخزين غير محدود", + "collaboratorsLabel": "أعضاء", + "collaboratorsUsage": "{} ل {}", + "aiResponseLabel": "استجابات الذكاء الاصطناعي", + "aiResponseUsage": "{} ل {}", + "unlimitedAILabel": "استجابات غير محدودة", + "proBadge": "احترافية", + "aiMaxBadge": "الذكاء الاصطناعي ماكس", + "aiOnDeviceBadge": "الذكاء الاصطناعي على الجهاز لنظام التشغيل Mac", + "memberProToggle": "مزيد من الأعضاء وذكاء اصطناعي غير محدود", + "aiMaxToggle": "ذكاء اصطناعي غير محدود وإمكانية الوصول إلى نماذج متقدمة", + "aiOnDeviceToggle": "الذكاء الاصطناعي المحلي لتحقيق الخصوصية القصوى", + "aiCredit": { + "title": "أضف رصيد الذكاء الاصطناعي @:appName", + "price": "{}", + "priceDescription": "مقابل 1000 نقطة", + "purchase": "شراء الذكاء الاصطناعي", + "info": "أضف 1000 أرصدة الذكاء الاصطناعي لكل مساحة عمل وقم بدمج الذكاء الاصطناعي القابل للتخصيص بسلاسة في سير عملك للحصول على نتائج أذكى وأسرع مع ما يصل إلى:", + "infoItemOne": "10000 استجابة لكل قاعدة بيانات", + "infoItemTwo": "1000 استجابة لكل مساحة عمل" + }, + "currentPlan": { + "bannerLabel": "الخطة الحالية", + "freeTitle": "مجاني", + "proTitle": "محترف", + "teamTitle": "فريق", + "freeInfo": "مثالي للأفراد حتى عضوين لتنظيم كل شيء", + "proInfo": "مثالي للفرق الصغيرة والمتوسطة التي يصل عددها إلى 10 أعضاء.", + "teamInfo": "مثالي لجميع الفرق المنتجة والمنظمة جيدًا.", + "upgrade": "تغيير الخطة", + "canceledInfo": "لقد تم إلغاء خطتك، وسيتم تخفيض مستوى خطتك إلى الخطة المجانية في {}." + }, + "addons": { + "title": "مَرافِق", + "addLabel": "إضافة", + "activeLabel": "تمت الإضافة", + "aiMax": { + "title": "الحد الأقصى للذكاء الاصطناعي", + "description": "استجابات الذكاء الاصطناعي غير المحدودة مدعومة بـ GPT-4o وClaude 3.5 Sonnet والمزيد", + "price": "{}", + "priceInfo": "لكل مستخدم شهريًا يتم تحصيل الرسوم سنويًا" + }, + "aiOnDevice": { + "title": "الذكاء الاصطناعي على الجهاز لنظام التشغيل Mac", + "description": "قم بتشغيل Mistral 7B وLLAMA 3 والمزيد من النماذج المحلية على جهازك", + "price": "{}", + "priceInfo": "لكل مستخدم شهريًا يتم تحصيل الرسوم سنويًا", + "recommend": "يوصى باستخدام M1 أو أحدث" + } + }, + "deal": { + "bannerLabel": "صفقة العام الجديد!", + "title": "تنمية فريقك!", + "info": "قم بالترقية واحصل على خصم 10% على خطط Pro وTeam! عزز إنتاجية مساحة العمل لديك من خلال ميزات جديدة قوية بما في ذلك الذكاء الاصطناعي @:appName .", + "viewPlans": "عرض الخطط" + } + } + }, + "billingPage": { + "menuLabel": "الفوترة", + "title": "الفوترة", + "plan": { + "title": "الخطة", + "proLabel": "مجاني" + }, + "addons": { + "aiMax": { + "canceledDescription": "سيكون AI Max متوفرا حتى {}" + }, + "aiOnDevice": { + "canceledDescription": "ستتوفر ميزة AI On-device for Mac حتى {}" + } } }, "comparePlanDialog": { @@ -1084,7 +1270,10 @@ "copy": "إنسخ الرابط" }, "menuName": "شبكة", - "referencedGridPrefix": "نظرا ل" + "referencedGridPrefix": "نظرا ل", + "media": { + "downloadFailedToken": "فشل تنزيل الملف، الرمز المميز للمستخدم غير متوفر" + } }, "document": { "menuName": "وثيقة", @@ -1261,8 +1450,12 @@ "codeBlock": { "language": { "label": "لغة", - "placeholder": "اختار اللغة" - } + "placeholder": "اختار اللغة", + "auto": "آلي" + }, + "copyTooltip": "نسخ", + "searchLanguageHint": "ابحث عن لغة", + "codeCopiedSnackbar": "تم نسخ الكود إلى الحافظة!" }, "inlineLink": { "placeholder": "الصق أو اكتب ارتباطًا", @@ -1603,7 +1796,8 @@ "rowDuplicate": "نسخة طبق الاصل", "colClear": "مسح المحتوى", "rowClear": "مسح المحتوى", - "slashPlaceHolder": "اكتب \"/\" لإدراج كتلة، أو ابدأ الكتابة" + "slashPlaceHolder": "اكتب \"/\" لإدراج كتلة، أو ابدأ الكتابة", + "codeBlockShortForm": "الكود" }, "favorite": { "noFavorite": "لا توجد صفحة مفضلة", @@ -1634,4 +1828,4 @@ "gallery": { "resetZoom": "إعادة ضبط التكبير" } -} \ No newline at end of file +} diff --git a/frontend/resources/translations/ca-ES.json b/frontend/resources/translations/ca-ES.json index 1e427411aa..f388cf9bd5 100644 --- a/frontend/resources/translations/ca-ES.json +++ b/frontend/resources/translations/ca-ES.json @@ -811,4 +811,4 @@ "deleteContentTitle": "Esteu segur que voleu suprimir {pageType}?", "deleteContentCaption": "si suprimiu aquest {pageType}, podeu restaurar-lo des de la paperera." } -} \ No newline at end of file +} diff --git a/frontend/resources/translations/ckb-KU.json b/frontend/resources/translations/ckb-KU.json index 841546f901..4acb7a1765 100644 --- a/frontend/resources/translations/ckb-KU.json +++ b/frontend/resources/translations/ckb-KU.json @@ -941,4 +941,4 @@ "frequentlyUsed": "زۆرجار بەکارت هێناوە" } } -} \ No newline at end of file +} diff --git a/frontend/resources/translations/cs-CZ.json b/frontend/resources/translations/cs-CZ.json index de895531f9..07e5a01bea 100644 --- a/frontend/resources/translations/cs-CZ.json +++ b/frontend/resources/translations/cs-CZ.json @@ -1094,4 +1094,4 @@ "font": "Písmo", "actions": "Příkazy" } -} \ No newline at end of file +} diff --git a/frontend/resources/translations/de-DE.json b/frontend/resources/translations/de-DE.json index 34b02063c9..34b7afde2a 100644 --- a/frontend/resources/translations/de-DE.json +++ b/frontend/resources/translations/de-DE.json @@ -2866,4 +2866,4 @@ "permissionDenied": "Keine Berechtigung zum Öffnen dieser Datei", "unknownError": "Öffnen der Datei fehlgeschlagen" } -} \ No newline at end of file +} diff --git a/frontend/resources/translations/es-VE.json b/frontend/resources/translations/es-VE.json index 4936844535..6b61b3c2a6 100644 --- a/frontend/resources/translations/es-VE.json +++ b/frontend/resources/translations/es-VE.json @@ -1547,4 +1547,4 @@ "betaTooltip": "Actualmente solo admitimos la búsqueda de páginas.", "fromTrashHint": "De la papelera" } -} \ No newline at end of file +} diff --git a/frontend/resources/translations/eu-ES.json b/frontend/resources/translations/eu-ES.json index fe153883c4..be987e7a53 100644 --- a/frontend/resources/translations/eu-ES.json +++ b/frontend/resources/translations/eu-ES.json @@ -600,4 +600,4 @@ "deleteContentTitle": "Ziur {pageType} ezabatu nahi duzula?", "deleteContentCaption": "{pageType} hau ezabatzen baduzu, zaborrontzitik leheneratu dezakezu." } -} \ No newline at end of file +} diff --git a/frontend/resources/translations/fa.json b/frontend/resources/translations/fa.json index 5baa50e8dd..21c8505c88 100644 --- a/frontend/resources/translations/fa.json +++ b/frontend/resources/translations/fa.json @@ -674,4 +674,4 @@ "frequentlyUsed": "استفاده‌شده" } } -} \ No newline at end of file +} diff --git a/frontend/resources/translations/fr-CA.json b/frontend/resources/translations/fr-CA.json index c3411ef3ef..a3396724aa 100644 --- a/frontend/resources/translations/fr-CA.json +++ b/frontend/resources/translations/fr-CA.json @@ -1256,4 +1256,4 @@ "userIcon": "Icône utilisateur" }, "noLogFiles": "Il n'y a pas de log" -} \ No newline at end of file +} diff --git a/frontend/resources/translations/fr-FR.json b/frontend/resources/translations/fr-FR.json index b21f11d442..90671a9f12 100644 --- a/frontend/resources/translations/fr-FR.json +++ b/frontend/resources/translations/fr-FR.json @@ -1367,7 +1367,6 @@ "typeAValue": "Tapez une valeur...", "layout": "Mise en page", "databaseLayout": "Mise en page", - "viewList": "Vues de base de données", "editView": "Modifier vue", "boardSettings": "Paramètres du tableau", "calendarSettings": "Paramètres du calendrier", @@ -1375,7 +1374,8 @@ "duplicateView": "Dupliquer la vue", "deleteView": "Supprimer la vue", "numberOfVisibleFields": "{} affiché(s)", - "Properties": "Propriétés" + "Properties": "Propriétés", + "viewList": "Vues de base de données" }, "filter": { "empty": "Aucun filtre actif", @@ -2148,11 +2148,11 @@ "layoutDateField": "Calendrier de mise en page par", "changeLayoutDateField": "Modifier le champ de mise en page", "noDateTitle": "Pas de date", - "noDateHint": "Les événements non planifiés s'afficheront ici", "unscheduledEventsTitle": "Événements non planifiés", "clickToAdd": "Cliquez pour ajouter au calendrier", "name": "Disposition du calendrier", - "clickToOpen": "Cliquez pour ouvrir l'évènement" + "clickToOpen": "Cliquez pour ouvrir l'évènement", + "noDateHint": "Les événements non planifiés s'afficheront ici" }, "referencedCalendarPrefix": "Vue", "quickJumpYear": "Sauter à", @@ -2945,4 +2945,4 @@ "permissionDenied": "Aucune autorisation d'ouvrir ce fichier", "unknownError": "Échec de l'ouverture du fichier" } -} \ No newline at end of file +} diff --git a/frontend/resources/translations/he.json b/frontend/resources/translations/he.json index 9894e5efaa..d47c33c6da 100644 --- a/frontend/resources/translations/he.json +++ b/frontend/resources/translations/he.json @@ -2081,4 +2081,4 @@ "signInError": "שגיאת כניסה", "login": "הרשמה או כניסה" } -} \ No newline at end of file +} diff --git a/frontend/resources/translations/hu-HU.json b/frontend/resources/translations/hu-HU.json index c747f7fbb9..40f05cccc6 100644 --- a/frontend/resources/translations/hu-HU.json +++ b/frontend/resources/translations/hu-HU.json @@ -598,4 +598,4 @@ "deleteContentTitle": "Biztosan törli a következőt: {pageType}?", "deleteContentCaption": "ha törli ezt a {pageType} oldalt, visszaállíthatja a kukából." } -} \ No newline at end of file +} diff --git a/frontend/resources/translations/id-ID.json b/frontend/resources/translations/id-ID.json index c4a3d324c8..ede3571878 100644 --- a/frontend/resources/translations/id-ID.json +++ b/frontend/resources/translations/id-ID.json @@ -1044,4 +1044,4 @@ "noFavorite": "Tidak ada halaman favorit", "noFavoriteHintText": "Geser halaman ke kiri untuk menambahkannya ke favorit Anda" } -} \ No newline at end of file +} diff --git a/frontend/resources/translations/it-IT.json b/frontend/resources/translations/it-IT.json index 3707e71753..d6877ecd59 100644 --- a/frontend/resources/translations/it-IT.json +++ b/frontend/resources/translations/it-IT.json @@ -1366,4 +1366,4 @@ "userIcon": "Icona utente" }, "noLogFiles": "Non ci sono file di log" -} \ No newline at end of file +} diff --git a/frontend/resources/translations/ko-KR.json b/frontend/resources/translations/ko-KR.json index dbd9f81120..8f145cbcbe 100644 --- a/frontend/resources/translations/ko-KR.json +++ b/frontend/resources/translations/ko-KR.json @@ -605,4 +605,4 @@ "deleteContentTitle": "{pageType}을(를) 삭제하시겠습니까?", "deleteContentCaption": "이 {pageType}을(를) 삭제하면 휴지통에서 복원할 수 있습니다." } -} \ No newline at end of file +} diff --git a/frontend/resources/translations/pl-PL.json b/frontend/resources/translations/pl-PL.json index cc56e634ae..e3ca580354 100644 --- a/frontend/resources/translations/pl-PL.json +++ b/frontend/resources/translations/pl-PL.json @@ -1145,4 +1145,4 @@ "language": "Język", "font": "Czcionka" } -} \ No newline at end of file +} diff --git a/frontend/resources/translations/pt-BR.json b/frontend/resources/translations/pt-BR.json index 538d09d5ea..51b585f14b 100644 --- a/frontend/resources/translations/pt-BR.json +++ b/frontend/resources/translations/pt-BR.json @@ -1544,4 +1544,4 @@ "addField": "Adicionar campo", "userIcon": "Ícone do usuário" } -} \ No newline at end of file +} diff --git a/frontend/resources/translations/pt-PT.json b/frontend/resources/translations/pt-PT.json index 5fc806e07f..617e097f6b 100644 --- a/frontend/resources/translations/pt-PT.json +++ b/frontend/resources/translations/pt-PT.json @@ -856,4 +856,4 @@ "noResult": "Nenhum resultado", "caseSensitive": "Maiúsculas e minúsculas" } -} \ No newline at end of file +} diff --git a/frontend/resources/translations/ru-RU.json b/frontend/resources/translations/ru-RU.json index bcdf45a8d2..68a3fbfd9e 100644 --- a/frontend/resources/translations/ru-RU.json +++ b/frontend/resources/translations/ru-RU.json @@ -2184,4 +2184,4 @@ "signInError": "Ошибка входа", "login": "Зарегистрироваться или войти" } -} \ No newline at end of file +} diff --git a/frontend/resources/translations/sv-SE.json b/frontend/resources/translations/sv-SE.json index 4c2d625946..42855011b2 100644 --- a/frontend/resources/translations/sv-SE.json +++ b/frontend/resources/translations/sv-SE.json @@ -662,4 +662,4 @@ "deleteContentTitle": "Är du säker på att du vill ta bort {pageType}?", "deleteContentCaption": "om du tar bort denna {pageType} kan du återställa den från papperskorgen." } -} \ No newline at end of file +} diff --git a/frontend/resources/translations/tr-TR.json b/frontend/resources/translations/tr-TR.json index 3984282364..0764181ab0 100644 --- a/frontend/resources/translations/tr-TR.json +++ b/frontend/resources/translations/tr-TR.json @@ -2295,4 +2295,4 @@ "signInError": "Oturum açma hatası", "login": "Kaydolun veya giriş yapın" } -} \ No newline at end of file +} diff --git a/frontend/resources/translations/uk-UA.json b/frontend/resources/translations/uk-UA.json index c0c647f975..7f354bd892 100644 --- a/frontend/resources/translations/uk-UA.json +++ b/frontend/resources/translations/uk-UA.json @@ -2587,4 +2587,4 @@ "uploadFailedDescription": "Не вдалося завантажити файл", "uploadingDescription": "Файл завантажується" } -} \ No newline at end of file +} diff --git a/frontend/resources/translations/vi-VN.json b/frontend/resources/translations/vi-VN.json index aa80386326..22d3027636 100644 --- a/frontend/resources/translations/vi-VN.json +++ b/frontend/resources/translations/vi-VN.json @@ -861,9 +861,8 @@ "itemThree": "5 GB", "itemFour": "Đúng", "itemFive": "Đúng", - "itemFileUpload": "Lên đến 7 MB", "itemSix": "20 trọn đời", - + "itemFileUpload": "Lên đến 7 MB", "intelligentSearch": "Tìm kiếm thông minh" }, "proLabels": { @@ -1229,7 +1228,6 @@ "typeAValue": "Nhập một giá trị...", "layout": "Bố cục", "databaseLayout": "Bố cục", - "viewList": "Database Views", "editView": "Chỉnh sửa chế độ xem", "boardSettings": "Cài đặt bảng", "calendarSettings": "Cài đặt lịch", @@ -1237,7 +1235,8 @@ "duplicateView": "Xem trùng lặp", "deleteView": "Xóa chế độ xem", "numberOfVisibleFields": "{} đã hiển thị", - "Properties": "Thuộc tính" + "Properties": "Thuộc tính", + "viewList": "Database Views" }, "textFilter": { "contains": "Chứa", @@ -1473,20 +1472,20 @@ "media": { "rename": "Đổi tên", "download": "Tải về", - "open": "Mở", "delete": "Xóa bỏ", "moreFilesHint": "+{}", "addFileOrImage": "Thêm tệp, hình ảnh hoặc liên kết", "attachmentsHint": "{}", "addFileMobile": "Thêm tập tin", + "deleteFileDescription": "Bạn có chắc chắn muốn xóa tệp này không? Hành động này không thể hoàn tác.", + "downloadSuccess": "Đã lưu tập tin thành công", + "downloadFailedToken": "Không tải được tệp, mã thông báo người dùng không khả dụng", + "open": "Mở", + "hideFileNames": "Ẩn tên tập tin", "showFile": "Hiển thị 1 tập tin", "showFiles": "Hiển thị {} tập tin", "hideFile": "Ẩn 1 tập tin", - "hideFiles": "Ẩn {} tập tin", - "deleteFileDescription": "Bạn có chắc chắn muốn xóa tệp này không? Hành động này không thể hoàn tác.", - "hideFileNames": "Ẩn tên tập tin", - "downloadSuccess": "Đã lưu tập tin thành công", - "downloadFailedToken": "Không tải được tệp, mã thông báo người dùng không khả dụng" + "hideFiles": "Ẩn {} tập tin" } }, "document": { @@ -2567,4 +2566,4 @@ "zoomIn": "Phóng to", "zoomOut": "Thu nhỏ" } -} \ No newline at end of file +} diff --git a/frontend/resources/translations/vi.json b/frontend/resources/translations/vi.json index 4d1716447a..b921c1844e 100644 --- a/frontend/resources/translations/vi.json +++ b/frontend/resources/translations/vi.json @@ -6,4 +6,4 @@ "failedToLoad": "Không tải được chế độ xem bảng" } } -} \ No newline at end of file +} diff --git a/frontend/resources/translations/zh-CN.json b/frontend/resources/translations/zh-CN.json index d8cfc88e2a..d7e9b70dac 100644 --- a/frontend/resources/translations/zh-CN.json +++ b/frontend/resources/translations/zh-CN.json @@ -1889,4 +1889,4 @@ "yesterday": "昨天", "today": "今天" } -} \ No newline at end of file +} diff --git a/frontend/resources/translations/zh-TW.json b/frontend/resources/translations/zh-TW.json index 55da2ee917..34f86cd383 100644 --- a/frontend/resources/translations/zh-TW.json +++ b/frontend/resources/translations/zh-TW.json @@ -1545,4 +1545,4 @@ "betaLabel": "BETA", "betaTooltip": "目前我們只支援搜尋頁面" } -} \ No newline at end of file +} From 11d720465b58cae7279ab171e66c129ead40929e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dar=C3=ADo=20Here=C3=B1=C3=BA?= Date: Tue, 31 Dec 2024 00:04:37 -0300 Subject: [PATCH 107/576] chore: update es-VE translations (#3645) Co-authored-by: Lucas.Xu --- frontend/resources/translations/es-VE.json | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/frontend/resources/translations/es-VE.json b/frontend/resources/translations/es-VE.json index 6b61b3c2a6..9ecbff9c1d 100644 --- a/frontend/resources/translations/es-VE.json +++ b/frontend/resources/translations/es-VE.json @@ -5,7 +5,7 @@ "welcomeTo": "Bienvenido a", "githubStarText": "Favorito en GitHub", "subscribeNewsletterText": "Suscribir al boletín", - "letsGoButtonText": "Vamos", + "letsGoButtonText": "Inicio rápido", "title": "Título", "youCanAlso": "Tú también puedes", "and": "y", @@ -19,17 +19,17 @@ "openMenuTooltip": "Haga clic para abrir el menú" }, "signUp": { - "buttonText": "Registrar", - "title": "Registrar en @:appName", + "buttonText": "Registro", + "title": "Registro en @:appName", "getStartedText": "Empezar", "emptyPasswordError": "La contraseña no puede estar en blanco", - "repeatPasswordEmptyError": "La contraseña no puede estar en blanco", + "repeatPasswordEmptyError": "La contraseña repetida no puede estar vacía", "unmatchedPasswordError": "Las contraseñas no coinciden", - "alreadyHaveAnAccount": "¿Posee credenciales?", - "emailHint": "Correo", + "alreadyHaveAnAccount": "¿Ya posee una cuenta?", + "emailHint": "Correo electrónico", "passwordHint": "Contraseña", "repeatPasswordHint": "Repetir contraseña", - "signUpWith": "Registrarte con:" + "signUpWith": "Registro con:" }, "signIn": { "loginTitle": "Ingresa a @:appName", @@ -113,7 +113,7 @@ "large": "grande", "fontSize": "Tamaño de fuente", "import": "Importar", - "moreOptions": "Mas opciones", + "moreOptions": "Más opciones", "wordCount": "El recuento de palabras: {}", "charCount": "Número de caracteres : {}", "createdAt": "Creado: {}", @@ -121,7 +121,7 @@ "duplicateView": "Duplicar" }, "importPanel": { - "textAndMarkdown": "Texto y descuento", + "textAndMarkdown": "Texto y Markdown", "documentFromV010": "Documento de v0.1.0", "databaseFromV010": "Base de datos desde v0.1.0", "csv": "CSV", From b2ca5c77f697f3554b1f15aa130b7ad7e656f414 Mon Sep 17 00:00:00 2001 From: Lucas Date: Tue, 31 Dec 2024 12:03:44 +0800 Subject: [PATCH 108/576] fix: simple tests on mobile (#7102) * fix: simple tests on mobile * fix: subpage block padding --- .../mobile/document/simple_table_test.dart | 24 +++++++--- frontend/appflowy_flutter/ios/Podfile.lock | 46 +++++++++---------- .../presentation/editor_configuration.dart | 13 +++++- .../math_equation_block_component.dart | 10 ++-- .../sub_page/sub_page_block_component.dart | 7 +++ frontend/appflowy_flutter/macos/Podfile.lock | 42 ++++++++--------- 6 files changed, 85 insertions(+), 57 deletions(-) diff --git a/frontend/appflowy_flutter/integration_test/mobile/document/simple_table_test.dart b/frontend/appflowy_flutter/integration_test/mobile/document/simple_table_test.dart index fcd5494218..d6ffc5d57f 100644 --- a/frontend/appflowy_flutter/integration_test/mobile/document/simple_table_test.dart +++ b/frontend/appflowy_flutter/integration_test/mobile/document/simple_table_test.dart @@ -4,6 +4,7 @@ import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_table/simple_table.dart'; import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; @@ -371,13 +372,22 @@ void main() { // click the column menu button await tester.clickColumnMenuButton(0); - // clear content - await tester.tapButton( - find.findTextInFlowyText( - LocaleKeys.document_plugins_simpleTable_moreActions_clearContents - .tr(), - ), + final clearContents = find.findTextInFlowyText( + LocaleKeys.document_plugins_simpleTable_moreActions_clearContents + .tr(), ); + + // clear content + final scrollable = find.descendant( + of: find.byType(SimpleTableBottomSheet), + matching: find.byType(Scrollable), + ); + await tester.scrollUntilVisible( + clearContents, + 100, + scrollable: scrollable, + ); + await tester.tapButton(clearContents); await tester.cancelTableActionMenu(); // check the first cell is empty @@ -427,7 +437,7 @@ void main() { // open the plus menu and select the heading block { await tester.openPlusMenuAndClickButton( - LocaleKeys.editor_toggleHeading1ShortForm.tr(), + LocaleKeys.editor_heading1.tr(), ); // check the heading block is inserted diff --git a/frontend/appflowy_flutter/ios/Podfile.lock b/frontend/appflowy_flutter/ios/Podfile.lock index ac6f338698..69bb7b20cf 100644 --- a/frontend/appflowy_flutter/ios/Podfile.lock +++ b/frontend/appflowy_flutter/ios/Podfile.lock @@ -175,37 +175,37 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/webview_flutter_wkwebview/ios" SPEC CHECKSUMS: - app_links: e70ca16b4b0f88253b3b3660200d4a10b4ea9795 - appflowy_backend: 144c20d8bfb298c4e10fa3fa6701a9f41bf98b88 - connectivity_plus: bf0076dd84a130856aa636df1c71ccaff908fa1d - device_info_plus: 97af1d7e84681a90d0693e63169a5d50e0839a0d + app_links: c5161ac5ab5383ad046884568b4b91cb52df5d91 + appflowy_backend: 78f6a053f756e6bc29bcc5a2106cbe77b756e97a + connectivity_plus: 481668c94744c30c53b8895afb39159d1e619bdf + device_info_plus: 71ffc6ab7634ade6267c7a93088ed7e4f74e5896 DKImagePickerController: b512c28220a2b8ac7419f21c491fc8534b7601ac DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179 - file_picker: 09aa5ec1ab24135ccd7a1621c46c84134bfd6655 - flowy_infra_ui: 0455e1fa8c51885aa1437848e361e99419f34ebc + file_picker: 9b3292d7c8bc68c8a7bf8eb78f730e49c8efc517 + flowy_infra_ui: 931b73a18b54a392ab6152eebe29a63a30751f53 Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 - fluttertoast: e9a18c7be5413da53898f660530c56f35edfba9c - image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1 - integration_test: ce0a3ffa1de96d1a89ca0ac26fca7ea18a749ef4 - irondash_engine_context: 3458bf979b90d616ffb8ae03a150bafe2e860cc9 - keyboard_height_plugin: 43fa8bba20fd5c4fdeed5076466b8b9d43cc6b86 - open_filex: 6e26e659846ec990262224a12ef1c528bb4edbe4 - package_info_plus: 58f0028419748fad15bf008b270aaa8e54380b1c - path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 - permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2 + fluttertoast: 76fea30fcf04176325f6864c87306927bd7d2038 + image_picker_ios: 7fe1ff8e34c1790d6fff70a32484959f563a928a + integration_test: d5929033778cc4991a187e4e1a85396fa4f59b3a + irondash_engine_context: 8e58ca8e0212ee9d1c7dc6a42121849986c88486 + keyboard_height_plugin: ef70a8181b29f27670e9e2450814ca6b6dc05b05 + open_filex: 432f3cd11432da3e39f47fcc0df2b1603854eff1 + package_info_plus: 580e9a5f1b6ca5594e7c9ed5f92d1dfb2a66b5e1 + path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564 + permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825 SDWebImage: b9a731e1d6307f44ca703b3976d18c24ca561e84 Sentry: 1fe34e9c2cbba1e347623610d26db121dcb569f1 - sentry_flutter: a39c2a2d67d5e5b9cb0b94a4985c76dd5b3fc737 - share_plus: 8875f4f2500512ea181eef553c3e27dba5135aad - shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 - sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec - super_native_extensions: 4916b3c627a9c7fffdc48a23a9eca0b1ac228fa7 + sentry_flutter: e24b397f9a61fa5bbefd8279c3b2242ca86faa90 + share_plus: 011d6fb4f9d2576b83179a3a5c5e323202cdabcf + shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7 + sqflite: c35dad70033b8862124f8337cc994a809fcd9fa3 + super_native_extensions: b763c02dc3a8fd078389f410bf15149179020cb4 SwiftyGif: 6c3eafd0ce693cad58bb63d2b2fb9bacb8552780 Toast: 91b396c56ee72a5790816f40d3a94dd357abc196 - url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe - webview_flutter_wkwebview: 2a23822e9039b7b1bc52e5add778e5d89ad488d1 + url_launcher_ios: 694010445543906933d732453a59da0a173ae33d + webview_flutter_wkwebview: 45a041c7831641076618876de3ba75c712860c6b PODFILE CHECKSUM: d0d9b4ff572d8695c38eb3f9b490f55cdfc57eca -COCOAPODS: 1.15.2 +COCOAPODS: 1.16.2 diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_configuration.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_configuration.dart index 1d10c1f161..aa4756cb42 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_configuration.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_configuration.dart @@ -670,7 +670,12 @@ DatabaseViewBlockComponentBuilder _buildDatabaseViewBlockComponentBuilder( ) { return DatabaseViewBlockComponentBuilder( configuration: configuration.copyWith( - padding: (_) => const EdgeInsets.symmetric(vertical: 10), + padding: (node) { + if (UniversalPlatform.isMobile) { + return configuration.padding(node); + } + return const EdgeInsets.symmetric(vertical: 10); + }, ), ); } @@ -873,6 +878,12 @@ SubPageBlockComponentBuilder _buildSubPageBlockComponentBuilder( return SubPageBlockComponentBuilder( configuration: configuration.copyWith( textStyle: (node) => styleCustomizer.subPageBlockTextStyleBuilder(), + padding: (node) { + if (UniversalPlatform.isMobile) { + return const EdgeInsets.symmetric(horizontal: 18); + } + return configuration.padding(node); + }, ), ); } diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/math_equation/math_equation_block_component.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/math_equation/math_equation_block_component.dart index 286eac3d75..e9d6b3297e 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/math_equation/math_equation_block_component.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/math_equation/math_equation_block_component.dart @@ -153,11 +153,6 @@ class MathEquationBlockComponentWidgetState ), ); - child = Padding( - padding: padding, - child: child, - ); - if (widget.showActions && widget.actionBuilder != null) { child = BlockComponentActionWrapper( node: node, @@ -174,6 +169,11 @@ class MathEquationBlockComponentWidgetState ); } + child = Padding( + padding: padding, + child: child, + ); + if (UniversalPlatform.isDesktopOrWeb) { child = Stack( children: [ diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/sub_page/sub_page_block_component.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/sub_page/sub_page_block_component.dart index 4b8550570a..c2b975ec05 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/sub_page/sub_page_block_component.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/sub_page/sub_page_block_component.dart @@ -299,6 +299,13 @@ class SubPageBlockComponentState extends State ); } + if (UniversalPlatform.isMobile) { + child = Padding( + padding: padding, + child: child, + ); + } + return child; }, ); diff --git a/frontend/appflowy_flutter/macos/Podfile.lock b/frontend/appflowy_flutter/macos/Podfile.lock index d5ac7975b9..2d9b24cdaa 100644 --- a/frontend/appflowy_flutter/macos/Podfile.lock +++ b/frontend/appflowy_flutter/macos/Podfile.lock @@ -130,31 +130,31 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral/.symlinks/plugins/window_manager/macos SPEC CHECKSUMS: - app_links: 10e0a0ab602ffaf34d142cd4862f29d34b303b2a - appflowy_backend: 865496343de667fc8c600e04b9fd05234e130cf9 - bitsdojo_window_macos: 44e3b8fe3dd463820e0321f6256c5b1c16bb6a00 - connectivity_plus: 18d3c32514c886e046de60e9c13895109866c747 - desktop_drop: 69eeff437544aa619c8db7f4481b3a65f7696898 - device_info_plus: ce1b7762849d3ec103d0e0517299f2db7ad60720 - file_selector_macos: 54fdab7caa3ac3fc43c9fac4d7d8d231277f8cf2 - flowy_infra_ui: 03301a39ad118771adbf051a664265c61c507f38 + app_links: 9028728e32c83a0831d9db8cf91c526d16cc5468 + appflowy_backend: 464aeb3e5c6966a41641a2111e5ead72ce2695f7 + bitsdojo_window_macos: 7959fb0ca65a3ccda30095c181ecb856fae48ea9 + connectivity_plus: e74b9f74717d2d99d45751750e266e55912baeb5 + desktop_drop: e0b672a7d84c0a6cbc378595e82cdb15f2970a43 + device_info_plus: a56e6e74dbbd2bb92f2da12c64ddd4f67a749041 + file_selector_macos: 585232b688707857504f9cb5f985a7c97fe4dd30 + flowy_infra_ui: 8760ff42a789de40bf5007a5f176b454722a341e FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 HotKey: e96d8a2ddbf4591131e2bb3f54e69554d90cdca6 - hotkey_manager: c32bf0bfe8f934b7bc17ab4ad5c4c142960b023c - irondash_engine_context: da62996ee25616d2f01bbeb85dc115d813359478 - local_notifier: e9506bc66fc70311e8bc7291fb70f743c081e4ff - package_info_plus: fa739dd842b393193c5ca93c26798dff6e3d0e0c - path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 + hotkey_manager: b443f35f4d772162937aa73fd8995e579f8ac4e2 + irondash_engine_context: 893c7d96d20ce361d7e996f39d360c4c2f9869ba + local_notifier: ebf072651e35ae5e47280ad52e2707375cb2ae4e + package_info_plus: a8a591e70e87ce97ce5d21b2594f69cea9e0312f + path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564 ReachabilitySwift: 7f151ff156cea1481a8411701195ac6a984f4979 - screen_retriever: 59634572a57080243dd1bf715e55b6c54f241a38 + screen_retriever: 4f97c103641aab8ce183fa5af3b87029df167936 Sentry: 1fe34e9c2cbba1e347623610d26db121dcb569f1 - sentry_flutter: a39c2a2d67d5e5b9cb0b94a4985c76dd5b3fc737 - share_plus: 36537c04ce0c3e3f5bd297ce4318b6d5ee5fd6cf - shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 - sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec - super_native_extensions: 85efee3a7495b46b04befcfc86ed12069264ebf3 - url_launcher_macos: 5f437abeda8c85500ceb03f5c1938a8c5a705399 - window_manager: 3a1844359a6295ab1e47659b1a777e36773cd6e8 + sentry_flutter: e24b397f9a61fa5bbefd8279c3b2242ca86faa90 + share_plus: 11c7b7fa7020465584eca3ff6392c5bc1e399d6e + shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7 + sqflite: c35dad70033b8862124f8337cc994a809fcd9fa3 + super_native_extensions: c2795d6d9aedf4a79fae25cb6160b71b50549189 + url_launcher_macos: de10e46d8d8b9e3a7b8a133e8de92b104379f05e + window_manager: 1d01fa7ac65a6e6f83b965471b1a7fdd3f06166c PODFILE CHECKSUM: 0532f3f001ca3110b8be345d6491fff690e95823 From 7dedb84504db2ca8ac99abfd80d389c26a1ac902 Mon Sep 17 00:00:00 2001 From: Ahad Patel <69256193+Ahad-patel@users.noreply.github.com> Date: Tue, 31 Dec 2024 13:24:02 +0530 Subject: [PATCH 109/576] feat: i18n for duplicated field names (#6769) * feat: i18n for duplicated field names * fix: flutter analyze --------- Co-authored-by: Lucas.Xu --- .../lib/workspace/application/view/view_bloc.dart | 3 +++ frontend/appflowy_flutter/pubspec.lock | 4 ++-- frontend/resources/translations/en.json | 3 ++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/frontend/appflowy_flutter/lib/workspace/application/view/view_bloc.dart b/frontend/appflowy_flutter/lib/workspace/application/view/view_bloc.dart index 30a32bbd2d..c31e6f0e06 100644 --- a/frontend/appflowy_flutter/lib/workspace/application/view/view_bloc.dart +++ b/frontend/appflowy_flutter/lib/workspace/application/view/view_bloc.dart @@ -3,6 +3,7 @@ import 'dart:convert'; import 'package:appflowy/core/config/kv.dart'; import 'package:appflowy/core/config/kv_keys.dart'; +import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/shared/icon_emoji_picker/flowy_icon_emoji_picker.dart'; import 'package:appflowy/startup/startup.dart'; import 'package:appflowy/workspace/application/favorite/favorite_listener.dart'; @@ -15,6 +16,7 @@ import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart'; import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart'; import 'package:appflowy_result/appflowy_result.dart'; import 'package:collection/collection.dart'; +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:protobuf/protobuf.dart'; @@ -174,6 +176,7 @@ class ViewBloc extends Bloc { openAfterDuplicate: true, syncAfterDuplicate: true, includeChildren: true, + suffix: ' (${LocaleKeys.menuAppHeader_pageNameSuffix.tr()})', ); emit( result.fold( diff --git a/frontend/appflowy_flutter/pubspec.lock b/frontend/appflowy_flutter/pubspec.lock index 8936824930..1327e1dd41 100644 --- a/frontend/appflowy_flutter/pubspec.lock +++ b/frontend/appflowy_flutter/pubspec.lock @@ -318,10 +318,10 @@ packages: dependency: transitive description: name: charcode - sha256: fb0f1107cac15a5ea6ef0a6ef71a807b9e4267c713bb93e00e92d737cc8dbd8a + sha256: fb98c0f6d12c920a02ee2d998da788bca066ca5f148492b7085ee23372b12306 url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.3.1" checked_yaml: dependency: transitive description: diff --git a/frontend/resources/translations/en.json b/frontend/resources/translations/en.json index bec922f153..cf5422764b 100644 --- a/frontend/resources/translations/en.json +++ b/frontend/resources/translations/en.json @@ -276,7 +276,8 @@ "moreButtonToolTip": "Remove, rename, and more...", "addPageTooltip": "Quickly add a page inside", "defaultNewPageName": "Untitled", - "renameDialog": "Rename" + "renameDialog": "Rename", + "pageNameSuffix": "Copy" }, "noPagesInside": "No pages inside", "toolbar": { From 8f7cb50dd44c3d9c1a715c2059b775daa0480ea0 Mon Sep 17 00:00:00 2001 From: Lucas Date: Tue, 31 Dec 2024 16:01:45 +0800 Subject: [PATCH 110/576] fix: simple table issues on mobile (#7115) * fix: header row/column tap areas are too small on mobile * test: header row/column tap areas are too small on mobile * feat: enable auto scroll after inserting column or row * fix: enter after emoji will create a softbreak on mobile * fix: header row/column tap areas are too small on mobile * fix: simple table alignment not work for item that wraps * test: simple table alignment not work for item that wraps --- .../mobile/document/simple_table_test.dart | 50 +++++++++- .../simple_table/simple_table_constants.dart | 3 + .../simple_table_style_operation.dart | 67 +++++++++++++ .../_desktop_simple_table_widget.dart | 8 ++ .../_mobile_simple_table_widget.dart | 8 ++ .../_simple_table_bottom_sheet_actions.dart | 96 ++++++++++++------- frontend/appflowy_flutter/pubspec.lock | 4 +- frontend/appflowy_flutter/pubspec.yaml | 2 +- .../simple_table_style_operation_test.dart | 41 ++++++++ 9 files changed, 239 insertions(+), 40 deletions(-) diff --git a/frontend/appflowy_flutter/integration_test/mobile/document/simple_table_test.dart b/frontend/appflowy_flutter/integration_test/mobile/document/simple_table_test.dart index d6ffc5d57f..9fa8be3a9c 100644 --- a/frontend/appflowy_flutter/integration_test/mobile/document/simple_table_test.dart +++ b/frontend/appflowy_flutter/integration_test/mobile/document/simple_table_test.dart @@ -2,9 +2,10 @@ import 'dart:async'; import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_table/simple_table.dart'; +import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/_simple_table_bottom_sheet_actions.dart'; import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:easy_localization/easy_localization.dart'; -import 'package:flutter/material.dart'; +import 'package:flutter/cupertino.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; @@ -244,6 +245,53 @@ void main() { expect(table.isHeaderColumnEnabled, isTrue); expect(table.isHeaderRowEnabled, isTrue); + // disable header column + { + // focus on the first cell + unawaited( + editorState.updateSelectionWithReason( + Selection.collapsed(Position(path: firstParagraphPath)), + reason: SelectionUpdateReason.uiEvent, + ), + ); + await tester.pumpAndSettle(); + + // click the row menu button + await tester.clickColumnMenuButton(0); + + final toggleButton = find.descendant( + of: find.byType(SimpleTableHeaderActionButton), + matching: find.byType(CupertinoSwitch), + ); + await tester.tapButton(toggleButton); + } + + // enable header row + { + // focus on the first cell + unawaited( + editorState.updateSelectionWithReason( + Selection.collapsed(Position(path: firstParagraphPath)), + reason: SelectionUpdateReason.uiEvent, + ), + ); + await tester.pumpAndSettle(); + + // click the row menu button + await tester.clickRowMenuButton(0); + + // enable header column + final toggleButton = find.descendant( + of: find.byType(SimpleTableHeaderActionButton), + matching: find.byType(CupertinoSwitch), + ); + await tester.tapButton(toggleButton); + } + + // check the table is updated + expect(table.isHeaderColumnEnabled, isFalse); + expect(table.isHeaderRowEnabled, isFalse); + // set to page width { final table = editorState.getNodeAtPath([0])!; diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_constants.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_constants.dart index 559b4d202d..bc6e3450e5 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_constants.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_constants.dart @@ -86,6 +86,9 @@ class SimpleTableContext { /// This value is available on mobile only final ValueNotifier isReorderingHitIndex = ValueNotifier(null); + /// Scroll controller for the table + ScrollController? horizontalScrollController; + void _onHoveringOnColumnsAndRowsChanged() { if (!_enableTableDebugLog) { return; diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_style_operation.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_style_operation.dart index 48ffaae3cd..59352af0ac 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_style_operation.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_style_operation.dart @@ -117,6 +117,8 @@ extension TableOptionOperation on EditorState { required Node tableCellNode, required TableAlign align, }) async { + await clearColumnTextAlign(tableCellNode: tableCellNode); + final columnIndex = tableCellNode.columnIndex; await _updateTableAttributes( tableCellNode: tableCellNode, @@ -144,6 +146,8 @@ extension TableOptionOperation on EditorState { required Node tableCellNode, required TableAlign align, }) async { + await clearRowTextAlign(tableCellNode: tableCellNode); + final rowIndex = tableCellNode.rowIndex; await _updateTableAttributes( tableCellNode: tableCellNode, @@ -385,4 +389,67 @@ extension TableOptionOperation on EditorState { transaction.updateNode(parentTableNode, attributes); await apply(transaction); } + + /// Clear the text align of the column at the index where the table cell node is located. + Future clearColumnTextAlign({ + required Node tableCellNode, + }) async { + final parentTableNode = tableCellNode.parentTableNode; + if (parentTableNode == null) { + Log.warn('parent table node is null'); + return; + } + final columnIndex = tableCellNode.columnIndex; + final transaction = this.transaction; + for (var i = 0; i < parentTableNode.rowLength; i++) { + final cell = parentTableNode.getTableCellNode( + rowIndex: i, + columnIndex: columnIndex, + ); + if (cell == null) { + continue; + } + for (final child in cell.children) { + transaction.updateNode(child, { + blockComponentAlign: null, + }); + } + } + if (transaction.operations.isNotEmpty) { + await apply(transaction); + } + } + + /// Clear the text align of the row at the index where the table cell node is located. + Future clearRowTextAlign({ + required Node tableCellNode, + }) async { + final parentTableNode = tableCellNode.parentTableNode; + if (parentTableNode == null) { + Log.warn('parent table node is null'); + return; + } + final rowIndex = tableCellNode.rowIndex; + final transaction = this.transaction; + for (var i = 0; i < parentTableNode.columnLength; i++) { + final cell = parentTableNode.getTableCellNode( + rowIndex: rowIndex, + columnIndex: i, + ); + if (cell == null) { + continue; + } + for (final child in cell.children) { + transaction.updateNode( + child, + { + blockComponentAlign: null, + }, + ); + } + } + if (transaction.operations.isNotEmpty) { + await apply(transaction); + } + } } diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/_desktop_simple_table_widget.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/_desktop_simple_table_widget.dart index 6e640e4561..3a3f02b530 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/_desktop_simple_table_widget.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/_desktop_simple_table_widget.dart @@ -47,8 +47,16 @@ class _DesktopSimpleTableWidgetState extends State { final scrollController = ScrollController(); late final editorState = context.read(); + @override + void initState() { + super.initState(); + + simpleTableContext.horizontalScrollController = scrollController; + } + @override void dispose() { + simpleTableContext.horizontalScrollController = null; scrollController.dispose(); super.dispose(); diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/_mobile_simple_table_widget.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/_mobile_simple_table_widget.dart index 41ae29c61c..9b3ad2d652 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/_mobile_simple_table_widget.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/_mobile_simple_table_widget.dart @@ -47,8 +47,16 @@ class _MobileSimpleTableWidgetState extends State { final scrollController = ScrollController(); late final editorState = context.read(); + @override + void initState() { + super.initState(); + + simpleTableContext.horizontalScrollController = scrollController; + } + @override void dispose() { + simpleTableContext.horizontalScrollController = null; scrollController.dispose(); super.dispose(); diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/_simple_table_bottom_sheet_actions.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/_simple_table_bottom_sheet_actions.dart index 2d8b9025cf..aae1acb68b 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/_simple_table_bottom_sheet_actions.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_widgets/_simple_table_bottom_sheet_actions.dart @@ -239,18 +239,20 @@ class SimpleTableInsertActions extends ISimpleTableBottomSheetActions { SimpleTableInsertAction( type: SimpleTableMoreAction.insertAbove, enableLeftBorder: true, - onTap: () => _onActionTap( + onTap: (increaseCounter) async => _onActionTap( context, - SimpleTableMoreAction.insertAbove, + type: SimpleTableMoreAction.insertAbove, + increaseCounter: increaseCounter, ), ), const HSpace(2), SimpleTableInsertAction( type: SimpleTableMoreAction.insertBelow, enableRightBorder: true, - onTap: () => _onActionTap( + onTap: (increaseCounter) async => _onActionTap( context, - SimpleTableMoreAction.insertBelow, + type: SimpleTableMoreAction.insertBelow, + increaseCounter: increaseCounter, ), ), ], @@ -260,18 +262,20 @@ class SimpleTableInsertActions extends ISimpleTableBottomSheetActions { SimpleTableInsertAction( type: SimpleTableMoreAction.insertLeft, enableLeftBorder: true, - onTap: () => _onActionTap( + onTap: (increaseCounter) async => _onActionTap( context, - SimpleTableMoreAction.insertLeft, + type: SimpleTableMoreAction.insertLeft, + increaseCounter: increaseCounter, ), ), const HSpace(2), SimpleTableInsertAction( type: SimpleTableMoreAction.insertRight, enableRightBorder: true, - onTap: () => _onActionTap( + onTap: (increaseCounter) async => _onActionTap( context, - SimpleTableMoreAction.insertRight, + type: SimpleTableMoreAction.insertRight, + increaseCounter: increaseCounter, ), ), ], @@ -279,7 +283,11 @@ class SimpleTableInsertActions extends ISimpleTableBottomSheetActions { }; } - void _onActionTap(BuildContext context, SimpleTableMoreAction type) { + Future _onActionTap( + BuildContext context, { + required SimpleTableMoreAction type, + required int increaseCounter, + }) async { final simpleTableContext = context.read(); final tableNode = cellNode.parentTableNode; if (tableNode == null) { @@ -291,34 +299,48 @@ class SimpleTableInsertActions extends ISimpleTableBottomSheetActions { case SimpleTableMoreAction.insertAbove: // update the highlight status for the selecting row simpleTableContext.selectingRow.value = cellNode.rowIndex + 1; - editorState.insertRowInTable( + await editorState.insertRowInTable( tableNode, cellNode.rowIndex, ); case SimpleTableMoreAction.insertBelow: - editorState.insertRowInTable( + await editorState.insertRowInTable( tableNode, cellNode.rowIndex + 1, ); + // scroll to the next cell position + editorState.scrollService?.scrollTo( + SimpleTableConstants.defaultRowHeight, + duration: Durations.short3, + ); case SimpleTableMoreAction.insertLeft: // update the highlight status for the selecting column simpleTableContext.selectingColumn.value = cellNode.columnIndex + 1; - editorState.insertColumnInTable( + await editorState.insertColumnInTable( tableNode, cellNode.columnIndex, ); case SimpleTableMoreAction.insertRight: - editorState.insertColumnInTable( + await editorState.insertColumnInTable( tableNode, cellNode.columnIndex + 1, ); + final horizontalScrollController = + simpleTableContext.horizontalScrollController; + if (horizontalScrollController != null) { + final previousWidth = horizontalScrollController.offset; + horizontalScrollController.jumpTo( + previousWidth + SimpleTableConstants.defaultColumnWidth, + ); + } + default: assert(false, 'Unsupported action: $type'); } } } -class SimpleTableInsertAction extends StatelessWidget { +class SimpleTableInsertAction extends StatefulWidget { const SimpleTableInsertAction({ super.key, required this.type, @@ -330,7 +352,16 @@ class SimpleTableInsertAction extends StatelessWidget { final SimpleTableMoreAction type; final bool enableLeftBorder; final bool enableRightBorder; - final void Function() onTap; + final ValueChanged onTap; + + @override + State createState() => + _SimpleTableInsertActionState(); +} + +class _SimpleTableInsertActionState extends State { + // used to count how many times the action is tapped + int increaseCounter = 0; @override Widget build(BuildContext context) { @@ -341,19 +372,19 @@ class SimpleTableInsertAction extends StatelessWidget { shape: _buildBorder(), ), child: AnimatedGestureDetector( - onTapUp: onTap, + onTapUp: () => widget.onTap(increaseCounter++), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Padding( padding: const EdgeInsets.all(1), child: FlowySvg( - type.leftIconSvg, + widget.type.leftIconSvg, size: const Size.square(22), ), ), FlowyText( - type.name, + widget.type.name, fontSize: 12, figmaLineHeight: 16, ), @@ -370,10 +401,10 @@ class SimpleTableInsertAction extends StatelessWidget { ); return RoundedRectangleBorder( borderRadius: BorderRadius.only( - topLeft: enableLeftBorder ? radius : Radius.zero, - bottomLeft: enableLeftBorder ? radius : Radius.zero, - topRight: enableRightBorder ? radius : Radius.zero, - bottomRight: enableRightBorder ? radius : Radius.zero, + topLeft: widget.enableLeftBorder ? radius : Radius.zero, + bottomLeft: widget.enableLeftBorder ? radius : Radius.zero, + topRight: widget.enableRightBorder ? radius : Radius.zero, + bottomRight: widget.enableRightBorder ? radius : Radius.zero, ), ); } @@ -592,7 +623,7 @@ class _SimpleTableHeaderActionButtonState child: CupertinoSwitch( value: value, activeColor: Theme.of(context).colorScheme.primary, - onChanged: (_) {}, + onChanged: (_) => _toggle(), ), ), ); @@ -1198,19 +1229,12 @@ class SimpleTableQuickActions extends StatelessWidget { SimpleTableMoreAction.copy, ), ), - FutureBuilder( - future: getIt().getData(), - builder: (context, snapshot) { - final hasContent = snapshot.data?.tableJson != null; - return SimpleTableQuickAction( - type: SimpleTableMoreAction.paste, - isEnabled: hasContent, - onTap: () => _onActionTap( - context, - SimpleTableMoreAction.paste, - ), - ); - }, + SimpleTableQuickAction( + type: SimpleTableMoreAction.paste, + onTap: () => _onActionTap( + context, + SimpleTableMoreAction.paste, + ), ), SimpleTableQuickAction( type: SimpleTableMoreAction.delete, diff --git a/frontend/appflowy_flutter/pubspec.lock b/frontend/appflowy_flutter/pubspec.lock index 1327e1dd41..70cc8edcfb 100644 --- a/frontend/appflowy_flutter/pubspec.lock +++ b/frontend/appflowy_flutter/pubspec.lock @@ -61,8 +61,8 @@ packages: dependency: "direct main" description: path: "." - ref: cfb8b1b - resolved-ref: cfb8b1b6eb06f73a4fb297b6fd1d54b0ccec2922 + ref: "9f6a299" + resolved-ref: "9f6a29968ecbb61678b8e0e8c9d90bcba44a24e3" url: "https://github.com/AppFlowy-IO/appflowy-editor.git" source: git version: "4.0.0" diff --git a/frontend/appflowy_flutter/pubspec.yaml b/frontend/appflowy_flutter/pubspec.yaml index f40d61b4b0..4c3e4c203f 100644 --- a/frontend/appflowy_flutter/pubspec.yaml +++ b/frontend/appflowy_flutter/pubspec.yaml @@ -174,7 +174,7 @@ dependency_overrides: appflowy_editor: git: url: https://github.com/AppFlowy-IO/appflowy-editor.git - ref: "cfb8b1b" + ref: "9f6a299" appflowy_editor_plugins: git: diff --git a/frontend/appflowy_flutter/test/unit_test/simple_table/simple_table_style_operation_test.dart b/frontend/appflowy_flutter/test/unit_test/simple_table/simple_table_style_operation_test.dart index 940f03711a..dd127d3d0b 100644 --- a/frontend/appflowy_flutter/test/unit_test/simple_table/simple_table_style_operation_test.dart +++ b/frontend/appflowy_flutter/test/unit_test/simple_table/simple_table_style_operation_test.dart @@ -1,5 +1,6 @@ import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_table/simple_table.dart'; import 'package:appflowy_backend/log.dart'; +import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:flutter_test/flutter_test.dart'; import 'simple_table_test_helper.dart'; @@ -193,5 +194,45 @@ void main() { expect(tableNode.tableAlign, align); } }); + + test('clear the existing align of the column before updating', () async { + final (editorState, tableNode) = createEditorStateAndTable( + rowCount: 2, + columnCount: 3, + ); + + final firstCellNode = tableNode.getTableCellNode( + rowIndex: 0, + columnIndex: 0, + ); + + Node firstParagraphNode = firstCellNode!.children.first; + + // format the first paragraph to center align + final transaction = editorState.transaction; + transaction.updateNode( + firstParagraphNode, + { + blockComponentAlign: TableAlign.right.key, + }, + ); + await editorState.apply(transaction); + + firstParagraphNode = editorState.getNodeAtPath([0, 0, 0, 0])!; + expect( + firstParagraphNode.attributes[blockComponentAlign], + TableAlign.right.key, + ); + + await editorState.updateColumnAlign( + tableCellNode: firstCellNode, + align: TableAlign.center, + ); + + expect( + firstParagraphNode.attributes[blockComponentAlign], + null, + ); + }); }); } From 512113877b42986eb726a81931af36410aea2a00 Mon Sep 17 00:00:00 2001 From: "Kilu.He" <108015703+qinluhe@users.noreply.github.com> Date: Thu, 2 Jan 2025 10:46:56 +0800 Subject: [PATCH 111/576] feat: support create workspace on web (#7122) --- .../services/js-services/http/http_api.ts | 167 ++++++++------ .../application/services/js-services/index.ts | 160 ++++++------- .../src/application/services/services.type.ts | 5 +- .../appflowy_web_app/src/application/types.ts | 210 +++++++++--------- .../app/workspaces/CreateWorkspace.tsx | 94 ++++++++ .../components/app/workspaces/Workspaces.tsx | 42 ++-- .../toolbar/block-controls/utils.ts | 2 +- frontend/resources/translations/en.json | 1 + 8 files changed, 406 insertions(+), 275 deletions(-) create mode 100644 frontend/appflowy_web_app/src/components/app/workspaces/CreateWorkspace.tsx diff --git a/frontend/appflowy_web_app/src/application/services/js-services/http/http_api.ts b/frontend/appflowy_web_app/src/application/services/js-services/http/http_api.ts index ebf6506fc0..21c45922eb 100644 --- a/frontend/appflowy_web_app/src/application/services/js-services/http/http_api.ts +++ b/frontend/appflowy_web_app/src/application/services/js-services/http/http_api.ts @@ -21,7 +21,7 @@ import { CreateSpacePayload, UpdateSpacePayload, Role, - WorkspaceMember, QuickNote, QuickNoteEditorData, + WorkspaceMember, QuickNote, QuickNoteEditorData, CreateWorkspacePayload, } from '@/application/types'; import { GlobalComment, Reaction } from '@/application/comment.type'; import { initGrantService, refreshToken } from '@/application/services/js-services/http/gotrue'; @@ -45,7 +45,7 @@ export * from './gotrue'; let axiosInstance: AxiosInstance | null = null; -export function initAPIService(config: AFCloudConfig) { +export function initAPIService (config: AFCloudConfig) { if (axiosInstance) { return; } @@ -120,7 +120,7 @@ export function initAPIService(config: AFCloudConfig) { }); } -export async function signInWithUrl(url: string) { +export async function signInWithUrl (url: string) { const hash = new URL(url).hash; if (!hash) { @@ -157,7 +157,7 @@ export async function signInWithUrl(url: string) { } } -export async function verifyToken(accessToken: string) { +export async function verifyToken (accessToken: string) { const url = `/api/user/verify/${accessToken}`; const response = await axiosInstance?.get<{ code: number; @@ -176,7 +176,7 @@ export async function verifyToken(accessToken: string) { return Promise.reject(data); } -export async function getCurrentUser(): Promise { +export async function getCurrentUser (): Promise { const url = '/api/user/profile'; const response = await axiosInstance?.get<{ code: number; @@ -224,7 +224,7 @@ interface AFWorkspace { database_storage_id: string, } -function afWorkspace2Workspace(workspace: AFWorkspace): Workspace { +function afWorkspace2Workspace (workspace: AFWorkspace): Workspace { return { id: workspace.workspace_id, owner: { @@ -239,7 +239,7 @@ function afWorkspace2Workspace(workspace: AFWorkspace): Workspace { }; } -export async function openWorkspace(workspaceId: string) { +export async function openWorkspace (workspaceId: string) { const url = `/api/workspace/${workspaceId}/open`; const response = await axiosInstance?.put<{ code: number; @@ -253,7 +253,26 @@ export async function openWorkspace(workspaceId: string) { return Promise.reject(response?.data); } -export async function getUserWorkspaceInfo(): Promise<{ +export async function createWorkspace (payload: CreateWorkspacePayload) { + const url = '/api/workspace'; + const response = await axiosInstance?.post<{ + code: number; + data?: { + workspace_id: string; + }; + message: string; + }>(url, payload); + + const data = response?.data; + + if (data?.code === 0 && data.data) { + return data.data.workspace_id; + } + + return Promise.reject(data); +} + +export async function getUserWorkspaceInfo (): Promise<{ user_id: string; selected_workspace: Workspace; workspaces: Workspace[]; @@ -287,7 +306,7 @@ export async function getUserWorkspaceInfo(): Promise<{ return Promise.reject(data); } -export async function getPublishViewMeta(namespace: string, publishName: string) { +export async function getPublishViewMeta (namespace: string, publishName: string) { const url = `/api/workspace/v1/published/${namespace}/${publishName}`; const response = await axiosInstance?.get<{ code: number; @@ -306,7 +325,7 @@ export async function getPublishViewMeta(namespace: string, publishName: string) return response?.data.data; } -export async function getPublishViewBlob(namespace: string, publishName: string) { +export async function getPublishViewBlob (namespace: string, publishName: string) { const url = `/api/workspace/published/${namespace}/${publishName}/blob`; const response = await axiosInstance?.get(url, { responseType: 'blob', @@ -315,7 +334,7 @@ export async function getPublishViewBlob(namespace: string, publishName: string) return blobToBytes(response?.data); } -export async function updateCollab(workspaceId: string, objectId: string, collabType: Types, docState: Uint8Array, context: { +export async function updateCollab (workspaceId: string, objectId: string, collabType: Types, docState: Uint8Array, context: { version_vector: number; }) { const url = `/api/workspace/v1/${workspaceId}/collab/${objectId}/web-update`; @@ -346,7 +365,7 @@ export async function updateCollab(workspaceId: string, objectId: string, collab return context; } -export async function getCollab(workspaceId: string, objectId: string, collabType: Types) { +export async function getCollab (workspaceId: string, objectId: string, collabType: Types) { const url = `/api/workspace/v1/${workspaceId}/collab/${objectId}`; const response = await axiosInstance?.get<{ code: number; @@ -372,7 +391,7 @@ export async function getCollab(workspaceId: string, objectId: string, collabTyp }; } -export async function getPageCollab(workspaceId: string, viewId: string) { +export async function getPageCollab (workspaceId: string, viewId: string) { const url = `/api/workspace/${workspaceId}/page-view/${viewId}`; const response = await axiosInstance?.get<{ code: number; @@ -406,7 +425,7 @@ export async function getPageCollab(workspaceId: string, viewId: string) { }; } -export async function getPublishView(publishNamespace: string, publishName: string) { +export async function getPublishView (publishNamespace: string, publishName: string) { const meta = await getPublishViewMeta(publishNamespace, publishName); const blob = await getPublishViewBlob(publishNamespace, publishName); @@ -443,7 +462,7 @@ export async function getPublishView(publishNamespace: string, publishName: stri } } -export async function getPublishInfoWithViewId(viewId: string) { +export async function getPublishInfoWithViewId (viewId: string) { const url = `/api/workspace/published-info/${viewId}`; const response = await axiosInstance?.get<{ code: number; @@ -463,7 +482,7 @@ export async function getPublishInfoWithViewId(viewId: string) { return Promise.reject(data); } -export async function getAppFavorites(workspaceId: string) { +export async function getAppFavorites (workspaceId: string) { const url = `/api/workspace/${workspaceId}/favorite`; const response = await axiosInstance?.get<{ code: number; @@ -482,7 +501,7 @@ export async function getAppFavorites(workspaceId: string) { return Promise.reject(data); } -export async function getAppTrash(workspaceId: string) { +export async function getAppTrash (workspaceId: string) { const url = `/api/workspace/${workspaceId}/trash`; const response = await axiosInstance?.get<{ code: number; @@ -501,7 +520,7 @@ export async function getAppTrash(workspaceId: string) { return Promise.reject(data); } -export async function getAppRecent(workspaceId: string) { +export async function getAppRecent (workspaceId: string) { const url = `/api/workspace/${workspaceId}/recent`; const response = await axiosInstance?.get<{ code: number; @@ -520,7 +539,7 @@ export async function getAppRecent(workspaceId: string) { return Promise.reject(data); } -export async function getAppOutline(workspaceId: string) { +export async function getAppOutline (workspaceId: string) { const url = `/api/workspace/${workspaceId}/folder?depth=10`; const response = await axiosInstance?.get<{ @@ -538,7 +557,7 @@ export async function getAppOutline(workspaceId: string) { return Promise.reject(data); } -export async function getView(workspaceId: string, viewId: string, depth: number = 1) { +export async function getView (workspaceId: string, viewId: string, depth: number = 1) { const url = `/api/workspace/${workspaceId}/folder?depth=${depth}&root_view_id=${viewId}`; const response = await axiosInstance?.get<{ code: number; @@ -555,7 +574,7 @@ export async function getView(workspaceId: string, viewId: string, depth: number return Promise.reject(data); } -export async function getPublishOutline(publishNamespace: string) { +export async function getPublishOutline (publishNamespace: string) { const url = `/api/workspace/published-outline/${publishNamespace}`; const response = await axiosInstance?.get<{ code: number; @@ -572,7 +591,7 @@ export async function getPublishOutline(publishNamespace: string) { return Promise.reject(data); } -export async function getPublishViewComments(viewId: string): Promise { +export async function getPublishViewComments (viewId: string): Promise { const url = `/api/workspace/published-info/${viewId}/comment`; const response = await axiosInstance?.get<{ code: number; @@ -621,7 +640,7 @@ export async function getPublishViewComments(viewId: string): Promise> { +export async function getReactions (viewId: string, commentId?: string): Promise> { let url = `/api/workspace/published-info/${viewId}/reaction`; if (commentId) { @@ -672,7 +691,7 @@ export async function getReactions(viewId: string, commentId?: string): Promise< return Promise.reject(data); } -export async function createGlobalCommentOnPublishView(viewId: string, content: string, replyCommentId?: string) { +export async function createGlobalCommentOnPublishView (viewId: string, content: string, replyCommentId?: string) { const url = `/api/workspace/published-info/${viewId}/comment`; const response = await axiosInstance?.post<{ code: number; message: string }>(url, { content, @@ -686,7 +705,7 @@ export async function createGlobalCommentOnPublishView(viewId: string, content: return Promise.reject(response?.data.message); } -export async function deleteGlobalCommentOnPublishView(viewId: string, commentId: string) { +export async function deleteGlobalCommentOnPublishView (viewId: string, commentId: string) { const url = `/api/workspace/published-info/${viewId}/comment`; const response = await axiosInstance?.delete<{ code: number; message: string }>(url, { data: { @@ -701,7 +720,7 @@ export async function deleteGlobalCommentOnPublishView(viewId: string, commentId return Promise.reject(response?.data.message); } -export async function addReaction(viewId: string, commentId: string, reactionType: string) { +export async function addReaction (viewId: string, commentId: string, reactionType: string) { const url = `/api/workspace/published-info/${viewId}/reaction`; const response = await axiosInstance?.post<{ code: number; message: string }>(url, { comment_id: commentId, @@ -715,7 +734,7 @@ export async function addReaction(viewId: string, commentId: string, reactionTyp return Promise.reject(response?.data.message); } -export async function removeReaction(viewId: string, commentId: string, reactionType: string) { +export async function removeReaction (viewId: string, commentId: string, reactionType: string) { const url = `/api/workspace/published-info/${viewId}/reaction`; const response = await axiosInstance?.delete<{ code: number; message: string }>(url, { data: { @@ -731,7 +750,7 @@ export async function removeReaction(viewId: string, commentId: string, reaction return Promise.reject(response?.data.message); } -export async function getWorkspaces(): Promise { +export async function getWorkspaces (): Promise { const query = new URLSearchParams({ include_member_count: 'true', }); @@ -769,7 +788,7 @@ export interface WorkspaceFolder { children: WorkspaceFolder[]; } -function iterateFolder(folder: WorkspaceFolder): FolderView { +function iterateFolder (folder: WorkspaceFolder): FolderView { return { id: folder.view_id, name: folder.name, @@ -783,7 +802,7 @@ function iterateFolder(folder: WorkspaceFolder): FolderView { }; } -export async function getWorkspaceFolder(workspaceId: string): Promise { +export async function getWorkspaceFolder (workspaceId: string): Promise { const url = `/api/workspace/${workspaceId}/folder`; const response = await axiosInstance?.get<{ code: number; @@ -806,7 +825,7 @@ export interface DuplicatePublishViewPayload { dest_view_id: string; } -export async function duplicatePublishView(workspaceId: string, payload: DuplicatePublishViewPayload) { +export async function duplicatePublishView (workspaceId: string, payload: DuplicatePublishViewPayload) { const url = `/api/workspace/${workspaceId}/published-duplicate`; const res = await axiosInstance?.post<{ @@ -821,7 +840,7 @@ export async function duplicatePublishView(workspaceId: string, payload: Duplica return Promise.reject(res?.data.message); } -export async function createTemplate(template: UploadTemplatePayload) { +export async function createTemplate (template: UploadTemplatePayload) { const url = '/api/template-center/template'; const response = await axiosInstance?.post<{ code: number; @@ -835,7 +854,7 @@ export async function createTemplate(template: UploadTemplatePayload) { return Promise.reject(response?.data.message); } -export async function updateTemplate(viewId: string, template: UploadTemplatePayload) { +export async function updateTemplate (viewId: string, template: UploadTemplatePayload) { const url = `/api/template-center/template/${viewId}`; const response = await axiosInstance?.put<{ code: number; @@ -849,7 +868,7 @@ export async function updateTemplate(viewId: string, template: UploadTemplatePay return Promise.reject(response?.data.message); } -export async function getTemplates({ +export async function getTemplates ({ categoryId, nameContains, }: { @@ -880,7 +899,7 @@ export async function getTemplates({ return Promise.reject(data); } -export async function getTemplateById(viewId: string) { +export async function getTemplateById (viewId: string) { const url = `/api/template-center/template/${viewId}`; const response = await axiosInstance?.get<{ code: number; @@ -897,7 +916,7 @@ export async function getTemplateById(viewId: string) { return Promise.reject(data); } -export async function deleteTemplate(viewId: string) { +export async function deleteTemplate (viewId: string) { const url = `/api/template-center/template/${viewId}`; const response = await axiosInstance?.delete<{ code: number; @@ -911,7 +930,7 @@ export async function deleteTemplate(viewId: string) { return Promise.reject(response?.data.message); } -export async function getTemplateCategories() { +export async function getTemplateCategories () { const url = '/api/template-center/category'; const response = await axiosInstance?.get<{ code: number; @@ -931,7 +950,7 @@ export async function getTemplateCategories() { return Promise.reject(data); } -export async function addTemplateCategory(category: TemplateCategoryFormValues) { +export async function addTemplateCategory (category: TemplateCategoryFormValues) { const url = '/api/template-center/category'; const response = await axiosInstance?.post<{ code: number; @@ -945,7 +964,7 @@ export async function addTemplateCategory(category: TemplateCategoryFormValues) return Promise.reject(response?.data.message); } -export async function updateTemplateCategory(id: string, category: TemplateCategoryFormValues) { +export async function updateTemplateCategory (id: string, category: TemplateCategoryFormValues) { const url = `/api/template-center/category/${id}`; const response = await axiosInstance?.put<{ code: number; @@ -959,7 +978,7 @@ export async function updateTemplateCategory(id: string, category: TemplateCateg return Promise.reject(response?.data.message); } -export async function deleteTemplateCategory(categoryId: string) { +export async function deleteTemplateCategory (categoryId: string) { const url = `/api/template-center/category/${categoryId}`; const response = await axiosInstance?.delete<{ code: number; @@ -973,7 +992,7 @@ export async function deleteTemplateCategory(categoryId: string) { return Promise.reject(response?.data.message); } -export async function getTemplateCreators() { +export async function getTemplateCreators () { const url = '/api/template-center/creator'; const response = await axiosInstance?.get<{ code: number; @@ -992,7 +1011,7 @@ export async function getTemplateCreators() { return Promise.reject(data); } -export async function createTemplateCreator(creator: TemplateCreatorFormValues) { +export async function createTemplateCreator (creator: TemplateCreatorFormValues) { const url = '/api/template-center/creator'; const response = await axiosInstance?.post<{ code: number; @@ -1006,7 +1025,7 @@ export async function createTemplateCreator(creator: TemplateCreatorFormValues) return Promise.reject(response?.data.message); } -export async function updateTemplateCreator(creatorId: string, creator: TemplateCreatorFormValues) { +export async function updateTemplateCreator (creatorId: string, creator: TemplateCreatorFormValues) { const url = `/api/template-center/creator/${creatorId}`; const response = await axiosInstance?.put<{ code: number; @@ -1020,7 +1039,7 @@ export async function updateTemplateCreator(creatorId: string, creator: Template return Promise.reject(response?.data.message); } -export async function deleteTemplateCreator(creatorId: string) { +export async function deleteTemplateCreator (creatorId: string) { const url = `/api/template-center/creator/${creatorId}`; const response = await axiosInstance?.delete<{ code: number; @@ -1034,7 +1053,7 @@ export async function deleteTemplateCreator(creatorId: string) { return Promise.reject(response?.data.message); } -export async function uploadTemplateAvatar(file: File) { +export async function uploadTemplateAvatar (file: File) { const url = '/api/template-center/avatar'; const formData = new FormData(); @@ -1064,7 +1083,7 @@ export async function uploadTemplateAvatar(file: File) { return Promise.reject(data); } -export async function getInvitation(invitationId: string) { +export async function getInvitation (invitationId: string) { const url = `/api/workspace/invite/${invitationId}`; const response = await axiosInstance?.get<{ code: number; @@ -1081,7 +1100,7 @@ export async function getInvitation(invitationId: string) { return Promise.reject(data); } -export async function acceptInvitation(invitationId: string) { +export async function acceptInvitation (invitationId: string) { const url = `/api/workspace/accept-invite/${invitationId}`; const response = await axiosInstance?.post<{ code: number; @@ -1095,7 +1114,7 @@ export async function acceptInvitation(invitationId: string) { return Promise.reject(response?.data.message); } -export async function getRequestAccessInfo(requestId: string): Promise { +export async function getRequestAccessInfo (requestId: string): Promise { const url = `/api/access-request/${requestId}`; const response = await axiosInstance?.get<{ code: number; @@ -1125,7 +1144,7 @@ export async function getRequestAccessInfo(requestId: string): Promise void) { +export async function uploadImportFile (presignedUrl: string, file: File, onProgress: (progress: number) => void) { const response = await axios.put(presignedUrl, file, { onUploadProgress: (progressEvent) => { const { progress = 0 } = progressEvent; @@ -1275,7 +1294,7 @@ export async function uploadImportFile(presignedUrl: string, file: File, onProgr }); } -export async function addAppPage(workspaceId: string, parentViewId: string, { +export async function addAppPage (workspaceId: string, parentViewId: string, { layout, name, }: CreatePagePayload) { @@ -1299,7 +1318,7 @@ export async function addAppPage(workspaceId: string, parentViewId: string, { return Promise.reject(response?.data); } -export async function updatePage(workspaceId: string, viewId: string, data: UpdatePagePayload) { +export async function updatePage (workspaceId: string, viewId: string, data: UpdatePagePayload) { const url = `/api/workspace/${workspaceId}/page-view/${viewId}`; const res = await axiosInstance?.patch<{ @@ -1314,7 +1333,7 @@ export async function updatePage(workspaceId: string, viewId: string, data: Upda return Promise.reject(res?.data); } -export async function deleteTrash(workspaceId: string, viewId?: string) { +export async function deleteTrash (workspaceId: string, viewId?: string) { if (viewId) { const url = `/api/workspace/${workspaceId}/trash/${viewId}`; const response = await axiosInstance?.delete<{ @@ -1343,7 +1362,7 @@ export async function deleteTrash(workspaceId: string, viewId?: string) { } -export async function moveToTrash(workspaceId: string, viewId: string) { +export async function moveToTrash (workspaceId: string, viewId: string) { const url = `/api/workspace/${workspaceId}/page-view/${viewId}/move-to-trash`; const response = await axiosInstance?.post<{ code: number; @@ -1357,7 +1376,7 @@ export async function moveToTrash(workspaceId: string, viewId: string) { return Promise.reject(response?.data); } -export async function movePageTo(workspaceId: string, viewId: string, parentViewId: string, prevViewId?: string) { +export async function movePageTo (workspaceId: string, viewId: string, parentViewId: string, prevViewId?: string) { const url = `/api/workspace/${workspaceId}/page-view/${viewId}/move`; const response = await axiosInstance?.post<{ code: number; @@ -1374,7 +1393,7 @@ export async function movePageTo(workspaceId: string, viewId: string, parentView return Promise.reject(response?.data); } -export async function restorePage(workspaceId: string, viewId?: string) { +export async function restorePage (workspaceId: string, viewId?: string) { const url = viewId ? `/api/workspace/${workspaceId}/page-view/${viewId}/restore-from-trash` : `/api/workspace/${workspaceId}/restore-all-pages-from-trash`; const response = await axiosInstance?.post<{ code: number; @@ -1388,7 +1407,7 @@ export async function restorePage(workspaceId: string, viewId?: string) { return Promise.reject(response?.data); } -export async function createSpace(workspaceId: string, payload: CreateSpacePayload) { +export async function createSpace (workspaceId: string, payload: CreateSpacePayload) { const url = `/api/workspace/${workspaceId}/space`; const response = await axiosInstance?.post<{ code: number; @@ -1405,7 +1424,7 @@ export async function createSpace(workspaceId: string, payload: CreateSpacePaylo return Promise.reject(response?.data); } -export async function updateSpace(workspaceId: string, payload: UpdateSpacePayload) { +export async function updateSpace (workspaceId: string, payload: UpdateSpacePayload) { const url = `/api/workspace/${workspaceId}/space/${payload.view_id}`; const data = omit(payload, ['view_id']); const response = await axiosInstance?.patch<{ @@ -1420,7 +1439,7 @@ export async function updateSpace(workspaceId: string, payload: UpdateSpacePaylo return Promise.reject(response?.data); } -export async function uploadFile(workspaceId: string, viewId: string, file: File, onProgress?: (progress: number) => void) { +export async function uploadFile (workspaceId: string, viewId: string, file: File, onProgress?: (progress: number) => void) { const url = `/api/file_storage/${workspaceId}/v1/blob/${viewId}`; // Check file size, if over 7MB, check subscription plan @@ -1481,7 +1500,7 @@ export async function uploadFile(workspaceId: string, viewId: string, file: File } -export async function inviteMembers(workspaceId: string, emails: string[]) { +export async function inviteMembers (workspaceId: string, emails: string[]) { const url = `/api/workspace/${workspaceId}/invite`; const payload = emails.map(e => ({ @@ -1501,7 +1520,7 @@ export async function inviteMembers(workspaceId: string, emails: string[]) { return Promise.reject(res?.data); } -export async function getMembers(workspaceId: string) { +export async function getMembers (workspaceId: string) { const url = `/api/workspace/${workspaceId}/member`; const res = await axiosInstance?.get<{ code: number; @@ -1516,7 +1535,7 @@ export async function getMembers(workspaceId: string) { return Promise.reject(res?.data); } -export async function leaveWorkspace(workspaceId: string) { +export async function leaveWorkspace (workspaceId: string) { const url = `/api/workspace/${workspaceId}/leave`; const res = await axiosInstance?.post<{ code: number; @@ -1530,7 +1549,7 @@ export async function leaveWorkspace(workspaceId: string) { return Promise.reject(res?.data); } -export async function deleteWorkspace(workspaceId: string) { +export async function deleteWorkspace (workspaceId: string) { const url = `/api/workspace/${workspaceId}`; const res = await axiosInstance?.delete<{ code: number; @@ -1544,7 +1563,7 @@ export async function deleteWorkspace(workspaceId: string) { return Promise.reject(res?.data); } -export async function getQuickNoteList(workspaceId: string, params: { +export async function getQuickNoteList (workspaceId: string, params: { offset?: number; limit?: number; searchTerm?: string; @@ -1575,7 +1594,7 @@ export async function getQuickNoteList(workspaceId: string, params: { return Promise.reject(res?.data); } -export async function createQuickNote(workspaceId: string, payload: QuickNoteEditorData[]): Promise { +export async function createQuickNote (workspaceId: string, payload: QuickNoteEditorData[]): Promise { const url = `/api/workspace/${workspaceId}/quick-note`; const res = await axiosInstance?.post<{ code: number; @@ -1592,7 +1611,7 @@ export async function createQuickNote(workspaceId: string, payload: QuickNoteEdi return Promise.reject(res?.data); } -export async function updateQuickNote(workspaceId: string, noteId: string, payload: QuickNoteEditorData[]) { +export async function updateQuickNote (workspaceId: string, noteId: string, payload: QuickNoteEditorData[]) { const url = `/api/workspace/${workspaceId}/quick-note/${noteId}`; const res = await axiosInstance?.put<{ code: number; @@ -1608,7 +1627,7 @@ export async function updateQuickNote(workspaceId: string, noteId: string, paylo return Promise.reject(res?.data); } -export async function deleteQuickNote(workspaceId: string, noteId: string) { +export async function deleteQuickNote (workspaceId: string, noteId: string) { const url = `/api/workspace/${workspaceId}/quick-note/${noteId}`; const res = await axiosInstance?.delete<{ code: number; @@ -1622,7 +1641,7 @@ export async function deleteQuickNote(workspaceId: string, noteId: string) { return Promise.reject(res?.data); } -export async function cancelSubscription(workspaceId: string, plan: SubscriptionPlan, reason?: string) { +export async function cancelSubscription (workspaceId: string, plan: SubscriptionPlan, reason?: string) { const url = `/billing/api/v1/cancel-subscription`; const res = await axiosInstance?.post<{ code: number; diff --git a/frontend/appflowy_web_app/src/application/services/js-services/index.ts b/frontend/appflowy_web_app/src/application/services/js-services/index.ts index 14f635ab80..d80bb543f2 100644 --- a/frontend/appflowy_web_app/src/application/services/js-services/index.ts +++ b/frontend/appflowy_web_app/src/application/services/js-services/index.ts @@ -29,7 +29,7 @@ import { UploadTemplatePayload, } from '@/application/template.type'; import { - CreatePagePayload, CreateSpacePayload, + CreatePagePayload, CreateSpacePayload, CreateWorkspacePayload, DatabaseRelations, DuplicatePublishView, QuickNoteEditorData, SubscriptionInterval, SubscriptionPlan, @@ -57,15 +57,15 @@ export class AFClientService implements AFService { } > = new Map(); - constructor(config: AFServiceConfig) { + constructor (config: AFServiceConfig) { APIService.initAPIService(config.cloudConfig); } - getClientId() { + getClientId () { return this.clientId; } - async getPublishViewMeta(namespace: string, publishName: string) { + async getPublishViewMeta (namespace: string, publishName: string) { const name = `${namespace}_${publishName}`; const isLoaded = this.publishViewLoaded.has(name); @@ -87,7 +87,7 @@ export class AFClientService implements AFService { return viewMeta; } - async getPublishView(namespace: string, publishName: string) { + async getPublishView (namespace: string, publishName: string) { const name = `${namespace}_${publishName}`; const isLoaded = this.publishViewLoaded.has(name); @@ -122,7 +122,7 @@ export class AFClientService implements AFService { return doc; } - async getPublishRowDocument(viewId: string) { + async getPublishRowDocument (viewId: string) { const doc = await openCollabDB(viewId); if (hasCollabCache(doc)) { @@ -133,15 +133,15 @@ export class AFClientService implements AFService { } - async createRowDoc(rowKey: string) { + async createRowDoc (rowKey: string) { return createRowDoc(rowKey); } - deleteRowDoc(rowKey: string) { + deleteRowDoc (rowKey: string) { return deleteRowDoc(rowKey); } - async getAppDatabaseViewRelations(workspaceId: string, databaseStorageId: string) { + async getAppDatabaseViewRelations (workspaceId: string, databaseStorageId: string) { const res = await APIService.getCollab(workspaceId, databaseStorageId, Types.WorkspaceDatabase); const doc = new Y.Doc(); @@ -160,7 +160,7 @@ export class AFClientService implements AFService { return result; } - async getPublishInfo(viewId: string) { + async getPublishInfo (viewId: string) { if (this.publishViewInfo.has(viewId)) { return this.publishViewInfo.get(viewId) as { namespace: string; @@ -186,31 +186,31 @@ export class AFClientService implements AFService { return data; } - async getPublishOutline(namespace: string) { + async getPublishOutline (namespace: string) { return APIService.getPublishOutline(namespace); } - async getAppOutline(workspaceId: string) { + async getAppOutline (workspaceId: string) { return APIService.getAppOutline(workspaceId); } - async getAppView(workspaceId: string, viewId: string) { + async getAppView (workspaceId: string, viewId: string) { return APIService.getView(workspaceId, viewId); } - async getAppFavorites(workspaceId: string) { + async getAppFavorites (workspaceId: string) { return APIService.getAppFavorites(workspaceId); } - async getAppRecent(workspaceId: string) { + async getAppRecent (workspaceId: string) { return APIService.getAppRecent(workspaceId); } - async getAppTrash(workspaceId: string) { + async getAppTrash (workspaceId: string) { return APIService.getAppTrash(workspaceId); } - async loginAuth(url: string) { + async loginAuth (url: string) { try { await APIService.signInWithUrl(url); emit(EventType.SESSION_VALID); @@ -223,43 +223,43 @@ export class AFClientService implements AFService { } @withSignIn() - async signInMagicLink({ email }: { email: string; redirectTo: string }) { + async signInMagicLink ({ email }: { email: string; redirectTo: string }) { return await APIService.signInWithMagicLink(email, AUTH_CALLBACK_URL); } @withSignIn() - async signInGoogle(_: { redirectTo: string }) { + async signInGoogle (_: { redirectTo: string }) { return APIService.signInGoogle(AUTH_CALLBACK_URL); } @withSignIn() - async signInApple(_: { redirectTo: string }) { + async signInApple (_: { redirectTo: string }) { return APIService.signInApple(AUTH_CALLBACK_URL); } @withSignIn() - async signInGithub(_: { redirectTo: string }) { + async signInGithub (_: { redirectTo: string }) { return APIService.signInGithub(AUTH_CALLBACK_URL); } @withSignIn() - async signInDiscord(_: { redirectTo: string }) { + async signInDiscord (_: { redirectTo: string }) { return APIService.signInDiscord(AUTH_CALLBACK_URL); } - async getWorkspaces() { + async getWorkspaces () { const data = APIService.getWorkspaces(); return data; } - async getWorkspaceFolder(workspaceId: string) { + async getWorkspaceFolder (workspaceId: string) { const data = await APIService.getWorkspaceFolder(workspaceId); return data; } - async getCurrentUser() { + async getCurrentUser () { const token = getTokenParsed(); const userId = token?.user?.id; @@ -276,11 +276,15 @@ export class AFClientService implements AFService { return user; } - async openWorkspace(workspaceId: string) { + async openWorkspace (workspaceId: string) { return APIService.openWorkspace(workspaceId); } - async getUserWorkspaceInfo() { + async createWorkspace (payload: CreateWorkspacePayload) { + return APIService.createWorkspace(payload); + } + + async getUserWorkspaceInfo () { const workspaceInfo = await APIService.getUserWorkspaceInfo(); if (!workspaceInfo) { @@ -294,7 +298,7 @@ export class AFClientService implements AFService { }; } - async duplicatePublishView(params: DuplicatePublishView) { + async duplicatePublishView (params: DuplicatePublishView) { return APIService.duplicatePublishView(params.workspaceId, { dest_view_id: params.spaceViewId, published_view_id: params.viewId, @@ -302,90 +306,90 @@ export class AFClientService implements AFService { }); } - createCommentOnPublishView(viewId: string, content: string, replyCommentId: string | undefined): Promise { + createCommentOnPublishView (viewId: string, content: string, replyCommentId: string | undefined): Promise { return APIService.createGlobalCommentOnPublishView(viewId, content, replyCommentId); } - deleteCommentOnPublishView(viewId: string, commentId: string): Promise { + deleteCommentOnPublishView (viewId: string, commentId: string): Promise { return APIService.deleteGlobalCommentOnPublishView(viewId, commentId); } - getPublishViewGlobalComments(viewId: string): Promise { + getPublishViewGlobalComments (viewId: string): Promise { return APIService.getPublishViewComments(viewId); } - getPublishViewReactions(viewId: string, commentId?: string): Promise> { + getPublishViewReactions (viewId: string, commentId?: string): Promise> { return APIService.getReactions(viewId, commentId); } - addPublishViewReaction(viewId: string, commentId: string, reactionType: string): Promise { + addPublishViewReaction (viewId: string, commentId: string, reactionType: string): Promise { return APIService.addReaction(viewId, commentId, reactionType); } - removePublishViewReaction(viewId: string, commentId: string, reactionType: string): Promise { + removePublishViewReaction (viewId: string, commentId: string, reactionType: string): Promise { return APIService.removeReaction(viewId, commentId, reactionType); } - async getTemplateCategories() { + async getTemplateCategories () { return APIService.getTemplateCategories(); } - async getTemplateCreators() { + async getTemplateCreators () { return APIService.getTemplateCreators(); } - async createTemplate(template: UploadTemplatePayload) { + async createTemplate (template: UploadTemplatePayload) { return APIService.createTemplate(template); } - async updateTemplate(id: string, template: UploadTemplatePayload) { + async updateTemplate (id: string, template: UploadTemplatePayload) { return APIService.updateTemplate(id, template); } - async getTemplateById(id: string) { + async getTemplateById (id: string) { return APIService.getTemplateById(id); } - async getTemplates(params: { + async getTemplates (params: { categoryId?: string; nameContains?: string; }) { return APIService.getTemplates(params); } - async deleteTemplate(id: string) { + async deleteTemplate (id: string) { return APIService.deleteTemplate(id); } - async addTemplateCategory(category: TemplateCategoryFormValues) { + async addTemplateCategory (category: TemplateCategoryFormValues) { return APIService.addTemplateCategory(category); } - async updateTemplateCategory(categoryId: string, category: TemplateCategoryFormValues) { + async updateTemplateCategory (categoryId: string, category: TemplateCategoryFormValues) { return APIService.updateTemplateCategory(categoryId, category); } - async deleteTemplateCategory(categoryId: string) { + async deleteTemplateCategory (categoryId: string) { return APIService.deleteTemplateCategory(categoryId); } - async updateTemplateCreator(creatorId: string, creator: TemplateCreatorFormValues) { + async updateTemplateCreator (creatorId: string, creator: TemplateCreatorFormValues) { return APIService.updateTemplateCreator(creatorId, creator); } - async createTemplateCreator(creator: TemplateCreatorFormValues) { + async createTemplateCreator (creator: TemplateCreatorFormValues) { return APIService.createTemplateCreator(creator); } - async deleteTemplateCreator(creatorId: string) { + async deleteTemplateCreator (creatorId: string) { return APIService.deleteTemplateCreator(creatorId); } - async uploadTemplateAvatar(file: File) { + async uploadTemplateAvatar (file: File) { return APIService.uploadTemplateAvatar(file); } - async getPageDoc(workspaceId: string, viewId: string, errorCallback?: (error: { + async getPageDoc (workspaceId: string, viewId: string, errorCallback?: (error: { code: number; }) => void) { @@ -428,47 +432,47 @@ export class AFClientService implements AFService { return doc; } - async getInvitation(invitationId: string) { + async getInvitation (invitationId: string) { return APIService.getInvitation(invitationId); } - async acceptInvitation(invitationId: string) { + async acceptInvitation (invitationId: string) { return APIService.acceptInvitation(invitationId); } - approveRequestAccess(requestId: string): Promise { + approveRequestAccess (requestId: string): Promise { return APIService.approveRequestAccess(requestId); } - getRequestAccessInfo(requestId: string) { + getRequestAccessInfo (requestId: string) { return APIService.getRequestAccessInfo(requestId); } - sendRequestAccess(workspaceId: string, viewId: string): Promise { + sendRequestAccess (workspaceId: string, viewId: string): Promise { return APIService.sendRequestAccess(workspaceId, viewId); } - getSubscriptionLink(workspaceId: string, plan: SubscriptionPlan, interval: SubscriptionInterval) { + getSubscriptionLink (workspaceId: string, plan: SubscriptionPlan, interval: SubscriptionInterval) { return APIService.getSubscriptionLink(workspaceId, plan, interval); } - cancelSubscription(workspaceId: string, plan: SubscriptionPlan, reason?: string) { + cancelSubscription (workspaceId: string, plan: SubscriptionPlan, reason?: string) { return APIService.cancelSubscription(workspaceId, plan, reason); } - getSubscriptions() { + getSubscriptions () { return APIService.getSubscriptions(); } - getActiveSubscription(workspaceId: string) { + getActiveSubscription (workspaceId: string) { return APIService.getActiveSubscription(workspaceId); } - getWorkspaceSubscriptions(workspaceId: string) { + getWorkspaceSubscriptions (workspaceId: string) { return APIService.getWorkspaceSubscriptions(workspaceId); } - registerDocUpdate(doc: Y.Doc, context: { + registerDocUpdate (doc: Y.Doc, context: { workspaceId: string, objectId: string, collabType: Types }) { const token = getTokenParsed(); @@ -483,65 +487,65 @@ export class AFClientService implements AFService { sync.initialize(); } - async importFile(file: File, onProgress: (progress: number) => void) { + async importFile (file: File, onProgress: (progress: number) => void) { const task = await APIService.createImportTask(file); await APIService.uploadImportFile(task.presignedUrl, file, onProgress); } - async createSpace(workspaceId: string, payload: CreateSpacePayload) { + async createSpace (workspaceId: string, payload: CreateSpacePayload) { return APIService.createSpace(workspaceId, payload); } - async updateSpace(workspaceId: string, payload: UpdateSpacePayload) { + async updateSpace (workspaceId: string, payload: UpdateSpacePayload) { return APIService.updateSpace(workspaceId, payload); } - async addAppPage(workspaceId: string, parentViewId: string, payload: CreatePagePayload) { + async addAppPage (workspaceId: string, parentViewId: string, payload: CreatePagePayload) { return APIService.addAppPage(workspaceId, parentViewId, payload); } - async updateAppPage(workspaceId: string, viewId: string, data: UpdatePagePayload) { + async updateAppPage (workspaceId: string, viewId: string, data: UpdatePagePayload) { return APIService.updatePage(workspaceId, viewId, data); } - async deleteTrash(workspaceId: string, viewId?: string) { + async deleteTrash (workspaceId: string, viewId?: string) { return APIService.deleteTrash(workspaceId, viewId); } - async moveToTrash(workspaceId: string, viewId: string) { + async moveToTrash (workspaceId: string, viewId: string) { return APIService.moveToTrash(workspaceId, viewId); } - async restoreFromTrash(workspaceId: string, viewId?: string) { + async restoreFromTrash (workspaceId: string, viewId?: string) { return APIService.restorePage(workspaceId, viewId); } - async movePage(workspaceId: string, viewId: string, parentId: string, prevViewId?: string) { + async movePage (workspaceId: string, viewId: string, parentId: string, prevViewId?: string) { return APIService.movePageTo(workspaceId, viewId, parentId, prevViewId); } - async uploadFile(workspaceId: string, viewId: string, file: File, onProgress?: (progress: number) => void) { + async uploadFile (workspaceId: string, viewId: string, file: File, onProgress?: (progress: number) => void) { return APIService.uploadFile(workspaceId, viewId, file, onProgress); } - deleteWorkspace(workspaceId: string): Promise { + deleteWorkspace (workspaceId: string): Promise { return APIService.deleteWorkspace(workspaceId); } - leaveWorkspace(workspaceId: string): Promise { + leaveWorkspace (workspaceId: string): Promise { return APIService.leaveWorkspace(workspaceId); } - inviteMembers(workspaceId: string, emails: string[]): Promise { + inviteMembers (workspaceId: string, emails: string[]): Promise { return APIService.inviteMembers(workspaceId, emails); } - getWorkspaceMembers(workspaceId: string): Promise { + getWorkspaceMembers (workspaceId: string): Promise { return APIService.getMembers(workspaceId); } - getQuickNoteList(workspaceId: string, params: { + getQuickNoteList (workspaceId: string, params: { offset?: number; limit?: number; searchTerm?: string; @@ -549,15 +553,15 @@ export class AFClientService implements AFService { return APIService.getQuickNoteList(workspaceId, params); } - createQuickNote(workspaceId: string, data: QuickNoteEditorData[]) { + createQuickNote (workspaceId: string, data: QuickNoteEditorData[]) { return APIService.createQuickNote(workspaceId, data); } - updateQuickNote(workspaceId: string, id: string, data: QuickNoteEditorData[]) { + updateQuickNote (workspaceId: string, id: string, data: QuickNoteEditorData[]) { return APIService.updateQuickNote(workspaceId, id, data); } - deleteQuickNote(workspaceId: string, id: string) { + deleteQuickNote (workspaceId: string, id: string) { return APIService.deleteQuickNote(workspaceId, id); } } diff --git a/frontend/appflowy_web_app/src/application/services/services.type.ts b/frontend/appflowy_web_app/src/application/services/services.type.ts index 8b329be754..0788a91827 100644 --- a/frontend/appflowy_web_app/src/application/services/services.type.ts +++ b/frontend/appflowy_web_app/src/application/services/services.type.ts @@ -19,7 +19,7 @@ import { UpdateSpacePayload, WorkspaceMember, QuickNoteEditorData, - QuickNote, Subscription, + QuickNote, Subscription, CreateWorkspacePayload, } from '@/application/types'; import { GlobalComment, Reaction } from '@/application/comment.type'; import { ViewMeta } from '@/application/db/tables/view_metas'; @@ -47,6 +47,7 @@ export interface AFCloudConfig { export interface WorkspaceService { openWorkspace: (workspaceId: string) => Promise; + createWorkspace: (payload: CreateWorkspacePayload) => Promise; leaveWorkspace: (workspaceId: string) => Promise; deleteWorkspace: (workspaceId: string) => Promise; getWorkspaceMembers: (workspaceId: string) => Promise; @@ -141,7 +142,7 @@ export interface PublishService { getPublishRowDocument: (viewId: string) => Promise; getPublishInfo: (viewId: string) => Promise<{ namespace: string; publishName: string }>; - getPublishOutline(namespace: string): Promise; + getPublishOutline (namespace: string): Promise; getPublishViewGlobalComments: (viewId: string) => Promise; createCommentOnPublishView: (viewId: string, content: string, replyCommentId?: string) => Promise; diff --git a/frontend/appflowy_web_app/src/application/types.ts b/frontend/appflowy_web_app/src/application/types.ts index 1ceb16b9d1..d8d5227c04 100644 --- a/frontend/appflowy_web_app/src/application/types.ts +++ b/frontend/appflowy_web_app/src/application/types.ts @@ -332,152 +332,152 @@ export enum YjsDatabaseKey { export interface YDoc extends Y.Doc { // eslint-disable-next-line @typescript-eslint/no-explicit-any - getMap(key: YjsEditorKey.data_section): YSharedRoot | any; + getMap (key: YjsEditorKey.data_section): YSharedRoot | any; } export interface YDatabaseRow extends Y.Map { - get(key: YjsDatabaseKey.id): RowId; + get (key: YjsDatabaseKey.id): RowId; - get(key: YjsDatabaseKey.height): string; + get (key: YjsDatabaseKey.height): string; - get(key: YjsDatabaseKey.visibility): boolean; + get (key: YjsDatabaseKey.visibility): boolean; - get(key: YjsDatabaseKey.cells): YDatabaseCells; + get (key: YjsDatabaseKey.cells): YDatabaseCells; - get(key: YjsDatabaseKey.created_at): CreatedAt; + get (key: YjsDatabaseKey.created_at): CreatedAt; - get(key: YjsDatabaseKey.last_modified): LastModified; + get (key: YjsDatabaseKey.last_modified): LastModified; } export interface YDatabaseCells extends Y.Map { - get(key: FieldId): YDatabaseCell; + get (key: FieldId): YDatabaseCell; } export type EndTimestamp = string; export type ReminderId = string; export interface YDatabaseCell extends Y.Map { - get(key: YjsDatabaseKey.created_at): CreatedAt; + get (key: YjsDatabaseKey.created_at): CreatedAt; - get(key: YjsDatabaseKey.last_modified): LastModified; + get (key: YjsDatabaseKey.last_modified): LastModified; - get(key: YjsDatabaseKey.field_type): string; + get (key: YjsDatabaseKey.field_type): string; - get(key: YjsDatabaseKey.data): object | string | boolean | number; + get (key: YjsDatabaseKey.data): object | string | boolean | number; - get(key: YjsDatabaseKey.end_timestamp): EndTimestamp; + get (key: YjsDatabaseKey.end_timestamp): EndTimestamp; - get(key: YjsDatabaseKey.include_time): boolean; + get (key: YjsDatabaseKey.include_time): boolean; // eslint-disable-next-line @typescript-eslint/unified-signatures - get(key: YjsDatabaseKey.is_range): boolean; + get (key: YjsDatabaseKey.is_range): boolean; - get(key: YjsDatabaseKey.reminder_id): ReminderId; + get (key: YjsDatabaseKey.reminder_id): ReminderId; } export interface YSharedRoot extends Y.Map { - get(key: YjsEditorKey.document): YDocument; + get (key: YjsEditorKey.document): YDocument; - get(key: YjsEditorKey.folder): YFolder; + get (key: YjsEditorKey.folder): YFolder; - get(key: YjsEditorKey.database): YDatabase; + get (key: YjsEditorKey.database): YDatabase; - get(key: YjsEditorKey.database_row): YDatabaseRow; + get (key: YjsEditorKey.database_row): YDatabaseRow; } export interface YFolder extends Y.Map { - get(key: YjsFolderKey.views): YViews; + get (key: YjsFolderKey.views): YViews; // eslint-disable-next-line @typescript-eslint/unified-signatures - get(key: YjsFolderKey.meta): YFolderMeta; + get (key: YjsFolderKey.meta): YFolderMeta; // eslint-disable-next-line @typescript-eslint/unified-signatures - get(key: YjsFolderKey.relation): YFolderRelation; + get (key: YjsFolderKey.relation): YFolderRelation; // eslint-disable-next-line @typescript-eslint/unified-signatures - get(key: YjsFolderKey.section): YFolderSection; + get (key: YjsFolderKey.section): YFolderSection; } export interface YViews extends Y.Map { - get(key: ViewId): YView; + get (key: ViewId): YView; } export interface YView extends Y.Map { - get(key: YjsFolderKey.id): ViewId; + get (key: YjsFolderKey.id): ViewId; - get(key: YjsFolderKey.bid): string; + get (key: YjsFolderKey.bid): string; // eslint-disable-next-line @typescript-eslint/unified-signatures - get(key: YjsFolderKey.name): string; + get (key: YjsFolderKey.name): string; // eslint-disable-next-line @typescript-eslint/unified-signatures - get(key: YjsFolderKey.icon | YjsFolderKey.extra): string; + get (key: YjsFolderKey.icon | YjsFolderKey.extra): string; // eslint-disable-next-line @typescript-eslint/unified-signatures - get(key: YjsFolderKey.layout): string; + get (key: YjsFolderKey.layout): string; } export interface YFolderRelation extends Y.Map { - get(key: ViewId): Y.Array; + get (key: ViewId): Y.Array; } export interface YFolderMeta extends Y.Map { - get(key: YjsFolderKey.current_view | YjsFolderKey.current_workspace): string; + get (key: YjsFolderKey.current_view | YjsFolderKey.current_workspace): string; } export interface YFolderSection extends Y.Map { - get(key: YjsFolderKey.favorite | YjsFolderKey.private | YjsFolderKey.recent | YjsFolderKey.trash): YFolderSectionItem; + get (key: YjsFolderKey.favorite | YjsFolderKey.private | YjsFolderKey.recent | YjsFolderKey.trash): YFolderSectionItem; } export interface YFolderSectionItem extends Y.Map { - get(key: string): Y.Array; + get (key: string): Y.Array; } export interface YDocument extends Y.Map { - get(key: YjsEditorKey.blocks | YjsEditorKey.page_id | YjsEditorKey.meta): YBlocks | YMeta | string; + get (key: YjsEditorKey.blocks | YjsEditorKey.page_id | YjsEditorKey.meta): YBlocks | YMeta | string; } export interface YBlocks extends Y.Map { - get(key: BlockId): YBlock; + get (key: BlockId): YBlock; } export interface YBlock extends Y.Map { - get(key: YjsEditorKey.block_id | YjsEditorKey.block_parent): BlockId; + get (key: YjsEditorKey.block_id | YjsEditorKey.block_parent): BlockId; - get(key: YjsEditorKey.block_type): BlockType; + get (key: YjsEditorKey.block_type): BlockType; - get(key: YjsEditorKey.block_data): string; + get (key: YjsEditorKey.block_data): string; - get(key: YjsEditorKey.block_children): ChildrenId; + get (key: YjsEditorKey.block_children): ChildrenId; - get(key: YjsEditorKey.block_external_id): ExternalId; + get (key: YjsEditorKey.block_external_id): ExternalId; } export interface YMeta extends Y.Map { - get(key: YjsEditorKey.children_map | YjsEditorKey.text_map): YChildrenMap | YTextMap; + get (key: YjsEditorKey.children_map | YjsEditorKey.text_map): YChildrenMap | YTextMap; } export interface YChildrenMap extends Y.Map { - get(key: ChildrenId): Y.Array; + get (key: ChildrenId): Y.Array; } export interface YTextMap extends Y.Map { - get(key: ExternalId): Y.Text; + get (key: ExternalId): Y.Text; } export interface YDatabase extends Y.Map { - get(key: YjsDatabaseKey.views): YDatabaseViews; + get (key: YjsDatabaseKey.views): YDatabaseViews; - get(key: YjsDatabaseKey.metas): YDatabaseMetas; + get (key: YjsDatabaseKey.metas): YDatabaseMetas; - get(key: YjsDatabaseKey.fields): YDatabaseFields; + get (key: YjsDatabaseKey.fields): YDatabaseFields; - get(key: YjsDatabaseKey.id): string; + get (key: YjsDatabaseKey.id): string; } export interface YDatabaseViews extends Y.Map { - get(key: ViewId): YDatabaseView; + get (key: ViewId): YDatabaseView; } export type DatabaseId = string; @@ -493,32 +493,32 @@ export enum DatabaseViewLayout { } export interface YDatabaseView extends Y.Map { - get(key: YjsDatabaseKey.database_id): DatabaseId; + get (key: YjsDatabaseKey.database_id): DatabaseId; - get(key: YjsDatabaseKey.name): string; + get (key: YjsDatabaseKey.name): string; - get(key: YjsDatabaseKey.created_at): CreatedAt; + get (key: YjsDatabaseKey.created_at): CreatedAt; - get(key: YjsDatabaseKey.modified_at): ModifiedAt; + get (key: YjsDatabaseKey.modified_at): ModifiedAt; // eslint-disable-next-line @typescript-eslint/unified-signatures - get(key: YjsDatabaseKey.layout): string; + get (key: YjsDatabaseKey.layout): string; - get(key: YjsDatabaseKey.layout_settings): YDatabaseLayoutSettings; + get (key: YjsDatabaseKey.layout_settings): YDatabaseLayoutSettings; - get(key: YjsDatabaseKey.filters): YDatabaseFilters; + get (key: YjsDatabaseKey.filters): YDatabaseFilters; - get(key: YjsDatabaseKey.groups): YDatabaseGroups; + get (key: YjsDatabaseKey.groups): YDatabaseGroups; - get(key: YjsDatabaseKey.sorts): YDatabaseSorts; + get (key: YjsDatabaseKey.sorts): YDatabaseSorts; - get(key: YjsDatabaseKey.field_settings): YDatabaseFieldSettings; + get (key: YjsDatabaseKey.field_settings): YDatabaseFieldSettings; - get(key: YjsDatabaseKey.field_orders): YDatabaseFieldOrders; + get (key: YjsDatabaseKey.field_orders): YDatabaseFieldOrders; - get(key: YjsDatabaseKey.row_orders): YDatabaseRowOrders; + get (key: YjsDatabaseKey.row_orders): YDatabaseRowOrders; - get(key: YjsDatabaseKey.calculations): YDatabaseCalculations; + get (key: YjsDatabaseKey.calculations): YDatabaseCalculations; } export type YDatabaseFieldOrders = Y.Array; // [ { id: FieldId } ] @@ -539,131 +539,131 @@ export type GroupId = string; export interface YDatabaseLayoutSettings extends Y.Map { // DatabaseViewLayout.Board - get(key: '1'): YDatabaseBoardLayoutSetting; + get (key: '1'): YDatabaseBoardLayoutSetting; // DatabaseViewLayout.Calendar - get(key: '2'): YDatabaseCalendarLayoutSetting; + get (key: '2'): YDatabaseCalendarLayoutSetting; } export interface YDatabaseBoardLayoutSetting extends Y.Map { - get(key: YjsDatabaseKey.hide_ungrouped_column | YjsDatabaseKey.collapse_hidden_groups): boolean; + get (key: YjsDatabaseKey.hide_ungrouped_column | YjsDatabaseKey.collapse_hidden_groups): boolean; } export interface YDatabaseCalendarLayoutSetting extends Y.Map { - get(key: YjsDatabaseKey.first_day_of_week | YjsDatabaseKey.field_id | YjsDatabaseKey.layout_ty): string; + get (key: YjsDatabaseKey.first_day_of_week | YjsDatabaseKey.field_id | YjsDatabaseKey.layout_ty): string; - get(key: YjsDatabaseKey.show_week_numbers | YjsDatabaseKey.show_weekends): boolean; + get (key: YjsDatabaseKey.show_week_numbers | YjsDatabaseKey.show_weekends): boolean; } export interface YDatabaseGroup extends Y.Map { - get(key: YjsDatabaseKey.id): GroupId; + get (key: YjsDatabaseKey.id): GroupId; - get(key: YjsDatabaseKey.field_id): FieldId; + get (key: YjsDatabaseKey.field_id): FieldId; // eslint-disable-next-line @typescript-eslint/unified-signatures - get(key: YjsDatabaseKey.content): string; + get (key: YjsDatabaseKey.content): string; - get(key: YjsDatabaseKey.groups): YDatabaseGroupColumns; + get (key: YjsDatabaseKey.groups): YDatabaseGroupColumns; } export type YDatabaseGroupColumns = Y.Array; export interface YDatabaseGroupColumn extends Y.Map { - get(key: YjsDatabaseKey.id): string; + get (key: YjsDatabaseKey.id): string; - get(key: YjsDatabaseKey.visible): boolean; + get (key: YjsDatabaseKey.visible): boolean; } export interface YDatabaseRowOrder extends Y.Map { - get(key: YjsDatabaseKey.id): SortId; + get (key: YjsDatabaseKey.id): SortId; - get(key: YjsDatabaseKey.height): number; + get (key: YjsDatabaseKey.height): number; } export interface YDatabaseSort extends Y.Map { - get(key: YjsDatabaseKey.id): SortId; + get (key: YjsDatabaseKey.id): SortId; - get(key: YjsDatabaseKey.field_id): FieldId; + get (key: YjsDatabaseKey.field_id): FieldId; - get(key: YjsDatabaseKey.condition): string; + get (key: YjsDatabaseKey.condition): string; } export type FilterId = string; export interface YDatabaseFilter extends Y.Map { - get(key: YjsDatabaseKey.id): FilterId; + get (key: YjsDatabaseKey.id): FilterId; - get(key: YjsDatabaseKey.field_id): FieldId; + get (key: YjsDatabaseKey.field_id): FieldId; - get(key: YjsDatabaseKey.type | YjsDatabaseKey.condition | YjsDatabaseKey.content | YjsDatabaseKey.filter_type): string; + get (key: YjsDatabaseKey.type | YjsDatabaseKey.condition | YjsDatabaseKey.content | YjsDatabaseKey.filter_type): string; } export interface YDatabaseCalculation extends Y.Map { - get(key: YjsDatabaseKey.field_id): FieldId; + get (key: YjsDatabaseKey.field_id): FieldId; - get(key: YjsDatabaseKey.id | YjsDatabaseKey.type | YjsDatabaseKey.calculation_value): string; + get (key: YjsDatabaseKey.id | YjsDatabaseKey.type | YjsDatabaseKey.calculation_value): string; } export interface YDatabaseFieldSettings extends Y.Map { - get(key: FieldId): YDatabaseFieldSetting; + get (key: FieldId): YDatabaseFieldSetting; } export interface YDatabaseFieldSetting extends Y.Map { - get(key: YjsDatabaseKey.visibility): string; + get (key: YjsDatabaseKey.visibility): string; - get(key: YjsDatabaseKey.wrap): boolean; + get (key: YjsDatabaseKey.wrap): boolean; // eslint-disable-next-line @typescript-eslint/unified-signatures - get(key: YjsDatabaseKey.width): string; + get (key: YjsDatabaseKey.width): string; } export interface YDatabaseMetas extends Y.Map { - get(key: YjsDatabaseKey.iid): string; + get (key: YjsDatabaseKey.iid): string; } export interface YDatabaseFields extends Y.Map { - get(key: FieldId): YDatabaseField; + get (key: FieldId): YDatabaseField; } export interface YDatabaseField extends Y.Map { - get(key: YjsDatabaseKey.name): string; + get (key: YjsDatabaseKey.name): string; - get(key: YjsDatabaseKey.id): FieldId; + get (key: YjsDatabaseKey.id): FieldId; // eslint-disable-next-line @typescript-eslint/unified-signatures - get(key: YjsDatabaseKey.icon): string; + get (key: YjsDatabaseKey.icon): string; // eslint-disable-next-line @typescript-eslint/unified-signatures - get(key: YjsDatabaseKey.type): string; + get (key: YjsDatabaseKey.type): string; - get(key: YjsDatabaseKey.type_option): YDatabaseFieldTypeOption; + get (key: YjsDatabaseKey.type_option): YDatabaseFieldTypeOption; - get(key: YjsDatabaseKey.is_primary): boolean; + get (key: YjsDatabaseKey.is_primary): boolean; - get(key: YjsDatabaseKey.last_modified): LastModified; + get (key: YjsDatabaseKey.last_modified): LastModified; } export interface YDatabaseFieldTypeOption extends Y.Map { // key is the field type - get(key: string): YMapFieldTypeOption; + get (key: string): YMapFieldTypeOption; } export interface YMapFieldTypeOption extends Y.Map { - get(key: YjsDatabaseKey.content): string; + get (key: YjsDatabaseKey.content): string; // eslint-disable-next-line @typescript-eslint/unified-signatures - get(key: YjsDatabaseKey.data): string; + get (key: YjsDatabaseKey.data): string; // eslint-disable-next-line @typescript-eslint/unified-signatures - get(key: YjsDatabaseKey.time_format): string; + get (key: YjsDatabaseKey.time_format): string; // eslint-disable-next-line @typescript-eslint/unified-signatures - get(key: YjsDatabaseKey.date_format): string; + get (key: YjsDatabaseKey.date_format): string; - get(key: YjsDatabaseKey.database_id): DatabaseId; + get (key: YjsDatabaseKey.database_id): DatabaseId; // eslint-disable-next-line @typescript-eslint/unified-signatures - get(key: YjsDatabaseKey.format): string; + get (key: YjsDatabaseKey.format): string; } export enum Types { @@ -1012,4 +1012,8 @@ export interface QuickNote { data: QuickNoteEditorData[], created_at: string; last_updated_at: string; +} + +export interface CreateWorkspacePayload { + workspace_name: string; } \ No newline at end of file diff --git a/frontend/appflowy_web_app/src/components/app/workspaces/CreateWorkspace.tsx b/frontend/appflowy_web_app/src/components/app/workspaces/CreateWorkspace.tsx new file mode 100644 index 0000000000..8b55cb373b --- /dev/null +++ b/frontend/appflowy_web_app/src/components/app/workspaces/CreateWorkspace.tsx @@ -0,0 +1,94 @@ +import { NormalModal } from '@/components/_shared/modal'; +import { notify } from '@/components/_shared/notify'; +import { useAppHandlers } from '@/components/app/app.hooks'; +import { useService } from '@/components/main/app.hooks'; +import { Button, OutlinedInput } from '@mui/material'; +import React, { useCallback, useRef } from 'react'; +import { useTranslation } from 'react-i18next'; +import { ReactComponent as AddIcon } from '@/assets/add.svg'; + +function CreateWorkspace () { + const { t } = useTranslation(); + const { + onChangeWorkspace: handleSelectedWorkspace, + } = useAppHandlers(); + const service = useService(); + const [loading, setLoading] = React.useState(false); + const [name, setName] = React.useState(''); + const [open, setOpen] = React.useState(false); + + const handleCreate = useCallback(async () => { + if (!service) return; + + setLoading(true); + try { + const workspaceId = await service.createWorkspace({ + workspace_name: name, + }); + + await handleSelectedWorkspace?.(workspaceId); + setOpen(false); + // eslint-disable-next-line + } catch (e: any) { + notify.error(e.message); + } finally { + setLoading(false); + } + }, [handleSelectedWorkspace, name, service]); + const inputRef = useRef(null); + + return ( + <> + + setOpen(false)} + okLoading={loading} + onOk={handleCreate} + okText={t('button.create')} + PaperProps={{ + className: 'w-96 max-w-[70vw]', + }} + classes={{ container: 'items-start max-md:mt-auto max-md:items-center mt-[10%] ' }} + > + { + if (!input) return; + if (!inputRef.current) { + setTimeout(() => { + input.setSelectionRange(0, input.value.length); + }, 100); + inputRef.current = input; + } + + }} + onChange={e => setName(e.target.value)} + fullWidth + onKeyDown={(e) => { + if (e.key === 'Enter') { + void handleCreate(); + } + }} + /> + + + ); +} + +export default CreateWorkspace; \ No newline at end of file diff --git a/frontend/appflowy_web_app/src/components/app/workspaces/Workspaces.tsx b/frontend/appflowy_web_app/src/components/app/workspaces/Workspaces.tsx index f5a9bc38fe..918d9fcc65 100644 --- a/frontend/appflowy_web_app/src/components/app/workspaces/Workspaces.tsx +++ b/frontend/appflowy_web_app/src/components/app/workspaces/Workspaces.tsx @@ -3,6 +3,7 @@ import Import from '@/components/_shared/more-actions/importer/Import'; import { notify } from '@/components/_shared/notify'; import { Popover } from '@/components/_shared/popover'; import { useAppHandlers, useCurrentWorkspaceId, useUserWorkspaceInfo } from '@/components/app/app.hooks'; +import CreateWorkspace from '@/components/app/workspaces/CreateWorkspace'; import CurrentWorkspace from '@/components/app/workspaces/CurrentWorkspace'; import WorkspaceList from '@/components/app/workspaces/WorkspaceList'; import { useCurrentUser } from '@/components/main/app.hooks'; @@ -19,7 +20,7 @@ import { useNavigate, useSearchParams } from 'react-router-dom'; import InviteMember from '@/components/app/workspaces/InviteMember'; import UpgradePlan from '@/components/billing/UpgradePlan'; -export function Workspaces() { +export function Workspaces () { const { t } = useTranslation(); const userWorkspaceInfo = useUserWorkspaceInfo(); const currentWorkspaceId = useCurrentWorkspaceId(); @@ -80,7 +81,7 @@ export function Workspaces() { avatarSize={24} /> - {hoveredHeader && } + {hoveredHeader && }
}
- - - {selectedWorkspace && { - setOpen(false); - }} workspace={selectedWorkspace}/>} + + + {selectedWorkspace && { + setOpen(false); + }} + workspace={selectedWorkspace} + />} @@ -140,15 +144,15 @@ export function Workspaces() { className={'justify-start px-2'} color={'inherit'} onClick={handleSignOut} - startIcon={} + startIcon={} >{t('button.logout')} {isOwner && <> - +
!OM-AMuP?!dDU;#ig`T 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 { - if (err) { - console.error(chalk.red(`error creating symlink: ${err.message}`)); - process.exit(1); - } - console.log(chalk.green(`symlink created: `) + chalk.blue(`${fullSourcePath}`) + ' -> ' + chalk.blue(`${fullTargetPath}`)); - -}); diff --git a/frontend/appflowy_web_app/scripts/generateTailwindColors.cjs b/frontend/appflowy_web_app/scripts/generateTailwindColors.cjs deleted file mode 100644 index 83f5bb25d5..0000000000 --- a/frontend/appflowy_web_app/scripts/generateTailwindColors.cjs +++ /dev/null @@ -1,61 +0,0 @@ -const fs = require('fs'); -const path = require('path'); - -// Read CSS file -const cssFilePath = path.join(__dirname, '../src/styles/variables/light.variables.css'); -const cssContent = fs.readFileSync(cssFilePath, 'utf-8'); - -// Extract color variables -const shadowVariables = cssContent.match(/--shadow:\s.*;/g); -const colorVariables = cssContent.match(/--[\w-]+:\s*#[0-9a-fA-F]{6}/g); - -if (!colorVariables) { - console.error('No color variables found in CSS file.'); - process.exit(1); -} - -const shadows = shadowVariables.reduce((shadows, variable) => { - const [name, value] = variable.split(':').map(str => str.trim()); - const formattedName = name.replace('--', '').replace(/-/g, '_'); - const key = 'md'; - - shadows[key] = `var(${name})`; - return shadows; -}, {}); -// Generate Tailwind CSS colors configuration -// Replace -- with _ and - with _ in color variable names -const tailwindColors = colorVariables.reduce((colors, variable) => { - const [name, value] = variable.split(':').map(str => str.trim()); - const formattedName = name.replace('--', '').replace(/-/g, '_'); - const category = formattedName.split('_')[0]; - const key = formattedName.replace(`${category}_`, ''); - - if (!colors[category]) { - colors[category] = {}; - } - colors[category][key] = `var(${name})`; - return colors; -}, {}); - -const tailwindColorsFormatted = JSON.stringify(tailwindColors, null, 2) - .replace(/_/g, '-'); -const header = `/**\n` + '* Do not edit directly\n' + `* Generated on ${new Date().toUTCString()}\n` + `* Generated from $pnpm css:variables \n` + `*/\n\n`; - -// Write Tailwind CSS colors configuration to file -const tailwindColorTemplate = ` -${header} -module.exports = ${tailwindColorsFormatted}; -`; - -const tailwindShadowTemplate = ` -${header} -module.exports = ${JSON.stringify(shadows, null, 2).replace(/_/g, '-')}; -`; - -const tailwindConfigFilePath = path.join(__dirname, '../tailwind/colors.cjs'); -fs.writeFileSync(tailwindConfigFilePath, tailwindColorTemplate, 'utf-8'); - -const tailwindShadowFilePath = path.join(__dirname, '../tailwind/box-shadow.cjs'); -fs.writeFileSync(tailwindShadowFilePath, tailwindShadowTemplate, 'utf-8'); - -console.log('Tailwind CSS colors configuration generated successfully.'); diff --git a/frontend/appflowy_web_app/scripts/generate_af_icons.cjs b/frontend/appflowy_web_app/scripts/generate_af_icons.cjs deleted file mode 100644 index 9763beba56..0000000000 --- a/frontend/appflowy_web_app/scripts/generate_af_icons.cjs +++ /dev/null @@ -1,64 +0,0 @@ -const fs = require('fs'); -const path = require('path'); - -const getIconsDir = () => path.resolve(__dirname, '../public/af_icons'); - -const readSvgFile = (filePath) => { - return fs.readFileSync(filePath, 'utf8'); -}; - -const renameSvgFile = (filePath, newName) => { - const newPath = path.join(path.dirname(filePath), newName); - fs.renameSync(filePath, newPath); -}; - -const processSvgFiles = (dirPath) => { - const categories = {}; - - const traverseDir = (currentPath) => { - const items = fs.readdirSync(currentPath); - - items.forEach((item) => { - const itemPath = path.join(currentPath, item); - const stat = fs.statSync(itemPath); - - if (stat.isDirectory()) { - traverseDir(itemPath); - } else if (stat.isFile() && path.extname(item) === '.svg') { - const category = path.basename(currentPath); - const [namePart, ...keywordParts] = path.basename(item, '.svg').split('--'); - const name = namePart; - const keywords = keywordParts.length > 0 ? keywordParts[0].split('-') : []; - const svgContent = readSvgFile(itemPath); - renameSvgFile(itemPath, `${name}.svg`); - if (!categories[category]) { - categories[category] = []; - } - - categories[category].push({ - id: `${category}/${name}`, - name, - keywords, - content: svgContent, - }); - } - }); - }; - - traverseDir(dirPath); - return categories; -}; - -const outputJson = (data, outputFilePath) => { - fs.writeFileSync(outputFilePath, JSON.stringify(data, null, 2)); -}; - -const main = () => { - const iconsDirPath = getIconsDir(); - const categories = processSvgFiles(iconsDirPath); - const outputFilePath = path.join(iconsDirPath, 'icons.json'); - outputJson(categories, outputFilePath); - console.log(`JSON data has been written to ${outputFilePath}`); -}; - -main(); diff --git a/frontend/appflowy_web_app/scripts/i18n.cjs b/frontend/appflowy_web_app/scripts/i18n.cjs deleted file mode 100644 index 407a03694a..0000000000 --- a/frontend/appflowy_web_app/scripts/i18n.cjs +++ /dev/null @@ -1,63 +0,0 @@ -const languages = [ - 'ar-SA', - 'ca-ES', - 'de-DE', - 'en', - 'es-VE', - 'eu-ES', - 'fr-FR', - 'hu-HU', - 'id-ID', - 'it-IT', - 'ja-JP', - 'ko-KR', - 'pl-PL', - 'pt-BR', - 'pt-PT', - 'ru-RU', - 'sv-SE', - 'th-TH', - 'tr-TR', - 'zh-CN', - 'zh-TW', -]; - -const fs = require('fs'); -languages.forEach(language => { - const json = require(`../../resources/translations/${language}.json`); - const outputJSON = flattenJSON(json); - const output = JSON.stringify(outputJSON); - const isExistDir = fs.existsSync('./src/@types/translations'); - if (!isExistDir) { - fs.mkdirSync('./src/@types/translations'); - } - fs.writeFile(`./src/@types/translations/${language}.json`, new Uint8Array(Buffer.from(output)), (res) => { - if (res) { - console.error(res); - } - }) -}); - - -function flattenJSON(obj, prefix = '') { - let result = {}; - const pluralsKey = ["one", "other", "few", "many", "two", "zero"]; - - for (let key in obj) { - if (typeof obj[key] === 'object' && obj[key] !== null) { - - const nestedKeys = flattenJSON(obj[key], `${prefix}${key}.`); - result = { ...result, ...nestedKeys }; - } else { - let newKey = `${prefix}${key}`; - let replaceChar = '{' - if (pluralsKey.includes(key)) { - newKey = `${prefix.slice(0, -1)}_${key}`; - } - result[newKey] = obj[key].replaceAll('{', '{{').replaceAll('}', '}}'); - } - } - - return result; -} - diff --git a/frontend/appflowy_web_app/scripts/merge-coverage.cjs b/frontend/appflowy_web_app/scripts/merge-coverage.cjs deleted file mode 100644 index 1939ca4ef9..0000000000 --- a/frontend/appflowy_web_app/scripts/merge-coverage.cjs +++ /dev/null @@ -1,35 +0,0 @@ -const { execSync } = require('child_process'); -const fs = require('fs'); -const path = require('path'); -const jestCoverageFile = path.join(__dirname, '../coverage/jest/coverage-final.json'); -const cypressCoverageFile = path.join(__dirname, '../coverage/cypress/coverage-final.json'); -const nycOutputDir = path.join(__dirname, '../coverage/.nyc_output'); - -// Ensure .nyc_output directory exists -if (fs.existsSync(nycOutputDir)) { - fs.rmSync(nycOutputDir, { recursive: true }); -} -fs.mkdirSync(nycOutputDir, { recursive: true }); - -if (fs.existsSync(path.join(__dirname, '../coverage/merged'))) { - fs.rmSync(path.join(__dirname, '../coverage/merged'), { recursive: true }); -} -// Copy Jest coverage file -fs.copyFileSync(jestCoverageFile, path.join(nycOutputDir, 'jest-coverage.json')); -// Copy Cypress E2E coverage file -fs.copyFileSync(cypressCoverageFile, path.join(nycOutputDir, 'cypress-coverage.json')); - -// Merge coverage files -execSync('nyc merge ./coverage/.nyc_output ./coverage/merged/coverage-final.json', { stdio: 'inherit' }); - -// Move the merged result to the .nyc_output directory -fs.rmSync(nycOutputDir, { recursive: true }); -fs.mkdirSync(nycOutputDir, { recursive: true }); -fs.copyFileSync(path.join(__dirname, '../coverage/merged/coverage-final.json'), path.join(nycOutputDir, 'out.json')); - -// Generate final merged report -execSync('nyc report --reporter=html --reporter=text-summary --report-dir=coverage/merged --temp-dir=coverage/.nyc_output', { stdio: 'inherit' }); -console.log(`Merged coverage report written to coverage/merged`); - - - diff --git a/frontend/appflowy_web_app/src-tauri/.gitignore b/frontend/appflowy_web_app/src-tauri/.gitignore deleted file mode 100644 index 9e4914893d..0000000000 --- a/frontend/appflowy_web_app/src-tauri/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -# Generated by Cargo -# will have compiled files and executables -/target/ -.env \ No newline at end of file diff --git a/frontend/appflowy_web_app/src-tauri/Cargo.lock b/frontend/appflowy_web_app/src-tauri/Cargo.lock deleted file mode 100644 index ea71138635..0000000000 --- a/frontend/appflowy_web_app/src-tauri/Cargo.lock +++ /dev/null @@ -1,9469 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "accessory" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "850bb534b9dc04744fbbb71d30ad6d25a7e4cf6dc33e223c81ef3a92ebab4e0b" -dependencies = [ - "macroific", - "proc-macro2", - "quote", - "syn 2.0.55", -] - -[[package]] -name = "addr2line" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "aead" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" -dependencies = [ - "crypto-common", - "generic-array", -] - -[[package]] -name = "aes" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" -dependencies = [ - "cfg-if", - "cipher", - "cpufeatures", -] - -[[package]] -name = "aes-gcm" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" -dependencies = [ - "aead", - "aes", - "cipher", - "ctr", - "ghash", - "subtle", -] - -[[package]] -name = "again" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05802a5ad4d172eaf796f7047b42d0af9db513585d16d4169660a21613d34b93" -dependencies = [ - "log", - "rand 0.7.3", - "wasm-timer", -] - -[[package]] -name = "ahash" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" -dependencies = [ - "getrandom 0.2.12", - "once_cell", - "version_check", -] - -[[package]] -name = "ahash" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" -dependencies = [ - "cfg-if", - "getrandom 0.2.12", - "once_cell", - "version_check", - "zerocopy", -] - -[[package]] -name = "aho-corasick" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" -dependencies = [ - "memchr", -] - -[[package]] -name = "allo-isolate" -version = "0.1.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b6d794345b06592d0ebeed8e477e41b71e5a0a49df4fc0e4184d5938b99509" -dependencies = [ - "atomic", - "pin-project", -] - -[[package]] -name = "alloc-no-stdlib" -version = "2.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" - -[[package]] -name = "alloc-stdlib" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" -dependencies = [ - "alloc-no-stdlib", -] - -[[package]] -name = "allocator-api2" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" - -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] -name = "anyhow" -version = "1.0.94" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7" - -[[package]] -name = "app-error" -version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ea131f0baab67defe7591067357eced490072372#ea131f0baab67defe7591067357eced490072372" -dependencies = [ - "anyhow", - "bincode", - "getrandom 0.2.12", - "reqwest", - "serde", - "serde_json", - "serde_repr", - "thiserror", - "tokio", - "tsify", - "url", - "uuid", - "wasm-bindgen", -] - -[[package]] -name = "appflowy-ai-client" -version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ea131f0baab67defe7591067357eced490072372#ea131f0baab67defe7591067357eced490072372" -dependencies = [ - "anyhow", - "bytes", - "futures", - "pin-project", - "serde", - "serde_json", - "serde_repr", - "thiserror", -] - -[[package]] -name = "appflowy-local-ai" -version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-LocalAI?rev=6f064efe232268f8d396edbb4b84d57fbb640f13#6f064efe232268f8d396edbb4b84d57fbb640f13" -dependencies = [ - "anyhow", - "appflowy-plugin", - "bytes", - "reqwest", - "serde", - "serde_json", - "tokio", - "tokio-stream", - "tokio-util", - "tracing", - "zip 2.2.0", - "zip-extensions", -] - -[[package]] -name = "appflowy-plugin" -version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-LocalAI?rev=6f064efe232268f8d396edbb4b84d57fbb640f13#6f064efe232268f8d396edbb4b84d57fbb640f13" -dependencies = [ - "anyhow", - "cfg-if", - "crossbeam-utils", - "log", - "once_cell", - "parking_lot 0.12.1", - "serde", - "serde_json", - "thiserror", - "tokio", - "tokio-stream", - "tracing", - "xattr", -] - -[[package]] -name = "appflowy_tauri" -version = "0.0.0" -dependencies = [ - "bytes", - "dotenv", - "flowy-ai", - "flowy-config", - "flowy-core", - "flowy-date", - "flowy-document", - "flowy-error", - "flowy-notification", - "flowy-user", - "lib-dispatch", - "semver", - "serde", - "serde_json", - "tauri", - "tauri-build", - "tauri-plugin-deep-link", - "tauri-utils", - "tracing", - "uuid", -] - -[[package]] -name = "arbitrary" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" -dependencies = [ - "derive_arbitrary", -] - -[[package]] -name = "arboard" -version = "3.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2041f1943049c7978768d84e6d0fd95de98b76d6c4727b09e78ec253d29fa58" -dependencies = [ - "clipboard-win", - "core-graphics 0.23.1", - "image", - "log", - "objc", - "objc-foundation", - "objc_id", - "parking_lot 0.12.1", - "thiserror", - "windows-sys 0.48.0", - "wl-clipboard-rs", - "x11rb", -] - -[[package]] -name = "arc-swap" -version = "1.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" - -[[package]] -name = "arrayvec" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" - -[[package]] -name = "async-compression" -version = "0.4.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "103db485efc3e41214fe4fda9f3dbeae2eb9082f48fd236e6095627a9422066e" -dependencies = [ - "bzip2", - "deflate64", - "flate2", - "futures-core", - "futures-io", - "memchr", - "pin-project-lite", - "xz2", - "zstd 0.13.2", - "zstd-safe 7.2.0", -] - -[[package]] -name = "async-lock" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" -dependencies = [ - "event-listener", - "event-listener-strategy", - "pin-project-lite", -] - -[[package]] -name = "async-recursion" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.55", -] - -[[package]] -name = "async-stream" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" -dependencies = [ - "async-stream-impl", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-stream-impl" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.55", -] - -[[package]] -name = "async-trait" -version = "0.1.81" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.55", -] - -[[package]] -name = "async_zip" -version = "0.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b9f7252833d5ed4b00aa9604b563529dd5e11de9c23615de2dcdf91eb87b52" -dependencies = [ - "async-compression", - "chrono", - "crc32fast", - "futures-lite", - "pin-project", - "thiserror", - "tokio", - "tokio-util", -] - -[[package]] -name = "atk" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c3d816ce6f0e2909a96830d6911c2aff044370b1ef92d7f267b43bae5addedd" -dependencies = [ - "atk-sys", - "bitflags 1.3.2", - "glib", - "libc", -] - -[[package]] -name = "atk-sys" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58aeb089fb698e06db8089971c7ee317ab9644bade33383f63631437b03aafb6" -dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps 6.2.2", -] - -[[package]] -name = "atomic" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59bdb34bc650a32731b31bd8f0829cc15d24a708ee31559e0bb34f2bc320cba" - -[[package]] -name = "atomic_refcell" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41e67cd8309bbd06cd603a9e693a784ac2e5d1e955f11286e355089fcab3047c" - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "backtrace" -version = "0.3.71" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" -dependencies = [ - "addr2line", - "cc", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", -] - -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - -[[package]] -name = "base64" -version = "0.21.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" - -[[package]] -name = "base64" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" - -[[package]] -name = "base64ct" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" - -[[package]] -name = "bincode" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" -dependencies = [ - "serde", -] - -[[package]] -name = "bindgen" -version = "0.69.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" -dependencies = [ - "bitflags 2.5.0", - "cexpr", - "clang-sys", - "itertools 0.12.1", - "lazy_static", - "lazycell", - "proc-macro2", - "quote", - "regex", - "rustc-hash", - "shlex", - "syn 2.0.55", -] - -[[package]] -name = "bit-set" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" -dependencies = [ - "bit-vec", -] - -[[package]] -name = "bit-vec" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" - -[[package]] -name = "bitpacking" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c1d3e2bfd8d06048a179f7b17afc3188effa10385e7b00dc65af6aae732ea92" -dependencies = [ - "crunchy", -] - -[[package]] -name = "bitvec" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" -dependencies = [ - "funty", - "radium", - "tap", - "wyz", -] - -[[package]] -name = "block" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - -[[package]] -name = "borsh" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f58b559fd6448c6e2fd0adb5720cd98a2506594cafa4737ff98c396f3e82f667" -dependencies = [ - "borsh-derive", - "cfg_aliases", -] - -[[package]] -name = "borsh-derive" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7aadb5b6ccbd078890f6d7003694e33816e6b784358f18e15e7e6d9f065a57cd" -dependencies = [ - "once_cell", - "proc-macro-crate 3.1.0", - "proc-macro2", - "quote", - "syn 2.0.55", - "syn_derive", -] - -[[package]] -name = "brotli" -version = "3.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d640d25bc63c50fb1f0b545ffd80207d2e10a4c965530809b40ba3386825c391" -dependencies = [ - "alloc-no-stdlib", - "alloc-stdlib", - "brotli-decompressor", -] - -[[package]] -name = "brotli-decompressor" -version = "2.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e2e4afe60d7dd600fdd3de8d0f08c2b7ec039712e3b6137ff98b7004e82de4f" -dependencies = [ - "alloc-no-stdlib", - "alloc-stdlib", -] - -[[package]] -name = "bstr" -version = "1.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706" -dependencies = [ - "memchr", - "serde", -] - -[[package]] -name = "bumpalo" -version = "3.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" - -[[package]] -name = "bytecheck" -version = "0.6.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2" -dependencies = [ - "bytecheck_derive", - "ptr_meta", - "simdutf8", -] - -[[package]] -name = "bytecheck_derive" -version = "0.6.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "bytemuck" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d6d68c57235a3a081186990eca2867354726650f42f7516ca50c28d6281fd15" - -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - -[[package]] -name = "bytes" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" -dependencies = [ - "serde", -] - -[[package]] -name = "bzip2" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" -dependencies = [ - "bzip2-sys", - "libc", -] - -[[package]] -name = "bzip2-sys" -version = "0.1.11+1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" -dependencies = [ - "cc", - "libc", - "pkg-config", -] - -[[package]] -name = "cairo-rs" -version = "0.15.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c76ee391b03d35510d9fa917357c7f1855bd9a6659c95a1b392e33f49b3369bc" -dependencies = [ - "bitflags 1.3.2", - "cairo-sys-rs", - "glib", - "libc", - "thiserror", -] - -[[package]] -name = "cairo-sys-rs" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c55d429bef56ac9172d25fecb85dc8068307d17acd74b377866b7a1ef25d3c8" -dependencies = [ - "glib-sys", - "libc", - "system-deps 6.2.2", -] - -[[package]] -name = "cargo_toml" -version = "0.15.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "599aa35200ffff8f04c1925aa1acc92fa2e08874379ef42e210a80e527e60838" -dependencies = [ - "serde", - "toml 0.7.8", -] - -[[package]] -name = "cc" -version = "1.0.90" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" -dependencies = [ - "jobserver", - "libc", -] - -[[package]] -name = "census" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f4c707c6a209cbe82d10abd08e1ea8995e9ea937d2550646e02798948992be0" - -[[package]] -name = "cesu8" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" - -[[package]] -name = "cexpr" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" -dependencies = [ - "nom", -] - -[[package]] -name = "cfb" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f" -dependencies = [ - "byteorder", - "fnv", - "uuid", -] - -[[package]] -name = "cfg-expr" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3431df59f28accaf4cb4eed4a9acc66bea3f3c3753aa6cdc2f024174ef232af7" -dependencies = [ - "smallvec", -] - -[[package]] -name = "cfg-expr" -version = "0.15.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa50868b64a9a6fda9d593ce778849ea8715cd2a3d2cc17ffdb4a2f2f2f1961d" -dependencies = [ - "smallvec", - "target-lexicon", -] - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "cfg_aliases" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" - -[[package]] -name = "chrono" -version = "0.4.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" -dependencies = [ - "android-tzdata", - "iana-time-zone", - "js-sys", - "num-traits", - "serde", - "wasm-bindgen", - "windows-targets 0.52.4", -] - -[[package]] -name = "chrono-tz" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d59ae0466b83e838b81a54256c39d5d7c20b9d7daa10510a242d9b75abd5936e" -dependencies = [ - "chrono", - "chrono-tz-build 0.2.1", - "phf 0.11.2", -] - -[[package]] -name = "chrono-tz" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd6dd8046d00723a59a2f8c5f295c515b9bb9a331ee4f8f3d4dd49e428acd3b6" -dependencies = [ - "chrono", - "chrono-tz-build 0.4.0", - "phf 0.11.2", -] - -[[package]] -name = "chrono-tz-build" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "433e39f13c9a060046954e0592a8d0a4bcb1040125cbf91cb8ee58964cfb350f" -dependencies = [ - "parse-zoneinfo", - "phf 0.11.2", - "phf_codegen 0.11.2", -] - -[[package]] -name = "chrono-tz-build" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94fea34d77a245229e7746bd2beb786cd2a896f306ff491fb8cecb3074b10a7" -dependencies = [ - "parse-zoneinfo", - "phf_codegen 0.11.2", -] - -[[package]] -name = "cipher" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" -dependencies = [ - "crypto-common", - "inout", -] - -[[package]] -name = "clang-sys" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" -dependencies = [ - "glob", - "libc", - "libloading", -] - -[[package]] -name = "client-api" -version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ea131f0baab67defe7591067357eced490072372#ea131f0baab67defe7591067357eced490072372" -dependencies = [ - "again", - "anyhow", - "app-error", - "arc-swap", - "async-trait", - "base64 0.22.1", - "bincode", - "brotli", - "bytes", - "chrono", - "client-api-entity", - "client-websocket", - "collab", - "collab-rt-entity", - "collab-rt-protocol", - "futures", - "futures-core", - "futures-util", - "getrandom 0.2.12", - "gotrue", - "infra", - "lazy_static", - "md5", - "mime", - "mime_guess", - "parking_lot 0.12.1", - "percent-encoding", - "pin-project", - "prost 0.13.3", - "rayon", - "reqwest", - "scraper 0.17.1", - "semver", - "serde", - "serde_json", - "serde_repr", - "serde_urlencoded", - "shared-entity", - "thiserror", - "tokio", - "tokio-retry", - "tokio-stream", - "tokio-util", - "tracing", - "url", - "uuid", - "wasm-bindgen-futures", - "yrs", - "zstd 0.13.2", -] - -[[package]] -name = "client-api-entity" -version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ea131f0baab67defe7591067357eced490072372#ea131f0baab67defe7591067357eced490072372" -dependencies = [ - "collab-entity", - "collab-rt-entity", - "database-entity", - "gotrue-entity", - "shared-entity", - "uuid", -] - -[[package]] -name = "client-websocket" -version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ea131f0baab67defe7591067357eced490072372#ea131f0baab67defe7591067357eced490072372" -dependencies = [ - "futures-channel", - "futures-util", - "http", - "httparse", - "js-sys", - "percent-encoding", - "thiserror", - "tokio", - "tokio-tungstenite", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "clipboard-win" -version = "5.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d517d4b86184dbb111d3556a10f1c8a04da7428d2987bf1081602bf11c3aa9ee" -dependencies = [ - "error-code", -] - -[[package]] -name = "cmd_lib" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5f4cbdcab51ca635c5b19c85ece4072ea42e0d2360242826a6fc96fb11f0d40" -dependencies = [ - "cmd_lib_macros", - "env_logger", - "faccess", - "lazy_static", - "log", - "os_pipe", -] - -[[package]] -name = "cmd_lib_macros" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae881960f7e2a409f91ef0b1c09558cf293031a1d6e8b45f908311f2a43f5fdf" -dependencies = [ - "proc-macro-error", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "cocoa" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f425db7937052c684daec3bd6375c8abe2d146dca4b8b143d6db777c39138f3a" -dependencies = [ - "bitflags 1.3.2", - "block", - "cocoa-foundation", - "core-foundation", - "core-graphics 0.22.3", - "foreign-types 0.3.2", - "libc", - "objc", -] - -[[package]] -name = "cocoa-foundation" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c6234cbb2e4c785b456c0644748b1ac416dd045799740356f8363dfe00c93f7" -dependencies = [ - "bitflags 1.3.2", - "block", - "core-foundation", - "core-graphics-types", - "libc", - "objc", -] - -[[package]] -name = "collab" -version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=67b31e4bcc64d81332c37efecb0a8618681f1db9#67b31e4bcc64d81332c37efecb0a8618681f1db9" -dependencies = [ - "anyhow", - "arc-swap", - "async-trait", - "bincode", - "bytes", - "chrono", - "js-sys", - "lazy_static", - "serde", - "serde_json", - "serde_repr", - "thiserror", - "tokio", - "tokio-stream", - "tracing", - "unicode-segmentation", - "web-sys", - "yrs", -] - -[[package]] -name = "collab-database" -version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=67b31e4bcc64d81332c37efecb0a8618681f1db9#67b31e4bcc64d81332c37efecb0a8618681f1db9" -dependencies = [ - "anyhow", - "async-trait", - "base64 0.22.1", - "chrono", - "chrono-tz 0.10.0", - "collab", - "collab-entity", - "csv", - "dashmap 5.5.3", - "fancy-regex 0.13.0", - "futures", - "getrandom 0.2.12", - "iana-time-zone", - "js-sys", - "lazy_static", - "nanoid", - "percent-encoding", - "rayon", - "rust_decimal", - "rusty-money", - "serde", - "serde_json", - "serde_repr", - "sha2", - "strum", - "strum_macros 0.25.3", - "thiserror", - "tokio", - "tokio-stream", - "tokio-util", - "tracing", - "uuid", - "yrs", -] - -[[package]] -name = "collab-document" -version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=67b31e4bcc64d81332c37efecb0a8618681f1db9#67b31e4bcc64d81332c37efecb0a8618681f1db9" -dependencies = [ - "anyhow", - "arc-swap", - "collab", - "collab-entity", - "getrandom 0.2.12", - "markdown", - "nanoid", - "serde", - "serde_json", - "thiserror", - "tokio", - "tokio-stream", - "tracing", - "uuid", -] - -[[package]] -name = "collab-entity" -version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=67b31e4bcc64d81332c37efecb0a8618681f1db9#67b31e4bcc64d81332c37efecb0a8618681f1db9" -dependencies = [ - "anyhow", - "bytes", - "collab", - "getrandom 0.2.12", - "prost 0.13.3", - "prost-build", - "protoc-bin-vendored", - "serde", - "serde_json", - "serde_repr", - "thiserror", - "uuid", - "walkdir", -] - -[[package]] -name = "collab-folder" -version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=67b31e4bcc64d81332c37efecb0a8618681f1db9#67b31e4bcc64d81332c37efecb0a8618681f1db9" -dependencies = [ - "anyhow", - "arc-swap", - "chrono", - "collab", - "collab-entity", - "dashmap 5.5.3", - "getrandom 0.2.12", - "serde", - "serde_json", - "serde_repr", - "thiserror", - "tokio", - "tokio-stream", - "tracing", - "uuid", -] - -[[package]] -name = "collab-importer" -version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=67b31e4bcc64d81332c37efecb0a8618681f1db9#67b31e4bcc64d81332c37efecb0a8618681f1db9" -dependencies = [ - "anyhow", - "async-recursion", - "async-trait", - "async_zip", - "base64 0.22.1", - "chrono", - "collab", - "collab-database", - "collab-document", - "collab-entity", - "collab-folder", - "csv", - "fancy-regex 0.13.0", - "futures", - "futures-lite", - "futures-util", - "fxhash", - "hex", - "markdown", - "percent-encoding", - "rayon", - "sanitize-filename", - "serde", - "serde_json", - "sha2", - "thiserror", - "tokio", - "tokio-util", - "tracing", - "uuid", - "walkdir", - "zip 0.6.6", -] - -[[package]] -name = "collab-integrate" -version = "0.1.0" -dependencies = [ - "anyhow", - "arc-swap", - "async-trait", - "collab", - "collab-database", - "collab-document", - "collab-entity", - "collab-folder", - "collab-plugins", - "collab-user", - "diesel", - "flowy-error", - "flowy-sqlite", - "futures", - "lib-infra", - "serde", - "serde_json", - "tokio", - "tracing", -] - -[[package]] -name = "collab-plugins" -version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=67b31e4bcc64d81332c37efecb0a8618681f1db9#67b31e4bcc64d81332c37efecb0a8618681f1db9" -dependencies = [ - "anyhow", - "async-stream", - "async-trait", - "bincode", - "bytes", - "chrono", - "collab", - "collab-entity", - "futures", - "futures-util", - "getrandom 0.2.12", - "indexed_db_futures", - "js-sys", - "lazy_static", - "rand 0.8.5", - "rocksdb", - "serde", - "serde_json", - "similar 2.4.0", - "smallvec", - "thiserror", - "tokio", - "tokio-retry", - "tokio-stream", - "tracing", - "tracing-wasm", - "uuid", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "yrs", -] - -[[package]] -name = "collab-rt-entity" -version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ea131f0baab67defe7591067357eced490072372#ea131f0baab67defe7591067357eced490072372" -dependencies = [ - "anyhow", - "bincode", - "bytes", - "chrono", - "client-websocket", - "collab", - "collab-entity", - "collab-rt-protocol", - "database-entity", - "prost 0.13.3", - "prost-build", - "protoc-bin-vendored", - "serde", - "serde_json", - "serde_repr", - "thiserror", - "tokio-tungstenite", - "yrs", -] - -[[package]] -name = "collab-rt-protocol" -version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ea131f0baab67defe7591067357eced490072372#ea131f0baab67defe7591067357eced490072372" -dependencies = [ - "anyhow", - "async-trait", - "bincode", - "collab", - "collab-entity", - "serde", - "thiserror", - "tokio", - "tracing", - "yrs", -] - -[[package]] -name = "collab-user" -version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=67b31e4bcc64d81332c37efecb0a8618681f1db9#67b31e4bcc64d81332c37efecb0a8618681f1db9" -dependencies = [ - "anyhow", - "collab", - "collab-entity", - "getrandom 0.2.12", - "serde", - "serde_json", - "tokio", - "tokio-stream", - "tracing", -] - -[[package]] -name = "color_quant" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" - -[[package]] -name = "combine" -version = "4.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" -dependencies = [ - "bytes", - "memchr", -] - -[[package]] -name = "concurrent-queue" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "console" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3993e6445baa160675931ec041a5e03ca84b9c6e32a056150d3aa2bdda0a1f45" -dependencies = [ - "encode_unicode", - "lazy_static", - "libc", - "regex", - "terminal_size", - "unicode-width", - "winapi", -] - -[[package]] -name = "constant_time_eq" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" - -[[package]] -name = "constant_time_eq" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" - -[[package]] -name = "convert_case" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" - -[[package]] -name = "cookie" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7efb37c3e1ccb1ff97164ad95ac1606e8ccd35b3fa0a7d99a304c7f4a428cc24" -dependencies = [ - "percent-encoding", - "time", - "version_check", -] - -[[package]] -name = "cookie_store" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "387461abbc748185c3a6e1673d826918b450b87ff22639429c694619a83b6cf6" -dependencies = [ - "cookie", - "idna 0.3.0", - "log", - "publicsuffix", - "serde", - "serde_derive", - "serde_json", - "time", - "url", -] - -[[package]] -name = "core-foundation" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" - -[[package]] -name = "core-graphics" -version = "0.22.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "core-graphics-types", - "foreign-types 0.3.2", - "libc", -] - -[[package]] -name = "core-graphics" -version = "0.23.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "970a29baf4110c26fedbc7f82107d42c23f7e88e404c4577ed73fe99ff85a212" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "core-graphics-types", - "foreign-types 0.5.0", - "libc", -] - -[[package]] -name = "core-graphics-types" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "libc", -] - -[[package]] -name = "cpufeatures" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" -dependencies = [ - "libc", -] - -[[package]] -name = "crc" -version = "3.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" -dependencies = [ - "crc-catalog", -] - -[[package]] -name = "crc-catalog" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" - -[[package]] -name = "crc32fast" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crossbeam-channel" -version = "0.5.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" -dependencies = [ - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" - -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "rand_core 0.6.4", - "typenum", -] - -[[package]] -name = "cssparser" -version = "0.27.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "754b69d351cdc2d8ee09ae203db831e005560fc6030da058f86ad60c92a9cb0a" -dependencies = [ - "cssparser-macros", - "dtoa-short", - "itoa 0.4.8", - "matches", - "phf 0.8.0", - "proc-macro2", - "quote", - "smallvec", - "syn 1.0.109", -] - -[[package]] -name = "cssparser" -version = "0.31.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b3df4f93e5fbbe73ec01ec8d3f68bba73107993a5b1e7519273c32db9b0d5be" -dependencies = [ - "cssparser-macros", - "dtoa-short", - "itoa 1.0.10", - "phf 0.8.0", - "smallvec", -] - -[[package]] -name = "cssparser-macros" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" -dependencies = [ - "quote", - "syn 2.0.55", -] - -[[package]] -name = "csv" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe" -dependencies = [ - "csv-core", - "itoa 1.0.10", - "ryu", - "serde", -] - -[[package]] -name = "csv-core" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70" -dependencies = [ - "memchr", -] - -[[package]] -name = "ctor" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad291aa74992b9b7a7e88c38acbbf6ad7e107f1d90ee8775b7bc1fc3394f485c" -dependencies = [ - "quote", - "syn 2.0.55", -] - -[[package]] -name = "ctr" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" -dependencies = [ - "cipher", -] - -[[package]] -name = "darling" -version = "0.20.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54e36fcd13ed84ffdfda6f5be89b31287cbb80c439841fe69e04841435464391" -dependencies = [ - "darling_core", - "darling_macro", -] - -[[package]] -name = "darling_core" -version = "0.20.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim 0.10.0", - "syn 2.0.55", -] - -[[package]] -name = "darling_macro" -version = "0.20.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" -dependencies = [ - "darling_core", - "quote", - "syn 2.0.55", -] - -[[package]] -name = "dashmap" -version = "5.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" -dependencies = [ - "cfg-if", - "hashbrown 0.14.3", - "lock_api", - "once_cell", - "parking_lot_core 0.9.9", -] - -[[package]] -name = "dashmap" -version = "6.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "804c8821570c3f8b70230c2ba75ffa5c0f9a4189b9a432b6656c536712acae28" -dependencies = [ - "cfg-if", - "crossbeam-utils", - "hashbrown 0.14.3", - "lock_api", - "once_cell", - "parking_lot_core 0.9.9", -] - -[[package]] -name = "data-encoding" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" - -[[package]] -name = "database-entity" -version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ea131f0baab67defe7591067357eced490072372#ea131f0baab67defe7591067357eced490072372" -dependencies = [ - "anyhow", - "app-error", - "appflowy-ai-client", - "bincode", - "bytes", - "chrono", - "collab-entity", - "infra", - "prost 0.13.3", - "serde", - "serde_json", - "serde_repr", - "thiserror", - "tracing", - "uuid", - "validator 0.19.0", -] - -[[package]] -name = "date_time_parser" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0521d96e513670773ac503e5f5239178c3aef16cffda1e77a3cdbdbe993fb5a" -dependencies = [ - "chrono", - "regex", -] - -[[package]] -name = "deflate64" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da692b8d1080ea3045efaab14434d40468c3d8657e42abddfffca87b428f4c1b" - -[[package]] -name = "delegate-display" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98a85201f233142ac819bbf6226e36d0b5e129a47bd325084674261c82d4cd66" -dependencies = [ - "macroific", - "proc-macro2", - "quote", - "syn 2.0.55", -] - -[[package]] -name = "deranged" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" -dependencies = [ - "powerfmt", - "serde", -] - -[[package]] -name = "derivative" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "derive-new" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d150dea618e920167e5973d70ae6ece4385b7164e0d799fe7c122dd0a5d912ad" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.55", -] - -[[package]] -name = "derive_arbitrary" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.55", -] - -[[package]] -name = "derive_more" -version = "0.99.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" -dependencies = [ - "convert_case", - "proc-macro2", - "quote", - "rustc_version", - "syn 1.0.109", -] - -[[package]] -name = "deunicode" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6e854126756c496b8c81dec88f9a706b15b875c5849d4097a3854476b9fdf94" - -[[package]] -name = "diesel" -version = "2.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03fc05c17098f21b89bc7d98fe1dd3cce2c11c2ad8e145f2a44fe08ed28eb559" -dependencies = [ - "chrono", - "diesel_derives", - "libsqlite3-sys", - "r2d2", - "serde_json", - "time", -] - -[[package]] -name = "diesel_derives" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d02eecb814ae714ffe61ddc2db2dd03e6c49a42e269b5001355500d431cce0c" -dependencies = [ - "diesel_table_macro_syntax", - "proc-macro2", - "quote", - "syn 2.0.55", -] - -[[package]] -name = "diesel_migrations" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6036b3f0120c5961381b570ee20a02432d7e2d27ea60de9578799cf9156914ac" -dependencies = [ - "diesel", - "migrations_internals", - "migrations_macros", -] - -[[package]] -name = "diesel_table_macro_syntax" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc5557efc453706fed5e4fa85006fe9817c224c3f480a34c7e5959fd700921c5" -dependencies = [ - "syn 2.0.55", -] - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "crypto-common", - "subtle", -] - -[[package]] -name = "dirs" -version = "5.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" -dependencies = [ - "dirs-sys", -] - -[[package]] -name = "dirs-next" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" -dependencies = [ - "cfg-if", - "dirs-sys-next", -] - -[[package]] -name = "dirs-sys" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" -dependencies = [ - "libc", - "option-ext", - "redox_users", - "windows-sys 0.48.0", -] - -[[package]] -name = "dirs-sys-next" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" -dependencies = [ - "libc", - "redox_users", - "winapi", -] - -[[package]] -name = "dispatch" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" - -[[package]] -name = "displaydoc" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.55", -] - -[[package]] -name = "dlib" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" -dependencies = [ - "libloading", -] - -[[package]] -name = "dotenv" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" - -[[package]] -name = "downcast-rs" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" - -[[package]] -name = "dtoa" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" - -[[package]] -name = "dtoa-short" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbaceec3c6e4211c79e7b1800fb9680527106beb2f9c51904a3210c03a448c74" -dependencies = [ - "dtoa", -] - -[[package]] -name = "dunce" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" - -[[package]] -name = "dyn-clone" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" - -[[package]] -name = "ego-tree" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a68a4904193147e0a8dec3314640e6db742afd5f6e634f428a6af230d9b3591" - -[[package]] -name = "either" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" - -[[package]] -name = "embed-resource" -version = "2.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6985554d0688b687c5cb73898a34fbe3ad6c24c58c238a4d91d5e840670ee9d" -dependencies = [ - "cc", - "memchr", - "rustc_version", - "toml 0.8.12", - "vswhom", - "winreg 0.52.0", -] - -[[package]] -name = "embed_plist" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7" - -[[package]] -name = "encode_unicode" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" - -[[package]] -name = "encoding_rs" -version = "0.8.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "env_logger" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" -dependencies = [ - "humantime", - "is-terminal", - "log", - "regex", - "termcolor", -] - -[[package]] -name = "equivalent" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" - -[[package]] -name = "errno" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "error-code" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0474425d51df81997e2f90a21591180b38eccf27292d755f3e30750225c175b" - -[[package]] -name = "event-listener" -version = "5.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" -dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite", -] - -[[package]] -name = "event-listener-strategy" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" -dependencies = [ - "event-listener", - "pin-project-lite", -] - -[[package]] -name = "faccess" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59ae66425802d6a903e268ae1a08b8c38ba143520f227a205edf4e9c7e3e26d5" -dependencies = [ - "bitflags 1.3.2", - "libc", - "winapi", -] - -[[package]] -name = "fancy-regex" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0678ab2d46fa5195aaf59ad034c083d351377d4af57f3e073c074d0da3e3c766" -dependencies = [ - "bit-set", - "regex", -] - -[[package]] -name = "fancy-regex" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b95f7c0680e4142284cf8b22c14a476e87d61b004a3a0861872b32ef7ead40a2" -dependencies = [ - "bit-set", - "regex", -] - -[[package]] -name = "fancy-regex" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "531e46835a22af56d1e3b66f04844bed63158bc094a628bec1d321d9b4c44bf2" -dependencies = [ - "bit-set", - "regex-automata 0.4.6", - "regex-syntax 0.8.2", -] - -[[package]] -name = "fancy_constructor" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f71f317e4af73b2f8f608fac190c52eac4b1879d2145df1db2fe48881ca69435" -dependencies = [ - "macroific", - "proc-macro2", - "quote", - "syn 2.0.55", -] - -[[package]] -name = "fastdivide" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59668941c55e5c186b8b58c391629af56774ec768f73c08bbcd56f09348eb00b" - -[[package]] -name = "fastrand" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" -dependencies = [ - "getrandom 0.2.12", -] - -[[package]] -name = "fdeflate" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645" -dependencies = [ - "simd-adler32", -] - -[[package]] -name = "field-offset" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" -dependencies = [ - "memoffset", - "rustc_version", -] - -[[package]] -name = "filetime" -version = "0.2.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall 0.4.1", - "windows-sys 0.52.0", -] - -[[package]] -name = "fixedbitset" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" - -[[package]] -name = "flate2" -version = "1.0.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" -dependencies = [ - "crc32fast", - "miniz_oxide", -] - -[[package]] -name = "flowy-ai" -version = "0.1.0" -dependencies = [ - "allo-isolate", - "anyhow", - "appflowy-local-ai", - "appflowy-plugin", - "arc-swap", - "base64 0.21.7", - "bytes", - "collab-integrate", - "dashmap 6.0.1", - "flowy-ai-pub", - "flowy-codegen", - "flowy-derive", - "flowy-error", - "flowy-notification", - "flowy-sqlite", - "flowy-storage-pub", - "futures", - "futures-util", - "lib-dispatch", - "lib-infra", - "log", - "md5", - "notify", - "pin-project", - "protobuf", - "reqwest", - "serde", - "serde_json", - "sha2", - "strum_macros 0.21.1", - "tokio", - "tokio-stream", - "tokio-util", - "tracing", - "uuid", - "validator 0.18.1", - "zip 2.2.0", - "zip-extensions", -] - -[[package]] -name = "flowy-ai-pub" -version = "0.1.0" -dependencies = [ - "bytes", - "client-api", - "flowy-error", - "futures", - "lib-infra", - "serde_json", -] - -[[package]] -name = "flowy-ast" -version = "0.1.0" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "flowy-codegen" -version = "0.1.0" -dependencies = [ - "cmd_lib", - "console", - "fancy-regex 0.10.0", - "flowy-ast", - "itertools 0.10.5", - "lazy_static", - "log", - "phf 0.8.0", - "protoc-bin-vendored", - "protoc-rust", - "quote", - "serde", - "serde_json", - "similar 1.3.0", - "syn 1.0.109", - "tera", - "toml 0.5.11", - "walkdir", -] - -[[package]] -name = "flowy-config" -version = "0.1.0" -dependencies = [ - "bytes", - "flowy-codegen", - "flowy-derive", - "flowy-error", - "flowy-sqlite", - "lib-dispatch", - "protobuf", - "strum_macros 0.21.1", -] - -[[package]] -name = "flowy-core" -version = "0.1.0" -dependencies = [ - "anyhow", - "appflowy-local-ai", - "arc-swap", - "base64 0.21.7", - "bytes", - "client-api", - "collab", - "collab-entity", - "collab-folder", - "collab-integrate", - "collab-plugins", - "dashmap 6.0.1", - "diesel", - "flowy-ai", - "flowy-ai-pub", - "flowy-config", - "flowy-database-pub", - "flowy-database2", - "flowy-date", - "flowy-document", - "flowy-document-pub", - "flowy-error", - "flowy-folder", - "flowy-folder-pub", - "flowy-search", - "flowy-search-pub", - "flowy-server", - "flowy-server-pub", - "flowy-sqlite", - "flowy-storage", - "flowy-storage-pub", - "flowy-user", - "flowy-user-pub", - "futures", - "futures-core", - "lib-dispatch", - "lib-infra", - "lib-log", - "semver", - "serde", - "serde_json", - "serde_repr", - "sysinfo", - "tokio", - "tokio-stream", - "tracing", - "uuid", - "walkdir", -] - -[[package]] -name = "flowy-database-pub" -version = "0.1.0" -dependencies = [ - "anyhow", - "client-api", - "collab", - "collab-entity", - "flowy-error", - "lib-infra", -] - -[[package]] -name = "flowy-database2" -version = "0.1.0" -dependencies = [ - "anyhow", - "arc-swap", - "async-stream", - "async-trait", - "bytes", - "chrono", - "chrono-tz 0.8.6", - "collab", - "collab-database", - "collab-entity", - "collab-integrate", - "collab-plugins", - "csv", - "dashmap 6.0.1", - "fancy-regex 0.11.0", - "flowy-codegen", - "flowy-database-pub", - "flowy-derive", - "flowy-error", - "flowy-notification", - "futures", - "indexmap 2.2.6", - "lazy_static", - "lib-dispatch", - "lib-infra", - "moka", - "nanoid", - "protobuf", - "rayon", - "rust_decimal", - "rusty-money", - "serde", - "serde_json", - "serde_repr", - "strum", - "strum_macros 0.25.3", - "tokio", - "tokio-util", - "tracing", - "url", - "validator 0.18.1", -] - -[[package]] -name = "flowy-date" -version = "0.1.0" -dependencies = [ - "bytes", - "chrono", - "date_time_parser", - "fancy-regex 0.11.0", - "flowy-codegen", - "flowy-derive", - "flowy-error", - "lib-dispatch", - "protobuf", - "strum_macros 0.21.1", - "tracing", -] - -[[package]] -name = "flowy-derive" -version = "0.1.0" -dependencies = [ - "dashmap 6.0.1", - "flowy-ast", - "flowy-codegen", - "lazy_static", - "proc-macro2", - "quote", - "serde_json", - "syn 1.0.109", - "walkdir", -] - -[[package]] -name = "flowy-document" -version = "0.1.0" -dependencies = [ - "anyhow", - "bytes", - "collab", - "collab-document", - "collab-entity", - "collab-integrate", - "collab-plugins", - "dashmap 6.0.1", - "flowy-codegen", - "flowy-derive", - "flowy-document-pub", - "flowy-error", - "flowy-notification", - "flowy-storage-pub", - "futures", - "getrandom 0.2.12", - "indexmap 2.2.6", - "lib-dispatch", - "lib-infra", - "nanoid", - "protobuf", - "scraper 0.18.1", - "serde", - "serde_json", - "strum_macros 0.21.1", - "tokio", - "tokio-stream", - "tracing", - "uuid", - "validator 0.18.1", -] - -[[package]] -name = "flowy-document-pub" -version = "0.1.0" -dependencies = [ - "anyhow", - "collab", - "collab-document", - "flowy-error", - "lib-infra", -] - -[[package]] -name = "flowy-encrypt" -version = "0.1.0" -dependencies = [ - "aes-gcm", - "anyhow", - "base64 0.21.7", - "getrandom 0.2.12", - "hmac", - "pbkdf2 0.12.2", - "rand 0.8.5", - "sha2", -] - -[[package]] -name = "flowy-error" -version = "0.1.0" -dependencies = [ - "anyhow", - "bytes", - "client-api", - "collab", - "collab-database", - "collab-document", - "collab-folder", - "collab-plugins", - "fancy-regex 0.11.0", - "flowy-codegen", - "flowy-derive", - "flowy-sqlite", - "lib-dispatch", - "protobuf", - "r2d2", - "reqwest", - "serde", - "serde_json", - "serde_repr", - "tantivy", - "thiserror", - "tokio", - "url", - "validator 0.18.1", -] - -[[package]] -name = "flowy-folder" -version = "0.1.0" -dependencies = [ - "arc-swap", - "async-trait", - "bytes", - "chrono", - "client-api", - "collab", - "collab-document", - "collab-entity", - "collab-folder", - "collab-integrate", - "collab-plugins", - "dashmap 6.0.1", - "flowy-codegen", - "flowy-derive", - "flowy-error", - "flowy-folder-pub", - "flowy-notification", - "flowy-search-pub", - "flowy-sqlite", - "futures", - "lazy_static", - "lib-dispatch", - "lib-infra", - "nanoid", - "protobuf", - "regex", - "serde", - "serde_json", - "strum_macros 0.21.1", - "tokio", - "tokio-stream", - "tracing", - "unicode-segmentation", - "uuid", - "validator 0.18.1", -] - -[[package]] -name = "flowy-folder-pub" -version = "0.1.0" -dependencies = [ - "anyhow", - "client-api", - "collab", - "collab-entity", - "collab-folder", - "flowy-error", - "lib-infra", - "serde", - "serde_json", - "uuid", -] - -[[package]] -name = "flowy-notification" -version = "0.1.0" -dependencies = [ - "bytes", - "dashmap 6.0.1", - "flowy-codegen", - "flowy-derive", - "lazy_static", - "lib-dispatch", - "protobuf", - "serde", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "flowy-search" -version = "0.1.0" -dependencies = [ - "async-stream", - "bytes", - "collab", - "collab-folder", - "diesel", - "diesel_derives", - "diesel_migrations", - "flowy-codegen", - "flowy-derive", - "flowy-error", - "flowy-folder", - "flowy-notification", - "flowy-search-pub", - "flowy-sqlite", - "flowy-user", - "futures", - "lib-dispatch", - "lib-infra", - "protobuf", - "serde", - "serde_json", - "strsim 0.11.1", - "strum_macros 0.26.2", - "tantivy", - "tempfile", - "tokio", - "tracing", - "validator 0.18.1", -] - -[[package]] -name = "flowy-search-pub" -version = "0.1.0" -dependencies = [ - "client-api", - "collab", - "collab-folder", - "flowy-error", - "futures", - "lib-infra", -] - -[[package]] -name = "flowy-server" -version = "0.1.0" -dependencies = [ - "anyhow", - "arc-swap", - "bytes", - "chrono", - "client-api", - "collab", - "collab-database", - "collab-document", - "collab-entity", - "collab-folder", - "collab-plugins", - "collab-user", - "dashmap 6.0.1", - "flowy-ai-pub", - "flowy-database-pub", - "flowy-document-pub", - "flowy-encrypt", - "flowy-error", - "flowy-folder-pub", - "flowy-search-pub", - "flowy-server-pub", - "flowy-storage", - "flowy-storage-pub", - "flowy-user-pub", - "futures", - "futures-util", - "hex", - "hyper", - "lazy_static", - "lib-dispatch", - "lib-infra", - "mime_guess", - "postgrest", - "rand 0.8.5", - "reqwest", - "semver", - "serde", - "serde_json", - "thiserror", - "tokio", - "tokio-retry", - "tokio-stream", - "tokio-util", - "tracing", - "url", - "uuid", - "yrs", -] - -[[package]] -name = "flowy-server-pub" -version = "0.1.0" -dependencies = [ - "flowy-error", - "serde", - "serde_repr", -] - -[[package]] -name = "flowy-sqlite" -version = "0.1.0" -dependencies = [ - "anyhow", - "diesel", - "diesel_derives", - "diesel_migrations", - "libsqlite3-sys", - "r2d2", - "scheduled-thread-pool", - "serde", - "serde_json", - "thiserror", - "tracing", -] - -[[package]] -name = "flowy-storage" -version = "0.1.0" -dependencies = [ - "allo-isolate", - "anyhow", - "async-trait", - "bytes", - "chrono", - "collab-importer", - "dashmap 6.0.1", - "flowy-codegen", - "flowy-derive", - "flowy-error", - "flowy-notification", - "flowy-sqlite", - "flowy-storage-pub", - "futures-util", - "fxhash", - "lib-dispatch", - "lib-infra", - "mime_guess", - "protobuf", - "serde", - "serde_json", - "strum_macros 0.25.3", - "tokio", - "tracing", - "url", -] - -[[package]] -name = "flowy-storage-pub" -version = "0.1.0" -dependencies = [ - "anyhow", - "async-trait", - "bytes", - "client-api-entity", - "flowy-error", - "lib-infra", - "mime", - "mime_guess", - "serde", - "serde_json", - "tokio", - "tracing", -] - -[[package]] -name = "flowy-user" -version = "0.1.0" -dependencies = [ - "anyhow", - "arc-swap", - "base64 0.21.7", - "bytes", - "chrono", - "client-api", - "collab", - "collab-database", - "collab-document", - "collab-entity", - "collab-folder", - "collab-integrate", - "collab-plugins", - "collab-user", - "dashmap 6.0.1", - "diesel", - "diesel_derives", - "fancy-regex 0.11.0", - "flowy-codegen", - "flowy-derive", - "flowy-encrypt", - "flowy-error", - "flowy-folder-pub", - "flowy-notification", - "flowy-server-pub", - "flowy-sqlite", - "flowy-user-pub", - "lazy_static", - "lib-dispatch", - "lib-infra", - "once_cell", - "protobuf", - "rayon", - "semver", - "serde", - "serde_json", - "serde_repr", - "strum", - "strum_macros 0.25.3", - "tokio", - "tokio-stream", - "tracing", - "unicode-segmentation", - "uuid", - "validator 0.18.1", -] - -[[package]] -name = "flowy-user-pub" -version = "0.1.0" -dependencies = [ - "anyhow", - "base64 0.21.7", - "chrono", - "client-api", - "collab", - "collab-entity", - "collab-folder", - "flowy-error", - "flowy-folder-pub", - "lib-infra", - "serde", - "serde_json", - "serde_repr", - "tokio", - "tokio-stream", - "tracing", - "uuid", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared 0.1.1", -] - -[[package]] -name = "foreign-types" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" -dependencies = [ - "foreign-types-macros", - "foreign-types-shared 0.3.1", -] - -[[package]] -name = "foreign-types-macros" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.55", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - -[[package]] -name = "foreign-types-shared" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" - -[[package]] -name = "form_urlencoded" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "fs4" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7e180ac76c23b45e767bd7ae9579bc0bb458618c4bc71835926e098e61d15f8" -dependencies = [ - "rustix", - "windows-sys 0.52.0", -] - -[[package]] -name = "fsevent-sys" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2" -dependencies = [ - "libc", -] - -[[package]] -name = "funty" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" - -[[package]] -name = "futf" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" -dependencies = [ - "mac", - "new_debug_unreachable", -] - -[[package]] -name = "futures" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" - -[[package]] -name = "futures-executor" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" - -[[package]] -name = "futures-lite" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" -dependencies = [ - "fastrand", - "futures-core", - "futures-io", - "parking", - "pin-project-lite", -] - -[[package]] -name = "futures-macro" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.55", -] - -[[package]] -name = "futures-sink" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" - -[[package]] -name = "futures-task" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" - -[[package]] -name = "futures-util" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] - -[[package]] -name = "fxhash" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" -dependencies = [ - "byteorder", -] - -[[package]] -name = "gdk" -version = "0.15.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6e05c1f572ab0e1f15be94217f0dc29088c248b14f792a5ff0af0d84bcda9e8" -dependencies = [ - "bitflags 1.3.2", - "cairo-rs", - "gdk-pixbuf", - "gdk-sys", - "gio", - "glib", - "libc", - "pango", -] - -[[package]] -name = "gdk-pixbuf" -version = "0.15.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad38dd9cc8b099cceecdf41375bb6d481b1b5a7cd5cd603e10a69a9383f8619a" -dependencies = [ - "bitflags 1.3.2", - "gdk-pixbuf-sys", - "gio", - "glib", - "libc", -] - -[[package]] -name = "gdk-pixbuf-sys" -version = "0.15.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "140b2f5378256527150350a8346dbdb08fadc13453a7a2d73aecd5fab3c402a7" -dependencies = [ - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "system-deps 6.2.2", -] - -[[package]] -name = "gdk-sys" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32e7a08c1e8f06f4177fb7e51a777b8c1689f743a7bc11ea91d44d2226073a88" -dependencies = [ - "cairo-sys-rs", - "gdk-pixbuf-sys", - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "pango-sys", - "pkg-config", - "system-deps 6.2.2", -] - -[[package]] -name = "gdkwayland-sys" -version = "0.15.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cca49a59ad8cfdf36ef7330fe7bdfbe1d34323220cc16a0de2679ee773aee2c2" -dependencies = [ - "gdk-sys", - "glib-sys", - "gobject-sys", - "libc", - "pkg-config", - "system-deps 6.2.2", -] - -[[package]] -name = "gdkx11-sys" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4b7f8c7a84b407aa9b143877e267e848ff34106578b64d1e0a24bf550716178" -dependencies = [ - "gdk-sys", - "glib-sys", - "libc", - "system-deps 6.2.2", - "x11", -] - -[[package]] -name = "generator" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc16584ff22b460a382b7feec54b23d2908d858152e5739a120b949293bd74e" -dependencies = [ - "cc", - "libc", - "log", - "rustversion", - "windows 0.48.0", -] - -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "gethostname" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1ebd34e35c46e00bb73e81363248d627782724609fe1b6396f553f68fe3862e" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "gethostname" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818" -dependencies = [ - "libc", - "windows-targets 0.48.5", -] - -[[package]] -name = "getopts" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" -dependencies = [ - "unicode-width", -] - -[[package]] -name = "getrandom" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.9.0+wasi-snapshot-preview1", -] - -[[package]] -name = "getrandom" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "wasi 0.11.0+wasi-snapshot-preview1", - "wasm-bindgen", -] - -[[package]] -name = "ghash" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" -dependencies = [ - "opaque-debug", - "polyval", -] - -[[package]] -name = "gimli" -version = "0.28.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" - -[[package]] -name = "gio" -version = "0.15.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68fdbc90312d462781a395f7a16d96a2b379bb6ef8cd6310a2df272771c4283b" -dependencies = [ - "bitflags 1.3.2", - "futures-channel", - "futures-core", - "futures-io", - "gio-sys", - "glib", - "libc", - "once_cell", - "thiserror", -] - -[[package]] -name = "gio-sys" -version = "0.15.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32157a475271e2c4a023382e9cab31c4584ee30a97da41d3c4e9fdd605abcf8d" -dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps 6.2.2", - "winapi", -] - -[[package]] -name = "glib" -version = "0.15.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edb0306fbad0ab5428b0ca674a23893db909a98582969c9b537be4ced78c505d" -dependencies = [ - "bitflags 1.3.2", - "futures-channel", - "futures-core", - "futures-executor", - "futures-task", - "glib-macros", - "glib-sys", - "gobject-sys", - "libc", - "once_cell", - "smallvec", - "thiserror", -] - -[[package]] -name = "glib-macros" -version = "0.15.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10c6ae9f6fa26f4fb2ac16b528d138d971ead56141de489f8111e259b9df3c4a" -dependencies = [ - "anyhow", - "heck 0.4.1", - "proc-macro-crate 1.3.1", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "glib-sys" -version = "0.15.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef4b192f8e65e9cf76cbf4ea71fa8e3be4a0e18ffe3d68b8da6836974cc5bad4" -dependencies = [ - "libc", - "system-deps 6.2.2", -] - -[[package]] -name = "glob" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" - -[[package]] -name = "globset" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1" -dependencies = [ - "aho-corasick", - "bstr", - "log", - "regex-automata 0.4.6", - "regex-syntax 0.8.2", -] - -[[package]] -name = "globwalk" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93e3af942408868f6934a7b85134a3230832b9977cf66125df2f9edcfce4ddcc" -dependencies = [ - "bitflags 1.3.2", - "ignore", - "walkdir", -] - -[[package]] -name = "gloo-utils" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037fcb07216cb3a30f7292bd0176b050b7b9a052ba830ef7d5d65f6dc64ba58e" -dependencies = [ - "js-sys", - "serde", - "serde_json", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "gobject-sys" -version = "0.15.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d57ce44246becd17153bd035ab4d32cfee096a657fc01f2231c9278378d1e0a" -dependencies = [ - "glib-sys", - "libc", - "system-deps 6.2.2", -] - -[[package]] -name = "gotrue" -version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ea131f0baab67defe7591067357eced490072372#ea131f0baab67defe7591067357eced490072372" -dependencies = [ - "anyhow", - "futures-util", - "getrandom 0.2.12", - "gotrue-entity", - "infra", - "reqwest", - "serde", - "serde_json", - "tokio", - "tracing", -] - -[[package]] -name = "gotrue-entity" -version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ea131f0baab67defe7591067357eced490072372#ea131f0baab67defe7591067357eced490072372" -dependencies = [ - "anyhow", - "app-error", - "chrono", - "jsonwebtoken", - "lazy_static", - "serde", - "serde_json", -] - -[[package]] -name = "gtk" -version = "0.15.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92e3004a2d5d6d8b5057d2b57b3712c9529b62e82c77f25c1fecde1fd5c23bd0" -dependencies = [ - "atk", - "bitflags 1.3.2", - "cairo-rs", - "field-offset", - "futures-channel", - "gdk", - "gdk-pixbuf", - "gio", - "glib", - "gtk-sys", - "gtk3-macros", - "libc", - "once_cell", - "pango", - "pkg-config", -] - -[[package]] -name = "gtk-sys" -version = "0.15.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5bc2f0587cba247f60246a0ca11fe25fb733eabc3de12d1965fc07efab87c84" -dependencies = [ - "atk-sys", - "cairo-sys-rs", - "gdk-pixbuf-sys", - "gdk-sys", - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "pango-sys", - "system-deps 6.2.2", -] - -[[package]] -name = "gtk3-macros" -version = "0.15.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "684c0456c086e8e7e9af73ec5b84e35938df394712054550e81558d21c44ab0d" -dependencies = [ - "anyhow", - "proc-macro-crate 1.3.1", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "h2" -version = "0.3.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fbd2820c5e49886948654ab546d0688ff24530286bdcf8fca3cefb16d4618eb" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http", - "indexmap 2.2.6", - "slab", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -dependencies = [ - "ahash 0.7.8", -] - -[[package]] -name = "hashbrown" -version = "0.14.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" -dependencies = [ - "ahash 0.8.11", - "allocator-api2", -] - -[[package]] -name = "heck" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - -[[package]] -name = "hermit-abi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest", -] - -[[package]] -name = "home" -version = "0.5.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" -dependencies = [ - "windows-sys 0.52.0", -] - -[[package]] -name = "html5ever" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7" -dependencies = [ - "log", - "mac", - "markup5ever", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "htmlescape" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9025058dae765dee5070ec375f591e2ba14638c63feff74f13805a72e523163" - -[[package]] -name = "http" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" -dependencies = [ - "bytes", - "fnv", - "itoa 1.0.10", -] - -[[package]] -name = "http-body" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" -dependencies = [ - "bytes", - "http", - "pin-project-lite", -] - -[[package]] -name = "http-range" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21dec9db110f5f872ed9699c3ecf50cf16f423502706ba5c72462e28d3157573" - -[[package]] -name = "httparse" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" - -[[package]] -name = "httpdate" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" - -[[package]] -name = "humansize" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cb51c9a029ddc91b07a787f1d86b53ccfa49b0e86688c946ebe8d3555685dd7" -dependencies = [ - "libm", -] - -[[package]] -name = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" - -[[package]] -name = "hyper" -version = "0.14.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "httparse", - "httpdate", - "itoa 1.0.10", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", - "want", -] - -[[package]] -name = "hyper-rustls" -version = "0.24.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" -dependencies = [ - "futures-util", - "http", - "hyper", - "rustls", - "tokio", - "tokio-rustls", -] - -[[package]] -name = "hyper-tls" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" -dependencies = [ - "bytes", - "hyper", - "native-tls", - "tokio", - "tokio-native-tls", -] - -[[package]] -name = "iana-time-zone" -version = "0.1.61" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - -[[package]] -name = "ico" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3804960be0bb5e4edb1e1ad67afd321a9ecfd875c3e65c099468fd2717d7cae" -dependencies = [ - "byteorder", - "png", -] - -[[package]] -name = "icu_collections" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" -dependencies = [ - "displaydoc", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_locid" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" -dependencies = [ - "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", -] - -[[package]] -name = "icu_locid_transform" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_locid_transform_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_locid_transform_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" - -[[package]] -name = "icu_normalizer" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" -dependencies = [ - "displaydoc", - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec", - "utf16_iter", - "utf8_iter", - "write16", - "zerovec", -] - -[[package]] -name = "icu_normalizer_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" - -[[package]] -name = "icu_properties" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" -dependencies = [ - "displaydoc", - "icu_collections", - "icu_locid_transform", - "icu_properties_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_properties_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" - -[[package]] -name = "icu_provider" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_provider_macros", - "stable_deref_trait", - "tinystr", - "writeable", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_provider_macros" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.55", -] - -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - -[[package]] -name = "idna" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "idna" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "idna" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" -dependencies = [ - "idna_adapter", - "smallvec", - "utf8_iter", -] - -[[package]] -name = "idna_adapter" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" -dependencies = [ - "icu_normalizer", - "icu_properties", -] - -[[package]] -name = "ignore" -version = "0.4.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b46810df39e66e925525d6e38ce1e7f6e1d208f72dc39757880fcb66e2c58af1" -dependencies = [ - "crossbeam-deque", - "globset", - "log", - "memchr", - "regex-automata 0.4.6", - "same-file", - "walkdir", - "winapi-util", -] - -[[package]] -name = "image" -version = "0.24.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d" -dependencies = [ - "bytemuck", - "byteorder", - "color_quant", - "num-traits", - "png", - "tiff", -] - -[[package]] -name = "indexed_db_futures" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0704b71f13f81b5933d791abf2de26b33c40935143985220299a357721166706" -dependencies = [ - "accessory", - "cfg-if", - "delegate-display", - "fancy_constructor", - "js-sys", - "uuid", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", - "serde", -] - -[[package]] -name = "indexmap" -version = "2.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" -dependencies = [ - "equivalent", - "hashbrown 0.14.3", - "serde", -] - -[[package]] -name = "infer" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f551f8c3a39f68f986517db0d1759de85881894fdc7db798bd2a9df9cb04b7fc" -dependencies = [ - "cfb", -] - -[[package]] -name = "infra" -version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ea131f0baab67defe7591067357eced490072372#ea131f0baab67defe7591067357eced490072372" -dependencies = [ - "anyhow", - "bytes", - "futures", - "pin-project", - "reqwest", - "serde", - "serde_json", - "tokio", - "tracing", - "validator 0.19.0", -] - -[[package]] -name = "inotify" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff" -dependencies = [ - "bitflags 1.3.2", - "inotify-sys", - "libc", -] - -[[package]] -name = "inotify-sys" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" -dependencies = [ - "libc", -] - -[[package]] -name = "inout" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" -dependencies = [ - "generic-array", -] - -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "interprocess" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81f2533f3be42fffe3b5e63b71aeca416c1c3bc33e4e27be018521e76b1f38fb" -dependencies = [ - "cfg-if", - "libc", - "rustc_version", - "to_method", - "winapi", -] - -[[package]] -name = "ipnet" -version = "2.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" - -[[package]] -name = "is-terminal" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" -dependencies = [ - "hermit-abi", - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] - -[[package]] -name = "itertools" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" - -[[package]] -name = "itoa" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" - -[[package]] -name = "javascriptcore-rs" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf053e7843f2812ff03ef5afe34bb9c06ffee120385caad4f6b9967fcd37d41c" -dependencies = [ - "bitflags 1.3.2", - "glib", - "javascriptcore-rs-sys", -] - -[[package]] -name = "javascriptcore-rs-sys" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "905fbb87419c5cde6e3269537e4ea7d46431f3008c5d057e915ef3f115e7793c" -dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps 5.0.0", -] - -[[package]] -name = "jni" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "039022cdf4d7b1cf548d31f60ae783138e5fd42013f6271049d7df7afadef96c" -dependencies = [ - "cesu8", - "combine", - "jni-sys", - "log", - "thiserror", - "walkdir", -] - -[[package]] -name = "jni-sys" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" - -[[package]] -name = "jobserver" -version = "0.1.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab46a6e9526ddef3ae7f787c06f0f2600639ba80ea3eade3d8e670a2230f51d6" -dependencies = [ - "libc", -] - -[[package]] -name = "jpeg-decoder" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" - -[[package]] -name = "js-sys" -version = "0.3.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "json-patch" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55ff1e1486799e3f64129f8ccad108b38290df9cd7015cd31bed17239f0789d6" -dependencies = [ - "serde", - "serde_json", - "thiserror", - "treediff", -] - -[[package]] -name = "jsonwebtoken" -version = "8.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6971da4d9c3aa03c3d8f3ff0f4155b534aad021292003895a469716b2a230378" -dependencies = [ - "base64 0.21.7", - "pem", - "ring 0.16.20", - "serde", - "serde_json", - "simple_asn1", -] - -[[package]] -name = "kqueue" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7447f1ca1b7b563588a205fe93dea8df60fd981423a768bc1c0ded35ed147d0c" -dependencies = [ - "kqueue-sys", - "libc", -] - -[[package]] -name = "kqueue-sys" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" -dependencies = [ - "bitflags 1.3.2", - "libc", -] - -[[package]] -name = "kuchikiki" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e4755b7b995046f510a7520c42b2fed58b77bd94d5a87a8eb43d2fd126da8" -dependencies = [ - "cssparser 0.27.2", - "html5ever", - "indexmap 1.9.3", - "matches", - "selectors 0.22.0", -] - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "lazycell" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" - -[[package]] -name = "levenshtein_automata" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c2cdeb66e45e9f36bfad5bbdb4d2384e70936afbee843c6f6543f0c551ebb25" - -[[package]] -name = "lib-dispatch" -version = "0.1.0" -dependencies = [ - "bincode", - "bytes", - "derivative", - "dyn-clone", - "futures", - "futures-channel", - "futures-core", - "futures-util", - "getrandom 0.2.12", - "nanoid", - "pin-project", - "protobuf", - "serde", - "serde_json", - "serde_repr", - "thread-id", - "tokio", - "tracing", - "validator 0.18.1", - "wasm-bindgen", - "wasm-bindgen-futures", -] - -[[package]] -name = "lib-infra" -version = "0.1.0" -dependencies = [ - "allo-isolate", - "anyhow", - "async-trait", - "atomic_refcell", - "bytes", - "cfg-if", - "chrono", - "futures", - "futures-core", - "futures-util", - "md5", - "pin-project", - "tempfile", - "tokio", - "tracing", - "validator 0.18.1", - "walkdir", - "zip 2.2.0", -] - -[[package]] -name = "lib-log" -version = "0.1.0" -dependencies = [ - "chrono", - "lazy_static", - "lib-infra", - "serde", - "serde_json", - "tracing", - "tracing-appender", - "tracing-bunyan-formatter", - "tracing-core", - "tracing-subscriber", -] - -[[package]] -name = "libc" -version = "0.2.153" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" - -[[package]] -name = "libloading" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" -dependencies = [ - "cfg-if", - "windows-targets 0.52.4", -] - -[[package]] -name = "libm" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" - -[[package]] -name = "libredox" -version = "0.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" -dependencies = [ - "bitflags 2.5.0", - "libc", - "redox_syscall 0.4.1", -] - -[[package]] -name = "librocksdb-sys" -version = "0.16.0+8.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce3d60bc059831dc1c83903fb45c103f75db65c5a7bf22272764d9cc683e348c" -dependencies = [ - "bindgen", - "bzip2-sys", - "cc", - "glob", - "libc", - "libz-sys", - "zstd-sys", -] - -[[package]] -name = "libsqlite3-sys" -version = "0.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4e226dcd58b4be396f7bd3c20da8fdee2911400705297ba7d2d7cc2c30f716" -dependencies = [ - "cc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "libz-sys" -version = "1.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e143b5e666b2695d28f6bca6497720813f699c9602dd7f5cac91008b8ada7f9" -dependencies = [ - "cc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "line-wrap" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd1bc4d24ad230d21fb898d1116b1801d7adfc449d42026475862ab48b11e70e" - -[[package]] -name = "linux-raw-sys" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" - -[[package]] -name = "litemap" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" - -[[package]] -name = "lock_api" -version = "0.4.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" -dependencies = [ - "autocfg", - "scopeguard", -] - -[[package]] -name = "lockfree-object-pool" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9374ef4228402d4b7e403e5838cb880d9ee663314b0a900d5a6aabf0c213552e" - -[[package]] -name = "log" -version = "0.4.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" - -[[package]] -name = "loom" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff50ecb28bb86013e935fb6683ab1f6d3a20016f123c76fd4c27470076ac30f5" -dependencies = [ - "cfg-if", - "generator", - "pin-utils", - "scoped-tls", - "serde", - "serde_json", - "tracing", - "tracing-subscriber", -] - -[[package]] -name = "lru" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc" -dependencies = [ - "hashbrown 0.14.3", -] - -[[package]] -name = "lz4_flex" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75761162ae2b0e580d7e7c390558127e5f01b4194debd6221fd8c207fc80e3f5" - -[[package]] -name = "lzma-rs" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "297e814c836ae64db86b36cf2a557ba54368d03f6afcd7d947c266692f71115e" -dependencies = [ - "byteorder", - "crc", -] - -[[package]] -name = "lzma-sys" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fda04ab3764e6cde78b9974eec4f779acaba7c4e84b36eca3cf77c581b85d27" -dependencies = [ - "cc", - "libc", - "pkg-config", -] - -[[package]] -name = "mac" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" - -[[package]] -name = "macroific" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f05c00ac596022625d01047c421a0d97d7f09a18e429187b341c201cb631b9dd" -dependencies = [ - "macroific_attr_parse", - "macroific_core", - "macroific_macro", -] - -[[package]] -name = "macroific_attr_parse" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd94d5da95b30ae6e10621ad02340909346ad91661f3f8c0f2b62345e46a2f67" -dependencies = [ - "cfg-if", - "proc-macro2", - "quote", - "syn 2.0.55", -] - -[[package]] -name = "macroific_core" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13198c120864097a565ccb3ff947672d969932b7975ebd4085732c9f09435e55" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.55", -] - -[[package]] -name = "macroific_macro" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0c9853143cbed7f1e41dc39fee95f9b361bec65c8dc2a01bf609be01b61f5ae" -dependencies = [ - "macroific_attr_parse", - "macroific_core", - "proc-macro2", - "quote", - "syn 2.0.55", -] - -[[package]] -name = "malloc_buf" -version = "0.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" -dependencies = [ - "libc", -] - -[[package]] -name = "markdown" -version = "1.0.0-alpha.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6491e6c702bf7e3b24e769d800746d5f2c06a6c6a2db7992612e0f429029e81" -dependencies = [ - "unicode-id", -] - -[[package]] -name = "markup5ever" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016" -dependencies = [ - "log", - "phf 0.10.1", - "phf_codegen 0.10.0", - "string_cache", - "string_cache_codegen", - "tendril", -] - -[[package]] -name = "matchers" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" -dependencies = [ - "regex-automata 0.1.10", -] - -[[package]] -name = "matches" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" - -[[package]] -name = "md5" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" - -[[package]] -name = "measure_time" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56220900f1a0923789ecd6bf25fbae8af3b2f1ff3e9e297fc9b6b8674dd4d852" -dependencies = [ - "instant", - "log", -] - -[[package]] -name = "memchr" -version = "2.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" - -[[package]] -name = "memmap2" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe751422e4a8caa417e13c3ea66452215d7d63e19e604f4980461212f3ae1322" -dependencies = [ - "libc", -] - -[[package]] -name = "memoffset" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" -dependencies = [ - "autocfg", -] - -[[package]] -name = "migrations_internals" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f23f71580015254b020e856feac3df5878c2c7a8812297edd6c0a485ac9dada" -dependencies = [ - "serde", - "toml 0.7.8", -] - -[[package]] -name = "migrations_macros" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cce3325ac70e67bbab5bd837a31cae01f1a6db64e0e744a33cb03a543469ef08" -dependencies = [ - "migrations_internals", - "proc-macro2", - "quote", -] - -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - -[[package]] -name = "mime_guess" -version = "2.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" -dependencies = [ - "mime", - "unicase", -] - -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - -[[package]] -name = "miniz_oxide" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" -dependencies = [ - "adler", - "simd-adler32", -] - -[[package]] -name = "mio" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" -dependencies = [ - "libc", - "log", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.48.0", -] - -[[package]] -name = "moka" -version = "0.12.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32cf62eb4dd975d2dde76432fb1075c49e3ee2331cf36f1f8fd4b66550d32b6f" -dependencies = [ - "async-lock", - "async-trait", - "crossbeam-channel", - "crossbeam-epoch", - "crossbeam-utils", - "event-listener", - "futures-util", - "once_cell", - "parking_lot 0.12.1", - "quanta", - "rustc_version", - "smallvec", - "tagptr", - "thiserror", - "triomphe", - "uuid", -] - -[[package]] -name = "multimap" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" - -[[package]] -name = "murmurhash32" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2195bf6aa996a481483b29d62a7663eed3fe39600c460e323f8ff41e90bdd89b" - -[[package]] -name = "nanoid" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ffa00dec017b5b1a8b7cf5e2c008bfda1aa7e0697ac1508b491fdf2622fb4d8" -dependencies = [ - "rand 0.8.5", -] - -[[package]] -name = "native-tls" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" -dependencies = [ - "lazy_static", - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - -[[package]] -name = "ndk" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2032c77e030ddee34a6787a64166008da93f6a352b629261d0fee232b8742dd4" -dependencies = [ - "bitflags 1.3.2", - "jni-sys", - "ndk-sys", - "num_enum", - "thiserror", -] - -[[package]] -name = "ndk-context" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" - -[[package]] -name = "ndk-sys" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e5a6ae77c8ee183dcbbba6150e2e6b9f3f4196a7666c02a715a95692ec1fa97" -dependencies = [ - "jni-sys", -] - -[[package]] -name = "new_debug_unreachable" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" - -[[package]] -name = "nix" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" -dependencies = [ - "bitflags 2.5.0", - "cfg-if", - "cfg_aliases", - "libc", -] - -[[package]] -name = "nodrop" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" - -[[package]] -name = "nom" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" -dependencies = [ - "memchr", - "minimal-lexical", -] - -[[package]] -name = "notify" -version = "6.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" -dependencies = [ - "bitflags 2.5.0", - "crossbeam-channel", - "filetime", - "fsevent-sys", - "inotify", - "kqueue", - "libc", - "log", - "mio", - "walkdir", - "windows-sys 0.48.0", -] - -[[package]] -name = "ntapi" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" -dependencies = [ - "winapi", -] - -[[package]] -name = "nu-ansi-term" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" -dependencies = [ - "overload", - "winapi", -] - -[[package]] -name = "num-bigint" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-conv" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" - -[[package]] -name = "num-integer" -version = "0.1.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" -dependencies = [ - "autocfg", - "libm", -] - -[[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi", - "libc", -] - -[[package]] -name = "num_enum" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" -dependencies = [ - "num_enum_derive", -] - -[[package]] -name = "num_enum_derive" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" -dependencies = [ - "proc-macro-crate 1.3.1", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "objc" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" -dependencies = [ - "malloc_buf", - "objc_exception", -] - -[[package]] -name = "objc-foundation" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" -dependencies = [ - "block", - "objc", - "objc_id", -] - -[[package]] -name = "objc-sys" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7c71324e4180d0899963fc83d9d241ac39e699609fc1025a850aadac8257459" - -[[package]] -name = "objc2" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "559c5a40fdd30eb5e344fbceacf7595a81e242529fb4e21cf5f43fb4f11ff98d" -dependencies = [ - "objc-sys", - "objc2-encode", -] - -[[package]] -name = "objc2-encode" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d079845b37af429bfe5dfa76e6d087d788031045b25cfc6fd898486fd9847666" - -[[package]] -name = "objc_exception" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4" -dependencies = [ - "cc", -] - -[[package]] -name = "objc_id" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" -dependencies = [ - "objc", -] - -[[package]] -name = "object" -version = "0.32.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" -dependencies = [ - "memchr", -] - -[[package]] -name = "once_cell" -version = "1.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" - -[[package]] -name = "oneshot" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f6640c6bda7731b1fdbab747981a0f896dd1fedaf9f4a53fa237a04a84431f4" -dependencies = [ - "loom", -] - -[[package]] -name = "opaque-debug" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" - -[[package]] -name = "open" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2078c0039e6a54a0c42c28faa984e115fb4c2d5bf2208f77d1961002df8576f8" -dependencies = [ - "pathdiff", - "windows-sys 0.42.0", -] - -[[package]] -name = "openssl" -version = "0.10.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" -dependencies = [ - "bitflags 2.5.0", - "cfg-if", - "foreign-types 0.3.2", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.55", -] - -[[package]] -name = "openssl-probe" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" - -[[package]] -name = "openssl-src" -version = "300.2.3+3.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cff92b6f71555b61bb9315f7c64da3ca43d87531622120fea0195fc761b4843" -dependencies = [ - "cc", -] - -[[package]] -name = "openssl-sys" -version = "0.9.101" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dda2b0f344e78efc2facf7d195d098df0dd72151b26ab98da807afc26c198dff" -dependencies = [ - "cc", - "libc", - "openssl-src", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "option-ext" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" - -[[package]] -name = "os_pipe" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57119c3b893986491ec9aa85056780d3a0f3cf4da7cc09dd3650dbd6c6738fb9" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - -[[package]] -name = "ownedbytes" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3a059efb063b8f425b948e042e6b9bd85edfe60e913630ed727b23e2dfcc558" -dependencies = [ - "stable_deref_trait", -] - -[[package]] -name = "pango" -version = "0.15.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e4045548659aee5313bde6c582b0d83a627b7904dd20dc2d9ef0895d414e4f" -dependencies = [ - "bitflags 1.3.2", - "glib", - "libc", - "once_cell", - "pango-sys", -] - -[[package]] -name = "pango-sys" -version = "0.15.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2a00081cde4661982ed91d80ef437c20eacaf6aa1a5962c0279ae194662c3aa" -dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps 6.2.2", -] - -[[package]] -name = "parking" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" - -[[package]] -name = "parking_lot" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" -dependencies = [ - "instant", - "lock_api", - "parking_lot_core 0.8.6", -] - -[[package]] -name = "parking_lot" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" -dependencies = [ - "lock_api", - "parking_lot_core 0.9.9", -] - -[[package]] -name = "parking_lot_core" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" -dependencies = [ - "cfg-if", - "instant", - "libc", - "redox_syscall 0.2.16", - "smallvec", - "winapi", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall 0.4.1", - "smallvec", - "windows-targets 0.48.5", -] - -[[package]] -name = "parse-zoneinfo" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c705f256449c60da65e11ff6626e0c16a0a0b96aaa348de61376b249bc340f41" -dependencies = [ - "regex", -] - -[[package]] -name = "password-hash" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700" -dependencies = [ - "base64ct", - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "pathdiff" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" - -[[package]] -name = "pbkdf2" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" -dependencies = [ - "digest", - "hmac", - "password-hash", - "sha2", -] - -[[package]] -name = "pbkdf2" -version = "0.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" -dependencies = [ - "digest", - "hmac", -] - -[[package]] -name = "pem" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8835c273a76a90455d7344889b0964598e3316e2a79ede8e36f16bdcf2228b8" -dependencies = [ - "base64 0.13.1", -] - -[[package]] -name = "percent-encoding" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" - -[[package]] -name = "pest" -version = "2.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f8023d0fb78c8e03784ea1c7f3fa36e68a723138990b8d5a47d916b651e7a8" -dependencies = [ - "memchr", - "thiserror", - "ucd-trie", -] - -[[package]] -name = "pest_derive" -version = "2.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0d24f72393fd16ab6ac5738bc33cdb6a9aa73f8b902e8fe29cf4e67d7dd1026" -dependencies = [ - "pest", - "pest_generator", -] - -[[package]] -name = "pest_generator" -version = "2.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdc17e2a6c7d0a492f0158d7a4bd66cc17280308bbaff78d5bef566dca35ab80" -dependencies = [ - "pest", - "pest_meta", - "proc-macro2", - "quote", - "syn 2.0.55", -] - -[[package]] -name = "pest_meta" -version = "2.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "934cd7631c050f4674352a6e835d5f6711ffbfb9345c2fc0107155ac495ae293" -dependencies = [ - "once_cell", - "pest", - "sha2", -] - -[[package]] -name = "petgraph" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" -dependencies = [ - "fixedbitset", - "indexmap 2.2.6", -] - -[[package]] -name = "phf" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" -dependencies = [ - "phf_macros 0.8.0", - "phf_shared 0.8.0", - "proc-macro-hack", -] - -[[package]] -name = "phf" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" -dependencies = [ - "phf_shared 0.10.0", -] - -[[package]] -name = "phf" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" -dependencies = [ - "phf_macros 0.11.2", - "phf_shared 0.11.2", -] - -[[package]] -name = "phf_codegen" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815" -dependencies = [ - "phf_generator 0.8.0", - "phf_shared 0.8.0", -] - -[[package]] -name = "phf_codegen" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd" -dependencies = [ - "phf_generator 0.10.0", - "phf_shared 0.10.0", -] - -[[package]] -name = "phf_codegen" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a" -dependencies = [ - "phf_generator 0.11.2", - "phf_shared 0.11.2", -] - -[[package]] -name = "phf_generator" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526" -dependencies = [ - "phf_shared 0.8.0", - "rand 0.7.3", -] - -[[package]] -name = "phf_generator" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" -dependencies = [ - "phf_shared 0.10.0", - "rand 0.8.5", -] - -[[package]] -name = "phf_generator" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" -dependencies = [ - "phf_shared 0.11.2", - "rand 0.8.5", -] - -[[package]] -name = "phf_macros" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6fde18ff429ffc8fe78e2bf7f8b7a5a5a6e2a8b58bc5a9ac69198bbda9189c" -dependencies = [ - "phf_generator 0.8.0", - "phf_shared 0.8.0", - "proc-macro-hack", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "phf_macros" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" -dependencies = [ - "phf_generator 0.11.2", - "phf_shared 0.11.2", - "proc-macro2", - "quote", - "syn 2.0.55", -] - -[[package]] -name = "phf_shared" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" -dependencies = [ - "siphasher", -] - -[[package]] -name = "phf_shared" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" -dependencies = [ - "siphasher", -] - -[[package]] -name = "phf_shared" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" -dependencies = [ - "siphasher", -] - -[[package]] -name = "pin-project" -version = "1.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be57f64e946e500c8ee36ef6331845d40a93055567ec57e8fae13efd33759b95" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.55", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "pkg-config" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" - -[[package]] -name = "plist" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9d34169e64b3c7a80c8621a48adaf44e0cf62c78a9b25dd9dd35f1881a17cf9" -dependencies = [ - "base64 0.21.7", - "indexmap 2.2.6", - "line-wrap", - "quick-xml", - "serde", - "time", -] - -[[package]] -name = "png" -version = "0.17.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1" -dependencies = [ - "bitflags 1.3.2", - "crc32fast", - "fdeflate", - "flate2", - "miniz_oxide", -] - -[[package]] -name = "polyval" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" -dependencies = [ - "cfg-if", - "cpufeatures", - "opaque-debug", - "universal-hash", -] - -[[package]] -name = "postgrest" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a966c650b47a064e7082170b4be74fca08c088d893244fc4b70123e3c1f3ee7" -dependencies = [ - "reqwest", -] - -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - -[[package]] -name = "ppv-lite86" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - -[[package]] -name = "precomputed-hash" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" - -[[package]] -name = "prettyplease" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d3928fb5db768cb86f891ff014f0144589297e3c6a1aba6ed7cecfdace270c7" -dependencies = [ - "proc-macro2", - "syn 2.0.55", -] - -[[package]] -name = "proc-macro-crate" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" -dependencies = [ - "once_cell", - "toml_edit 0.19.15", -] - -[[package]] -name = "proc-macro-crate" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" -dependencies = [ - "toml_edit 0.21.1", -] - -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.109", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr2" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" -dependencies = [ - "proc-macro2", - "quote", -] - -[[package]] -name = "proc-macro-error2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" -dependencies = [ - "proc-macro-error-attr2", - "proc-macro2", - "quote", - "syn 2.0.55", -] - -[[package]] -name = "proc-macro-hack" -version = "0.5.20+deprecated" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" - -[[package]] -name = "proc-macro2" -version = "1.0.79" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "prost" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "146c289cda302b98a28d40c8b3b90498d6e526dd24ac2ecea73e4e491685b94a" -dependencies = [ - "bytes", - "prost-derive 0.12.3", -] - -[[package]] -name = "prost" -version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b0487d90e047de87f984913713b85c601c05609aad5b0df4b4573fbf69aa13f" -dependencies = [ - "bytes", - "prost-derive 0.13.3", -] - -[[package]] -name = "prost-build" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c55e02e35260070b6f716a2423c2ff1c3bb1642ddca6f99e1f26d06268a0e2d2" -dependencies = [ - "bytes", - "heck 0.4.1", - "itertools 0.10.5", - "log", - "multimap", - "once_cell", - "petgraph", - "prettyplease", - "prost 0.12.3", - "prost-types", - "regex", - "syn 2.0.55", - "tempfile", - "which", -] - -[[package]] -name = "prost-derive" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efb6c9a1dd1def8e2124d17e83a20af56f1570d6c2d2bd9e266ccb768df3840e" -dependencies = [ - "anyhow", - "itertools 0.10.5", - "proc-macro2", - "quote", - "syn 2.0.55", -] - -[[package]] -name = "prost-derive" -version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9552f850d5f0964a4e4d0bf306459ac29323ddfbae05e35a7c0d35cb0803cc5" -dependencies = [ - "anyhow", - "itertools 0.12.1", - "proc-macro2", - "quote", - "syn 2.0.55", -] - -[[package]] -name = "prost-types" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "193898f59edcf43c26227dcd4c8427f00d99d61e95dcde58dabd49fa291d470e" -dependencies = [ - "prost 0.12.3", -] - -[[package]] -name = "protobuf" -version = "2.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94" - -[[package]] -name = "protobuf-codegen" -version = "2.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "033460afb75cf755fcfc16dfaed20b86468082a2ea24e05ac35ab4a099a017d6" -dependencies = [ - "protobuf", -] - -[[package]] -name = "protoc" -version = "2.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0218039c514f9e14a5060742ecd50427f8ac4f85a6dc58f2ddb806e318c55ee" -dependencies = [ - "log", - "which", -] - -[[package]] -name = "protoc-bin-vendored" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "005ca8623e5633e298ad1f917d8be0a44bcf406bf3cde3b80e63003e49a3f27d" -dependencies = [ - "protoc-bin-vendored-linux-aarch_64", - "protoc-bin-vendored-linux-ppcle_64", - "protoc-bin-vendored-linux-x86_32", - "protoc-bin-vendored-linux-x86_64", - "protoc-bin-vendored-macos-x86_64", - "protoc-bin-vendored-win32", -] - -[[package]] -name = "protoc-bin-vendored-linux-aarch_64" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fb9fc9cce84c8694b6ea01cc6296617b288b703719b725b8c9c65f7c5874435" - -[[package]] -name = "protoc-bin-vendored-linux-ppcle_64" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02d2a07dcf7173a04d49974930ccbfb7fd4d74df30ecfc8762cf2f895a094516" - -[[package]] -name = "protoc-bin-vendored-linux-x86_32" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54fef0b04fcacba64d1d80eed74a20356d96847da8497a59b0a0a436c9165b0" - -[[package]] -name = "protoc-bin-vendored-linux-x86_64" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8782f2ce7d43a9a5c74ea4936f001e9e8442205c244f7a3d4286bd4c37bc924" - -[[package]] -name = "protoc-bin-vendored-macos-x86_64" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5de656c7ee83f08e0ae5b81792ccfdc1d04e7876b1d9a38e6876a9e09e02537" - -[[package]] -name = "protoc-bin-vendored-win32" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9653c3ed92974e34c5a6e0a510864dab979760481714c172e0a34e437cb98804" - -[[package]] -name = "protoc-rust" -version = "2.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22f8a182bb17c485f20bdc4274a8c39000a61024cfe461c799b50fec77267838" -dependencies = [ - "protobuf", - "protobuf-codegen", - "protoc", - "tempfile", -] - -[[package]] -name = "psl-types" -version = "2.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac" - -[[package]] -name = "ptr_meta" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" -dependencies = [ - "ptr_meta_derive", -] - -[[package]] -name = "ptr_meta_derive" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "publicsuffix" -version = "2.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96a8c1bda5ae1af7f99a2962e49df150414a43d62404644d98dd5c3a93d07457" -dependencies = [ - "idna 0.3.0", - "psl-types", -] - -[[package]] -name = "quanta" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5167a477619228a0b284fac2674e3c388cba90631d7b7de620e6f1fcd08da5" -dependencies = [ - "crossbeam-utils", - "libc", - "once_cell", - "raw-cpuid", - "wasi 0.11.0+wasi-snapshot-preview1", - "web-sys", - "winapi", -] - -[[package]] -name = "quick-xml" -version = "0.31.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33" -dependencies = [ - "memchr", -] - -[[package]] -name = "quote" -version = "1.0.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "r2d2" -version = "0.8.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51de85fb3fb6524929c8a2eb85e6b6d363de4e8c48f9e2c2eac4944abc181c93" -dependencies = [ - "log", - "parking_lot 0.12.1", - "scheduled-thread-pool", -] - -[[package]] -name = "radium" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" - -[[package]] -name = "rand" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom 0.1.16", - "libc", - "rand_chacha 0.2.2", - "rand_core 0.5.1", - "rand_hc", - "rand_pcg", -] - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core 0.5.1", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom 0.1.16", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.12", -] - -[[package]] -name = "rand_distr" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32cb0b9bc82b0a0876c2dd994a7e7a2683d3e7390ca40e6886785ef0c7e3ee31" -dependencies = [ - "num-traits", - "rand 0.8.5", -] - -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core 0.5.1", -] - -[[package]] -name = "rand_pcg" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" -dependencies = [ - "rand_core 0.5.1", -] - -[[package]] -name = "raw-cpuid" -version = "11.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb9ee317cfe3fbd54b36a511efc1edd42e216903c9cd575e686dd68a2ba90d8d" -dependencies = [ - "bitflags 2.5.0", -] - -[[package]] -name = "raw-window-handle" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" - -[[package]] -name = "rayon" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" -dependencies = [ - "crossbeam-deque", - "crossbeam-utils", -] - -[[package]] -name = "redox_syscall" -version = "0.1.57" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" - -[[package]] -name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags 1.3.2", -] - -[[package]] -name = "redox_syscall" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" -dependencies = [ - "bitflags 1.3.2", -] - -[[package]] -name = "redox_users" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" -dependencies = [ - "getrandom 0.2.12", - "libredox", - "thiserror", -] - -[[package]] -name = "regex" -version = "1.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata 0.4.6", - "regex-syntax 0.8.2", -] - -[[package]] -name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" -dependencies = [ - "regex-syntax 0.6.29", -] - -[[package]] -name = "regex-automata" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax 0.8.2", -] - -[[package]] -name = "regex-syntax" -version = "0.6.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" - -[[package]] -name = "regex-syntax" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" - -[[package]] -name = "rend" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c" -dependencies = [ - "bytecheck", -] - -[[package]] -name = "reqwest" -version = "0.11.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" -dependencies = [ - "base64 0.21.7", - "bytes", - "cookie", - "cookie_store", - "encoding_rs", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "hyper", - "hyper-rustls", - "hyper-tls", - "ipnet", - "js-sys", - "log", - "mime", - "mime_guess", - "native-tls", - "once_cell", - "percent-encoding", - "pin-project-lite", - "rustls", - "rustls-pemfile", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper", - "system-configuration", - "tokio", - "tokio-native-tls", - "tokio-rustls", - "tokio-util", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-streams", - "web-sys", - "webpki-roots", - "winreg 0.50.0", -] - -[[package]] -name = "rfd" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0149778bd99b6959285b0933288206090c50e2327f47a9c463bfdbf45c8823ea" -dependencies = [ - "block", - "dispatch", - "glib-sys", - "gobject-sys", - "gtk-sys", - "js-sys", - "lazy_static", - "log", - "objc", - "objc-foundation", - "objc_id", - "raw-window-handle", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "windows 0.37.0", -] - -[[package]] -name = "ring" -version = "0.16.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" -dependencies = [ - "cc", - "libc", - "once_cell", - "spin 0.5.2", - "untrusted 0.7.1", - "web-sys", - "winapi", -] - -[[package]] -name = "ring" -version = "0.17.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" -dependencies = [ - "cc", - "cfg-if", - "getrandom 0.2.12", - "libc", - "spin 0.9.8", - "untrusted 0.9.0", - "windows-sys 0.52.0", -] - -[[package]] -name = "rkyv" -version = "0.7.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cba464629b3394fc4dbc6f940ff8f5b4ff5c7aef40f29166fd4ad12acbc99c0" -dependencies = [ - "bitvec", - "bytecheck", - "bytes", - "hashbrown 0.12.3", - "ptr_meta", - "rend", - "rkyv_derive", - "seahash", - "tinyvec", - "uuid", -] - -[[package]] -name = "rkyv_derive" -version = "0.7.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7dddfff8de25e6f62b9d64e6e432bf1c6736c57d20323e15ee10435fbda7c65" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "rocksdb" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bd13e55d6d7b8cd0ea569161127567cd587676c99f4472f779a0279aa60a7a7" -dependencies = [ - "libc", - "librocksdb-sys", -] - -[[package]] -name = "rust-stemmers" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e46a2036019fdb888131db7a4c847a1063a7493f971ed94ea82c67eada63ca54" -dependencies = [ - "serde", - "serde_derive", -] - -[[package]] -name = "rust_decimal" -version = "1.36.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b082d80e3e3cc52b2ed634388d436fe1f4de6af5786cc2de9ba9737527bdf555" -dependencies = [ - "arrayvec", - "borsh", - "bytes", - "num-traits", - "rand 0.8.5", - "rkyv", - "serde", - "serde_json", -] - -[[package]] -name = "rust_decimal_macros" -version = "1.34.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e418701588729bef95e7a655f2b483ad64bb97c46e8e79fde83efd92aaab6d82" -dependencies = [ - "quote", - "rust_decimal", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" - -[[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - -[[package]] -name = "rustc_version" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" -dependencies = [ - "semver", -] - -[[package]] -name = "rustix" -version = "0.38.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" -dependencies = [ - "bitflags 2.5.0", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.52.0", -] - -[[package]] -name = "rustls" -version = "0.21.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" -dependencies = [ - "log", - "ring 0.17.8", - "rustls-webpki", - "sct", -] - -[[package]] -name = "rustls-pemfile" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" -dependencies = [ - "base64 0.21.7", -] - -[[package]] -name = "rustls-webpki" -version = "0.101.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" -dependencies = [ - "ring 0.17.8", - "untrusted 0.9.0", -] - -[[package]] -name = "rustversion" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" - -[[package]] -name = "rusty-money" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b28f881005eac7ad8d46b6f075da5f322bd7f4f83a38720fc069694ddadd683" -dependencies = [ - "rust_decimal", - "rust_decimal_macros", -] - -[[package]] -name = "ryu" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "sanitize-filename" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ed72fbaf78e6f2d41744923916966c4fbe3d7c74e3037a8ee482f1115572603" -dependencies = [ - "lazy_static", - "regex", -] - -[[package]] -name = "schannel" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" -dependencies = [ - "windows-sys 0.52.0", -] - -[[package]] -name = "scheduled-thread-pool" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cbc66816425a074528352f5789333ecff06ca41b36b0b0efdfbb29edc391a19" -dependencies = [ - "parking_lot 0.12.1", -] - -[[package]] -name = "scoped-tls" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "scraper" -version = "0.17.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c95a930e03325234c18c7071fd2b60118307e025d6fff3e12745ffbf63a3d29c" -dependencies = [ - "ahash 0.8.11", - "cssparser 0.31.2", - "ego-tree", - "getopts", - "html5ever", - "once_cell", - "selectors 0.25.0", - "smallvec", - "tendril", -] - -[[package]] -name = "scraper" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "585480e3719b311b78a573db1c9d9c4c1f8010c2dee4cc59c2efe58ea4dbc3e1" -dependencies = [ - "ahash 0.8.11", - "cssparser 0.31.2", - "ego-tree", - "getopts", - "html5ever", - "once_cell", - "selectors 0.25.0", - "tendril", -] - -[[package]] -name = "sct" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" -dependencies = [ - "ring 0.17.8", - "untrusted 0.9.0", -] - -[[package]] -name = "seahash" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" - -[[package]] -name = "security-framework" -version = "2.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "selectors" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df320f1889ac4ba6bc0cdc9c9af7af4bd64bb927bccdf32d81140dc1f9be12fe" -dependencies = [ - "bitflags 1.3.2", - "cssparser 0.27.2", - "derive_more", - "fxhash", - "log", - "matches", - "phf 0.8.0", - "phf_codegen 0.8.0", - "precomputed-hash", - "servo_arc 0.1.1", - "smallvec", - "thin-slice", -] - -[[package]] -name = "selectors" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eb30575f3638fc8f6815f448d50cb1a2e255b0897985c8c59f4d37b72a07b06" -dependencies = [ - "bitflags 2.5.0", - "cssparser 0.31.2", - "derive_more", - "fxhash", - "log", - "new_debug_unreachable", - "phf 0.10.1", - "phf_codegen 0.10.0", - "precomputed-hash", - "servo_arc 0.3.0", - "smallvec", -] - -[[package]] -name = "semver" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" -dependencies = [ - "serde", -] - -[[package]] -name = "serde" -version = "1.0.210" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.210" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.55", -] - -[[package]] -name = "serde_derive_internals" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e578a843d40b4189a4d66bba51d7684f57da5bd7c304c64e14bd63efbef49509" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.55", -] - -[[package]] -name = "serde_json" -version = "1.0.128" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" -dependencies = [ - "indexmap 2.2.6", - "itoa 1.0.10", - "memchr", - "ryu", - "serde", -] - -[[package]] -name = "serde_repr" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.55", -] - -[[package]] -name = "serde_spanned" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa 1.0.10", - "ryu", - "serde", -] - -[[package]] -name = "serde_with" -version = "3.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee80b0e361bbf88fd2f6e242ccd19cfda072cb0faa6ae694ecee08199938569a" -dependencies = [ - "base64 0.21.7", - "chrono", - "hex", - "indexmap 1.9.3", - "indexmap 2.2.6", - "serde", - "serde_derive", - "serde_json", - "serde_with_macros", - "time", -] - -[[package]] -name = "serde_with_macros" -version = "3.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6561dc161a9224638a31d876ccdfefbc1df91d3f3a8342eddb35f055d48c7655" -dependencies = [ - "darling", - "proc-macro2", - "quote", - "syn 2.0.55", -] - -[[package]] -name = "serialize-to-javascript" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9823f2d3b6a81d98228151fdeaf848206a7855a7a042bbf9bf870449a66cafb" -dependencies = [ - "serde", - "serde_json", - "serialize-to-javascript-impl", -] - -[[package]] -name = "serialize-to-javascript-impl" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74064874e9f6a15f04c1f3cb627902d0e6b410abbf36668afa873c61889f1763" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "servo_arc" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d98238b800e0d1576d8b6e3de32827c2d74bee68bb97748dcf5071fb53965432" -dependencies = [ - "nodrop", - "stable_deref_trait", -] - -[[package]] -name = "servo_arc" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d036d71a959e00c77a63538b90a6c2390969f9772b096ea837205c6bd0491a44" -dependencies = [ - "stable_deref_trait", -] - -[[package]] -name = "sha1" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sha1_smol" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" - -[[package]] -name = "sha2" -version = "0.10.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sharded-slab" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "shared-entity" -version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=ea131f0baab67defe7591067357eced490072372#ea131f0baab67defe7591067357eced490072372" -dependencies = [ - "anyhow", - "app-error", - "appflowy-ai-client", - "bytes", - "chrono", - "collab-entity", - "database-entity", - "futures", - "gotrue-entity", - "infra", - "log", - "pin-project", - "reqwest", - "serde", - "serde_json", - "serde_repr", - "thiserror", - "tracing", - "uuid", - "validator 0.19.0", -] - -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - -[[package]] -name = "signal-hook-registry" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" -dependencies = [ - "libc", -] - -[[package]] -name = "simd-adler32" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" - -[[package]] -name = "simdutf8" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a" - -[[package]] -name = "similar" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad1d488a557b235fc46dae55512ffbfc429d2482b08b4d9435ab07384ca8aec" - -[[package]] -name = "similar" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32fea41aca09ee824cc9724996433064c89f7777e60762749a4170a14abbfa21" - -[[package]] -name = "simple_asn1" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc4e5204eb1910f40f9cfa375f6f05b68c3abac4b6fd879c8ff5e7ae8a0a085" -dependencies = [ - "num-bigint", - "num-traits", - "thiserror", - "time", -] - -[[package]] -name = "siphasher" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" - -[[package]] -name = "sketches-ddsketch" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85636c14b73d81f541e525f585c0a2109e6744e1565b5c1668e31c70c10ed65c" -dependencies = [ - "serde", -] - -[[package]] -name = "slab" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] - -[[package]] -name = "slug" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bd94acec9c8da640005f8e135a39fc0372e74535e6b368b7a04b875f784c8c4" -dependencies = [ - "deunicode", - "wasm-bindgen", -] - -[[package]] -name = "smallstr" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63b1aefdf380735ff8ded0b15f31aab05daf1f70216c01c02a12926badd1df9d" -dependencies = [ - "smallvec", -] - -[[package]] -name = "smallvec" -version = "1.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" - -[[package]] -name = "socket2" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "soup2" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2b4d76501d8ba387cf0fefbe055c3e0a59891d09f0f995ae4e4b16f6b60f3c0" -dependencies = [ - "bitflags 1.3.2", - "gio", - "glib", - "libc", - "once_cell", - "soup2-sys", -] - -[[package]] -name = "soup2-sys" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "009ef427103fcb17f802871647a7fa6c60cbb654b4c4e4c0ac60a31c5f6dc9cf" -dependencies = [ - "bitflags 1.3.2", - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "system-deps 5.0.0", -] - -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" - -[[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - -[[package]] -name = "state" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbe866e1e51e8260c9eed836a042a5e7f6726bb2b411dffeaa712e19c388f23b" -dependencies = [ - "loom", -] - -[[package]] -name = "string_cache" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" -dependencies = [ - "new_debug_unreachable", - "once_cell", - "parking_lot 0.12.1", - "phf_shared 0.10.0", - "precomputed-hash", - "serde", -] - -[[package]] -name = "string_cache_codegen" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988" -dependencies = [ - "phf_generator 0.10.0", - "phf_shared 0.10.0", - "proc-macro2", - "quote", -] - -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - -[[package]] -name = "strsim" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" - -[[package]] -name = "strum" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" - -[[package]] -name = "strum_macros" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d06aaeeee809dbc59eb4556183dd927df67db1540de5be8d3ec0b6636358a5ec" -dependencies = [ - "heck 0.3.3", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "strum_macros" -version = "0.25.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" -dependencies = [ - "heck 0.4.1", - "proc-macro2", - "quote", - "rustversion", - "syn 2.0.55", -] - -[[package]] -name = "strum_macros" -version = "0.26.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946" -dependencies = [ - "heck 0.4.1", - "proc-macro2", - "quote", - "rustversion", - "syn 2.0.55", -] - -[[package]] -name = "subtle" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.55" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "002a1b3dbf967edfafc32655d0f377ab0bb7b994aa1d32c8cc7e9b8bf3ebb8f0" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn_derive" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1329189c02ff984e9736652b1631330da25eaa6bc639089ed4915d25446cbe7b" -dependencies = [ - "proc-macro-error", - "proc-macro2", - "quote", - "syn 2.0.55", -] - -[[package]] -name = "sync_wrapper" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" - -[[package]] -name = "synstructure" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.55", -] - -[[package]] -name = "sysinfo" -version = "0.30.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c385888ef380a852a16209afc8cfad22795dd8873d69c9a14d2e2088f118d18" -dependencies = [ - "cfg-if", - "core-foundation-sys", - "libc", - "ntapi", - "once_cell", - "rayon", - "windows 0.52.0", -] - -[[package]] -name = "system-configuration" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "system-configuration-sys", -] - -[[package]] -name = "system-configuration-sys" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "system-deps" -version = "5.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18db855554db7bd0e73e06cf7ba3df39f97812cb11d3f75e71c39bf45171797e" -dependencies = [ - "cfg-expr 0.9.1", - "heck 0.3.3", - "pkg-config", - "toml 0.5.11", - "version-compare 0.0.11", -] - -[[package]] -name = "system-deps" -version = "6.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" -dependencies = [ - "cfg-expr 0.15.7", - "heck 0.5.0", - "pkg-config", - "toml 0.8.12", - "version-compare 0.2.0", -] - -[[package]] -name = "tagptr" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417" - -[[package]] -name = "tantivy" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8d0582f186c0a6d55655d24543f15e43607299425c5ad8352c242b914b31856" -dependencies = [ - "aho-corasick", - "arc-swap", - "base64 0.22.1", - "bitpacking", - "byteorder", - "census", - "crc32fast", - "crossbeam-channel", - "downcast-rs", - "fastdivide", - "fnv", - "fs4", - "htmlescape", - "itertools 0.12.1", - "levenshtein_automata", - "log", - "lru", - "lz4_flex", - "measure_time", - "memmap2", - "num_cpus", - "once_cell", - "oneshot", - "rayon", - "regex", - "rust-stemmers", - "rustc-hash", - "serde", - "serde_json", - "sketches-ddsketch", - "smallvec", - "tantivy-bitpacker", - "tantivy-columnar", - "tantivy-common", - "tantivy-fst", - "tantivy-query-grammar", - "tantivy-stacker", - "tantivy-tokenizer-api", - "tempfile", - "thiserror", - "time", - "uuid", - "winapi", -] - -[[package]] -name = "tantivy-bitpacker" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "284899c2325d6832203ac6ff5891b297fc5239c3dc754c5bc1977855b23c10df" -dependencies = [ - "bitpacking", -] - -[[package]] -name = "tantivy-columnar" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12722224ffbe346c7fec3275c699e508fd0d4710e629e933d5736ec524a1f44e" -dependencies = [ - "downcast-rs", - "fastdivide", - "itertools 0.12.1", - "serde", - "tantivy-bitpacker", - "tantivy-common", - "tantivy-sstable", - "tantivy-stacker", -] - -[[package]] -name = "tantivy-common" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8019e3cabcfd20a1380b491e13ff42f57bb38bf97c3d5fa5c07e50816e0621f4" -dependencies = [ - "async-trait", - "byteorder", - "ownedbytes", - "serde", - "time", -] - -[[package]] -name = "tantivy-fst" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d60769b80ad7953d8a7b2c70cdfe722bbcdcac6bccc8ac934c40c034d866fc18" -dependencies = [ - "byteorder", - "regex-syntax 0.8.2", - "utf8-ranges", -] - -[[package]] -name = "tantivy-query-grammar" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "847434d4af57b32e309f4ab1b4f1707a6c566656264caa427ff4285c4d9d0b82" -dependencies = [ - "nom", -] - -[[package]] -name = "tantivy-sstable" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c69578242e8e9fc989119f522ba5b49a38ac20f576fc778035b96cc94f41f98e" -dependencies = [ - "tantivy-bitpacker", - "tantivy-common", - "tantivy-fst", - "zstd 0.13.2", -] - -[[package]] -name = "tantivy-stacker" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c56d6ff5591fc332739b3ce7035b57995a3ce29a93ffd6012660e0949c956ea8" -dependencies = [ - "murmurhash32", - "rand_distr", - "tantivy-common", -] - -[[package]] -name = "tantivy-tokenizer-api" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0dcade25819a89cfe6f17d932c9cedff11989936bf6dd4f336d50392053b04" -dependencies = [ - "serde", -] - -[[package]] -name = "tao" -version = "0.16.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d22205b267a679ca1c590b9f178488d50981fc3e48a1b91641ae31593db875ce" -dependencies = [ - "bitflags 1.3.2", - "cairo-rs", - "cc", - "cocoa", - "core-foundation", - "core-graphics 0.22.3", - "crossbeam-channel", - "dispatch", - "gdk", - "gdk-pixbuf", - "gdk-sys", - "gdkwayland-sys", - "gdkx11-sys", - "gio", - "glib", - "glib-sys", - "gtk", - "image", - "instant", - "jni", - "lazy_static", - "libc", - "log", - "ndk", - "ndk-context", - "ndk-sys", - "objc", - "once_cell", - "parking_lot 0.12.1", - "png", - "raw-window-handle", - "scopeguard", - "serde", - "tao-macros", - "unicode-segmentation", - "uuid", - "windows 0.39.0", - "windows-implement", - "x11-dl", -] - -[[package]] -name = "tao-macros" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec114582505d158b669b136e6851f85840c109819d77c42bb7c0709f727d18c2" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "tap" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" - -[[package]] -name = "tar" -version = "0.4.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b16afcea1f22891c49a00c751c7b63b2233284064f11a200fc624137c51e2ddb" -dependencies = [ - "filetime", - "libc", - "xattr", -] - -[[package]] -name = "target-lexicon" -version = "0.12.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" - -[[package]] -name = "tauri" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f078117725e36d55d29fafcbb4b1e909073807ca328ae8deb8c0b3843aac0fed" -dependencies = [ - "anyhow", - "cocoa", - "dirs-next", - "dunce", - "embed_plist", - "encoding_rs", - "flate2", - "futures-util", - "glib", - "glob", - "gtk", - "heck 0.4.1", - "http", - "ignore", - "objc", - "once_cell", - "open", - "percent-encoding", - "rand 0.8.5", - "raw-window-handle", - "regex", - "rfd", - "semver", - "serde", - "serde_json", - "serde_repr", - "serialize-to-javascript", - "state", - "tar", - "tauri-macros", - "tauri-runtime", - "tauri-runtime-wry", - "tauri-utils", - "tempfile", - "thiserror", - "tokio", - "url", - "uuid", - "webkit2gtk", - "webview2-com", - "windows 0.39.0", -] - -[[package]] -name = "tauri-build" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9914a4715e0b75d9f387a285c7e26b5bbfeb1249ad9f842675a82481565c532" -dependencies = [ - "anyhow", - "cargo_toml", - "dirs-next", - "heck 0.4.1", - "json-patch", - "semver", - "serde", - "serde_json", - "tauri-utils", - "tauri-winres", - "walkdir", -] - -[[package]] -name = "tauri-codegen" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1554c5857f65dbc377cefb6b97c8ac77b1cb2a90d30d3448114d5d6b48a77fc" -dependencies = [ - "base64 0.21.7", - "brotli", - "ico", - "json-patch", - "plist", - "png", - "proc-macro2", - "quote", - "regex", - "semver", - "serde", - "serde_json", - "sha2", - "tauri-utils", - "thiserror", - "time", - "uuid", - "walkdir", -] - -[[package]] -name = "tauri-macros" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "277abf361a3a6993ec16bcbb179de0d6518009b851090a01adfea12ac89fa875" -dependencies = [ - "heck 0.4.1", - "proc-macro2", - "quote", - "syn 1.0.109", - "tauri-codegen", - "tauri-utils", -] - -[[package]] -name = "tauri-plugin-deep-link" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4536f5f6602e8fdfaa7b3b185076c2a0704f8eb7015f4e58461eb483ec3ed1f8" -dependencies = [ - "dirs", - "interprocess", - "log", - "objc2", - "once_cell", - "tauri-utils", - "windows-sys 0.48.0", - "winreg 0.50.0", -] - -[[package]] -name = "tauri-runtime" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf2d0652aa2891ff3e9caa2401405257ea29ab8372cce01f186a5825f1bd0e76" -dependencies = [ - "gtk", - "http", - "http-range", - "rand 0.8.5", - "raw-window-handle", - "serde", - "serde_json", - "tauri-utils", - "thiserror", - "url", - "uuid", - "webview2-com", - "windows 0.39.0", -] - -[[package]] -name = "tauri-runtime-wry" -version = "0.14.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "067c56fc153b3caf406d7cd6de4486c80d1d66c0f414f39e94cb2f5543f6445f" -dependencies = [ - "arboard", - "cocoa", - "gtk", - "percent-encoding", - "rand 0.8.5", - "raw-window-handle", - "tauri-runtime", - "tauri-utils", - "uuid", - "webkit2gtk", - "webview2-com", - "windows 0.39.0", - "wry", -] - -[[package]] -name = "tauri-utils" -version = "1.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75ad0bbb31fccd1f4c56275d0a5c3abdf1f59999f72cb4ef8b79b4ed42082a21" -dependencies = [ - "brotli", - "ctor", - "dunce", - "glob", - "heck 0.4.1", - "html5ever", - "infer", - "json-patch", - "kuchikiki", - "log", - "memchr", - "phf 0.11.2", - "proc-macro2", - "quote", - "semver", - "serde", - "serde_json", - "serde_with", - "thiserror", - "url", - "walkdir", - "windows-version", -] - -[[package]] -name = "tauri-winres" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5993dc129e544393574288923d1ec447c857f3f644187f4fbf7d9a875fbfc4fb" -dependencies = [ - "embed-resource", - "toml 0.7.8", -] - -[[package]] -name = "tempfile" -version = "3.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" -dependencies = [ - "cfg-if", - "fastrand", - "rustix", - "windows-sys 0.52.0", -] - -[[package]] -name = "tendril" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" -dependencies = [ - "futf", - "mac", - "utf-8", -] - -[[package]] -name = "tera" -version = "1.19.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "970dff17c11e884a4a09bc76e3a17ef71e01bb13447a11e85226e254fe6d10b8" -dependencies = [ - "chrono", - "chrono-tz 0.8.6", - "globwalk", - "humansize", - "lazy_static", - "percent-encoding", - "pest", - "pest_derive", - "rand 0.8.5", - "regex", - "serde", - "serde_json", - "slug", - "unic-segment", -] - -[[package]] -name = "termcolor" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "terminal_size" -version = "0.1.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "thin-slice" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c" - -[[package]] -name = "thiserror" -version = "1.0.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.55", -] - -[[package]] -name = "thread-id" -version = "3.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7fbf4c9d56b320106cd64fd024dadfa0be7cb4706725fc44a7d7ce952d820c1" -dependencies = [ - "libc", - "redox_syscall 0.1.57", - "winapi", -] - -[[package]] -name = "thread_local" -version = "1.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" -dependencies = [ - "cfg-if", - "once_cell", -] - -[[package]] -name = "tiff" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e" -dependencies = [ - "flate2", - "jpeg-decoder", - "weezl", -] - -[[package]] -name = "time" -version = "0.3.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" -dependencies = [ - "deranged", - "itoa 1.0.10", - "num-conv", - "powerfmt", - "serde", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" - -[[package]] -name = "time-macros" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" -dependencies = [ - "num-conv", - "time-core", -] - -[[package]] -name = "tinystr" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" -dependencies = [ - "displaydoc", - "zerovec", -] - -[[package]] -name = "tinyvec" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "to_method" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7c4ceeeca15c8384bbc3e011dbd8fccb7f068a440b752b7d9b32ceb0ca0e2e8" - -[[package]] -name = "tokio" -version = "1.38.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" -dependencies = [ - "backtrace", - "bytes", - "libc", - "mio", - "num_cpus", - "parking_lot 0.12.1", - "pin-project-lite", - "signal-hook-registry", - "socket2", - "tokio-macros", - "tracing", - "windows-sys 0.48.0", -] - -[[package]] -name = "tokio-macros" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.55", -] - -[[package]] -name = "tokio-native-tls" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" -dependencies = [ - "native-tls", - "tokio", -] - -[[package]] -name = "tokio-retry" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f57eb36ecbe0fc510036adff84824dd3c24bb781e21bfa67b69d556aa85214f" -dependencies = [ - "pin-project", - "rand 0.8.5", - "tokio", -] - -[[package]] -name = "tokio-rustls" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" -dependencies = [ - "rustls", - "tokio", -] - -[[package]] -name = "tokio-stream" -version = "0.1.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", - "tokio-util", -] - -[[package]] -name = "tokio-tungstenite" -version = "0.20.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c" -dependencies = [ - "futures-util", - "log", - "native-tls", - "tokio", - "tokio-native-tls", - "tungstenite", -] - -[[package]] -name = "tokio-util" -version = "0.7.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" -dependencies = [ - "bytes", - "futures-core", - "futures-io", - "futures-sink", - "futures-util", - "hashbrown 0.14.3", - "pin-project-lite", - "slab", - "tokio", -] - -[[package]] -name = "toml" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" -dependencies = [ - "serde", -] - -[[package]] -name = "toml" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit 0.19.15", -] - -[[package]] -name = "toml" -version = "0.8.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit 0.22.9", -] - -[[package]] -name = "toml_datetime" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" -dependencies = [ - "serde", -] - -[[package]] -name = "toml_edit" -version = "0.19.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" -dependencies = [ - "indexmap 2.2.6", - "serde", - "serde_spanned", - "toml_datetime", - "winnow 0.5.40", -] - -[[package]] -name = "toml_edit" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" -dependencies = [ - "indexmap 2.2.6", - "toml_datetime", - "winnow 0.5.40", -] - -[[package]] -name = "toml_edit" -version = "0.22.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e40bb779c5187258fd7aad0eb68cb8706a0a81fa712fbea808ab43c4b8374c4" -dependencies = [ - "indexmap 2.2.6", - "serde", - "serde_spanned", - "toml_datetime", - "winnow 0.6.5", -] - -[[package]] -name = "tower-service" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" - -[[package]] -name = "tracing" -version = "0.1.41" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" -dependencies = [ - "log", - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-appender" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3566e8ce28cc0a3fe42519fc80e6b4c943cc4c8cef275620eb8dac2d3d4e06cf" -dependencies = [ - "crossbeam-channel", - "thiserror", - "time", - "tracing-subscriber", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.55", -] - -[[package]] -name = "tracing-bunyan-formatter" -version = "0.3.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d637245a0d8774bd48df6482e086c59a8b5348a910c3b0579354045a9d82411" -dependencies = [ - "ahash 0.8.11", - "gethostname 0.2.3", - "log", - "serde", - "serde_json", - "time", - "tracing", - "tracing-core", - "tracing-log 0.1.4", - "tracing-subscriber", -] - -[[package]] -name = "tracing-core" -version = "0.1.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" -dependencies = [ - "once_cell", - "valuable", -] - -[[package]] -name = "tracing-log" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f751112709b4e791d8ce53e32c4ed2d353565a795ce84da2285393f41557bdf2" -dependencies = [ - "log", - "once_cell", - "tracing-core", -] - -[[package]] -name = "tracing-log" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" -dependencies = [ - "log", - "once_cell", - "tracing-core", -] - -[[package]] -name = "tracing-serde" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "704b1aeb7be0d0a84fc9828cae51dab5970fee5088f83d1dd7ee6f6246fc6ff1" -dependencies = [ - "serde", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" -dependencies = [ - "matchers", - "nu-ansi-term", - "once_cell", - "regex", - "serde", - "serde_json", - "sharded-slab", - "smallvec", - "thread_local", - "tracing", - "tracing-core", - "tracing-log 0.2.0", - "tracing-serde", -] - -[[package]] -name = "tracing-wasm" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4575c663a174420fa2d78f4108ff68f65bf2fbb7dd89f33749b6e826b3626e07" -dependencies = [ - "tracing", - "tracing-subscriber", - "wasm-bindgen", -] - -[[package]] -name = "tree_magic_mini" -version = "3.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ee137597cdb361b55a4746983e4ac1b35ab6024396a419944ad473bb915265" -dependencies = [ - "fnv", - "home", - "memchr", - "nom", - "once_cell", - "petgraph", -] - -[[package]] -name = "treediff" -version = "4.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d127780145176e2b5d16611cc25a900150e86e9fd79d3bde6ff3a37359c9cb5" -dependencies = [ - "serde_json", -] - -[[package]] -name = "triomphe" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "859eb650cfee7434994602c3a68b25d77ad9e68c8a6cd491616ef86661382eb3" - -[[package]] -name = "try-lock" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" - -[[package]] -name = "tsify" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6b26cf145f2f3b9ff84e182c448eaf05468e247f148cf3d2a7d67d78ff023a0" -dependencies = [ - "gloo-utils", - "serde", - "serde_json", - "tsify-macros", - "wasm-bindgen", -] - -[[package]] -name = "tsify-macros" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a94b0f0954b3e59bfc2c246b4c8574390d94a4ad4ad246aaf2fb07d7dfd3b47" -dependencies = [ - "proc-macro2", - "quote", - "serde_derive_internals", - "syn 2.0.55", -] - -[[package]] -name = "tungstenite" -version = "0.20.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9" -dependencies = [ - "byteorder", - "bytes", - "data-encoding", - "http", - "httparse", - "log", - "native-tls", - "rand 0.8.5", - "sha1", - "thiserror", - "url", - "utf-8", -] - -[[package]] -name = "typenum" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" - -[[package]] -name = "ucd-trie" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" - -[[package]] -name = "unic-char-property" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" -dependencies = [ - "unic-char-range", -] - -[[package]] -name = "unic-char-range" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" - -[[package]] -name = "unic-common" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" - -[[package]] -name = "unic-segment" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4ed5d26be57f84f176157270c112ef57b86debac9cd21daaabbe56db0f88f23" -dependencies = [ - "unic-ucd-segment", -] - -[[package]] -name = "unic-ucd-segment" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2079c122a62205b421f499da10f3ee0f7697f012f55b675e002483c73ea34700" -dependencies = [ - "unic-char-property", - "unic-char-range", - "unic-ucd-version", -] - -[[package]] -name = "unic-ucd-version" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" -dependencies = [ - "unic-common", -] - -[[package]] -name = "unicase" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" -dependencies = [ - "version_check", -] - -[[package]] -name = "unicode-bidi" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" - -[[package]] -name = "unicode-id" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1b6def86329695390197b82c1e244a54a131ceb66c996f2088a3876e2ae083f" - -[[package]] -name = "unicode-ident" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" - -[[package]] -name = "unicode-normalization" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "unicode-segmentation" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" - -[[package]] -name = "unicode-width" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" - -[[package]] -name = "universal-hash" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" -dependencies = [ - "crypto-common", - "subtle", -] - -[[package]] -name = "untrusted" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" - -[[package]] -name = "untrusted" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" - -[[package]] -name = "url" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" -dependencies = [ - "form_urlencoded", - "idna 0.5.0", - "percent-encoding", - "serde", -] - -[[package]] -name = "utf-8" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" - -[[package]] -name = "utf16_iter" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" - -[[package]] -name = "utf8-ranges" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcfc827f90e53a02eaef5e535ee14266c1d569214c6aa70133a624d8a3164ba" - -[[package]] -name = "utf8_iter" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" - -[[package]] -name = "uuid" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" -dependencies = [ - "getrandom 0.2.12", - "serde", - "sha1_smol", - "wasm-bindgen", -] - -[[package]] -name = "validator" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db79c75af171630a3148bd3e6d7c4f42b6a9a014c2945bc5ed0020cbb8d9478e" -dependencies = [ - "idna 0.5.0", - "once_cell", - "regex", - "serde", - "serde_derive", - "serde_json", - "url", - "validator_derive 0.18.2", -] - -[[package]] -name = "validator" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0b4a29d8709210980a09379f27ee31549b73292c87ab9899beee1c0d3be6303" -dependencies = [ - "idna 1.0.3", - "once_cell", - "regex", - "serde", - "serde_derive", - "serde_json", - "url", - "validator_derive 0.19.0", -] - -[[package]] -name = "validator_derive" -version = "0.18.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df0bcf92720c40105ac4b2dda2a4ea3aa717d4d6a862cc217da653a4bd5c6b10" -dependencies = [ - "darling", - "once_cell", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 2.0.55", -] - -[[package]] -name = "validator_derive" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bac855a2ce6f843beb229757e6e570a42e837bcb15e5f449dd48d5747d41bf77" -dependencies = [ - "darling", - "once_cell", - "proc-macro-error2", - "proc-macro2", - "quote", - "syn 2.0.55", -] - -[[package]] -name = "valuable" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" - -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - -[[package]] -name = "version-compare" -version = "0.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c18c859eead79d8b95d09e4678566e8d70105c4e7b251f707a03df32442661b" - -[[package]] -name = "version-compare" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "vswhom" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be979b7f07507105799e854203b470ff7c78a1639e330a58f183b5fea574608b" -dependencies = [ - "libc", - "vswhom-sys", -] - -[[package]] -name = "vswhom-sys" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3b17ae1f6c8a2b28506cd96d412eebf83b4a0ff2cbefeeb952f2f9dfa44ba18" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "walkdir" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" -dependencies = [ - "same-file", - "winapi-util", -] - -[[package]] -name = "want" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" -dependencies = [ - "try-lock", -] - -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "wasm-bindgen" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn 2.0.55", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.55", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" - -[[package]] -name = "wasm-streams" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129" -dependencies = [ - "futures-util", - "js-sys", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "wasm-timer" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be0ecb0db480561e9a7642b5d3e4187c128914e58aa84330b9493e3eb68c5e7f" -dependencies = [ - "futures", - "js-sys", - "parking_lot 0.11.2", - "pin-utils", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "wayland-backend" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d50fa61ce90d76474c87f5fc002828d81b32677340112b4ef08079a9d459a40" -dependencies = [ - "cc", - "downcast-rs", - "rustix", - "scoped-tls", - "smallvec", - "wayland-sys", -] - -[[package]] -name = "wayland-client" -version = "0.31.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82fb96ee935c2cea6668ccb470fb7771f6215d1691746c2d896b447a00ad3f1f" -dependencies = [ - "bitflags 2.5.0", - "rustix", - "wayland-backend", - "wayland-scanner", -] - -[[package]] -name = "wayland-protocols" -version = "0.31.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f81f365b8b4a97f422ac0e8737c438024b5951734506b0e1d775c73030561f4" -dependencies = [ - "bitflags 2.5.0", - "wayland-backend", - "wayland-client", - "wayland-scanner", -] - -[[package]] -name = "wayland-protocols-wlr" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad1f61b76b6c2d8742e10f9ba5c3737f6530b4c243132c2a2ccc8aa96fe25cd6" -dependencies = [ - "bitflags 2.5.0", - "wayland-backend", - "wayland-client", - "wayland-protocols", - "wayland-scanner", -] - -[[package]] -name = "wayland-scanner" -version = "0.31.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63b3a62929287001986fb58c789dce9b67604a397c15c611ad9f747300b6c283" -dependencies = [ - "proc-macro2", - "quick-xml", - "quote", -] - -[[package]] -name = "wayland-sys" -version = "0.31.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15a0c8eaff5216d07f226cb7a549159267f3467b289d9a2e52fd3ef5aae2b7af" -dependencies = [ - "dlib", - "log", - "pkg-config", -] - -[[package]] -name = "web-sys" -version = "0.3.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "webkit2gtk" -version = "0.18.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8f859735e4a452aeb28c6c56a852967a8a76c8eb1cc32dbf931ad28a13d6370" -dependencies = [ - "bitflags 1.3.2", - "cairo-rs", - "gdk", - "gdk-sys", - "gio", - "gio-sys", - "glib", - "glib-sys", - "gobject-sys", - "gtk", - "gtk-sys", - "javascriptcore-rs", - "libc", - "once_cell", - "soup2", - "webkit2gtk-sys", -] - -[[package]] -name = "webkit2gtk-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d76ca6ecc47aeba01ec61e480139dda143796abcae6f83bcddf50d6b5b1dcf3" -dependencies = [ - "atk-sys", - "bitflags 1.3.2", - "cairo-sys-rs", - "gdk-pixbuf-sys", - "gdk-sys", - "gio-sys", - "glib-sys", - "gobject-sys", - "gtk-sys", - "javascriptcore-rs-sys", - "libc", - "pango-sys", - "pkg-config", - "soup2-sys", - "system-deps 6.2.2", -] - -[[package]] -name = "webpki-roots" -version = "0.25.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" - -[[package]] -name = "webview2-com" -version = "0.19.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4a769c9f1a64a8734bde70caafac2b96cada12cd4aefa49196b3a386b8b4178" -dependencies = [ - "webview2-com-macros", - "webview2-com-sys", - "windows 0.39.0", - "windows-implement", -] - -[[package]] -name = "webview2-com-macros" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaebe196c01691db62e9e4ca52c5ef1e4fd837dcae27dae3ada599b5a8fd05ac" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "webview2-com-sys" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aac48ef20ddf657755fdcda8dfed2a7b4fc7e4581acce6fe9b88c3d64f29dee7" -dependencies = [ - "regex", - "serde", - "serde_json", - "thiserror", - "windows 0.39.0", - "windows-bindgen", - "windows-metadata", -] - -[[package]] -name = "weezl" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" - -[[package]] -name = "which" -version = "4.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" -dependencies = [ - "either", - "home", - "once_cell", - "rustix", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" -dependencies = [ - "winapi", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57b543186b344cc61c85b5aab0d2e3adf4e0f99bc076eff9aa5927bcc0b8a647" -dependencies = [ - "windows_aarch64_msvc 0.37.0", - "windows_i686_gnu 0.37.0", - "windows_i686_msvc 0.37.0", - "windows_x86_64_gnu 0.37.0", - "windows_x86_64_msvc 0.37.0", -] - -[[package]] -name = "windows" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1c4bd0a50ac6020f65184721f758dba47bb9fbc2133df715ec74a237b26794a" -dependencies = [ - "windows-implement", - "windows_aarch64_msvc 0.39.0", - "windows_i686_gnu 0.39.0", - "windows_i686_msvc 0.39.0", - "windows_x86_64_gnu 0.39.0", - "windows_x86_64_msvc 0.39.0", -] - -[[package]] -name = "windows" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" -dependencies = [ - "windows-targets 0.48.5", -] - -[[package]] -name = "windows" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" -dependencies = [ - "windows-core", - "windows-targets 0.52.4", -] - -[[package]] -name = "windows-bindgen" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68003dbd0e38abc0fb85b939240f4bce37c43a5981d3df37ccbaaa981b47cb41" -dependencies = [ - "windows-metadata", - "windows-tokens", -] - -[[package]] -name = "windows-core" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" -dependencies = [ - "windows-targets 0.52.4", -] - -[[package]] -name = "windows-implement" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba01f98f509cb5dc05f4e5fc95e535f78260f15fea8fe1a8abdd08f774f1cee7" -dependencies = [ - "syn 1.0.109", - "windows-tokens", -] - -[[package]] -name = "windows-metadata" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ee5e275231f07c6e240d14f34e1b635bf1faa1c76c57cfd59a5cdb9848e4278" - -[[package]] -name = "windows-sys" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", -] - -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets 0.52.4", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", -] - -[[package]] -name = "windows-targets" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" -dependencies = [ - "windows_aarch64_gnullvm 0.52.4", - "windows_aarch64_msvc 0.52.4", - "windows_i686_gnu 0.52.4", - "windows_i686_msvc 0.52.4", - "windows_x86_64_gnu 0.52.4", - "windows_x86_64_gnullvm 0.52.4", - "windows_x86_64_msvc 0.52.4", -] - -[[package]] -name = "windows-tokens" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f838de2fe15fe6bac988e74b798f26499a8b21a9d97edec321e79b28d1d7f597" - -[[package]] -name = "windows-version" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75aa004c988e080ad34aff5739c39d0312f4684699d6d71fc8a198d057b8b9b4" -dependencies = [ - "windows-targets 0.52.4", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2623277cb2d1c216ba3b578c0f3cf9cdebeddb6e66b1b218bb33596ea7769c3a" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec7711666096bd4096ffa835238905bb33fb87267910e154b18b44eaabb340f2" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" - -[[package]] -name = "windows_i686_gnu" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3925fd0b0b804730d44d4b6278c50f9699703ec49bcd628020f46f4ba07d9e1" - -[[package]] -name = "windows_i686_gnu" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "763fc57100a5f7042e3057e7e8d9bdd7860d330070251a73d003563a3bb49e1b" - -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" - -[[package]] -name = "windows_i686_msvc" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce907ac74fe331b524c1298683efbf598bb031bc84d5e274db2083696d07c57c" - -[[package]] -name = "windows_i686_msvc" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bc7cbfe58828921e10a9f446fcaaf649204dcfe6c1ddd712c5eebae6bda1106" - -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2babfba0828f2e6b32457d5341427dcbb577ceef556273229959ac23a10af33d" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6868c165637d653ae1e8dc4d82c25d4f97dd6605eaa8d784b5c6e0ab2a252b65" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4dd6dc7df2d84cf7b33822ed5b86318fb1781948e9663bacd047fc9dd52259d" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e4d40883ae9cae962787ca76ba76390ffa29214667a111db9e0a1ad8377e809" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" - -[[package]] -name = "winnow" -version = "0.5.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" -dependencies = [ - "memchr", -] - -[[package]] -name = "winnow" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dffa400e67ed5a4dd237983829e66475f0a4a26938c4b04c21baede6262215b8" -dependencies = [ - "memchr", -] - -[[package]] -name = "winreg" -version = "0.50.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" -dependencies = [ - "cfg-if", - "windows-sys 0.48.0", -] - -[[package]] -name = "winreg" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" -dependencies = [ - "cfg-if", - "windows-sys 0.48.0", -] - -[[package]] -name = "wl-clipboard-rs" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12b41773911497b18ca8553c3daaf8ec9fe9819caf93d451d3055f69de028adb" -dependencies = [ - "derive-new", - "libc", - "log", - "nix", - "os_pipe", - "tempfile", - "thiserror", - "tree_magic_mini", - "wayland-backend", - "wayland-client", - "wayland-protocols", - "wayland-protocols-wlr", -] - -[[package]] -name = "write16" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" - -[[package]] -name = "writeable" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" - -[[package]] -name = "wry" -version = "0.24.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ad85d0e067359e409fcb88903c3eac817c392e5d638258abfb3da5ad8ba6fc4" -dependencies = [ - "base64 0.13.1", - "block", - "cocoa", - "core-graphics 0.22.3", - "crossbeam-channel", - "dunce", - "gdk", - "gio", - "glib", - "gtk", - "html5ever", - "http", - "kuchikiki", - "libc", - "log", - "objc", - "objc_id", - "once_cell", - "serde", - "serde_json", - "sha2", - "soup2", - "tao", - "thiserror", - "url", - "webkit2gtk", - "webkit2gtk-sys", - "webview2-com", - "windows 0.39.0", - "windows-implement", -] - -[[package]] -name = "wyz" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" -dependencies = [ - "tap", -] - -[[package]] -name = "x11" -version = "2.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "502da5464ccd04011667b11c435cb992822c2c0dbde1770c988480d312a0db2e" -dependencies = [ - "libc", - "pkg-config", -] - -[[package]] -name = "x11-dl" -version = "2.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" -dependencies = [ - "libc", - "once_cell", - "pkg-config", -] - -[[package]] -name = "x11rb" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8f25ead8c7e4cba123243a6367da5d3990e0d3affa708ea19dce96356bd9f1a" -dependencies = [ - "gethostname 0.4.3", - "rustix", - "x11rb-protocol", -] - -[[package]] -name = "x11rb-protocol" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e63e71c4b8bd9ffec2c963173a4dc4cbde9ee96961d4fcb4429db9929b606c34" - -[[package]] -name = "xattr" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da84f1a25939b27f6820d92aed108f83ff920fdf11a7b19366c27c4cda81d4f" -dependencies = [ - "libc", - "linux-raw-sys", - "rustix", -] - -[[package]] -name = "xz2" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388c44dc09d76f1536602ead6d325eb532f5c122f17782bd57fb47baeeb767e2" -dependencies = [ - "lzma-sys", -] - -[[package]] -name = "yoke" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" -dependencies = [ - "serde", - "stable_deref_trait", - "yoke-derive", - "zerofrom", -] - -[[package]] -name = "yoke-derive" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.55", - "synstructure", -] - -[[package]] -name = "yrs" -version = "0.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81de5913bca29f43a1d12ca92a7b39a2945e9420e01602a7563917c7bfc60f70" -dependencies = [ - "arc-swap", - "async-lock", - "async-trait", - "dashmap 6.0.1", - "fastrand", - "serde", - "serde_json", - "smallstr", - "smallvec", - "thiserror", -] - -[[package]] -name = "zerocopy" -version = "0.7.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.55", -] - -[[package]] -name = "zerofrom" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" -dependencies = [ - "zerofrom-derive", -] - -[[package]] -name = "zerofrom-derive" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.55", - "synstructure", -] - -[[package]] -name = "zeroize" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" -dependencies = [ - "zeroize_derive", -] - -[[package]] -name = "zeroize_derive" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.55", -] - -[[package]] -name = "zerovec" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" -dependencies = [ - "yoke", - "zerofrom", - "zerovec-derive", -] - -[[package]] -name = "zerovec-derive" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.55", -] - -[[package]] -name = "zip" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" -dependencies = [ - "aes", - "byteorder", - "bzip2", - "constant_time_eq 0.1.5", - "crc32fast", - "crossbeam-utils", - "flate2", - "hmac", - "pbkdf2 0.11.0", - "sha1", - "time", - "zstd 0.11.2+zstd.1.5.2", -] - -[[package]] -name = "zip" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc5e4288ea4057ae23afc69a4472434a87a2495cafce6632fd1c4ec9f5cf3494" -dependencies = [ - "aes", - "arbitrary", - "bzip2", - "constant_time_eq 0.3.0", - "crc32fast", - "crossbeam-utils", - "deflate64", - "displaydoc", - "flate2", - "hmac", - "indexmap 2.2.6", - "lzma-rs", - "memchr", - "pbkdf2 0.12.2", - "rand 0.8.5", - "sha1", - "thiserror", - "time", - "zeroize", - "zopfli", - "zstd 0.13.2", -] - -[[package]] -name = "zip-extensions" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb0a99499b3497d765525c5d05e3ade9ca4a731c184365c19472c3fd6ba86341" -dependencies = [ - "zip 2.2.0", -] - -[[package]] -name = "zopfli" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5019f391bac5cf252e93bbcc53d039ffd62c7bfb7c150414d61369afe57e946" -dependencies = [ - "bumpalo", - "crc32fast", - "lockfree-object-pool", - "log", - "once_cell", - "simd-adler32", -] - -[[package]] -name = "zstd" -version = "0.11.2+zstd.1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" -dependencies = [ - "zstd-safe 5.0.2+zstd.1.5.2", -] - -[[package]] -name = "zstd" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcf2b778a664581e31e389454a7072dab1647606d44f7feea22cd5abb9c9f3f9" -dependencies = [ - "zstd-safe 7.2.0", -] - -[[package]] -name = "zstd-safe" -version = "5.0.2+zstd.1.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db" -dependencies = [ - "libc", - "zstd-sys", -] - -[[package]] -name = "zstd-safe" -version = "7.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa556e971e7b568dc775c136fc9de8c779b1c2fc3a63defaafadffdbd3181afa" -dependencies = [ - "zstd-sys", -] - -[[package]] -name = "zstd-sys" -version = "2.0.12+zstd.1.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a4e40c320c3cb459d9a9ff6de98cff88f4751ee9275d140e2be94a2b74e4c13" -dependencies = [ - "cc", - "pkg-config", -] diff --git a/frontend/appflowy_web_app/src-tauri/Cargo.toml b/frontend/appflowy_web_app/src-tauri/Cargo.toml deleted file mode 100644 index 90479b42d1..0000000000 --- a/frontend/appflowy_web_app/src-tauri/Cargo.toml +++ /dev/null @@ -1,135 +0,0 @@ -[package] -name = "appflowy_tauri" -version = "0.0.0" -description = "A Tauri App" -authors = ["you"] -license = "" -repository = "" -edition = "2021" -rust-version = "1.57" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[build-dependencies] -tauri-build = { version = "1.5", features = [] } - -[workspace.dependencies] -anyhow = "1.0" -tracing = "0.1.40" -bytes = "1.5.0" -serde = "1.0" -serde_json = "1.0.108" -protobuf.workspace = true -diesel = { version = "2.1.0", features = [ - "sqlite", - "chrono", - "r2d2", - "serde_json", -] } -uuid = { version = "1.5.0", features = ["serde", "v4"] } -serde_repr = "0.1" -parking_lot = "0.12" -futures = "0.3.29" -tokio = "1.34.0" -tokio-stream = "0.1.14" -async-trait = "0.1.74" -chrono = { version = "0.4.31", default-features = false, features = ["clock"] } -yrs = "0.19.1" -# Please use the following script to update collab. -# Working directory: frontend -# -# To update the commit ID, run: -# scripts/tool/update_collab_rev.sh new_rev_id -# -# To switch to the local path, run: -# scripts/tool/update_collab_source.sh -# ⚠️⚠️⚠️️ -collab = { version = "0.2" } -collab-entity = { version = "0.2" } -collab-folder = { version = "0.2" } -collab-document = { version = "0.2" } -collab-database = { version = "0.2" } -collab-plugins = { version = "0.2" } -collab-user = { version = "0.2" } -collab-importer = { version = "0.1" } - -# Please using the following command to update the revision id -# Current directory: frontend -# Run the script: -# scripts/tool/update_client_api_rev.sh new_rev_id -# ⚠️⚠️⚠️️ -client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "ea131f0baab67defe7591067357eced490072372" } - -[dependencies] -serde_json.workspace = true -serde.workspace = true -tauri = { version = "1.5", features = [ - "dialog-all", - "clipboard-all", - "fs-all", - "shell-open", -] } -tauri-utils = "1.5.2" -bytes.workspace = true -tracing.workspace = true -lib-dispatch = { path = "../../rust-lib/lib-dispatch", features = [ - "use_serde", -] } -flowy-core = { path = "../../rust-lib/flowy-core", features = ["ts"] } -flowy-user = { path = "../../rust-lib/flowy-user", features = ["tauri_ts"] } -flowy-date = { path = "../../rust-lib/flowy-date", features = ["tauri_ts"] } -flowy-error = { path = "../../rust-lib/flowy-error", features = [ - "impl_from_sqlite", - "impl_from_dispatch_error", - "impl_from_appflowy_cloud", - "impl_from_reqwest", - "impl_from_serde", - "tauri_ts", -] } -flowy-document = { path = "../../rust-lib/flowy-document", features = [ - "tauri_ts", -] } -flowy-notification = { path = "../../rust-lib/flowy-notification", features = [ - "tauri_ts", -] } -flowy-ai = { path = "../../rust-lib/flowy-ai", features = ["tauri_ts"] } - -uuid = "1.5.0" -tauri-plugin-deep-link = "0.1.2" -dotenv = "0.15.0" -semver = "1.0.23" - -[features] -# by default Tauri runs in production mode -# when `tauri dev` runs it is executed with `cargo run --no-default-features` if `devPath` is an URL -default = ["custom-protocol"] -# this feature is used used for production builds where `devPath` points to the filesystem -# DO NOT remove this -custom-protocol = ["tauri/custom-protocol"] - -[patch.crates-io] -# Please use the following script to update collab. -# Working directory: frontend -# -# To update the commit ID, run: -# scripts/tool/update_collab_rev.sh new_rev_id -# -# To switch to the local path, run: -# scripts/tool/update_collab_source.sh -# ⚠️⚠️⚠️️ -collab = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "67b31e4bcc64d81332c37efecb0a8618681f1db9" } -collab-entity = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "67b31e4bcc64d81332c37efecb0a8618681f1db9" } -collab-folder = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "67b31e4bcc64d81332c37efecb0a8618681f1db9" } -collab-document = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "67b31e4bcc64d81332c37efecb0a8618681f1db9" } -collab-database = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "67b31e4bcc64d81332c37efecb0a8618681f1db9" } -collab-plugins = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "67b31e4bcc64d81332c37efecb0a8618681f1db9" } -collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "67b31e4bcc64d81332c37efecb0a8618681f1db9" } -collab-importer = { version = "0.1", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "67b31e4bcc64d81332c37efecb0a8618681f1db9" } - - -# Working directory: frontend -# To update the commit ID, run: -# scripts/tool/update_local_ai_rev.sh new_rev_id -# ⚠️⚠️⚠️️ -appflowy-local-ai = { version = "0.1", git = "https://github.com/AppFlowy-IO/AppFlowy-LocalAI", rev = "6f064efe232268f8d396edbb4b84d57fbb640f13" } -appflowy-plugin = { version = "0.1", git = "https://github.com/AppFlowy-IO/AppFlowy-LocalAI", rev = "6f064efe232268f8d396edbb4b84d57fbb640f13" } diff --git a/frontend/appflowy_web_app/src-tauri/Info.plist b/frontend/appflowy_web_app/src-tauri/Info.plist deleted file mode 100644 index 25b430c049..0000000000 --- a/frontend/appflowy_web_app/src-tauri/Info.plist +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - CFBundleURLTypes - - - CFBundleURLName - - appflowy-flutter - CFBundleURLSchemes - - appflowy-flutter - - - - - \ No newline at end of file diff --git a/frontend/appflowy_web_app/src-tauri/build.rs b/frontend/appflowy_web_app/src-tauri/build.rs deleted file mode 100644 index 795b9b7c83..0000000000 --- a/frontend/appflowy_web_app/src-tauri/build.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - tauri_build::build() -} diff --git a/frontend/appflowy_web_app/src-tauri/env.development b/frontend/appflowy_web_app/src-tauri/env.development deleted file mode 100644 index 188835e3d0..0000000000 --- a/frontend/appflowy_web_app/src-tauri/env.development +++ /dev/null @@ -1,4 +0,0 @@ -APPFLOWY_CLOUD_ENV_APPFLOWY_CLOUD_BASE_URL=https://test.appflowy.cloud -APPFLOWY_CLOUD_ENV_APPFLOWY_CLOUD_WS_BASE_URL=wss://test.appflowy.cloud/ws/v1 -APPFLOWY_CLOUD_ENV_APPFLOWY_CLOUD_GOTRUE_URL=https://test.appflowy.cloud/gotrue -APPFLOWY_CLOUD_ENV_CLOUD_TYPE=2 diff --git a/frontend/appflowy_web_app/src-tauri/env.production b/frontend/appflowy_web_app/src-tauri/env.production deleted file mode 100644 index b03c328b84..0000000000 --- a/frontend/appflowy_web_app/src-tauri/env.production +++ /dev/null @@ -1,4 +0,0 @@ -APPFLOWY_CLOUD_ENV_APPFLOWY_CLOUD_BASE_URL=https://beta.appflowy.cloud -APPFLOWY_CLOUD_ENV_APPFLOWY_CLOUD_WS_BASE_URL=wss://beta.appflowy.cloud/ws/v1 -APPFLOWY_CLOUD_ENV_APPFLOWY_CLOUD_GOTRUE_URL=https://beta.appflowy.cloud/gotrue -APPFLOWY_CLOUD_ENV_CLOUD_TYPE=2 diff --git a/frontend/appflowy_web_app/src-tauri/icons/128x128.png b/frontend/appflowy_web_app/src-tauri/icons/128x128.png deleted file mode 100644 index 3a51041313f50b6a508d22530b8ab0ebf852d0b9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9256 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 KMNUMnLSTXwZGAET diff --git a/frontend/appflowy_web_app/src-tauri/icons/128x128@2x.png b/frontend/appflowy_web_app/src-tauri/icons/128x128@2x.png deleted file mode 100644 index 9076de3a4b004c3ff7a4e4ce79cafb11049f8a05..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19737 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

YTwDpB&Y6uSef*F=+2e!^+l~3Z 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 PdB$dLj5R7XaKir&Th(Uv diff --git a/frontend/appflowy_web_app/src-tauri/icons/32x32.png b/frontend/appflowy_web_app/src-tauri/icons/32x32.png deleted file mode 100644 index 6ae6683fefb9797ef769e75c1efc625051b5296b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1922 zcmV-|2YvX7P)DA(5(5cqpii=b;RRuCvi>AAF?fUHFq@kgqyEc*vgPjo8G( zCsSf%WaQB-9IX%t;6$Lf{nV*buaPGmsu*mg#ntbf1;%~?O#dBdEJ}=k%|pOLF93@h z`1=^pY;t{_a0Ys4-`BYQ)OKJ?0f>2CTVn*(Q4}=>1_pMNN~N+EN?o6POU#p)8WYQ! zte*b4!>@J&6MqzJ0C@^2l6h$qc=gA?TAHvyY*_>3xEB_wOb&Qy7WmZbz_F`b;~A?H z;ZZJ^%kSO0_tDI7oeE4qA%OF42?9aGsplj7Wj`6vio#84Uh||SjtdlaxkRB{d+=lY z*Wky}S}VTeIyY+Me*ZY|%3P|1^74jCNx_A}mCqELPPti)dMP_6+_&m_tu8uXH~bl2_0 zM=PHm<$pKv?msko*0x*jAbDcQEUH`F`?N*h79SF!AMONhCEJK)Cs3VAh!=i&#fK*` zLhCTA?z*jJD`$O`n>rc6ECq?R$ssKYDlA@&k>6aySAT7>^GiArt^;3)gx}H62%r)u zv?`oSrCcCJ-l=C5F-KFqnz~zQzA{yZ3F-+`6&h4)Ra~iD#=zI-@yMTYxaD(U;+afA zVI?siCQyI$2?}la_{%4SK~gD0)hc)`fnm3bs0o%z?GoaJ5cR6TrrSe&`HA(o^}e+p zkOZ4v_GT^x-}koyGu(f!oSLJ#+GNOX*4R;8S^vXst?788u|u`xbuOv7}{Mx zK$N@Gw>o_PW^{;vyyqhh&t5c`XoR@y<^YGc!81_Tdq_w9Ur(APr4`c}`TypOa~Eck zh2%Ll|Fhm(IcuZVb0cmzw=n8lbH>HivzeTnJjY6`R(c@Kw*;(vUqXEJ1b+w6w{3xb z<^kZoPZMO^4fJZ`2UC~nt3n=&ph^eP^X~Jo3sbc4cBoyi3#dBsx|5UfdE$t&OKd2$x7%|A-Lk-M z|80V-dOW-n_!fAu0Q=aBi2qsg)uvqu7yFc;)i^%_jDLk^E&v;IQ0sVhom3>i z%Oa4drfw>FEi+e9mr z(#KqiFR@_+E|V3l@PG`E)f!7)8V&vIgFYYRpuV2CnhNC*q4GrPh3Xk#{;wV{%Y{p2 z4;|v^b+Ai$nMx9=i=?asXvs_YA=9f2kjZ<^*hv`NlMgXo3s8(1a*QEM29ud>wTSxh zvuK{Ka4_I2;y0B`E1B|wM0{USDw|l5ZKEDZBpvyc1OB=C5`Wu2#F2ZQZ){<1)S8O_CW- z#wf-=%mrzeSIK14HhK8COM1sqBrb^-mlhYxB#HTvBQG3fud6J-8Fjtqb-Tl5pvfkg z)kyMNicGirY`1IKs>}qKGA3Go`LQ>0Lr(IXY((*-&UNAOUqOn{`jmN%0ssI207*qo IM6N<$f^|T`)c^nh diff --git a/frontend/appflowy_web_app/src-tauri/icons/Square107x107Logo.png b/frontend/appflowy_web_app/src-tauri/icons/Square107x107Logo.png deleted file mode 100644 index b08dcf7d21ab955d1c587b5725620607132bc01e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7695 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 JPDHLkV1hA7>?QyJ diff --git a/frontend/appflowy_web_app/src-tauri/icons/Square142x142Logo.png b/frontend/appflowy_web_app/src-tauri/icons/Square142x142Logo.png deleted file mode 100644 index f3e437b76e43efab7651f695cea467ec49e42f1b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10254 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 diff --git a/frontend/appflowy_web_app/src-tauri/icons/Square150x150Logo.png b/frontend/appflowy_web_app/src-tauri/icons/Square150x150Logo.png deleted file mode 100644 index 6a1dc04864d952fd21030b1586d25989415d2c92..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11031 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 R=fnU2002ovPDHLkV1k&8tAhXl diff --git a/frontend/appflowy_web_app/src-tauri/icons/Square284x284Logo.png b/frontend/appflowy_web_app/src-tauri/icons/Square284x284Logo.png deleted file mode 100644 index 2f2d9d6fe630caa77b6679a907d8f183d74b8ef4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22113 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 r6a*_)u%&@JlQ^*aU!M2m&rRwC;6+b zt*w;|YU-dJXX1VP_U+Z6`pB0KO8G^ll(loD{CF<4yQJJvCS~W3QeNYT{gzr2wKLRy zrFK%vrRStPA64sJ-Y)>(wryLD#xoh+CE-A-s;YX%A23594!h=^9)8#W#Cm~(nG|f( z=sAnV!$3J-IQol>Y zpyaT9VC`p4nNTPc<^`is&p7oVX;@-{(_e})`A6fzAx&RW-<$6N7MB1Hfm=axI`ZM1JlFcKpdDcfakw- z@znN^Pk2598(H)2Tm&pCr1fOph@m#tV~LE_Oaz$=JIBRS8QkUx;}RDyH*6CzGBJ%Ha1>Ok7+7yw~mF;E>6Ai5>HS2l9n~L^BsvE+bJC>G{;* z^bmnHUy`zaNM+^LgL51X8$Z-j~q`B=I)9XJByK_4*)C|6l%;hJ6=_`38OVhb-DY5kc z#LxiDa3Ks@DfcwNS+!Gnh(X8Q?NWuV!A`viqjeqZ3(rC(njwqAenud6R;Nx5VBHrb zzU#V09-%yUJQY2W88A*?Kx)f@;Kr5X7cppmBXItHAWqL27K~DA1?1ZT*2JE4`J0nU zf)a|5vSKDc)yI3MTrznnyj@2z`g5BfG(LOih27hL4tB*b0a3cifLmqoIvP^v6@qzs zlBqKeFsmPB;-knj$JS9`K8T4Pp`H1@2gbx8^?;hR?afh&+lzK?%)0^%lj{6SO|S0t`M{fPub z%;$BLz#Akd+rn2a8jVFqMn)3jH0~wo>+9=P)^eq_ektVc4diJ$pqn7JGRV&c?EDCs zE{2hY7P`LXCdPu;#%Jv>9rbb5_NJl$c34_ zgL35Pu|xTJdEv2zrID+c=US|$re-VGNL;QIk=TYAqBVwT1o6nxo>?vUq6F9LoH87_ zQ4;oPLw!Si-BpF07*qoM6N<$f_%PGd;kCd diff --git a/frontend/appflowy_web_app/src-tauri/icons/Square310x310Logo.png b/frontend/appflowy_web_app/src-tauri/icons/Square310x310Logo.png deleted file mode 100644 index 230b1abe58c88622d22d15e4546f25527c4a1971..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24225 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 diff --git a/frontend/appflowy_web_app/src-tauri/icons/Square44x44Logo.png b/frontend/appflowy_web_app/src-tauri/icons/Square44x44Logo.png deleted file mode 100644 index ad188037a377ea08307915d91900bc4f79820a9d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2808 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 KMNUMnLSTX?Xm0}m diff --git a/frontend/appflowy_web_app/src-tauri/icons/Square71x71Logo.png b/frontend/appflowy_web_app/src-tauri/icons/Square71x71Logo.png deleted file mode 100644 index ceae9ad1bb025a48c0f4b5bfabae54e1bd0fcd6a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4873 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 Hu0mjfsYf*K diff --git a/frontend/appflowy_web_app/src-tauri/icons/icon.icns b/frontend/appflowy_web_app/src-tauri/icons/icon.icns deleted file mode 100644 index 74b585f25dfbd90bdf3e8f3c8f0a98b2055f10fb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 181898 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%0A{eq2aLfXVhS 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

Mik>o^lfQ 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 M^7n!xE;9Om07AgfUH||9 diff --git a/frontend/appflowy_web_app/src-tauri/icons/icon.ico b/frontend/appflowy_web_app/src-tauri/icons/icon.ico deleted file mode 100644 index cd9ad402d1c9d4c4a96c21c3b5596f26c7394fc9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32830 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%0AY=UEapfpDaZvc}sy zJ55zm!DZ54J2QQ>Q{DoF4Jgu}B838_kj%9C?W$Ka`G25X@5lmQ{aR~)FI(isf^we^ zdq{iWqh21aJcHJHC;EYZg*6ti#zGfyE(rhv@7V>lw2MX>>gvA+)hBHj=hCWt3({kh z%iJ^fmeZ(!o&)rD^>Om8UyzpH*@+Cd#vG{h<| zt=hW{9Ke#bggM8{yhnzVom7`EjlUd1TdL~MfE^7<*i5{G|;63VjdW)Y!h*@EceVSV<`n~qv4W=I|^c=vH19V+e$)b-tw6AaYhhCC`)PX7{&mZn``^ZU{@v6l2E2Dg8qqLw^EIG^QS>f9Bt5@Pa5eY#Z zJ`RTYcpDtpf9W*#NA%k76G_RB5%elp3-jQkk22zy1=3tARLt;Ut>6E83R*!rv6cE87XLwDe2gyI6fuPuWzpB((CLa%jp)?ZdR_fz@3|`u;LJj z-KgDjI_u9mbQLw7VRPwk8Y%1U*H4wr%DPG0Z>LLVF4)~e#mvd_qT@Y9YZYz(s)<=@JaKP2mxD1p z?N;E0I>s|_o~+QgZt|Jm65mB@@W=K0N+gG-B4f@LR2%pm#eDxeP?~1bc-kZV3Cc^Y z_c}*Ega2W%97}o?__-tYp6ZOpYk;S=nr zKOG-fP}sEq=cVWlTp#Z@Vr8`{wI7y!?(Lgvuln#3AOP8zbt~Mi^g|pa4NU{c^dZel z+?8a$7pOluTx;^rsMd%2_i&ZGRw!(ZwUyKTc$%^O1ls8d-BgC^8IXI*+&Hc?cz~@h zjmu}LN5jSdh5b0`-;eVNMEDE9Q#=sbwfTiHj(2i*InP(n6`vIU(8@XvE{yS4#YhG> z*;=|}5xMS)BG@#uV%|!)iK5b=HB``Ylz5ML;OKsaoYtCI30z%^k<4 zSwkq>`W`PW_v-dV;AnMQO~$;`EeNv1OEdB9EkE{}YSgiAzedw{QfgTNeA(j`G94+d zVd215-N@>z8XUU7lh*CV07}$vePDTU-g~#~5CZ#j=DkXb`>M2a)A;n569A2tbJ@+V zJ9pdr{N}U?yHx6*+xjO+XZKGHIO>AX#|{_KO?FKJR1BKRy&TY>pVIBTA_rSs9}k&) zNh`Wttxf=6@-FVAnz5F9xjQ4g?jtNh5N=M6noB_O)(_+q=S0gMXePZp1mBP6GZnWBDsz9_R<#GB^ zO@CTL0>G!0Y~@}OG3{jes%n(y*{ip<^H?sgnRnGh{vJ<=^sn=&#;)~I0PkPbBKc(o z(b!`Lr62ak9Qf%iI_nKvkbtih7Dig>R+1uSDr&}AFl_rvz&yfe4`8VDXQiW$xnycI^2ZNro1864OZGwvn0e z8j6?OWP$ioiOUQlQg=pqoYP!n53+s34r1Iwpf`kfyyz)TpF0|!qfzL5?s+0v1^Fi- z&N`n)HJglVuNjG*E-?RbmOm%sXCIGiiMN}6(8-0=5JcLkVh{T2$>hjCEkvpb(7@wp zzlEgF=iJPEpQYcJ_T${W272%F2Eg!5MulGDM?=l~rK4|PlrOe1`W!7a^tUv;z;WoP zSyOlWyVxT|{iXIsNx)}{5FME(*e^LEW+kE<({u9MuF%9!AdqUEs0??#Bw|KvV`~rC zAj#a%5`ApH)1v^9{zn|hml8~1c(9@Xt-|-Kf`g$NUIe=@Rg=^8I))#cw99rax(w^ssi zWX!F1UtxJsYt9B_R5MXx|E+v&kte}T0v|wya6gaF%*_sY03#e9(@^sG#cxRof9- zu?u98N_760G{bt8O$ztHo*Z->rZ*$f@v8r>EF@z^bfpbY6Ef#-zx&Nw2@Rm%qY({l z%Lk5mC_XT5Yzy^Y)xAZ2mBguyEei4aGk_c^)m#ty&h+9HIFKa`yX$$O$UHE^3}owr zkKFJ?l$##gqm%;pA5TI&`9OR3c<}SL01mmZ$HKir3Mg|BqE=Qw*52Olnz-`d9(T4P z5D|pDSYZy-ad=f0WNa}b{)_M;m(@ZpQtzQ0G(IXxR%@b#dso;5V7)q!eNO7bn0y?H zhSj!5yFd7iUYSN-OW6I542d3NJpY%7<2jaJfcTBt;R51oorIAdB><4VTE-8LggS3l z%{y;K98le&`yyqL6tZm+QRiJsq`sW(pl-7|Z~_VHW^@X<92c@uP5@+&DNl$Xm3&U@ zgWX2Eyay6L5l00tBkRpaJ}R#MA-yYN3-PaaL#uBwe^nkITyT{^r61yNdhfQQ81B`u zdc{xY=l^v|pPO0V(}+*isGNw}Oy)ZB+YrbzdlwQ?9({dc+=9Bvp+Tj2kr?Ipf%XDM zSQ;Tq%RQ5qm0${otYfQ>w)GFVsKMg?O3XNb*~sosMV4(vbX?|^esuQmJMFRhL15FSgV{1K>@EboYS}O$h4spIq)0uUXV-ffu z@r@#u0JJ7fkC|Qqw$aMP1g|hIuf}z3w0y6+bT8!C6)4|HNmIO${xj_t6yq_)yg+a{ z8P)(s{6depfKFR)$6fMe3J4|}s#~;wt6xrbAMA?7*jSVN(2+fC#@hi7p?X_;5l|<)Usv6)#S9C!M zZ&;Bj;4~!&^-Lv)_fyUyEbB=~oGnpU{hn1-)WLGarUL$6E*M4?f!q5_a^}XV-^XDL zXcQy_--;g=2wZ)4H92J~RS2Gm(LeAYXyX7V-7nrR;#cOFhH4nDGP%N_?-c%hLjQ0pC}m@bb+VOH4tB z5J2wTe$OtDc;@(f2X2hB8YGNgE9qfU(zbaOV^+_2N=tVFAA~_OE{L}8%d&7PPi1ve z$kay18Y0~YH#6d#1xRf@TfB_gYN+Ad%~ec6xIQ22Z4R=gan^H5`Lj)lG#jWDH&`D$ z&Wy>!gH^Q8yeNC5O*#l&S9E2a|B{ZKlJqQiPkwye$66h6Y`7I8x3$&3@~uK}WqUgK zE`aRr<8QignP$o*Qb4`!uO)vJ+s@0b(f~qs3D{nJhQaDIv|IPJR%4ucnABKskZjR)U6_koYC8Mf>;o}IR;`d zW--_SCme1`m2nO1F(3ci-t_$C7de>E+&3XHP9OQyRFJ^sJ5)eI1Ay2ut$8J_Dxqh& z%aefkQ2AM?10P?qw_kVMeVwn>D<@6v@rCnY!u8jfFKNm3QAi^A;1QCVI87h|UUA7T zw0ClPy?S%kKDK!9Lkt_SH(Ym2>ND~n7r~VHN3oNivQ|WK?WdgYrM#Y;AYRHcoa+!P zE~V9v6<+(yHb8fId6!jHAb`v@f}@v@bSvvn9d~h@XQKVnhXC{CdSPDd*Z-y8e`Z{? zDfk5rjv6%OF(;}o9EV+WO(j3xOXG!}Jj7VBREN(!;|d=WOvv*O+4gqmo@INtPjRHvge zes!ep-RQq$&{Ld5avcH}MS~aD*x!%U6~N;)Z|G|mnv}AJfIw7jdw<7%JMPN<2LR)I z*zbm`_=@CQPLv9JAQ{8%R0~nSWA{B<(ihaQw?~H4gcGi`0S_PzqWZf_g7H&yp^}4u zxx$x|2%nGWxl{gzheLm3yX#V_HOgmnX7;zz;?3ZO%n8+)iTH@u0c%TSfWHWr20I>d z{fV$wz>>O50$@i0&ewugPudj|PeFK>;IDtoqayMt4t|!tC&ype+c~mH;;8`&wgLpG z9_w93U?RVZw zW30hJTy@Vk&N6YY<3K4_#QCL9wn>W6*}3M6R0KdP6z z!I#7eD06w^OM(^PC$N&9U;r18HIMEbfmYbv$|v&(bU5L_hu#1a(J3{re>7_F6yptf z-=0oPCC!)AYz>EQj>!Nw+o`j0FMn{YIR38Ml>->$5RKQ+Bgw<%pWsLM$Li-S(=N+5 z7=-;UwUyWheJdn2<`3@mT02f9K>2_h$%m~*v_dUB>*PgR~X{7Es>o zzrAwWo+gDKDfA&_=6>XHWvbG8Y@7!P!}l@|;L1SY^IGEJzqk^3>k7ds9yXlOp~Kn& zfRhLPyY+h7Aq)QNp4O#)P}DmZ-75gq+ivFeSgihmei{mm5^Nq;kYU!mgk95e@?s+8JYnGh*va*0!T} z<4Pv`KUl`!rBIQcZ`3awTfpu#%HiKhn+=`AE}*Tb2j|8Ks!DPqKC#6JiF(oUmpmClx>lvaoqD++O@84u~%e6iA3&9gR&!NGV6rwVTG_aCb39 zI`29jUXIA;_+6-tC49#R|Da}j$AHoJypON;<4>f|UCf&lGqeUH2@-{AzL@~VA+*Hy zk7TVGR+5Xel>J`|nFHd<6sMLWm~8LkI7#1s36&QHf(ZW zcZU<@Q#30otC@jc7x_$Fn>bk#N!?|9hLB0bKt#=PCk0p`X=XWe)VyOL z{7tVZaH4o7&f>?RZ`HjFAnUVv$Q9Ohp*aLPFGJt+Z>lrHq_m(S@}lNk7GAaRA;T|^N8heT zsQn$Uf30mXeyH27GteWoWbKjH&7U^X6Yahexsb#8E%@lg~Z2@9A1YbtE56QhRu< z?D`imeJwLO8if~9SUT|66hG!E=)i(S+)Pi~!8Q990$`+3h3LWNN)Y>tkielGAC}}< zDU@oW2(VB3(aoJNCp53+LSg>$zH_XAi-*(j(z6s~>#v4w`FRIQaz6p;D<>tn2bXHF zE9SMSz4a;m6fb2J`)d4!i@kjaMig5~U#DkE!%G7RxU)o8BobglFYvafhybtHU-!p^ z#%X#=!_wkwzsFTDpbM`|o!-%@isjJeR}Jf4r;=4$#Jt?eXMj95HtSe< zX#cY4H(3~GUBDtqz$2?i_lXthqf{oj=-DQz%c>~5syL`kL~eqvxxv(2U)h0`{%c(tsT9!a#VL| zzs^__zxqb$aQ-@IEP2Da53;~0d7a{-rqYY3rFLw^a&YDf++9PO{WtK{#9-rx^!;Zf z!4rBUPM6YSV{9p8i60(*vx$FMlqO0WiVeu3n6L_6%op0Pc$@pTXgwdZPNP=x+Obhh zD%Bz}k4=b`wf!(v3?@VwvkH4K0(GOY4g_sZYAyQihETHxISUt-lM61dxX)rO14W5H zjjQat{CbH{mb%~m!xb^t9^e33 z>a!#&c<77^13X^TL~tS40C31GE)9gy`Kx$id6ZKV;lG@Q&aYsB5!1$~;h^k`p_q_2 z<{7D}Gmo#H7RMHRRn*ZF046#%Tk1gGv%Y%RWJ8W*c3$S3Mw2EwqY$B-bder1)ye0i z3}2{V>22;{vkKL{gOoLD00BC82Rgy*^?j1+$wYQ`HV&>GA^lD%mW5co`<51a@wnkL zIgZKhC`jYQt#$|%H(-RL9N|byC%#0Vw8KNFKcw}uix|hN^)Kx&h6v%iV}4@8kZFp+ zA3GHUnL+3OrYEbl4t(vPa+<8b@qOglnS$ewV(oI^ZTs%T+gK#|DJ~hsDO8N1uM`2= zzL}C*z=se{;E#tzMYooqgFxx`wvcopHjBg^=y1l)7`tNQBu=~Ne*;mN9Y(SGi~n(O zzhYpWZ*A=Hif3O49TIH8&zi3-^2~>Cwm&ckpJL&F*wUy-gD-Moni28@h^umHX<e z+*b8NMb-C?X}XBnzcCto7ZNz}n(}ib4C3Rk(I$O>8WM)|-ypjtOngI`$2BhShTBM> z7TcecMBJGHVS&%JGSSdEEBh%9;gg`N&Gq{I{Edx7(An+$#No1eie_2RFt(?M$1kX~ z4?o+^d2KJC|Io$RIl^hao)PIj`LrLRl1ahnfE1mdokp z$UJsXQ=kLCS6?Ivbm>#>;Vy-7qRThklWTY6iXSx8_m?YsGf3FqOvu%)7ngDL+llQk zy@zaT$b;-`Af9ia^E-gj;MlAQPSwpxDem1(lOj1FGYkEhaB?tl()v}+yAcEbXuMRI zl;V$5xX~YPzQ~_0rdX=OUd_x9pK)^9GgvZU`~=qQC8lJ!C;3Aw&*g;Nij0$H17)}G za>yG+ZN9lgZt$<93AJ@NqSY^}apAJi=vI=_kz7zvE01$mk73{%+S&qg)WGjl0k*3s zlcyavz)${LzLX%w@q#4BBjO~I0T2w^Hno}4L#9eAapJf<9unU@_IV=$2z$%GbGY~l zze@3CIpm7bJrlB;gSlpREu{ZDoQiOM4hTgEP=a>#J*T?1n0R=x2iqTXIUmO8b0=cp zTz1c`DJ!CanHM?Sv3{0@i-VPLooQJ_Ye@`1>zb5@r&VB-UvcFINpHQN+2*doruWIn z!D5TtY>f?x2-but9{@sG{A~sURSKyJ=NF@7?Ow-b{w!E-r~DD*O<$Y4KVv~>!Q$@2 zd>~^DL`K5uPf3*w52&|rVDBFRxTU+6Kf*aHRSPL)ogMYry8HJ?nj5yCos7g#aV@d5 zli-BoA3Z^W6`7I+>YTd?39U|XJCMf(Y3qdjU;tId;o|y4ITE;b| zXBG(@VB5yYg4N$$M61>IkkxnE<0+1{)LGO~v7BZ)+XR}}qFHT$m09JV^JH(+Il=GT z5O=qi-W4Vd&aa-j$WmE&f;h25QXDLP-MGL9G@%h*`B?vSRg`r|`)6}A`PVzG+yEX} zqD-qiUBXU?JlMxWEVW|Pk9i;EEP}ZD^;c@K6@`MApOBkSuouZaxrb`f7@S`-yZKd} z{qzYY1gRyLD;WGd8f9AqCJbYPh|<)zsATa_<_QqvB2TmlayRcyI+ZkM5xnw3_aoyA z`B-+pl{=Q0{rlf(IEpqL*8#(Vw^aqvQ58rkM6iImW8k5(S{byu^FW2mwhI#FTtXgZ z*OGk=?wAb;`hp+${Ew!MwLw9zd9%lkI_C&+z63?Ty(p5e|7{H?B&OXJBiT42;!JZV z_voHO0<{~GOaOmuu$pnLjD$wk0KM@M-QhPFyaGf8$Uod*kE96=)+!6CgycC)tXHU} z$XOFv5-yxl_Vd-^x9$`dW&3I!=?^{^5n=;4elqUFi0~TZZue?}G|v^k5xyWHU2zK@ zk29vNy4cle+O~?*GXBVG3uhi@7GoXwxj(SKO-LJI!UFQ$4E`?9} z(@-gzhxWONZ-U216#H7dhQCQZ8Tg$0!i zYiUvmL2C~X9e(1z$L!dl>dYenZn3@DdfUup%R1eU%xh{;=##>TYr9Sk5;^pd;#mv` zR-m0Ia6{z`UrD#geifO#_o~)j*w7eA4%FqhLW=O$P}`f`k-vDjCx(114q@^j$Ph4J zQlk#0cF9k_uwW)Kn5pbg9oIy6g*L_Tz7Ezyq-1X`e&Oi~P{7oSeT;}OC6IDz%Xxvo zH+qA71jgotI_IQ>VyBvDdH)df$&FQljEbjlb1g(KJPr?_+lmquYrS0Z3u0#)UnHab z@|5y7z}PfAM;c4T>5ljX=KX?DvnIW%M}l)=Ux!lHLh|C}HP1sQM2fMx6R{9&OllQR z=~h)kUSqaWVx@&V*#_VPr`Qe<*8UShi}nOU?m3_UFI8HXi8vauci}!dM)o~*w6A%! zvgkq9)r8j@eIY@$FCs?;2NAWtNzXHO{$L)`n={P*#|l)WaDN4m58(wPHw_=|avHx6 zrO7?*6dM;TEL6Ql<|7Dlk^Mzp)+nlE_rIgAg<3Cu2yPx~y!*)su{}pcrQU;2cr$*6 z&*^yS*IQ-nupR;(J1CJw@lGl~!xVeH`$#L~Wno@^khr@7i{H7fH6G>1ky0zgpyb-c zX+Z7=Z~3FH<-cF1Nc*!ef$qKh{=;0Y-4w5d(04n7A7FUzo$FIuBuvHBbg_`$x$$p} zHF(?X)yMo;bdz1ESq8~9G@Us7!2@U1K{gHgEBKLJk6bPrDMSH#c|Meq^N*rCL-KxY zAn~I7*7|8f)XW9|`hJu-#NL1ip!WGRL#4);Sdq$KEYPAVP;6yLWV+NDo@fxQ3D+T?G$~> z(8S)DjfsaHkY?iy^yb&SD$Hk&2IhJ~JSutAi=m7AkqO|CK44ju25=TAvS`Ywd-yEd!$uC*nJ$@ogfmz4M+kDMZpJ{|L)ECykf!5-s>CO0*IZtU7- zLUb+T+M?wS4EW`d95;Bgle$ps&|X96T=Z{M%i{7yG7v@n^*cr8*{^Tp)eNWu(Cxia zsIszANr*-(!_S!qwqxng9L?*^izzKBr+*m#EXlLLXMH?2%DZ}Gq*P)I;;w)iyE*1s z>~iH?o5|3S85j@fFP)D1$@rP>HeD8Rc#b$xNd6C$^T&`6->DvT5IoAcI3hqWK^SVx z?JJ1sY4f_7aIX(q0(@a{@@#Cb|t7 zk}ZBY9`h@FRR6moZTp6LF$FVQUWbok%(K63%*fm6!DN5iMr9$Ak^t3cRi)5VaPO4& zr{7iLE5-_$k^}w)lep#Nd}$+;ujsK9=*1f>7a>*N7r}p~Mwf=mLUls$)wKE%dh`L7m3SnnSAds`#JM+ z`_i#DQtoF9p%f8t{@88uXq)~I?RMqFW!2m%>L+LWqUNQ>n&s)m>+&$th`;3Hs@eF# z5)XvI&j`AOh(BH@-4ne}xZL}5I{E2|K5w*2z8xO=y#2ORK0~x0Ey9VNs3*OLe>On> zQwCP(9#`8`gy*|72bQUD3f=xEW8o+PGWez+MtD6aGx;T>Hdv(1a9j%lrlzPqQdRf= zZ3$u%_rj)J5~hi{UyfwT<)tJh zLb#2yH5v1TkNYQmNxKi{G6GsVS_Q|sv_NH+$>MBxEuG6Xp$8c7bhp&5#IQm(QEO_M z3PTIA6N6>=XOELG&V{Y9JUr_+fbNg6M|zJ5Ypoz%3p1c9%}D0@djKtz0msS@D-G9V zd|5en{E;%zvs`(Kcu}y&I|aR9Qvsz;)e7SbQxa=P&S4PC`or)U$0;g%;~D7}Fh|#(636|!EMROaf^?h622j}nOC&@Oz>y(Eu+abX zPnkM9Ba0XjOT&7o#$u=ggRrmd)4<=q$1#&jLxU0FUr)&LegeYK0*RkUd3aeseq^SWZ6ePM#9r)UX_Q8)+7B zKJ7$_g@Rl3rsu2}uO;DxgC1|wf31AKF(`I=daB)Dj6#MS!ggpr&*f&iv|szj-MFxc zA;J{u4+f@}@`uCoNWb^)p$I5xDW9Yu=MPQgJMq&0bs?7Don7;>)R;eQa$HI+DG_Ek zfOlao%xL#a#{_Kk!#tHA0VgK|u2NAyntFn#@Wu>j+tIaU=)7+M{{!?@CIC=lJSxz7 zKrcA2J|sk4-i_<}G?`63FwS{T_pFp3TPKh*>`xE|S2PQ@np4A4FQoF+9&j_(ccoF= zRo`@J!6>jRc9r`u?$MWO0~c>7 z_!`H5%o&mV=Jp2*P19-G0sYM3#gQRNYr@5e%)Lbxb?AUU)o+8~zNMd4N;onXe89h6 zblVXvV653Tj*HFb5|OYA$g8l6fq`-zHklj|gXgv~}t8E@&8NAP9|TCfAAGY`Dpcxc=BVzMb!+seB= zrhoMHqXI?9FcfY#a{HDUgR>h4{~BOuxC!|C(0VeakR2*)6@}qs7WqC3kjqw$~@T)Jw%>Q2Jlq znEH2@&iY2Q@3p@1*u~Q|s0b`D@;elA)Kr~dUnL!gz6y!P#4~Vtej+zPrWDl#rT`YE zx}Kh%X|=}%q{}X%GNcN+0$XFdM&04h{~= zyMJlD^z`)9G&D5q!33{E+LAlp1kZ@nlQ?6JC6zJMQuwz0`roly);6AYz)jebHh1i;M^(Zm%qOs&0<_nkeUfq)9 z$6ap6FbcmhsghX!?o3!ln;WKn_Ck5iV+9rLYmFAA1gXfCU3%&^^>M>H_ye>E5LWMu zW<-j{#x$^};`&G;LNE~HReU+!GKKN)r{b4QN%W?=G>`rEW~+eqp(=izPh550rAjLP z?X2&kE_!QXEg|+^RYcv`^YKh3#(<@a^X(6q=!_V0gSiKbe`b(Ts2oCwLIq~_Fq{xJ za5=Qc;?vMjrd_CIl{q4L7nlJhCyqzv_}BS;9clv(4HRiX&xt;~4o^Hj{!U0j%q+E? b&_DKs^Z)l<;XXj|?iex$_|QcESB&;Q(k;u` diff --git a/frontend/appflowy_web_app/src-tauri/icons/icon.png b/frontend/appflowy_web_app/src-tauri/icons/icon.png deleted file mode 100644 index 7cc3853d67255311995dbac598d7edc6ab1e00fb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 41650 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 diff --git a/frontend/appflowy_web_app/src-tauri/rust-toolchain.toml b/frontend/appflowy_web_app/src-tauri/rust-toolchain.toml deleted file mode 100644 index 6f14058b2e..0000000000 --- a/frontend/appflowy_web_app/src-tauri/rust-toolchain.toml +++ /dev/null @@ -1,2 +0,0 @@ -[toolchain] -channel = "1.77.2" diff --git a/frontend/appflowy_web_app/src-tauri/rustfmt.toml b/frontend/appflowy_web_app/src-tauri/rustfmt.toml deleted file mode 100644 index 5cb0d67ee5..0000000000 --- a/frontend/appflowy_web_app/src-tauri/rustfmt.toml +++ /dev/null @@ -1,12 +0,0 @@ -# https://rust-lang.github.io/rustfmt/?version=master&search= -max_width = 100 -tab_spaces = 2 -newline_style = "Auto" -match_block_trailing_comma = true -use_field_init_shorthand = true -use_try_shorthand = true -reorder_imports = true -reorder_modules = true -remove_nested_parens = true -merge_derives = true -edition = "2021" \ No newline at end of file diff --git a/frontend/appflowy_web_app/src-tauri/src/init.rs b/frontend/appflowy_web_app/src-tauri/src/init.rs deleted file mode 100644 index 7af31af362..0000000000 --- a/frontend/appflowy_web_app/src-tauri/src/init.rs +++ /dev/null @@ -1,79 +0,0 @@ -use dotenv::dotenv; -use flowy_core::config::AppFlowyCoreConfig; -use flowy_core::{AppFlowyCore, DEFAULT_NAME}; -use lib_dispatch::runtime::AFPluginRuntime; -use std::rc::Rc; -use std::sync::{Arc, Mutex}; - -pub fn read_env() { - dotenv().ok(); - - let env = if cfg!(debug_assertions) { - include_str!("../env.development") - } else { - include_str!("../env.production") - }; - - for line in env.lines() { - if let Some((key, value)) = line.split_once('=') { - // Check if the environment variable is not already set in the system - let current_value = std::env::var(key).unwrap_or_default(); - if current_value.is_empty() { - std::env::set_var(key, value); - } - } - } -} - -pub fn init_appflowy_core() -> MutexAppFlowyCore { - let config_json = include_str!("../tauri.conf.json"); - let config: tauri_utils::config::Config = serde_json::from_str(config_json).unwrap(); - - let app_version = config - .package - .version - .clone() - .map(|v| v.to_string()) - .unwrap_or_else(|| "0.5.8".to_string()); - let app_version = - semver::Version::parse(&app_version).unwrap_or_else(|_| semver::Version::new(0, 5, 8)); - let mut data_path = tauri::api::path::app_local_data_dir(&config).unwrap(); - if cfg!(debug_assertions) { - data_path.push("data_dev"); - } else { - data_path.push("data"); - } - - let custom_application_path = data_path.to_str().unwrap().to_string(); - let application_path = data_path.to_str().unwrap().to_string(); - let device_id = uuid::Uuid::new_v4().to_string(); - - read_env(); - std::env::set_var("RUST_LOG", "trace"); - - let config = AppFlowyCoreConfig::new( - app_version, - custom_application_path, - application_path, - device_id, - "tauri".to_string(), - DEFAULT_NAME.to_string(), - ) - .log_filter("trace", vec!["appflowy_tauri".to_string()]); - - let runtime = Arc::new(AFPluginRuntime::new().unwrap()); - let cloned_runtime = runtime.clone(); - runtime.block_on(async move { - MutexAppFlowyCore::new(AppFlowyCore::new(config, cloned_runtime, None).await) - }) -} - -pub struct MutexAppFlowyCore(pub Arc>); - -impl MutexAppFlowyCore { - pub(crate) fn new(appflowy_core: AppFlowyCore) -> Self { - Self(Arc::new(Mutex::new(appflowy_core))) - } -} -unsafe impl Sync for MutexAppFlowyCore {} -unsafe impl Send for MutexAppFlowyCore {} diff --git a/frontend/appflowy_web_app/src-tauri/src/main.rs b/frontend/appflowy_web_app/src-tauri/src/main.rs deleted file mode 100644 index 781ce55098..0000000000 --- a/frontend/appflowy_web_app/src-tauri/src/main.rs +++ /dev/null @@ -1,71 +0,0 @@ -#![cfg_attr( - all(not(debug_assertions), target_os = "windows"), - windows_subsystem = "windows" -)] - -#[allow(dead_code)] -pub const DEEP_LINK_SCHEME: &str = "appflowy-flutter"; -pub const OPEN_DEEP_LINK: &str = "open_deep_link"; - -mod init; -mod notification; -mod request; - -use flowy_notification::{register_notification_sender, unregister_all_notification_sender}; -use init::*; -use notification::*; -use request::*; -use tauri::Manager; -extern crate dotenv; - -fn main() { - tauri_plugin_deep_link::prepare(DEEP_LINK_SCHEME); - - let flowy_core = init_appflowy_core(); - tauri::Builder::default() - .invoke_handler(tauri::generate_handler![invoke_request]) - .manage(flowy_core) - .on_window_event(|_window_event| {}) - .on_menu_event(|_menu| {}) - .on_page_load(|window, _payload| { - let app_handler = window.app_handle(); - // Make sure hot reload won't register the notification sender twice - unregister_all_notification_sender(); - register_notification_sender(TSNotificationSender::new(app_handler.clone())); - // tauri::async_runtime::spawn(async move {}); - - window.listen_global(AF_EVENT, move |event| { - on_event(app_handler.clone(), event); - }); - }) - .setup(|_app| { - let splashscreen_window = _app.get_window("splashscreen").unwrap(); - let window = _app.get_window("main").unwrap(); - let handle = _app.handle(); - - // we perform the initialization code on a new task so the app doesn't freeze - tauri::async_runtime::spawn(async move { - // initialize your app here instead of sleeping :) - std::thread::sleep(std::time::Duration::from_secs(2)); - - // After it's done, close the splashscreen and display the main window - splashscreen_window.close().unwrap(); - window.show().unwrap(); - // If you need macOS support this must be called in .setup() ! - // Otherwise this could be called right after prepare() but then you don't have access to tauri APIs - // On macOS You still have to install a .app bundle you got from tauri build --debug for this to work! - tauri_plugin_deep_link::register( - DEEP_LINK_SCHEME, - move |request| { - dbg!(&request); - handle.emit_all(OPEN_DEEP_LINK, request).unwrap(); - }, - ) - .unwrap(/* If listening to the scheme is optional for your app, you don't want to unwrap here. */); - }); - - Ok(()) - }) - .run(tauri::generate_context!()) - .expect("error while running tauri application"); -} diff --git a/frontend/appflowy_web_app/src-tauri/src/notification.rs b/frontend/appflowy_web_app/src-tauri/src/notification.rs deleted file mode 100644 index b42541edec..0000000000 --- a/frontend/appflowy_web_app/src-tauri/src/notification.rs +++ /dev/null @@ -1,35 +0,0 @@ -use flowy_notification::entities::SubscribeObject; -use flowy_notification::NotificationSender; -use serde::Serialize; -use tauri::{AppHandle, Event, Manager, Wry}; - -#[allow(dead_code)] -pub const AF_EVENT: &str = "af-event"; -pub const AF_NOTIFICATION: &str = "af-notification"; - -#[tracing::instrument(level = "trace")] -pub fn on_event(app_handler: AppHandle, event: Event) {} - -#[allow(dead_code)] -pub fn send_notification(app_handler: AppHandle, payload: P) { - app_handler.emit_all(AF_NOTIFICATION, payload).unwrap(); -} - -pub struct TSNotificationSender { - handler: AppHandle, -} - -impl TSNotificationSender { - pub fn new(handler: AppHandle) -> Self { - Self { handler } - } -} - -impl NotificationSender for TSNotificationSender { - fn send_subject(&self, subject: SubscribeObject) -> Result<(), String> { - self - .handler - .emit_all(AF_NOTIFICATION, subject) - .map_err(|e| format!("{:?}", e)) - } -} diff --git a/frontend/appflowy_web_app/src-tauri/src/request.rs b/frontend/appflowy_web_app/src-tauri/src/request.rs deleted file mode 100644 index ff69a438c9..0000000000 --- a/frontend/appflowy_web_app/src-tauri/src/request.rs +++ /dev/null @@ -1,45 +0,0 @@ -use crate::init::MutexAppFlowyCore; -use lib_dispatch::prelude::{ - AFPluginDispatcher, AFPluginEventResponse, AFPluginRequest, StatusCode, -}; -use tauri::{AppHandle, Manager, State, Wry}; - -#[derive(Clone, Debug, serde::Deserialize)] -pub struct AFTauriRequest { - ty: String, - payload: Vec, -} - -impl std::convert::From for AFPluginRequest { - fn from(event: AFTauriRequest) -> Self { - AFPluginRequest::new(event.ty).payload(event.payload) - } -} - -#[derive(Clone, serde::Serialize)] -pub struct AFTauriResponse { - code: StatusCode, - payload: Vec, -} - -impl std::convert::From for AFTauriResponse { - fn from(response: AFPluginEventResponse) -> Self { - Self { - code: response.status_code, - payload: response.payload.to_vec(), - } - } -} - -// Learn more about Tauri commands at https://tauri.app/v1/guides/features/command -#[tauri::command] -pub async fn invoke_request( - request: AFTauriRequest, - app_handler: AppHandle, -) -> AFTauriResponse { - let request: AFPluginRequest = request.into(); - let state: State = app_handler.state(); - let dispatcher = state.0.lock().unwrap().dispatcher(); - let response = AFPluginDispatcher::sync_send(dispatcher, request); - response.into() -} diff --git a/frontend/appflowy_web_app/src-tauri/tauri.conf.json b/frontend/appflowy_web_app/src-tauri/tauri.conf.json deleted file mode 100644 index ea11f47def..0000000000 --- a/frontend/appflowy_web_app/src-tauri/tauri.conf.json +++ /dev/null @@ -1,113 +0,0 @@ -{ - "build": { - "beforeBuildCommand": "npm run build:tauri", - "beforeDevCommand": "npm run dev:tauri", - "devPath": "http://localhost:5173", - "distDir": "../dist", - "withGlobalTauri": false - }, - "package": { - "productName": "AppFlowy", - "version": "0.0.1" - }, - "tauri": { - "allowlist": { - "all": false, - "shell": { - "all": false, - "open": true - }, - "fs": { - "all": true, - "scope": [ - "$APPLOCALDATA/**" - ], - "readFile": true, - "writeFile": true, - "readDir": true, - "copyFile": true, - "createDir": true, - "removeDir": true, - "removeFile": true, - "renameFile": true, - "exists": true - }, - "clipboard": { - "all": true, - "writeText": true, - "readText": true - }, - "dialog": { - "all": true, - "ask": true, - "confirm": true, - "message": true, - "open": true, - "save": true - } - }, - "bundle": { - "active": true, - "category": "DeveloperTool", - "copyright": "", - "deb": { - "depends": [] - }, - "icon": [ - "icons/32x32.png", - "icons/128x128.png", - "icons/128x128@2x.png", - "icons/icon.icns", - "icons/icon.ico" - ], - "externalBin": [], - "identifier": "com.appflowy.tauri", - "longDescription": "", - "macOS": { - "entitlements": null, - "exceptionDomain": "", - "frameworks": [], - "providerShortName": null, - "signingIdentity": null, - "minimumSystemVersion": "10.15.0" - }, - "resources": [], - "shortDescription": "", - "targets": "all", - "windows": { - "certificateThumbprint": null, - "digestAlgorithm": "sha256", - "timestampUrl": "" - } - }, - "security": { - "csp": null - }, - "updater": { - "active": false - }, - "windows": [ - { - "fileDropEnabled": false, - "fullscreen": false, - "height": 800, - "resizable": true, - "title": "AppFlowy", - "width": 1200, - "minWidth": 800, - "minHeight": 600, - "visible": false, - "label": "main" - }, - { - "height": 300, - "width": 549, - "decorations": false, - "url": "launch_splash.jpg", - "label": "splashscreen", - "center": true, - "visible": true - } - ] - } -} diff --git a/frontend/appflowy_web_app/src/@types/i18next.d.ts b/frontend/appflowy_web_app/src/@types/i18next.d.ts deleted file mode 100644 index 6adbb4a512..0000000000 --- a/frontend/appflowy_web_app/src/@types/i18next.d.ts +++ /dev/null @@ -1,8 +0,0 @@ -import resources from './resources'; - -declare module 'i18next' { - interface CustomTypeOptions { - defaultNS: 'translation'; - resources: typeof resources; - } -} diff --git a/frontend/appflowy_web_app/src/@types/resources.ts b/frontend/appflowy_web_app/src/@types/resources.ts deleted file mode 100644 index 6bd90364e0..0000000000 --- a/frontend/appflowy_web_app/src/@types/resources.ts +++ /dev/null @@ -1,7 +0,0 @@ -import translation from './translations/en.json'; - -const resources = { - translation, -} as const; - -export default resources; diff --git a/frontend/appflowy_web_app/src/application/comment.type.ts b/frontend/appflowy_web_app/src/application/comment.type.ts deleted file mode 100644 index 0d5bdecf53..0000000000 --- a/frontend/appflowy_web_app/src/application/comment.type.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { AFWebUser } from '@/application/types'; - -export interface GlobalComment { - commentId: string; - user: AFWebUser | null; - content: string; - createdAt: string; - lastUpdatedAt: string; - replyCommentId: string | null; - isDeleted: boolean; - canDeleted: boolean; -} - -export interface Reaction { - reactionType: string; - reactUsers: AFWebUser[]; - commentId: string; -} diff --git a/frontend/appflowy_web_app/src/application/constants.ts b/frontend/appflowy_web_app/src/application/constants.ts deleted file mode 100644 index 5880ff6326..0000000000 --- a/frontend/appflowy_web_app/src/application/constants.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const databasePrefix = 'af_database'; - -export const HEADER_HEIGHT = 48; \ No newline at end of file diff --git a/frontend/appflowy_web_app/src/application/database-yjs/__tests__/filter.test.ts b/frontend/appflowy_web_app/src/application/database-yjs/__tests__/filter.test.ts deleted file mode 100644 index b07dc9beac..0000000000 --- a/frontend/appflowy_web_app/src/application/database-yjs/__tests__/filter.test.ts +++ /dev/null @@ -1,672 +0,0 @@ -import { - NumberFilterCondition, - TextFilterCondition, - CheckboxFilterCondition, - ChecklistFilterCondition, - SelectOptionFilterCondition, - Row, -} from '@/application/database-yjs'; -import { withTestingData } from '@/application/database-yjs/__tests__/withTestingData'; -import { - withCheckboxFilter, - withChecklistFilter, - withDateTimeFilter, - withMultiSelectOptionFilter, - withNumberFilter, - withRichTextFilter, - withSingleSelectOptionFilter, - withUrlFilter, -} from '@/application/database-yjs/__tests__/withTestingFilters'; -import { withTestingRows } from '@/application/database-yjs/__tests__/withTestingRows'; -import { RowId, YDoc } from '@/application/types'; -import { - textFilterCheck, - numberFilterCheck, - checkboxFilterCheck, - checklistFilterCheck, - selectOptionFilterCheck, - filterBy, -} from '../filter'; -import { expect } from '@jest/globals'; -import * as Y from 'yjs'; - -describe('Text filter check', () => { - const text = 'Hello, world!'; - it('should return true for TextIs condition', () => { - const condition = TextFilterCondition.TextIs; - const content = 'Hello, world!'; - - const result = textFilterCheck(text, content, condition); - - expect(result).toBe(true); - }); - - it('should return false for TextIs condition', () => { - const condition = TextFilterCondition.TextIs; - const content = 'Hello, world'; - - const result = textFilterCheck(text, content, condition); - - expect(result).toBe(false); - }); - - it('should return true for TextIsNot condition', () => { - const condition = TextFilterCondition.TextIsNot; - const content = 'Hello, world'; - - const result = textFilterCheck(text, content, condition); - - expect(result).toBe(true); - }); - - it('should return false for TextIsNot condition', () => { - const condition = TextFilterCondition.TextIsNot; - const content = 'Hello, world!'; - - const result = textFilterCheck(text, content, condition); - - expect(result).toBe(false); - }); - - it('should return true for TextContains condition', () => { - const condition = TextFilterCondition.TextContains; - const content = 'world'; - - const result = textFilterCheck(text, content, condition); - - expect(result).toBe(true); - }); - - it('should return false for TextContains condition', () => { - const condition = TextFilterCondition.TextContains; - const content = 'planet'; - - const result = textFilterCheck(text, content, condition); - - expect(result).toBe(false); - }); - - it('should return true for TextDoesNotContain condition', () => { - const condition = TextFilterCondition.TextDoesNotContain; - const content = 'planet'; - - const result = textFilterCheck(text, content, condition); - - expect(result).toBe(true); - }); - - it('should return false for TextDoesNotContain condition', () => { - const condition = TextFilterCondition.TextDoesNotContain; - const content = 'world'; - - const result = textFilterCheck(text, content, condition); - - expect(result).toBe(false); - }); - - it('should return true for TextIsEmpty condition', () => { - const condition = TextFilterCondition.TextIsEmpty; - const text = ''; - - const result = textFilterCheck(text, '', condition); - - expect(result).toBe(true); - }); - - it('should return false for TextIsEmpty condition', () => { - const condition = TextFilterCondition.TextIsEmpty; - const text = 'Hello, world!'; - - const result = textFilterCheck(text, '', condition); - - expect(result).toBe(false); - }); - - it('should return true for TextIsNotEmpty condition', () => { - const condition = TextFilterCondition.TextIsNotEmpty; - const text = 'Hello, world!'; - - const result = textFilterCheck(text, '', condition); - - expect(result).toBe(true); - }); - - it('should return false for TextIsNotEmpty condition', () => { - const condition = TextFilterCondition.TextIsNotEmpty; - const text = ''; - - const result = textFilterCheck(text, '', condition); - - expect(result).toBe(false); - }); - - it('should return false for unknown condition', () => { - const condition = 42; - const content = 'Hello, world!'; - - const result = textFilterCheck(text, content, condition); - - expect(result).toBe(false); - }); -}); - -describe('Number filter check', () => { - const num = '42'; - it('should return true for Equal condition', () => { - const condition = NumberFilterCondition.Equal; - const content = '42'; - - const result = numberFilterCheck(num, content, condition); - - expect(result).toBe(true); - }); - - it('should return false for Equal condition', () => { - const condition = NumberFilterCondition.Equal; - const content = '43'; - - const result = numberFilterCheck(num, content, condition); - - expect(result).toBe(false); - }); - - it('should return true for NotEqual condition', () => { - const condition = NumberFilterCondition.NotEqual; - const content = '43'; - - const result = numberFilterCheck(num, content, condition); - - expect(result).toBe(true); - }); - - it('should return false for NotEqual condition', () => { - const condition = NumberFilterCondition.NotEqual; - const content = '42'; - - const result = numberFilterCheck(num, content, condition); - - expect(result).toBe(false); - }); - - it('should return true for GreaterThan condition', () => { - const condition = NumberFilterCondition.GreaterThan; - const content = '41'; - - const result = numberFilterCheck(num, content, condition); - - expect(result).toBe(true); - }); - - it('should return false for GreaterThan condition', () => { - const condition = NumberFilterCondition.GreaterThan; - const content = '42'; - - const result = numberFilterCheck(num, content, condition); - - expect(result).toBe(false); - }); - - it('should return true for GreaterThanOrEqualTo condition', () => { - const condition = NumberFilterCondition.GreaterThanOrEqualTo; - const content = '42'; - - const result = numberFilterCheck(num, content, condition); - - expect(result).toBe(true); - }); - - it('should return false for GreaterThanOrEqualTo condition', () => { - const condition = NumberFilterCondition.GreaterThanOrEqualTo; - const content = '43'; - - const result = numberFilterCheck(num, content, condition); - - expect(result).toBe(false); - }); - - it('should return true for LessThan condition', () => { - const condition = NumberFilterCondition.LessThan; - const content = '43'; - - const result = numberFilterCheck(num, content, condition); - - expect(result).toBe(true); - }); - - it('should return false for LessThan condition', () => { - const condition = NumberFilterCondition.LessThan; - const content = '42'; - - const result = numberFilterCheck(num, content, condition); - - expect(result).toBe(false); - }); - - it('should return true for LessThanOrEqualTo condition', () => { - const condition = NumberFilterCondition.LessThanOrEqualTo; - const content = '42'; - - const result = numberFilterCheck(num, content, condition); - - expect(result).toBe(true); - }); - - it('should return false for LessThanOrEqualTo condition', () => { - const condition = NumberFilterCondition.LessThanOrEqualTo; - const content = '41'; - - const result = numberFilterCheck(num, content, condition); - - expect(result).toBe(false); - }); - - it('should return true for NumberIsEmpty condition', () => { - const condition = NumberFilterCondition.NumberIsEmpty; - - const result = numberFilterCheck('', '', condition); - - expect(result).toBe(true); - }); - - it('should return false for NumberIsEmpty condition', () => { - const condition = NumberFilterCondition.NumberIsEmpty; - const num = '42'; - - const result = numberFilterCheck(num, '', condition); - - expect(result).toBe(false); - }); - - it('should return true for NumberIsNotEmpty condition', () => { - const condition = NumberFilterCondition.NumberIsNotEmpty; - const num = '42'; - - const result = numberFilterCheck(num, '', condition); - - expect(result).toBe(true); - }); - - it('should return false for NumberIsNotEmpty condition', () => { - const condition = NumberFilterCondition.NumberIsNotEmpty; - const num = ''; - - const result = numberFilterCheck(num, '', condition); - - expect(result).toBe(false); - }); - - it('should return false for unknown condition', () => { - const condition = 42; - const content = '42'; - - const result = numberFilterCheck(num, content, condition); - - expect(result).toBe(false); - }); -}); - -describe('Checkbox filter check', () => { - it('should return true for IsChecked condition', () => { - const condition = CheckboxFilterCondition.IsChecked; - const data = 'Yes'; - - const result = checkboxFilterCheck(data, condition); - - expect(result).toBe(true); - }); - - it('should return false for IsChecked condition', () => { - const condition = CheckboxFilterCondition.IsChecked; - const data = 'No'; - - const result = checkboxFilterCheck(data, condition); - - expect(result).toBe(false); - }); - - it('should return true for IsUnChecked condition', () => { - const condition = CheckboxFilterCondition.IsUnChecked; - const data = 'No'; - - const result = checkboxFilterCheck(data, condition); - - expect(result).toBe(true); - }); - - it('should return false for IsUnChecked condition', () => { - const condition = CheckboxFilterCondition.IsUnChecked; - const data = 'Yes'; - - const result = checkboxFilterCheck(data, condition); - - expect(result).toBe(false); - }); - - it('should return false for unknown condition', () => { - const condition = 42; - const data = 'Yes'; - - const result = checkboxFilterCheck(data, condition); - - expect(result).toBe(false); - }); -}); - -describe('Checklist filter check', () => { - it('should return true for IsComplete condition', () => { - const condition = ChecklistFilterCondition.IsComplete; - const data = JSON.stringify({ - options: [ - { id: '1', name: 'Option 1' }, - { id: '2', name: 'Option 2' }, - ], - selected_option_ids: ['1', '2'], - }); - - const result = checklistFilterCheck(data, '', condition); - - expect(result).toBe(true); - }); - - it('should return false for IsComplete condition', () => { - const condition = ChecklistFilterCondition.IsComplete; - const data = JSON.stringify({ - options: [ - { id: '1', name: 'Option 1' }, - { id: '2', name: 'Option 2' }, - ], - selected_option_ids: ['1'], - }); - - const result = checklistFilterCheck(data, '', condition); - - expect(result).toBe(false); - }); - - it('should return false for unknown condition', () => { - const condition = 42; - const data = JSON.stringify({ - options: [ - { id: '1', name: 'Option 1' }, - { id: '2', name: 'Option 2' }, - ], - selected_option_ids: ['1', '2'], - }); - - const result = checklistFilterCheck(data, '', condition); - - expect(result).toBe(false); - }); -}); - -describe('SelectOption filter check', () => { - it('should return true for OptionIs condition', () => { - const condition = SelectOptionFilterCondition.OptionIs; - const content = '1'; - const data = '1,2'; - - const result = selectOptionFilterCheck(data, content, condition); - - expect(result).toBe(true); - }); - - it('should return false for OptionIs condition', () => { - const condition = SelectOptionFilterCondition.OptionIs; - const content = '3'; - const data = '1,2'; - - const result = selectOptionFilterCheck(data, content, condition); - - expect(result).toBe(false); - }); - - it('should return true for OptionIsNot condition', () => { - const condition = SelectOptionFilterCondition.OptionIsNot; - const content = '3'; - const data = '1,2'; - - const result = selectOptionFilterCheck(data, content, condition); - - expect(result).toBe(true); - }); - - it('should return false for OptionIsNot condition', () => { - const condition = SelectOptionFilterCondition.OptionIsNot; - const content = '1'; - const data = '1,2'; - - const result = selectOptionFilterCheck(data, content, condition); - - expect(result).toBe(false); - }); - - it('should return true for OptionContains condition', () => { - const condition = SelectOptionFilterCondition.OptionContains; - const content = '1,3'; - const data = '1,2,3'; - - const result = selectOptionFilterCheck(data, content, condition); - - expect(result).toBe(true); - }); - - it('should return false for OptionContains condition', () => { - const condition = SelectOptionFilterCondition.OptionContains; - const content = '4'; - const data = '1,2,3'; - - const result = selectOptionFilterCheck(data, content, condition); - - expect(result).toBe(false); - }); - - it('should return true for OptionDoesNotContain condition', () => { - const condition = SelectOptionFilterCondition.OptionDoesNotContain; - const content = '4,5'; - const data = '1,2,3'; - - const result = selectOptionFilterCheck(data, content, condition); - - expect(result).toBe(true); - }); - - it('should return false for OptionDoesNotContain condition', () => { - const condition = SelectOptionFilterCondition.OptionDoesNotContain; - const content = '1,3'; - const data = '1,2,3'; - - const result = selectOptionFilterCheck(data, content, condition); - - expect(result).toBe(false); - }); - - it('should return true for OptionIsEmpty condition', () => { - const condition = SelectOptionFilterCondition.OptionIsEmpty; - const data = ''; - - const result = selectOptionFilterCheck(data, '', condition); - - expect(result).toBe(true); - }); - - it('should return false for OptionIsEmpty condition', () => { - const condition = SelectOptionFilterCondition.OptionIsEmpty; - const data = '1,2'; - - const result = selectOptionFilterCheck(data, '', condition); - - expect(result).toBe(false); - }); - - it('should return true for OptionIsNotEmpty condition', () => { - const condition = SelectOptionFilterCondition.OptionIsNotEmpty; - const data = '1,2'; - - const result = selectOptionFilterCheck(data, '', condition); - - expect(result).toBe(true); - }); - - it('should return false for OptionIsNotEmpty condition', () => { - const condition = SelectOptionFilterCondition.OptionIsNotEmpty; - const data = ''; - - const result = selectOptionFilterCheck(data, '', condition); - - expect(result).toBe(false); - }); - - it('should return false for unknown condition', () => { - const condition = 42; - const content = '1'; - const data = '1,2'; - - const result = selectOptionFilterCheck(data, content, condition); - - expect(result).toBe(false); - }); -}); - -describe('Database filterBy', () => { - let rows: Row[]; - - beforeEach(() => { - rows = withTestingRows(); - }); - - it('should return all rows for empty filter', () => { - const { filters, fields, rowMap } = withTestingData(); - const result = filterBy(rows, filters, fields, rowMap) - .map((row) => row.id) - .join(','); - expect(result).toBe('1,2,3,4,5,6,7,8,9,10'); - }); - - it('should return all rows for empty rowMap', () => { - const { filters, fields } = withTestingData(); - const rowMap: Record = {}; - const result = filterBy(rows, filters, fields, rowMap) - .map((row) => row.id) - .join(','); - expect(result).toBe('1,2,3,4,5,6,7,8,9,10'); - }); - - it('should return rows that match text filter', () => { - const { filters, fields, rowMap } = withTestingData(); - const filter = withRichTextFilter(); - filters.push([filter]); - const result = filterBy(rows, filters, fields, rowMap) - .map((row) => row.id) - .join(','); - expect(result).toBe('1,5'); - }); - - it('should return rows that match number filter', () => { - const { filters, fields, rowMap } = withTestingData(); - const filter = withNumberFilter(); - filters.push([filter]); - const result = filterBy(rows, filters, fields, rowMap) - .map((row) => row.id) - .join(','); - expect(result).toBe('4,5,6,7,8,9,10'); - }); - - it('should return rows that match checkbox filter', () => { - const { filters, fields, rowMap } = withTestingData(); - const filter = withCheckboxFilter(); - filters.push([filter]); - const result = filterBy(rows, filters, fields, rowMap) - .map((row) => row.id) - .join(','); - expect(result).toBe('2,4,6,8,10'); - }); - - it('should return rows that match checklist filter', () => { - const { filters, fields, rowMap } = withTestingData(); - const filter = withChecklistFilter(); - filters.push([filter]); - const result = filterBy(rows, filters, fields, rowMap) - .map((row) => row.id) - .join(','); - expect(result).toBe('1,2,4,5,6,7,8,10'); - }); - - it('should return rows that match multiple filters', () => { - const { filters, fields, rowMap } = withTestingData(); - const filter1 = withRichTextFilter(); - const filter2 = withNumberFilter(); - filters.push([filter1, filter2]); - const result = filterBy(rows, filters, fields, rowMap) - .map((row) => row.id) - .join(','); - expect(result).toBe('5'); - }); - - it('should return rows that match url filter', () => { - const { filters, fields, rowMap } = withTestingData(); - const filter = withUrlFilter(); - filters.push([filter]); - const result = filterBy(rows, filters, fields, rowMap) - .map((row) => row.id) - .join(','); - expect(result).toBe('4'); - }); - - it('should return rows that match date filter', () => { - const { filters, fields, rowMap } = withTestingData(); - const filter = withDateTimeFilter(); - filters.push([filter]); - const result = filterBy(rows, filters, fields, rowMap) - .map((row) => row.id) - .join(','); - expect(result).toBe('1,2,3,4,5,6,7,8,9,10'); - }); - - it('should return rows that match select option filter', () => { - const { filters, fields, rowMap } = withTestingData(); - const filter = withSingleSelectOptionFilter(); - filters.push([filter]); - const result = filterBy(rows, filters, fields, rowMap) - .map((row) => row.id) - .join(','); - expect(result).toBe('2,5,8'); - }); - - it('should return rows that match multi select option filter', () => { - const { filters, fields, rowMap } = withTestingData(); - const filter = withMultiSelectOptionFilter(); - filters.push([filter]); - const result = filterBy(rows, filters, fields, rowMap) - .map((row) => row.id) - .join(','); - expect(result).toBe('1,2,3,5,6,7,8,9'); - }); - - it('should return rows that match multiple filters', () => { - const { filters, fields, rowMap } = withTestingData(); - const filter1 = withNumberFilter(); - const filter2 = withChecklistFilter(); - filters.push([filter1, filter2]); - const result = filterBy(rows, filters, fields, rowMap) - .map((row) => row.id) - .join(','); - expect(result).toBe('4,5,6,7,8,10'); - }); - - it('should return empty array for all filters', () => { - const { filters, fields, rowMap } = withTestingData(); - const filter1 = withNumberFilter(); - const filter2 = withChecklistFilter(); - const filter3 = withRichTextFilter(); - const filter4 = withCheckboxFilter(); - const filter5 = withSingleSelectOptionFilter(); - const filter6 = withMultiSelectOptionFilter(); - const filter7 = withUrlFilter(); - const filter8 = withDateTimeFilter(); - filters.push([filter1, filter2, filter3, filter4, filter5, filter6, filter7, filter8]); - const result = filterBy(rows, filters, fields, rowMap) - .map((row) => row.id) - .join(','); - expect(result).toBe(''); - }); -}); diff --git a/frontend/appflowy_web_app/src/application/database-yjs/__tests__/fixtures/filters.json b/frontend/appflowy_web_app/src/application/database-yjs/__tests__/fixtures/filters.json deleted file mode 100644 index eb0688a5de..0000000000 --- a/frontend/appflowy_web_app/src/application/database-yjs/__tests__/fixtures/filters.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "filter_text_field": { - "field_id": "text_field", - "condition": 2, - "content": "w" - }, - "filter_number_field": { - "field_id": "number_field", - "condition": 2, - "content": 1000 - }, - "filter_date_field": { - "field_id": "date_field", - "condition": 1, - "content": 1685798400000 - }, - "filter_checkbox_field": { - "field_id": "checkbox_field", - "condition": 1 - }, - "filter_checklist_field": { - "field_id": "checklist_field", - "condition": 1 - }, - "filter_url_field": { - "field_id": "url_field", - "condition": 0, - "content": "https://example.com/4" - }, - "filter_single_select_field": { - "field_id": "single_select_field", - "condition": 0, - "content": "2" - }, - "filter_multi_select_field": { - "field_id": "multi_select_field", - "condition": 2, - "content": "1,3" - } -} \ No newline at end of file diff --git a/frontend/appflowy_web_app/src/application/database-yjs/__tests__/fixtures/rows.json b/frontend/appflowy_web_app/src/application/database-yjs/__tests__/fixtures/rows.json deleted file mode 100644 index 989a335527..0000000000 --- a/frontend/appflowy_web_app/src/application/database-yjs/__tests__/fixtures/rows.json +++ /dev/null @@ -1,412 +0,0 @@ -[ - { - "id": "1", - "cells": { - "text_field": { - "id": "text_field", - "data": "Hello world" - }, - "number_field": { - "id": "number_field", - "data": 123 - }, - "checkbox_field": { - "id": "checkbox_field", - "data": "Yes" - }, - "date_field": { - "id": "date_field", - "data": 1685539200000, - "end_timestamp": 1685625600000, - "include_time": true, - "is_range": false, - "reminder_id": "rem1" - }, - "url_field": { - "id": "url_field", - "data": "https://example.com/1" - }, - "single_select_field": { - "id": "single_select_field", - "data": "1" - }, - "multi_select_field": { - "id": "multi_select_field", - "data": "1,2" - }, - "checklist_field": { - "id": "checklist_field", - "data": "{\"options\":[{\"id\":\"1\",\"name\":\"Task 1\"},{\"id\":\"2\",\"name\":\"Task 2\"}],\"selected_option_ids\":[\"1\"]}" - } - } - }, - { - "id": "2", - "cells": { - "text_field": { - "id": "text_field", - "data": "Good morning" - }, - "number_field": { - "id": "number_field", - "data": 456 - }, - "checkbox_field": { - "id": "checkbox_field", - "data": "No" - }, - "date_field": { - "id": "date_field", - "data": 1685625600000, - "end_timestamp": 1685712000000, - "include_time": false, - "is_range": true, - "reminder_id": "rem2" - }, - "url_field": { - "id": "url_field", - "data": "https://example.com/2" - }, - "single_select_field": { - "id": "single_select_field", - "data": "2" - }, - "multi_select_field": { - "id": "multi_select_field", - "data": "2,3" - }, - "checklist_field": { - "id": "checklist_field", - "data": "{\"options\":[{\"id\":\"1\",\"name\":\"Task 1\"},{\"id\":\"2\",\"name\":\"Task 2\"}],\"selected_option_ids\":[\"2\"]}" - } - } - }, - { - "id": "3", - "cells": { - "text_field": { - "id": "text_field", - "data": "Good night" - }, - "number_field": { - "id": "number_field", - "data": 789 - }, - "checkbox_field": { - "id": "checkbox_field", - "data": "Yes" - }, - "date_field": { - "id": "date_field", - "data": 1685712000000, - "end_timestamp": 1685798400000, - "include_time": true, - "is_range": false, - "reminder_id": "rem3" - }, - "url_field": { - "id": "url_field", - "data": "https://example.com/3" - }, - "single_select_field": { - "id": "single_select_field", - "data": "3" - }, - "multi_select_field": { - "id": "multi_select_field", - "data": "1,3" - }, - "checklist_field": { - "id": "checklist_field", - "data": "{\"options\":[{\"id\":\"1\",\"name\":\"Task 1\"},{\"id\":\"2\",\"name\":\"Task 2\"}],\"selected_option_ids\":[\"1\",\"2\"]}" - } - } - }, - { - "id": "4", - "cells": { - "text_field": { - "id": "text_field", - "data": "Happy day" - }, - "number_field": { - "id": "number_field", - "data": 1011 - }, - "checkbox_field": { - "id": "checkbox_field", - "data": "No" - }, - "date_field": { - "id": "date_field", - "data": 1685798400000, - "end_timestamp": 1685884800000, - "include_time": false, - "is_range": true, - "reminder_id": "rem4" - }, - "url_field": { - "id": "url_field", - "data": "https://example.com/4" - }, - "single_select_field": { - "id": "single_select_field", - "data": "1" - }, - "multi_select_field": { - "id": "multi_select_field", - "data": "2" - }, - "checklist_field": { - "id": "checklist_field", - "data": "{\"options\":[{\"id\":\"1\",\"name\":\"Task 1\"},{\"id\":\"2\",\"name\":\"Task 2\"}],\"selected_option_ids\":[]}" - } - } - }, - { - "id": "5", - "cells": { - "text_field": { - "id": "text_field", - "data": "Sunny weather" - }, - "number_field": { - "id": "number_field", - "data": 1213 - }, - "checkbox_field": { - "id": "checkbox_field", - "data": "Yes" - }, - "date_field": { - "id": "date_field", - "data": 1685884800000, - "end_timestamp": 1685971200000, - "include_time": true, - "is_range": false, - "reminder_id": "rem5" - }, - "url_field": { - "id": "url_field", - "data": "https://example.com/5" - }, - "single_select_field": { - "id": "single_select_field", - "data": "2" - }, - "multi_select_field": { - "id": "multi_select_field", - "data": "1,2,3" - }, - "checklist_field": { - "id": "checklist_field", - "data": "{\"options\":[{\"id\":\"1\",\"name\":\"Task 1\"},{\"id\":\"2\",\"name\":\"Task 2\"}],\"selected_option_ids\":[\"1\"]}" - } - } - }, - { - "id": "6", - "cells": { - "text_field": { - "id": "text_field", - "data": "Rainy day" - }, - "number_field": { - "id": "number_field", - "data": 1415 - }, - "checkbox_field": { - "id": "checkbox_field", - "data": "No" - }, - "date_field": { - "id": "date_field", - "data": 1685971200000, - "end_timestamp": 1686057600000, - "include_time": false, - "is_range": true, - "reminder_id": "rem6" - }, - "url_field": { - "id": "url_field", - "data": "https://example.com/6" - }, - "single_select_field": { - "id": "single_select_field", - "data": "3" - }, - "multi_select_field": { - "id": "multi_select_field", - "data": "1,3" - }, - "checklist_field": { - "id": "checklist_field", - "data": "{\"options\":[{\"id\":\"1\",\"name\":\"Task 1\"},{\"id\":\"2\",\"name\":\"Task 2\"}],\"selected_option_ids\":[\"2\"]}" - } - } - }, - { - "id": "7", - "cells": { - "text_field": { - "id": "text_field", - "data": "Winter is coming" - }, - "number_field": { - "id": "number_field", - "data": 1617 - }, - "checkbox_field": { - "id": "checkbox_field", - "data": "Yes" - }, - "date_field": { - "id": "date_field", - "data": 1686057600000, - "end_timestamp": 1686144000000, - "include_time": true, - "is_range": false, - "reminder_id": "rem7" - }, - "url_field": { - "id": "url_field", - "data": "https://example.com/7" - }, - "single_select_field": { - "id": "single_select_field", - "data": "1" - }, - "multi_select_field": { - "id": "multi_select_field", - "data": "1,2" - }, - "checklist_field": { - "id": "checklist_field", - "data": "{\"options\":[{\"id\":\"1\",\"name\":\"Task 1\"},{\"id\":\"2\",\"name\":\"Task 2\"}],\"selected_option_ids\":[\"1\"]}" - } - } - }, - { - "id": "8", - "cells": { - "text_field": { - "id": "text_field", - "data": "Summer vibes" - }, - "number_field": { - "id": "number_field", - "data": 1819 - }, - "checkbox_field": { - "id": "checkbox_field", - "data": "No" - }, - "date_field": { - "id": "date_field", - "data": 1686144000000, - "end_timestamp": 1686230400000, - "include_time": false, - "is_range": true, - "reminder_id": "rem8" - }, - "url_field": { - "id": "url_field", - "data": "https://example.com/8" - }, - "single_select_field": { - "id": "single_select_field", - "data": "2" - }, - "multi_select_field": { - "id": "multi_select_field", - "data": "2,3" - }, - "checklist_field": { - "id": "checklist_field", - "data": "{\"options\":[{\"id\":\"1\",\"name\":\"Task 1\"},{\"id\":\"2\",\"name\":\"Task 2\"}],\"selected_option_ids\":[\"2\"]}" - } - } - }, - { - "id": "9", - "cells": { - "text_field": { - "id": "text_field", - "data": "Autumn leaves" - }, - "number_field": { - "id": "number_field", - "data": 2021 - }, - "checkbox_field": { - "id": "checkbox_field", - "data": "Yes" - }, - "date_field": { - "id": "date_field", - "data": 1686230400000, - "end_timestamp": 1686316800000, - "include_time": true, - "is_range": false, - "reminder_id": "rem9" - }, - "url_field": { - "id": "url_field", - "data": "https://example.com/9" - }, - "single_select_field": { - "id": "single_select_field", - "data": "3" - }, - "multi_select_field": { - "id": "multi_select_field", - "data": "1,3" - }, - "checklist_field": { - "id": "checklist_field", - "data": "{\"options\":[{\"id\":\"1\",\"name\":\"Task 1\"},{\"id\":\"2\",\"name\":\"Task 2\"}],\"selected_option_ids\":[\"1\",\"2\"]}" - } - } - }, - { - "id": "10", - "cells": { - "text_field": { - "id": "text_field", - "data": "Spring blossoms" - }, - "number_field": { - "id": "number_field", - "data": 2223 - }, - "checkbox_field": { - "id": "checkbox_field", - "data": "No" - }, - "date_field": { - "id": "date_field", - "data": 1686316800000, - "end_timestamp": 1686403200000, - "include_time": false, - "is_range": true, - "reminder_id": "rem10" - }, - "url_field": { - "id": "url_field", - "data": "https://example.com/10" - }, - "single_select_field": { - "id": "single_select_field", - "data": "1" - }, - "multi_select_field": { - "id": "multi_select_field", - "data": "2" - }, - "checklist_field": { - "id": "checklist_field", - "data": "{\"options\":[{\"id\":\"1\",\"name\":\"Task 1\"},{\"id\":\"2\",\"name\":\"Task 2\"}],\"selected_option_ids\":[]}" - } - } - } -] diff --git a/frontend/appflowy_web_app/src/application/database-yjs/__tests__/fixtures/sorts.json b/frontend/appflowy_web_app/src/application/database-yjs/__tests__/fixtures/sorts.json deleted file mode 100644 index 11ae36cf60..0000000000 --- a/frontend/appflowy_web_app/src/application/database-yjs/__tests__/fixtures/sorts.json +++ /dev/null @@ -1,102 +0,0 @@ -{ - "sort_asc_text_field": { - "id": "sort_asc_text_field", - "field_id": "text_field", - "condition": "asc" - }, - "sort_desc_text_field": { - "field_id": "text_field", - "condition": "desc", - "id": "sort_desc_text_field" - }, - "sort_asc_number_field": { - "field_id": "number_field", - "condition": "asc", - "id": "sort_asc_number_field" - }, - "sort_desc_number_field": { - "field_id": "number_field", - "condition": "desc", - "id": "sort_desc_number_field" - }, - "sort_asc_date_field": { - "field_id": "date_field", - "condition": "asc", - "id": "sort_asc_date_field" - }, - "sort_desc_date_field": { - "field_id": "date_field", - "condition": "desc", - "id": "sort_desc_date_field" - }, - "sort_asc_checkbox_field": { - "field_id": "checkbox_field", - "condition": "asc", - "id": "sort_asc_checkbox_field" - }, - "sort_desc_checkbox_field": { - "field_id": "checkbox_field", - "condition": "desc", - "id": "sort_desc_checkbox_field" - }, - "sort_asc_checklist_field": { - "field_id": "checklist_field", - "condition": "asc", - "id": "sort_asc_checklist_field" - }, - "sort_desc_checklist_field": { - "field_id": "checklist_field", - "condition": "desc", - "id": "sort_desc_checklist_field" - }, - "sort_asc_single_select_field": { - "field_id": "single_select_field", - "condition": "asc", - "id": "sort_asc_single_select_field" - }, - "sort_desc_single_select_field": { - "field_id": "single_select_field", - "condition": "desc", - "id": "sort_desc_single_select_field" - }, - "sort_asc_multi_select_field": { - "field_id": "multi_select_field", - "condition": "asc", - "id": "sort_asc_multi_select_field" - }, - "sort_desc_multi_select_field": { - "field_id": "multi_select_field", - "condition": "desc", - "id": "sort_desc_multi_select_field" - }, - "sort_asc_url_field": { - "field_id": "url_field", - "condition": "asc", - "id": "sort_asc_url_field" - }, - "sort_desc_url_field": { - "field_id": "url_field", - "condition": "desc", - "id": "sort_desc_url_field" - }, - "sort_asc_created_at": { - "field_id": "created_at_field", - "condition": "asc", - "id": "sort_asc_created_at" - }, - "sort_desc_created_at": { - "field_id": "created_at_field", - "condition": "desc", - "id": "sort_desc_created_at" - }, - "sort_asc_updated_at": { - "field_id": "last_modified_field", - "condition": "asc", - "id": "sort_asc_updated_at" - }, - "sort_desc_updated_at": { - "field_id": "last_modified_field", - "condition": "desc", - "id": "sort_desc_updated_at" - } -} \ No newline at end of file diff --git a/frontend/appflowy_web_app/src/application/database-yjs/__tests__/group.test.ts b/frontend/appflowy_web_app/src/application/database-yjs/__tests__/group.test.ts deleted file mode 100644 index a38ed139d6..0000000000 --- a/frontend/appflowy_web_app/src/application/database-yjs/__tests__/group.test.ts +++ /dev/null @@ -1,198 +0,0 @@ -import { FieldType, Row } from '@/application/database-yjs'; -import { withTestingData } from '@/application/database-yjs/__tests__/withTestingData'; -import { withTestingRows } from '@/application/database-yjs/__tests__/withTestingRows'; -import { expect } from '@jest/globals'; -import { groupByField } from '../group'; -import * as Y from 'yjs'; -import { - YDatabaseField, - YDatabaseFieldTypeOption, - YjsDatabaseKey, - YjsEditorKey, - YMapFieldTypeOption, -} from '@/application/types'; -import { YjsEditor } from '@/application/slate-yjs'; - -describe('Database group', () => { - let rows: Row[]; - - beforeEach(() => { - rows = withTestingRows(); - }); - - it('should return undefined if field is not select option', () => { - const { fields, rowMap } = withTestingData(); - expect(groupByField(rows, rowMap, fields.get('text_field'))).toBeUndefined(); - expect(groupByField(rows, rowMap, fields.get('number_field'))).toBeUndefined(); - expect(groupByField(rows, rowMap, fields.get('checklist_field'))).toBeUndefined(); - }); - - it('should gourp by checkbox field', () => { - const { fields, rowMap } = withTestingData(); - const field = fields.get('checkbox_field'); - const result = groupByField(rows, rowMap, field); - const expectRes = new Map([ - [ - 'Yes', - [ - { id: '1', height: 37 }, - { id: '3', height: 37 }, - { id: '5', height: 37 }, - { id: '7', height: 37 }, - { id: '9', height: 37 }, - ], - ], - [ - 'No', - [ - { id: '2', height: 37 }, - { id: '4', height: 37 }, - { id: '6', height: 37 }, - { id: '8', height: 37 }, - { id: '10', height: 37 }, - ], - ], - ]); - expect(result).toEqual(expectRes); - }); - it('should group by select option field', () => { - const { fields, rowMap } = withTestingData(); - const field = fields.get('single_select_field'); - const result = groupByField(rows, rowMap, field); - const expectRes = new Map([ - [ - '1', - [ - { id: '1', height: 37 }, - { id: '4', height: 37 }, - { id: '7', height: 37 }, - { id: '10', height: 37 }, - ], - ], - [ - '2', - [ - { id: '2', height: 37 }, - { id: '5', height: 37 }, - { id: '8', height: 37 }, - ], - ], - [ - '3', - [ - { id: '3', height: 37 }, - { id: '6', height: 37 }, - { id: '9', height: 37 }, - ], - ], - ]); - expect(result).toEqual(expectRes); - }); - - it('should group by multi select option field', () => { - const { fields, rowMap } = withTestingData(); - const field = fields.get('multi_select_field'); - const result = groupByField(rows, rowMap, field); - const expectRes = new Map([ - [ - '1', - [ - { id: '1', height: 37 }, - { id: '3', height: 37 }, - { id: '5', height: 37 }, - { id: '6', height: 37 }, - { id: '7', height: 37 }, - { id: '9', height: 37 }, - ], - ], - [ - '2', - [ - { id: '1', height: 37 }, - { id: '2', height: 37 }, - { id: '4', height: 37 }, - { id: '5', height: 37 }, - { id: '7', height: 37 }, - { id: '8', height: 37 }, - { id: '10', height: 37 }, - ], - ], - [ - '3', - [ - { id: '2', height: 37 }, - { id: '3', height: 37 }, - { id: '5', height: 37 }, - { id: '6', height: 37 }, - { id: '8', height: 37 }, - { id: '9', height: 37 }, - ], - ], - ]); - expect(result).toEqual(expectRes); - }); - - it('should not group if no options', () => { - const { fields, rowMap } = withTestingData(); - const field = new Y.Map() as YDatabaseField; - const typeOption = new Y.Map() as YDatabaseFieldTypeOption; - const now = Date.now().toString(); - - field.set(YjsDatabaseKey.name, 'Single Select Field'); - field.set(YjsDatabaseKey.id, 'another_single_select_field'); - field.set(YjsDatabaseKey.type, String(FieldType.SingleSelect)); - field.set(YjsDatabaseKey.last_modified, now.valueOf()); - field.set(YjsDatabaseKey.type_option, typeOption); - fields.set('another_single_select_field', field); - expect(groupByField(rows, rowMap, field)).toBeUndefined(); - - const selectTypeOption = new Y.Map() as YMapFieldTypeOption; - - typeOption.set(String(FieldType.SingleSelect), selectTypeOption); - selectTypeOption.set(YjsDatabaseKey.content, JSON.stringify({ disable_color: false, options: [] })); - const expectRes = new Map([['another_single_select_field', rows]]); - expect(groupByField(rows, rowMap, field)).toEqual(expectRes); - }); - - it('should handle empty selected ids', () => { - const { fields, rowMap } = withTestingData(); - const cell = rowMap['1'] - ?.getMap(YjsEditorKey.data_section) - ?.get(YjsEditorKey.database_row) - ?.get(YjsDatabaseKey.cells) - ?.get('single_select_field'); - cell?.set(YjsDatabaseKey.data, null); - - const field = fields.get('single_select_field'); - const result = groupByField(rows, rowMap, field); - expect(result).toEqual( - new Map([ - ['single_select_field', [{ id: '1', height: 37 }]], - [ - '2', - [ - { id: '2', height: 37 }, - { id: '5', height: 37 }, - { id: '8', height: 37 }, - ], - ], - [ - '3', - [ - { id: '3', height: 37 }, - { id: '6', height: 37 }, - { id: '9', height: 37 }, - ], - ], - [ - '1', - [ - { id: '4', height: 37 }, - { id: '7', height: 37 }, - { id: '10', height: 37 }, - ], - ], - ]), - ); - }); -}); diff --git a/frontend/appflowy_web_app/src/application/database-yjs/__tests__/parse.test.ts b/frontend/appflowy_web_app/src/application/database-yjs/__tests__/parse.test.ts deleted file mode 100644 index 52e6df60e7..0000000000 --- a/frontend/appflowy_web_app/src/application/database-yjs/__tests__/parse.test.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { parseYDatabaseCellToCell } from '@/application/database-yjs/cell.parse'; -import { expect } from '@jest/globals'; -import { withTestingCheckboxCell, withTestingDateCell } from '@/application/database-yjs/__tests__/withTestingCell'; -import * as Y from 'yjs'; -import { - FieldType, - parseSelectOptionTypeOptions, - parseRelationTypeOption, - parseNumberTypeOptions, -} from '@/application/database-yjs'; -import { YDatabaseField, YDatabaseFieldTypeOption, YjsDatabaseKey } from '@/application/types'; -import { - withNumberTestingField, - withRelationTestingField, -} from '@/application/database-yjs/__tests__/withTestingField'; - -describe('parseYDatabaseCellToCell', () => { - it('should parse a DateTime cell', () => { - const doc = new Y.Doc(); - const cell = withTestingDateCell(); - doc.getMap('cells').set('date_field', cell); - const parsedCell = parseYDatabaseCellToCell(cell); - expect(parsedCell.data).not.toBe(undefined); - expect(parsedCell.createdAt).not.toBe(undefined); - expect(parsedCell.lastModified).not.toBe(undefined); - expect(parsedCell.fieldType).toBe(Number(FieldType.DateTime)); - }); - it('should parse a Checkbox cell', () => { - const doc = new Y.Doc(); - const cell = withTestingCheckboxCell(); - doc.getMap('cells').set('checkbox_field', cell); - const parsedCell = parseYDatabaseCellToCell(cell); - expect(parsedCell.data).toBe(true); - expect(parsedCell.createdAt).not.toBe(undefined); - expect(parsedCell.lastModified).not.toBe(undefined); - expect(parsedCell.fieldType).toBe(Number(FieldType.Checkbox)); - }); -}); - -describe('Select option field parse', () => { - it('should parse select option type options', () => { - const doc = new Y.Doc(); - const field = new Y.Map() as YDatabaseField; - const typeOption = new Y.Map() as YDatabaseFieldTypeOption; - const now = Date.now().toString(); - - field.set(YjsDatabaseKey.name, 'Single Select Field'); - field.set(YjsDatabaseKey.id, 'single_select_field'); - field.set(YjsDatabaseKey.type, String(FieldType.SingleSelect)); - field.set(YjsDatabaseKey.last_modified, now.valueOf()); - field.set(YjsDatabaseKey.type_option, typeOption); - doc.getMap('fields').set('single_select_field', field); - expect(parseSelectOptionTypeOptions(field)).toEqual(null); - }); -}); - -describe('number field parse', () => { - it('should parse number field', () => { - const doc = new Y.Doc(); - const field = withNumberTestingField(); - doc.getMap('fields').set('number_field', field); - expect(parseNumberTypeOptions(field)).toEqual({ - format: 0, - }); - }); -}); - -describe('relation field parse', () => { - it('should parse relation field', () => { - const doc = new Y.Doc(); - const field = withRelationTestingField(); - doc.getMap('fields').set('relation_field', field); - expect(parseRelationTypeOption(field)).toEqual(undefined); - }); -}); diff --git a/frontend/appflowy_web_app/src/application/database-yjs/__tests__/selector.test.tsx b/frontend/appflowy_web_app/src/application/database-yjs/__tests__/selector.test.tsx deleted file mode 100644 index 1cdfd3dbfd..0000000000 --- a/frontend/appflowy_web_app/src/application/database-yjs/__tests__/selector.test.tsx +++ /dev/null @@ -1,272 +0,0 @@ -import { renderHook } from '@testing-library/react'; -import { - useCellSelector, - useFieldSelector, - useFieldsSelector, - useFilterSelector, - useFiltersSelector, - useGroup, - useGroupsSelector, - usePrimaryFieldId, - useRowDataSelector, - useRowMetaSelector, - useRowOrdersSelector, - useRowsByGroup, - useSortSelector, - useSortsSelector, -} from '../selector'; -import { useDatabaseViewId } from '../context'; -import { DatabaseContextProvider } from '@/components/database/DatabaseContext'; -import { withTestingDatabase } from '@/application/database-yjs/__tests__/withTestingData'; -import { expect } from '@jest/globals'; -import { YDoc, YjsDatabaseKey, YjsEditorKey, YSharedRoot } from '@/application/types'; -import * as Y from 'yjs'; -import { withNumberTestingField, withTestingFields } from '@/application/database-yjs/__tests__/withTestingField'; -import { withTestingRows } from '@/application/database-yjs/__tests__/withTestingRows'; - -const wrapperCreator = - (viewId: string, doc: YDoc, rowDocMap: Record) => - ({ children }: { children: React.ReactNode }) => { - return ( - - {children} - - ); - }; - -describe('Database selector', () => { - let wrapper: ({ children }: { children: React.ReactNode }) => JSX.Element; - let rowDocMap: Record; - let doc: YDoc; - - beforeEach(() => { - const data = withTestingDatabase('1'); - - doc = data.doc; - rowDocMap = data.rowDocMap; - wrapper = wrapperCreator('1', doc, rowDocMap); - }); - - it('should select a field', () => { - const { result } = renderHook(() => useFieldSelector('number_field'), { wrapper }); - - const tempDoc = new Y.Doc(); - const field = withNumberTestingField(); - - tempDoc.getMap().set('number_field', field); - - expect(result.current.field?.toJSON()).toEqual(field.toJSON()); - }); - - it('should select all fields', () => { - const { result } = renderHook(() => useFieldsSelector(), { wrapper }); - - expect(result.current.map((item) => item.fieldId)).toEqual(Array.from(withTestingFields().keys())); - }); - - it('should select all filters', () => { - const { result } = renderHook(() => useFiltersSelector(), { wrapper }); - - expect(result.current).toEqual(['filter_multi_select_field']); - }); - - it('should select a filter', () => { - const { result } = renderHook(() => useFilterSelector('filter_multi_select_field'), { wrapper }); - - expect(result.current).toEqual({ - content: '1,3', - condition: 2, - fieldId: 'multi_select_field', - id: 'filter_multi_select_field', - filterType: NaN, - optionIds: ['1', '3'], - }); - }); - - it('should select all sorts', () => { - const { result } = renderHook(() => useSortsSelector(), { wrapper }); - - expect(result.current).toEqual(['sort_asc_text_field']); - }); - - it('should select a sort', () => { - const { result } = renderHook(() => useSortSelector('sort_asc_text_field'), { wrapper }); - - expect(result.current).toEqual({ - fieldId: 'text_field', - id: 'sort_asc_text_field', - condition: 0, - }); - }); - - it('should select all groups', () => { - const { result } = renderHook(() => useGroupsSelector(), { wrapper }); - - expect(result.current).toEqual(['g:single_select_field']); - }); - - it('should select a group', () => { - const { result } = renderHook(() => useGroup('g:single_select_field'), { wrapper }); - - expect(result.current).toEqual({ - fieldId: 'single_select_field', - columns: [ - { - id: '1', - visible: true, - }, - { - id: 'single_select_field', - visible: true, - }, - ], - }); - }); - - it('should select rows by group', () => { - const { result } = renderHook(() => useRowsByGroup('g:single_select_field'), { wrapper }); - - const { fieldId, columns, notFound, groupResult } = result.current; - - expect(fieldId).toEqual('single_select_field'); - expect(columns).toEqual([ - { - id: '1', - visible: true, - }, - { - id: 'single_select_field', - visible: true, - }, - ]); - expect(notFound).toBeFalsy(); - - expect(groupResult).toEqual( - new Map([ - [ - '1', - [ - { id: '1', height: 37 }, - { id: '7', height: 37 }, - ], - ], - [ - '2', - [ - { id: '2', height: 37 }, - { id: '8', height: 37 }, - { id: '5', height: 37 }, - ], - ], - [ - '3', - [ - { id: '9', height: 37 }, - { id: '3', height: 37 }, - { id: '6', height: 37 }, - ], - ], - ]), - ); - }); - - it('should select all row orders', () => { - const { result } = renderHook(() => useRowOrdersSelector(), { wrapper }); - - expect(result.current?.map((item) => item.id).join(',')).toEqual('9,2,3,1,6,8,5,7'); - }); - - it('should select a row data', () => { - const rows = withTestingRows(); - const { result } = renderHook(() => useRowDataSelector(rows[0].id), { wrapper }); - - expect(result.current.row?.toJSON()).toEqual( - rowDocMap[rows[0].id]?.getMap(YjsEditorKey.data_section)?.get(YjsEditorKey.database_row)?.toJSON(), - ); - }); - - it('should select a cell', () => { - const rows = withTestingRows(); - const { result } = renderHook( - () => - useCellSelector({ - rowId: rows[0].id, - fieldId: 'number_field', - }), - { wrapper }, - ); - - expect(result.current).toEqual({ - createdAt: NaN, - data: 123, - fieldType: 1, - lastModified: NaN, - }); - }); - - it('should select a primary field id', () => { - const { result } = renderHook(() => usePrimaryFieldId(), { wrapper }); - - expect(result.current).toEqual('text_field'); - }); - - it('should select a row meta', () => { - const rows = withTestingRows(); - const { result } = renderHook(() => useRowMetaSelector(rows[0].id), { wrapper }); - - expect(result.current?.documentId).not.toBeNull(); - }); - - it('should select view id', () => { - const { result } = renderHook(() => useDatabaseViewId(), { wrapper }); - - expect(result.current).toEqual('1'); - }); - - it('should select all rows if filter is not found', () => { - const view = (doc.get(YjsEditorKey.data_section) as YSharedRoot) - .get(YjsEditorKey.database) - .get(YjsDatabaseKey.views) - .get('1'); - - view.set(YjsDatabaseKey.filters, new Y.Array()); - - const { result } = renderHook(() => useRowOrdersSelector(), { wrapper }); - - expect(result.current?.map((item) => item.id).join(',')).toEqual('9,2,3,4,1,6,10,8,5,7'); - }); - - it('should select original row orders if sorts is not found', () => { - const view = (doc.get(YjsEditorKey.data_section) as YSharedRoot) - .get(YjsEditorKey.database) - .get(YjsDatabaseKey.views) - .get('1'); - - view.set(YjsDatabaseKey.sorts, new Y.Array()); - - const { result } = renderHook(() => useRowOrdersSelector(), { wrapper }); - - expect(result.current?.map((item) => item.id).join(',')).toEqual('1,2,3,5,6,7,8,9'); - }); - - it('should select all rows if filters and sorts are not found', () => { - const view = (doc.get(YjsEditorKey.data_section) as YSharedRoot) - .get(YjsEditorKey.database) - .get(YjsDatabaseKey.views) - .get('1'); - - view.set(YjsDatabaseKey.filters, new Y.Array()); - view.set(YjsDatabaseKey.sorts, new Y.Array()); - - const { result } = renderHook(() => useRowOrdersSelector(), { wrapper }); - - expect(result.current?.map((item) => item.id).join(',')).toEqual('1,2,3,4,5,6,7,8,9,10'); - }); -}); diff --git a/frontend/appflowy_web_app/src/application/database-yjs/__tests__/sort.test.ts b/frontend/appflowy_web_app/src/application/database-yjs/__tests__/sort.test.ts deleted file mode 100644 index ce41777e26..0000000000 --- a/frontend/appflowy_web_app/src/application/database-yjs/__tests__/sort.test.ts +++ /dev/null @@ -1,397 +0,0 @@ -import { Row } from '@/application/database-yjs'; -import { withTestingData } from '@/application/database-yjs/__tests__/withTestingData'; -import { withTestingRows } from '@/application/database-yjs/__tests__/withTestingRows'; -import { - withCheckboxSort, - withChecklistSort, - withCreatedAtSort, - withDateTimeSort, - withLastModifiedSort, - withMultiSelectOptionSort, - withNumberSort, - withRichTextSort, - withSingleSelectOptionSort, - withUrlSort, -} from '@/application/database-yjs/__tests__/withTestingSorts'; -import { - withCheckboxTestingField, - withDateTimeTestingField, - withNumberTestingField, - withRichTextTestingField, - withSelectOptionTestingField, - withURLTestingField, - withChecklistTestingField, - withRelationTestingField, -} from './withTestingField'; -import { sortBy, parseCellDataForSort } from '../sort'; -import * as Y from 'yjs'; -import { expect } from '@jest/globals'; -import { RowId, YDoc, YjsDatabaseKey, YjsEditorKey } from '@/application/types'; - -describe('parseCellDataForSort', () => { - it('should parse data correctly based on field type', () => { - const doc = new Y.Doc(); - const field = withNumberTestingField(); - doc.getMap().set('field', field); - const data = 42; - - const result = parseCellDataForSort(field, data); - - expect(result).toEqual(data); - }); - - it('should return default value for empty rich text', () => { - const doc = new Y.Doc(); - const field = withRichTextTestingField(); - doc.getMap().set('field', field); - const data = ''; - - const result = parseCellDataForSort(field, data); - - expect(result).toEqual('\uFFFF'); - }); - - it('should return default value for empty URL', () => { - const doc = new Y.Doc(); - const field = withURLTestingField(); - doc.getMap().set('field', field); - const data = ''; - - const result = parseCellDataForSort(field, data); - - expect(result).toBe('\uFFFF'); - }); - - it('should return data for non-empty rich text', () => { - const doc = new Y.Doc(); - const field = withRichTextTestingField(); - doc.getMap().set('field', field); - const data = 'Hello, world!'; - - const result = parseCellDataForSort(field, data); - - expect(result).toBe(data); - }); - - it('should parse checkbox data correctly', () => { - const doc = new Y.Doc(); - const field = withCheckboxTestingField(); - doc.getMap().set('field', field); - const data = 'Yes'; - - const result = parseCellDataForSort(field, data); - - expect(result).toBe(true); - - const noData = 'No'; - const noResult = parseCellDataForSort(field, noData); - expect(noResult).toBe(false); - }); - - it('should parse DateTime data correctly', () => { - const doc = new Y.Doc(); - const field = withDateTimeTestingField(); - doc.getMap().set('field', field); - const data = '1633046400000'; - - const result = parseCellDataForSort(field, data); - - expect(result).toBe(Number(data)); - }); - - it('should parse SingleSelect data correctly', () => { - const doc = new Y.Doc(); - const field = withSelectOptionTestingField(); - doc.getMap().set('field', field); - const data = '1'; - - const result = parseCellDataForSort(field, data); - - expect(result).toBe('Option 1'); - }); - - it('should parse MultiSelect data correctly', () => { - const doc = new Y.Doc(); - const field = withSelectOptionTestingField(); - doc.getMap().set('field', field); - const data = '1,2'; - - const result = parseCellDataForSort(field, data); - - expect(result).toBe('Option 1, Option 2'); - }); - - it('should parse Checklist data correctly', () => { - const doc = new Y.Doc(); - const field = withChecklistTestingField(); - doc.getMap().set('field', field); - const data = '[]'; - - const result = parseCellDataForSort(field, data); - - expect(result).toBe(0); - }); - - it('should return empty string for Relation field', () => { - const doc = new Y.Doc(); - const field = withRelationTestingField(); - doc.getMap().set('field', field); - const data = ''; - - const result = parseCellDataForSort(field, data); - - expect(result).toBe(''); - }); -}); - -describe('Database sortBy', () => { - let rows: Row[]; - - beforeEach(() => { - rows = withTestingRows(); - }); - - it('should not sort rows if no sort is provided', () => { - const { sorts, fields, rowMap } = withTestingData(); - - const sortedRows = sortBy(rows, sorts, fields, rowMap) - .map((row) => row.id) - .join(','); - expect(sortedRows).toBe('1,2,3,4,5,6,7,8,9,10'); - }); - - it('should not sort rows if no rows are provided', () => { - const { sorts, fields } = withTestingData(); - const rowMap: Record = {}; - const sortedRows = sortBy(rows, sorts, fields, rowMap) - .map((row) => row.id) - .join(','); - expect(sortedRows).toBe('1,2,3,4,5,6,7,8,9,10'); - }); - - it('should return default data if rowMeta is not found', () => { - const { sorts, fields, rowMap } = withTestingData(); - const sort = withNumberSort(); - sorts.push([sort]); - delete rowMap['1']; - - const sortedRows = sortBy(rows, sorts, fields, rowMap) - .map((row) => row.id) - .join(','); - expect(sortedRows).toBe('1,2,3,4,5,6,7,8,9,10'); - }); - - it('should return default data if cell is not found', () => { - const { sorts, fields, rowMap } = withTestingData(); - const sort = withNumberSort(); - sorts.push([sort]); - const rowDoc = rowMap['1']; - rowDoc - ?.getMap(YjsEditorKey.data_section) - .get(YjsEditorKey.database_row) - ?.get(YjsDatabaseKey.cells) - .delete('number_field'); - - const sortedRows = sortBy(rows, sorts, fields, rowMap) - .map((row) => row.id) - .join(','); - expect(sortedRows).toBe('1,2,3,4,5,6,7,8,9,10'); - }); - - it('should sort by number field in ascending order', () => { - const { sorts, fields, rowMap } = withTestingData(); - const sort = withNumberSort(); - sorts.push([sort]); - - const sortedRows = sortBy(rows, sorts, fields, rowMap) - .map((row) => row.id) - .join(','); - expect(sortedRows).toBe('1,2,3,4,5,6,7,8,9,10'); - }); - - it('should sort by number field in descending order', () => { - const { sorts, fields, rowMap } = withTestingData(); - const sort = withNumberSort(false); - sorts.push([sort]); - - const sortedRows = sortBy(rows, sorts, fields, rowMap) - .map((row) => row.id) - .join(','); - expect(sortedRows).toBe('10,9,8,7,6,5,4,3,2,1'); - }); - - it('should sort by rich text field in ascending order', () => { - const { sorts, fields, rowMap } = withTestingData(); - const sort = withRichTextSort(); - sorts.push([sort]); - - const sortedRows = sortBy(rows, sorts, fields, rowMap) - .map((row) => row.id) - .join(','); - expect(sortedRows).toBe('9,2,3,4,1,6,10,8,5,7'); - }); - - it('should sort by rich text field in descending order', () => { - const { sorts, fields, rowMap } = withTestingData(); - const sort = withRichTextSort(false); - sorts.push([sort]); - - const sortedRows = sortBy(rows, sorts, fields, rowMap) - .map((row) => row.id) - .join(','); - expect(sortedRows).toBe('7,5,8,10,6,1,4,3,2,9'); - }); - - it('should sort by url field in ascending order', () => { - const { sorts, fields, rowMap } = withTestingData(); - const sort = withUrlSort(); - sorts.push([sort]); - - const sortedRows = sortBy(rows, sorts, fields, rowMap) - .map((row) => row.id) - .join(','); - expect(sortedRows).toBe('1,10,2,3,4,5,6,7,8,9'); - }); - - it('should sort by url field in descending order', () => { - const { sorts, fields, rowMap } = withTestingData(); - const sort = withUrlSort(false); - sorts.push([sort]); - - const sortedRows = sortBy(rows, sorts, fields, rowMap) - .map((row) => row.id) - .join(','); - expect(sortedRows).toBe('9,8,7,6,5,4,3,2,10,1'); - }); - - it('should sort by checkbox field in ascending order', () => { - const { sorts, fields, rowMap } = withTestingData(); - const sort = withCheckboxSort(); - sorts.push([sort]); - - const sortedRows = sortBy(rows, sorts, fields, rowMap) - .map((row) => row.id) - .join(','); - expect(sortedRows).toBe('2,4,6,8,10,1,3,5,7,9'); - }); - - it('should sort by checkbox field in descending order', () => { - const { sorts, fields, rowMap } = withTestingData(); - const sort = withCheckboxSort(false); - sorts.push([sort]); - - const sortedRows = sortBy(rows, sorts, fields, rowMap) - .map((row) => row.id) - .join(','); - expect(sortedRows).toBe('1,3,5,7,9,2,4,6,8,10'); - }); - - it('should sort by DateTime field in ascending order', () => { - const { sorts, fields, rowMap } = withTestingData(); - const sort = withDateTimeSort(); - sorts.push([sort]); - - const sortedRows = sortBy(rows, sorts, fields, rowMap) - .map((row) => row.id) - .join(','); - expect(sortedRows).toBe('1,2,3,4,5,6,7,8,9,10'); - }); - - it('should sort by DateTime field in descending order', () => { - const { sorts, fields, rowMap } = withTestingData(); - const sort = withDateTimeSort(false); - sorts.push([sort]); - - const sortedRows = sortBy(rows, sorts, fields, rowMap) - .map((row) => row.id) - .join(','); - expect(sortedRows).toBe('10,9,8,7,6,5,4,3,2,1'); - }); - - it('should sort by SingleSelect field in ascending order', () => { - const { sorts, fields, rowMap } = withTestingData(); - const sort = withSingleSelectOptionSort(); - sorts.push([sort]); - - const sortedRows = sortBy(rows, sorts, fields, rowMap) - .map((row) => row.id) - .join(','); - expect(sortedRows).toBe('1,4,7,10,2,5,8,3,6,9'); - }); - - it('should sort by SingleSelect field in descending order', () => { - const { sorts, fields, rowMap } = withTestingData(); - const sort = withSingleSelectOptionSort(false); - sorts.push([sort]); - - const sortedRows = sortBy(rows, sorts, fields, rowMap) - .map((row) => row.id) - .join(','); - expect(sortedRows).toBe('3,6,9,2,5,8,1,4,7,10'); - }); - - it('should sort by MultiSelect field in ascending order', () => { - const { sorts, fields, rowMap } = withTestingData(); - const sort = withMultiSelectOptionSort(); - sorts.push([sort]); - - const sortedRows = sortBy(rows, sorts, fields, rowMap) - .map((row) => row.id) - .join(','); - expect(sortedRows).toBe('1,7,5,3,6,9,4,10,2,8'); - }); - - it('should sort by MultiSelect field in descending order', () => { - const { sorts, fields, rowMap } = withTestingData(); - const sort = withMultiSelectOptionSort(false); - sorts.push([sort]); - - const sortedRows = sortBy(rows, sorts, fields, rowMap) - .map((row) => row.id) - .join(','); - expect(sortedRows).toBe('2,8,4,10,3,6,9,5,1,7'); - }); - - it('should sort by Checklist field in ascending order', () => { - const { sorts, fields, rowMap } = withTestingData(); - const sort = withChecklistSort(); - sorts.push([sort]); - - const sortedRows = sortBy(rows, sorts, fields, rowMap) - .map((row) => row.id) - .join(','); - expect(sortedRows).toBe('4,10,1,2,5,6,7,8,3,9'); - }); - - it('should sort by Checklist field in descending order', () => { - const { sorts, fields, rowMap } = withTestingData(); - const sort = withChecklistSort(false); - sorts.push([sort]); - - const sortedRows = sortBy(rows, sorts, fields, rowMap) - .map((row) => row.id) - .join(','); - expect(sortedRows).toBe('3,9,1,2,5,6,7,8,4,10'); - }); - - it('should sort by CreatedAt field in ascending order', () => { - const { sorts, fields, rowMap } = withTestingData(); - const sort = withCreatedAtSort(); - sorts.push([sort]); - - const sortedRows = sortBy(rows, sorts, fields, rowMap) - .map((row) => row.id) - .join(','); - expect(sortedRows).toBe('1,2,3,4,5,6,7,8,9,10'); - }); - - it('should sort by LastEditedTime field', () => { - const { sorts, fields, rowMap } = withTestingData(); - const sort = withLastModifiedSort(); - sorts.push([sort]); - const sortedRows = sortBy(rows, sorts, fields, rowMap) - .map((row) => row.id) - .join(','); - expect(sortedRows).toBe('1,2,3,4,5,6,7,8,9,10'); - }); -}); diff --git a/frontend/appflowy_web_app/src/application/database-yjs/__tests__/withTestingCell.ts b/frontend/appflowy_web_app/src/application/database-yjs/__tests__/withTestingCell.ts deleted file mode 100644 index de0f7d68e3..0000000000 --- a/frontend/appflowy_web_app/src/application/database-yjs/__tests__/withTestingCell.ts +++ /dev/null @@ -1,43 +0,0 @@ -import * as Y from 'yjs'; -import { YDatabaseCell, YjsDatabaseKey } from '@/application/types'; -import { FieldType } from '@/application/database-yjs'; - -export function withTestingDateCell() { - const cell = new Y.Map() as YDatabaseCell; - - cell.set(YjsDatabaseKey.id, 'date_field'); - cell.set(YjsDatabaseKey.data, Date.now()); - cell.set(YjsDatabaseKey.field_type, Number(FieldType.DateTime)); - cell.set(YjsDatabaseKey.created_at, Date.now()); - cell.set(YjsDatabaseKey.last_modified, Date.now()); - cell.set(YjsDatabaseKey.end_timestamp, Date.now() + 1000); - cell.set(YjsDatabaseKey.include_time, true); - cell.set(YjsDatabaseKey.is_range, true); - cell.set(YjsDatabaseKey.reminder_id, 'reminderId'); - - return cell; -} - -export function withTestingCheckboxCell() { - const cell = new Y.Map() as YDatabaseCell; - - cell.set(YjsDatabaseKey.id, 'checkbox_field'); - cell.set(YjsDatabaseKey.data, 'Yes'); - cell.set(YjsDatabaseKey.field_type, Number(FieldType.Checkbox)); - cell.set(YjsDatabaseKey.created_at, Date.now()); - cell.set(YjsDatabaseKey.last_modified, Date.now()); - - return cell; -} - -export function withTestingSingleOptionCell() { - const cell = new Y.Map() as YDatabaseCell; - - cell.set(YjsDatabaseKey.id, 'single_select_field'); - cell.set(YjsDatabaseKey.data, 'optionId'); - cell.set(YjsDatabaseKey.field_type, Number(FieldType.SingleSelect)); - cell.set(YjsDatabaseKey.created_at, Date.now()); - cell.set(YjsDatabaseKey.last_modified, Date.now()); - - return cell; -} diff --git a/frontend/appflowy_web_app/src/application/database-yjs/__tests__/withTestingData.ts b/frontend/appflowy_web_app/src/application/database-yjs/__tests__/withTestingData.ts deleted file mode 100644 index 282590eb35..0000000000 --- a/frontend/appflowy_web_app/src/application/database-yjs/__tests__/withTestingData.ts +++ /dev/null @@ -1,181 +0,0 @@ -import { - RowId, - YDatabase, - YDatabaseFields, - YDatabaseFilters, - YDatabaseGroup, - YDatabaseGroupColumn, - YDatabaseGroupColumns, - YDatabaseLayoutSettings, - YDatabaseSorts, - YDatabaseView, - YDatabaseViews, - YDoc, - YjsDatabaseKey, - YjsEditorKey, -} from '@/application/types'; -import { withTestingFields } from '@/application/database-yjs/__tests__/withTestingField'; -import { - withTestingRowData, - withTestingRowDataMap, - withTestingRows, -} from '@/application/database-yjs/__tests__/withTestingRows'; -import * as Y from 'yjs'; -import { withMultiSelectOptionFilter } from '@/application/database-yjs/__tests__/withTestingFilters'; -import { withRichTextSort } from '@/application/database-yjs/__tests__/withTestingSorts'; -import { metaIdFromRowId, RowMetaKey } from '@/application/database-yjs'; - -export function withTestingData () { - const doc = new Y.Doc(); - const sharedRoot = doc.getMap(); - const fields = withTestingFields() as YDatabaseFields; - - sharedRoot.set('fields', fields); - - const rowMap = withTestingRowDataMap(); - - sharedRoot.set('rows', rowMap); - - const sorts = new Y.Array() as YDatabaseSorts; - - sharedRoot.set('sorts', sorts); - - const filters = new Y.Array() as YDatabaseFilters; - - sharedRoot.set('filters', filters); - - return { - fields, - rowMap, - sorts, - filters, - doc, - }; -} - -export function withTestingDatabase (viewId: string) { - const doc = new Y.Doc(); - const sharedRoot = doc.getMap(YjsEditorKey.data_section); - const database = new Y.Map() as YDatabase; - - sharedRoot.set(YjsEditorKey.database, database); - - const fields = withTestingFields() as YDatabaseFields; - - database.set(YjsDatabaseKey.fields, fields); - database.set(YjsDatabaseKey.id, viewId); - - const metas = new Y.Map(); - - database.set(YjsDatabaseKey.metas, metas); - metas.set(YjsDatabaseKey.iid, viewId); - - const views = new Y.Map() as YDatabaseViews; - - database.set(YjsDatabaseKey.views, views); - - const view = new Y.Map() as YDatabaseView; - - views.set('1', view); - view.set(YjsDatabaseKey.id, viewId); - view.set(YjsDatabaseKey.layout, 0); - view.set(YjsDatabaseKey.name, 'View 1'); - view.set(YjsDatabaseKey.database_id, viewId); - - const layoutSetting = new Y.Map() as YDatabaseLayoutSettings; - - const calendarSetting = new Y.Map(); - - calendarSetting.set(YjsDatabaseKey.field_id, 'date_field'); - layoutSetting.set('2', calendarSetting); - - view.set(YjsDatabaseKey.layout_settings, layoutSetting); - - const filters = new Y.Array() as YDatabaseFilters; - const filter = withMultiSelectOptionFilter(); - - filters.push([filter]); - - const sorts = new Y.Array() as YDatabaseSorts; - const sort = withRichTextSort(); - - sorts.push([sort]); - - const groups = new Y.Array(); - const group = new Y.Map() as YDatabaseGroup; - - groups.push([group]); - group.set(YjsDatabaseKey.id, 'g:single_select_field'); - group.set(YjsDatabaseKey.field_id, 'single_select_field'); - group.set(YjsDatabaseKey.type, '3'); - group.set(YjsDatabaseKey.content, ''); - - const groupColumns = new Y.Array() as YDatabaseGroupColumns; - - group.set(YjsDatabaseKey.groups, groupColumns); - - const column1 = new Y.Map() as YDatabaseGroupColumn; - const column2 = new Y.Map() as YDatabaseGroupColumn; - - column1.set(YjsDatabaseKey.id, '1'); - column1.set(YjsDatabaseKey.visible, true); - column2.set(YjsDatabaseKey.id, 'single_select_field'); - column2.set(YjsDatabaseKey.visible, true); - - groupColumns.push([column1]); - groupColumns.push([column2]); - - view.set(YjsDatabaseKey.filters, filters); - view.set(YjsDatabaseKey.sorts, sorts); - view.set(YjsDatabaseKey.groups, groups); - - const fieldSettings = new Y.Map(); - const fieldOrder = new Y.Array(); - const rowOrders = new Y.Array(); - - fields.forEach((field) => { - const setting = new Y.Map(); - - const fieldId = field.get(YjsDatabaseKey.id); - - if (fieldId === 'text_field') { - field.set(YjsDatabaseKey.is_primary, true); - } - - fieldOrder.push([fieldId]); - fieldSettings.set(fieldId, setting); - setting.set(YjsDatabaseKey.visibility, 0); - }); - const rows = withTestingRows(); - - rows.forEach(({ id, height }) => { - const row = new Y.Map(); - - row.set(YjsDatabaseKey.id, id); - row.set(YjsDatabaseKey.height, height); - rowOrders.push([row]); - }); - - view.set(YjsDatabaseKey.field_settings, fieldSettings); - view.set(YjsDatabaseKey.field_orders, fieldOrder); - view.set(YjsDatabaseKey.row_orders, rowOrders); - - const rowMap: Record = {}; - - rows.forEach((row, index) => { - const rowDoc = new Y.Doc(); - const rowData = withTestingRowData(row.id, index); - const rowMeta = new Y.Map(); - const parser = metaIdFromRowId('281e76fb-712e-59e2-8370-678bf0788355'); - - rowMeta.set(parser(RowMetaKey.IconId), '😊'); - rowDoc.getMap(YjsEditorKey.data_section).set(YjsEditorKey.meta, rowMeta); - rowDoc.getMap(YjsEditorKey.data_section).set(YjsEditorKey.database_row, rowData); - rowMap[row.id] = rowDoc; - }); - - return { - rowDocMap: rowMap, - doc: doc as YDoc, - }; -} diff --git a/frontend/appflowy_web_app/src/application/database-yjs/__tests__/withTestingField.ts b/frontend/appflowy_web_app/src/application/database-yjs/__tests__/withTestingField.ts deleted file mode 100644 index e9329f341d..0000000000 --- a/frontend/appflowy_web_app/src/application/database-yjs/__tests__/withTestingField.ts +++ /dev/null @@ -1,204 +0,0 @@ -import { - YDatabaseField, - YDatabaseFieldTypeOption, - YjsDatabaseKey, - YMapFieldTypeOption, -} from '@/application/types'; -import { FieldType } from '@/application/database-yjs'; -import { SelectOptionColor } from '@/application/database-yjs/fields/select-option'; -import * as Y from 'yjs'; - -export function withTestingFields() { - const fields = new Y.Map(); - const textField = withRichTextTestingField(); - - fields.set('text_field', textField); - const numberField = withNumberTestingField(); - - fields.set('number_field', numberField); - - const checkboxField = withCheckboxTestingField(); - - fields.set('checkbox_field', checkboxField); - - const dateTimeField = withDateTimeTestingField(); - - fields.set('date_field', dateTimeField); - - const singleSelectField = withSelectOptionTestingField(); - - fields.set('single_select_field', singleSelectField); - const multipleSelectField = withSelectOptionTestingField(true); - - fields.set('multi_select_field', multipleSelectField); - - const urlField = withURLTestingField(); - - fields.set('url_field', urlField); - - const checklistField = withChecklistTestingField(); - - fields.set('checklist_field', checklistField); - - const createdAtField = withCreatedAtTestingField(); - - fields.set('created_at_field', createdAtField); - - const lastModifiedField = withLastModifiedTestingField(); - - fields.set('last_modified_field', lastModifiedField); - - return fields; -} - -export function withRichTextTestingField() { - const field = new Y.Map() as YDatabaseField; - const now = Date.now().toString(); - - field.set(YjsDatabaseKey.name, 'Rich Text Field'); - field.set(YjsDatabaseKey.id, 'text_field'); - field.set(YjsDatabaseKey.type, String(FieldType.RichText)); - field.set(YjsDatabaseKey.last_modified, now.valueOf()); - - return field; -} - -export function withNumberTestingField() { - const field = new Y.Map() as YDatabaseField; - - field.set(YjsDatabaseKey.name, 'Number Field'); - field.set(YjsDatabaseKey.id, 'number_field'); - field.set(YjsDatabaseKey.type, String(FieldType.Number)); - const typeOption = new Y.Map() as YDatabaseFieldTypeOption; - - const numberTypeOption = new Y.Map() as YMapFieldTypeOption; - - typeOption.set(String(FieldType.Number), numberTypeOption); - numberTypeOption.set(YjsDatabaseKey.format, '0'); - field.set(YjsDatabaseKey.type_option, typeOption); - - return field; -} - -export function withRelationTestingField() { - const field = new Y.Map() as YDatabaseField; - const typeOption = new Y.Map() as YDatabaseFieldTypeOption; - const now = Date.now().toString(); - - field.set(YjsDatabaseKey.name, 'Relation Field'); - field.set(YjsDatabaseKey.id, 'relation_field'); - field.set(YjsDatabaseKey.type, String(FieldType.Relation)); - field.set(YjsDatabaseKey.last_modified, now.valueOf()); - field.set(YjsDatabaseKey.type_option, typeOption); - - return field; -} - -export function withCheckboxTestingField() { - const field = new Y.Map() as YDatabaseField; - const now = Date.now().toString(); - - field.set(YjsDatabaseKey.name, 'Checkbox Field'); - field.set(YjsDatabaseKey.id, 'checkbox_field'); - field.set(YjsDatabaseKey.type, String(FieldType.Checkbox)); - field.set(YjsDatabaseKey.last_modified, now.valueOf()); - - return field; -} - -export function withDateTimeTestingField() { - const field = new Y.Map() as YDatabaseField; - const typeOption = new Y.Map() as YDatabaseFieldTypeOption; - const now = Date.now().toString(); - - field.set(YjsDatabaseKey.name, 'DateTime Field'); - field.set(YjsDatabaseKey.id, 'date_field'); - field.set(YjsDatabaseKey.type, String(FieldType.DateTime)); - field.set(YjsDatabaseKey.last_modified, now.valueOf()); - field.set(YjsDatabaseKey.type_option, typeOption); - - const dateTypeOption = new Y.Map() as YMapFieldTypeOption; - - typeOption.set(String(FieldType.DateTime), dateTypeOption); - - dateTypeOption.set(YjsDatabaseKey.time_format, '0'); - dateTypeOption.set(YjsDatabaseKey.date_format, '0'); - return field; -} - -export function withURLTestingField() { - const field = new Y.Map() as YDatabaseField; - const now = Date.now().toString(); - - field.set(YjsDatabaseKey.name, 'URL Field'); - field.set(YjsDatabaseKey.id, 'url_field'); - field.set(YjsDatabaseKey.type, String(FieldType.URL)); - field.set(YjsDatabaseKey.last_modified, now.valueOf()); - - return field; -} - -export function withSelectOptionTestingField(isMultiple = false) { - const field = new Y.Map() as YDatabaseField; - const typeOption = new Y.Map() as YDatabaseFieldTypeOption; - const now = Date.now().toString(); - - field.set(YjsDatabaseKey.name, 'Single Select Field'); - field.set(YjsDatabaseKey.id, isMultiple ? 'multi_select_field' : 'single_select_field'); - field.set(YjsDatabaseKey.type, String(FieldType.SingleSelect)); - field.set(YjsDatabaseKey.last_modified, now.valueOf()); - field.set(YjsDatabaseKey.type_option, typeOption); - - const selectTypeOption = new Y.Map() as YMapFieldTypeOption; - - typeOption.set(String(FieldType.SingleSelect), selectTypeOption); - - selectTypeOption.set( - YjsDatabaseKey.content, - JSON.stringify({ - disable_color: false, - options: [ - { id: '1', name: 'Option 1', color: SelectOptionColor.Purple }, - { id: '2', name: 'Option 2', color: SelectOptionColor.Pink }, - { id: '3', name: 'Option 3', color: SelectOptionColor.LightPink }, - ], - }) - ); - return field; -} - -export function withChecklistTestingField() { - const field = new Y.Map() as YDatabaseField; - const now = Date.now().toString(); - - field.set(YjsDatabaseKey.name, 'Checklist Field'); - field.set(YjsDatabaseKey.id, 'checklist_field'); - field.set(YjsDatabaseKey.type, String(FieldType.Checklist)); - field.set(YjsDatabaseKey.last_modified, now.valueOf()); - - return field; -} - -export function withCreatedAtTestingField() { - const field = new Y.Map() as YDatabaseField; - const now = Date.now().toString(); - - field.set(YjsDatabaseKey.name, 'Created At Field'); - field.set(YjsDatabaseKey.id, 'created_at_field'); - field.set(YjsDatabaseKey.type, String(FieldType.CreatedTime)); - field.set(YjsDatabaseKey.last_modified, now.valueOf()); - - return field; -} - -export function withLastModifiedTestingField() { - const field = new Y.Map() as YDatabaseField; - const now = Date.now().toString(); - - field.set(YjsDatabaseKey.name, 'Last Modified Field'); - field.set(YjsDatabaseKey.id, 'last_modified_field'); - field.set(YjsDatabaseKey.type, String(FieldType.LastEditedTime)); - field.set(YjsDatabaseKey.last_modified, now.valueOf()); - - return field; -} diff --git a/frontend/appflowy_web_app/src/application/database-yjs/__tests__/withTestingFilters.ts b/frontend/appflowy_web_app/src/application/database-yjs/__tests__/withTestingFilters.ts deleted file mode 100644 index 57a64402f8..0000000000 --- a/frontend/appflowy_web_app/src/application/database-yjs/__tests__/withTestingFilters.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { YDatabaseFilter, YjsDatabaseKey } from '@/application/types'; -import * as Y from 'yjs'; -import * as filtersJson from './fixtures/filters.json'; - -export function withRichTextFilter() { - const filter = new Y.Map() as YDatabaseFilter; - - filter.set(YjsDatabaseKey.id, 'filter_text_field'); - filter.set(YjsDatabaseKey.field_id, filtersJson.filter_text_field.field_id); - filter.set(YjsDatabaseKey.condition, filtersJson.filter_text_field.condition); - filter.set(YjsDatabaseKey.content, filtersJson.filter_text_field.content); - return filter; -} - -export function withUrlFilter() { - const filter = new Y.Map() as YDatabaseFilter; - - filter.set(YjsDatabaseKey.id, 'filter_url_field'); - filter.set(YjsDatabaseKey.field_id, filtersJson.filter_url_field.field_id); - filter.set(YjsDatabaseKey.condition, filtersJson.filter_url_field.condition); - filter.set(YjsDatabaseKey.content, filtersJson.filter_url_field.content); - return filter; -} - -export function withNumberFilter() { - const filter = new Y.Map() as YDatabaseFilter; - - filter.set(YjsDatabaseKey.id, 'filter_number_field'); - filter.set(YjsDatabaseKey.field_id, filtersJson.filter_number_field.field_id); - filter.set(YjsDatabaseKey.condition, filtersJson.filter_number_field.condition); - filter.set(YjsDatabaseKey.content, filtersJson.filter_number_field.content); - return filter; -} - -export function withCheckboxFilter() { - const filter = new Y.Map() as YDatabaseFilter; - - filter.set(YjsDatabaseKey.id, 'filter_checkbox_field'); - filter.set(YjsDatabaseKey.field_id, filtersJson.filter_checkbox_field.field_id); - filter.set(YjsDatabaseKey.condition, filtersJson.filter_checkbox_field.condition); - filter.set(YjsDatabaseKey.content, ''); - return filter; -} - -export function withChecklistFilter() { - const filter = new Y.Map() as YDatabaseFilter; - - filter.set(YjsDatabaseKey.id, 'filter_checklist_field'); - filter.set(YjsDatabaseKey.field_id, filtersJson.filter_checklist_field.field_id); - filter.set(YjsDatabaseKey.condition, filtersJson.filter_checklist_field.condition); - filter.set(YjsDatabaseKey.content, ''); - return filter; -} - -export function withSingleSelectOptionFilter() { - const filter = new Y.Map() as YDatabaseFilter; - - filter.set(YjsDatabaseKey.id, 'filter_single_select_field'); - filter.set(YjsDatabaseKey.field_id, filtersJson.filter_single_select_field.field_id); - filter.set(YjsDatabaseKey.condition, filtersJson.filter_single_select_field.condition); - filter.set(YjsDatabaseKey.content, filtersJson.filter_single_select_field.content); - return filter; -} - -export function withMultiSelectOptionFilter() { - const filter = new Y.Map() as YDatabaseFilter; - - filter.set(YjsDatabaseKey.id, 'filter_multi_select_field'); - filter.set(YjsDatabaseKey.field_id, filtersJson.filter_multi_select_field.field_id); - filter.set(YjsDatabaseKey.condition, filtersJson.filter_multi_select_field.condition); - filter.set(YjsDatabaseKey.content, filtersJson.filter_multi_select_field.content); - return filter; -} - -export function withDateTimeFilter() { - const filter = new Y.Map() as YDatabaseFilter; - - filter.set(YjsDatabaseKey.id, 'filter_date_field'); - filter.set(YjsDatabaseKey.field_id, filtersJson.filter_date_field.field_id); - filter.set(YjsDatabaseKey.condition, filtersJson.filter_date_field.condition); - filter.set(YjsDatabaseKey.content, filtersJson.filter_date_field.content); - return filter; -} diff --git a/frontend/appflowy_web_app/src/application/database-yjs/__tests__/withTestingRows.ts b/frontend/appflowy_web_app/src/application/database-yjs/__tests__/withTestingRows.ts deleted file mode 100644 index bffaccf28a..0000000000 --- a/frontend/appflowy_web_app/src/application/database-yjs/__tests__/withTestingRows.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { - RowId, - YDatabaseCell, - YDatabaseCells, - YDatabaseRow, - YDoc, - YjsDatabaseKey, - YjsEditorKey, -} from '@/application/types'; -import { FieldType, Row } from '@/application/database-yjs'; -import * as Y from 'yjs'; -import * as rowsJson from './fixtures/rows.json'; - -export function withTestingRows (): Row[] { - return rowsJson.map((row) => { - return { - id: row.id, - height: 37, - }; - }); -} - -export function withTestingRowDataMap (): Record { - const folder: Record = {}; - const rows = withTestingRows(); - - rows.forEach((row, index) => { - const rowDoc = new Y.Doc(); - const rowData = withTestingRowData(row.id, index); - - rowDoc.getMap(YjsEditorKey.data_section).set(YjsEditorKey.database_row, rowData); - folder[row.id] = rowDoc; - }); - - return folder; -} - -export function withTestingRowData (id: string, index: number) { - const rowData = new Y.Map() as YDatabaseRow; - - rowData.set(YjsDatabaseKey.id, id); - rowData.set(YjsDatabaseKey.height, 37); - rowData.set(YjsDatabaseKey.last_modified, Date.now() + index * 1000); - rowData.set(YjsDatabaseKey.created_at, Date.now() + index * 1000); - - const cells = new Y.Map() as YDatabaseCells; - - const textFieldCell = withTestingCell(rowsJson[index].cells.text_field.data); - - textFieldCell.set(YjsDatabaseKey.field_type, Number(FieldType.RichText)); - cells.set('text_field', textFieldCell); - - const numberFieldCell = withTestingCell(rowsJson[index].cells.number_field.data); - - numberFieldCell.set(YjsDatabaseKey.field_type, Number(FieldType.Number)); - cells.set('number_field', numberFieldCell); - - const checkboxFieldCell = withTestingCell(rowsJson[index].cells.checkbox_field.data); - - checkboxFieldCell.set(YjsDatabaseKey.field_type, Number(FieldType.Checkbox)); - cells.set('checkbox_field', checkboxFieldCell); - - const dateTimeFieldCell = withTestingCell(rowsJson[index].cells.date_field.data); - - dateTimeFieldCell.set(YjsDatabaseKey.field_type, Number(FieldType.DateTime)); - cells.set('date_field', dateTimeFieldCell); - - const urlFieldCell = withTestingCell(rowsJson[index].cells.url_field.data); - - urlFieldCell.set(YjsDatabaseKey.field_type, Number(FieldType.URL)); - cells.set('url_field', urlFieldCell); - - const singleSelectFieldCell = withTestingCell(rowsJson[index].cells.single_select_field.data); - - singleSelectFieldCell.set(YjsDatabaseKey.field_type, Number(FieldType.SingleSelect)); - cells.set('single_select_field', singleSelectFieldCell); - - const multiSelectFieldCell = withTestingCell(rowsJson[index].cells.multi_select_field.data); - - multiSelectFieldCell.set(YjsDatabaseKey.field_type, Number(FieldType.MultiSelect)); - cells.set('multi_select_field', multiSelectFieldCell); - - const checlistFieldCell = withTestingCell(rowsJson[index].cells.checklist_field.data); - - checlistFieldCell.set(YjsDatabaseKey.field_type, Number(FieldType.Checklist)); - cells.set('checklist_field', checlistFieldCell); - - rowData.set(YjsDatabaseKey.cells, cells); - return rowData; -} - -export function withTestingCell (cellData: string | number) { - const cell = new Y.Map() as YDatabaseCell; - - cell.set(YjsDatabaseKey.data, cellData); - return cell; -} diff --git a/frontend/appflowy_web_app/src/application/database-yjs/__tests__/withTestingSorts.ts b/frontend/appflowy_web_app/src/application/database-yjs/__tests__/withTestingSorts.ts deleted file mode 100644 index d9421d5e7c..0000000000 --- a/frontend/appflowy_web_app/src/application/database-yjs/__tests__/withTestingSorts.ts +++ /dev/null @@ -1,113 +0,0 @@ -import { YDatabaseSort, YjsDatabaseKey } from '@/application/types'; -import * as Y from 'yjs'; -import * as sortsJson from './fixtures/sorts.json'; - -export function withRichTextSort(isAscending: boolean = true) { - const sort = new Y.Map() as YDatabaseSort; - const sortJSON = isAscending ? sortsJson.sort_asc_text_field : sortsJson.sort_desc_text_field; - - sort.set(YjsDatabaseKey.id, sortJSON.id); - sort.set(YjsDatabaseKey.field_id, sortJSON.field_id); - sort.set(YjsDatabaseKey.condition, sortJSON.condition === 'asc' ? '0' : '1'); - - return sort; -} - -export function withUrlSort(isAscending: boolean = true) { - const sort = new Y.Map() as YDatabaseSort; - const sortJSON = isAscending ? sortsJson.sort_asc_url_field : sortsJson.sort_desc_url_field; - - sort.set(YjsDatabaseKey.id, sortJSON.id); - sort.set(YjsDatabaseKey.field_id, sortJSON.field_id); - sort.set(YjsDatabaseKey.condition, sortJSON.condition === 'asc' ? '0' : '1'); - - return sort; -} - -export function withNumberSort(isAscending: boolean = true) { - const sort = new Y.Map() as YDatabaseSort; - const sortJSON = isAscending ? sortsJson.sort_asc_number_field : sortsJson.sort_desc_number_field; - - sort.set(YjsDatabaseKey.id, sortJSON.id); - sort.set(YjsDatabaseKey.field_id, sortJSON.field_id); - sort.set(YjsDatabaseKey.condition, sortJSON.condition === 'asc' ? '0' : '1'); - - return sort; -} - -export function withCheckboxSort(isAscending: boolean = true) { - const sort = new Y.Map() as YDatabaseSort; - const sortJSON = isAscending ? sortsJson.sort_asc_checkbox_field : sortsJson.sort_desc_checkbox_field; - - sort.set(YjsDatabaseKey.id, sortJSON.id); - sort.set(YjsDatabaseKey.field_id, sortJSON.field_id); - sort.set(YjsDatabaseKey.condition, sortJSON.condition === 'asc' ? '0' : '1'); - - return sort; -} - -export function withDateTimeSort(isAscending: boolean = true) { - const sort = new Y.Map() as YDatabaseSort; - const sortJSON = isAscending ? sortsJson.sort_asc_date_field : sortsJson.sort_desc_date_field; - - sort.set(YjsDatabaseKey.id, sortJSON.id); - sort.set(YjsDatabaseKey.field_id, sortJSON.field_id); - sort.set(YjsDatabaseKey.condition, sortJSON.condition === 'asc' ? '0' : '1'); - - return sort; -} - -export function withSingleSelectOptionSort(isAscending: boolean = true) { - const sort = new Y.Map() as YDatabaseSort; - const sortJSON = isAscending ? sortsJson.sort_asc_single_select_field : sortsJson.sort_desc_single_select_field; - - sort.set(YjsDatabaseKey.id, sortJSON.id); - sort.set(YjsDatabaseKey.field_id, sortJSON.field_id); - sort.set(YjsDatabaseKey.condition, sortJSON.condition === 'asc' ? '0' : '1'); - - return sort; -} - -export function withMultiSelectOptionSort(isAscending: boolean = true) { - const sort = new Y.Map() as YDatabaseSort; - const sortJSON = isAscending ? sortsJson.sort_asc_multi_select_field : sortsJson.sort_desc_multi_select_field; - - sort.set(YjsDatabaseKey.id, sortJSON.id); - sort.set(YjsDatabaseKey.field_id, sortJSON.field_id); - sort.set(YjsDatabaseKey.condition, sortJSON.condition === 'asc' ? '0' : '1'); - - return sort; -} - -export function withChecklistSort(isAscending: boolean = true) { - const sort = new Y.Map() as YDatabaseSort; - const sortJSON = isAscending ? sortsJson.sort_asc_checklist_field : sortsJson.sort_desc_checklist_field; - - sort.set(YjsDatabaseKey.id, sortJSON.id); - sort.set(YjsDatabaseKey.field_id, sortJSON.field_id); - sort.set(YjsDatabaseKey.condition, sortJSON.condition === 'asc' ? '0' : '1'); - - return sort; -} - -export function withCreatedAtSort(isAscending: boolean = true) { - const sort = new Y.Map() as YDatabaseSort; - const sortJSON = isAscending ? sortsJson.sort_asc_created_at : sortsJson.sort_desc_created_at; - - sort.set(YjsDatabaseKey.id, sortJSON.id); - sort.set(YjsDatabaseKey.field_id, sortJSON.field_id); - sort.set(YjsDatabaseKey.condition, sortJSON.condition === 'asc' ? '0' : '1'); - - return sort; -} - -export function withLastModifiedSort(isAscending: boolean = true) { - const sort = new Y.Map() as YDatabaseSort; - const sortJSON = isAscending ? sortsJson.sort_asc_updated_at : sortsJson.sort_desc_updated_at; - - sort.set(YjsDatabaseKey.id, sortJSON.id); - sort.set(YjsDatabaseKey.field_id, sortJSON.field_id); - sort.set(YjsDatabaseKey.condition, sortJSON.condition === 'asc' ? '0' : '1'); - - return sort; -} diff --git a/frontend/appflowy_web_app/src/application/database-yjs/cell.parse.ts b/frontend/appflowy_web_app/src/application/database-yjs/cell.parse.ts deleted file mode 100644 index 97bc5ca10d..0000000000 --- a/frontend/appflowy_web_app/src/application/database-yjs/cell.parse.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { YDatabaseCell, YjsDatabaseKey } from '@/application/types'; -import { FieldType } from '@/application/database-yjs/database.type'; -import { YArray } from 'yjs/dist/src/types/YArray'; -import { Cell, CheckboxCell, DateTimeCell, FileMediaCell, FileMediaCellData } from './cell.type'; - -export function parseYDatabaseCommonCellToCell (cell: YDatabaseCell): Cell { - return { - createdAt: Number(cell.get(YjsDatabaseKey.created_at)), - lastModified: Number(cell.get(YjsDatabaseKey.last_modified)), - fieldType: parseInt(cell.get(YjsDatabaseKey.field_type)) as FieldType, - data: cell.get(YjsDatabaseKey.data), - }; -} - -export function parseYDatabaseCellToCell (cell: YDatabaseCell): Cell { - const fieldType = parseInt(cell.get(YjsDatabaseKey.field_type)); - - if (fieldType === FieldType.DateTime) { - return parseYDatabaseDateTimeCellToCell(cell); - } - - if (fieldType === FieldType.Checkbox) { - return parseYDatabaseCheckboxCellToCell(cell); - } - - if (fieldType === FieldType.FileMedia) { - return parseYDatabaseFileMediaCellToCell(cell); - } - - return parseYDatabaseCommonCellToCell(cell); -} - -export function parseYDatabaseDateTimeCellToCell (cell: YDatabaseCell): DateTimeCell { - return { - ...parseYDatabaseCommonCellToCell(cell), - data: cell.get(YjsDatabaseKey.data) as string, - fieldType: FieldType.DateTime, - endTimestamp: cell.get(YjsDatabaseKey.end_timestamp), - includeTime: cell.get(YjsDatabaseKey.include_time), - isRange: cell.get(YjsDatabaseKey.is_range), - reminderId: cell.get(YjsDatabaseKey.reminder_id), - }; -} - -export function parseYDatabaseFileMediaCellToCell (cell: YDatabaseCell): FileMediaCell { - const data = cell.get(YjsDatabaseKey.data) as YArray; - const dataJson = data.toJSON().map((item: string) => JSON.parse(item)) as FileMediaCellData; - - return { - ...parseYDatabaseCommonCellToCell(cell), - data: dataJson, - fieldType: FieldType.FileMedia, - }; -} - -export function parseYDatabaseCheckboxCellToCell (cell: YDatabaseCell): CheckboxCell { - return { - ...parseYDatabaseCommonCellToCell(cell), - data: cell.get(YjsDatabaseKey.data) === 'Yes', - fieldType: FieldType.Checkbox, - }; -} diff --git a/frontend/appflowy_web_app/src/application/database-yjs/cell.type.ts b/frontend/appflowy_web_app/src/application/database-yjs/cell.type.ts deleted file mode 100644 index 67b991dc03..0000000000 --- a/frontend/appflowy_web_app/src/application/database-yjs/cell.type.ts +++ /dev/null @@ -1,113 +0,0 @@ -import { FieldId, RowId } from '@/application/types'; -import { DateFormat, TimeFormat } from '@/application/database-yjs/index'; -import { FieldType } from '@/application/database-yjs/database.type'; -import React from 'react'; -import { YArray } from 'yjs/dist/src/types/YArray'; - -export interface Cell { - createdAt: number; - lastModified: number; - fieldType: FieldType; - data: unknown; -} - -export interface TextCell extends Cell { - fieldType: FieldType.RichText; - data: string; -} - -export interface NumberCell extends Cell { - fieldType: FieldType.Number; - data: string; -} - -export interface CheckboxCell extends Cell { - fieldType: FieldType.Checkbox; - data: boolean; -} - -export interface UrlCell extends Cell { - fieldType: FieldType.URL; - data: string; -} - -export type SelectionId = string; - -export interface SelectOptionCell extends Cell { - fieldType: FieldType.SingleSelect | FieldType.MultiSelect; - data: SelectionId; -} - -export interface DataTimeTypeOption { - timeFormat: TimeFormat; - dateFormat: DateFormat; -} - -export interface DateTimeCell extends Cell { - fieldType: FieldType.DateTime; - data: string; - endTimestamp?: string; - includeTime?: boolean; - isRange?: boolean; - reminderId?: string; -} - -export enum FileMediaType { - Image = 'Image', - Video = 'Video', - Link = 'Link', - Other = 'Other', -} - -export enum FileMediaUploadType { - CloudMedia = 'CloudMedia', - NetworkMedia = 'NetworkMedia', -} - -export interface FileMediaCellDataItem { - file_type: FileMediaType; - id: string; - name: string; - upload_type: FileMediaUploadType; - url: string; -} - -export type FileMediaCellData = FileMediaCellDataItem[] - -export interface FileMediaCell extends Cell { - fieldType: FieldType.FileMedia; - data: FileMediaCellData; -} - -export interface DateTimeCellData { - date?: string; - time?: string; - timestamp?: number; - includeTime?: boolean; - endDate?: string; - endTime?: string; - endTimestamp?: number; - isRange?: boolean; -} - -export interface ChecklistCell extends Cell { - fieldType: FieldType.Checklist; - data: string; -} - -export interface RelationCell extends Cell { - fieldType: FieldType.Relation; - data: YArray; -} - -export type RelationCellData = RowId[]; - -export interface CellProps { - cell?: T; - rowId: string; - fieldId: FieldId; - style?: React.CSSProperties; - readOnly?: boolean; - placeholder?: string; - className?: string; -} diff --git a/frontend/appflowy_web_app/src/application/database-yjs/const.ts b/frontend/appflowy_web_app/src/application/database-yjs/const.ts deleted file mode 100644 index 12deaaa218..0000000000 --- a/frontend/appflowy_web_app/src/application/database-yjs/const.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { RowId, YDatabaseRow, YDoc, YjsDatabaseKey, YjsEditorKey } from '@/application/types'; -import { RowMetaKey } from '@/application/database-yjs/database.type'; -import { v5 as uuidv5, parse as uuidParse } from 'uuid'; - -export const DEFAULT_ROW_HEIGHT = 36; -export const MIN_COLUMN_WIDTH = 150; - -export const getCell = (rowId: string, fieldId: string, rowMetas: Record) => { - const rowMeta = rowMetas[rowId]; - - const meta = rowMeta?.getMap(YjsEditorKey.data_section).get(YjsEditorKey.database_row) as YDatabaseRow; - - return meta?.get(YjsDatabaseKey.cells)?.get(fieldId); -}; - -export const getCellData = (rowId: string, fieldId: string, rowMetas: Record) => { - return getCell(rowId, fieldId, rowMetas)?.get(YjsDatabaseKey.data); -}; - -export const metaIdFromRowId = (rowId: string) => { - let namespace: Uint8Array; - - try { - namespace = uuidParse(rowId); - } catch (e) { - namespace = uuidParse(generateUUID()); - } - - return (key: RowMetaKey) => uuidv5(key, namespace).toString(); -}; - -export const generateUUID = () => uuidv5(Date.now().toString(), uuidv5.URL); diff --git a/frontend/appflowy_web_app/src/application/database-yjs/context.ts b/frontend/appflowy_web_app/src/application/database-yjs/context.ts deleted file mode 100644 index 0125e0f5c3..0000000000 --- a/frontend/appflowy_web_app/src/application/database-yjs/context.ts +++ /dev/null @@ -1,102 +0,0 @@ -import { - CreateRowDoc, - LoadView, - LoadViewMeta, RowId, - YDatabase, - YDatabaseRow, - YDoc, - YjsDatabaseKey, - YjsEditorKey, -} from '@/application/types'; -import { createContext, useContext } from 'react'; - -export interface DatabaseContextState { - readOnly: boolean; - databaseDoc: YDoc; - iidIndex: string; - viewId: string; - rowDocMap: Record | null; - isDatabaseRowPage?: boolean; - scrollLeft?: number; - isDocumentBlock?: boolean; - navigateToRow?: (rowId: string) => void; - loadView?: LoadView; - createRowDoc?: CreateRowDoc; - loadViewMeta?: LoadViewMeta; - navigateToView?: (viewId: string, blockId?: string) => Promise; - onRendered?: (height: number) => void; - showActions?: boolean; -} - -export const DatabaseContext = createContext(null); - -export const useDatabaseContext = () => { - const context = useContext(DatabaseContext); - - if (!context) { - throw new Error('DatabaseContext is not provided'); - } - - return context; -}; - -export const useDatabase = () => { - const database = useDatabaseContext() - .databaseDoc?.getMap(YjsEditorKey.data_section) - .get(YjsEditorKey.database) as YDatabase; - - return database; -}; - -export const useNavigateToRow = () => { - return useDatabaseContext().navigateToRow; -}; - -export const useRowDocMap = () => { - return useDatabaseContext().rowDocMap; -}; - -export const useIsDatabaseRowPage = () => { - return useDatabaseContext().isDatabaseRowPage; -}; - -export const useRow = (rowId: string) => { - const rows = useRowDocMap(); - - return rows?.[rowId]?.getMap(YjsEditorKey.data_section); -}; - -export const useRowData = (rowId: string) => { - return useRow(rowId)?.get(YjsEditorKey.database_row) as YDatabaseRow; -}; - -export const useDatabaseViewId = () => { - const context = useDatabaseContext(); - - return context?.viewId; -}; - -export const useReadOnly = () => { - const context = useDatabaseContext(); - - return context?.readOnly || true; -}; - -export const useDatabaseView = () => { - const database = useDatabase(); - const viewId = useDatabaseViewId(); - - return viewId ? database?.get(YjsDatabaseKey.views)?.get(viewId) : undefined; -}; - -export function useDatabaseFields () { - const database = useDatabase(); - - return database.get(YjsDatabaseKey.fields); -} - -export const useDatabaseSelectedView = (viewId: string) => { - const database = useDatabase(); - - return database.get(YjsDatabaseKey.views).get(viewId); -}; \ No newline at end of file diff --git a/frontend/appflowy_web_app/src/application/database-yjs/database.type.ts b/frontend/appflowy_web_app/src/application/database-yjs/database.type.ts deleted file mode 100644 index c73ceb7bef..0000000000 --- a/frontend/appflowy_web_app/src/application/database-yjs/database.type.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { FieldId } from '@/application/types'; - -export enum FieldVisibility { - AlwaysShown = 0, - HideWhenEmpty = 1, - AlwaysHidden = 2, -} - -export enum FieldType { - RichText = 0, - Number = 1, - DateTime = 2, - SingleSelect = 3, - MultiSelect = 4, - Checkbox = 5, - URL = 6, - Checklist = 7, - LastEditedTime = 8, - CreatedTime = 9, - Relation = 10, - AISummaries = 11, - AITranslations = 12, - FileMedia = 14 -} - -export enum CalculationType { - Average = 0, - Max = 1, - Median = 2, - Min = 3, - Sum = 4, - Count = 5, - CountEmpty = 6, - CountNonEmpty = 7, -} - -export enum SortCondition { - Ascending = 0, - Descending = 1, -} - -export enum FilterType { - Data = 0, - And = 1, - Or = 2, -} - -export interface Filter { - fieldId: FieldId; - filterType: FilterType; - condition: number; - id: string; - content: string; -} - -export enum CalendarLayout { - MonthLayout = 0, - WeekLayout = 1, - DayLayout = 2, -} - -export interface CalendarLayoutSetting { - fieldId: string; - firstDayOfWeek: number; - showWeekNumbers: boolean; - showWeekends: boolean; - layout: CalendarLayout; -} - -export enum RowMetaKey { - DocumentId = 'document_id', - IconId = 'icon_id', - CoverId = 'cover_id', - IsDocumentEmpty = 'is_document_empty', -} diff --git a/frontend/appflowy_web_app/src/application/database-yjs/fields/checkbox/checkbox.type.ts b/frontend/appflowy_web_app/src/application/database-yjs/fields/checkbox/checkbox.type.ts deleted file mode 100644 index b9da4341f6..0000000000 --- a/frontend/appflowy_web_app/src/application/database-yjs/fields/checkbox/checkbox.type.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Filter } from '@/application/database-yjs'; - -export enum CheckboxFilterCondition { - IsChecked = 0, - IsUnChecked = 1, -} - -export interface CheckboxFilter extends Filter { - condition: CheckboxFilterCondition; -} diff --git a/frontend/appflowy_web_app/src/application/database-yjs/fields/checkbox/index.ts b/frontend/appflowy_web_app/src/application/database-yjs/fields/checkbox/index.ts deleted file mode 100644 index 9ccd409dc8..0000000000 --- a/frontend/appflowy_web_app/src/application/database-yjs/fields/checkbox/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './checkbox.type'; diff --git a/frontend/appflowy_web_app/src/application/database-yjs/fields/checklist/checklist.type.ts b/frontend/appflowy_web_app/src/application/database-yjs/fields/checklist/checklist.type.ts deleted file mode 100644 index 2b504ded8a..0000000000 --- a/frontend/appflowy_web_app/src/application/database-yjs/fields/checklist/checklist.type.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Filter } from '@/application/database-yjs'; - -export enum ChecklistFilterCondition { - IsComplete = 0, - IsIncomplete = 1, -} - -export interface ChecklistFilter extends Filter { - condition: ChecklistFilterCondition; -} diff --git a/frontend/appflowy_web_app/src/application/database-yjs/fields/checklist/index.ts b/frontend/appflowy_web_app/src/application/database-yjs/fields/checklist/index.ts deleted file mode 100644 index 15d37f912b..0000000000 --- a/frontend/appflowy_web_app/src/application/database-yjs/fields/checklist/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './checklist.type'; -export * from './parse'; diff --git a/frontend/appflowy_web_app/src/application/database-yjs/fields/checklist/parse.ts b/frontend/appflowy_web_app/src/application/database-yjs/fields/checklist/parse.ts deleted file mode 100644 index c93fee7a38..0000000000 --- a/frontend/appflowy_web_app/src/application/database-yjs/fields/checklist/parse.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { SelectOption } from '../select-option'; - -export interface ChecklistCellData { - selectedOptionIds?: string[]; - options?: SelectOption[]; - percentage: number; -} - -export function parseChecklistData(data: string): ChecklistCellData | null { - try { - const { options, selected_option_ids } = JSON.parse(data); - const percentage = selected_option_ids.length / options.length; - - return { - percentage, - options, - selectedOptionIds: selected_option_ids, - }; - } catch (e) { - return null; - } -} diff --git a/frontend/appflowy_web_app/src/application/database-yjs/fields/date/date.type.ts b/frontend/appflowy_web_app/src/application/database-yjs/fields/date/date.type.ts deleted file mode 100644 index 0db15f21eb..0000000000 --- a/frontend/appflowy_web_app/src/application/database-yjs/fields/date/date.type.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { Filter } from '@/application/database-yjs'; - -export enum TimeFormat { - TwelveHour = 0, - TwentyFourHour = 1, -} - -export enum DateFormat { - Local = 0, - US = 1, - ISO = 2, - Friendly = 3, - DayMonthYear = 4, -} - -export enum DateFilterCondition { - DateIs = 0, - DateBefore = 1, - DateAfter = 2, - DateOnOrBefore = 3, - DateOnOrAfter = 4, - DateWithIn = 5, - DateIsEmpty = 6, - DateIsNotEmpty = 7, -} - -export interface DateFilter extends Filter { - condition: DateFilterCondition; - start?: number; - end?: number; - timestamp?: number; -} diff --git a/frontend/appflowy_web_app/src/application/database-yjs/fields/date/index.ts b/frontend/appflowy_web_app/src/application/database-yjs/fields/date/index.ts deleted file mode 100644 index 106279c949..0000000000 --- a/frontend/appflowy_web_app/src/application/database-yjs/fields/date/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './date.type'; -export * from './utils'; diff --git a/frontend/appflowy_web_app/src/application/database-yjs/fields/date/utils.test.ts b/frontend/appflowy_web_app/src/application/database-yjs/fields/date/utils.test.ts deleted file mode 100644 index 9d3821ba1c..0000000000 --- a/frontend/appflowy_web_app/src/application/database-yjs/fields/date/utils.test.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { getTimeFormat, getDateFormat } from './utils'; -import { expect } from '@jest/globals'; -import { DateFormat, TimeFormat } from '@/application/database-yjs'; - -describe('DateFormat', () => { - it('should return time format', () => { - expect(getTimeFormat(TimeFormat.TwelveHour)).toEqual('h:mm A'); - expect(getTimeFormat(TimeFormat.TwentyFourHour)).toEqual('HH:mm'); - expect(getTimeFormat(56)).toEqual('HH:mm'); - }); - - it('should return date format', () => { - expect(getDateFormat(DateFormat.US)).toEqual('YYYY/MM/DD'); - expect(getDateFormat(DateFormat.ISO)).toEqual('YYYY-MM-DD'); - expect(getDateFormat(DateFormat.Friendly)).toEqual('MMM DD, YYYY'); - expect(getDateFormat(DateFormat.Local)).toEqual('MM/DD/YYYY'); - expect(getDateFormat(DateFormat.DayMonthYear)).toEqual('DD/MM/YYYY'); - - expect(getDateFormat(56)).toEqual('YYYY-MM-DD'); - }); -}); diff --git a/frontend/appflowy_web_app/src/application/database-yjs/fields/date/utils.ts b/frontend/appflowy_web_app/src/application/database-yjs/fields/date/utils.ts deleted file mode 100644 index 985402768b..0000000000 --- a/frontend/appflowy_web_app/src/application/database-yjs/fields/date/utils.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { TimeFormat, DateFormat } from '@/application/database-yjs'; - -export function getTimeFormat(timeFormat?: TimeFormat) { - switch (timeFormat) { - case TimeFormat.TwelveHour: - return 'h:mm A'; - case TimeFormat.TwentyFourHour: - return 'HH:mm'; - default: - return 'HH:mm'; - } -} - -export function getDateFormat(dateFormat?: DateFormat) { - switch (dateFormat) { - case DateFormat.Friendly: - return 'MMM DD, YYYY'; - case DateFormat.ISO: - return 'YYYY-MM-DD'; - case DateFormat.US: - return 'YYYY/MM/DD'; - case DateFormat.Local: - return 'MM/DD/YYYY'; - case DateFormat.DayMonthYear: - return 'DD/MM/YYYY'; - default: - return 'YYYY-MM-DD'; - } -} diff --git a/frontend/appflowy_web_app/src/application/database-yjs/fields/index.ts b/frontend/appflowy_web_app/src/application/database-yjs/fields/index.ts deleted file mode 100644 index 5505f0e4ed..0000000000 --- a/frontend/appflowy_web_app/src/application/database-yjs/fields/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -export * from './type_option'; -export * from './date'; -export * from './number'; -export * from './select-option'; -export * from './text'; -export * from './checkbox'; -export * from './checklist'; -export * from './relation'; diff --git a/frontend/appflowy_web_app/src/application/database-yjs/fields/number/__tests__/format.test.ts b/frontend/appflowy_web_app/src/application/database-yjs/fields/number/__tests__/format.test.ts deleted file mode 100644 index f80b1db220..0000000000 --- a/frontend/appflowy_web_app/src/application/database-yjs/fields/number/__tests__/format.test.ts +++ /dev/null @@ -1,628 +0,0 @@ -import { currencyFormaterMap } from '../format'; -import { NumberFormat } from '../number.type'; -import { expect } from '@jest/globals'; - -const testCases = [0, 1, 0.5, 0.5666, 1000, 10000, 1000000, 10000000, 1000000.0]; -describe('currencyFormaterMap', () => { - test('should return the correct formatter for Num', () => { - const formater = currencyFormaterMap[NumberFormat.Num]; - const result = ['0', '1', '0.5', '0.5666', '1,000', '10,000', '1,000,000', '10,000,000', '1,000,000']; - testCases.forEach((testCase) => { - expect(formater(testCase)).toBe(result[testCases.indexOf(testCase)]); - }); - }); - - test('should return the correct formatter for Percent', () => { - const formater = currencyFormaterMap[NumberFormat.Percent]; - const result = ['0%', '1%', '0.5%', '0.57%', '1,000%', '10,000%', '1,000,000%', '10,000,000%', '1,000,000%']; - testCases.forEach((testCase, index) => { - expect(formater(testCase)).toBe(result[index]); - }); - }); - - test('should return the correct formatter for USD', () => { - const formater = currencyFormaterMap[NumberFormat.USD]; - const result = ['$0', '$1', '$0.5', '$0.57', '$1,000', '$10,000', '$1,000,000', '$10,000,000', '$1,000,000']; - - testCases.forEach((testCase, index) => { - expect(formater(testCase)).toBe(result[index]); - }); - }); - - test('should return the correct formatter for CanadianDollar', () => { - const formater = currencyFormaterMap[NumberFormat.CanadianDollar]; - const result = [ - 'CA$0', - 'CA$1', - 'CA$0.5', - 'CA$0.57', - 'CA$1,000', - 'CA$10,000', - 'CA$1,000,000', - 'CA$10,000,000', - 'CA$1,000,000', - ]; - testCases.forEach((testCase, index) => { - expect(formater(testCase)).toBe(result[index]); - }); - }); - - test('should return the correct formatter for EUR', () => { - const formater = currencyFormaterMap[NumberFormat.EUR]; - - const result = ['€0', '€1', '€0,5', '€0,57', '€1.000', '€10.000', '€1.000.000', '€10.000.000', '€1.000.000']; - - testCases.forEach((testCase, index) => { - expect(formater(testCase)).toBe(result[index]); - }); - }); - - test('should return the correct formatter for Pound', () => { - const formater = currencyFormaterMap[NumberFormat.Pound]; - - const result = ['£0', '£1', '£0.5', '£0.57', '£1,000', '£10,000', '£1,000,000', '£10,000,000', '£1,000,000']; - - testCases.forEach((testCase, index) => { - expect(formater(testCase)).toBe(result[index]); - }); - }); - - test('should return the correct formatter for Yen', () => { - const formater = currencyFormaterMap[NumberFormat.Yen]; - - const result = [ - '¥0', - '¥1', - '¥0.5', - '¥0.57', - '¥1,000', - '¥10,000', - '¥1,000,000', - '¥10,000,000', - '¥1,000,000', - ]; - - testCases.forEach((testCase, index) => { - expect(formater(testCase)).toBe(result[index]); - }); - }); - - test('should return the correct formatter for Ruble', () => { - const formater = currencyFormaterMap[NumberFormat.Ruble]; - - const result = [ - '0 RUB', - '1 RUB', - '0,5 RUB', - '0,57 RUB', - '1 000 RUB', - '10 000 RUB', - '1 000 000 RUB', - '10 000 000 RUB', - '1 000 000 RUB', - ]; - - testCases.forEach((testCase, index) => { - expect(formater(testCase)).toBe(result[index]); - }); - }); - - test('should return the correct formatter for Rupee', () => { - const formater = currencyFormaterMap[NumberFormat.Rupee]; - - const result = ['₹0', '₹1', '₹0.5', '₹0.57', '₹1,000', '₹10,000', '₹10,00,000', '₹1,00,00,000', '₹10,00,000']; - - testCases.forEach((testCase, index) => { - expect(formater(testCase)).toBe(result[index]); - }); - }); - - test('should return the correct formatter for Won', () => { - const formater = currencyFormaterMap[NumberFormat.Won]; - - const result = ['₩0', '₩1', '₩0.5', '₩0.57', '₩1,000', '₩10,000', '₩1,000,000', '₩10,000,000', '₩1,000,000']; - - testCases.forEach((testCase, index) => { - expect(formater(testCase)).toBe(result[index]); - }); - }); - - test('should return the correct formatter for Yuan', () => { - const formater = currencyFormaterMap[NumberFormat.Yuan]; - - const result = [ - 'CN¥0', - 'CN¥1', - 'CN¥0.5', - 'CN¥0.57', - 'CN¥1,000', - 'CN¥10,000', - 'CN¥1,000,000', - 'CN¥10,000,000', - 'CN¥1,000,000', - ]; - - testCases.forEach((testCase, index) => { - expect(formater(testCase)).toBe(result[index]); - }); - }); - - test('should return the correct formatter for Real', () => { - const formater = currencyFormaterMap[NumberFormat.Real]; - - const result = [ - 'R$ 0', - 'R$ 1', - 'R$ 0,5', - 'R$ 0,57', - 'R$ 1.000', - 'R$ 10.000', - 'R$ 1.000.000', - 'R$ 10.000.000', - 'R$ 1.000.000', - ]; - - testCases.forEach((testCase, index) => { - expect(formater(testCase)).toBe(result[index]); - }); - }); - - test('should return the correct formatter for Lira', () => { - const formater = currencyFormaterMap[NumberFormat.Lira]; - - const result = [ - 'TRY 0', - 'TRY 1', - 'TRY 0,5', - 'TRY 0,57', - 'TRY 1.000', - 'TRY 10.000', - 'TRY 1.000.000', - 'TRY 10.000.000', - 'TRY 1.000.000', - ]; - - testCases.forEach((testCase, index) => { - expect(formater(testCase)).toBe(result[index]); - }); - }); - - test('should return the correct formatter for Rupiah', () => { - const formater = currencyFormaterMap[NumberFormat.Rupiah]; - - const result = [ - 'IDR 0', - 'IDR 1', - 'IDR 0,5', - 'IDR 0,57', - 'IDR 1.000', - 'IDR 10.000', - 'IDR 1.000.000', - 'IDR 10.000.000', - 'IDR 1.000.000', - ]; - - testCases.forEach((testCase, index) => { - expect(formater(testCase)).toBe(result[index]); - }); - }); - - test('should return the correct formatter for Franc', () => { - const formater = currencyFormaterMap[NumberFormat.Franc]; - - const result = [ - 'CHF 0', - 'CHF 1', - 'CHF 0.5', - 'CHF 0.57', - `CHF 1’000`, - `CHF 10’000`, - `CHF 1’000’000`, - `CHF 10’000’000`, - `CHF 1’000’000`, - ]; - - testCases.forEach((testCase, index) => { - expect(formater(testCase)).toBe(result[index]); - }); - }); - - test('should return the correct formatter for HongKongDollar', () => { - const formater = currencyFormaterMap[NumberFormat.HongKongDollar]; - - const result = [ - 'HK$0', - 'HK$1', - 'HK$0.5', - 'HK$0.57', - 'HK$1,000', - 'HK$10,000', - 'HK$1,000,000', - 'HK$10,000,000', - 'HK$1,000,000', - ]; - - testCases.forEach((testCase, index) => { - expect(formater(testCase)).toBe(result[index]); - }); - }); - - test('should return the correct formatter for NewZealandDollar', () => { - const formater = currencyFormaterMap[NumberFormat.NewZealandDollar]; - - const result = [ - 'NZ$0', - 'NZ$1', - 'NZ$0.5', - 'NZ$0.57', - 'NZ$1,000', - 'NZ$10,000', - 'NZ$1,000,000', - 'NZ$10,000,000', - 'NZ$1,000,000', - ]; - - testCases.forEach((testCase, index) => { - expect(formater(testCase)).toBe(result[index]); - }); - }); - - test('should return the correct formatter for Krona', () => { - const formater = currencyFormaterMap[NumberFormat.Krona]; - - const result = [ - '0 SEK', - '1 SEK', - '0,5 SEK', - '0,57 SEK', - '1 000 SEK', - '10 000 SEK', - '1 000 000 SEK', - '10 000 000 SEK', - '1 000 000 SEK', - ]; - - testCases.forEach((testCase, index) => { - expect(formater(testCase)).toBe(result[index]); - }); - }); - test('should return the correct formatter for NorwegianKrone', () => { - const formater = currencyFormaterMap[NumberFormat.NorwegianKrone]; - - const result = [ - 'NOK 0', - 'NOK 1', - 'NOK 0,5', - 'NOK 0,57', - 'NOK 1 000', - 'NOK 10 000', - 'NOK 1 000 000', - 'NOK 10 000 000', - 'NOK 1 000 000', - ]; - - testCases.forEach((testCase, index) => { - expect(formater(testCase)).toBe(result[index]); - }); - }); - - test('should return the correct formatter for MexicanPeso', () => { - const formater = currencyFormaterMap[NumberFormat.MexicanPeso]; - - const result = [ - 'MX$0', - 'MX$1', - 'MX$0.5', - 'MX$0.57', - 'MX$1,000', - 'MX$10,000', - 'MX$1,000,000', - 'MX$10,000,000', - 'MX$1,000,000', - ]; - - testCases.forEach((testCase, index) => { - expect(formater(testCase)).toBe(result[index]); - }); - }); - - test('should return the correct formatter for Rand', () => { - const formater = currencyFormaterMap[NumberFormat.Rand]; - - const result = [ - 'ZAR 0', - 'ZAR 1', - 'ZAR 0,5', - 'ZAR 0,57', - 'ZAR 1 000', - 'ZAR 10 000', - 'ZAR 1 000 000', - 'ZAR 10 000 000', - 'ZAR 1 000 000', - ]; - testCases.forEach((testCase, index) => { - expect(formater(testCase)).toBe(result[index]); - }); - }); - - test('should return the correct formatter for NewTaiwanDollar', () => { - const formater = currencyFormaterMap[NumberFormat.NewTaiwanDollar]; - - const result = [ - 'NT$0', - 'NT$1', - 'NT$0.5', - 'NT$0.57', - 'NT$1,000', - 'NT$10,000', - 'NT$1,000,000', - 'NT$10,000,000', - 'NT$1,000,000', - ]; - - testCases.forEach((testCase, index) => { - expect(formater(testCase)).toBe(result[index]); - }); - }); - - test('should return the correct formatter for DanishKrone', () => { - const formater = currencyFormaterMap[NumberFormat.DanishKrone]; - - const result = [ - '0 DKK', - '1 DKK', - '0,5 DKK', - '0,57 DKK', - '1.000 DKK', - '10.000 DKK', - '1.000.000 DKK', - '10.000.000 DKK', - '1.000.000 DKK', - ]; - - testCases.forEach((testCase, index) => { - expect(formater(testCase)).toBe(result[index]); - }); - }); - test('should return the correct formatter for Baht', () => { - const formater = currencyFormaterMap[NumberFormat.Baht]; - - const result = [ - 'THB 0', - 'THB 1', - 'THB 0.5', - 'THB 0.57', - 'THB 1,000', - 'THB 10,000', - 'THB 1,000,000', - 'THB 10,000,000', - 'THB 1,000,000', - ]; - - testCases.forEach((testCase, index) => { - expect(formater(testCase)).toBe(result[index]); - }); - }); - test('should return the correct formatter for Forint', () => { - const formater = currencyFormaterMap[NumberFormat.Forint]; - - const result = [ - '0 HUF', - '1 HUF', - '0,5 HUF', - '0,57 HUF', - '1 000 HUF', - '10 000 HUF', - '1 000 000 HUF', - '10 000 000 HUF', - '1 000 000 HUF', - ]; - - testCases.forEach((testCase, index) => { - expect(formater(testCase)).toBe(result[index]); - }); - }); - - test('should return the correct formatter for Koruna', () => { - const formater = currencyFormaterMap[NumberFormat.Koruna]; - - const result = [ - '0 CZK', - '1 CZK', - '0,5 CZK', - '0,57 CZK', - '1 000 CZK', - '10 000 CZK', - '1 000 000 CZK', - '10 000 000 CZK', - '1 000 000 CZK', - ]; - - testCases.forEach((testCase, index) => { - expect(formater(testCase)).toBe(result[index]); - }); - }); - - test('should return the correct formatter for Shekel', () => { - const formater = currencyFormaterMap[NumberFormat.Shekel]; - - const result = [ - '‏0 ‏₪', - '‏1 ‏₪', - '‏0.5 ‏₪', - '‏0.57 ‏₪', - '‏1,000 ‏₪', - '‏10,000 ‏₪', - '‏1,000,000 ‏₪', - '‏10,000,000 ‏₪', - '‏1,000,000 ‏₪', - ]; - - testCases.forEach((testCase, index) => { - expect(formater(testCase)).toBe(result[index]); - }); - }); - test('should return the correct formatter for ChileanPeso', () => { - const formater = currencyFormaterMap[NumberFormat.ChileanPeso]; - - const result = [ - 'CLP 0', - 'CLP 1', - 'CLP 0,5', - 'CLP 0,57', - 'CLP 1.000', - 'CLP 10.000', - 'CLP 1.000.000', - 'CLP 10.000.000', - 'CLP 1.000.000', - ]; - - testCases.forEach((testCase, index) => { - expect(formater(testCase)).toBe(result[index]); - }); - }); - test('should return the correct formatter for PhilippinePeso', () => { - const formater = currencyFormaterMap[NumberFormat.PhilippinePeso]; - - const result = ['₱0', '₱1', '₱0.5', '₱0.57', '₱1,000', '₱10,000', '₱1,000,000', '₱10,000,000', '₱1,000,000']; - testCases.forEach((testCase, index) => { - expect(formater(testCase)).toBe(result[index]); - }); - }); - test('should return the correct formatter for Dirham', () => { - const formater = currencyFormaterMap[NumberFormat.Dirham]; - - const result = [ - '‏0 AED', - '‏1 AED', - '‏0.5 AED', - '‏0.57 AED', - '‏1,000 AED', - '‏10,000 AED', - '‏1,000,000 AED', - '‏10,000,000 AED', - '‏1,000,000 AED', - ]; - testCases.forEach((testCase, index) => { - expect(formater(testCase)).toBe(result[index]); - }); - }); - test('should return the correct formatter for ColombianPeso', () => { - const formater = currencyFormaterMap[NumberFormat.ColombianPeso]; - - const result = [ - 'COP 0', - 'COP 1', - 'COP 0,5', - 'COP 0,57', - 'COP 1.000', - 'COP 10.000', - 'COP 1.000.000', - 'COP 10.000.000', - 'COP 1.000.000', - ]; - - testCases.forEach((testCase, index) => { - expect(formater(testCase)).toBe(result[index]); - }); - }); - test('should return the correct formatter for Riyal', () => { - const formater = currencyFormaterMap[NumberFormat.Riyal]; - - const result = [ - 'SAR 0', - 'SAR 1', - 'SAR 0.5', - 'SAR 0.57', - 'SAR 1,000', - 'SAR 10,000', - 'SAR 1,000,000', - 'SAR 10,000,000', - 'SAR 1,000,000', - ]; - - testCases.forEach((testCase, index) => { - expect(formater(testCase)).toBe(result[index]); - }); - }); - - test('should return the correct formatter for Ringgit', () => { - const formater = currencyFormaterMap[NumberFormat.Ringgit]; - - const result = [ - 'RM 0', - 'RM 1', - 'RM 0.5', - 'RM 0.57', - 'RM 1,000', - 'RM 10,000', - 'RM 1,000,000', - 'RM 10,000,000', - 'RM 1,000,000', - ]; - - testCases.forEach((testCase, index) => { - expect(formater(testCase)).toBe(result[index]); - }); - }); - - test('should return the correct formatter for Leu', () => { - const formater = currencyFormaterMap[NumberFormat.Leu]; - - const result = [ - '0 RON', - '1 RON', - '0,5 RON', - '0,57 RON', - '1.000 RON', - '10.000 RON', - '1.000.000 RON', - '10.000.000 RON', - '1.000.000 RON', - ]; - - testCases.forEach((testCase, index) => { - expect(formater(testCase)).toBe(result[index]); - }); - }); - - test('should return the correct formatter for ArgentinePeso', () => { - const formater = currencyFormaterMap[NumberFormat.ArgentinePeso]; - - const result = [ - 'ARS 0', - 'ARS 1', - 'ARS 0,5', - 'ARS 0,57', - 'ARS 1.000', - 'ARS 10.000', - 'ARS 1.000.000', - 'ARS 10.000.000', - 'ARS 1.000.000', - ]; - - testCases.forEach((testCase, index) => { - expect(formater(testCase)).toBe(result[index]); - }); - }); - - test('should return the correct formatter for UruguayanPeso', () => { - const formater = currencyFormaterMap[NumberFormat.UruguayanPeso]; - - const result = [ - 'UYU 0', - 'UYU 1', - 'UYU 0,5', - 'UYU 0,57', - 'UYU 1.000', - 'UYU 10.000', - 'UYU 1.000.000', - 'UYU 10.000.000', - 'UYU 1.000.000', - ]; - - testCases.forEach((testCase, index) => { - expect(formater(testCase)).toBe(result[index]); - }); - }); -}); diff --git a/frontend/appflowy_web_app/src/application/database-yjs/fields/number/format.ts b/frontend/appflowy_web_app/src/application/database-yjs/fields/number/format.ts deleted file mode 100644 index 61e0942b01..0000000000 --- a/frontend/appflowy_web_app/src/application/database-yjs/fields/number/format.ts +++ /dev/null @@ -1,236 +0,0 @@ -import { NumberFormat } from './number.type'; - -const commonProps = { - minimumFractionDigits: 0, - maximumFractionDigits: 2, - style: 'currency', - currencyDisplay: 'symbol', - useGrouping: true, -}; - -export const currencyFormaterMap: Record string> = { - [NumberFormat.Num]: (n: number) => - new Intl.NumberFormat('en-US', { - style: 'decimal', - minimumFractionDigits: 0, - maximumFractionDigits: 20, - }).format(n), - [NumberFormat.Percent]: (n: number) => - new Intl.NumberFormat('en-US', { - ...commonProps, - style: 'decimal', - }).format(n) + '%', - [NumberFormat.USD]: (n: number) => - new Intl.NumberFormat('en-US', { - ...commonProps, - currency: 'USD', - }).format(n), - [NumberFormat.CanadianDollar]: (n: number) => - new Intl.NumberFormat('en-CA', { - ...commonProps, - currency: 'CAD', - }) - .format(n) - .replace('$', 'CA$'), - [NumberFormat.EUR]: (n: number) => { - const formattedAmount = new Intl.NumberFormat('de-DE', { - ...commonProps, - currency: 'EUR', - }) - .format(n) - .replace('€', '') - .trim(); - - return `€${formattedAmount}`; - }, - - [NumberFormat.Pound]: (n: number) => - new Intl.NumberFormat('en-GB', { - ...commonProps, - currency: 'GBP', - }).format(n), - [NumberFormat.Yen]: (n: number) => - new Intl.NumberFormat('ja-JP', { - ...commonProps, - currency: 'JPY', - }).format(n), - [NumberFormat.Ruble]: (n: number) => - new Intl.NumberFormat('ru-RU', { - ...commonProps, - currency: 'RUB', - currencyDisplay: 'code', - }) - .format(n) - .replaceAll(' ', ' '), - [NumberFormat.Rupee]: (n: number) => - new Intl.NumberFormat('hi-IN', { - ...commonProps, - currency: 'INR', - }).format(n), - [NumberFormat.Won]: (n: number) => - new Intl.NumberFormat('ko-KR', { - ...commonProps, - currency: 'KRW', - }).format(n), - [NumberFormat.Yuan]: (n: number) => - new Intl.NumberFormat('zh-CN', { - ...commonProps, - currency: 'CNY', - }) - .format(n) - .replace('¥', 'CN¥'), - [NumberFormat.Real]: (n: number) => - new Intl.NumberFormat('pt-BR', { - ...commonProps, - currency: 'BRL', - }) - .format(n) - .replaceAll(' ', ' '), - [NumberFormat.Lira]: (n: number) => - new Intl.NumberFormat('tr-TR', { - ...commonProps, - currency: 'TRY', - currencyDisplay: 'code', - }) - .format(n) - .replaceAll(' ', ' '), - [NumberFormat.Rupiah]: (n: number) => - new Intl.NumberFormat('id-ID', { - ...commonProps, - currency: 'IDR', - currencyDisplay: 'code', - }) - .format(n) - .replaceAll(' ', ' '), - [NumberFormat.Franc]: (n: number) => - new Intl.NumberFormat('de-CH', { - ...commonProps, - currency: 'CHF', - }) - .format(n) - .replaceAll(' ', ' '), - [NumberFormat.HongKongDollar]: (n: number) => - new Intl.NumberFormat('zh-HK', { - ...commonProps, - currency: 'HKD', - }).format(n), - [NumberFormat.NewZealandDollar]: (n: number) => - new Intl.NumberFormat('en-NZ', { - ...commonProps, - currency: 'NZD', - }) - .format(n) - .replace('$', 'NZ$'), - [NumberFormat.Krona]: (n: number) => - new Intl.NumberFormat('sv-SE', { - ...commonProps, - currency: 'SEK', - currencyDisplay: 'code', - }).format(n), - [NumberFormat.NorwegianKrone]: (n: number) => - new Intl.NumberFormat('nb-NO', { - ...commonProps, - currency: 'NOK', - currencyDisplay: 'code', - }).format(n), - [NumberFormat.MexicanPeso]: (n: number) => - new Intl.NumberFormat('es-MX', { - ...commonProps, - currency: 'MXN', - }) - .format(n) - .replace('$', 'MX$'), - [NumberFormat.Rand]: (n: number) => - new Intl.NumberFormat('en-ZA', { - ...commonProps, - currency: 'ZAR', - currencyDisplay: 'code', - }).format(n), - [NumberFormat.NewTaiwanDollar]: (n: number) => - new Intl.NumberFormat('zh-TW', { - ...commonProps, - currency: 'TWD', - }) - .format(n) - .replace('$', 'NT$'), - [NumberFormat.DanishKrone]: (n: number) => - new Intl.NumberFormat('da-DK', { - ...commonProps, - currency: 'DKK', - currencyDisplay: 'code', - }).format(n), - [NumberFormat.Baht]: (n: number) => - new Intl.NumberFormat('th-TH', { - ...commonProps, - currency: 'THB', - currencyDisplay: 'code', - }).format(n), - [NumberFormat.Forint]: (n: number) => - new Intl.NumberFormat('hu-HU', { - ...commonProps, - currency: 'HUF', - currencyDisplay: 'code', - }).format(n), - [NumberFormat.Koruna]: (n: number) => - new Intl.NumberFormat('cs-CZ', { - ...commonProps, - currency: 'CZK', - currencyDisplay: 'code', - }).format(n), - [NumberFormat.Shekel]: (n: number) => - new Intl.NumberFormat('he-IL', { - ...commonProps, - currency: 'ILS', - }).format(n), - [NumberFormat.ChileanPeso]: (n: number) => - new Intl.NumberFormat('es-CL', { - ...commonProps, - currency: 'CLP', - currencyDisplay: 'code', - }).format(n), - [NumberFormat.PhilippinePeso]: (n: number) => - new Intl.NumberFormat('fil-PH', { - ...commonProps, - currency: 'PHP', - }).format(n), - [NumberFormat.Dirham]: (n: number) => - new Intl.NumberFormat('ar-AE', { - ...commonProps, - currency: 'AED', - currencyDisplay: 'code', - }).format(n), - [NumberFormat.ColombianPeso]: (n: number) => - new Intl.NumberFormat('es-CO', { - ...commonProps, - currency: 'COP', - currencyDisplay: 'code', - }).format(n), - [NumberFormat.Riyal]: (n: number) => - new Intl.NumberFormat('en-US', { - ...commonProps, - currency: 'SAR', - currencyDisplay: 'code', - }).format(n), - [NumberFormat.Ringgit]: (n: number) => - new Intl.NumberFormat('ms-MY', { - ...commonProps, - currency: 'MYR', - }).format(n), - [NumberFormat.Leu]: (n: number) => - new Intl.NumberFormat('ro-RO', { - ...commonProps, - currency: 'RON', - }).format(n), - [NumberFormat.ArgentinePeso]: (n: number) => - new Intl.NumberFormat('es-AR', { - ...commonProps, - currency: 'ARS', - currencyDisplay: 'code', - }).format(n), - [NumberFormat.UruguayanPeso]: (n: number) => - new Intl.NumberFormat('es-UY', { - ...commonProps, - currency: 'UYU', - currencyDisplay: 'code', - }).format(n), -}; diff --git a/frontend/appflowy_web_app/src/application/database-yjs/fields/number/index.ts b/frontend/appflowy_web_app/src/application/database-yjs/fields/number/index.ts deleted file mode 100644 index 27ca7cd8d8..0000000000 --- a/frontend/appflowy_web_app/src/application/database-yjs/fields/number/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './format'; -export * from './number.type'; -export * from './parse'; diff --git a/frontend/appflowy_web_app/src/application/database-yjs/fields/number/number.type.ts b/frontend/appflowy_web_app/src/application/database-yjs/fields/number/number.type.ts deleted file mode 100644 index 9140531325..0000000000 --- a/frontend/appflowy_web_app/src/application/database-yjs/fields/number/number.type.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { Filter } from '@/application/database-yjs'; - -export enum NumberFormat { - Num = 0, - USD = 1, - CanadianDollar = 2, - EUR = 4, - Pound = 5, - Yen = 6, - Ruble = 7, - Rupee = 8, - Won = 9, - Yuan = 10, - Real = 11, - Lira = 12, - Rupiah = 13, - Franc = 14, - HongKongDollar = 15, - NewZealandDollar = 16, - Krona = 17, - NorwegianKrone = 18, - MexicanPeso = 19, - Rand = 20, - NewTaiwanDollar = 21, - DanishKrone = 22, - Baht = 23, - Forint = 24, - Koruna = 25, - Shekel = 26, - ChileanPeso = 27, - PhilippinePeso = 28, - Dirham = 29, - ColombianPeso = 30, - Riyal = 31, - Ringgit = 32, - Leu = 33, - ArgentinePeso = 34, - UruguayanPeso = 35, - Percent = 36, -} - -export enum NumberFilterCondition { - Equal = 0, - NotEqual = 1, - GreaterThan = 2, - LessThan = 3, - GreaterThanOrEqualTo = 4, - LessThanOrEqualTo = 5, - NumberIsEmpty = 6, - NumberIsNotEmpty = 7, -} - -export interface NumberFilter extends Filter { - condition: NumberFilterCondition; - content: string; -} diff --git a/frontend/appflowy_web_app/src/application/database-yjs/fields/number/parse.ts b/frontend/appflowy_web_app/src/application/database-yjs/fields/number/parse.ts deleted file mode 100644 index d96ea87962..0000000000 --- a/frontend/appflowy_web_app/src/application/database-yjs/fields/number/parse.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { YDatabaseField } from '@/application/types'; -import { getTypeOptions } from '../type_option'; -import { NumberFormat } from './number.type'; - -export function parseNumberTypeOptions(field: YDatabaseField) { - const numberTypeOption = getTypeOptions(field)?.toJSON(); - - return { - format: parseInt(numberTypeOption.format) as NumberFormat, - }; -} diff --git a/frontend/appflowy_web_app/src/application/database-yjs/fields/relation/index.ts b/frontend/appflowy_web_app/src/application/database-yjs/fields/relation/index.ts deleted file mode 100644 index 4b94064b52..0000000000 --- a/frontend/appflowy_web_app/src/application/database-yjs/fields/relation/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './parse'; -export * from './relation.type'; diff --git a/frontend/appflowy_web_app/src/application/database-yjs/fields/relation/parse.ts b/frontend/appflowy_web_app/src/application/database-yjs/fields/relation/parse.ts deleted file mode 100644 index 42bbfe42e2..0000000000 --- a/frontend/appflowy_web_app/src/application/database-yjs/fields/relation/parse.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { YDatabaseField } from '@/application/types'; -import { RelationTypeOption } from './relation.type'; -import { getTypeOptions } from '../type_option'; - -export function parseRelationTypeOption(field: YDatabaseField) { - const relationTypeOption = getTypeOptions(field)?.toJSON(); - - return relationTypeOption as RelationTypeOption; -} diff --git a/frontend/appflowy_web_app/src/application/database-yjs/fields/relation/relation.type.ts b/frontend/appflowy_web_app/src/application/database-yjs/fields/relation/relation.type.ts deleted file mode 100644 index 31021afc38..0000000000 --- a/frontend/appflowy_web_app/src/application/database-yjs/fields/relation/relation.type.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Filter } from '@/application/database-yjs'; - -export interface RelationTypeOption { - database_id: string; -} - -export interface RelationFilter extends Filter { - condition: number; -} diff --git a/frontend/appflowy_web_app/src/application/database-yjs/fields/select-option/index.ts b/frontend/appflowy_web_app/src/application/database-yjs/fields/select-option/index.ts deleted file mode 100644 index a569b2ca47..0000000000 --- a/frontend/appflowy_web_app/src/application/database-yjs/fields/select-option/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './select_option.type'; -export * from './parse'; diff --git a/frontend/appflowy_web_app/src/application/database-yjs/fields/select-option/parse.ts b/frontend/appflowy_web_app/src/application/database-yjs/fields/select-option/parse.ts deleted file mode 100644 index 83446338d2..0000000000 --- a/frontend/appflowy_web_app/src/application/database-yjs/fields/select-option/parse.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { YDatabaseField, YjsDatabaseKey } from '@/application/types'; -import { getTypeOptions } from '../type_option'; -import { SelectTypeOption } from './select_option.type'; - -export function parseSelectOptionTypeOptions(field: YDatabaseField) { - const content = getTypeOptions(field)?.get(YjsDatabaseKey.content); - - if (!content) return null; - - try { - return JSON.parse(content) as SelectTypeOption; - } catch (e) { - return null; - } -} - -export function parseSelectOptionCellData(field: YDatabaseField, data: string) { - const typeOption = parseSelectOptionTypeOptions(field); - const selectedIds = typeof data === 'string' ? data.split(',') : []; - - return selectedIds - .map((id) => { - const option = typeOption?.options?.find((option) => option.id === id); - - return option?.name ?? ''; - }) - .join(', '); -} diff --git a/frontend/appflowy_web_app/src/application/database-yjs/fields/select-option/select_option.type.ts b/frontend/appflowy_web_app/src/application/database-yjs/fields/select-option/select_option.type.ts deleted file mode 100644 index 343941d588..0000000000 --- a/frontend/appflowy_web_app/src/application/database-yjs/fields/select-option/select_option.type.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { Filter } from '@/application/database-yjs'; - -export enum SelectOptionColor { - Purple = 'Purple', - Pink = 'Pink', - LightPink = 'LightPink', - Orange = 'Orange', - Yellow = 'Yellow', - Lime = 'Lime', - Green = 'Green', - Aqua = 'Aqua', - Blue = 'Blue', -} - -export enum SelectOptionFilterCondition { - OptionIs = 0, - OptionIsNot = 1, - OptionContains = 2, - OptionDoesNotContain = 3, - OptionIsEmpty = 4, - OptionIsNotEmpty = 5, -} - -export interface SelectOptionFilter extends Filter { - condition: SelectOptionFilterCondition; - optionIds: string[]; -} - -export interface SelectOption { - id: string; - name: string; - color: SelectOptionColor; -} - -export interface SelectTypeOption { - disable_color: boolean; - options: SelectOption[]; -} diff --git a/frontend/appflowy_web_app/src/application/database-yjs/fields/text/index.ts b/frontend/appflowy_web_app/src/application/database-yjs/fields/text/index.ts deleted file mode 100644 index 7d0a52cd9d..0000000000 --- a/frontend/appflowy_web_app/src/application/database-yjs/fields/text/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './text.type'; diff --git a/frontend/appflowy_web_app/src/application/database-yjs/fields/text/text.type.ts b/frontend/appflowy_web_app/src/application/database-yjs/fields/text/text.type.ts deleted file mode 100644 index c2f230c738..0000000000 --- a/frontend/appflowy_web_app/src/application/database-yjs/fields/text/text.type.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Filter } from '@/application/database-yjs'; - -export enum TextFilterCondition { - TextIs = 0, - TextIsNot = 1, - TextContains = 2, - TextDoesNotContain = 3, - TextStartsWith = 4, - TextEndsWith = 5, - TextIsEmpty = 6, - TextIsNotEmpty = 7, -} - -export interface TextFilter extends Filter { - condition: TextFilterCondition; - content: string; -} diff --git a/frontend/appflowy_web_app/src/application/database-yjs/fields/type_option.ts b/frontend/appflowy_web_app/src/application/database-yjs/fields/type_option.ts deleted file mode 100644 index 11da994873..0000000000 --- a/frontend/appflowy_web_app/src/application/database-yjs/fields/type_option.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { YDatabaseField, YjsDatabaseKey } from '@/application/types'; -import { FieldType } from '@/application/database-yjs'; - -export function getTypeOptions(field: YDatabaseField) { - const fieldType = Number(field?.get(YjsDatabaseKey.type)) as FieldType; - - return field?.get(YjsDatabaseKey.type_option)?.get(String(fieldType)); -} diff --git a/frontend/appflowy_web_app/src/application/database-yjs/filter.ts b/frontend/appflowy_web_app/src/application/database-yjs/filter.ts deleted file mode 100644 index e3cb188cda..0000000000 --- a/frontend/appflowy_web_app/src/application/database-yjs/filter.ts +++ /dev/null @@ -1,234 +0,0 @@ -import { - RowId, - YDatabaseFields, - YDatabaseFilter, - YDatabaseFilters, - YDatabaseRow, - YDoc, - YjsDatabaseKey, - YjsEditorKey, -} from '@/application/types'; -import { FieldType } from '@/application/database-yjs/database.type'; -import { - CheckboxFilter, - CheckboxFilterCondition, - ChecklistFilter, - ChecklistFilterCondition, - DateFilter, - NumberFilter, - NumberFilterCondition, - parseChecklistData, - SelectOptionFilter, - SelectOptionFilterCondition, - TextFilter, - TextFilterCondition, -} from '@/application/database-yjs/fields'; -import { Row } from '@/application/database-yjs/selector'; -import Decimal from 'decimal.js'; -import { every, filter, some } from 'lodash-es'; - -export function parseFilter (fieldType: FieldType, filter: YDatabaseFilter) { - const fieldId = filter.get(YjsDatabaseKey.field_id); - const filterType = Number(filter.get(YjsDatabaseKey.filter_type)); - const id = filter.get(YjsDatabaseKey.id); - const content = filter.get(YjsDatabaseKey.content); - const condition = Number(filter.get(YjsDatabaseKey.condition)); - - const value = { - fieldId, - filterType, - condition, - id, - content, - }; - - switch (fieldType) { - case FieldType.URL: - case FieldType.RichText: - return value as TextFilter; - case FieldType.Number: - return value as NumberFilter; - case FieldType.Checklist: - return value as ChecklistFilter; - case FieldType.Checkbox: - return value as CheckboxFilter; - case FieldType.SingleSelect: - case FieldType.MultiSelect: - // eslint-disable-next-line no-case-declarations - const options = content.split(','); - - return { - ...value, - optionIds: options, - } as SelectOptionFilter; - case FieldType.DateTime: - case FieldType.CreatedTime: - case FieldType.LastEditedTime: - return value as DateFilter; - } - - return value; -} - -function createPredicate (conditions: ((row: Row) => boolean)[]) { - return function (item: Row) { - return every(conditions, (condition) => condition(item)); - }; -} - -export function filterBy (rows: Row[], filters: YDatabaseFilters, fields: YDatabaseFields, rowMetas: Record) { - const filterArray = filters.toArray(); - - if (filterArray.length === 0 || Object.keys(rowMetas).length === 0 || fields.size === 0) return rows; - - const conditions = filterArray.map((filter) => { - return (row: { id: string }) => { - const fieldId = filter.get(YjsDatabaseKey.field_id); - const field = fields.get(fieldId); - const fieldType = Number(field.get(YjsDatabaseKey.type)); - const rowId = row.id; - const rowMeta = rowMetas[rowId]; - - if (!rowMeta) return false; - const filterValue = parseFilter(fieldType, filter); - const meta = rowMeta.getMap(YjsEditorKey.data_section).get(YjsEditorKey.database_row) as YDatabaseRow; - - if (!meta) return false; - - const cells = meta.get(YjsDatabaseKey.cells); - const cell = cells.get(fieldId); - - if (!cell) return false; - const { condition, content } = filterValue; - - switch (fieldType) { - case FieldType.URL: - case FieldType.RichText: - return textFilterCheck(cell.get(YjsDatabaseKey.data) as string, content, condition); - case FieldType.Number: - return numberFilterCheck(cell.get(YjsDatabaseKey.data) as string, content, condition); - case FieldType.Checkbox: - return checkboxFilterCheck(cell.get(YjsDatabaseKey.data) as string, condition); - case FieldType.SingleSelect: - case FieldType.MultiSelect: - return selectOptionFilterCheck(cell.get(YjsDatabaseKey.data) as string, content, condition); - case FieldType.Checklist: - return checklistFilterCheck(cell.get(YjsDatabaseKey.data) as string, content, condition); - default: - return true; - } - }; - }); - const predicate = createPredicate(conditions); - - return filter(rows, predicate); -} - -export function textFilterCheck (data: string, content: string, condition: TextFilterCondition) { - switch (condition) { - case TextFilterCondition.TextContains: - return data.includes(content); - case TextFilterCondition.TextDoesNotContain: - return !data.includes(content); - case TextFilterCondition.TextIs: - return data === content; - case TextFilterCondition.TextIsNot: - return data !== content; - case TextFilterCondition.TextIsEmpty: - return data === ''; - case TextFilterCondition.TextIsNotEmpty: - return data !== ''; - default: - return false; - } -} - -export function numberFilterCheck (data: string, content: string, condition: number) { - if (isNaN(Number(data)) || isNaN(Number(content)) || data === '' || content === '') { - if (condition === NumberFilterCondition.NumberIsEmpty) { - return data === ''; - } - - if (condition === NumberFilterCondition.NumberIsNotEmpty) { - return data !== ''; - } - - return false; - } - - const decimal = new Decimal(data).toNumber(); - const filterDecimal = new Decimal(content).toNumber(); - - switch (condition) { - case NumberFilterCondition.Equal: - return decimal === filterDecimal; - case NumberFilterCondition.NotEqual: - return decimal !== filterDecimal; - case NumberFilterCondition.GreaterThan: - return decimal > filterDecimal; - case NumberFilterCondition.GreaterThanOrEqualTo: - return decimal >= filterDecimal; - case NumberFilterCondition.LessThan: - return decimal < filterDecimal; - case NumberFilterCondition.LessThanOrEqualTo: - return decimal <= filterDecimal; - default: - return false; - } -} - -export function checkboxFilterCheck (data: string, condition: number) { - switch (condition) { - case CheckboxFilterCondition.IsChecked: - return data === 'Yes'; - case CheckboxFilterCondition.IsUnChecked: - return data !== 'Yes'; - default: - return false; - } -} - -export function checklistFilterCheck (data: string, content: string, condition: number) { - const percentage = parseChecklistData(data)?.percentage ?? 0; - - if (condition === ChecklistFilterCondition.IsComplete) { - return percentage === 1; - } - - return percentage !== 1; -} - -export function selectOptionFilterCheck (data: string, content: string, condition: number) { - if (SelectOptionFilterCondition.OptionIsEmpty === condition) { - return data === ''; - } - - if (SelectOptionFilterCondition.OptionIsNotEmpty === condition) { - return data !== ''; - } - - const selectedOptionIds = data.split(','); - const filterOptionIds = content.split(','); - - switch (condition) { - // Ensure all filterOptionIds are included in selectedOptionIds - case SelectOptionFilterCondition.OptionIs: - return every(filterOptionIds, (option) => selectedOptionIds.includes(option)); - - // Ensure none of the filterOptionIds are included in selectedOptionIds - case SelectOptionFilterCondition.OptionIsNot: - return every(filterOptionIds, (option) => !selectedOptionIds.includes(option)); - - // Ensure at least one of the filterOptionIds is included in selectedOptionIds - case SelectOptionFilterCondition.OptionContains: - return some(filterOptionIds, (option) => selectedOptionIds.includes(option)); - - // Ensure at least one of the filterOptionIds is not included in selectedOptionIds - case SelectOptionFilterCondition.OptionDoesNotContain: - return some(filterOptionIds, (option) => !selectedOptionIds.includes(option)); - - // Default case, if no conditions match - default: - return false; - } -} diff --git a/frontend/appflowy_web_app/src/application/database-yjs/group.ts b/frontend/appflowy_web_app/src/application/database-yjs/group.ts deleted file mode 100644 index 461748605e..0000000000 --- a/frontend/appflowy_web_app/src/application/database-yjs/group.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { RowId, YDatabaseField, YDoc, YjsDatabaseKey } from '@/application/types'; -import { getCellData } from '@/application/database-yjs/const'; -import { FieldType } from '@/application/database-yjs/database.type'; -import { parseSelectOptionTypeOptions } from '@/application/database-yjs/fields'; -import { Row } from '@/application/database-yjs/selector'; - -export function groupByField (rows: Row[], rowMetas: Record, field: YDatabaseField) { - const fieldType = Number(field.get(YjsDatabaseKey.type)); - const isSelectOptionField = [FieldType.SingleSelect, FieldType.MultiSelect].includes(fieldType); - - if (isSelectOptionField) { - return groupBySelectOption(rows, rowMetas, field); - } - - if (fieldType === FieldType.Checkbox) { - return groupByCheckbox(rows, rowMetas, field); - } - - return; -} - -export function groupByCheckbox (rows: Row[], rowMetas: Record, field: YDatabaseField) { - const fieldId = field.get(YjsDatabaseKey.id); - const result = new Map(); - - rows.forEach((row) => { - const cellData = getCellData(row.id, fieldId, rowMetas); - - const groupName = cellData === 'Yes' ? 'Yes' : 'No'; - const group = result.get(groupName) ?? []; - - group.push(row); - result.set(groupName, group); - }); - return result; -} - -export function groupBySelectOption (rows: Row[], rowMetas: Record, field: YDatabaseField) { - const fieldId = field.get(YjsDatabaseKey.id); - const result = new Map(); - const typeOption = parseSelectOptionTypeOptions(field); - - if (!typeOption) { - return; - } - - if (typeOption.options.length === 0) { - result.set(fieldId, rows); - return result; - } - - rows.forEach((row) => { - const cellData = getCellData(row.id, fieldId, rowMetas); - - const selectedIds = (cellData as string)?.split(',') ?? []; - - if (selectedIds.length === 0) { - const group = result.get(fieldId) ?? []; - - group.push(row); - result.set(fieldId, group); - return; - } - - selectedIds.forEach((id) => { - const option = typeOption.options.find((option) => option.id === id); - const groupName = option?.id ?? fieldId; - const group = result.get(groupName) ?? []; - - group.push(row); - result.set(groupName, group); - }); - }); - - return result; -} diff --git a/frontend/appflowy_web_app/src/application/database-yjs/index.ts b/frontend/appflowy_web_app/src/application/database-yjs/index.ts deleted file mode 100644 index 1d5aa0ce3d..0000000000 --- a/frontend/appflowy_web_app/src/application/database-yjs/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -export * from './context'; -export * from './fields'; -export * from './context'; -export * from './selector'; -export * from './database.type'; -export * from './const'; diff --git a/frontend/appflowy_web_app/src/application/database-yjs/selector.ts b/frontend/appflowy_web_app/src/application/database-yjs/selector.ts deleted file mode 100644 index 63ec29d70a..0000000000 --- a/frontend/appflowy_web_app/src/application/database-yjs/selector.ts +++ /dev/null @@ -1,733 +0,0 @@ -import { - FieldId, RowCoverType, - SortId, - YDatabase, - YDatabaseField, YDatabaseMetas, YDatabaseRow, - YjsDatabaseKey, - YjsEditorKey, -} from '@/application/types'; -import { getCell, metaIdFromRowId, MIN_COLUMN_WIDTH } from '@/application/database-yjs/const'; -import { - useDatabase, - useDatabaseFields, - useDatabaseView, - useRowDocMap, - useDatabaseViewId, -} from '@/application/database-yjs/context'; -import { filterBy, parseFilter } from '@/application/database-yjs/filter'; -import { groupByField } from '@/application/database-yjs/group'; -import { sortBy } from '@/application/database-yjs/sort'; -import { parseYDatabaseCellToCell } from '@/application/database-yjs/cell.parse'; -import { DateTimeCell } from '@/application/database-yjs/cell.type'; -import dayjs from 'dayjs'; -import { debounce } from 'lodash-es'; -import { useCallback, useEffect, useMemo, useState } from 'react'; -import { CalendarLayoutSetting, FieldType, FieldVisibility, Filter, RowMetaKey, SortCondition } from './database.type'; - -export interface Column { - fieldId: string; - width: number; - visibility: FieldVisibility; - wrap?: boolean; -} - -export interface Row { - id: string; - height: number; -} - -const defaultVisible = [FieldVisibility.AlwaysShown, FieldVisibility.HideWhenEmpty]; - -export function useDatabaseViewsSelector (_iidIndex: string, visibleViewIds?: string[]) { - const database = useDatabase(); - - const views = database?.get(YjsDatabaseKey.views); - const [viewIds, setViewIds] = useState([]); - const childViews = useMemo(() => { - return viewIds.map((viewId) => views?.get(viewId)); - }, [viewIds, views]); - - useEffect(() => { - if (!views) return; - - const observerEvent = () => { - const viewsObj = views.toJSON() as Record< - string, - { - created_at: number; - } - >; - - const viewsSorted = Object.entries(viewsObj).sort((a, b) => { - const [, viewA] = a; - const [, viewB] = b; - - return Number(viewB.created_at) - Number(viewA.created_at); - }); - - setViewIds( - viewsSorted - .map(([key]) => key) - .filter((id) => { - return !visibleViewIds || visibleViewIds.includes(id); - }), - ); - }; - - observerEvent(); - views.observe(observerEvent); - - return () => { - views.unobserve(observerEvent); - }; - }, [views, visibleViewIds]); - - return { - childViews, - viewIds, - }; -} - -export function useFieldsSelector (visibilitys: FieldVisibility[] = defaultVisible) { - const viewId = useDatabaseViewId(); - const database = useDatabase(); - const [columns, setColumns] = useState([]); - - useEffect(() => { - if (!viewId) return; - const view = database?.get(YjsDatabaseKey.views)?.get(viewId); - const fields = database?.get(YjsDatabaseKey.fields); - const fieldsOrder = view?.get(YjsDatabaseKey.field_orders); - const fieldSettings = view?.get(YjsDatabaseKey.field_settings); - const getColumns = () => { - if (!fields || !fieldsOrder || !fieldSettings) return []; - - const fieldIds = (fieldsOrder.toJSON() as { id: string }[]).map((item) => item.id); - - return fieldIds - .map((fieldId) => { - const setting = fieldSettings.get(fieldId); - - return { - fieldId, - width: parseInt(setting?.get(YjsDatabaseKey.width)) || MIN_COLUMN_WIDTH, - visibility: Number( - setting?.get(YjsDatabaseKey.visibility) || FieldVisibility.AlwaysShown, - ) as FieldVisibility, - wrap: setting?.get(YjsDatabaseKey.wrap) ?? true, - }; - }) - .filter((column) => { - return visibilitys.includes(column.visibility); - }); - }; - - const observerEvent = () => setColumns(getColumns()); - - setColumns(getColumns()); - - fieldsOrder?.observe(observerEvent); - fieldSettings?.observe(observerEvent); - - return () => { - fieldsOrder?.unobserve(observerEvent); - fieldSettings?.unobserve(observerEvent); - }; - }, [database, viewId, visibilitys]); - - return columns; -} - -export function useFieldSelector (fieldId: string) { - const database = useDatabase(); - const [field, setField] = useState(null); - const [clock, setClock] = useState(0); - - useEffect(() => { - if (!database) return; - - const field = database.get(YjsDatabaseKey.fields)?.get(fieldId); - - setField(field || null); - const observerEvent = () => setClock((prev) => prev + 1); - - field?.observe(observerEvent); - - return () => { - field?.unobserve(observerEvent); - }; - }, [database, fieldId]); - - return { - field, - clock, - }; -} - -export function useFiltersSelector () { - const database = useDatabase(); - const viewId = useDatabaseViewId(); - const [filters, setFilters] = useState([]); - - useEffect(() => { - if (!viewId) return; - const view = database?.get(YjsDatabaseKey.views)?.get(viewId); - const filterOrders = view?.get(YjsDatabaseKey.filters); - - if (!filterOrders) return; - - const getFilters = () => { - return (filterOrders.toJSON() as { id: string }[]).map((item) => item.id); - }; - - const observerEvent = () => setFilters(getFilters()); - - setFilters(getFilters()); - - filterOrders.observe(observerEvent); - - return () => { - filterOrders.unobserve(observerEvent); - }; - }, [database, viewId]); - - return filters; -} - -export function useFilterSelector (filterId: string) { - const database = useDatabase(); - const viewId = useDatabaseViewId(); - const fields = database?.get(YjsDatabaseKey.fields); - const [filterValue, setFilterValue] = useState(null); - - useEffect(() => { - if (!viewId) return; - const view = database?.get(YjsDatabaseKey.views)?.get(viewId); - const filter = view - ?.get(YjsDatabaseKey.filters) - .toArray() - .find((filter) => filter.get(YjsDatabaseKey.id) === filterId); - const field = fields?.get(filter?.get(YjsDatabaseKey.field_id) as FieldId); - - const observerEvent = () => { - if (!filter || !field) return; - const fieldType = Number(field.get(YjsDatabaseKey.type)) as FieldType; - - setFilterValue(parseFilter(fieldType, filter)); - }; - - observerEvent(); - field?.observe(observerEvent); - filter?.observe(observerEvent); - return () => { - field?.unobserve(observerEvent); - filter?.unobserve(observerEvent); - }; - }, [fields, viewId, filterId, database]); - return filterValue; -} - -export function useSortsSelector () { - const database = useDatabase(); - const viewId = useDatabaseViewId(); - const [sorts, setSorts] = useState([]); - - useEffect(() => { - if (!viewId) return; - const view = database?.get(YjsDatabaseKey.views)?.get(viewId); - const sortOrders = view?.get(YjsDatabaseKey.sorts); - - if (!sortOrders) return; - - const getSorts = () => { - return (sortOrders.toJSON() as { id: string }[]).map((item) => item.id); - }; - - const observerEvent = () => setSorts(getSorts()); - - setSorts(getSorts()); - - sortOrders.observe(observerEvent); - - return () => { - sortOrders.unobserve(observerEvent); - }; - }, [database, viewId]); - - return sorts; -} - -export interface Sort { - fieldId: FieldId; - condition: SortCondition; - id: SortId; -} - -export function useSortSelector (sortId: SortId) { - const database = useDatabase(); - const viewId = useDatabaseViewId(); - const [sortValue, setSortValue] = useState(null); - const views = database?.get(YjsDatabaseKey.views); - - useEffect(() => { - if (!viewId) return; - const view = views?.get(viewId); - const sort = view - ?.get(YjsDatabaseKey.sorts) - .toArray() - .find((sort) => sort.get(YjsDatabaseKey.id) === sortId); - - const observerEvent = () => { - setSortValue({ - fieldId: sort?.get(YjsDatabaseKey.field_id) as FieldId, - condition: Number(sort?.get(YjsDatabaseKey.condition)), - id: sort?.get(YjsDatabaseKey.id) as SortId, - }); - }; - - observerEvent(); - sort?.observe(observerEvent); - - return () => { - sort?.unobserve(observerEvent); - }; - }, [viewId, sortId, views]); - - return sortValue; -} - -export function useGroupsSelector () { - const database = useDatabase(); - const viewId = useDatabaseViewId(); - const [groups, setGroups] = useState([]); - - useEffect(() => { - if (!viewId) return; - const view = database?.get(YjsDatabaseKey.views)?.get(viewId); - - const groupOrders = view?.get(YjsDatabaseKey.groups); - - if (!groupOrders) return; - - const getGroups = () => { - return (groupOrders.toJSON() as { id: string }[]).map((item) => item.id); - }; - - const observerEvent = () => setGroups(getGroups()); - - setGroups(getGroups()); - - groupOrders.observe(observerEvent); - - return () => { - groupOrders.unobserve(observerEvent); - }; - }, [database, viewId]); - - return groups; -} - -export interface GroupColumn { - id: string; - visible: boolean; -} - -export function useGroup (groupId: string) { - const database = useDatabase(); - const viewId = useDatabaseViewId(); - const view = database?.get(YjsDatabaseKey.views)?.get(viewId); - const group = view - ?.get(YjsDatabaseKey.groups) - ?.toArray() - .find((group) => group.get(YjsDatabaseKey.id) === groupId); - const groupColumns = group?.get(YjsDatabaseKey.groups); - const [fieldId, setFieldId] = useState(null); - const [columns, setColumns] = useState([]); - - useEffect(() => { - if (!viewId) return; - - const observerEvent = () => { - setFieldId(group?.get(YjsDatabaseKey.field_id) as string); - }; - - observerEvent(); - group?.observe(observerEvent); - - const observerColumns = () => { - if (!groupColumns) return; - setColumns(groupColumns.toJSON()); - }; - - observerColumns(); - groupColumns?.observe(observerColumns); - - return () => { - group?.unobserve(observerEvent); - groupColumns?.unobserve(observerColumns); - }; - }, [database, viewId, groupId, group, groupColumns]); - - return { - columns, - fieldId, - }; -} - -export function useRowsByGroup (groupId: string) { - const { columns, fieldId } = useGroup(groupId); - const rows = useRowDocMap(); - const rowOrders = useRowOrdersSelector(); - - const fields = useDatabaseFields(); - const [notFound, setNotFound] = useState(false); - const [groupResult, setGroupResult] = useState>(new Map()); - const view = useDatabaseView(); - const layoutSetting = view?.get(YjsDatabaseKey.layout_settings)?.get('1'); - - useEffect(() => { - if (!fieldId || !rowOrders || !rows) return; - - const onConditionsChange = () => { - const newResult = new Map(); - - const field = fields.get(fieldId); - - if (!field) { - setNotFound(true); - setGroupResult(newResult); - return; - } - - const groupResult = groupByField(rowOrders, rows, field); - - if (!groupResult) { - setGroupResult(newResult); - return; - } - - setGroupResult(groupResult); - }; - - onConditionsChange(); - - fields.observeDeep(onConditionsChange); - return () => { - fields.unobserveDeep(onConditionsChange); - }; - }, [fieldId, fields, rowOrders, rows]); - - const visibleColumns = columns.filter((column) => { - if (column.id === fieldId) return !layoutSetting?.get(YjsDatabaseKey.hide_ungrouped_column); - return column.visible; - }); - - return { - fieldId, - groupResult, - columns: visibleColumns, - notFound, - }; -} - -export function useRowOrdersSelector () { - const rows = useRowDocMap(); - const [rowOrders, setRowOrders] = useState(); - const view = useDatabaseView(); - const sorts = view?.get(YjsDatabaseKey.sorts); - const fields = useDatabaseFields(); - const filters = view?.get(YjsDatabaseKey.filters); - const onConditionsChange = useCallback(() => { - const originalRowOrders = view?.get(YjsDatabaseKey.row_orders).toJSON(); - - if (!originalRowOrders || !rows) return; - - if (sorts?.length === 0 && filters?.length === 0) { - setRowOrders(originalRowOrders); - return; - } - - let rowOrders: Row[] | undefined; - - if (sorts?.length) { - rowOrders = sortBy(originalRowOrders, sorts, fields, rows); - } - - if (filters?.length) { - rowOrders = filterBy(rowOrders ?? originalRowOrders, filters, fields, rows); - } - - if (rowOrders) { - setRowOrders(rowOrders); - } else { - setRowOrders(originalRowOrders); - } - }, [fields, filters, rows, sorts, view]); - - useEffect(() => { - onConditionsChange(); - }, [onConditionsChange]); - - useEffect(() => { - const throttleChange = debounce(onConditionsChange, 200); - - view?.get(YjsDatabaseKey.row_orders)?.observeDeep(throttleChange); - sorts?.observeDeep(throttleChange); - filters?.observeDeep(throttleChange); - fields?.observeDeep(throttleChange); - - return () => { - view?.get(YjsDatabaseKey.row_orders)?.unobserveDeep(throttleChange); - sorts?.unobserveDeep(throttleChange); - filters?.unobserveDeep(throttleChange); - fields?.unobserveDeep(throttleChange); - }; - }, [onConditionsChange, view, fields, filters, sorts]); - - return rowOrders; -} - -export function useRowDataSelector (rowId: string) { - const rowMap = useRowDocMap(); - const [row, setRow] = useState(null); - - useEffect(() => { - const rowDoc = rowMap?.[rowId]; - - if (!rowDoc || !rowDoc.share.has(YjsEditorKey.data_section)) return; - const rowSharedRoot = rowDoc?.getMap(YjsEditorKey.data_section); - const row = rowSharedRoot?.get(YjsEditorKey.database_row); - - setRow(row); - }, [rowId, rowMap]); - return { - row, - }; -} - -export function useCellSelector ({ rowId, fieldId }: { rowId: string; fieldId: string }) { - const { row } = useRowDataSelector(rowId); - const cell = row?.get(YjsDatabaseKey.cells)?.get(fieldId); - - const [cellValue, setCellValue] = useState(() => (cell ? parseYDatabaseCellToCell(cell) : undefined)); - - useEffect(() => { - if (!cell) return; - setCellValue(parseYDatabaseCellToCell(cell)); - const observerEvent = () => setCellValue(parseYDatabaseCellToCell(cell)); - - cell.observeDeep(observerEvent); - - return () => { - cell.unobserveDeep(observerEvent); - }; - }, [cell]); - - return cellValue; -} - -export interface CalendarEvent { - start?: Date; - end?: Date; - id: string; -} - -export function useCalendarEventsSelector () { - const setting = useCalendarLayoutSetting(); - const filedId = setting.fieldId; - const { field } = useFieldSelector(filedId); - const rowOrders = useRowOrdersSelector(); - const rows = useRowDocMap(); - const [events, setEvents] = useState([]); - const [emptyEvents, setEmptyEvents] = useState([]); - - useEffect(() => { - if (!field || !rowOrders || !rows) return; - const fieldType = Number(field?.get(YjsDatabaseKey.type)) as FieldType; - - if (fieldType !== FieldType.DateTime) return; - const newEvents: CalendarEvent[] = []; - const emptyEvents: CalendarEvent[] = []; - - rowOrders?.forEach((row) => { - const cell = getCell(row.id, filedId, rows); - - if (!cell) { - emptyEvents.push({ - id: `${row.id}:${filedId}`, - }); - return; - } - - const value = parseYDatabaseCellToCell(cell) as DateTimeCell; - - if (!value || !value.data) { - emptyEvents.push({ - id: `${row.id}:${filedId}`, - }); - return; - } - - const getDate = (timestamp: string) => { - const dayjsResult = timestamp.length === 10 ? dayjs.unix(Number(timestamp)) : dayjs(timestamp); - - return dayjsResult.toDate(); - }; - - newEvents.push({ - id: `${row.id}:${filedId}`, - start: getDate(value.data), - end: value.endTimestamp && value.isRange ? getDate(value.endTimestamp) : getDate(value.data), - }); - }); - - setEvents(newEvents); - setEmptyEvents(emptyEvents); - }, [field, rowOrders, rows, filedId]); - - return { events, emptyEvents }; -} - -export function useCalendarLayoutSetting () { - const view = useDatabaseView(); - const layoutSetting = view?.get(YjsDatabaseKey.layout_settings)?.get('2'); - const [setting, setSetting] = useState({ - fieldId: '', - firstDayOfWeek: 0, - showWeekNumbers: true, - showWeekends: true, - layout: 0, - }); - - useEffect(() => { - const observerHandler = () => { - setSetting({ - fieldId: layoutSetting?.get(YjsDatabaseKey.field_id) as string, - firstDayOfWeek: Number(layoutSetting?.get(YjsDatabaseKey.first_day_of_week)), - showWeekNumbers: Boolean(layoutSetting?.get(YjsDatabaseKey.show_week_numbers)), - showWeekends: Boolean(layoutSetting?.get(YjsDatabaseKey.show_weekends)), - layout: Number(layoutSetting?.get(YjsDatabaseKey.layout_ty)), - }); - }; - - observerHandler(); - layoutSetting?.observe(observerHandler); - return () => { - layoutSetting?.unobserve(observerHandler); - }; - }, [layoutSetting]); - - return setting; -} - -export function getPrimaryFieldId (database: YDatabase) { - const fields = database?.get(YjsDatabaseKey.fields); - - return Array.from(fields?.keys() || []).find((fieldId) => { - return fields?.get(fieldId)?.get(YjsDatabaseKey.is_primary); - }); -} - -export function usePrimaryFieldId () { - const database = useDatabase(); - const [primaryFieldId, setPrimaryFieldId] = useState(null); - - useEffect(() => { - setPrimaryFieldId(getPrimaryFieldId(database) || null); - }, [database]); - - return primaryFieldId; -} - -export interface RowMeta { - documentId: string; - cover: { - data: string, - cover_type: RowCoverType, - } | null; - icon: string; - isEmptyDocument: boolean; -} - -const metaIdMapFromRowIdMap = new Map>(); - -function getMetaIdMap (rowId: string) { - const hasMetaIdMap = metaIdMapFromRowIdMap.has(rowId); - - if (!hasMetaIdMap) { - const parser = metaIdFromRowId(rowId); - const map = new Map(); - - map.set(RowMetaKey.IconId, parser(RowMetaKey.IconId)); - map.set(RowMetaKey.CoverId, parser(RowMetaKey.CoverId)); - map.set(RowMetaKey.DocumentId, parser(RowMetaKey.DocumentId)); - map.set(RowMetaKey.IsDocumentEmpty, parser(RowMetaKey.IsDocumentEmpty)); - metaIdMapFromRowIdMap.set(rowId, map); - return map; - } - - return metaIdMapFromRowIdMap.get(rowId) as Map; -} - -export const useRowMetaSelector = (rowId: string) => { - const [meta, setMeta] = useState(); - const rowMap = useRowDocMap(); - - const updateMeta = useCallback(() => { - - const row = rowMap?.[rowId]; - - if (!row || !row.share.has(YjsEditorKey.data_section)) return; - - const rowSharedRoot = row.getMap(YjsEditorKey.data_section); - - const yMeta = rowSharedRoot?.get(YjsEditorKey.meta); - - if (!yMeta) return; - - const metaKeyMap = getMetaIdMap(rowId); - - const iconKey = metaKeyMap.get(RowMetaKey.IconId) ?? ''; - const coverKey = metaKeyMap.get(RowMetaKey.CoverId) ?? ''; - const documentId = metaKeyMap.get(RowMetaKey.DocumentId) ?? ''; - const isEmptyDocumentKey = metaKeyMap.get(RowMetaKey.IsDocumentEmpty) ?? ''; - const metaJson = yMeta.toJSON(); - - const icon = metaJson[iconKey]; - let cover = null; - - try { - cover = metaJson[coverKey] ? JSON.parse(metaJson[coverKey]) : null; - } catch (e) { - // do nothing - } - - const isEmptyDocument = metaJson[isEmptyDocumentKey]; - - setMeta({ - icon, - cover, - documentId, - isEmptyDocument, - }); - }, [rowId, rowMap]); - - useEffect(() => { - if (!rowMap) return; - updateMeta(); - const observerEvent = () => updateMeta(); - - const rowDoc = rowMap[rowId]; - - if (!rowDoc || !rowDoc.share.has(YjsEditorKey.data_section)) return; - const rowSharedRoot = rowDoc.getMap(YjsEditorKey.data_section); - const meta = rowSharedRoot?.get(YjsEditorKey.meta) as YDatabaseMetas; - - meta?.observeDeep(observerEvent); - return () => { - meta?.unobserveDeep(observerEvent); - }; - }, [rowId, rowMap, updateMeta]); - - return meta; -}; diff --git a/frontend/appflowy_web_app/src/application/database-yjs/sort.ts b/frontend/appflowy_web_app/src/application/database-yjs/sort.ts deleted file mode 100644 index 5e6e078d89..0000000000 --- a/frontend/appflowy_web_app/src/application/database-yjs/sort.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { - RowId, - YDatabaseField, - YDatabaseFields, - YDatabaseRow, - YDatabaseSorts, - YDoc, - YjsDatabaseKey, - YjsEditorKey, -} from '@/application/types'; -import { FieldType, SortCondition } from '@/application/database-yjs/database.type'; -import { parseChecklistData, parseSelectOptionCellData } from '@/application/database-yjs/fields'; -import { Row } from '@/application/database-yjs/selector'; -import { orderBy } from 'lodash-es'; - -export function sortBy (rows: Row[], sorts: YDatabaseSorts, fields: YDatabaseFields, rowMetas: Record) { - const sortArray = sorts.toArray(); - - if (sortArray.length === 0 || Object.keys(rowMetas).length === 0 || fields.size === 0) return rows; - const iteratees = sortArray.map((sort) => { - return (row: { id: string }) => { - const fieldId = sort.get(YjsDatabaseKey.field_id); - const field = fields.get(fieldId); - const fieldType = Number(field.get(YjsDatabaseKey.type)); - - const rowId = row.id; - const rowMeta = rowMetas[rowId]; - - const defaultData = parseCellDataForSort(field, ''); - - const meta = rowMeta?.getMap(YjsEditorKey.data_section).get(YjsEditorKey.database_row) as YDatabaseRow; - - if (!meta) return defaultData; - if (fieldType === FieldType.LastEditedTime) { - return meta.get(YjsDatabaseKey.last_modified); - } - - if (fieldType === FieldType.CreatedTime) { - return meta.get(YjsDatabaseKey.created_at); - } - - const cells = meta.get(YjsDatabaseKey.cells); - const cell = cells.get(fieldId); - - if (!cell) return defaultData; - - return parseCellDataForSort(field, cell.get(YjsDatabaseKey.data) ?? ''); - }; - }); - const orders = sortArray.map((sort) => { - const condition = Number(sort.get(YjsDatabaseKey.condition)); - - if (condition === SortCondition.Descending) return 'desc'; - return 'asc'; - }); - - return orderBy(rows, iteratees, orders); -} - -export function parseCellDataForSort (field: YDatabaseField, data: string | boolean | number | object) { - const fieldType = Number(field.get(YjsDatabaseKey.type)); - - switch (fieldType) { - case FieldType.RichText: - case FieldType.URL: - return data ? data : '\uFFFF'; - case FieldType.Number: - return data === 'string' && !isNaN(parseInt(data)) ? parseInt(data) : data; - case FieldType.Checkbox: - return data === 'Yes'; - case FieldType.SingleSelect: - case FieldType.MultiSelect: - return parseSelectOptionCellData(field, data as string); - case FieldType.Checklist: - return parseChecklistData(data as string)?.percentage ?? 0; - case FieldType.DateTime: - return Number(data); - case FieldType.Relation: - return ''; - } -} diff --git a/frontend/appflowy_web_app/src/application/db/index.ts b/frontend/appflowy_web_app/src/application/db/index.ts deleted file mode 100644 index 25406a4cf7..0000000000 --- a/frontend/appflowy_web_app/src/application/db/index.ts +++ /dev/null @@ -1,113 +0,0 @@ -import { userSchema, UserTable } from '@/application/db/tables/users'; -import { YDoc } from '@/application/types'; -import { databasePrefix } from '@/application/constants'; -import { IndexeddbPersistence } from 'y-indexeddb'; -import * as Y from 'yjs'; -import BaseDexie from 'dexie'; -import { viewMetasSchema, ViewMetasTable } from '@/application/db/tables/view_metas'; -import { rowSchema, rowTable } from '@/application/db/tables/rows'; - -type DexieTables = ViewMetasTable & UserTable & rowTable; - -export type Dexie = BaseDexie & T; - -export const db = new BaseDexie(`${databasePrefix}_cache`) as Dexie; -const schema = Object.assign({}, { ...viewMetasSchema, ...userSchema, ...rowSchema }); - -db.version(1).stores(schema); - -const openedSet = new Set(); - -/** - * Open the collaboration database, and return a function to close it - */ -export async function openCollabDB(docName: string): Promise { - const name = `${databasePrefix}_${docName}`; - const doc = new Y.Doc({ - guid: docName, - }); - - const provider = new IndexeddbPersistence(name, doc); - - let resolve: (value: unknown) => void; - const promise = new Promise((resolveFn) => { - resolve = resolveFn; - }); - - provider.on('synced', () => { - if (!openedSet.has(name)) { - openedSet.add(name); - } - - resolve(true); - }); - - await promise; - - return doc as YDoc; -} - -export async function closeCollabDB(docName: string) { - const name = `${databasePrefix}_${docName}`; - - if (openedSet.has(name)) { - openedSet.delete(name); - } - - const doc = new Y.Doc(); - - const provider = new IndexeddbPersistence(name, doc); - - await provider.destroy(); -} - -export async function clearData() { - const databases = await indexedDB.databases(); - - const deleteDatabase = async (dbInfo: IDBDatabaseInfo): Promise => { - const dbName = dbInfo.name; - - if (!dbName) return false; - - return new Promise((resolve) => { - const request = indexedDB.open(dbName); - - request.onsuccess = (event) => { - const db = (event.target as IDBOpenDBRequest).result; - - db.close(); - - const deleteRequest = indexedDB.deleteDatabase(dbName); - - deleteRequest.onsuccess = () => { - console.log(`Database ${dbName} deleted successfully`); - resolve(true); - }; - - deleteRequest.onerror = (event) => { - console.error(`Error deleting database ${dbName}`, event); - resolve(false); - }; - - deleteRequest.onblocked = () => { - console.warn(`Delete operation blocked for database ${dbName}`); - resolve(false); - }; - }; - - request.onerror = (event) => { - console.error(`Error opening database ${dbName}`, event); - resolve(false); - }; - }); - }; - - try { - const results = await Promise.all(databases.map(deleteDatabase)); - - return results.every(Boolean); - } catch (error) { - console.error('Error during database deletion process:', error); - return false; - } -} diff --git a/frontend/appflowy_web_app/src/application/db/tables/rows.ts b/frontend/appflowy_web_app/src/application/db/tables/rows.ts deleted file mode 100644 index 1275a347f4..0000000000 --- a/frontend/appflowy_web_app/src/application/db/tables/rows.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Table } from 'dexie'; - -export type rowTable = { - rows: Table<{ - row_id: string; - row_key: string; - version: number; - }>; -}; - -export const rowSchema = { - rows: 'row_key', -}; \ No newline at end of file diff --git a/frontend/appflowy_web_app/src/application/db/tables/users.ts b/frontend/appflowy_web_app/src/application/db/tables/users.ts deleted file mode 100644 index 2b84d1ad0e..0000000000 --- a/frontend/appflowy_web_app/src/application/db/tables/users.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { User } from '@/application/types'; -import { Table } from 'dexie'; - -export type UserTable = { - users: Table; -}; - -export const userSchema = { - users: 'uuid', -}; \ No newline at end of file diff --git a/frontend/appflowy_web_app/src/application/db/tables/view_metas.ts b/frontend/appflowy_web_app/src/application/db/tables/view_metas.ts deleted file mode 100644 index 9c851e6fb1..0000000000 --- a/frontend/appflowy_web_app/src/application/db/tables/view_metas.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Table } from 'dexie'; -import { ViewInfo } from '@/application/types'; - -export type ViewMeta = { - publish_name: string; - - child_views: ViewInfo[]; - ancestor_views: ViewInfo[]; - - visible_view_ids: string[]; - database_relations: Record; -} & ViewInfo; - -export type ViewMetasTable = { - view_metas: Table; -}; - -export const viewMetasSchema = { - view_metas: 'publish_name', -}; diff --git a/frontend/appflowy_web_app/src/application/publish/context.tsx b/frontend/appflowy_web_app/src/application/publish/context.tsx deleted file mode 100644 index bb33c38cec..0000000000 --- a/frontend/appflowy_web_app/src/application/publish/context.tsx +++ /dev/null @@ -1,397 +0,0 @@ -import { db } from '@/application/db'; -import { ViewMeta } from '@/application/db/tables/view_metas'; -import { - AppendBreadcrumb, - CreateRowDoc, - LoadView, - LoadViewMeta, - View, - ViewInfo, - ViewLayout, -} from '@/application/types'; -import { notify } from '@/components/_shared/notify'; -import { findAncestors, findView } from '@/components/_shared/outline/utils'; -import { useService } from '@/components/main/app.hooks'; -import { useLiveQuery } from 'dexie-react-hooks'; -import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react'; -import { useNavigate } from 'react-router-dom'; - -export interface PublishContextType { - namespace: string; - publishName: string; - isTemplate?: boolean; - isTemplateThumb?: boolean; - viewMeta?: ViewMeta; - toView: (viewId: string, blockId?: string) => Promise; - loadViewMeta: LoadViewMeta; - createRowDoc?: CreateRowDoc; - loadView: LoadView; - outline?: View[]; - appendBreadcrumb?: AppendBreadcrumb; - breadcrumbs: View[]; - rendered?: boolean; - onRendered?: () => void; -} - -export const PublishContext = createContext(null); - -export const PublishProvider = ({ - children, - namespace, - publishName, - isTemplateThumb, - isTemplate, -}: { - children: React.ReactNode; - namespace: string; - publishName: string; - isTemplateThumb?: boolean; - isTemplate?: boolean; -}) => { - const [outline, setOutline] = useState([]); - const createdRowKeys = useRef([]); - const [rendered, setRendered] = useState(false); - - const [subscribers, setSubscribers] = useState void>>(new Map()); - - useEffect(() => { - return () => { - setSubscribers(new Map()); - }; - }, []); - - const viewMeta = useLiveQuery(async () => { - const name = `${namespace}_${publishName}`; - - const view = await db.view_metas.get(name); - - if (!view) return; - - return { - ...view, - name: findView(outline, view.view_id)?.name || view.name, - }; - }, [namespace, publishName, outline]); - - const originalCrumbs = useMemo(() => { - if (!viewMeta || !outline) return []; - const ancestors = findAncestors(outline, viewMeta?.view_id); - - if (ancestors) return ancestors; - if (!viewMeta?.ancestor_views) return []; - const parseToView = (ancestor: ViewInfo): View => { - let extra = null; - - try { - extra = ancestor.extra ? JSON.parse(ancestor.extra) : null; - } catch (e) { - // do nothing - } - - return { - view_id: ancestor.view_id, - name: ancestor.name, - icon: ancestor.icon, - layout: ancestor.layout, - extra, - is_published: true, - children: [], - is_private: false, - }; - }; - - const currentView = parseToView(viewMeta); - - return viewMeta?.ancestor_views.slice(1).map(item => findView(outline, item.view_id) || parseToView(item)) || [currentView]; - }, [viewMeta, outline]); - - const [breadcrumbs, setBreadcrumbs] = useState([]); - - useEffect(() => { - setBreadcrumbs(originalCrumbs); - }, [originalCrumbs]); - - const appendBreadcrumb = useCallback((view?: View) => { - setBreadcrumbs((prev) => { - if (!view) { - return prev.slice(0, -1); - } - - const index = prev.findIndex((v) => v.view_id === view.view_id); - - if (index === -1) { - return [...prev, view]; - } - - const rest = prev.slice(0, index); - - return [...rest, view]; - }); - }, []); - - useEffect(() => { - db.view_metas.hook('creating', (primaryKey, obj) => { - const subscriber = subscribers.get(primaryKey); - - subscriber?.(obj); - - return obj; - }); - db.view_metas.hook('deleting', (primaryKey, obj) => { - const subscriber = subscribers.get(primaryKey); - - subscriber?.(obj); - - return; - }); - db.view_metas.hook('updating', (modifications, primaryKey, obj) => { - const subscriber = subscribers.get(primaryKey); - - subscriber?.({ - ...obj, - ...modifications, - }); - - return modifications; - }); - }, [subscribers]); - - const prevViewMeta = useRef(viewMeta); - - const service = useService(); - - useEffect(() => { - const rowKeys = createdRowKeys.current; - - createdRowKeys.current = []; - - if (!rowKeys.length) return; - rowKeys.forEach((rowKey) => { - try { - service?.deleteRowDoc(rowKey); - } catch (e) { - console.error(e); - } - }); - - }, [service, publishName]); - const navigate = useNavigate(); - - const loadViewMeta = useCallback( - async (viewId: string, callback?: (meta: View) => void) => { - try { - const info = await service?.getPublishInfo(viewId); - - if (!info) { - throw new Error('View has not been published yet'); - } - - const { namespace, publishName } = info; - - const name = `${namespace}_${publishName}`; - - const meta = await service?.getPublishViewMeta(namespace, publishName); - - if (!meta) { - return Promise.reject(new Error('View meta has not been published yet')); - } - - const parseMetaToView = (meta: ViewInfo | ViewMeta): View => { - return { - is_private: false, - view_id: meta.view_id, - name: meta.name, - layout: meta.layout, - extra: meta.extra ? JSON.parse(meta.extra) : undefined, - icon: meta.icon, - children: meta.child_views?.map(parseMetaToView) || [], - is_published: true, - database_relations: 'database_relations' in meta ? meta.database_relations : undefined, - }; - }; - - const res = parseMetaToView(meta); - - callback?.(res); - - if (callback) { - setSubscribers((prev) => { - prev.set(name, (meta) => { - return callback?.(parseMetaToView(meta)); - }); - - return prev; - }); - } - - return res; - } catch (e) { - return Promise.reject(e); - } - }, - [service], - ); - - const toView = useCallback( - async (viewId: string, blockId?: string) => { - try { - const view = await loadViewMeta(viewId); - - const res = await service?.getPublishInfo(viewId); - - if (!res) { - throw new Error('View has not been published yet'); - } - - const { namespace: viewNamespace, publishName } = res; - - prevViewMeta.current = undefined; - const searchParams = new URLSearchParams(''); - - if (blockId) { - switch (view.layout) { - case ViewLayout.Document: - searchParams.set('blockId', blockId); - break; - case ViewLayout.Grid: - case ViewLayout.Board: - case ViewLayout.Calendar: - searchParams.set('r', blockId); - break; - default: - break; - } - } - - if (isTemplate) { - searchParams.set('template', 'true'); - } - - let url = `/${viewNamespace}/${publishName}`; - - if (searchParams.toString()) { - url += `?${searchParams.toString()}`; - } - - navigate(url, { - replace: true, - }); - return; - } catch (e) { - return Promise.reject(e); - } - }, - [loadViewMeta, service, isTemplate, navigate], - ); - - const loadOutline = useCallback(async () => { - if (!service || !namespace) return; - try { - const res = await service?.getPublishOutline(namespace); - - if (!res) { - throw new Error('Publish outline not found'); - } - - setOutline(res); - } catch (e) { - notify.error('Publish outline not found'); - } - }, [namespace, service]); - - const createRowDoc = useCallback( - async (rowKey: string) => { - try { - const doc = await service?.createRowDoc(rowKey); - - if (!doc) { - throw new Error('Failed to create row doc'); - } - - createdRowKeys.current.push(rowKey); - return doc; - } catch (e) { - return Promise.reject(e); - } - }, - [service], - ); - - const loadView = useCallback( - async (viewId: string, isSubDocument?: boolean) => { - if (isSubDocument) { - const data = await service?.getPublishRowDocument(viewId); - - if (!data) { - return Promise.reject(new Error('View has not been published yet')); - } - - return data; - } - - try { - const res = await service?.getPublishInfo(viewId); - - if (!res) { - throw new Error('View has not been published yet'); - } - - const { namespace, publishName } = res; - - const data = service?.getPublishView(namespace, publishName); - - if (!data) { - throw new Error('View has not been published yet'); - } - - return data; - } catch (e) { - return Promise.reject(e); - } - }, - [service], - ); - - const onRendered = useCallback(() => { - setRendered(true); - }, []); - - useEffect(() => { - if (!viewMeta && prevViewMeta.current) { - window.location.reload(); - return; - } - - prevViewMeta.current = viewMeta; - }, [viewMeta]); - - useEffect(() => { - void loadOutline(); - }, [loadOutline]); - - return ( - - {children} - - ); -}; - -export function usePublishContext () { - return useContext(PublishContext); -} diff --git a/frontend/appflowy_web_app/src/application/publish/index.ts b/frontend/appflowy_web_app/src/application/publish/index.ts deleted file mode 100644 index c38e8e8215..0000000000 --- a/frontend/appflowy_web_app/src/application/publish/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './context'; diff --git a/frontend/appflowy_web_app/src/application/services/index.ts b/frontend/appflowy_web_app/src/application/services/index.ts deleted file mode 100644 index 70c63ed3cd..0000000000 --- a/frontend/appflowy_web_app/src/application/services/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { AFService, AFServiceConfig } from '@/application/services/services.type'; -import { AFClientService } from '$client-services'; - -let service: AFService; - -export function getService (config: AFServiceConfig) { - if (service) return service; - - service = new AFClientService(config); - return service; -} diff --git a/frontend/appflowy_web_app/src/application/services/js-services/__tests__/fetch.test.ts b/frontend/appflowy_web_app/src/application/services/js-services/__tests__/fetch.test.ts deleted file mode 100644 index b80434a93d..0000000000 --- a/frontend/appflowy_web_app/src/application/services/js-services/__tests__/fetch.test.ts +++ /dev/null @@ -1,116 +0,0 @@ -import { expect } from '@jest/globals'; -import { fetchPublishView, fetchPublishViewMeta, fetchViewInfo } from '../fetch'; -import { APIService } from '@/application/services/js-services/http'; - -jest.mock('@/application/services/js-services/http', () => { - return { - APIService: { - getPublishView: jest.fn(), - getPublishViewMeta: jest.fn(), - getPublishInfoWithViewId: jest.fn(), - }, - }; -}); - -describe('Collab fetch functions with deduplication', () => { - beforeEach(() => { - jest.clearAllMocks(); - }); - - describe('fetchPublishView', () => { - it('should fetch publish view without duplicating requests', async () => { - const namespace = 'namespace1'; - const publishName = 'publish1'; - const mockResponse = { data: 'mockData' }; - - (APIService.getPublishView as jest.Mock).mockResolvedValue(mockResponse); - - const result1 = fetchPublishView(namespace, publishName); - const result2 = fetchPublishView(namespace, publishName); - - expect(result1).toBe(result2); - await expect(result1).resolves.toEqual(mockResponse); - expect(APIService.getPublishView).toHaveBeenCalledTimes(1); - }); - - it('should fetch publish view with different params', async () => { - const namespace = 'namespace1'; - const publishName = 'publish1'; - const mockResponse = { data: 'mockData' }; - - (APIService.getPublishView as jest.Mock).mockResolvedValue(mockResponse); - - const result1 = fetchPublishView(namespace, publishName); - const result2 = fetchPublishView(namespace, 'publish2'); - - expect(result1).not.toBe(result2); - await expect(result1).resolves.toEqual(mockResponse); - await expect(result2).resolves.toEqual(mockResponse); - expect(APIService.getPublishView).toHaveBeenCalledTimes(2); - }); - }); - - describe('fetchViewInfo', () => { - it('should fetch view info without duplicating requests', async () => { - const viewId = 'view1'; - const mockResponse = { data: 'mockData' }; - - (APIService.getPublishInfoWithViewId as jest.Mock).mockResolvedValue(mockResponse); - - const result1 = fetchViewInfo(viewId); - const result2 = fetchViewInfo(viewId); - - expect(result1).toBe(result2); - await expect(result1).resolves.toEqual(mockResponse); - expect(APIService.getPublishInfoWithViewId).toHaveBeenCalledTimes(1); - }); - - it('should fetch view info with different params', async () => { - const viewId = 'view1'; - const mockResponse = { data: 'mockData' }; - - (APIService.getPublishInfoWithViewId as jest.Mock).mockResolvedValue(mockResponse); - - const result1 = fetchViewInfo(viewId); - const result2 = fetchViewInfo('view2'); - - expect(result1).not.toBe(result2); - await expect(result1).resolves.toEqual(mockResponse); - await expect(result2).resolves.toEqual(mockResponse); - expect(APIService.getPublishInfoWithViewId).toHaveBeenCalledTimes(2); - }); - }); - - describe('fetchPublishViewMeta', () => { - it('should fetch publish view meta without duplicating requests', async () => { - const namespace = 'namespace1'; - const publishName = 'publish1'; - const mockResponse = { data: 'mockData' }; - - (APIService.getPublishViewMeta as jest.Mock).mockResolvedValue(mockResponse); - - const result1 = fetchPublishViewMeta(namespace, publishName); - const result2 = fetchPublishViewMeta(namespace, publishName); - - expect(result1).toBe(result2); - await expect(result1).resolves.toEqual(mockResponse); - expect(APIService.getPublishViewMeta).toHaveBeenCalledTimes(1); - }); - - it('should fetch publish view meta with different params', async () => { - const namespace = 'namespace1'; - const publishName = 'publish1'; - const mockResponse = { data: 'mockData' }; - - (APIService.getPublishViewMeta as jest.Mock).mockResolvedValue(mockResponse); - - const result1 = fetchPublishViewMeta(namespace, publishName); - const result2 = fetchPublishViewMeta(namespace, 'publish2'); - - expect(result1).not.toBe(result2); - await expect(result1).resolves.toEqual(mockResponse); - await expect(result2).resolves.toEqual(mockResponse); - expect(APIService.getPublishViewMeta).toHaveBeenCalledTimes(2); - }); - }); -}); diff --git a/frontend/appflowy_web_app/src/application/services/js-services/__tests__/index.test.ts b/frontend/appflowy_web_app/src/application/services/js-services/__tests__/index.test.ts deleted file mode 100644 index 9bab9c9352..0000000000 --- a/frontend/appflowy_web_app/src/application/services/js-services/__tests__/index.test.ts +++ /dev/null @@ -1,104 +0,0 @@ -import { withTestingYDoc } from '@/application/slate-yjs/__tests__/withTestingYjsEditor'; -import * as Y from 'yjs'; -import { AFClientService } from '../index'; -import { fetchViewInfo } from '@/application/services/js-services/fetch'; -import { expect, jest } from '@jest/globals'; -import { getPublishView, getPublishViewMeta } from '@/application/services/js-services/cache'; - -jest.mock('@/application/services/js-services/http/http_api', () => { - return { - initAPIService: jest.fn(), - }; -}); -jest.mock('nanoid', () => { - return { - nanoid: jest.fn().mockReturnValue('12345678'), - }; -}); -jest.mock('@/application/services/js-services/fetch', () => { - return { - fetchPublishView: jest.fn(), - fetchPublishViewMeta: jest.fn(), - fetchViewInfo: jest.fn(), - }; -}); - -jest.mock('@/application/services/js-services/cache', () => { - return { - getPublishView: jest.fn(), - getPublishViewMeta: jest.fn(), - getBatchCollabs: jest.fn(), - }; -}); -describe('AFClientService', () => { - let service: AFClientService; - beforeEach(() => { - jest.clearAllMocks(); - service = new AFClientService({ - cloudConfig: { - baseURL: 'http://localhost:3000', - gotrueURL: 'http://localhost:3000', - wsURL: 'ws://localhost:3000', - }, - }); - }); - - it('should get view meta', async () => { - const namespace = 'namespace'; - const publishName = 'publishName'; - const mockResponse = { - view_id: 'view_id', - publish_name: publishName, - metadata: { - view: { - name: 'viewName', - view_id: 'view_id', - }, - child_views: [], - ancestor_views: [], - }, - }; - - // @ts-ignore - (getPublishViewMeta as jest.Mock).mockResolvedValue(mockResponse); - - const result = await service.getPublishViewMeta(namespace, publishName); - - expect(result).toEqual(mockResponse); - }); - - it('should get view', async () => { - const namespace = 'namespace'; - const publishName = 'publishName'; - const rowDoc = new Y.Doc(); - const mockResponse = { - doc: withTestingYDoc('1'), - rowDocMap: rowDoc.getMap(), - }; - - // @ts-ignore - (getPublishView as jest.Mock).mockResolvedValue(mockResponse); - - const result = await service.getPublishView(namespace, publishName); - - expect(result).toEqual(mockResponse.doc); - }); - - it('should get view info', async () => { - const viewId = 'viewId'; - const mockResponse = { - namespace: 'namespace', - publish_name: 'publishName', - }; - - // @ts-ignore - (fetchViewInfo as jest.Mock).mockResolvedValue(mockResponse); - - const result = await service.getPublishInfo(viewId); - - expect(result).toEqual({ - namespace: 'namespace', - publishName: 'publishName', - }); - }); -}); diff --git a/frontend/appflowy_web_app/src/application/services/js-services/cache/__tests__/cache.test.ts b/frontend/appflowy_web_app/src/application/services/js-services/cache/__tests__/cache.test.ts deleted file mode 100644 index 2acb4a8b3b..0000000000 --- a/frontend/appflowy_web_app/src/application/services/js-services/cache/__tests__/cache.test.ts +++ /dev/null @@ -1,126 +0,0 @@ -import { Types } from '@/application/types'; -import { withTestingYDoc } from '@/application/slate-yjs/__tests__/withTestingYjsEditor'; -import { expect } from '@jest/globals'; -import { collabTypeToDBType, getPublishView, getPublishViewMeta } from '@/application/services/js-services/cache'; -import { openCollabDB, db } from '@/application/db'; -import { StrategyType } from '@/application/services/js-services/cache/types'; - -jest.mock('@/application/ydoc/apply', () => ({ - applyYDoc: jest.fn(), -})); - -jest.mock('@/application/db', () => ({ - openCollabDB: jest.fn(), - db: { - view_metas: { - get: jest.fn(), - put: jest.fn(), - }, - }, -})); - -const normalDoc = withTestingYDoc('1'); -const mockFetcher = jest.fn(); - -async function runTestWithStrategy (strategy: StrategyType) { - return getPublishView( - mockFetcher, - { - namespace: 'appflowy', - publishName: 'test', - }, - strategy, - ); -} - -async function runGetPublishViewMetaWithStrategy (strategy: StrategyType) { - return getPublishViewMeta( - mockFetcher, - { - namespace: 'appflowy', - publishName: 'test', - }, - strategy, - ); -} - -describe('Cache functions', () => { - beforeEach(() => { - jest.clearAllMocks(); - mockFetcher.mockClear(); - (openCollabDB as jest.Mock).mockClear(); - }); - - describe('getPublishView', () => { - it('should call fetcher when no cache found', async () => { - (openCollabDB as jest.Mock).mockResolvedValue(normalDoc); - mockFetcher.mockResolvedValue({ data: [1, 2, 3], meta: { metadata: { view: { id: '1' } } } }); - (db.view_metas.get as jest.Mock).mockResolvedValue(undefined); - await runTestWithStrategy(StrategyType.CACHE_FIRST); - expect(mockFetcher).toBeCalledTimes(1); - - await runTestWithStrategy(StrategyType.CACHE_AND_NETWORK); - expect(mockFetcher).toBeCalledTimes(2); - await expect(runTestWithStrategy(StrategyType.CACHE_ONLY)).rejects.toThrow('No cache found'); - }); - it('should call fetcher when cache is invalid or strategy is CACHE_AND_NETWORK', async () => { - (openCollabDB as jest.Mock).mockResolvedValue(normalDoc); - (db.view_metas.get as jest.Mock).mockResolvedValue({ view_id: '1' }); - mockFetcher.mockResolvedValue({ data: [1, 2, 3], meta: { metadata: { view: { id: '1' } } } }); - await runTestWithStrategy(StrategyType.CACHE_ONLY); - expect(openCollabDB).toBeCalledTimes(1); - - await runTestWithStrategy(StrategyType.CACHE_FIRST); - expect(openCollabDB).toBeCalledTimes(2); - expect(mockFetcher).toBeCalledTimes(0); - - await runTestWithStrategy(StrategyType.CACHE_AND_NETWORK); - expect(openCollabDB).toBeCalledTimes(3); - expect(mockFetcher).toBeCalledTimes(1); - }); - }); - - describe('getPublishViewMeta', () => { - it('should call fetcher when no cache found', async () => { - mockFetcher.mockResolvedValue({ metadata: { view: { id: '1' }, child_views: [], ancestor_views: [] } }); - (db.view_metas.get as jest.Mock).mockResolvedValue(undefined); - await runGetPublishViewMetaWithStrategy(StrategyType.CACHE_FIRST); - expect(mockFetcher).toBeCalledTimes(1); - - await runGetPublishViewMetaWithStrategy(StrategyType.CACHE_AND_NETWORK); - expect(mockFetcher).toBeCalledTimes(2); - - await expect(runGetPublishViewMetaWithStrategy(StrategyType.CACHE_ONLY)).rejects.toThrow('No cache found'); - }); - - it('should call fetcher when cache is invalid or strategy is CACHE_AND_NETWORK', async () => { - (openCollabDB as jest.Mock).mockResolvedValue(normalDoc); - (db.view_metas.get as jest.Mock).mockResolvedValue({ view_id: '1' }); - - mockFetcher.mockResolvedValue({ metadata: { view: { id: '1' }, child_views: [], ancestor_views: [] } }); - const meta = await runGetPublishViewMetaWithStrategy(StrategyType.CACHE_ONLY); - expect(openCollabDB).toBeCalledTimes(0); - expect(meta).toBeDefined(); - - await runGetPublishViewMetaWithStrategy(StrategyType.CACHE_FIRST); - expect(openCollabDB).toBeCalledTimes(0); - expect(mockFetcher).toBeCalledTimes(0); - - await runGetPublishViewMetaWithStrategy(StrategyType.CACHE_AND_NETWORK); - expect(openCollabDB).toBeCalledTimes(0); - expect(mockFetcher).toBeCalledTimes(1); - }); - }); -}); - -describe('collabTypeToDBType', () => { - it('should return correct DB type', () => { - expect(collabTypeToDBType(Types.Document)).toBe('document'); - expect(collabTypeToDBType(Types.Folder)).toBe('folder'); - expect(collabTypeToDBType(Types.Database)).toBe('database'); - expect(collabTypeToDBType(Types.WorkspaceDatabase)).toBe('databases'); - expect(collabTypeToDBType(Types.DatabaseRow)).toBe('database_row'); - expect(collabTypeToDBType(Types.UserAwareness)).toBe('user_awareness'); - expect(collabTypeToDBType(Types.Empty)).toBe(''); - }); -}); diff --git a/frontend/appflowy_web_app/src/application/services/js-services/cache/index.ts b/frontend/appflowy_web_app/src/application/services/js-services/cache/index.ts deleted file mode 100644 index ba34ebbfaa..0000000000 --- a/frontend/appflowy_web_app/src/application/services/js-services/cache/index.ts +++ /dev/null @@ -1,421 +0,0 @@ -import { closeCollabDB, db, openCollabDB } from '@/application/db'; -import { Fetcher, StrategyType } from '@/application/services/js-services/cache/types'; -import { - DatabaseId, - PublishViewMetaData, - RowId, - Types, - User, - ViewId, - ViewInfo, - YDoc, - YjsEditorKey, - YSharedRoot, -} from '@/application/types'; -import { applyYDoc } from '@/application/ydoc/apply'; - -export function collabTypeToDBType (type: Types) { - switch (type) { - case Types.Folder: - return 'folder'; - case Types.Document: - return 'document'; - case Types.Database: - return 'database'; - case Types.WorkspaceDatabase: - return 'databases'; - case Types.DatabaseRow: - return 'database_row'; - case Types.UserAwareness: - return 'user_awareness'; - default: - return ''; - } -} - -const collabSharedRootKeyMap = { - [Types.Folder]: YjsEditorKey.folder, - [Types.Document]: YjsEditorKey.document, - [Types.Database]: YjsEditorKey.database, - [Types.WorkspaceDatabase]: YjsEditorKey.workspace_database, - [Types.DatabaseRow]: YjsEditorKey.database_row, - [Types.UserAwareness]: YjsEditorKey.user_awareness, - [Types.Empty]: YjsEditorKey.empty, -}; - -export function hasCollabCache (doc: YDoc) { - const data = doc.getMap(YjsEditorKey.data_section) as YSharedRoot; - - return Object.values(collabSharedRootKeyMap).some((key) => { - return data.has(key); - }); -} - -export async function hasViewMetaCache (name: string) { - const data = await db.view_metas.get(name); - - return !!data; -} - -export async function hasUserCache (userId: string) { - const data = await db.users.get(userId); - - return !!data; -} - -export async function getPublishViewMeta< - T extends { - view: ViewInfo; - child_views: ViewInfo[]; - ancestor_views: ViewInfo[]; - } -> ( - fetcher: Fetcher, - { - namespace, - publishName, - }: { - namespace: string; - publishName: string; - }, - strategy: StrategyType = StrategyType.CACHE_AND_NETWORK, -) { - const name = `${namespace}_${publishName}`; - const exist = await hasViewMetaCache(name); - const meta = await db.view_metas.get(name); - - switch (strategy) { - case StrategyType.CACHE_ONLY: { - if (!exist) { - throw new Error('No cache found'); - } - - return meta; - } - - case StrategyType.CACHE_FIRST: { - if (!exist) { - return revalidatePublishViewMeta(name, fetcher); - } - - return meta; - } - - case StrategyType.CACHE_AND_NETWORK: { - if (!exist) { - return revalidatePublishViewMeta(name, fetcher); - } else { - void revalidatePublishViewMeta(name, fetcher); - } - - return meta; - } - - default: { - return revalidatePublishViewMeta(name, fetcher); - } - } -} - -export async function getUser< - T extends User -> ( - fetcher: Fetcher, - userId?: string, - strategy: StrategyType = StrategyType.CACHE_AND_NETWORK, -) { - const exist = userId && (await hasUserCache(userId)); - const data = await db.users.get(userId); - - switch (strategy) { - case StrategyType.CACHE_ONLY: { - if (!exist) { - throw new Error('No cache found'); - } - - return data; - } - - case StrategyType.CACHE_FIRST: { - if (!exist) { - return revalidateUser(fetcher); - } - - return data; - } - - case StrategyType.CACHE_AND_NETWORK: { - if (!exist) { - return revalidateUser(fetcher); - } else { - void revalidateUser(fetcher); - } - - return data; - } - - default: { - return revalidateUser(fetcher); - } - } -} - -export async function getPublishView< - T extends { - data: Uint8Array; - rows?: Record; - visibleViewIds?: ViewId[]; - relations?: Record; - subDocuments?: Record; - meta: { - view: ViewInfo; - child_views: ViewInfo[]; - ancestor_views: ViewInfo[]; - }; - } -> ( - fetcher: Fetcher, - { - namespace, - publishName, - }: { - namespace: string; - publishName: string; - }, - strategy: StrategyType = StrategyType.CACHE_AND_NETWORK, -) { - const name = `${namespace}_${publishName}`; - - const doc = await openCollabDB(name); - - const exist = (await hasViewMetaCache(name)) && hasCollabCache(doc); - - switch (strategy) { - case StrategyType.CACHE_ONLY: { - if (!exist) { - throw new Error('No cache found'); - } - - break; - } - - case StrategyType.CACHE_FIRST: { - if (!exist) { - await revalidatePublishView(name, fetcher, doc); - } - - break; - } - - case StrategyType.CACHE_AND_NETWORK: { - if (!exist) { - await revalidatePublishView(name, fetcher, doc); - } else { - void revalidatePublishView(name, fetcher, doc); - } - - break; - } - - default: { - await revalidatePublishView(name, fetcher, doc); - break; - } - } - - return { doc }; -} - -export async function getPageDoc; -}> (fetcher: Fetcher, name: string, strategy: StrategyType = StrategyType.CACHE_AND_NETWORK) { - - const doc = await openCollabDB(name); - - const exist = hasCollabCache(doc); - - switch (strategy) { - case StrategyType.CACHE_ONLY: { - if (!exist) { - throw new Error('No cache found'); - } - - break; - } - - case StrategyType.CACHE_FIRST: { - if (!exist) { - await revalidateView(fetcher, doc); - } - - break; - } - - case StrategyType.CACHE_AND_NETWORK: { - if (!exist) { - await revalidateView(fetcher, doc); - } else { - void revalidateView(fetcher, doc); - } - - break; - } - - default: { - await revalidateView(fetcher, doc); - break; - } - } - - return { doc }; -} - -async function updateRows (collab: YDoc, rows: Record) { - const bulkData = []; - - for (const [key, value] of Object.entries(rows)) { - const rowKey = `${collab.guid}_rows_${key}`; - const doc = await createRowDoc(rowKey); - - const dbRow = await db.rows.get(key); - - applyYDoc(doc, new Uint8Array(value)); - - bulkData.push({ - row_id: key, - version: (dbRow?.version || 0) + 1, - row_key: rowKey, - }); - } - - await db.rows.bulkPut(bulkData); -} - -export async function revalidateView< - T extends { - data: Uint8Array; - rows?: Record; - }> (fetcher: Fetcher, collab: YDoc) { - try { - const { data, rows } = await fetcher(); - - if (rows) { - await updateRows(collab, rows); - } - - applyYDoc(collab, data); - } catch (e) { - return Promise.reject(e); - } - -} - -export async function revalidatePublishViewMeta< - T extends { - view: ViewInfo; - child_views: ViewInfo[]; - ancestor_views: ViewInfo[]; - } -> (name: string, fetcher: Fetcher) { - const { view, child_views, ancestor_views } = await fetcher(); - - const dbView = await db.view_metas.get(name); - - await db.view_metas.put( - { - publish_name: name, - ...view, - child_views: child_views, - ancestor_views: ancestor_views, - visible_view_ids: dbView?.visible_view_ids ?? [], - database_relations: dbView?.database_relations ?? {}, - }, - name, - ); - - return db.view_metas.get(name); -} - -export async function revalidatePublishView< - T extends { - data: Uint8Array; - rows?: Record; - visibleViewIds?: ViewId[]; - relations?: Record; - subDocuments?: Record; - meta: PublishViewMetaData; - } -> (name: string, fetcher: Fetcher, collab: YDoc) { - const { data, meta, rows, visibleViewIds = [], relations = {}, subDocuments } = await fetcher(); - - await db.view_metas.put( - { - publish_name: name, - ...meta.view, - child_views: meta.child_views, - ancestor_views: meta.ancestor_views, - visible_view_ids: visibleViewIds, - database_relations: relations, - }, - name, - ); - - if (rows) { - await updateRows(collab, rows); - } - - if (subDocuments) { - for (const [key, value] of Object.entries(subDocuments)) { - const doc = await openCollabDB(key); - - applyYDoc(doc, new Uint8Array(value)); - } - } - - applyYDoc(collab, data); -} - -export async function deleteViewMeta (name: string) { - try { - await db.view_metas.delete(name); - - } catch (e) { - console.error(e); - } -} - -export async function deleteView (name: string) { - console.log('deleteView', name); - await deleteViewMeta(name); - await closeCollabDB(name); - - await closeCollabDB(`${name}_rows`); -} - -export async function revalidateUser< - T extends User> (fetcher: Fetcher) { - const data = await fetcher(); - - await db.users.put(data, data.uuid); - - return data; -} - -const rowDocs = new Map(); - -export async function createRowDoc (rowKey: string) { - if (rowDocs.has(rowKey)) { - return rowDocs.get(rowKey) as YDoc; - } - - const doc = await openCollabDB(rowKey); - - rowDocs.set(rowKey, doc); - - return doc; -} - -export function deleteRowDoc (rowKey: string) { - rowDocs.delete(rowKey); -} diff --git a/frontend/appflowy_web_app/src/application/services/js-services/cache/types.ts b/frontend/appflowy_web_app/src/application/services/js-services/cache/types.ts deleted file mode 100644 index 1c1a949723..0000000000 --- a/frontend/appflowy_web_app/src/application/services/js-services/cache/types.ts +++ /dev/null @@ -1,12 +0,0 @@ -export enum StrategyType { - // Cache only: return the cache if it exists, otherwise throw an error - CACHE_ONLY = 'CACHE_ONLY', - // Cache first: return the cache if it exists, otherwise fetch from the network - CACHE_FIRST = 'CACHE_FIRST', - // Cache and network: return the cache if it exists, otherwise fetch from the network and update the cache - CACHE_AND_NETWORK = 'CACHE_AND_NETWORK', - // Network only: fetch from the network and update the cache - NETWORK_ONLY = 'NETWORK_ONLY', -} - -export type Fetcher = () => Promise; diff --git a/frontend/appflowy_web_app/src/application/services/js-services/fetch.ts b/frontend/appflowy_web_app/src/application/services/js-services/fetch.ts deleted file mode 100644 index 783ba76475..0000000000 --- a/frontend/appflowy_web_app/src/application/services/js-services/fetch.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { APIService } from '@/application/services/js-services/http'; - -const pendingRequests = new Map(); - -function generateRequestKey (url: string, params: T) { - if (!params) return url; - - try { - return `${url}_${JSON.stringify(params)}`; - } catch (_e) { - return `${url}_${params}`; - } -} - -// Deduplication fetch requests -// When multiple requests are made to the same URL with the same params, only one request is made -// and the result is shared with all the requests -function fetchWithDeduplication (url: string, params: Req, fetchFunction: () => Promise): Promise { - const requestKey = generateRequestKey(url, params); - - if (pendingRequests.has(requestKey)) { - return pendingRequests.get(requestKey); - } - - const fetchPromise = fetchFunction().finally(() => { - pendingRequests.delete(requestKey); - }); - - pendingRequests.set(requestKey, fetchPromise); - return fetchPromise; -} - -export function fetchPublishView (namespace: string, publishName: string) { - const fetchFunction = () => APIService.getPublishView(namespace, publishName); - - return fetchWithDeduplication(`fetchPublishView_${namespace}`, { publishName }, fetchFunction); -} - -export function fetchPageCollab (workspaceId: string, viewId: string) { - const fetchFunction = () => APIService.getPageCollab(workspaceId, viewId); - - return fetchWithDeduplication(`fetchPageCollab_${workspaceId}`, { viewId }, fetchFunction); -} - -export function fetchViewInfo (viewId: string) { - const fetchFunction = () => APIService.getPublishInfoWithViewId(viewId); - - return fetchWithDeduplication(`fetchViewInfo`, { viewId }, fetchFunction); -} - -export function fetchPublishViewMeta (namespace: string, publishName: string) { - const fetchFunction = () => APIService.getPublishViewMeta(namespace, publishName); - - return fetchWithDeduplication(`fetchPublishViewMeta_${namespace}`, { publishName }, fetchFunction); -} diff --git a/frontend/appflowy_web_app/src/application/services/js-services/http/gotrue.ts b/frontend/appflowy_web_app/src/application/services/js-services/http/gotrue.ts deleted file mode 100644 index 32ff40bbfa..0000000000 --- a/frontend/appflowy_web_app/src/application/services/js-services/http/gotrue.ts +++ /dev/null @@ -1,105 +0,0 @@ -import { refreshToken as refreshSessionToken } from '@/application/session/token'; -import axios, { AxiosInstance } from 'axios'; - -let axiosInstance: AxiosInstance | null = null; - -export function initGrantService(baseURL: string) { - if (axiosInstance) { - return; - } - - axiosInstance = axios.create({ - baseURL, - }); - - axiosInstance.interceptors.request.use((config) => { - Object.assign(config.headers, { - 'Content-Type': 'application/json', - }); - - return config; - }); -} - -export async function refreshToken(refresh_token: string) { - const response = await axiosInstance?.post<{ - access_token: string; - expires_at: number; - refresh_token: string; - }>('/token?grant_type=refresh_token', { - refresh_token, - }); - - const newToken = response?.data; - - if (newToken) { - refreshSessionToken(JSON.stringify(newToken)); - } else { - return Promise.reject('Failed to refresh token'); - } - - return newToken; -} - -export async function signInWithMagicLink(email: string, authUrl: string) { - const res = await axiosInstance?.post( - '/magiclink', - { - code_challenge: '', - code_challenge_method: '', - data: {}, - email, - }, - { - headers: { - Redirect_to: authUrl, - }, - }, - ); - - return res?.data; -} - -export async function settings() { - const res = await axiosInstance?.get('/settings'); - - return res?.data; -} - -export function signInGoogle(authUrl: string) { - const provider = 'google'; - const redirectTo = encodeURIComponent(authUrl); - const accessType = 'offline'; - const prompt = 'consent'; - const baseURL = axiosInstance?.defaults.baseURL; - const url = `${baseURL}/authorize?provider=${provider}&redirect_to=${redirectTo}&access_type=${accessType}&prompt=${prompt}`; - - window.open(url, '_current'); -} - -export function signInApple(authUrl: string) { - const provider = 'apple'; - const redirectTo = encodeURIComponent(authUrl); - const baseURL = axiosInstance?.defaults.baseURL; - const url = `${baseURL}/authorize?provider=${provider}&redirect_to=${redirectTo}`; - - window.open(url, '_current'); -} - -export function signInGithub(authUrl: string) { - const provider = 'github'; - const redirectTo = encodeURIComponent(authUrl); - const baseURL = axiosInstance?.defaults.baseURL; - const url = `${baseURL}/authorize?provider=${provider}&redirect_to=${redirectTo}`; - - window.open(url, '_current'); -} - -export function signInDiscord(authUrl: string) { - const provider = 'discord'; - const redirectTo = encodeURIComponent(authUrl); - const baseURL = axiosInstance?.defaults.baseURL; - const url = `${baseURL}/authorize?provider=${provider}&redirect_to=${redirectTo}`; - - window.open(url, '_current'); -} diff --git a/frontend/appflowy_web_app/src/application/services/js-services/http/http_api.ts b/frontend/appflowy_web_app/src/application/services/js-services/http/http_api.ts deleted file mode 100644 index 21c45922eb..0000000000 --- a/frontend/appflowy_web_app/src/application/services/js-services/http/http_api.ts +++ /dev/null @@ -1,1661 +0,0 @@ -import { - DatabaseId, - FolderView, - RowId, - User, - View, - ViewId, - ViewLayout, - Workspace, - Invitation, - Types, - AFWebUser, - GetRequestAccessInfoResponse, - Subscriptions, - SubscriptionPlan, - SubscriptionInterval, - RequestAccessInfoStatus, - ViewInfo, - UpdatePagePayload, - CreatePagePayload, - CreateSpacePayload, - UpdateSpacePayload, - Role, - WorkspaceMember, QuickNote, QuickNoteEditorData, CreateWorkspacePayload, -} from '@/application/types'; -import { GlobalComment, Reaction } from '@/application/comment.type'; -import { initGrantService, refreshToken } from '@/application/services/js-services/http/gotrue'; -import { blobToBytes } from '@/application/services/js-services/http/utils'; -import { AFCloudConfig } from '@/application/services/services.type'; -import { getTokenParsed, invalidToken } from '@/application/session/token'; -import { - Template, - TemplateCategory, - TemplateCategoryFormValues, - TemplateCreator, TemplateCreatorFormValues, TemplateSummary, - UploadTemplatePayload, -} from '@/application/template.type'; -import axios, { AxiosInstance } from 'axios'; -import dayjs from 'dayjs'; -import { omit } from 'lodash-es'; -import { nanoid } from 'nanoid'; -import { notify } from '@/components/_shared/notify'; - -export * from './gotrue'; - -let axiosInstance: AxiosInstance | null = null; - -export function initAPIService (config: AFCloudConfig) { - if (axiosInstance) { - return; - } - - axiosInstance = axios.create({ - baseURL: config.baseURL, - headers: { - 'Content-Type': 'application/json', - }, - }); - - initGrantService(config.gotrueURL); - - axiosInstance.interceptors.request.use( - async (config) => { - const token = getTokenParsed(); - - if (!token) { - return config; - } - - const isExpired = dayjs().isAfter(dayjs.unix(token.expires_at)); - - let access_token = token.access_token; - const refresh_token = token.refresh_token; - - if (isExpired) { - try { - const newToken = await refreshToken(refresh_token); - - access_token = newToken?.access_token || ''; - } catch (e) { - invalidToken(); - return config; - } - } - - if (access_token) { - Object.assign(config.headers, { - Authorization: `Bearer ${access_token}`, - }); - } - - return config; - }, - (error) => { - return Promise.reject(error); - }, - ); - - axiosInstance.interceptors.response.use(async (response) => { - const status = response.status; - - if (status === 401) { - const token = getTokenParsed(); - - if (!token) { - invalidToken(); - return response; - } - - const refresh_token = token.refresh_token; - - try { - await refreshToken(refresh_token); - } catch (e) { - invalidToken(); - } - } - - return response; - }); -} - -export async function signInWithUrl (url: string) { - const hash = new URL(url).hash; - - if (!hash) { - return Promise.reject('No hash found'); - } - - const params = new URLSearchParams(hash.slice(1)); - const accessToken = params.get('access_token'); - const refresh_token = params.get('refresh_token'); - - if (!accessToken || !refresh_token) { - return Promise.reject({ - code: -1, - message: 'No access token or refresh token found', - }); - } - - try { - await verifyToken(accessToken); - } catch (e) { - return Promise.reject({ - code: -1, - message: 'Verify token failed', - }); - } - - try { - await refreshToken(refresh_token); - } catch (e) { - return Promise.reject({ - code: -1, - message: 'Refresh token failed', - }); - } -} - -export async function verifyToken (accessToken: string) { - const url = `/api/user/verify/${accessToken}`; - const response = await axiosInstance?.get<{ - code: number; - data?: { - is_new: boolean; - }; - message: string; - }>(url); - - const data = response?.data; - - if (data?.code === 0 && data.data) { - return data.data; - } - - return Promise.reject(data); -} - -export async function getCurrentUser (): Promise { - const url = '/api/user/profile'; - const response = await axiosInstance?.get<{ - code: number; - data?: { - uid: number; - uuid: string; - email: string; - name: string; - metadata: { - icon_url: string; - }; - encryption_sign: null; - latest_workspace_id: string; - updated_at: number; - }; - message: string; - }>(url); - - const data = response?.data; - - if (data?.code === 0 && data.data) { - const { uid, uuid, email, name, metadata } = data.data; - - return { - uid: String(uid), - uuid, - email, - name, - avatar: metadata.icon_url, - latestWorkspaceId: data.data.latest_workspace_id, - }; - } - - return Promise.reject(data); -} - -interface AFWorkspace { - workspace_id: string, - owner_uid: number, - owner_name: string, - workspace_name: string, - icon: string, - created_at: string, - member_count: number, - database_storage_id: string, -} - -function afWorkspace2Workspace (workspace: AFWorkspace): Workspace { - return { - id: workspace.workspace_id, - owner: { - uid: workspace.owner_uid, - name: workspace.owner_name, - }, - name: workspace.workspace_name, - icon: workspace.icon, - memberCount: workspace.member_count, - databaseStorageId: workspace.database_storage_id, - createdAt: workspace.created_at, - }; -} - -export async function openWorkspace (workspaceId: string) { - const url = `/api/workspace/${workspaceId}/open`; - const response = await axiosInstance?.put<{ - code: number; - message: string; - }>(url); - - if (response?.data.code === 0) { - return; - } - - return Promise.reject(response?.data); -} - -export async function createWorkspace (payload: CreateWorkspacePayload) { - const url = '/api/workspace'; - const response = await axiosInstance?.post<{ - code: number; - data?: { - workspace_id: string; - }; - message: string; - }>(url, payload); - - const data = response?.data; - - if (data?.code === 0 && data.data) { - return data.data.workspace_id; - } - - return Promise.reject(data); -} - -export async function getUserWorkspaceInfo (): Promise<{ - user_id: string; - selected_workspace: Workspace; - workspaces: Workspace[]; -}> { - const url = '/api/user/workspace'; - const response = await axiosInstance?.get<{ - code: number, - message: string, - data: { - user_profile: { - uuid: string; - }, - visiting_workspace: AFWorkspace, - workspaces: AFWorkspace[] - } - - }>(url); - - const data = response?.data; - - if (data?.code === 0) { - const { visiting_workspace, workspaces, user_profile } = data.data; - - return { - user_id: user_profile.uuid, - selected_workspace: afWorkspace2Workspace(visiting_workspace), - workspaces: workspaces.map(afWorkspace2Workspace), - }; - } - - return Promise.reject(data); -} - -export async function getPublishViewMeta (namespace: string, publishName: string) { - const url = `/api/workspace/v1/published/${namespace}/${publishName}`; - const response = await axiosInstance?.get<{ - code: number; - data: { - view: ViewInfo; - child_views: ViewInfo[]; - ancestor_views: ViewInfo[]; - }; - message: string; - }>(url); - - if (response?.data.code !== 0) { - return Promise.reject(response?.data); - } - - return response?.data.data; -} - -export async function getPublishViewBlob (namespace: string, publishName: string) { - const url = `/api/workspace/published/${namespace}/${publishName}/blob`; - const response = await axiosInstance?.get(url, { - responseType: 'blob', - }); - - return blobToBytes(response?.data); -} - -export async function updateCollab (workspaceId: string, objectId: string, collabType: Types, docState: Uint8Array, context: { - version_vector: number; -}) { - const url = `/api/workspace/v1/${workspaceId}/collab/${objectId}/web-update`; - let deviceId = localStorage.getItem('x-device-id'); - - if (!deviceId) { - deviceId = nanoid(8); - localStorage.setItem('x-device-id', deviceId); - } - - const response = await axiosInstance?.post<{ - code: number; - message: string; - }>(url, { - doc_state: Array.from(docState), - collab_type: collabType, - }, { - headers: { - 'client-version': 'web', - 'device-id': deviceId, - }, - }); - - if (response?.data.code !== 0) { - return Promise.reject(response?.data); - } - - return context; -} - -export async function getCollab (workspaceId: string, objectId: string, collabType: Types) { - const url = `/api/workspace/v1/${workspaceId}/collab/${objectId}`; - const response = await axiosInstance?.get<{ - code: number; - data: { - doc_state: number[]; - object_id: string; - }; - message: string; - }>(url, { - params: { - collab_type: collabType, - }, - }); - - if (response?.data.code !== 0) { - return Promise.reject(response?.data); - } - - const docState = response?.data.data.doc_state; - - return { - data: new Uint8Array(docState), - }; -} - -export async function getPageCollab (workspaceId: string, viewId: string) { - const url = `/api/workspace/${workspaceId}/page-view/${viewId}`; - const response = await axiosInstance?.get<{ - code: number; - data: { - view: View; - data: { - encoded_collab: number[]; - row_data: Record; - owner?: User; - last_editor?: User; - } - }; - message: string; - }>(url); - - if (!response) { - return Promise.reject('No response'); - } - - if (response.data.code !== 0) { - return Promise.reject(response?.data); - } - - const { encoded_collab, row_data, owner, last_editor } = response.data.data.data; - - return { - data: new Uint8Array(encoded_collab), - rows: row_data, - owner, - lastEditor: last_editor, - }; -} - -export async function getPublishView (publishNamespace: string, publishName: string) { - const meta = await getPublishViewMeta(publishNamespace, publishName); - const blob = await getPublishViewBlob(publishNamespace, publishName); - - if (meta.view.layout === ViewLayout.Document) { - return { - data: blob, - meta, - }; - } - - try { - const decoder = new TextDecoder('utf-8'); - - const jsonStr = decoder.decode(blob); - - const res = JSON.parse(jsonStr) as { - database_collab: Uint8Array; - database_row_collabs: Record; - database_row_document_collabs: Record; - visible_database_view_ids: ViewId[]; - database_relations: Record; - }; - - return { - data: new Uint8Array(res.database_collab), - rows: res.database_row_collabs, - visibleViewIds: res.visible_database_view_ids, - relations: res.database_relations, - subDocuments: res.database_row_document_collabs, - meta, - }; - } catch (e) { - return Promise.reject(e); - } -} - -export async function getPublishInfoWithViewId (viewId: string) { - const url = `/api/workspace/published-info/${viewId}`; - const response = await axiosInstance?.get<{ - code: number; - data?: { - namespace: string; - publish_name: string; - }; - message: string; - }>(url); - - const data = response?.data; - - if (data?.code === 0 && data.data) { - return data.data; - } - - return Promise.reject(data); -} - -export async function getAppFavorites (workspaceId: string) { - const url = `/api/workspace/${workspaceId}/favorite`; - const response = await axiosInstance?.get<{ - code: number; - data?: { - views: View[] - }; - message: string; - }>(url); - - const data = response?.data; - - if (data?.code === 0 && data.data) { - return data.data.views; - } - - return Promise.reject(data); -} - -export async function getAppTrash (workspaceId: string) { - const url = `/api/workspace/${workspaceId}/trash`; - const response = await axiosInstance?.get<{ - code: number; - data?: { - views: View[] - }; - message: string; - }>(url); - - const data = response?.data; - - if (data?.code === 0 && data.data) { - return data.data.views; - } - - return Promise.reject(data); -} - -export async function getAppRecent (workspaceId: string) { - const url = `/api/workspace/${workspaceId}/recent`; - const response = await axiosInstance?.get<{ - code: number; - data?: { - views: View[] - }; - message: string; - }>(url); - - const data = response?.data; - - if (data?.code === 0 && data.data) { - return data.data.views; - } - - return Promise.reject(data); -} - -export async function getAppOutline (workspaceId: string) { - const url = `/api/workspace/${workspaceId}/folder?depth=10`; - - const response = await axiosInstance?.get<{ - code: number; - data?: View; - message: string; - }>(url); - - const data = response?.data; - - if (data?.code === 0 && data.data) { - return data.data.children; - } - - return Promise.reject(data); -} - -export async function getView (workspaceId: string, viewId: string, depth: number = 1) { - const url = `/api/workspace/${workspaceId}/folder?depth=${depth}&root_view_id=${viewId}`; - const response = await axiosInstance?.get<{ - code: number; - data?: View; - message: string; - }>(url); - - const data = response?.data; - - if (data?.code === 0 && data.data) { - return data.data; - } - - return Promise.reject(data); -} - -export async function getPublishOutline (publishNamespace: string) { - const url = `/api/workspace/published-outline/${publishNamespace}`; - const response = await axiosInstance?.get<{ - code: number; - data?: View; - message: string; - }>(url); - - const data = response?.data; - - if (data?.code === 0 && data.data) { - return data.data.children; - } - - return Promise.reject(data); -} - -export async function getPublishViewComments (viewId: string): Promise { - const url = `/api/workspace/published-info/${viewId}/comment`; - const response = await axiosInstance?.get<{ - code: number; - data?: { - comments: { - comment_id: string; - user: { - uuid: string; - name: string; - avatar_url: string | null; - }; - content: string; - created_at: string; - last_updated_at: string; - reply_comment_id: string | null; - is_deleted: boolean; - can_be_deleted: boolean; - }[]; - }; - message: string; - }>(url); - - const data = response?.data; - - if (data?.code === 0 && data.data) { - const { comments } = data.data; - - return comments.map((comment) => { - return { - commentId: comment.comment_id, - user: { - uuid: comment.user?.uuid || '', - name: comment.user?.name || '', - avatarUrl: comment.user?.avatar_url || null, - }, - content: comment.content, - createdAt: comment.created_at, - lastUpdatedAt: comment.last_updated_at, - replyCommentId: comment.reply_comment_id, - isDeleted: comment.is_deleted, - canDeleted: comment.can_be_deleted, - }; - }); - } - - return Promise.reject(data); -} - -export async function getReactions (viewId: string, commentId?: string): Promise> { - let url = `/api/workspace/published-info/${viewId}/reaction`; - - if (commentId) { - url += `?comment_id=${commentId}`; - } - - const response = await axiosInstance?.get<{ - code: number; - data?: { - reactions: { - reaction_type: string; - react_users: { - uuid: string; - name: string; - avatar_url: string | null; - }[]; - comment_id: string; - }[]; - }; - message: string; - }>(url); - - const data = response?.data; - - if (data?.code === 0 && data.data) { - const { reactions } = data.data; - const reactionsMap: Record = {}; - - for (const reaction of reactions) { - if (!reactionsMap[reaction.comment_id]) { - reactionsMap[reaction.comment_id] = []; - } - - reactionsMap[reaction.comment_id].push({ - reactionType: reaction.reaction_type, - commentId: reaction.comment_id, - reactUsers: reaction.react_users.map((user) => ({ - uuid: user.uuid, - name: user.name, - avatarUrl: user.avatar_url, - })), - }); - } - - return reactionsMap; - } - - return Promise.reject(data); -} - -export async function createGlobalCommentOnPublishView (viewId: string, content: string, replyCommentId?: string) { - const url = `/api/workspace/published-info/${viewId}/comment`; - const response = await axiosInstance?.post<{ code: number; message: string }>(url, { - content, - reply_comment_id: replyCommentId, - }); - - if (response?.data.code === 0) { - return; - } - - return Promise.reject(response?.data.message); -} - -export async function deleteGlobalCommentOnPublishView (viewId: string, commentId: string) { - const url = `/api/workspace/published-info/${viewId}/comment`; - const response = await axiosInstance?.delete<{ code: number; message: string }>(url, { - data: { - comment_id: commentId, - }, - }); - - if (response?.data.code === 0) { - return; - } - - return Promise.reject(response?.data.message); -} - -export async function addReaction (viewId: string, commentId: string, reactionType: string) { - const url = `/api/workspace/published-info/${viewId}/reaction`; - const response = await axiosInstance?.post<{ code: number; message: string }>(url, { - comment_id: commentId, - reaction_type: reactionType, - }); - - if (response?.data.code === 0) { - return; - } - - return Promise.reject(response?.data.message); -} - -export async function removeReaction (viewId: string, commentId: string, reactionType: string) { - const url = `/api/workspace/published-info/${viewId}/reaction`; - const response = await axiosInstance?.delete<{ code: number; message: string }>(url, { - data: { - comment_id: commentId, - reaction_type: reactionType, - }, - }); - - if (response?.data.code === 0) { - return; - } - - return Promise.reject(response?.data.message); -} - -export async function getWorkspaces (): Promise { - const query = new URLSearchParams({ - include_member_count: 'true', - }); - - const url = `/api/workspace?${query.toString()}`; - const response = await axiosInstance?.get<{ - code: number; - data?: AFWorkspace[]; - message: string; - }>(url); - - const data = response?.data; - - if (data?.code === 0 && data.data) { - return data.data.map(afWorkspace2Workspace); - } - - return Promise.reject(data); -} - -export interface WorkspaceFolder { - view_id: string; - icon: string | null; - name: string; - is_space: boolean; - is_private: boolean; - extra: { - is_space: boolean; - space_created_at: number; - space_icon: string; - space_icon_color: string; - space_permission: number; - }; - - children: WorkspaceFolder[]; -} - -function iterateFolder (folder: WorkspaceFolder): FolderView { - return { - id: folder.view_id, - name: folder.name, - icon: folder.icon, - isSpace: folder.is_space, - extra: folder.extra ? JSON.stringify(folder.extra) : null, - isPrivate: folder.is_private, - children: folder.children.map((child: WorkspaceFolder) => { - return iterateFolder(child); - }), - }; -} - -export async function getWorkspaceFolder (workspaceId: string): Promise { - const url = `/api/workspace/${workspaceId}/folder`; - const response = await axiosInstance?.get<{ - code: number; - data?: WorkspaceFolder; - message: string; - }>(url); - - const data = response?.data; - - if (data?.code === 0 && data.data) { - return iterateFolder(data.data); - } - - return Promise.reject(data); -} - -export interface DuplicatePublishViewPayload { - published_collab_type: 0 | 1 | 2 | 3 | 4 | 5 | 6; - published_view_id: string; - dest_view_id: string; -} - -export async function duplicatePublishView (workspaceId: string, payload: DuplicatePublishViewPayload) { - const url = `/api/workspace/${workspaceId}/published-duplicate`; - - const res = await axiosInstance?.post<{ - code: number; - message: string; - }>(url, payload); - - if (res?.data.code === 0) { - return; - } - - return Promise.reject(res?.data.message); -} - -export async function createTemplate (template: UploadTemplatePayload) { - const url = '/api/template-center/template'; - const response = await axiosInstance?.post<{ - code: number; - message: string; - }>(url, template); - - if (response?.data.code === 0) { - return; - } - - return Promise.reject(response?.data.message); -} - -export async function updateTemplate (viewId: string, template: UploadTemplatePayload) { - const url = `/api/template-center/template/${viewId}`; - const response = await axiosInstance?.put<{ - code: number; - message: string; - }>(url, template); - - if (response?.data.code === 0) { - return; - } - - return Promise.reject(response?.data.message); -} - -export async function getTemplates ({ - categoryId, - nameContains, -}: { - categoryId?: string; - nameContains?: string; -}) { - const url = `/api/template-center/template`; - - const response = await axiosInstance?.get<{ - code: number; - data?: { - templates: TemplateSummary[]; - }; - message: string; - }>(url, { - params: { - category_id: categoryId, - name_contains: nameContains, - }, - }); - - const data = response?.data; - - if (data?.code === 0 && data.data) { - return data.data.templates; - } - - return Promise.reject(data); -} - -export async function getTemplateById (viewId: string) { - const url = `/api/template-center/template/${viewId}`; - const response = await axiosInstance?.get<{ - code: number; - data?: Template; - message: string; - }>(url); - - const data = response?.data; - - if (data?.code === 0 && data.data) { - return data.data; - } - - return Promise.reject(data); -} - -export async function deleteTemplate (viewId: string) { - const url = `/api/template-center/template/${viewId}`; - const response = await axiosInstance?.delete<{ - code: number; - message: string; - }>(url); - - if (response?.data.code === 0) { - return; - } - - return Promise.reject(response?.data.message); -} - -export async function getTemplateCategories () { - const url = '/api/template-center/category'; - const response = await axiosInstance?.get<{ - code: number; - data?: { - categories: TemplateCategory[] - - }; - message: string; - }>(url); - - const data = response?.data; - - if (data?.code === 0 && data.data) { - return data.data.categories; - } - - return Promise.reject(data); -} - -export async function addTemplateCategory (category: TemplateCategoryFormValues) { - const url = '/api/template-center/category'; - const response = await axiosInstance?.post<{ - code: number; - message: string; - }>(url, category); - - if (response?.data.code === 0) { - return; - } - - return Promise.reject(response?.data.message); -} - -export async function updateTemplateCategory (id: string, category: TemplateCategoryFormValues) { - const url = `/api/template-center/category/${id}`; - const response = await axiosInstance?.put<{ - code: number; - message: string; - }>(url, category); - - if (response?.data.code === 0) { - return; - } - - return Promise.reject(response?.data.message); -} - -export async function deleteTemplateCategory (categoryId: string) { - const url = `/api/template-center/category/${categoryId}`; - const response = await axiosInstance?.delete<{ - code: number; - message: string; - }>(url); - - if (response?.data.code === 0) { - return; - } - - return Promise.reject(response?.data.message); -} - -export async function getTemplateCreators () { - const url = '/api/template-center/creator'; - const response = await axiosInstance?.get<{ - code: number; - data?: { - creators: TemplateCreator[]; - }; - message: string; - }>(url); - - const data = response?.data; - - if (data?.code === 0 && data.data) { - return data.data.creators; - } - - return Promise.reject(data); -} - -export async function createTemplateCreator (creator: TemplateCreatorFormValues) { - const url = '/api/template-center/creator'; - const response = await axiosInstance?.post<{ - code: number; - message: string; - }>(url, creator); - - if (response?.data.code === 0) { - return; - } - - return Promise.reject(response?.data.message); -} - -export async function updateTemplateCreator (creatorId: string, creator: TemplateCreatorFormValues) { - const url = `/api/template-center/creator/${creatorId}`; - const response = await axiosInstance?.put<{ - code: number; - message: string; - }>(url, creator); - - if (response?.data.code === 0) { - return; - } - - return Promise.reject(response?.data.message); -} - -export async function deleteTemplateCreator (creatorId: string) { - const url = `/api/template-center/creator/${creatorId}`; - const response = await axiosInstance?.delete<{ - code: number; - message: string; - }>(url); - - if (response?.data.code === 0) { - return; - } - - return Promise.reject(response?.data.message); -} - -export async function uploadTemplateAvatar (file: File) { - const url = '/api/template-center/avatar'; - const formData = new FormData(); - - formData.append('avatar', file); - - const response = await axiosInstance?.request<{ - code: number; - data?: { - file_id: string; - }; - message: string; - }>({ - method: 'PUT', - url, - data: formData, - headers: { - 'Content-Type': 'multipart/form-data', - }, - }); - - const data = response?.data; - - if (data?.code === 0 && data.data) { - return axiosInstance?.defaults.baseURL + '/api/template-center/avatar/' + data.data.file_id; - } - - return Promise.reject(data); -} - -export async function getInvitation (invitationId: string) { - const url = `/api/workspace/invite/${invitationId}`; - const response = await axiosInstance?.get<{ - code: number; - data?: Invitation; - message: string; - }>(url); - - const data = response?.data; - - if (data?.code === 0 && data.data) { - return data.data; - } - - return Promise.reject(data); -} - -export async function acceptInvitation (invitationId: string) { - const url = `/api/workspace/accept-invite/${invitationId}`; - const response = await axiosInstance?.post<{ - code: number; - message: string; - }>(url); - - if (response?.data.code === 0) { - return; - } - - return Promise.reject(response?.data.message); -} - -export async function getRequestAccessInfo (requestId: string): Promise { - const url = `/api/access-request/${requestId}`; - const response = await axiosInstance?.get<{ - code: number; - data?: { - request_id: string; - workspace: AFWorkspace; - requester: AFWebUser & { - email: string; - }; - view: View; - status: RequestAccessInfoStatus; - }; - message: string; - }>(url); - - const data = response?.data; - - if (data?.code === 0 && data.data) { - const workspace = data.data.workspace; - - return { - ...data.data, - workspace: afWorkspace2Workspace(workspace), - }; - } - - return Promise.reject(data); -} - -export async function approveRequestAccess (requestId: string) { - const url = `/api/access-request/${requestId}/approve`; - const response = await axiosInstance?.post<{ - code: number; - message: string; - }>(url, { - is_approved: true, - }); - - if (response?.data.code === 0) { - return; - } - - return Promise.reject(response?.data); -} - -export async function sendRequestAccess (workspaceId: string, viewId: string) { - const url = `/api/access-request`; - const response = await axiosInstance?.post<{ - code: number; - message: string; - }>(url, { - workspace_id: workspaceId, - view_id: viewId, - }); - - if (response?.data.code === 0) { - return; - } - - return Promise.reject(response?.data); -} - -export async function getSubscriptionLink (workspaceId: string, plan: SubscriptionPlan, interval: SubscriptionInterval) { - const url = `/billing/api/v1/subscription-link`; - const response = await axiosInstance?.get<{ - code: number; - data?: string; - message: string; - }>(url, { - params: { - workspace_subscription_plan: plan, - recurring_interval: interval, - workspace_id: workspaceId, - success_url: window.location.href, - }, - }); - - const data = response?.data; - - if (data?.code === 0 && data.data) { - return data.data; - } - - return Promise.reject(data); -} - -export async function getSubscriptions () { - const url = `/billing/api/v1/subscriptions`; - const response = await axiosInstance?.get<{ - code: number; - data: Subscriptions; - message: string; - }>(url); - - if (response?.data.code === 0) { - return response?.data.data; - } - - return Promise.reject(response?.data); - -} - -export async function getWorkspaceSubscriptions (workspaceId: string) { - try { - const plans = await getActiveSubscription(workspaceId); - const subscriptions = await getSubscriptions(); - - return subscriptions?.filter((subscription) => plans?.includes(subscription.plan)); - - } catch (e) { - return Promise.reject(e); - } -} - -export async function getActiveSubscription (workspaceId: string) { - const url = `/billing/api/v1/active-subscription/${workspaceId}`; - - const response = await axiosInstance?.get<{ - code: number; - data: SubscriptionPlan[]; - message: string; - }>(url); - - if (response?.data.code === 0) { - return response?.data.data; - } - - return Promise.reject(response?.data); -} - -export async function createImportTask (file: File) { - const url = `/api/import/create`; - const fileName = file.name.split('.').slice(0, -1).join('.') || crypto.randomUUID(); - - const res = await axiosInstance?.post<{ - code: number; - data: { - task_id: string; - presigned_url: string; - }; - message: string; - }>(url, { - workspace_name: fileName, - content_length: file.size, - }); - - if (res?.data.code === 0) { - return { - taskId: res?.data.data.task_id, - presignedUrl: res?.data.data.presigned_url, - }; - } - - return Promise.reject(res?.data); -} - -export async function uploadImportFile (presignedUrl: string, file: File, onProgress: (progress: number) => void) { - const response = await axios.put(presignedUrl, file, { - onUploadProgress: (progressEvent) => { - const { progress = 0 } = progressEvent; - - console.log(`Upload progress: ${progress * 100}%`); - onProgress(progress); - }, - headers: { - 'Content-Type': 'application/zip', - }, - }); - - if (response.status === 200) { - return; - } - - return Promise.reject({ - code: -1, - message: `Upload file failed. ${response.statusText}`, - }); -} - -export async function addAppPage (workspaceId: string, parentViewId: string, { - layout, - name, -}: CreatePagePayload) { - const url = `/api/workspace/${workspaceId}/page-view`; - const response = await axiosInstance?.post<{ - code: number; - data: { - view_id: string; - }; - message: string; - }>(url, { - parent_view_id: parentViewId, - layout, - name, - }); - - if (response?.data.code === 0) { - return response?.data.data.view_id; - } - - return Promise.reject(response?.data); -} - -export async function updatePage (workspaceId: string, viewId: string, data: UpdatePagePayload) { - const url = `/api/workspace/${workspaceId}/page-view/${viewId}`; - - const res = await axiosInstance?.patch<{ - code: number; - message: string; - }>(url, data); - - if (res?.data.code === 0) { - return; - } - - return Promise.reject(res?.data); -} - -export async function deleteTrash (workspaceId: string, viewId?: string) { - if (viewId) { - const url = `/api/workspace/${workspaceId}/trash/${viewId}`; - const response = await axiosInstance?.delete<{ - code: number; - message: string; - }>(url); - - if (response?.data.code === 0) { - return; - } - - return Promise.reject(response?.data); - } else { - const url = `/api/workspace/${workspaceId}/delete-all-pages-from-trash`; - const response = await axiosInstance?.post<{ - code: number; - message: string; - }>(url); - - if (response?.data.code === 0) { - return; - } - - return Promise.reject(response?.data); - } - -} - -export async function moveToTrash (workspaceId: string, viewId: string) { - const url = `/api/workspace/${workspaceId}/page-view/${viewId}/move-to-trash`; - const response = await axiosInstance?.post<{ - code: number; - message: string; - }>(url); - - if (response?.data.code === 0) { - return; - } - - return Promise.reject(response?.data); -} - -export async function movePageTo (workspaceId: string, viewId: string, parentViewId: string, prevViewId?: string) { - const url = `/api/workspace/${workspaceId}/page-view/${viewId}/move`; - const response = await axiosInstance?.post<{ - code: number; - message: string; - }>(url, { - new_parent_view_id: parentViewId, - prev_view_id: prevViewId, - }); - - if (response?.data.code === 0) { - return; - } - - return Promise.reject(response?.data); -} - -export async function restorePage (workspaceId: string, viewId?: string) { - const url = viewId ? `/api/workspace/${workspaceId}/page-view/${viewId}/restore-from-trash` : `/api/workspace/${workspaceId}/restore-all-pages-from-trash`; - const response = await axiosInstance?.post<{ - code: number; - message: string; - }>(url); - - if (response?.data.code === 0) { - return; - } - - return Promise.reject(response?.data); -} - -export async function createSpace (workspaceId: string, payload: CreateSpacePayload) { - const url = `/api/workspace/${workspaceId}/space`; - const response = await axiosInstance?.post<{ - code: number; - data: { - view_id: string; - }; - message: string; - }>(url, payload); - - if (response?.data.code === 0) { - return response?.data.data.view_id; - } - - return Promise.reject(response?.data); -} - -export async function updateSpace (workspaceId: string, payload: UpdateSpacePayload) { - const url = `/api/workspace/${workspaceId}/space/${payload.view_id}`; - const data = omit(payload, ['view_id']); - const response = await axiosInstance?.patch<{ - code: number; - message: string; - }>(url, data); - - if (response?.data.code === 0) { - return; - } - - return Promise.reject(response?.data); -} - -export async function uploadFile (workspaceId: string, viewId: string, file: File, onProgress?: (progress: number) => void) { - const url = `/api/file_storage/${workspaceId}/v1/blob/${viewId}`; - - // Check file size, if over 7MB, check subscription plan - if (file.size > 7 * 1024 * 1024) { - const plan = await getActiveSubscription(workspaceId); - - if (plan?.length === 0 || plan?.[0] === SubscriptionPlan.Free) { - notify.error('Your file is over 7 MB limit of the Free plan. Upgrade for unlimited uploads.'); - - return Promise.reject({ - code: 413, - message: 'File size is too large. Please upgrade your plan for unlimited uploads.', - }); - } - } - - try { - const response = await axiosInstance?.put<{ - code: number; - message: string; - data: { - file_id: string; - } - }>(url, file, { - onUploadProgress: (progressEvent) => { - const { progress = 0 } = progressEvent; - - onProgress?.(progress); - }, - headers: { - 'Content-Type': file.type || 'application/octet-stream', - }, - }); - - if (response?.data.code === 0) { - const baseURL = axiosInstance?.defaults.baseURL; - const url = `${baseURL}/api/file_storage/${workspaceId}/v1/blob/${viewId}/${response?.data.data.file_id}`; - - return url; - } - - return Promise.reject(response?.data); - // eslint-disable-next-line - } catch (e: any) { - - if (e.response.status === 413) { - return Promise.reject({ - code: 413, - message: 'File size is too large. Please upgrade your plan for unlimited uploads.', - }); - } - } - - return Promise.reject({ - code: -1, - message: 'Upload file failed.', - }); - -} - -export async function inviteMembers (workspaceId: string, emails: string[]) { - const url = `/api/workspace/${workspaceId}/invite`; - - const payload = emails.map(e => ({ - email: e, - role: Role.Member, - })); - - const res = await axiosInstance?.post<{ - code: number; - message: string; - }>(url, payload); - - if (res?.data.code === 0) { - return; - } - - return Promise.reject(res?.data); -} - -export async function getMembers (workspaceId: string) { - const url = `/api/workspace/${workspaceId}/member`; - const res = await axiosInstance?.get<{ - code: number; - data: WorkspaceMember[]; - message: string; - }>(url); - - if (res?.data.code === 0) { - return res?.data.data; - } - - return Promise.reject(res?.data); -} - -export async function leaveWorkspace (workspaceId: string) { - const url = `/api/workspace/${workspaceId}/leave`; - const res = await axiosInstance?.post<{ - code: number; - message: string; - }>(url); - - if (res?.data.code === 0) { - return; - } - - return Promise.reject(res?.data); -} - -export async function deleteWorkspace (workspaceId: string) { - const url = `/api/workspace/${workspaceId}`; - const res = await axiosInstance?.delete<{ - code: number; - message: string; - }>(url); - - if (res?.data.code === 0) { - return; - } - - return Promise.reject(res?.data); -} - -export async function getQuickNoteList (workspaceId: string, params: { - offset?: number; - limit?: number; - searchTerm?: string; -}) { - const url = `/api/workspace/${workspaceId}/quick-note`; - const res = await axiosInstance?.get<{ - code: number; - data: { - quick_notes: QuickNote[]; - has_more: boolean; - }; - message: string; - }>(url, { - params: { - offset: params.offset, - limit: params.limit, - search_term: params.searchTerm || undefined, - }, - }); - - if (res?.data.code === 0) { - return { - data: res?.data.data.quick_notes, - has_more: res?.data.data.has_more, - }; - } - - return Promise.reject(res?.data); -} - -export async function createQuickNote (workspaceId: string, payload: QuickNoteEditorData[]): Promise { - const url = `/api/workspace/${workspaceId}/quick-note`; - const res = await axiosInstance?.post<{ - code: number; - data: QuickNote; - message: string; - }>(url, { - data: payload, - }); - - if (res?.data.code === 0) { - return res?.data.data; - } - - return Promise.reject(res?.data); -} - -export async function updateQuickNote (workspaceId: string, noteId: string, payload: QuickNoteEditorData[]) { - const url = `/api/workspace/${workspaceId}/quick-note/${noteId}`; - const res = await axiosInstance?.put<{ - code: number; - message: string; - }>(url, { - data: payload, - }); - - if (res?.data.code === 0) { - return; - } - - return Promise.reject(res?.data); -} - -export async function deleteQuickNote (workspaceId: string, noteId: string) { - const url = `/api/workspace/${workspaceId}/quick-note/${noteId}`; - const res = await axiosInstance?.delete<{ - code: number; - message: string; - }>(url); - - if (res?.data.code === 0) { - return; - } - - return Promise.reject(res?.data); -} - -export async function cancelSubscription (workspaceId: string, plan: SubscriptionPlan, reason?: string) { - const url = `/billing/api/v1/cancel-subscription`; - const res = await axiosInstance?.post<{ - code: number; - message: string; - }>(url, { - workspace_id: workspaceId, - plan, - sync: true, - reason, - }); - - if (res?.data.code === 0) { - return; - } - - return Promise.reject(res?.data); -} \ No newline at end of file diff --git a/frontend/appflowy_web_app/src/application/services/js-services/http/index.ts b/frontend/appflowy_web_app/src/application/services/js-services/http/index.ts deleted file mode 100644 index e170c830a4..0000000000 --- a/frontend/appflowy_web_app/src/application/services/js-services/http/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * as APIService from './http_api'; diff --git a/frontend/appflowy_web_app/src/application/services/js-services/http/utils.ts b/frontend/appflowy_web_app/src/application/services/js-services/http/utils.ts deleted file mode 100644 index aa197a7516..0000000000 --- a/frontend/appflowy_web_app/src/application/services/js-services/http/utils.ts +++ /dev/null @@ -1,17 +0,0 @@ -export function blobToBytes (blob: Blob): Promise { - return new Promise((resolve, reject) => { - const reader = new FileReader(); - - reader.onloadend = () => { - if (!(reader.result instanceof ArrayBuffer)) { - reject(new Error('Failed to convert blob to bytes')); - return; - } - - resolve(new Uint8Array(reader.result)); - }; - - reader.onerror = reject; - reader.readAsArrayBuffer(blob); - }); -} diff --git a/frontend/appflowy_web_app/src/application/services/js-services/index.ts b/frontend/appflowy_web_app/src/application/services/js-services/index.ts deleted file mode 100644 index d80bb543f2..0000000000 --- a/frontend/appflowy_web_app/src/application/services/js-services/index.ts +++ /dev/null @@ -1,567 +0,0 @@ -import { GlobalComment, Reaction } from '@/application/comment.type'; -import { openCollabDB } from '@/application/db'; -import { - createRowDoc, deleteRowDoc, - deleteView, - getPageDoc, - getPublishView, - getPublishViewMeta, - getUser, hasCollabCache, - hasViewMetaCache, -} from '@/application/services/js-services/cache'; -import { StrategyType } from '@/application/services/js-services/cache/types'; -import { - fetchPageCollab, - fetchPublishView, - fetchPublishViewMeta, - fetchViewInfo, -} from '@/application/services/js-services/fetch'; -import { APIService } from '@/application/services/js-services/http'; -import { SyncManager } from '@/application/services/js-services/sync'; - -import { AFService, AFServiceConfig } from '@/application/services/services.type'; -import { emit, EventType } from '@/application/session'; -import { afterAuth, AUTH_CALLBACK_URL, withSignIn } from '@/application/session/sign_in'; -import { getTokenParsed } from '@/application/session/token'; -import { - TemplateCategoryFormValues, - TemplateCreatorFormValues, - UploadTemplatePayload, -} from '@/application/template.type'; -import { - CreatePagePayload, CreateSpacePayload, CreateWorkspacePayload, - DatabaseRelations, - DuplicatePublishView, QuickNoteEditorData, - SubscriptionInterval, SubscriptionPlan, - Types, UpdatePagePayload, UpdateSpacePayload, WorkspaceMember, - YjsEditorKey, -} from '@/application/types'; -import { applyYDoc } from '@/application/ydoc/apply'; -import { nanoid } from 'nanoid'; -import * as Y from 'yjs'; - -export class AFClientService implements AFService { - private deviceId: string = nanoid(8); - - private clientId: string = 'web'; - - private viewLoaded: Set = new Set(); - - private publishViewLoaded: Set = new Set(); - - private publishViewInfo: Map< - string, - { - namespace: string; - publishName: string; - } - > = new Map(); - - constructor (config: AFServiceConfig) { - APIService.initAPIService(config.cloudConfig); - } - - getClientId () { - return this.clientId; - } - - async getPublishViewMeta (namespace: string, publishName: string) { - const name = `${namespace}_${publishName}`; - - const isLoaded = this.publishViewLoaded.has(name); - const viewMeta = await getPublishViewMeta( - () => { - return fetchPublishViewMeta(namespace, publishName); - }, - { - namespace, - publishName, - }, - isLoaded ? StrategyType.CACHE_FIRST : StrategyType.CACHE_AND_NETWORK, - ); - - if (!viewMeta) { - return Promise.reject(new Error('View has not been published yet')); - } - - return viewMeta; - } - - async getPublishView (namespace: string, publishName: string) { - const name = `${namespace}_${publishName}`; - - const isLoaded = this.publishViewLoaded.has(name); - - const { doc } = await getPublishView( - async () => { - try { - return await fetchPublishView(namespace, publishName); - } catch (e) { - console.error(e); - void (async () => { - if (await hasViewMetaCache(name)) { - this.publishViewLoaded.delete(name); - void deleteView(name); - } - })(); - - return Promise.reject(e); - } - }, - { - namespace, - publishName, - }, - isLoaded ? StrategyType.CACHE_FIRST : StrategyType.CACHE_AND_NETWORK, - ); - - if (!isLoaded) { - this.publishViewLoaded.add(name); - } - - return doc; - } - - async getPublishRowDocument (viewId: string) { - const doc = await openCollabDB(viewId); - - if (hasCollabCache(doc)) { - return doc; - } - - return Promise.reject(new Error('Document not found')); - - } - - async createRowDoc (rowKey: string) { - return createRowDoc(rowKey); - } - - deleteRowDoc (rowKey: string) { - return deleteRowDoc(rowKey); - } - - async getAppDatabaseViewRelations (workspaceId: string, databaseStorageId: string) { - - const res = await APIService.getCollab(workspaceId, databaseStorageId, Types.WorkspaceDatabase); - const doc = new Y.Doc(); - - applyYDoc(doc, res.data); - - const { databases } = doc.getMap(YjsEditorKey.data_section).toJSON(); - const result: DatabaseRelations = {}; - - databases.forEach((database: { - database_id: string; - views: string[] - }) => { - result[database.database_id] = database.views[0]; - }); - return result; - } - - async getPublishInfo (viewId: string) { - if (this.publishViewInfo.has(viewId)) { - return this.publishViewInfo.get(viewId) as { - namespace: string; - publishName: string; - }; - } - - const info = await fetchViewInfo(viewId); - - const namespace = info.namespace; - - if (!namespace) { - return Promise.reject(new Error('View not found')); - } - - const data = { - namespace, - publishName: info.publish_name, - }; - - this.publishViewInfo.set(viewId, data); - - return data; - } - - async getPublishOutline (namespace: string) { - return APIService.getPublishOutline(namespace); - } - - async getAppOutline (workspaceId: string) { - return APIService.getAppOutline(workspaceId); - } - - async getAppView (workspaceId: string, viewId: string) { - return APIService.getView(workspaceId, viewId); - } - - async getAppFavorites (workspaceId: string) { - return APIService.getAppFavorites(workspaceId); - } - - async getAppRecent (workspaceId: string) { - return APIService.getAppRecent(workspaceId); - } - - async getAppTrash (workspaceId: string) { - return APIService.getAppTrash(workspaceId); - } - - async loginAuth (url: string) { - try { - await APIService.signInWithUrl(url); - emit(EventType.SESSION_VALID); - afterAuth(); - return; - } catch (e) { - emit(EventType.SESSION_INVALID); - return Promise.reject(e); - } - } - - @withSignIn() - async signInMagicLink ({ email }: { email: string; redirectTo: string }) { - return await APIService.signInWithMagicLink(email, AUTH_CALLBACK_URL); - } - - @withSignIn() - async signInGoogle (_: { redirectTo: string }) { - return APIService.signInGoogle(AUTH_CALLBACK_URL); - } - - @withSignIn() - async signInApple (_: { redirectTo: string }) { - return APIService.signInApple(AUTH_CALLBACK_URL); - } - - @withSignIn() - async signInGithub (_: { redirectTo: string }) { - return APIService.signInGithub(AUTH_CALLBACK_URL); - } - - @withSignIn() - async signInDiscord (_: { redirectTo: string }) { - return APIService.signInDiscord(AUTH_CALLBACK_URL); - } - - async getWorkspaces () { - const data = APIService.getWorkspaces(); - - return data; - } - - async getWorkspaceFolder (workspaceId: string) { - const data = await APIService.getWorkspaceFolder(workspaceId); - - return data; - } - - async getCurrentUser () { - const token = getTokenParsed(); - const userId = token?.user?.id; - - const user = await getUser( - () => APIService.getCurrentUser(), - userId, - StrategyType.CACHE_AND_NETWORK, - ); - - if (!user) { - return Promise.reject(new Error('User not found')); - } - - return user; - } - - async openWorkspace (workspaceId: string) { - return APIService.openWorkspace(workspaceId); - } - - async createWorkspace (payload: CreateWorkspacePayload) { - return APIService.createWorkspace(payload); - } - - async getUserWorkspaceInfo () { - const workspaceInfo = await APIService.getUserWorkspaceInfo(); - - if (!workspaceInfo) { - return Promise.reject(new Error('Workspace info not found')); - } - - return { - userId: workspaceInfo.user_id, - selectedWorkspace: workspaceInfo.selected_workspace, - workspaces: workspaceInfo.workspaces, - }; - } - - async duplicatePublishView (params: DuplicatePublishView) { - return APIService.duplicatePublishView(params.workspaceId, { - dest_view_id: params.spaceViewId, - published_view_id: params.viewId, - published_collab_type: params.collabType, - }); - } - - createCommentOnPublishView (viewId: string, content: string, replyCommentId: string | undefined): Promise { - return APIService.createGlobalCommentOnPublishView(viewId, content, replyCommentId); - } - - deleteCommentOnPublishView (viewId: string, commentId: string): Promise { - return APIService.deleteGlobalCommentOnPublishView(viewId, commentId); - } - - getPublishViewGlobalComments (viewId: string): Promise { - return APIService.getPublishViewComments(viewId); - } - - getPublishViewReactions (viewId: string, commentId?: string): Promise> { - return APIService.getReactions(viewId, commentId); - } - - addPublishViewReaction (viewId: string, commentId: string, reactionType: string): Promise { - return APIService.addReaction(viewId, commentId, reactionType); - } - - removePublishViewReaction (viewId: string, commentId: string, reactionType: string): Promise { - return APIService.removeReaction(viewId, commentId, reactionType); - } - - async getTemplateCategories () { - return APIService.getTemplateCategories(); - } - - async getTemplateCreators () { - return APIService.getTemplateCreators(); - } - - async createTemplate (template: UploadTemplatePayload) { - return APIService.createTemplate(template); - } - - async updateTemplate (id: string, template: UploadTemplatePayload) { - return APIService.updateTemplate(id, template); - } - - async getTemplateById (id: string) { - return APIService.getTemplateById(id); - } - - async getTemplates (params: { - categoryId?: string; - nameContains?: string; - }) { - return APIService.getTemplates(params); - } - - async deleteTemplate (id: string) { - return APIService.deleteTemplate(id); - } - - async addTemplateCategory (category: TemplateCategoryFormValues) { - return APIService.addTemplateCategory(category); - } - - async updateTemplateCategory (categoryId: string, category: TemplateCategoryFormValues) { - return APIService.updateTemplateCategory(categoryId, category); - } - - async deleteTemplateCategory (categoryId: string) { - return APIService.deleteTemplateCategory(categoryId); - } - - async updateTemplateCreator (creatorId: string, creator: TemplateCreatorFormValues) { - return APIService.updateTemplateCreator(creatorId, creator); - } - - async createTemplateCreator (creator: TemplateCreatorFormValues) { - return APIService.createTemplateCreator(creator); - } - - async deleteTemplateCreator (creatorId: string) { - return APIService.deleteTemplateCreator(creatorId); - } - - async uploadTemplateAvatar (file: File) { - return APIService.uploadTemplateAvatar(file); - } - - async getPageDoc (workspaceId: string, viewId: string, errorCallback?: (error: { - code: number; - }) => void) { - - const token = getTokenParsed(); - const userId = token?.user.id; - - if (!userId) { - throw new Error('User not found'); - } - - const name = `${userId}_${workspaceId}_${viewId}`; - - const isLoaded = this.viewLoaded.has(name); - - const { doc } = await getPageDoc( - async () => { - try { - return await fetchPageCollab(workspaceId, viewId); - // eslint-disable-next-line - } catch (e: any) { - console.error(e); - - errorCallback?.(e); - void (async () => { - this.viewLoaded.delete(name); - void deleteView(name); - })(); - - return Promise.reject(e); - } - }, - name, - isLoaded ? StrategyType.CACHE_FIRST : StrategyType.CACHE_AND_NETWORK, - ); - - if (!isLoaded) { - this.viewLoaded.add(name); - } - - return doc; - } - - async getInvitation (invitationId: string) { - return APIService.getInvitation(invitationId); - } - - async acceptInvitation (invitationId: string) { - return APIService.acceptInvitation(invitationId); - } - - approveRequestAccess (requestId: string): Promise { - return APIService.approveRequestAccess(requestId); - } - - getRequestAccessInfo (requestId: string) { - return APIService.getRequestAccessInfo(requestId); - } - - sendRequestAccess (workspaceId: string, viewId: string): Promise { - return APIService.sendRequestAccess(workspaceId, viewId); - } - - getSubscriptionLink (workspaceId: string, plan: SubscriptionPlan, interval: SubscriptionInterval) { - return APIService.getSubscriptionLink(workspaceId, plan, interval); - } - - cancelSubscription (workspaceId: string, plan: SubscriptionPlan, reason?: string) { - return APIService.cancelSubscription(workspaceId, plan, reason); - } - - getSubscriptions () { - return APIService.getSubscriptions(); - } - - getActiveSubscription (workspaceId: string) { - return APIService.getActiveSubscription(workspaceId); - } - - getWorkspaceSubscriptions (workspaceId: string) { - return APIService.getWorkspaceSubscriptions(workspaceId); - } - - registerDocUpdate (doc: Y.Doc, context: { - workspaceId: string, objectId: string, collabType: Types - }) { - const token = getTokenParsed(); - const userId = token?.user.id; - - if (!userId) { - throw new Error('User not found'); - } - - const sync = new SyncManager(doc, { userId, ...context }); - - sync.initialize(); - } - - async importFile (file: File, onProgress: (progress: number) => void) { - const task = await APIService.createImportTask(file); - - await APIService.uploadImportFile(task.presignedUrl, file, onProgress); - } - - async createSpace (workspaceId: string, payload: CreateSpacePayload) { - return APIService.createSpace(workspaceId, payload); - } - - async updateSpace (workspaceId: string, payload: UpdateSpacePayload) { - return APIService.updateSpace(workspaceId, payload); - } - - async addAppPage (workspaceId: string, parentViewId: string, payload: CreatePagePayload) { - return APIService.addAppPage(workspaceId, parentViewId, payload); - } - - async updateAppPage (workspaceId: string, viewId: string, data: UpdatePagePayload) { - return APIService.updatePage(workspaceId, viewId, data); - } - - async deleteTrash (workspaceId: string, viewId?: string) { - return APIService.deleteTrash(workspaceId, viewId); - } - - async moveToTrash (workspaceId: string, viewId: string) { - return APIService.moveToTrash(workspaceId, viewId); - } - - async restoreFromTrash (workspaceId: string, viewId?: string) { - return APIService.restorePage(workspaceId, viewId); - } - - async movePage (workspaceId: string, viewId: string, parentId: string, prevViewId?: string) { - return APIService.movePageTo(workspaceId, viewId, parentId, prevViewId); - } - - async uploadFile (workspaceId: string, viewId: string, file: File, onProgress?: (progress: number) => void) { - return APIService.uploadFile(workspaceId, viewId, file, onProgress); - } - - deleteWorkspace (workspaceId: string): Promise { - return APIService.deleteWorkspace(workspaceId); - } - - leaveWorkspace (workspaceId: string): Promise { - return APIService.leaveWorkspace(workspaceId); - } - - inviteMembers (workspaceId: string, emails: string[]): Promise { - return APIService.inviteMembers(workspaceId, emails); - } - - getWorkspaceMembers (workspaceId: string): Promise { - return APIService.getMembers(workspaceId); - } - - getQuickNoteList (workspaceId: string, params: { - offset?: number; - limit?: number; - searchTerm?: string; - }) { - return APIService.getQuickNoteList(workspaceId, params); - } - - createQuickNote (workspaceId: string, data: QuickNoteEditorData[]) { - return APIService.createQuickNote(workspaceId, data); - } - - updateQuickNote (workspaceId: string, id: string, data: QuickNoteEditorData[]) { - return APIService.updateQuickNote(workspaceId, id, data); - } - - deleteQuickNote (workspaceId: string, id: string) { - return APIService.deleteQuickNote(workspaceId, id); - } -} diff --git a/frontend/appflowy_web_app/src/application/services/js-services/sync.ts b/frontend/appflowy_web_app/src/application/services/js-services/sync.ts deleted file mode 100644 index 231d560581..0000000000 --- a/frontend/appflowy_web_app/src/application/services/js-services/sync.ts +++ /dev/null @@ -1,137 +0,0 @@ -import { updateCollab } from '@/application/services/js-services/http/http_api'; -import { CollabOrigin, Types } from '@/application/types'; -import { debounce } from 'lodash-es'; -import * as Y from 'yjs'; - -const VERSION_VECTOR_KEY = 'ydoc_version_vector'; -const UNSYNCED_FLAG_KEY = 'ydoc_unsynced_changes'; -const LAST_SYNCED_AT_KEY = 'ydoc_last_synced_at'; - -export class SyncManager { - - private versionVector: number; - - private lastSyncedAt: string; - - private hasUnsyncedChanges: boolean = false; - - private isSending = false; - - constructor (private doc: Y.Doc, private context: { - userId: string, workspaceId: string, objectId: string, collabType: Types - }) { - this.versionVector = this.loadVersionVector(); - this.hasUnsyncedChanges = this.loadUnsyncedFlag(); - this.lastSyncedAt = this.loadLastSyncedAt(); - this.setupListener(); - } - - private setupListener () { - this.doc.on('update', (_update: Uint8Array, origin: CollabOrigin) => { - if (origin === CollabOrigin.Remote) return; - - this.debouncedSendUpdate(); - }); - } - - private getStorageKey (baseKey: string): string { - return `${this.context.userId}_${baseKey}_${this.context.workspaceId}_${this.context.objectId}`; - } - - private loadVersionVector (): number { - const storedVector = localStorage.getItem(this.getStorageKey(VERSION_VECTOR_KEY)); - - return storedVector ? parseInt(storedVector, 10) : 0; - } - - private saveVersionVector () { - localStorage.setItem(this.getStorageKey(VERSION_VECTOR_KEY), this.versionVector.toString()); - } - - private loadUnsyncedFlag (): boolean { - return localStorage.getItem(this.getStorageKey(UNSYNCED_FLAG_KEY)) === 'true'; - } - - private saveUnsyncedFlag () { - localStorage.setItem(this.getStorageKey(UNSYNCED_FLAG_KEY), this.hasUnsyncedChanges.toString()); - } - - private loadLastSyncedAt (): string { - return localStorage.getItem(this.getStorageKey(LAST_SYNCED_AT_KEY)) || ''; - } - - private saveLastSyncedAt () { - localStorage.setItem(this.getStorageKey(LAST_SYNCED_AT_KEY), this.lastSyncedAt); - } - - private debouncedSendUpdate = debounce(() => { - this.hasUnsyncedChanges = true; - this.saveUnsyncedFlag(); - - void this.sendUpdate(); - }, 1000); // 1 second debounce - - private async sendUpdate () { - if (this.isSending) return; - this.isSending = true; - - try { - // Increment version vector before sending - this.versionVector++; - this.saveVersionVector(); - - const update = Y.encodeStateAsUpdate(this.doc); - const context = { version_vector: this.versionVector }; - - const response = await updateCollab(this.context.workspaceId, this.context.objectId, this.context.collabType, update, context); - - if (response) { - console.log(`Update sent successfully. Server version: ${response.version_vector}`); - - // Update last synced time - this.lastSyncedAt = String(Date.now()); - this.saveLastSyncedAt(); - - if (response.version_vector === this.versionVector) { - // Our update was the latest - this.hasUnsyncedChanges = false; - this.saveUnsyncedFlag(); - console.log('Local changes fully synced'); - } else { - // There are still unsynced changes (possibly from other clients) - this.hasUnsyncedChanges = true; - this.saveUnsyncedFlag(); - console.log('There are still unsynced changes'); - } - } else { - return Promise.reject(response); - } - } catch (error) { - console.error('Failed to send update:', error); - // Keep the unsynced flag as true - this.hasUnsyncedChanges = true; - this.saveUnsyncedFlag(); - } finally { - this.isSending = false; - } - } - - public initialize () { - if (this.hasUnsyncedChanges) { - // Send an update if there are unsynced changes - this.debouncedSendUpdate(); - } - } - - public getUnsyncedStatus (): boolean { - return this.hasUnsyncedChanges; - } - - public getLastSyncedAt (): string { - return this.lastSyncedAt; - } - - public getCurrentVersionVector (): number { - return this.versionVector; - } -} \ No newline at end of file diff --git a/frontend/appflowy_web_app/src/application/services/services.type.ts b/frontend/appflowy_web_app/src/application/services/services.type.ts deleted file mode 100644 index 0788a91827..0000000000 --- a/frontend/appflowy_web_app/src/application/services/services.type.ts +++ /dev/null @@ -1,155 +0,0 @@ -import { - Invitation, - DuplicatePublishView, - FolderView, - User, - UserWorkspaceInfo, - View, - Workspace, - YDoc, - DatabaseRelations, - GetRequestAccessInfoResponse, - Subscriptions, - SubscriptionPlan, - SubscriptionInterval, - Types, - UpdatePagePayload, - CreatePagePayload, - CreateSpacePayload, - UpdateSpacePayload, - WorkspaceMember, - QuickNoteEditorData, - QuickNote, Subscription, CreateWorkspacePayload, -} from '@/application/types'; -import { GlobalComment, Reaction } from '@/application/comment.type'; -import { ViewMeta } from '@/application/db/tables/view_metas'; -import { - Template, - TemplateCategory, - TemplateCategoryFormValues, - TemplateCreator, TemplateCreatorFormValues, TemplateSummary, - UploadTemplatePayload, -} from '@/application/template.type'; - -export type AFService = PublishService & AppService & WorkspaceService & TemplateService & QuickNoteService & { - getClientId: () => string; -}; - -export interface AFServiceConfig { - cloudConfig: AFCloudConfig; -} - -export interface AFCloudConfig { - baseURL: string; - gotrueURL: string; - wsURL: string; -} - -export interface WorkspaceService { - openWorkspace: (workspaceId: string) => Promise; - createWorkspace: (payload: CreateWorkspacePayload) => Promise; - leaveWorkspace: (workspaceId: string) => Promise; - deleteWorkspace: (workspaceId: string) => Promise; - getWorkspaceMembers: (workspaceId: string) => Promise; - inviteMembers: (workspaceId: string, emails: string[]) => Promise; -} - -export interface AppService { - getPageDoc: (workspaceId: string, viewId: string, errorCallback?: (error: { - code: number; - }) => void) => Promise; - createRowDoc: (rowKey: string) => Promise; - deleteRowDoc: (rowKey: string) => void; - getAppDatabaseViewRelations: (workspaceId: string, databaseStorageId: string) => Promise; - getAppOutline: (workspaceId: string) => Promise; - getAppView: (workspaceId: string, viewId: string) => Promise; - getAppFavorites: (workspaceId: string) => Promise; - getAppRecent: (workspaceId: string) => Promise; - getAppTrash: (workspaceId: string) => Promise; - loginAuth: (url: string) => Promise; - signInMagicLink: (params: { email: string; redirectTo: string }) => Promise; - signInGoogle: (params: { redirectTo: string }) => Promise; - signInGithub: (params: { redirectTo: string }) => Promise; - signInDiscord: (params: { redirectTo: string }) => Promise; - signInApple: (params: { redirectTo: string }) => Promise; - getWorkspaces: () => Promise; - getWorkspaceFolder: (workspaceId: string) => Promise; - getCurrentUser: () => Promise; - getUserWorkspaceInfo: () => Promise; - uploadTemplateAvatar: (file: File) => Promise; - getInvitation: (invitationId: string) => Promise; - acceptInvitation: (invitationId: string) => Promise; - getRequestAccessInfo: (requestId: string) => Promise; - approveRequestAccess: (requestId: string) => Promise; - sendRequestAccess: (workspaceId: string, viewId: string) => Promise; - getSubscriptionLink: (workspaceId: string, plan: SubscriptionPlan, interval: SubscriptionInterval) => Promise; - getSubscriptions: () => Promise; - cancelSubscription: (workspaceId: string, plan: SubscriptionPlan, reason?: string) => Promise; - getActiveSubscription: (workspaceId: string) => Promise; - getWorkspaceSubscriptions: (workspaceId: string) => Promise; - registerDocUpdate: (doc: YDoc, context: { - workspaceId: string, objectId: string, collabType: Types - }) => void; - importFile: (file: File, onProgress: (progress: number) => void) => Promise; - createSpace: (workspaceId: string, payload: CreateSpacePayload) => Promise; - updateSpace: (workspaceId: string, payload: UpdateSpacePayload) => Promise; - addAppPage: (workspaceId: string, parentViewId: string, payload: CreatePagePayload) => Promise; - updateAppPage: (workspaceId: string, viewId: string, data: UpdatePagePayload) => Promise; - deleteTrash: (workspaceId: string, viewId?: string) => Promise; - moveToTrash: (workspaceId: string, viewId: string) => Promise; - restoreFromTrash: (workspaceId: string, viewId?: string) => Promise; - movePage: (workspaceId: string, viewId: string, parentId: string, prevViewId?: string) => Promise; - uploadFile: (workspaceId: string, viewId: string, file: File, onProgress?: (progress: number) => void) => Promise; -} - -export interface QuickNoteService { - getQuickNoteList: (workspaceId: string, params: { - offset?: number; - limit?: number; - searchTerm?: string; - }) => Promise<{ - data: QuickNote[]; - has_more: boolean; - }>; - createQuickNote: (workspaceId: string, data: QuickNoteEditorData[]) => Promise; - updateQuickNote: (workspaceId: string, id: string, data: QuickNoteEditorData[]) => Promise; - deleteQuickNote: (workspaceId: string, id: string) => Promise; -} - -export interface TemplateService { - getTemplateCategories: () => Promise; - addTemplateCategory: (category: TemplateCategoryFormValues) => Promise; - deleteTemplateCategory: (categoryId: string) => Promise; - getTemplateCreators: () => Promise; - createTemplateCreator: (creator: TemplateCreatorFormValues) => Promise; - deleteTemplateCreator: (creatorId: string) => Promise; - getTemplateById: (id: string) => Promise