mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
Merge branch 'main' into feat/support-get-encoded-collab-event
This commit is contained in:
commit
be4d60ca0d
83
.github/workflows/web_ci.yaml
vendored
83
.github/workflows/web_ci.yaml
vendored
@ -1,83 +0,0 @@
|
|||||||
name: WEB-CI
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
inputs:
|
|
||||||
build:
|
|
||||||
description: 'Build the web app'
|
|
||||||
required: true
|
|
||||||
default: 'true'
|
|
||||||
|
|
||||||
env:
|
|
||||||
CARGO_TERM_COLOR: always
|
|
||||||
NODE_VERSION: "18.16.0"
|
|
||||||
PNPM_VERSION: "8.5.0"
|
|
||||||
RUST_TOOLCHAIN: "1.77.2"
|
|
||||||
CARGO_MAKE_VERSION: "0.36.6"
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
web-build:
|
|
||||||
if: github.event.pull_request.draft != true
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
platform: [ ubuntu-latest ]
|
|
||||||
|
|
||||||
runs-on: ${{ matrix.platform }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: setup node
|
|
||||||
uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: ${{ env.NODE_VERSION }}
|
|
||||||
|
|
||||||
- name: Cache Rust Dependencies
|
|
||||||
uses: Swatinem/rust-cache@v2
|
|
||||||
with:
|
|
||||||
key: rust-dependencies-${{ runner.os }}
|
|
||||||
workspaces: |
|
|
||||||
frontend/rust-lib
|
|
||||||
frontend/appflowy_web/appflowy_wasm
|
|
||||||
|
|
||||||
# TODO: Can combine caching deps and node_modules in one
|
|
||||||
# See Glob patterns: https://github.com/actions/toolkit/tree/main/packages/glob
|
|
||||||
- name: Cache Node.js dependencies
|
|
||||||
uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: ~/.npm
|
|
||||||
key: npm-${{ runner.os }}
|
|
||||||
|
|
||||||
- name: Cache node_modules
|
|
||||||
uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: frontend/appflowy_web/node_modules
|
|
||||||
key: node-modules-${{ runner.os }}
|
|
||||||
|
|
||||||
- name: Install Rust toolchain
|
|
||||||
id: rust_toolchain
|
|
||||||
uses: actions-rs/toolchain@v1
|
|
||||||
with:
|
|
||||||
toolchain: ${{ env.RUST_TOOLCHAIN }}
|
|
||||||
override: true
|
|
||||||
profile: minimal
|
|
||||||
|
|
||||||
- name: Install wasm-pack
|
|
||||||
run: cargo install wasm-pack
|
|
||||||
|
|
||||||
- uses: taiki-e/install-action@v2
|
|
||||||
with:
|
|
||||||
tool: cargo-make@${{ env.CARGO_MAKE_VERSION }}
|
|
||||||
|
|
||||||
- name: install dependencies
|
|
||||||
if: matrix.platform == 'ubuntu-latest'
|
|
||||||
working-directory: frontend
|
|
||||||
run: |
|
|
||||||
sudo apt-get update
|
|
||||||
npm install -g pnpm@${{ env.PNPM_VERSION }}
|
|
||||||
cargo make install_web_protobuf
|
|
||||||
|
|
||||||
- name: Build
|
|
||||||
working-directory: frontend/appflowy_web
|
|
||||||
run: |
|
|
||||||
pnpm install
|
|
||||||
pnpm run build_release_wasm
|
|
@ -1,7 +1,13 @@
|
|||||||
# Release Notes
|
# Release Notes
|
||||||
## Version 0.6.2 - 01/07/2024
|
## Version 0.6.2 - 01/07/2024
|
||||||
### New Features
|
### New Features
|
||||||
|
- Added support for duplicating spaces.
|
||||||
|
- Added support for moving pages across spaces.
|
||||||
|
- Undo markdown formatting with `Ctrl + Z` or `Cmd + Z`.
|
||||||
|
- Improved shortcuts settings UI.
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
- Fixed unable to zoom in with `Ctrl` and `+` or `Cmd` and `+` on some keyboards.
|
||||||
|
- Fixed unable to paste nested lists in existing lists.
|
||||||
|
|
||||||
## Version 0.6.1 - 22/06/2024
|
## Version 0.6.1 - 22/06/2024
|
||||||
### New Features
|
### New Features
|
||||||
|
@ -50,7 +50,6 @@ APP_ENVIRONMENT = "local"
|
|||||||
FLUTTER_FLOWY_SDK_PATH = "appflowy_flutter/packages/appflowy_backend"
|
FLUTTER_FLOWY_SDK_PATH = "appflowy_flutter/packages/appflowy_backend"
|
||||||
TAURI_BACKEND_SERVICE_PATH = "appflowy_tauri/src/services/backend"
|
TAURI_BACKEND_SERVICE_PATH = "appflowy_tauri/src/services/backend"
|
||||||
WEB_BACKEND_SERVICE_PATH = "appflowy_web/src/services/backend"
|
WEB_BACKEND_SERVICE_PATH = "appflowy_web/src/services/backend"
|
||||||
WEB_LIB_PATH = "appflowy_web/wasm-libs/af-wasm"
|
|
||||||
TAURI_APP_BACKEND_SERVICE_PATH = "appflowy_web_app/src/application/services/tauri-services/backend"
|
TAURI_APP_BACKEND_SERVICE_PATH = "appflowy_web_app/src/application/services/tauri-services/backend"
|
||||||
# Test default config
|
# Test default config
|
||||||
TEST_CRATE_TYPE = "cdylib"
|
TEST_CRATE_TYPE = "cdylib"
|
||||||
|
@ -170,7 +170,7 @@ SPEC CHECKSUMS:
|
|||||||
file_picker: 09aa5ec1ab24135ccd7a1621c46c84134bfd6655
|
file_picker: 09aa5ec1ab24135ccd7a1621c46c84134bfd6655
|
||||||
flowy_infra_ui: 0455e1fa8c51885aa1437848e361e99419f34ebc
|
flowy_infra_ui: 0455e1fa8c51885aa1437848e361e99419f34ebc
|
||||||
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
||||||
fluttertoast: 31b00dabfa7fb7bacd9e7dbee580d7a2ff4bf265
|
fluttertoast: fafc4fa4d01a6a9e4f772ecd190ffa525e9e2d9c
|
||||||
image_gallery_saver: cb43cc43141711190510e92c460eb1655cd343cb
|
image_gallery_saver: cb43cc43141711190510e92c460eb1655cd343cb
|
||||||
image_picker_ios: 99dfe1854b4fa34d0364e74a78448a0151025425
|
image_picker_ios: 99dfe1854b4fa34d0364e74a78448a0151025425
|
||||||
integration_test: ce0a3ffa1de96d1a89ca0ac26fca7ea18a749ef4
|
integration_test: ce0a3ffa1de96d1a89ca0ac26fca7ea18a749ef4
|
||||||
@ -191,4 +191,4 @@ SPEC CHECKSUMS:
|
|||||||
|
|
||||||
PODFILE CHECKSUM: d0d9b4ff572d8695c38eb3f9b490f55cdfc57eca
|
PODFILE CHECKSUM: d0d9b4ff572d8695c38eb3f9b490f55cdfc57eca
|
||||||
|
|
||||||
COCOAPODS: 1.15.2
|
COCOAPODS: 1.11.3
|
||||||
|
@ -71,7 +71,6 @@ class AIChatPagePlugin extends Plugin {
|
|||||||
void dispose() {
|
void dispose() {
|
||||||
_viewInfoBloc.close();
|
_viewInfoBloc.close();
|
||||||
notifier.dispose();
|
notifier.dispose();
|
||||||
super.dispose();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,4 +118,7 @@ class AIChatPagePluginWidgetBuilder extends PluginWidgetBuilder
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
List<NavigationItem> get navigationItems => [this];
|
List<NavigationItem> get navigationItems => [this];
|
||||||
|
|
||||||
|
@override
|
||||||
|
EdgeInsets get contentPadding => EdgeInsets.zero;
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
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_bloc.dart';
|
||||||
import 'package:appflowy/plugins/ai_chat/presentation/ai_message_bubble.dart';
|
import 'package:appflowy/plugins/ai_chat/presentation/ai_message_bubble.dart';
|
||||||
@ -10,12 +13,10 @@ import 'package:easy_localization/easy_localization.dart';
|
|||||||
import 'package:flowy_infra/theme_extension.dart';
|
import 'package:flowy_infra/theme_extension.dart';
|
||||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||||
import 'package:flowy_infra_ui/widget/spacing.dart';
|
import 'package:flowy_infra_ui/widget/spacing.dart';
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:flutter_chat_types/flutter_chat_types.dart';
|
import 'package:flutter_chat_types/flutter_chat_types.dart';
|
||||||
import 'package:flutter_chat_ui/flutter_chat_ui.dart' show Chat;
|
|
||||||
import 'package:flutter_chat_types/flutter_chat_types.dart' as types;
|
import 'package:flutter_chat_types/flutter_chat_types.dart' as types;
|
||||||
|
import 'package:flutter_chat_ui/flutter_chat_ui.dart' show Chat;
|
||||||
|
|
||||||
import 'presentation/chat_input.dart';
|
import 'presentation/chat_input.dart';
|
||||||
import 'presentation/chat_popmenu.dart';
|
import 'presentation/chat_popmenu.dart';
|
||||||
@ -27,11 +28,11 @@ import 'presentation/message/user_text_message.dart';
|
|||||||
|
|
||||||
class AIChatUILayout {
|
class AIChatUILayout {
|
||||||
static EdgeInsets get chatPadding =>
|
static EdgeInsets get chatPadding =>
|
||||||
isMobile ? EdgeInsets.zero : const EdgeInsets.symmetric(horizontal: 70);
|
isMobile ? EdgeInsets.zero : const EdgeInsets.symmetric(horizontal: 20);
|
||||||
|
|
||||||
static EdgeInsets get welcomePagePadding => isMobile
|
static EdgeInsets get welcomePagePadding => isMobile
|
||||||
? const EdgeInsets.symmetric(horizontal: 20)
|
? const EdgeInsets.symmetric(horizontal: 20)
|
||||||
: const EdgeInsets.symmetric(horizontal: 100);
|
: const EdgeInsets.symmetric(horizontal: 50);
|
||||||
|
|
||||||
static double get messageWidthRatio => 0.85;
|
static double get messageWidthRatio => 0.85;
|
||||||
|
|
||||||
@ -44,7 +45,8 @@ class AIChatUILayout {
|
|||||||
query.padding.right,
|
query.padding.right,
|
||||||
query.viewInsets.bottom + query.padding.bottom,
|
query.viewInsets.bottom + query.padding.bottom,
|
||||||
)
|
)
|
||||||
: const EdgeInsets.symmetric(horizontal: 70);
|
: const EdgeInsets.symmetric(horizontal: 50) +
|
||||||
|
const EdgeInsets.only(bottom: 20);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,92 +79,89 @@ class _AIChatPageState extends State<AIChatPage> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if (widget.userProfile.authenticator == AuthenticatorPB.AppFlowyCloud) {
|
if (widget.userProfile.authenticator == AuthenticatorPB.AppFlowyCloud) {
|
||||||
return buildChatWidget();
|
return buildChatWidget();
|
||||||
} else {
|
|
||||||
return Center(
|
|
||||||
child: FlowyText(
|
|
||||||
LocaleKeys.chat_unsupportedCloudPrompt.tr(),
|
|
||||||
fontSize: 20,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return Center(
|
||||||
|
child: FlowyText(
|
||||||
|
LocaleKeys.chat_unsupportedCloudPrompt.tr(),
|
||||||
|
fontSize: 20,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget buildChatWidget() {
|
Widget buildChatWidget() {
|
||||||
return SizedBox.expand(
|
return Row(
|
||||||
child: Padding(
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
padding: AIChatUILayout.chatPadding,
|
children: [
|
||||||
child: BlocProvider(
|
Flexible(
|
||||||
create: (context) => ChatBloc(
|
child: ConstrainedBox(
|
||||||
view: widget.view,
|
constraints: const BoxConstraints(maxWidth: 784),
|
||||||
userProfile: widget.userProfile,
|
child: BlocProvider(
|
||||||
)..add(const ChatEvent.initialLoad()),
|
create: (_) => ChatBloc(
|
||||||
child: BlocBuilder<ChatBloc, ChatState>(
|
view: widget.view,
|
||||||
builder: (blocContext, state) {
|
userProfile: widget.userProfile,
|
||||||
return Chat(
|
)..add(const ChatEvent.initialLoad()),
|
||||||
messages: state.messages,
|
child: BlocBuilder<ChatBloc, ChatState>(
|
||||||
onAttachmentPressed: () {},
|
builder: (blocContext, state) => Chat(
|
||||||
onSendPressed: (types.PartialText message) {
|
messages: state.messages,
|
||||||
// We use custom bottom widget for chat input, so
|
onSendPressed: (_) {
|
||||||
// do not need to handle this event.
|
// We use custom bottom widget for chat input, so
|
||||||
},
|
// do not need to handle this event.
|
||||||
customBottomWidget: buildChatInput(blocContext),
|
},
|
||||||
user: _user,
|
customBottomWidget: buildChatInput(blocContext),
|
||||||
theme: buildTheme(context),
|
user: _user,
|
||||||
onEndReached: () async {
|
theme: buildTheme(context),
|
||||||
if (state.hasMorePrevMessage &&
|
onEndReached: () async {
|
||||||
state.loadingPreviousStatus !=
|
if (state.hasMorePrevMessage &&
|
||||||
const LoadingState.loading()) {
|
state.loadingPreviousStatus !=
|
||||||
blocContext
|
const LoadingState.loading()) {
|
||||||
.read<ChatBloc>()
|
blocContext
|
||||||
.add(const ChatEvent.startLoadingPrevMessage());
|
.read<ChatBloc>()
|
||||||
}
|
.add(const ChatEvent.startLoadingPrevMessage());
|
||||||
},
|
}
|
||||||
emptyState: BlocBuilder<ChatBloc, ChatState>(
|
},
|
||||||
builder: (context, state) {
|
emptyState: BlocBuilder<ChatBloc, ChatState>(
|
||||||
return state.initialLoadingStatus ==
|
builder: (_, state) => state.initialLoadingStatus ==
|
||||||
const LoadingState.finish()
|
const LoadingState.finish()
|
||||||
? Padding(
|
? Padding(
|
||||||
padding: AIChatUILayout.welcomePagePadding,
|
padding: AIChatUILayout.welcomePagePadding,
|
||||||
child: ChatWelcomePage(
|
child: ChatWelcomePage(
|
||||||
onSelectedQuestion: (question) {
|
onSelectedQuestion: (question) => blocContext
|
||||||
blocContext
|
.read<ChatBloc>()
|
||||||
.read<ChatBloc>()
|
.add(ChatEvent.sendMessage(question)),
|
||||||
.add(ChatEvent.sendMessage(question));
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: const Center(
|
: const Center(
|
||||||
child: CircularProgressIndicator.adaptive(),
|
child: CircularProgressIndicator.adaptive(),
|
||||||
);
|
),
|
||||||
|
),
|
||||||
|
messageWidthRatio: AIChatUILayout.messageWidthRatio,
|
||||||
|
textMessageBuilder: (
|
||||||
|
textMessage, {
|
||||||
|
required messageWidth,
|
||||||
|
required showName,
|
||||||
|
}) =>
|
||||||
|
_buildAITextMessage(blocContext, textMessage),
|
||||||
|
bubbleBuilder: (
|
||||||
|
child, {
|
||||||
|
required message,
|
||||||
|
required nextMessageInGroup,
|
||||||
|
}) {
|
||||||
|
if (message.author.id == _user.id) {
|
||||||
|
return ChatUserMessageBubble(
|
||||||
|
message: message,
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _buildAIBubble(message, blocContext, state, child);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
messageWidthRatio: AIChatUILayout.messageWidthRatio,
|
),
|
||||||
textMessageBuilder: (
|
),
|
||||||
textMessage, {
|
|
||||||
required messageWidth,
|
|
||||||
required showName,
|
|
||||||
}) {
|
|
||||||
return _buildAITextMessage(blocContext, textMessage);
|
|
||||||
},
|
|
||||||
bubbleBuilder: (
|
|
||||||
child, {
|
|
||||||
required message,
|
|
||||||
required nextMessageInGroup,
|
|
||||||
}) {
|
|
||||||
if (message.author.id == _user.id) {
|
|
||||||
return ChatUserMessageBubble(
|
|
||||||
message: message,
|
|
||||||
child: child,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return _buildAIBubble(message, blocContext, state, child);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
||||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
import 'chat_input.dart';
|
import 'chat_input.dart';
|
||||||
|
|
||||||
@ -31,19 +32,15 @@ class ChatWelcomePage extends StatelessWidget {
|
|||||||
size: Size.square(44),
|
size: Size.square(44),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 40),
|
const SizedBox(height: 40),
|
||||||
GridView.builder(
|
Wrap(
|
||||||
shrinkWrap: true,
|
children: items
|
||||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
.map(
|
||||||
crossAxisCount: isMobile ? 2 : 4,
|
(i) => WelcomeQuestion(
|
||||||
crossAxisSpacing: 6,
|
question: i,
|
||||||
mainAxisSpacing: 6,
|
onSelected: onSelectedQuestion,
|
||||||
childAspectRatio: 16.0 / 9.0,
|
),
|
||||||
),
|
)
|
||||||
itemCount: items.length,
|
.toList(),
|
||||||
itemBuilder: (context, index) => WelcomeQuestion(
|
|
||||||
question: items[index],
|
|
||||||
onSelected: onSelectedQuestion,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import 'package:appflowy/plugins/database/grid/presentation/layout/sizes.dart';
|
|
||||||
import 'package:appflowy/plugins/database/widgets/row/cells/cell_container.dart';
|
|
||||||
import 'package:appflowy/plugins/database/widgets/cell_editor/extension.dart';
|
|
||||||
import 'package:appflowy/plugins/database/application/cell/bloc/select_option_cell_bloc.dart';
|
import 'package:appflowy/plugins/database/application/cell/bloc/select_option_cell_bloc.dart';
|
||||||
|
import 'package:appflowy/plugins/database/grid/presentation/layout/sizes.dart';
|
||||||
|
import 'package:appflowy/plugins/database/widgets/cell_editor/extension.dart';
|
||||||
import 'package:appflowy/plugins/database/widgets/cell_editor/select_option_cell_editor.dart';
|
import 'package:appflowy/plugins/database/widgets/cell_editor/select_option_cell_editor.dart';
|
||||||
|
import 'package:appflowy/plugins/database/widgets/row/cells/cell_container.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
@ -56,7 +56,7 @@ class DesktopGridSelectOptionCellSkin extends IEditableSelectOptionCellSkin {
|
|||||||
child: SelectOptionTag(
|
child: SelectOptionTag(
|
||||||
option: option,
|
option: option,
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
vertical: 1,
|
vertical: 4,
|
||||||
horizontal: 8,
|
horizontal: 8,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
import 'package:appflowy/plugins/database/widgets/row/cells/cell_container.dart';
|
|
||||||
import 'package:appflowy/plugins/database/widgets/cell_editor/extension.dart';
|
|
||||||
import 'package:appflowy/plugins/database/application/cell/bloc/select_option_cell_bloc.dart';
|
import 'package:appflowy/plugins/database/application/cell/bloc/select_option_cell_bloc.dart';
|
||||||
|
import 'package:appflowy/plugins/database/widgets/cell_editor/extension.dart';
|
||||||
import 'package:appflowy/plugins/database/widgets/cell_editor/select_option_cell_editor.dart';
|
import 'package:appflowy/plugins/database/widgets/cell_editor/select_option_cell_editor.dart';
|
||||||
|
import 'package:appflowy/plugins/database/widgets/row/cells/cell_container.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
@ -67,7 +67,7 @@ class DesktopRowDetailSelectOptionCellSkin
|
|||||||
return SelectOptionTag(
|
return SelectOptionTag(
|
||||||
option: option,
|
option: option,
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
vertical: 1,
|
vertical: 4,
|
||||||
horizontal: 8,
|
horizontal: 8,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -96,7 +96,6 @@ class SelectOptionTag extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
height: 20,
|
|
||||||
padding: onRemove == null ? padding : padding.copyWith(right: 2.0),
|
padding: onRemove == null ? padding : padding.copyWith(right: 2.0),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: optionColor,
|
color: optionColor,
|
||||||
|
@ -1,10 +1,6 @@
|
|||||||
import 'dart:collection';
|
import 'dart:collection';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
|
|
||||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
import 'package:appflowy/plugins/database/application/cell/bloc/select_option_cell_editor_bloc.dart';
|
import 'package:appflowy/plugins/database/application/cell/bloc/select_option_cell_editor_bloc.dart';
|
||||||
@ -14,12 +10,14 @@ import 'package:appflowy_popover/appflowy_popover.dart';
|
|||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flowy_infra/theme_extension.dart';
|
import 'package:flowy_infra/theme_extension.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
import '../../grid/presentation/layout/sizes.dart';
|
import '../../grid/presentation/layout/sizes.dart';
|
||||||
import '../../grid/presentation/widgets/common/type_option_separator.dart';
|
import '../../grid/presentation/widgets/common/type_option_separator.dart';
|
||||||
import '../field/type_option_editor/select/select_option_editor.dart';
|
import '../field/type_option_editor/select/select_option_editor.dart';
|
||||||
|
|
||||||
import 'extension.dart';
|
import 'extension.dart';
|
||||||
import 'select_option_text_field.dart';
|
import 'select_option_text_field.dart';
|
||||||
|
|
||||||
@ -476,11 +474,11 @@ class SelectOptionTagCell extends StatelessWidget {
|
|||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
horizontal: 6.0,
|
horizontal: 6.0,
|
||||||
vertical: 4.0,
|
|
||||||
),
|
),
|
||||||
child: SelectOptionTag(
|
child: SelectOptionTag(
|
||||||
option: option,
|
option: option,
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
padding:
|
||||||
|
const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -132,7 +132,10 @@ class _SelectOptionTextFieldState extends State<SelectOptionTextField> {
|
|||||||
(option) => SelectOptionTag(
|
(option) => SelectOptionTag(
|
||||||
option: option,
|
option: option,
|
||||||
onRemove: (option) => widget.onRemove(option),
|
onRemove: (option) => widget.onRemove(option),
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 1),
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 8,
|
||||||
|
vertical: 4,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.toList();
|
.toList();
|
||||||
|
@ -8,7 +8,6 @@ import 'package:appflowy/plugins/document/application/document_data_pb_extension
|
|||||||
import 'package:appflowy/plugins/document/application/document_listener.dart';
|
import 'package:appflowy/plugins/document/application/document_listener.dart';
|
||||||
import 'package:appflowy/plugins/document/application/document_service.dart';
|
import 'package:appflowy/plugins/document/application/document_service.dart';
|
||||||
import 'package:appflowy/plugins/document/application/editor_transaction_adapter.dart';
|
import 'package:appflowy/plugins/document/application/editor_transaction_adapter.dart';
|
||||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/migration/editor_migration.dart';
|
|
||||||
import 'package:appflowy/plugins/trash/application/trash_service.dart';
|
import 'package:appflowy/plugins/trash/application/trash_service.dart';
|
||||||
import 'package:appflowy/shared/feature_flags.dart';
|
import 'package:appflowy/shared/feature_flags.dart';
|
||||||
import 'package:appflowy/startup/startup.dart';
|
import 'package:appflowy/startup/startup.dart';
|
||||||
@ -19,7 +18,6 @@ import 'package:appflowy/util/color_to_hex_string.dart';
|
|||||||
import 'package:appflowy/util/debounce.dart';
|
import 'package:appflowy/util/debounce.dart';
|
||||||
import 'package:appflowy/util/throttle.dart';
|
import 'package:appflowy/util/throttle.dart';
|
||||||
import 'package:appflowy/workspace/application/view/view_listener.dart';
|
import 'package:appflowy/workspace/application/view/view_listener.dart';
|
||||||
import 'package:appflowy/workspace/application/view/view_service.dart';
|
|
||||||
import 'package:appflowy_backend/protobuf/flowy-document/entities.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-document/entities.pb.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-document/protobuf.dart';
|
import 'package:appflowy_backend/protobuf/flowy-document/protobuf.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||||
@ -118,11 +116,6 @@ class DocumentBloc extends Bloc<DocumentEvent, DocumentState> {
|
|||||||
final result = await _fetchDocumentState();
|
final result = await _fetchDocumentState();
|
||||||
_onViewChanged();
|
_onViewChanged();
|
||||||
_onDocumentChanged();
|
_onDocumentChanged();
|
||||||
result.onSuccess((s) {
|
|
||||||
if (s != null) {
|
|
||||||
_migrateCover(s);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
final newState = await result.fold(
|
final newState = await result.fold(
|
||||||
(s) async {
|
(s) async {
|
||||||
final userProfilePB =
|
final userProfilePB =
|
||||||
@ -390,14 +383,6 @@ class DocumentBloc extends Bloc<DocumentEvent, DocumentState> {
|
|||||||
metadata: jsonEncode(metadata.toJson()),
|
metadata: jsonEncode(metadata.toJson()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// from version 0.5.5, the cover is stored in the view.ext
|
|
||||||
Future<void> _migrateCover(EditorState editorState) async {
|
|
||||||
final view = await ViewBackendService.getView(documentId);
|
|
||||||
view.onSuccess((s) {
|
|
||||||
return EditorMigration.migrateCoverIfNeeded(s, editorState);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@freezed
|
@freezed
|
||||||
|
@ -214,7 +214,11 @@ class _AppFlowyEditorPageState extends State<AppFlowyEditorPage> {
|
|||||||
|
|
||||||
AFFocusManager? focusManager;
|
AFFocusManager? focusManager;
|
||||||
|
|
||||||
void _loseFocus() => widget.editorState.selection = null;
|
void _loseFocus() {
|
||||||
|
if (!widget.editorState.isDisposed) {
|
||||||
|
widget.editorState.selection = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
|
@ -196,6 +196,7 @@ class _CalloutBlockComponentWidgetState
|
|||||||
), // force to refresh the popover state
|
), // force to refresh the popover state
|
||||||
title: '',
|
title: '',
|
||||||
emoji: emoji,
|
emoji: emoji,
|
||||||
|
emojiSize: 16.0,
|
||||||
onSubmitted: (emoji, controller) {
|
onSubmitted: (emoji, controller) {
|
||||||
setEmoji(emoji);
|
setEmoji(emoji);
|
||||||
controller?.close();
|
controller?.close();
|
||||||
@ -204,7 +205,7 @@ class _CalloutBlockComponentWidgetState
|
|||||||
),
|
),
|
||||||
Flexible(
|
Flexible(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
padding: const EdgeInsets.symmetric(vertical: 6.0),
|
||||||
child: buildCalloutBlockComponent(context, textDirection),
|
child: buildCalloutBlockComponent(context, textDirection),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -91,6 +91,7 @@ class _DocumentCoverWidgetState extends State<DocumentCoverWidget> {
|
|||||||
|
|
||||||
String viewIcon = '';
|
String viewIcon = '';
|
||||||
PageStyleCover? cover;
|
PageStyleCover? cover;
|
||||||
|
late ViewPB view;
|
||||||
late final ViewListener viewListener;
|
late final ViewListener viewListener;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -99,6 +100,7 @@ class _DocumentCoverWidgetState extends State<DocumentCoverWidget> {
|
|||||||
final value = widget.view.icon.value;
|
final value = widget.view.icon.value;
|
||||||
viewIcon = value.isNotEmpty ? value : icon ?? '';
|
viewIcon = value.isNotEmpty ? value : icon ?? '';
|
||||||
cover = widget.view.cover;
|
cover = widget.view.cover;
|
||||||
|
view = widget.view;
|
||||||
widget.node.addListener(_reload);
|
widget.node.addListener(_reload);
|
||||||
viewListener = ViewListener(
|
viewListener = ViewListener(
|
||||||
viewId: widget.view.id,
|
viewId: widget.view.id,
|
||||||
@ -107,6 +109,7 @@ class _DocumentCoverWidgetState extends State<DocumentCoverWidget> {
|
|||||||
setState(() {
|
setState(() {
|
||||||
viewIcon = p0.icon.value;
|
viewIcon = p0.icon.value;
|
||||||
cover = p0.cover;
|
cover = p0.cover;
|
||||||
|
view = p0;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -137,7 +140,7 @@ class _DocumentCoverWidgetState extends State<DocumentCoverWidget> {
|
|||||||
),
|
),
|
||||||
if (hasCover)
|
if (hasCover)
|
||||||
DocumentCover(
|
DocumentCover(
|
||||||
view: widget.view,
|
view: view,
|
||||||
editorState: widget.editorState,
|
editorState: widget.editorState,
|
||||||
node: widget.node,
|
node: widget.node,
|
||||||
coverType: coverType,
|
coverType: coverType,
|
||||||
@ -204,7 +207,7 @@ class _DocumentCoverWidgetState extends State<DocumentCoverWidget> {
|
|||||||
// compatible with version > 0.5.5.
|
// compatible with version > 0.5.5.
|
||||||
EditorMigration.migrateCoverIfNeeded(
|
EditorMigration.migrateCoverIfNeeded(
|
||||||
widget.view,
|
widget.view,
|
||||||
widget.editorState,
|
attributes,
|
||||||
overwrite: true,
|
overwrite: true,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -164,18 +164,17 @@ class EditorMigration {
|
|||||||
// Now, the cover is stored in the view.ext.
|
// Now, the cover is stored in the view.ext.
|
||||||
static void migrateCoverIfNeeded(
|
static void migrateCoverIfNeeded(
|
||||||
ViewPB view,
|
ViewPB view,
|
||||||
EditorState editorState, {
|
Attributes attributes, {
|
||||||
bool overwrite = false,
|
bool overwrite = false,
|
||||||
}) async {
|
}) async {
|
||||||
if (view.extra.isNotEmpty && !overwrite) {
|
if (view.extra.isNotEmpty && !overwrite) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final root = editorState.document.root;
|
|
||||||
final coverType = CoverType.fromString(
|
final coverType = CoverType.fromString(
|
||||||
root.attributes[DocumentHeaderBlockKeys.coverType],
|
attributes[DocumentHeaderBlockKeys.coverType],
|
||||||
);
|
);
|
||||||
final coverDetails = root.attributes[DocumentHeaderBlockKeys.coverDetails];
|
final coverDetails = attributes[DocumentHeaderBlockKeys.coverDetails];
|
||||||
|
|
||||||
Map extra = {};
|
Map extra = {};
|
||||||
|
|
||||||
|
@ -0,0 +1,195 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:appflowy/plugins/ai_chat/application/chat_bloc.dart';
|
||||||
|
import 'package:appflowy_backend/dispatch/dispatch.dart';
|
||||||
|
import 'package:appflowy_backend/log.dart';
|
||||||
|
import 'package:appflowy_backend/protobuf/flowy-chat/entities.pb.dart';
|
||||||
|
import 'package:bloc/bloc.dart';
|
||||||
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
import 'package:path/path.dart' as path;
|
||||||
|
import 'package:protobuf/protobuf.dart';
|
||||||
|
|
||||||
|
part 'setting_local_ai_bloc.freezed.dart';
|
||||||
|
|
||||||
|
class SettingsAILocalBloc
|
||||||
|
extends Bloc<SettingsAILocalEvent, SettingsAILocalState> {
|
||||||
|
SettingsAILocalBloc() : super(const SettingsAILocalState()) {
|
||||||
|
on<SettingsAILocalEvent>(_handleEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handles incoming events and dispatches them to the appropriate handler.
|
||||||
|
Future<void> _handleEvent(
|
||||||
|
SettingsAILocalEvent event,
|
||||||
|
Emitter<SettingsAILocalState> emit,
|
||||||
|
) async {
|
||||||
|
await event.when(
|
||||||
|
started: _handleStarted,
|
||||||
|
didUpdateAISetting: (settings) async {
|
||||||
|
_handleDidUpdateAISetting(settings, emit);
|
||||||
|
},
|
||||||
|
updateChatBin: (chatBinPath) async {
|
||||||
|
await _handleUpdatePath(
|
||||||
|
filePath: chatBinPath,
|
||||||
|
emit: emit,
|
||||||
|
stateUpdater: () => state.copyWith(
|
||||||
|
chatBinPath: chatBinPath.trim(),
|
||||||
|
chatBinPathError: null,
|
||||||
|
),
|
||||||
|
errorUpdater: (error) => state.copyWith(chatBinPathError: error),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
updateChatModelPath: (chatModelPath) async {
|
||||||
|
await _handleUpdatePath(
|
||||||
|
filePath: chatModelPath,
|
||||||
|
emit: emit,
|
||||||
|
stateUpdater: () => state.copyWith(
|
||||||
|
chatModelPath: chatModelPath.trim(),
|
||||||
|
chatModelPathError: null,
|
||||||
|
),
|
||||||
|
errorUpdater: (error) => state.copyWith(chatModelPathError: error),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
toggleLocalAI: () async {
|
||||||
|
emit(state.copyWith(localAIEnabled: !state.localAIEnabled));
|
||||||
|
},
|
||||||
|
saveSetting: () async {
|
||||||
|
_handleSaveSetting();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handles the event to fetch local AI settings when the application starts.
|
||||||
|
Future<void> _handleStarted() async {
|
||||||
|
final result = await ChatEventGetLocalAISetting().send();
|
||||||
|
result.fold(
|
||||||
|
(setting) {
|
||||||
|
if (!isClosed) {
|
||||||
|
add(SettingsAILocalEvent.didUpdateAISetting(setting));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(err) => Log.error('Failed to get local AI setting: $err'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handles the event to update the AI settings in the state.
|
||||||
|
void _handleDidUpdateAISetting(
|
||||||
|
LocalLLMSettingPB settings,
|
||||||
|
Emitter<SettingsAILocalState> emit,
|
||||||
|
) {
|
||||||
|
final newState = state.copyWith(
|
||||||
|
aiSettings: settings,
|
||||||
|
chatBinPath: settings.chatBinPath,
|
||||||
|
chatModelPath: settings.chatModelPath,
|
||||||
|
localAIEnabled: settings.enabled,
|
||||||
|
loadingState: const LoadingState.finish(),
|
||||||
|
);
|
||||||
|
emit(newState.copyWith(saveButtonEnabled: _saveButtonEnabled(newState)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handles updating file paths (both chat binary and chat model paths).
|
||||||
|
Future<void> _handleUpdatePath({
|
||||||
|
required String filePath,
|
||||||
|
required Emitter<SettingsAILocalState> emit,
|
||||||
|
required SettingsAILocalState Function() stateUpdater,
|
||||||
|
required SettingsAILocalState Function(String) errorUpdater,
|
||||||
|
}) async {
|
||||||
|
filePath = filePath.trim();
|
||||||
|
if (filePath.isEmpty) {
|
||||||
|
emit(stateUpdater());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final validationError = await _validatePath(filePath);
|
||||||
|
if (validationError != null) {
|
||||||
|
emit(errorUpdater(validationError));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final newState = stateUpdater();
|
||||||
|
emit(newState.copyWith(saveButtonEnabled: _saveButtonEnabled(newState)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Validates the provided file path.
|
||||||
|
Future<String?> _validatePath(String filePath) async {
|
||||||
|
if (!isAbsolutePath(filePath)) {
|
||||||
|
return "$filePath must be absolute";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!await pathExists(filePath)) {
|
||||||
|
return "$filePath does not exist";
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handles saving the updated settings.
|
||||||
|
void _handleSaveSetting() {
|
||||||
|
if (state.aiSettings == null) return;
|
||||||
|
state.aiSettings!.freeze();
|
||||||
|
final newSetting = state.aiSettings!.rebuild((value) {
|
||||||
|
value
|
||||||
|
..chatBinPath = state.chatBinPath ?? value.chatBinPath
|
||||||
|
..chatModelPath = state.chatModelPath ?? value.chatModelPath
|
||||||
|
..enabled = state.localAIEnabled;
|
||||||
|
});
|
||||||
|
|
||||||
|
ChatEventUpdateLocalAISetting(newSetting).send().then((result) {
|
||||||
|
result.fold(
|
||||||
|
(_) {
|
||||||
|
if (!isClosed) {
|
||||||
|
add(SettingsAILocalEvent.didUpdateAISetting(newSetting));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(err) => Log.error('Failed to update local AI setting: $err'),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Determines if the save button should be enabled based on the state.
|
||||||
|
bool _saveButtonEnabled(SettingsAILocalState newState) {
|
||||||
|
return newState.chatBinPathError == null &&
|
||||||
|
newState.chatModelPathError == null &&
|
||||||
|
newState.chatBinPath != null &&
|
||||||
|
newState.chatModelPath != null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
class SettingsAILocalEvent with _$SettingsAILocalEvent {
|
||||||
|
const factory SettingsAILocalEvent.started() = _Started;
|
||||||
|
const factory SettingsAILocalEvent.didUpdateAISetting(
|
||||||
|
LocalLLMSettingPB settings,
|
||||||
|
) = _GetAISetting;
|
||||||
|
const factory SettingsAILocalEvent.updateChatBin(String chatBinPath) =
|
||||||
|
_UpdateChatBin;
|
||||||
|
const factory SettingsAILocalEvent.updateChatModelPath(String chatModelPath) =
|
||||||
|
_UpdateChatModelPath;
|
||||||
|
const factory SettingsAILocalEvent.toggleLocalAI() = _EnableLocalAI;
|
||||||
|
const factory SettingsAILocalEvent.saveSetting() = _SaveSetting;
|
||||||
|
}
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
class SettingsAILocalState with _$SettingsAILocalState {
|
||||||
|
const factory SettingsAILocalState({
|
||||||
|
LocalLLMSettingPB? aiSettings,
|
||||||
|
String? chatBinPath,
|
||||||
|
String? chatBinPathError,
|
||||||
|
String? chatModelPath,
|
||||||
|
String? chatModelPathError,
|
||||||
|
@Default(false) bool localAIEnabled,
|
||||||
|
@Default(false) bool saveButtonEnabled,
|
||||||
|
@Default(LoadingState.loading()) LoadingState loadingState,
|
||||||
|
}) = _SettingsAILocalState;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if a given file path is absolute.
|
||||||
|
bool isAbsolutePath(String filePath) {
|
||||||
|
return path.isAbsolute(filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if a given file or directory path exists.
|
||||||
|
Future<bool> pathExists(String filePath) async {
|
||||||
|
final file = File(filePath);
|
||||||
|
final directory = Directory(filePath);
|
||||||
|
|
||||||
|
return await file.exists() || await directory.exists();
|
||||||
|
}
|
@ -4,34 +4,29 @@ import 'package:appflowy_backend/protobuf/flowy-folder/import.pb.dart';
|
|||||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pbenum.dart';
|
import 'package:appflowy_backend/protobuf/flowy-folder/view.pbenum.dart';
|
||||||
import 'package:appflowy_result/appflowy_result.dart';
|
import 'package:appflowy_result/appflowy_result.dart';
|
||||||
|
|
||||||
class ImportBackendService {
|
class ImportPayload {
|
||||||
static Future<FlowyResult<void, FlowyError>> importData(
|
ImportPayload({
|
||||||
List<int> data,
|
required this.name,
|
||||||
String name,
|
required this.data,
|
||||||
String parentViewId,
|
required this.layout,
|
||||||
ImportTypePB importType,
|
});
|
||||||
) async {
|
|
||||||
final payload = ImportPB.create()
|
final String name;
|
||||||
..data = data
|
final List<int> data;
|
||||||
..parentViewId = parentViewId
|
final ViewLayoutPB layout;
|
||||||
..viewLayout = importType.toLayout()
|
|
||||||
..name = name
|
|
||||||
..importType = importType;
|
|
||||||
return FolderEventImportData(payload).send();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension on ImportTypePB {
|
class ImportBackendService {
|
||||||
ViewLayoutPB toLayout() {
|
static Future<FlowyResult<void, FlowyError>> importPages(
|
||||||
switch (this) {
|
String parentViewId,
|
||||||
case ImportTypePB.HistoryDocument:
|
List<ImportValuePayloadPB> values,
|
||||||
return ViewLayoutPB.Document;
|
) async {
|
||||||
case ImportTypePB.HistoryDatabase ||
|
final request = ImportPayloadPB(
|
||||||
ImportTypePB.CSV ||
|
parentViewId: parentViewId,
|
||||||
ImportTypePB.RawDatabase:
|
values: values,
|
||||||
return ViewLayoutPB.Grid;
|
syncAfterCreate: true,
|
||||||
default:
|
);
|
||||||
throw UnimplementedError('Unsupported import type $this');
|
|
||||||
}
|
return FolderEventImportData(request).send();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -297,11 +297,16 @@ class SpaceBloc extends Bloc<SpaceEvent, SpaceState> {
|
|||||||
if (currentSpace == null) {
|
if (currentSpace == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
emit(state.copyWith(isDuplicatingSpace: true));
|
||||||
|
|
||||||
final newSpace = await _duplicateSpace(currentSpace);
|
final newSpace = await _duplicateSpace(currentSpace);
|
||||||
// open the duplicated space
|
// open the duplicated space
|
||||||
if (newSpace != null) {
|
if (newSpace != null) {
|
||||||
|
add(const SpaceEvent.didReceiveSpaceUpdate());
|
||||||
add(SpaceEvent.open(newSpace));
|
add(SpaceEvent.open(newSpace));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
emit(state.copyWith(isDuplicatingSpace: false));
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -346,25 +351,22 @@ class SpaceBloc extends Bloc<SpaceEvent, SpaceState> {
|
|||||||
SpacePermission.private => ViewSectionPB.Private,
|
SpacePermission.private => ViewSectionPB.Private,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
final extra = {
|
||||||
|
ViewExtKeys.isSpaceKey: true,
|
||||||
|
ViewExtKeys.spaceIconKey: icon,
|
||||||
|
ViewExtKeys.spaceIconColorKey: iconColor,
|
||||||
|
ViewExtKeys.spacePermissionKey: permission.index,
|
||||||
|
ViewExtKeys.spaceCreatedAtKey: DateTime.now().millisecondsSinceEpoch,
|
||||||
|
};
|
||||||
final result = await _workspaceService.createView(
|
final result = await _workspaceService.createView(
|
||||||
name: name,
|
name: name,
|
||||||
viewSection: section,
|
viewSection: section,
|
||||||
setAsCurrent: false,
|
setAsCurrent: true,
|
||||||
viewId: viewId,
|
viewId: viewId,
|
||||||
|
extra: jsonEncode(extra),
|
||||||
);
|
);
|
||||||
return await result.fold((space) async {
|
return await result.fold((space) async {
|
||||||
Log.info('Space created: $space');
|
Log.info('Space created: $space');
|
||||||
final extra = {
|
|
||||||
ViewExtKeys.isSpaceKey: true,
|
|
||||||
ViewExtKeys.spaceIconKey: icon,
|
|
||||||
ViewExtKeys.spaceIconColorKey: iconColor,
|
|
||||||
ViewExtKeys.spacePermissionKey: permission.index,
|
|
||||||
ViewExtKeys.spaceCreatedAtKey: DateTime.now().millisecondsSinceEpoch,
|
|
||||||
};
|
|
||||||
await ViewBackendService.updateView(
|
|
||||||
viewId: space.id,
|
|
||||||
extra: jsonEncode(extra),
|
|
||||||
);
|
|
||||||
return space;
|
return space;
|
||||||
}, (error) {
|
}, (error) {
|
||||||
Log.error('Failed to create space: $error');
|
Log.error('Failed to create space: $error');
|
||||||
@ -620,19 +622,18 @@ class SpaceBloc extends Bloc<SpaceEvent, SpaceState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (final view in space.childViews) {
|
for (final view in space.childViews) {
|
||||||
unawaited(
|
await ViewBackendService.duplicate(
|
||||||
ViewBackendService.duplicate(
|
view: view,
|
||||||
view: view,
|
openAfterDuplicate: true,
|
||||||
openAfterDuplicate: true,
|
syncAfterDuplicate: true,
|
||||||
includeChildren: true,
|
includeChildren: true,
|
||||||
parentViewId: newSpace.id,
|
parentViewId: newSpace.id,
|
||||||
suffix: '',
|
suffix: '',
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.info('Space duplicated: $newSpace');
|
Log.info('Space duplicated: $newSpace');
|
||||||
add(const SpaceEvent.didReceiveSpaceUpdate());
|
|
||||||
return newSpace;
|
return newSpace;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -688,6 +689,7 @@ class SpaceState with _$SpaceState {
|
|||||||
@Default(null) ViewPB? lastCreatedPage,
|
@Default(null) ViewPB? lastCreatedPage,
|
||||||
FlowyResult<void, FlowyError>? createPageResult,
|
FlowyResult<void, FlowyError>? createPageResult,
|
||||||
@Default(false) bool shouldShowUpgradeDialog,
|
@Default(false) bool shouldShowUpgradeDialog,
|
||||||
|
@Default(false) bool isDuplicatingSpace,
|
||||||
}) = _SpaceState;
|
}) = _SpaceState;
|
||||||
|
|
||||||
factory SpaceState.initial() => const SpaceState();
|
factory SpaceState.initial() => const SpaceState();
|
||||||
|
@ -70,7 +70,6 @@ class UserWorkspaceBloc extends Bloc<UserWorkspaceEvent, UserWorkspaceState> {
|
|||||||
final workspaceMemberResult =
|
final workspaceMemberResult =
|
||||||
await _userService.getWorkspaceMember();
|
await _userService.getWorkspaceMember();
|
||||||
final workspaceMember = workspaceMemberResult.toNullable();
|
final workspaceMember = workspaceMemberResult.toNullable();
|
||||||
|
|
||||||
emit(state.copyWith(currentWorkspaceMember: workspaceMember));
|
emit(state.copyWith(currentWorkspaceMember: workspaceMember));
|
||||||
},
|
},
|
||||||
fetchWorkspaces: () async {
|
fetchWorkspaces: () async {
|
||||||
@ -510,6 +509,7 @@ class UserWorkspaceState with _$UserWorkspaceState {
|
|||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) return true;
|
||||||
|
|
||||||
return other is UserWorkspaceState &&
|
return other is UserWorkspaceState &&
|
||||||
|
other.currentWorkspaceMember == currentWorkspaceMember &&
|
||||||
other.currentWorkspace == currentWorkspace &&
|
other.currentWorkspace == currentWorkspace &&
|
||||||
_deepCollectionEquality.equals(other.workspaces, workspaces) &&
|
_deepCollectionEquality.equals(other.workspaces, workspaces) &&
|
||||||
identical(other.actionResult, actionResult);
|
identical(other.actionResult, actionResult);
|
||||||
|
@ -157,6 +157,7 @@ class ViewBloc extends Bloc<ViewEvent, ViewState> {
|
|||||||
final result = await ViewBackendService.duplicate(
|
final result = await ViewBackendService.duplicate(
|
||||||
view: view,
|
view: view,
|
||||||
openAfterDuplicate: true,
|
openAfterDuplicate: true,
|
||||||
|
syncAfterDuplicate: true,
|
||||||
includeChildren: true,
|
includeChildren: true,
|
||||||
);
|
);
|
||||||
emit(
|
emit(
|
||||||
|
@ -133,13 +133,13 @@ extension ViewExtension on ViewPB {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FlowySvg get spaceIconSvg {
|
FlowySvg? get spaceIconSvg {
|
||||||
try {
|
try {
|
||||||
final ext = jsonDecode(extra);
|
final ext = jsonDecode(extra);
|
||||||
final icon = ext[ViewExtKeys.spaceIconKey];
|
final icon = ext[ViewExtKeys.spaceIconKey];
|
||||||
final color = ext[ViewExtKeys.spaceIconColorKey];
|
final color = ext[ViewExtKeys.spaceIconColorKey];
|
||||||
if (icon == null || color == null) {
|
if (icon == null || color == null) {
|
||||||
return const FlowySvg(FlowySvgs.space_icon_s, blendMode: null);
|
return null;
|
||||||
}
|
}
|
||||||
return FlowySvg(
|
return FlowySvg(
|
||||||
FlowySvgData('assets/flowy_icons/16x/$icon.svg'),
|
FlowySvgData('assets/flowy_icons/16x/$icon.svg'),
|
||||||
@ -147,7 +147,7 @@ extension ViewExtension on ViewPB {
|
|||||||
blendMode: BlendMode.srcOut,
|
blendMode: BlendMode.srcOut,
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return const FlowySvg(FlowySvgs.space_icon_s, blendMode: null);
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,11 +143,13 @@ class ViewBackendService {
|
|||||||
required bool includeChildren,
|
required bool includeChildren,
|
||||||
String? parentViewId,
|
String? parentViewId,
|
||||||
String? suffix,
|
String? suffix,
|
||||||
|
required bool syncAfterDuplicate,
|
||||||
}) {
|
}) {
|
||||||
final payload = DuplicateViewPayloadPB.create()
|
final payload = DuplicateViewPayloadPB.create()
|
||||||
..viewId = view.id
|
..viewId = view.id
|
||||||
..openAfterDuplicate = openAfterDuplicate
|
..openAfterDuplicate = openAfterDuplicate
|
||||||
..includeChildren = includeChildren;
|
..includeChildren = includeChildren
|
||||||
|
..syncAfterCreate = syncAfterDuplicate;
|
||||||
|
|
||||||
if (parentViewId != null) {
|
if (parentViewId != null) {
|
||||||
payload.parentViewId = parentViewId;
|
payload.parentViewId = parentViewId;
|
||||||
|
@ -19,6 +19,7 @@ class WorkspaceService {
|
|||||||
ViewLayoutPB? layout,
|
ViewLayoutPB? layout,
|
||||||
bool? setAsCurrent,
|
bool? setAsCurrent,
|
||||||
String? viewId,
|
String? viewId,
|
||||||
|
String? extra,
|
||||||
}) {
|
}) {
|
||||||
final payload = CreateViewPayloadPB.create()
|
final payload = CreateViewPayloadPB.create()
|
||||||
..parentViewId = workspaceId
|
..parentViewId = workspaceId
|
||||||
@ -42,6 +43,10 @@ class WorkspaceService {
|
|||||||
payload.viewId = viewId;
|
payload.viewId = viewId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (extra != null) {
|
||||||
|
payload.extra = extra;
|
||||||
|
}
|
||||||
|
|
||||||
return FolderEventCreateView(payload).send();
|
return FolderEventCreateView(payload).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,8 +7,6 @@ import 'package:appflowy/plugins/document/presentation/editor_plugins/migration/
|
|||||||
import 'package:appflowy/startup/startup.dart';
|
import 'package:appflowy/startup/startup.dart';
|
||||||
import 'package:appflowy/workspace/application/settings/share/import_service.dart';
|
import 'package:appflowy/workspace/application/settings/share/import_service.dart';
|
||||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/import/import_type.dart';
|
import 'package:appflowy/workspace/presentation/home/menu/sidebar/import/import_type.dart';
|
||||||
import 'package:appflowy/workspace/presentation/home/toast.dart';
|
|
||||||
import 'package:appflowy_backend/log.dart';
|
|
||||||
import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart';
|
import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart';
|
||||||
import 'package:appflowy_editor/appflowy_editor.dart' hide Log;
|
import 'package:appflowy_editor/appflowy_editor.dart' hide Log;
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
@ -153,6 +151,8 @@ class _ImportPanelState extends State<ImportPanel> {
|
|||||||
|
|
||||||
showLoading.value = true;
|
showLoading.value = true;
|
||||||
|
|
||||||
|
final importValues = <ImportValuePayloadPB>[];
|
||||||
|
|
||||||
for (final file in result.files) {
|
for (final file in result.files) {
|
||||||
final path = file.path;
|
final path = file.path;
|
||||||
if (path == null) {
|
if (path == null) {
|
||||||
@ -166,59 +166,52 @@ class _ImportPanelState extends State<ImportPanel> {
|
|||||||
case ImportType.historyDocument:
|
case ImportType.historyDocument:
|
||||||
final bytes = _documentDataFrom(importType, data);
|
final bytes = _documentDataFrom(importType, data);
|
||||||
if (bytes != null) {
|
if (bytes != null) {
|
||||||
final result = await ImportBackendService.importData(
|
importValues.add(
|
||||||
bytes,
|
ImportValuePayloadPB.create()
|
||||||
name,
|
..name = name
|
||||||
parentViewId,
|
..data = bytes
|
||||||
ImportTypePB.HistoryDocument,
|
..viewLayout = ViewLayoutPB.Document
|
||||||
|
..importType = ImportTypePB.HistoryDocument,
|
||||||
);
|
);
|
||||||
result.onFailure((error) {
|
|
||||||
showSnackBarMessage(context, error.msg);
|
|
||||||
Log.error('Failed to import markdown $error');
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ImportType.historyDatabase:
|
case ImportType.historyDatabase:
|
||||||
final result = await ImportBackendService.importData(
|
importValues.add(
|
||||||
utf8.encode(data),
|
ImportValuePayloadPB.create()
|
||||||
name,
|
..name = name
|
||||||
parentViewId,
|
..data = utf8.encode(data)
|
||||||
ImportTypePB.HistoryDatabase,
|
..viewLayout = ViewLayoutPB.Grid
|
||||||
|
..importType = ImportTypePB.HistoryDatabase,
|
||||||
);
|
);
|
||||||
result.onFailure((error) {
|
|
||||||
showSnackBarMessage(context, error.msg);
|
|
||||||
Log.error('Failed to import history database $error');
|
|
||||||
});
|
|
||||||
break;
|
break;
|
||||||
case ImportType.databaseRawData:
|
case ImportType.databaseRawData:
|
||||||
final result = await ImportBackendService.importData(
|
importValues.add(
|
||||||
utf8.encode(data),
|
ImportValuePayloadPB.create()
|
||||||
name,
|
..name = name
|
||||||
parentViewId,
|
..data = utf8.encode(data)
|
||||||
ImportTypePB.RawDatabase,
|
..viewLayout = ViewLayoutPB.Grid
|
||||||
|
..importType = ImportTypePB.RawDatabase,
|
||||||
);
|
);
|
||||||
result.onFailure((error) {
|
|
||||||
showSnackBarMessage(context, error.msg);
|
|
||||||
Log.error('Failed to import database raw data $error');
|
|
||||||
});
|
|
||||||
break;
|
break;
|
||||||
case ImportType.databaseCSV:
|
case ImportType.databaseCSV:
|
||||||
final result = await ImportBackendService.importData(
|
importValues.add(
|
||||||
utf8.encode(data),
|
ImportValuePayloadPB.create()
|
||||||
name,
|
..name = name
|
||||||
parentViewId,
|
..data = utf8.encode(data)
|
||||||
ImportTypePB.CSV,
|
..viewLayout = ViewLayoutPB.Grid
|
||||||
|
..importType = ImportTypePB.CSV,
|
||||||
);
|
);
|
||||||
result.onFailure((error) {
|
|
||||||
showSnackBarMessage(context, error.msg);
|
|
||||||
Log.error('Failed to import CSV $error');
|
|
||||||
});
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
assert(false, 'Unsupported Type $importType');
|
assert(false, 'Unsupported Type $importType');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await ImportBackendService.importPages(
|
||||||
|
parentViewId,
|
||||||
|
importValues,
|
||||||
|
);
|
||||||
|
|
||||||
showLoading.value = false;
|
showLoading.value = false;
|
||||||
widget.importCallback(importType, '', null);
|
widget.importCallback(importType, '', null);
|
||||||
}
|
}
|
||||||
|
@ -117,7 +117,15 @@ class _MovePageMenuState extends State<MovePageMenu> {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
SpacePopup(
|
SpacePopup(
|
||||||
|
useIntrinsicWidth: false,
|
||||||
|
expand: true,
|
||||||
|
height: 30,
|
||||||
|
showCreateButton: false,
|
||||||
child: CurrentSpace(
|
child: CurrentSpace(
|
||||||
|
onTapBlankArea: () {
|
||||||
|
// move the page to current space
|
||||||
|
widget.onSelected(space);
|
||||||
|
},
|
||||||
space: space,
|
space: space,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
import 'package:appflowy/plugins/blank/blank.dart';
|
import 'package:appflowy/plugins/blank/blank.dart';
|
||||||
|
import 'package:appflowy/plugins/document/presentation/editor_plugins/openai/widgets/loading.dart';
|
||||||
import 'package:appflowy/shared/feature_flags.dart';
|
import 'package:appflowy/shared/feature_flags.dart';
|
||||||
import 'package:appflowy/startup/startup.dart';
|
import 'package:appflowy/startup/startup.dart';
|
||||||
import 'package:appflowy/workspace/application/action_navigation/action_navigation_bloc.dart';
|
import 'package:appflowy/workspace/application/action_navigation/action_navigation_bloc.dart';
|
||||||
@ -34,9 +37,10 @@ import 'package:easy_localization/easy_localization.dart';
|
|||||||
import 'package:flowy_infra_ui/style_widget/button.dart';
|
import 'package:flowy_infra_ui/style_widget/button.dart';
|
||||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||||
import 'package:flowy_infra_ui/widget/spacing.dart';
|
import 'package:flowy_infra_ui/widget/spacing.dart';
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
|
Loading? _duplicateSpaceLoading;
|
||||||
|
|
||||||
/// Home Sidebar is the left side bar of the home page.
|
/// Home Sidebar is the left side bar of the home page.
|
||||||
///
|
///
|
||||||
/// in the sidebar, we have:
|
/// in the sidebar, we have:
|
||||||
@ -135,7 +139,8 @@ class HomeSideBar extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
BlocListener<SpaceBloc, SpaceState>(
|
BlocListener<SpaceBloc, SpaceState>(
|
||||||
listenWhen: (p, c) =>
|
listenWhen: (p, c) =>
|
||||||
p.lastCreatedPage?.id != c.lastCreatedPage?.id,
|
p.lastCreatedPage?.id != c.lastCreatedPage?.id ||
|
||||||
|
p.isDuplicatingSpace != c.isDuplicatingSpace,
|
||||||
listener: (context, state) {
|
listener: (context, state) {
|
||||||
final page = state.lastCreatedPage;
|
final page = state.lastCreatedPage;
|
||||||
if (page == null || page.id.isEmpty) {
|
if (page == null || page.id.isEmpty) {
|
||||||
@ -152,6 +157,14 @@ class HomeSideBar extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (state.isDuplicatingSpace) {
|
||||||
|
_duplicateSpaceLoading ??= Loading(context);
|
||||||
|
_duplicateSpaceLoading?.start();
|
||||||
|
} else if (_duplicateSpaceLoading != null) {
|
||||||
|
_duplicateSpaceLoading?.stop();
|
||||||
|
_duplicateSpaceLoading = null;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
BlocListener<ActionNavigationBloc, ActionNavigationState>(
|
BlocListener<ActionNavigationBloc, ActionNavigationState>(
|
||||||
|
@ -15,6 +15,8 @@ import 'package:appflowy_popover/appflowy_popover.dart';
|
|||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flowy_infra_ui/style_widget/decoration.dart';
|
import 'package:flowy_infra_ui/style_widget/decoration.dart';
|
||||||
|
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
||||||
|
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
@ -277,15 +279,23 @@ class DeleteSpacePopup extends StatelessWidget {
|
|||||||
class SpacePopup extends StatelessWidget {
|
class SpacePopup extends StatelessWidget {
|
||||||
const SpacePopup({
|
const SpacePopup({
|
||||||
super.key,
|
super.key,
|
||||||
|
this.height,
|
||||||
|
this.useIntrinsicWidth = true,
|
||||||
|
this.expand = false,
|
||||||
|
required this.showCreateButton,
|
||||||
required this.child,
|
required this.child,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
final bool showCreateButton;
|
||||||
|
final bool useIntrinsicWidth;
|
||||||
|
final bool expand;
|
||||||
|
final double? height;
|
||||||
final Widget child;
|
final Widget child;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
height: HomeSizes.workspaceSectionHeight,
|
height: height ?? HomeSizes.workspaceSectionHeight,
|
||||||
child: AppFlowyPopover(
|
child: AppFlowyPopover(
|
||||||
constraints: const BoxConstraints(maxWidth: 260),
|
constraints: const BoxConstraints(maxWidth: 260),
|
||||||
direction: PopoverDirection.bottomWithLeftAligned,
|
direction: PopoverDirection.bottomWithLeftAligned,
|
||||||
@ -293,10 +303,13 @@ class SpacePopup extends StatelessWidget {
|
|||||||
offset: const Offset(0, 4),
|
offset: const Offset(0, 4),
|
||||||
popupBuilder: (_) => BlocProvider.value(
|
popupBuilder: (_) => BlocProvider.value(
|
||||||
value: context.read<SpaceBloc>(),
|
value: context.read<SpaceBloc>(),
|
||||||
child: const SidebarSpaceMenu(),
|
child: SidebarSpaceMenu(
|
||||||
|
showCreateButton: showCreateButton,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
child: FlowyButton(
|
child: FlowyButton(
|
||||||
useIntrinsicWidth: true,
|
useIntrinsicWidth: useIntrinsicWidth,
|
||||||
|
expand: expand,
|
||||||
margin: const EdgeInsets.only(left: 3.0, right: 4.0),
|
margin: const EdgeInsets.only(left: 3.0, right: 4.0),
|
||||||
iconPadding: 10.0,
|
iconPadding: 10.0,
|
||||||
text: child,
|
text: child,
|
||||||
@ -309,35 +322,68 @@ class SpacePopup extends StatelessWidget {
|
|||||||
class CurrentSpace extends StatelessWidget {
|
class CurrentSpace extends StatelessWidget {
|
||||||
const CurrentSpace({
|
const CurrentSpace({
|
||||||
super.key,
|
super.key,
|
||||||
|
this.onTapBlankArea,
|
||||||
required this.space,
|
required this.space,
|
||||||
});
|
});
|
||||||
|
|
||||||
final ViewPB space;
|
final ViewPB space;
|
||||||
|
final VoidCallback? onTapBlankArea;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Row(
|
final child = FlowyTooltip(
|
||||||
mainAxisSize: MainAxisSize.min,
|
message: LocaleKeys.space_switchSpace.tr(),
|
||||||
children: [
|
child: Row(
|
||||||
SpaceIcon(
|
mainAxisSize: MainAxisSize.min,
|
||||||
dimension: 20,
|
children: [
|
||||||
space: space,
|
SpaceIcon(
|
||||||
cornerRadius: 6.0,
|
dimension: 20,
|
||||||
),
|
space: space,
|
||||||
const HSpace(10),
|
cornerRadius: 6.0,
|
||||||
Flexible(
|
|
||||||
child: FlowyText.medium(
|
|
||||||
space.name,
|
|
||||||
fontSize: 14.0,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
),
|
||||||
),
|
const HSpace(10),
|
||||||
const HSpace(4.0),
|
Flexible(
|
||||||
const FlowySvg(
|
child: FlowyText.medium(
|
||||||
FlowySvgs.workspace_drop_down_menu_show_s,
|
space.name,
|
||||||
),
|
fontSize: 14.0,
|
||||||
],
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const HSpace(4.0),
|
||||||
|
FlowySvg(
|
||||||
|
context.read<SpaceBloc>().state.isExpanded
|
||||||
|
? FlowySvgs.workspace_drop_down_menu_show_s
|
||||||
|
: FlowySvgs.workspace_drop_down_menu_hide_s,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (onTapBlankArea != null) {
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
flex: 2,
|
||||||
|
child: FlowyHover(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(2.0),
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: FlowyTooltip(
|
||||||
|
message: LocaleKeys.space_movePageToSpace.tr(),
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: onTapBlankArea,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return child;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,6 +89,7 @@ class _SidebarSpaceHeaderState extends State<SidebarSpaceHeader> {
|
|||||||
bottom: 3,
|
bottom: 3,
|
||||||
right: isHovered.value || onEditing ? 88 : 0,
|
right: isHovered.value || onEditing ? 88 : 0,
|
||||||
child: SpacePopup(
|
child: SpacePopup(
|
||||||
|
showCreateButton: true,
|
||||||
child: _buildChild(),
|
child: _buildChild(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -14,7 +14,12 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
class SidebarSpaceMenu extends StatelessWidget {
|
class SidebarSpaceMenu extends StatelessWidget {
|
||||||
const SidebarSpaceMenu({super.key});
|
const SidebarSpaceMenu({
|
||||||
|
super.key,
|
||||||
|
required this.showCreateButton,
|
||||||
|
});
|
||||||
|
|
||||||
|
final bool showCreateButton;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -32,16 +37,18 @@ class SidebarSpaceMenu extends StatelessWidget {
|
|||||||
isSelected: state.currentSpace?.id == space.id,
|
isSelected: state.currentSpace?.id == space.id,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const Padding(
|
if (showCreateButton) ...[
|
||||||
padding: EdgeInsets.symmetric(vertical: 8.0),
|
const Padding(
|
||||||
child: Divider(
|
padding: EdgeInsets.symmetric(vertical: 8.0),
|
||||||
height: 0.5,
|
child: Divider(
|
||||||
|
height: 0.5,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
const SizedBox(
|
||||||
const SizedBox(
|
height: HomeSpaceViewSizes.viewHeight,
|
||||||
height: HomeSpaceViewSizes.viewHeight,
|
child: _CreateSpaceButton(),
|
||||||
child: _CreateSpaceButton(),
|
),
|
||||||
),
|
],
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -257,26 +257,33 @@ class _InnerViewItemState extends State<InnerViewItem> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Widget child = SingleInnerViewItem(
|
Widget child = ValueListenableBuilder(
|
||||||
view: widget.view,
|
valueListenable: getIt<MenuSharedState>().notifier,
|
||||||
parentView: widget.parentView,
|
builder: (context, value, _) {
|
||||||
level: widget.level,
|
final isSelected = value?.id == widget.view.id;
|
||||||
showActions: widget.showActions,
|
return SingleInnerViewItem(
|
||||||
spaceType: widget.spaceType,
|
view: widget.view,
|
||||||
onSelected: widget.onSelected,
|
parentView: widget.parentView,
|
||||||
onTertiarySelected: widget.onTertiarySelected,
|
level: widget.level,
|
||||||
isExpanded: widget.isExpanded,
|
showActions: widget.showActions,
|
||||||
isDraggable: widget.isDraggable,
|
spaceType: widget.spaceType,
|
||||||
leftPadding: widget.leftPadding,
|
onSelected: widget.onSelected,
|
||||||
isFeedback: widget.isFeedback,
|
onTertiarySelected: widget.onTertiarySelected,
|
||||||
height: widget.height,
|
isExpanded: widget.isExpanded,
|
||||||
isPlaceholder: widget.isPlaceholder,
|
isDraggable: widget.isDraggable,
|
||||||
isHovered: widget.isHovered,
|
leftPadding: widget.leftPadding,
|
||||||
leftIconBuilder: widget.leftIconBuilder,
|
isFeedback: widget.isFeedback,
|
||||||
rightIconsBuilder: widget.rightIconsBuilder,
|
height: widget.height,
|
||||||
extendBuilder: widget.extendBuilder,
|
isPlaceholder: widget.isPlaceholder,
|
||||||
disableSelectedStatus: widget.disableSelectedStatus,
|
isHovered: widget.isHovered,
|
||||||
shouldIgnoreView: widget.shouldIgnoreView,
|
leftIconBuilder: widget.leftIconBuilder,
|
||||||
|
rightIconsBuilder: widget.rightIconsBuilder,
|
||||||
|
extendBuilder: widget.extendBuilder,
|
||||||
|
disableSelectedStatus: widget.disableSelectedStatus,
|
||||||
|
shouldIgnoreView: widget.shouldIgnoreView,
|
||||||
|
isSelected: isSelected,
|
||||||
|
);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
// if the view is expanded and has child views, render its child views
|
// if the view is expanded and has child views, render its child views
|
||||||
@ -403,6 +410,7 @@ class SingleInnerViewItem extends StatefulWidget {
|
|||||||
required this.extendBuilder,
|
required this.extendBuilder,
|
||||||
required this.disableSelectedStatus,
|
required this.disableSelectedStatus,
|
||||||
required this.shouldIgnoreView,
|
required this.shouldIgnoreView,
|
||||||
|
required this.isSelected,
|
||||||
});
|
});
|
||||||
|
|
||||||
final ViewPB view;
|
final ViewPB view;
|
||||||
@ -430,6 +438,7 @@ class SingleInnerViewItem extends StatefulWidget {
|
|||||||
|
|
||||||
final List<Widget> Function(ViewPB view)? extendBuilder;
|
final List<Widget> Function(ViewPB view)? extendBuilder;
|
||||||
final bool Function(ViewPB view)? shouldIgnoreView;
|
final bool Function(ViewPB view)? shouldIgnoreView;
|
||||||
|
final bool isSelected;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<SingleInnerViewItem> createState() => _SingleInnerViewItemState();
|
State<SingleInnerViewItem> createState() => _SingleInnerViewItemState();
|
||||||
@ -441,8 +450,8 @@ class _SingleInnerViewItemState extends State<SingleInnerViewItem> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var isSelected =
|
var isSelected = widget.isSelected;
|
||||||
getIt<MenuSharedState>().latestOpenView?.id == widget.view.id;
|
|
||||||
if (widget.disableSelectedStatus == true) {
|
if (widget.disableSelectedStatus == true) {
|
||||||
isSelected = false;
|
isSelected = false;
|
||||||
}
|
}
|
||||||
|
@ -242,14 +242,11 @@ class ViewMoreActionTypeWrapper extends CustomActionCell {
|
|||||||
leftIcon: inner.leftIcon,
|
leftIcon: inner.leftIcon,
|
||||||
rightIcon: inner.rightIcon,
|
rightIcon: inner.rightIcon,
|
||||||
iconPadding: 10.0,
|
iconPadding: 10.0,
|
||||||
text: SizedBox(
|
text: FlowyText.regular(
|
||||||
height: 18.0,
|
inner.name,
|
||||||
child: FlowyText.regular(
|
color: inner == ViewMoreActionType.delete
|
||||||
inner.name,
|
? Theme.of(context).colorScheme.error
|
||||||
color: inner == ViewMoreActionType.delete
|
: null,
|
||||||
? Theme.of(context).colorScheme.error
|
|
||||||
: null,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
),
|
),
|
||||||
|
@ -1,3 +1,7 @@
|
|||||||
|
import 'package:appflowy/workspace/application/settings/ai/setting_local_ai_bloc.dart';
|
||||||
|
import 'package:flowy_infra_ui/style_widget/button.dart';
|
||||||
|
import 'package:flowy_infra_ui/widget/rounded_input_field.dart';
|
||||||
|
import 'package:flowy_infra_ui/widget/spacing.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
@ -10,7 +14,6 @@ import 'package:appflowy_backend/log.dart';
|
|||||||
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
|
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||||
import 'package:flowy_infra_ui/widget/spacing.dart';
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
class AIFeatureOnlySupportedWhenUsingAppFlowyCloud extends StatelessWidget {
|
class AIFeatureOnlySupportedWhenUsingAppFlowyCloud extends StatelessWidget {
|
||||||
@ -31,17 +34,14 @@ class AIFeatureOnlySupportedWhenUsingAppFlowyCloud extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class SettingsAIView extends StatelessWidget {
|
class SettingsAIView extends StatelessWidget {
|
||||||
const SettingsAIView({
|
const SettingsAIView({super.key, required this.userProfile});
|
||||||
super.key,
|
|
||||||
required this.userProfile,
|
|
||||||
});
|
|
||||||
|
|
||||||
final UserProfilePB userProfile;
|
final UserProfilePB userProfile;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocProvider<SettingsAIBloc>(
|
return BlocProvider<SettingsAIBloc>(
|
||||||
create: (context) =>
|
create: (_) =>
|
||||||
SettingsAIBloc(userProfile)..add(const SettingsAIEvent.started()),
|
SettingsAIBloc(userProfile)..add(const SettingsAIEvent.started()),
|
||||||
child: BlocBuilder<SettingsAIBloc, SettingsAIState>(
|
child: BlocBuilder<SettingsAIBloc, SettingsAIState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
@ -50,8 +50,10 @@ class SettingsAIView extends StatelessWidget {
|
|||||||
description:
|
description:
|
||||||
LocaleKeys.settings_aiPage_keys_aiSettingsDescription.tr(),
|
LocaleKeys.settings_aiPage_keys_aiSettingsDescription.tr(),
|
||||||
children: const [
|
children: const [
|
||||||
AIModelSeclection(),
|
AIModelSelection(),
|
||||||
_AISearchToggle(value: false),
|
_AISearchToggle(value: false),
|
||||||
|
// Disable local AI configuration for now. It's not ready for production.
|
||||||
|
// LocalAIConfiguration(),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -60,29 +62,28 @@ class SettingsAIView extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class AIModelSeclection extends StatelessWidget {
|
class AIModelSelection extends StatelessWidget {
|
||||||
const AIModelSeclection({super.key});
|
const AIModelSelection({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Row(
|
return Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
FlowyText(
|
Flexible(
|
||||||
LocaleKeys.settings_aiPage_keys_llmModel.tr(),
|
child: FlowyText.medium(
|
||||||
fontSize: 14,
|
LocaleKeys.settings_aiPage_keys_llmModel.tr(),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
BlocBuilder<SettingsAIBloc, SettingsAIState>(
|
Flexible(
|
||||||
builder: (context, state) {
|
child: BlocBuilder<SettingsAIBloc, SettingsAIState>(
|
||||||
return Expanded(
|
builder: (context, state) {
|
||||||
child: SettingsDropdown<AIModelPB>(
|
return SettingsDropdown<AIModelPB>(
|
||||||
key: const Key('AIModelDropdown'),
|
key: const Key('AIModelDropdown'),
|
||||||
expandWidth: false,
|
onChanged: (model) => context
|
||||||
onChanged: (format) {
|
.read<SettingsAIBloc>()
|
||||||
context.read<SettingsAIBloc>().add(
|
.add(SettingsAIEvent.selectModel(model)),
|
||||||
SettingsAIEvent.selectModel(format),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
selectedOption: state.userProfile.aiModel,
|
selectedOption: state.userProfile.aiModel,
|
||||||
options: _availableModels
|
options: _availableModels
|
||||||
.map(
|
.map(
|
||||||
@ -93,9 +94,9 @@ class AIModelSeclection extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
.toList(),
|
.toList(),
|
||||||
),
|
);
|
||||||
);
|
},
|
||||||
},
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@ -139,17 +140,21 @@ class _AISearchToggle extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Row(
|
return Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
FlowyText.medium(
|
||||||
child: FlowyText.regular(
|
LocaleKeys.settings_aiPage_keys_enableAISearchTitle.tr(),
|
||||||
LocaleKeys.settings_aiPage_keys_enableAISearchTitle.tr(),
|
|
||||||
fontSize: 16,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
const HSpace(16),
|
const Spacer(),
|
||||||
BlocBuilder<SettingsAIBloc, SettingsAIState>(
|
BlocBuilder<SettingsAIBloc, SettingsAIState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
if (state.aiSettings == null) {
|
if (state.aiSettings == null) {
|
||||||
return const CircularProgressIndicator.adaptive();
|
return const Padding(
|
||||||
|
padding: EdgeInsets.only(top: 6),
|
||||||
|
child: SizedBox(
|
||||||
|
height: 26,
|
||||||
|
width: 26,
|
||||||
|
child: CircularProgressIndicator.adaptive(),
|
||||||
|
),
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
return Toggle(
|
return Toggle(
|
||||||
value: state.enableSearchIndexing,
|
value: state.enableSearchIndexing,
|
||||||
@ -164,3 +169,111 @@ class _AISearchToggle extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class LocalAIConfiguration extends StatelessWidget {
|
||||||
|
const LocalAIConfiguration({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocProvider(
|
||||||
|
create: (context) =>
|
||||||
|
SettingsAILocalBloc()..add(const SettingsAILocalEvent.started()),
|
||||||
|
child: BlocBuilder<SettingsAILocalBloc, SettingsAILocalState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
return state.loadingState.when(
|
||||||
|
loading: () {
|
||||||
|
return const SizedBox.shrink();
|
||||||
|
},
|
||||||
|
finish: () {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
AIConfigurateTextField(
|
||||||
|
title: 'chat bin path',
|
||||||
|
hitText: '',
|
||||||
|
errorText: state.chatBinPathError ?? '',
|
||||||
|
value: state.aiSettings?.chatBinPath ?? '',
|
||||||
|
onChanged: (value) {
|
||||||
|
context.read<SettingsAILocalBloc>().add(
|
||||||
|
SettingsAILocalEvent.updateChatBin(value),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const VSpace(16),
|
||||||
|
AIConfigurateTextField(
|
||||||
|
title: 'chat model path',
|
||||||
|
hitText: '',
|
||||||
|
errorText: state.chatModelPathError ?? '',
|
||||||
|
value: state.aiSettings?.chatModelPath ?? '',
|
||||||
|
onChanged: (value) {
|
||||||
|
context.read<SettingsAILocalBloc>().add(
|
||||||
|
SettingsAILocalEvent.updateChatModelPath(value),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const VSpace(16),
|
||||||
|
Toggle(
|
||||||
|
value: state.localAIEnabled,
|
||||||
|
onChanged: (_) => context
|
||||||
|
.read<SettingsAILocalBloc>()
|
||||||
|
.add(const SettingsAILocalEvent.toggleLocalAI()),
|
||||||
|
),
|
||||||
|
const VSpace(16),
|
||||||
|
FlowyButton(
|
||||||
|
disable: !state.saveButtonEnabled,
|
||||||
|
text: const FlowyText("save"),
|
||||||
|
onTap: () {
|
||||||
|
context.read<SettingsAILocalBloc>().add(
|
||||||
|
const SettingsAILocalEvent.saveSetting(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AIConfigurateTextField extends StatelessWidget {
|
||||||
|
const AIConfigurateTextField({
|
||||||
|
required this.title,
|
||||||
|
required this.hitText,
|
||||||
|
required this.errorText,
|
||||||
|
required this.value,
|
||||||
|
required this.onChanged,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
final String title;
|
||||||
|
final String hitText;
|
||||||
|
final String errorText;
|
||||||
|
final String value;
|
||||||
|
final void Function(String) onChanged;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
FlowyText(
|
||||||
|
title,
|
||||||
|
),
|
||||||
|
const VSpace(8),
|
||||||
|
RoundedInputField(
|
||||||
|
hintText: hitText,
|
||||||
|
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500),
|
||||||
|
normalBorderColor: Theme.of(context).colorScheme.outline,
|
||||||
|
errorBorderColor: Theme.of(context).colorScheme.error,
|
||||||
|
cursorColor: Theme.of(context).colorScheme.primary,
|
||||||
|
errorText: errorText,
|
||||||
|
initialValue: value,
|
||||||
|
onChanged: onChanged,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -494,6 +494,7 @@ class _ActionButton extends StatelessWidget {
|
|||||||
? SystemMouseCursors.click
|
? SystemMouseCursors.click
|
||||||
: MouseCursor.defer,
|
: MouseCursor.defer,
|
||||||
child: _drawBorder(
|
child: _drawBorder(
|
||||||
|
context,
|
||||||
isLM: isLM,
|
isLM: isLM,
|
||||||
isUpgrade: isUpgrade,
|
isUpgrade: isUpgrade,
|
||||||
child: Container(
|
child: Container(
|
||||||
@ -544,7 +545,8 @@ class _ActionButton extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _drawBorder({
|
Widget _drawBorder(
|
||||||
|
BuildContext context, {
|
||||||
required bool isLM,
|
required bool isLM,
|
||||||
required bool isUpgrade,
|
required bool isUpgrade,
|
||||||
required Widget child,
|
required Widget child,
|
||||||
@ -562,7 +564,7 @@ class _ActionButton extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
border: isUpgrade ? null : Border.all(),
|
border: isUpgrade ? null : Border.all(color: const Color(0xFF333333)),
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: BorderRadius.circular(16),
|
||||||
),
|
),
|
||||||
child: child,
|
child: child,
|
||||||
|
@ -70,6 +70,7 @@ class SettingsPlanView extends StatelessWidget {
|
|||||||
usage: state.workspaceUsage,
|
usage: state.workspaceUsage,
|
||||||
subscription: state.subscription,
|
subscription: state.subscription,
|
||||||
),
|
),
|
||||||
|
const VSpace(16),
|
||||||
_CurrentPlanBox(subscription: state.subscription),
|
_CurrentPlanBox(subscription: state.subscription),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -490,14 +490,11 @@ class KeyBadge extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
child: Center(
|
child: Center(
|
||||||
child: iconData != null
|
child: iconData != null
|
||||||
? FlowySvg(
|
? FlowySvg(iconData!, color: Colors.black)
|
||||||
iconData!,
|
|
||||||
color: AFThemeExtension.of(context).strongText,
|
|
||||||
)
|
|
||||||
: FlowyText.medium(
|
: FlowyText.medium(
|
||||||
keyLabel.toLowerCase(),
|
keyLabel.toLowerCase(),
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
color: AFThemeExtension.of(context).strongText,
|
color: Colors.black,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -24,6 +24,7 @@ import 'package:appflowy/workspace/presentation/settings/shared/setting_list_til
|
|||||||
import 'package:appflowy/workspace/presentation/settings/shared/settings_alert_dialog.dart';
|
import 'package:appflowy/workspace/presentation/settings/shared/settings_alert_dialog.dart';
|
||||||
import 'package:appflowy/workspace/presentation/settings/shared/settings_body.dart';
|
import 'package:appflowy/workspace/presentation/settings/shared/settings_body.dart';
|
||||||
import 'package:appflowy/workspace/presentation/settings/shared/settings_category.dart';
|
import 'package:appflowy/workspace/presentation/settings/shared/settings_category.dart';
|
||||||
|
import 'package:appflowy/workspace/presentation/settings/shared/settings_category_spacer.dart';
|
||||||
import 'package:appflowy/workspace/presentation/settings/shared/settings_dashed_divider.dart';
|
import 'package:appflowy/workspace/presentation/settings/shared/settings_dashed_divider.dart';
|
||||||
import 'package:appflowy/workspace/presentation/settings/shared/settings_dropdown.dart';
|
import 'package:appflowy/workspace/presentation/settings/shared/settings_dropdown.dart';
|
||||||
import 'package:appflowy/workspace/presentation/settings/shared/settings_input_field.dart';
|
import 'package:appflowy/workspace/presentation/settings/shared/settings_input_field.dart';
|
||||||
@ -85,6 +86,7 @@ class SettingsWorkspaceView extends StatelessWidget {
|
|||||||
return SettingsBody(
|
return SettingsBody(
|
||||||
title: LocaleKeys.settings_workspacePage_title.tr(),
|
title: LocaleKeys.settings_workspacePage_title.tr(),
|
||||||
description: LocaleKeys.settings_workspacePage_description.tr(),
|
description: LocaleKeys.settings_workspacePage_description.tr(),
|
||||||
|
autoSeparate: false,
|
||||||
children: [
|
children: [
|
||||||
// We don't allow changing workspace name/icon for local/offline
|
// We don't allow changing workspace name/icon for local/offline
|
||||||
if (userProfile.authenticator != AuthenticatorPB.Local) ...[
|
if (userProfile.authenticator != AuthenticatorPB.Local) ...[
|
||||||
@ -93,6 +95,7 @@ class SettingsWorkspaceView extends StatelessWidget {
|
|||||||
.tr(),
|
.tr(),
|
||||||
children: [_WorkspaceNameSetting(member: workspaceMember)],
|
children: [_WorkspaceNameSetting(member: workspaceMember)],
|
||||||
),
|
),
|
||||||
|
const SettingsCategorySpacer(),
|
||||||
SettingsCategory(
|
SettingsCategory(
|
||||||
title: LocaleKeys.settings_workspacePage_workspaceIcon_title
|
title: LocaleKeys.settings_workspacePage_workspaceIcon_title
|
||||||
.tr(),
|
.tr(),
|
||||||
@ -106,11 +109,14 @@ class SettingsWorkspaceView extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
const SettingsCategorySpacer(),
|
||||||
],
|
],
|
||||||
SettingsCategory(
|
SettingsCategory(
|
||||||
title: LocaleKeys.settings_workspacePage_appearance_title.tr(),
|
title: LocaleKeys.settings_workspacePage_appearance_title.tr(),
|
||||||
children: const [AppearanceSelector()],
|
children: const [AppearanceSelector()],
|
||||||
),
|
),
|
||||||
|
const VSpace(16),
|
||||||
|
// const SettingsCategorySpacer(),
|
||||||
SettingsCategory(
|
SettingsCategory(
|
||||||
title: LocaleKeys.settings_workspacePage_theme_title.tr(),
|
title: LocaleKeys.settings_workspacePage_theme_title.tr(),
|
||||||
description:
|
description:
|
||||||
@ -121,6 +127,7 @@ class SettingsWorkspaceView extends StatelessWidget {
|
|||||||
_DocumentSelectionColorSetting(),
|
_DocumentSelectionColorSetting(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
const SettingsCategorySpacer(),
|
||||||
SettingsCategory(
|
SettingsCategory(
|
||||||
title:
|
title:
|
||||||
LocaleKeys.settings_workspacePage_workspaceFont_title.tr(),
|
LocaleKeys.settings_workspacePage_workspaceFont_title.tr(),
|
||||||
@ -129,7 +136,9 @@ class SettingsWorkspaceView extends StatelessWidget {
|
|||||||
currentFont:
|
currentFont:
|
||||||
context.read<AppearanceSettingsCubit>().state.font,
|
context.read<AppearanceSettingsCubit>().state.font,
|
||||||
),
|
),
|
||||||
const SettingsDashedDivider(),
|
SettingsDashedDivider(
|
||||||
|
color: Theme.of(context).colorScheme.outline,
|
||||||
|
),
|
||||||
SettingsCategory(
|
SettingsCategory(
|
||||||
title: LocaleKeys.settings_workspacePage_textDirection_title
|
title: LocaleKeys.settings_workspacePage_textDirection_title
|
||||||
.tr(),
|
.tr(),
|
||||||
@ -140,11 +149,14 @@ class SettingsWorkspaceView extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
const VSpace(16),
|
||||||
SettingsCategory(
|
SettingsCategory(
|
||||||
title: LocaleKeys.settings_workspacePage_layoutDirection_title
|
title: LocaleKeys.settings_workspacePage_layoutDirection_title
|
||||||
.tr(),
|
.tr(),
|
||||||
children: const [_LayoutDirectionSelect()],
|
children: const [_LayoutDirectionSelect()],
|
||||||
),
|
),
|
||||||
|
const SettingsCategorySpacer(),
|
||||||
|
|
||||||
SettingsCategory(
|
SettingsCategory(
|
||||||
title: LocaleKeys.settings_workspacePage_dateTime_title.tr(),
|
title: LocaleKeys.settings_workspacePage_dateTime_title.tr(),
|
||||||
children: [
|
children: [
|
||||||
@ -156,10 +168,14 @@ class SettingsWorkspaceView extends StatelessWidget {
|
|||||||
const _DateFormatDropdown(),
|
const _DateFormatDropdown(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
const SettingsCategorySpacer(),
|
||||||
|
|
||||||
SettingsCategory(
|
SettingsCategory(
|
||||||
title: LocaleKeys.settings_workspacePage_language_title.tr(),
|
title: LocaleKeys.settings_workspacePage_language_title.tr(),
|
||||||
children: const [LanguageDropdown()],
|
children: const [LanguageDropdown()],
|
||||||
),
|
),
|
||||||
|
const SettingsCategorySpacer(),
|
||||||
|
|
||||||
if (userProfile.authenticator != AuthenticatorPB.Local) ...[
|
if (userProfile.authenticator != AuthenticatorPB.Local) ...[
|
||||||
SingleSettingAction(
|
SingleSettingAction(
|
||||||
label: LocaleKeys.settings_workspacePage_manageWorkspace_title
|
label: LocaleKeys.settings_workspacePage_manageWorkspace_title
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:appflowy/startup/startup.dart';
|
import 'package:appflowy/startup/startup.dart';
|
||||||
@ -15,6 +14,7 @@ import 'package:appflowy/workspace/presentation/settings/widgets/feature_flags/f
|
|||||||
import 'package:appflowy/workspace/presentation/settings/widgets/members/workspace_member_page.dart';
|
import 'package:appflowy/workspace/presentation/settings/widgets/members/workspace_member_page.dart';
|
||||||
import 'package:appflowy/workspace/presentation/settings/widgets/settings_menu.dart';
|
import 'package:appflowy/workspace/presentation/settings/widgets/settings_menu.dart';
|
||||||
import 'package:appflowy/workspace/presentation/settings/widgets/settings_notifications_view.dart';
|
import 'package:appflowy/workspace/presentation/settings/widgets/settings_notifications_view.dart';
|
||||||
|
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ class SettingsBody extends StatelessWidget {
|
|||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
separatorBuilder: () => autoSeparate
|
separatorBuilder: () => autoSeparate
|
||||||
? const SettingsCategorySpacer()
|
? const SettingsCategorySpacer()
|
||||||
: const VSpace(16),
|
: const SizedBox.shrink(),
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: children,
|
children: children,
|
||||||
),
|
),
|
||||||
|
75
frontend/appflowy_tauri/src-tauri/Cargo.lock
generated
75
frontend/appflowy_tauri/src-tauri/Cargo.lock
generated
@ -172,7 +172,7 @@ checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "app-error"
|
name = "app-error"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=d61524d63605aa010afa6a734cbbe4fb4cd68ea1#d61524d63605aa010afa6a734cbbe4fb4cd68ea1"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9884d93aa2805013f36a79c1757174a0b5063065#9884d93aa2805013f36a79c1757174a0b5063065"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bincode",
|
"bincode",
|
||||||
@ -192,7 +192,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "appflowy-ai-client"
|
name = "appflowy-ai-client"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=d61524d63605aa010afa6a734cbbe4fb4cd68ea1#d61524d63605aa010afa6a734cbbe4fb4cd68ea1"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9884d93aa2805013f36a79c1757174a0b5063065#9884d93aa2805013f36a79c1757174a0b5063065"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bytes",
|
"bytes",
|
||||||
@ -772,7 +772,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "client-api"
|
name = "client-api"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=d61524d63605aa010afa6a734cbbe4fb4cd68ea1#d61524d63605aa010afa6a734cbbe4fb4cd68ea1"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9884d93aa2805013f36a79c1757174a0b5063065#9884d93aa2805013f36a79c1757174a0b5063065"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"again",
|
"again",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
@ -821,7 +821,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "client-api-entity"
|
name = "client-api-entity"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=d61524d63605aa010afa6a734cbbe4fb4cd68ea1#d61524d63605aa010afa6a734cbbe4fb4cd68ea1"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9884d93aa2805013f36a79c1757174a0b5063065#9884d93aa2805013f36a79c1757174a0b5063065"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"collab-entity",
|
"collab-entity",
|
||||||
"collab-rt-entity",
|
"collab-rt-entity",
|
||||||
@ -833,7 +833,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "client-websocket"
|
name = "client-websocket"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=d61524d63605aa010afa6a734cbbe4fb4cd68ea1#d61524d63605aa010afa6a734cbbe4fb4cd68ea1"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9884d93aa2805013f36a79c1757174a0b5063065#9884d93aa2805013f36a79c1757174a0b5063065"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
@ -1073,7 +1073,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-rt-entity"
|
name = "collab-rt-entity"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=d61524d63605aa010afa6a734cbbe4fb4cd68ea1#d61524d63605aa010afa6a734cbbe4fb4cd68ea1"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9884d93aa2805013f36a79c1757174a0b5063065#9884d93aa2805013f36a79c1757174a0b5063065"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bincode",
|
"bincode",
|
||||||
@ -1098,7 +1098,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-rt-protocol"
|
name = "collab-rt-protocol"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=d61524d63605aa010afa6a734cbbe4fb4cd68ea1#d61524d63605aa010afa6a734cbbe4fb4cd68ea1"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9884d93aa2805013f36a79c1757174a0b5063065#9884d93aa2805013f36a79c1757174a0b5063065"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -1294,12 +1294,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-utils"
|
name = "crossbeam-utils"
|
||||||
version = "0.8.16"
|
version = "0.8.20"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294"
|
checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crunchy"
|
name = "crunchy"
|
||||||
@ -1344,7 +1341,7 @@ dependencies = [
|
|||||||
"cssparser-macros",
|
"cssparser-macros",
|
||||||
"dtoa-short",
|
"dtoa-short",
|
||||||
"itoa 1.0.6",
|
"itoa 1.0.6",
|
||||||
"phf 0.11.2",
|
"phf 0.8.0",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1455,7 +1452,7 @@ checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "database-entity"
|
name = "database-entity"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=d61524d63605aa010afa6a734cbbe4fb4cd68ea1#d61524d63605aa010afa6a734cbbe4fb4cd68ea1"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9884d93aa2805013f36a79c1757174a0b5063065#9884d93aa2805013f36a79c1757174a0b5063065"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"app-error",
|
"app-error",
|
||||||
@ -1849,6 +1846,7 @@ name = "flowy-chat"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"allo-isolate",
|
"allo-isolate",
|
||||||
|
"anyhow",
|
||||||
"bytes",
|
"bytes",
|
||||||
"dashmap",
|
"dashmap",
|
||||||
"flowy-chat-pub",
|
"flowy-chat-pub",
|
||||||
@ -1856,14 +1854,19 @@ dependencies = [
|
|||||||
"flowy-derive",
|
"flowy-derive",
|
||||||
"flowy-error",
|
"flowy-error",
|
||||||
"flowy-notification",
|
"flowy-notification",
|
||||||
|
"flowy-sidecar",
|
||||||
"flowy-sqlite",
|
"flowy-sqlite",
|
||||||
"futures",
|
"futures",
|
||||||
"lib-dispatch",
|
"lib-dispatch",
|
||||||
"lib-infra",
|
"lib-infra",
|
||||||
"log",
|
"log",
|
||||||
|
"parking_lot 0.12.1",
|
||||||
"protobuf",
|
"protobuf",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
"strum_macros 0.21.1",
|
"strum_macros 0.21.1",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"tokio-stream",
|
||||||
"tracing",
|
"tracing",
|
||||||
"uuid",
|
"uuid",
|
||||||
"validator",
|
"validator",
|
||||||
@ -2133,6 +2136,7 @@ dependencies = [
|
|||||||
"fancy-regex 0.11.0",
|
"fancy-regex 0.11.0",
|
||||||
"flowy-codegen",
|
"flowy-codegen",
|
||||||
"flowy-derive",
|
"flowy-derive",
|
||||||
|
"flowy-sidecar",
|
||||||
"flowy-sqlite",
|
"flowy-sqlite",
|
||||||
"lib-dispatch",
|
"lib-dispatch",
|
||||||
"protobuf",
|
"protobuf",
|
||||||
@ -2319,6 +2323,24 @@ dependencies = [
|
|||||||
"serde_repr",
|
"serde_repr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "flowy-sidecar"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"crossbeam-utils",
|
||||||
|
"lib-infra",
|
||||||
|
"log",
|
||||||
|
"once_cell",
|
||||||
|
"parking_lot 0.12.1",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"thiserror",
|
||||||
|
"tokio",
|
||||||
|
"tokio-stream",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "flowy-sqlite"
|
name = "flowy-sqlite"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@ -2899,7 +2921,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "gotrue"
|
name = "gotrue"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=d61524d63605aa010afa6a734cbbe4fb4cd68ea1#d61524d63605aa010afa6a734cbbe4fb4cd68ea1"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9884d93aa2805013f36a79c1757174a0b5063065#9884d93aa2805013f36a79c1757174a0b5063065"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
@ -2916,7 +2938,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "gotrue-entity"
|
name = "gotrue-entity"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=d61524d63605aa010afa6a734cbbe4fb4cd68ea1#d61524d63605aa010afa6a734cbbe4fb4cd68ea1"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9884d93aa2805013f36a79c1757174a0b5063065#9884d93aa2805013f36a79c1757174a0b5063065"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"app-error",
|
"app-error",
|
||||||
@ -3348,7 +3370,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "infra"
|
name = "infra"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=d61524d63605aa010afa6a734cbbe4fb4cd68ea1#d61524d63605aa010afa6a734cbbe4fb4cd68ea1"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9884d93aa2805013f36a79c1757174a0b5063065#9884d93aa2805013f36a79c1757174a0b5063065"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bytes",
|
"bytes",
|
||||||
@ -3596,6 +3618,7 @@ dependencies = [
|
|||||||
"async-trait",
|
"async-trait",
|
||||||
"atomic_refcell",
|
"atomic_refcell",
|
||||||
"bytes",
|
"bytes",
|
||||||
|
"cfg-if",
|
||||||
"chrono",
|
"chrono",
|
||||||
"futures",
|
"futures",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
@ -4216,9 +4239,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "once_cell"
|
||||||
version = "1.18.0"
|
version = "1.19.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
|
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oneshot"
|
name = "oneshot"
|
||||||
@ -4855,7 +4878,7 @@ checksum = "c55e02e35260070b6f716a2423c2ff1c3bb1642ddca6f99e1f26d06268a0e2d2"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"heck 0.4.1",
|
"heck 0.4.1",
|
||||||
"itertools 0.11.0",
|
"itertools 0.10.5",
|
||||||
"log",
|
"log",
|
||||||
"multimap",
|
"multimap",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
@ -4876,7 +4899,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "efb6c9a1dd1def8e2124d17e83a20af56f1570d6c2d2bd9e266ccb768df3840e"
|
checksum = "efb6c9a1dd1def8e2124d17e83a20af56f1570d6c2d2bd9e266ccb768df3840e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"itertools 0.11.0",
|
"itertools 0.10.5",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.47",
|
"syn 2.0.47",
|
||||||
@ -5690,9 +5713,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.111"
|
version = "1.0.118"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4"
|
checksum = "d947f6b3163d8857ea16c4fa0dd4840d52f3041039a85decd46867eb1abef2e4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa 1.0.6",
|
"itoa 1.0.6",
|
||||||
"ryu",
|
"ryu",
|
||||||
@ -5840,7 +5863,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "shared-entity"
|
name = "shared-entity"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=d61524d63605aa010afa6a734cbbe4fb4cd68ea1#d61524d63605aa010afa6a734cbbe4fb4cd68ea1"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=9884d93aa2805013f36a79c1757174a0b5063065#9884d93aa2805013f36a79c1757174a0b5063065"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"app-error",
|
"app-error",
|
||||||
@ -6847,9 +6870,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-stream"
|
name = "tokio-stream"
|
||||||
version = "0.1.14"
|
version = "0.1.15"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842"
|
checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
|
@ -52,7 +52,7 @@ collab-user = { version = "0.2" }
|
|||||||
# Run the script:
|
# Run the script:
|
||||||
# scripts/tool/update_client_api_rev.sh new_rev_id
|
# scripts/tool/update_client_api_rev.sh new_rev_id
|
||||||
# ⚠️⚠️⚠️️
|
# ⚠️⚠️⚠️️
|
||||||
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "d61524d63605aa010afa6a734cbbe4fb4cd68ea1" }
|
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "9884d93aa2805013f36a79c1757174a0b5063065" }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
serde_json.workspace = true
|
serde_json.workspace = true
|
||||||
|
@ -1,7 +0,0 @@
|
|||||||
src/services
|
|
||||||
src/styles
|
|
||||||
node_modules/
|
|
||||||
dist/
|
|
||||||
src-tauri/
|
|
||||||
.eslintrc.cjs
|
|
||||||
tsconfig.json
|
|
@ -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']
|
|
||||||
};
|
|
29
frontend/appflowy_web/.gitignore
vendored
29
frontend/appflowy_web/.gitignore
vendored
@ -1,29 +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?
|
|
||||||
|
|
||||||
wasm-libs/**/target
|
|
||||||
**/src/services/backend/models/
|
|
||||||
**/src/services/backend/events/
|
|
||||||
**/src/appflowy_app/i18n/translations/
|
|
@ -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
|
|
@ -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,
|
|
||||||
};
|
|
@ -1,45 +0,0 @@
|
|||||||
# AppFlowy Web Project
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd appflowy-web
|
|
||||||
|
|
||||||
# Install dependencies
|
|
||||||
rm -rf node_modules && pnpm install
|
|
||||||
```
|
|
||||||
|
|
||||||
## Running the app
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# development
|
|
||||||
pnpm run dev
|
|
||||||
|
|
||||||
# production mode
|
|
||||||
pnpm run build
|
|
||||||
|
|
||||||
# generate wasm
|
|
||||||
pnpm run wasm
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Run tests in Chrome
|
|
||||||
|
|
||||||
> Before executing the test, you need to install the [Chrome Driver](https://chromedriver.chromium.org/downloads). If
|
|
||||||
> you are using a Mac, you can easily install it using Homebrew.
|
|
||||||
>
|
|
||||||
> ```shell
|
|
||||||
> brew install chromedriver
|
|
||||||
> ```
|
|
||||||
|
|
||||||
|
|
||||||
Go to `frontend/appflowy_web/wasm-libs` and run:
|
|
||||||
```shell
|
|
||||||
wasm-pack test --chrome
|
|
||||||
```
|
|
||||||
|
|
||||||
Run tests in headless Chrome
|
|
||||||
```shell
|
|
||||||
wasm-pack test --headless --chrome
|
|
||||||
```
|
|
@ -1,13 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<title>Vite + React + TS</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="root"></div>
|
|
||||||
<script type="module" src="/src/main.tsx"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1,41 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "appflowy_web",
|
|
||||||
"private": true,
|
|
||||||
"version": "0.0.0",
|
|
||||||
"type": "module",
|
|
||||||
"scripts": {
|
|
||||||
"build_release_wasm": "cd wasm-libs/af-wasm && wasm-pack build",
|
|
||||||
"build_dev_wasm": "cd wasm-libs/af-wasm && wasm-pack build --features=\"localhost_dev\"",
|
|
||||||
"dev": "pnpm run build_dev_wasm && vite",
|
|
||||||
"build": "tsc && vite build",
|
|
||||||
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
|
||||||
"clean": "cargo make --cwd .. web_clean",
|
|
||||||
"test": "cargo test && wasm-pack test --headless",
|
|
||||||
"preview": "vite preview"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"events": "^3.3.0",
|
|
||||||
"google-protobuf": "^3.21.2",
|
|
||||||
"protoc-gen-ts": "^0.8.5",
|
|
||||||
"react": "^18.2.0",
|
|
||||||
"react-dom": "^18.2.0",
|
|
||||||
"ts-results": "^3.3.0",
|
|
||||||
"uuid": "^9.0.1"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@types/events": "^3.0.3",
|
|
||||||
"@types/node": "^20.10.6",
|
|
||||||
"@types/react": "^18.2.43",
|
|
||||||
"@types/react-dom": "^18.2.17",
|
|
||||||
"@typescript-eslint/eslint-plugin": "^6.14.0",
|
|
||||||
"@typescript-eslint/parser": "^6.14.0",
|
|
||||||
"@vitejs/plugin-react": "^4.2.1",
|
|
||||||
"eslint": "^8.55.0",
|
|
||||||
"eslint-plugin-react-hooks": "^4.6.0",
|
|
||||||
"eslint-plugin-react-refresh": "^0.4.5",
|
|
||||||
"typescript": "^5.2.2",
|
|
||||||
"vite": "^5.0.8",
|
|
||||||
"vite-plugin-wasm": "^3.3.0",
|
|
||||||
"rimraf": "^5.0.5"
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -1 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
|
Before Width: | Height: | Size: 1.5 KiB |
15
frontend/appflowy_web/src/@types/global.d.ts
vendored
15
frontend/appflowy_web/src/@types/global.d.ts
vendored
@ -1,15 +0,0 @@
|
|||||||
export interface NotifyArgs {
|
|
||||||
source: string;
|
|
||||||
ty: number;
|
|
||||||
id: string;
|
|
||||||
payload?: Unit8Array;
|
|
||||||
error?: Unit8Array;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface Window {
|
|
||||||
onFlowyNotify: (eventName: string, args: NotifyArgs) => void;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export {};
|
|
@ -1,42 +0,0 @@
|
|||||||
#root {
|
|
||||||
max-width: 1280px;
|
|
||||||
margin: 0 auto;
|
|
||||||
padding: 2rem;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo {
|
|
||||||
height: 6em;
|
|
||||||
padding: 1.5em;
|
|
||||||
will-change: filter;
|
|
||||||
transition: filter 300ms;
|
|
||||||
}
|
|
||||||
.logo:hover {
|
|
||||||
filter: drop-shadow(0 0 2em #646cffaa);
|
|
||||||
}
|
|
||||||
.logo.react:hover {
|
|
||||||
filter: drop-shadow(0 0 2em #61dafbaa);
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes logo-spin {
|
|
||||||
from {
|
|
||||||
transform: rotate(0deg);
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
transform: rotate(360deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (prefers-reduced-motion: no-preference) {
|
|
||||||
a:nth-of-type(2) .logo {
|
|
||||||
animation: logo-spin infinite 20s linear;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.card {
|
|
||||||
padding: 2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.read-the-docs {
|
|
||||||
color: #888;
|
|
||||||
}
|
|
@ -1,58 +0,0 @@
|
|||||||
import reactLogo from "./assets/react.svg";
|
|
||||||
import viteLogo from "/vite.svg";
|
|
||||||
import "./App.css";
|
|
||||||
import { useEffect } from "react";
|
|
||||||
import { initApp } from "@/application/app.ts";
|
|
||||||
import { subscribeNotification } from "@/application/notification.ts";
|
|
||||||
import { NotifyArgs } from "./@types/global";
|
|
||||||
import { init_tracing_log, init_wasm_core } from "../wasm-libs/af-wasm/pkg";
|
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
|
||||||
import {AddUserPB, UserWasmEventAddUser} from "@/services/backend/events/user";
|
|
||||||
|
|
||||||
init_tracing_log();
|
|
||||||
// FIXME: handle the promise that init_wasm_core returns
|
|
||||||
init_wasm_core();
|
|
||||||
|
|
||||||
function App() {
|
|
||||||
useEffect(() => {
|
|
||||||
initApp();
|
|
||||||
return subscribeNotification((event: NotifyArgs) => {
|
|
||||||
console.log(event);
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const handleClick = async () => {
|
|
||||||
let email = `${uuidv4()}@example.com`;
|
|
||||||
let password = "AppFlowy!2024";
|
|
||||||
const payload = AddUserPB.fromObject({email: email, password: password })
|
|
||||||
let result = await UserWasmEventAddUser(payload);
|
|
||||||
if (!result.ok) {
|
|
||||||
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div>
|
|
||||||
<a href="https://vitejs.dev" target="_blank">
|
|
||||||
<img src={viteLogo} className="logo" alt="Vite logo" />
|
|
||||||
</a>
|
|
||||||
<a href="https://react.dev" target="_blank">
|
|
||||||
<img src={reactLogo} className="logo react" alt="React logo" />
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<h1>Vite + React</h1>
|
|
||||||
<div className="card">
|
|
||||||
<button onClick={handleClick}>Click me!</button>
|
|
||||||
<p>
|
|
||||||
Edit <code>src/App.tsx</code> and save to test HMR
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<p className="read-the-docs">
|
|
||||||
Click on the Vite and React logos to learn more
|
|
||||||
</p>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default App;
|
|
@ -1,35 +0,0 @@
|
|||||||
|
|
||||||
|
|
||||||
import { initEventBus } from "./event_bus.ts";
|
|
||||||
import {async_event, register_listener} from "../../wasm-libs/af-wasm/pkg";
|
|
||||||
|
|
||||||
export function initApp() {
|
|
||||||
initEventBus();
|
|
||||||
register_listener();
|
|
||||||
}
|
|
||||||
|
|
||||||
type InvokeArgs = Record<string, unknown>;
|
|
||||||
|
|
||||||
export async function invoke<T>(cmd: string, args?: InvokeArgs): Promise<T> {
|
|
||||||
switch (cmd) {
|
|
||||||
case "invoke_request":
|
|
||||||
const request = args?.request as { ty?: unknown, payload?: unknown } | undefined;
|
|
||||||
if (!request || typeof request !== 'object') {
|
|
||||||
throw new Error("Invalid or missing 'request' argument in 'invoke_request'");
|
|
||||||
}
|
|
||||||
|
|
||||||
const { ty, payload } = request;
|
|
||||||
|
|
||||||
if (typeof ty !== 'string') {
|
|
||||||
throw new Error("Invalid 'ty' in request for 'invoke_request'");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(payload instanceof Array)) {
|
|
||||||
throw new Error("Invalid 'payload' in request for 'invoke_request'");
|
|
||||||
}
|
|
||||||
|
|
||||||
return async_event(ty, new Uint8Array(payload));
|
|
||||||
default:
|
|
||||||
throw new Error(`Unknown command: ${cmd}`);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,34 +0,0 @@
|
|||||||
import { EventEmitter } from "events";
|
|
||||||
import { NotifyArgs } from "../@types/global";
|
|
||||||
|
|
||||||
const AF_NOTIFICATION = "af-notification";
|
|
||||||
|
|
||||||
let eventEmitter: EventEmitter;
|
|
||||||
export function getEventEmitterInstance() {
|
|
||||||
if (!eventEmitter) {
|
|
||||||
eventEmitter = new EventEmitter();
|
|
||||||
}
|
|
||||||
return eventEmitter;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function initEventBus() {
|
|
||||||
window.onFlowyNotify = (eventName: string, args: NotifyArgs) => {
|
|
||||||
notify(eventName, args);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function notify(_eventName: string, args: NotifyArgs) {
|
|
||||||
const eventEmitter = getEventEmitterInstance();
|
|
||||||
eventEmitter.emit(AF_NOTIFICATION, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function onNotify(callback: (args: NotifyArgs) => void) {
|
|
||||||
const eventEmitter = getEventEmitterInstance();
|
|
||||||
eventEmitter.on(AF_NOTIFICATION, callback);
|
|
||||||
return offNotify;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function offNotify() {
|
|
||||||
const eventEmitter = getEventEmitterInstance();
|
|
||||||
eventEmitter.removeAllListeners(AF_NOTIFICATION);
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
import { NotifyArgs } from "../@types/global";
|
|
||||||
import { onNotify } from "./event_bus.ts";
|
|
||||||
|
|
||||||
export function subscribeNotification(
|
|
||||||
callback: (args: NotifyArgs) => void,
|
|
||||||
options?: { id?: string }
|
|
||||||
) {
|
|
||||||
return onNotify((payload) => {
|
|
||||||
const { id } = payload;
|
|
||||||
|
|
||||||
if (options?.id !== undefined && id !== options.id) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
callback(payload);
|
|
||||||
});
|
|
||||||
}
|
|
@ -1 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>
|
|
Before Width: | Height: | Size: 4.0 KiB |
@ -1,68 +0,0 @@
|
|||||||
:root {
|
|
||||||
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
|
|
||||||
line-height: 1.5;
|
|
||||||
font-weight: 400;
|
|
||||||
|
|
||||||
color-scheme: light dark;
|
|
||||||
color: rgba(255, 255, 255, 0.87);
|
|
||||||
background-color: #242424;
|
|
||||||
|
|
||||||
font-synthesis: none;
|
|
||||||
text-rendering: optimizeLegibility;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
-moz-osx-font-smoothing: grayscale;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
font-weight: 500;
|
|
||||||
color: #646cff;
|
|
||||||
text-decoration: inherit;
|
|
||||||
}
|
|
||||||
a:hover {
|
|
||||||
color: #535bf2;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
margin: 0;
|
|
||||||
display: flex;
|
|
||||||
place-items: center;
|
|
||||||
min-width: 320px;
|
|
||||||
min-height: 100vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: 3.2em;
|
|
||||||
line-height: 1.1;
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
border-radius: 8px;
|
|
||||||
border: 1px solid transparent;
|
|
||||||
padding: 0.6em 1.2em;
|
|
||||||
font-size: 1em;
|
|
||||||
font-weight: 500;
|
|
||||||
font-family: inherit;
|
|
||||||
background-color: #1a1a1a;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: border-color 0.25s;
|
|
||||||
}
|
|
||||||
button:hover {
|
|
||||||
border-color: #646cff;
|
|
||||||
}
|
|
||||||
button:focus,
|
|
||||||
button:focus-visible {
|
|
||||||
outline: 4px auto -webkit-focus-ring-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (prefers-color-scheme: light) {
|
|
||||||
:root {
|
|
||||||
color: #213547;
|
|
||||||
background-color: #ffffff;
|
|
||||||
}
|
|
||||||
a:hover {
|
|
||||||
color: #747bff;
|
|
||||||
}
|
|
||||||
button {
|
|
||||||
background-color: #f9f9f9;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
import React from 'react'
|
|
||||||
import ReactDOM from 'react-dom/client'
|
|
||||||
import App from './App.tsx'
|
|
||||||
import './index.css'
|
|
||||||
|
|
||||||
ReactDOM.createRoot(document.getElementById('root')!).render(
|
|
||||||
<React.StrictMode>
|
|
||||||
<App />
|
|
||||||
</React.StrictMode>,
|
|
||||||
)
|
|
@ -1,5 +0,0 @@
|
|||||||
export * from "./models/af-user";
|
|
||||||
export * from "./models/flowy-error";
|
|
||||||
export * from "./models/flowy-folder";
|
|
||||||
export * from "./models/flowy-document";
|
|
||||||
export * from "./models/flowy-notification";
|
|
1
frontend/appflowy_web/src/vite-env.d.ts
vendored
1
frontend/appflowy_web/src/vite-env.d.ts
vendored
@ -1 +0,0 @@
|
|||||||
/// <reference types="vite/client" />
|
|
@ -1,31 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"target": "ES2020",
|
|
||||||
"useDefineForClassFields": true,
|
|
||||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
|
||||||
"module": "ESNext",
|
|
||||||
"skipLibCheck": true,
|
|
||||||
|
|
||||||
/* Bundler mode */
|
|
||||||
"moduleResolution": "bundler",
|
|
||||||
"allowImportingTsExtensions": true,
|
|
||||||
"resolveJsonModule": true,
|
|
||||||
"isolatedModules": true,
|
|
||||||
"noEmit": true,
|
|
||||||
"jsx": "react-jsx",
|
|
||||||
|
|
||||||
/* Linting */
|
|
||||||
"strict": true,
|
|
||||||
"noUnusedLocals": true,
|
|
||||||
"noUnusedParameters": true,
|
|
||||||
"noFallthroughCasesInSwitch": true,
|
|
||||||
"typeRoots": ["./node_modules/@types", "./src/@types"],
|
|
||||||
|
|
||||||
"baseUrl": "./",
|
|
||||||
"paths": {
|
|
||||||
"@/*": ["src/*"],
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"include": ["src", "vite.config.ts"],
|
|
||||||
"references": [{ "path": "./tsconfig.node.json" }]
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"composite": true,
|
|
||||||
"skipLibCheck": true,
|
|
||||||
"module": "ESNext",
|
|
||||||
"moduleResolution": "bundler",
|
|
||||||
"allowSyntheticDefaultImports": true
|
|
||||||
},
|
|
||||||
"include": ["vite.config.ts"]
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
import { defineConfig } from 'vite'
|
|
||||||
import react from '@vitejs/plugin-react'
|
|
||||||
import wasm from "vite-plugin-wasm";
|
|
||||||
|
|
||||||
// https://vitejs.dev/config/
|
|
||||||
export default defineConfig({
|
|
||||||
plugins: [react(), wasm()],
|
|
||||||
resolve: {
|
|
||||||
alias: [
|
|
||||||
{ find: 'src/', replacement: `${__dirname}/src/` },
|
|
||||||
{ find: '@/', replacement: `${__dirname}/src/` },
|
|
||||||
{ find: '$app/', replacement: `${__dirname}/src/application/` },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
})
|
|
5283
frontend/appflowy_web/wasm-libs/Cargo.lock
generated
5283
frontend/appflowy_web/wasm-libs/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -1,77 +0,0 @@
|
|||||||
[workspace]
|
|
||||||
members = ["af-wasm", "af-user", "af-persistence"]
|
|
||||||
resolver = "2"
|
|
||||||
|
|
||||||
[workspace.dependencies]
|
|
||||||
af-user = { path = "af-user" }
|
|
||||||
af-persistence = { path = "af-persistence" }
|
|
||||||
lib-dispatch = { path = "../../rust-lib/lib-dispatch" }
|
|
||||||
parking_lot = { version = "0.12.1" }
|
|
||||||
tracing = { version = "0.1.22" }
|
|
||||||
serde = { version = "1.0.194", features = ["derive"] }
|
|
||||||
serde_json = "1.0"
|
|
||||||
collab-integrate = { path = "../../rust-lib/collab-integrate" }
|
|
||||||
flowy-notification = { path = "../../rust-lib/flowy-notification" }
|
|
||||||
flowy-user-pub = { path = "../../rust-lib/flowy-user-pub" }
|
|
||||||
flowy-server = { path = "../../rust-lib/flowy-server" }
|
|
||||||
flowy-server-pub = { path = "../../rust-lib/flowy-server-pub" }
|
|
||||||
flowy-error = { path = "../../rust-lib/flowy-error" }
|
|
||||||
flowy-derive = { path = "../../rust-lib/build-tool/flowy-derive" }
|
|
||||||
flowy-codegen = { path = "../../rust-lib/build-tool/flowy-codegen" }
|
|
||||||
flowy-document = { path = "../../rust-lib/flowy-document" }
|
|
||||||
flowy-folder = { path = "../../rust-lib/flowy-folder" }
|
|
||||||
flowy-storage = { path = "../../rust-lib/flowy-storage" }
|
|
||||||
lib-infra = { path = "../../rust-lib/lib-infra" }
|
|
||||||
bytes = { version = "1.5" }
|
|
||||||
protobuf = { version = "2.28.0" }
|
|
||||||
thiserror = "1.0"
|
|
||||||
anyhow = "1.0"
|
|
||||||
futures-util = "0.3"
|
|
||||||
uuid = { version = "1.5", features = ["serde", "v4", "v5"] }
|
|
||||||
tokio-stream = "0.1"
|
|
||||||
tokio = { version = "1.35", features = ["sync"] }
|
|
||||||
wasm-bindgen-futures = "0.4.40"
|
|
||||||
serde-wasm-bindgen = "0.4"
|
|
||||||
# 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" }
|
|
||||||
yrs = "0.18.8"
|
|
||||||
|
|
||||||
# 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 = "d61524d63605aa010afa6a734cbbe4fb4cd68ea1" }
|
|
||||||
|
|
||||||
[profile.dev]
|
|
||||||
opt-level = 0
|
|
||||||
lto = false
|
|
||||||
codegen-units = 16
|
|
||||||
|
|
||||||
[profile.release]
|
|
||||||
lto = true
|
|
||||||
opt-level = 3
|
|
||||||
codegen-units = 1
|
|
||||||
|
|
||||||
[patch.crates-io]
|
|
||||||
collab = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "5048762" }
|
|
||||||
collab-entity = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "5048762" }
|
|
||||||
collab-folder = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "5048762" }
|
|
||||||
collab-document = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "5048762" }
|
|
||||||
collab-database = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "5048762" }
|
|
||||||
collab-plugins = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "5048762" }
|
|
||||||
collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "5048762" }
|
|
@ -1,20 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "af-persistence"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
indexed_db_futures = { version = "0.4" }
|
|
||||||
js-sys = "0.3"
|
|
||||||
wasm-bindgen = "0.2"
|
|
||||||
web-sys = { version = "0.3", features = ["console", "Window"] }
|
|
||||||
tokio = { version = "1.26.0", features = ["sync", "rt"] }
|
|
||||||
flowy-error.workspace = true
|
|
||||||
thiserror.workspace = true
|
|
||||||
anyhow.workspace = true
|
|
||||||
serde.workspace = true
|
|
||||||
serde_json.workspace = true
|
|
||||||
futures-util.workspace = true
|
|
||||||
wasm-bindgen-futures.workspace = true
|
|
@ -1,30 +0,0 @@
|
|||||||
use flowy_error::FlowyError;
|
|
||||||
use web_sys::DomException;
|
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
|
||||||
pub enum PersistenceError {
|
|
||||||
#[error(transparent)]
|
|
||||||
Internal(#[from] anyhow::Error),
|
|
||||||
|
|
||||||
#[error(transparent)]
|
|
||||||
SerdeError(#[from] serde_json::Error),
|
|
||||||
|
|
||||||
#[error("{0}")]
|
|
||||||
RecordNotFound(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<DomException> for PersistenceError {
|
|
||||||
fn from(value: DomException) -> Self {
|
|
||||||
PersistenceError::Internal(anyhow::anyhow!("DOMException: {:?}", value))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<PersistenceError> for FlowyError {
|
|
||||||
fn from(value: PersistenceError) -> Self {
|
|
||||||
match value {
|
|
||||||
PersistenceError::Internal(value) => FlowyError::internal().with_context(value),
|
|
||||||
PersistenceError::SerdeError(value) => FlowyError::serde().with_context(value),
|
|
||||||
PersistenceError::RecordNotFound(value) => FlowyError::record_not_found().with_context(value),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,2 +0,0 @@
|
|||||||
pub mod error;
|
|
||||||
pub mod store;
|
|
@ -1,192 +0,0 @@
|
|||||||
use crate::error::PersistenceError;
|
|
||||||
use anyhow::anyhow;
|
|
||||||
use futures_util::future::LocalBoxFuture;
|
|
||||||
use indexed_db_futures::idb_object_store::IdbObjectStore;
|
|
||||||
use indexed_db_futures::idb_transaction::{IdbTransaction, IdbTransactionResult};
|
|
||||||
use indexed_db_futures::prelude::IdbOpenDbRequestLike;
|
|
||||||
use indexed_db_futures::{IdbDatabase, IdbQuerySource, IdbVersionChangeEvent};
|
|
||||||
use js_sys::Uint8Array;
|
|
||||||
use serde::de::DeserializeOwned;
|
|
||||||
use serde::Serialize;
|
|
||||||
use std::future::Future;
|
|
||||||
use std::sync::Arc;
|
|
||||||
use tokio::sync::RwLock;
|
|
||||||
use wasm_bindgen::JsValue;
|
|
||||||
use web_sys::IdbTransactionMode;
|
|
||||||
|
|
||||||
pub trait IndexddbStore {
|
|
||||||
fn get<'a, T>(&'a self, key: &'a str) -> LocalBoxFuture<Result<Option<T>, PersistenceError>>
|
|
||||||
where
|
|
||||||
T: serde::de::DeserializeOwned + Sync;
|
|
||||||
fn set<'a, T>(&'a self, key: &'a str, value: T) -> LocalBoxFuture<Result<(), PersistenceError>>
|
|
||||||
where
|
|
||||||
T: serde::Serialize + Sync + 'a;
|
|
||||||
fn remove<'a>(&'a self, key: &'a str) -> LocalBoxFuture<Result<(), PersistenceError>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
const APPFLOWY_STORE: &str = "appflowy_store";
|
|
||||||
pub struct AppFlowyWASMStore {
|
|
||||||
db: Arc<RwLock<IdbDatabase>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl Send for AppFlowyWASMStore {}
|
|
||||||
unsafe impl Sync for AppFlowyWASMStore {}
|
|
||||||
|
|
||||||
impl AppFlowyWASMStore {
|
|
||||||
pub async fn new() -> Result<Self, PersistenceError> {
|
|
||||||
let mut db_req = IdbDatabase::open_u32("appflowy", 1)?;
|
|
||||||
db_req.set_on_upgrade_needed(Some(|evt: &IdbVersionChangeEvent| -> Result<(), JsValue> {
|
|
||||||
if !evt.db().object_store_names().any(|n| &n == APPFLOWY_STORE) {
|
|
||||||
evt.db().create_object_store(APPFLOWY_STORE)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}));
|
|
||||||
let db = Arc::new(RwLock::new(db_req.await?));
|
|
||||||
Ok(Self { db })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn begin_read_transaction<F, Fut>(&self, f: F) -> Result<(), PersistenceError>
|
|
||||||
where
|
|
||||||
F: FnOnce(&IndexddbStoreImpl<'_>) -> Fut,
|
|
||||||
Fut: Future<Output = Result<(), PersistenceError>>,
|
|
||||||
{
|
|
||||||
let db = self.db.read().await;
|
|
||||||
let txn = db.transaction_on_one_with_mode(APPFLOWY_STORE, IdbTransactionMode::Readonly)?;
|
|
||||||
let store = store_from_transaction(&txn)?;
|
|
||||||
let operation = IndexddbStoreImpl(store);
|
|
||||||
f(&operation).await?;
|
|
||||||
transaction_result_to_result(txn.await)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn begin_write_transaction<F>(&self, f: F) -> Result<(), PersistenceError>
|
|
||||||
where
|
|
||||||
F: FnOnce(IndexddbStoreImpl<'_>) -> LocalBoxFuture<'_, Result<(), PersistenceError>>,
|
|
||||||
{
|
|
||||||
let db = self.db.write().await;
|
|
||||||
let txn = db.transaction_on_one_with_mode(APPFLOWY_STORE, IdbTransactionMode::Readwrite)?;
|
|
||||||
let store = store_from_transaction(&txn)?;
|
|
||||||
let operation = IndexddbStoreImpl(store);
|
|
||||||
f(operation).await?;
|
|
||||||
transaction_result_to_result(txn.await)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct IndexddbStoreImpl<'i>(IdbObjectStore<'i>);
|
|
||||||
|
|
||||||
impl<'i> IndexddbStore for IndexddbStoreImpl<'i> {
|
|
||||||
fn get<'a, T>(&'a self, key: &'a str) -> LocalBoxFuture<Result<Option<T>, PersistenceError>>
|
|
||||||
where
|
|
||||||
T: DeserializeOwned + Sync,
|
|
||||||
{
|
|
||||||
let js_key = to_js_value(key);
|
|
||||||
Box::pin(async move {
|
|
||||||
match self.0.get(&js_key)?.await? {
|
|
||||||
None => Err(PersistenceError::RecordNotFound(format!(
|
|
||||||
"Can't find the value for given key: {} ",
|
|
||||||
key
|
|
||||||
))),
|
|
||||||
Some(value) => {
|
|
||||||
let bytes = Uint8Array::new(&value).to_vec();
|
|
||||||
let object = serde_json::from_slice::<T>(&bytes)?;
|
|
||||||
Ok(Some(object))
|
|
||||||
},
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set<'a, T>(&'a self, key: &'a str, value: T) -> LocalBoxFuture<Result<(), PersistenceError>>
|
|
||||||
where
|
|
||||||
T: Serialize + Sync + 'a,
|
|
||||||
{
|
|
||||||
let js_key = to_js_value(key);
|
|
||||||
Box::pin(async move {
|
|
||||||
let js_value = to_js_value(serde_json::to_vec(&value)?);
|
|
||||||
self.0.put_key_val(&js_key, &js_value)?.await?;
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn remove<'a>(&'a self, key: &'a str) -> LocalBoxFuture<Result<(), PersistenceError>> {
|
|
||||||
let js_key = to_js_value(key);
|
|
||||||
Box::pin(async move {
|
|
||||||
self.0.delete(&js_key)?.await?;
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IndexddbStore for AppFlowyWASMStore {
|
|
||||||
fn get<'a, T>(&'a self, key: &'a str) -> LocalBoxFuture<Result<Option<T>, PersistenceError>>
|
|
||||||
where
|
|
||||||
T: serde::de::DeserializeOwned + Sync,
|
|
||||||
{
|
|
||||||
let db = Arc::downgrade(&self.db);
|
|
||||||
Box::pin(async move {
|
|
||||||
let db = db.upgrade().ok_or_else(|| {
|
|
||||||
PersistenceError::Internal(anyhow!("Failed to upgrade the database reference"))
|
|
||||||
})?;
|
|
||||||
let read_guard = db.read().await;
|
|
||||||
let txn =
|
|
||||||
read_guard.transaction_on_one_with_mode(APPFLOWY_STORE, IdbTransactionMode::Readonly)?;
|
|
||||||
let store = store_from_transaction(&txn)?;
|
|
||||||
IndexddbStoreImpl(store).get(key).await
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set<'a, T>(&'a self, key: &'a str, value: T) -> LocalBoxFuture<Result<(), PersistenceError>>
|
|
||||||
where
|
|
||||||
T: serde::Serialize + Sync + 'a,
|
|
||||||
{
|
|
||||||
let db = Arc::downgrade(&self.db);
|
|
||||||
Box::pin(async move {
|
|
||||||
let db = db.upgrade().ok_or_else(|| {
|
|
||||||
PersistenceError::Internal(anyhow!("Failed to upgrade the database reference"))
|
|
||||||
})?;
|
|
||||||
let read_guard = db.read().await;
|
|
||||||
let txn =
|
|
||||||
read_guard.transaction_on_one_with_mode(APPFLOWY_STORE, IdbTransactionMode::Readwrite)?;
|
|
||||||
let store = store_from_transaction(&txn)?;
|
|
||||||
IndexddbStoreImpl(store).set(key, value).await?;
|
|
||||||
transaction_result_to_result(txn.await)?;
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn remove<'a>(&'a self, key: &'a str) -> LocalBoxFuture<Result<(), PersistenceError>> {
|
|
||||||
let db = Arc::downgrade(&self.db);
|
|
||||||
Box::pin(async move {
|
|
||||||
let db = db.upgrade().ok_or_else(|| {
|
|
||||||
PersistenceError::Internal(anyhow!("Failed to upgrade the database reference"))
|
|
||||||
})?;
|
|
||||||
let read_guard = db.read().await;
|
|
||||||
let txn =
|
|
||||||
read_guard.transaction_on_one_with_mode(APPFLOWY_STORE, IdbTransactionMode::Readwrite)?;
|
|
||||||
let store = store_from_transaction(&txn)?;
|
|
||||||
IndexddbStoreImpl(store).remove(key).await?;
|
|
||||||
transaction_result_to_result(txn.await)?;
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_js_value<K: AsRef<[u8]>>(key: K) -> JsValue {
|
|
||||||
JsValue::from(Uint8Array::from(key.as_ref()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn store_from_transaction<'a>(
|
|
||||||
txn: &'a IdbTransaction<'a>,
|
|
||||||
) -> Result<IdbObjectStore<'a>, PersistenceError> {
|
|
||||||
txn
|
|
||||||
.object_store(APPFLOWY_STORE)
|
|
||||||
.map_err(PersistenceError::from)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn transaction_result_to_result(result: IdbTransactionResult) -> Result<(), PersistenceError> {
|
|
||||||
match result {
|
|
||||||
IdbTransactionResult::Success => Ok(()),
|
|
||||||
IdbTransactionResult::Error(err) => Err(PersistenceError::from(err)),
|
|
||||||
IdbTransactionResult::Abort => Err(PersistenceError::Internal(anyhow!("Transaction aborted"))),
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "af-user"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
af-persistence.workspace = true
|
|
||||||
lib-dispatch = { workspace = true }
|
|
||||||
flowy-derive = { workspace = true }
|
|
||||||
flowy-error = { workspace = true, features = ["impl_from_dispatch_error", "impl_from_collab_persistence"] }
|
|
||||||
flowy-user-pub = { workspace = true }
|
|
||||||
strum_macros = "0.25.2"
|
|
||||||
tracing.workspace = true
|
|
||||||
lib-infra = { workspace = true }
|
|
||||||
collab = { workspace = true }
|
|
||||||
collab-entity.workspace = true
|
|
||||||
collab-user.workspace = true
|
|
||||||
collab-integrate = { workspace = true }
|
|
||||||
protobuf.workspace = true
|
|
||||||
bytes.workspace = true
|
|
||||||
anyhow.workspace = true
|
|
||||||
wasm-bindgen-futures.workspace = true
|
|
||||||
tokio = { workspace = true, features = ["sync"] }
|
|
||||||
|
|
||||||
[build-dependencies]
|
|
||||||
flowy-codegen = { workspace = true, features = ["ts"] }
|
|
@ -1,3 +0,0 @@
|
|||||||
# Check out the FlowyConfig (located in flowy_toml.rs) for more details.
|
|
||||||
proto_input = ["src/entities", "src/event_map.rs"]
|
|
||||||
event_files = ["src/event_map.rs"]
|
|
@ -1,17 +0,0 @@
|
|||||||
use flowy_codegen::Project;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
flowy_codegen::protobuf_file::ts_gen(
|
|
||||||
env!("CARGO_PKG_NAME"),
|
|
||||||
"user",
|
|
||||||
Project::Web {
|
|
||||||
relative_path: "../../../".to_string(),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
flowy_codegen::ts_event::gen(
|
|
||||||
"user",
|
|
||||||
Project::Web {
|
|
||||||
relative_path: "../../../".to_string(),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
use af_persistence::store::AppFlowyWASMStore;
|
|
||||||
use flowy_error::FlowyResult;
|
|
||||||
use flowy_user_pub::session::Session;
|
|
||||||
use std::rc::Rc;
|
|
||||||
use std::sync::Arc;
|
|
||||||
use tokio::sync::RwLock;
|
|
||||||
|
|
||||||
pub struct AuthenticateUser {
|
|
||||||
session: Arc<RwLock<Option<Session>>>,
|
|
||||||
store: Rc<AppFlowyWASMStore>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AuthenticateUser {
|
|
||||||
pub async fn new(store: Rc<AppFlowyWASMStore>) -> FlowyResult<Self> {
|
|
||||||
Ok(Self {
|
|
||||||
session: Arc::new(RwLock::new(None)),
|
|
||||||
store,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
pub(crate) const AF_USER_SESSION_KEY: &str = "af-user-session";
|
|
||||||
|
|
||||||
pub(crate) fn user_workspace_key(uid: i64) -> String {
|
|
||||||
format!("af-user-workspaces-{}", uid)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn user_profile_key(uid: i64) -> String {
|
|
||||||
format!("af-user-profile-{}", uid)
|
|
||||||
}
|
|
@ -1,68 +0,0 @@
|
|||||||
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
|
|
||||||
use flowy_user_pub::entities::Authenticator;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
#[derive(ProtoBuf, Default)]
|
|
||||||
pub struct OauthSignInPB {
|
|
||||||
/// Use this field to store the third party auth information.
|
|
||||||
/// Different auth type has different fields.
|
|
||||||
/// Supabase:
|
|
||||||
/// - map: { "uuid": "xxx" }
|
|
||||||
///
|
|
||||||
#[pb(index = 1)]
|
|
||||||
pub map: HashMap<String, String>,
|
|
||||||
|
|
||||||
#[pb(index = 2)]
|
|
||||||
pub authenticator: AuthenticatorPB,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(ProtoBuf_Enum, Eq, PartialEq, Debug, Clone)]
|
|
||||||
pub enum AuthenticatorPB {
|
|
||||||
Local = 0,
|
|
||||||
Supabase = 1,
|
|
||||||
AppFlowyCloud = 2,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Authenticator> for AuthenticatorPB {
|
|
||||||
fn from(auth_type: Authenticator) -> Self {
|
|
||||||
match auth_type {
|
|
||||||
Authenticator::Supabase => AuthenticatorPB::Supabase,
|
|
||||||
Authenticator::Local => AuthenticatorPB::Local,
|
|
||||||
Authenticator::AppFlowyCloud => AuthenticatorPB::AppFlowyCloud,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<AuthenticatorPB> for Authenticator {
|
|
||||||
fn from(pb: AuthenticatorPB) -> Self {
|
|
||||||
match pb {
|
|
||||||
AuthenticatorPB::Supabase => Authenticator::Supabase,
|
|
||||||
AuthenticatorPB::Local => Authenticator::Local,
|
|
||||||
AuthenticatorPB::AppFlowyCloud => Authenticator::AppFlowyCloud,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for AuthenticatorPB {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::AppFlowyCloud
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(ProtoBuf, Default)]
|
|
||||||
pub struct AddUserPB {
|
|
||||||
#[pb(index = 1)]
|
|
||||||
pub email: String,
|
|
||||||
|
|
||||||
#[pb(index = 2)]
|
|
||||||
pub password: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(ProtoBuf, Default)]
|
|
||||||
pub struct UserSignInPB {
|
|
||||||
#[pb(index = 1)]
|
|
||||||
pub email: String,
|
|
||||||
|
|
||||||
#[pb(index = 2)]
|
|
||||||
pub password: String,
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
mod auth;
|
|
||||||
mod user;
|
|
||||||
|
|
||||||
pub use auth::*;
|
|
||||||
pub use user::*;
|
|
@ -1,73 +0,0 @@
|
|||||||
use crate::entities::AuthenticatorPB;
|
|
||||||
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
|
|
||||||
use flowy_user_pub::entities::{EncryptionType, UserProfile};
|
|
||||||
|
|
||||||
#[derive(ProtoBuf, Default, Eq, PartialEq, Debug, Clone)]
|
|
||||||
pub struct UserProfilePB {
|
|
||||||
#[pb(index = 1)]
|
|
||||||
pub id: i64,
|
|
||||||
|
|
||||||
#[pb(index = 2)]
|
|
||||||
pub email: String,
|
|
||||||
|
|
||||||
#[pb(index = 3)]
|
|
||||||
pub name: String,
|
|
||||||
|
|
||||||
#[pb(index = 4)]
|
|
||||||
pub token: String,
|
|
||||||
|
|
||||||
#[pb(index = 5)]
|
|
||||||
pub icon_url: String,
|
|
||||||
|
|
||||||
#[pb(index = 6)]
|
|
||||||
pub openai_key: String,
|
|
||||||
|
|
||||||
#[pb(index = 7)]
|
|
||||||
pub authenticator: AuthenticatorPB,
|
|
||||||
|
|
||||||
#[pb(index = 8)]
|
|
||||||
pub encryption_sign: String,
|
|
||||||
|
|
||||||
#[pb(index = 9)]
|
|
||||||
pub workspace_id: String,
|
|
||||||
|
|
||||||
#[pb(index = 10)]
|
|
||||||
pub stability_ai_key: String,
|
|
||||||
|
|
||||||
#[pb(index = 11)]
|
|
||||||
pub ai_model: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<UserProfile> for UserProfilePB {
|
|
||||||
fn from(user_profile: UserProfile) -> Self {
|
|
||||||
let (encryption_sign, _encryption_ty) = match user_profile.encryption_type {
|
|
||||||
EncryptionType::NoEncryption => ("".to_string(), EncryptionTypePB::NoEncryption),
|
|
||||||
EncryptionType::SelfEncryption(sign) => (sign, EncryptionTypePB::Symmetric),
|
|
||||||
};
|
|
||||||
Self {
|
|
||||||
id: user_profile.uid,
|
|
||||||
email: user_profile.email,
|
|
||||||
name: user_profile.name,
|
|
||||||
token: user_profile.token,
|
|
||||||
icon_url: user_profile.icon_url,
|
|
||||||
openai_key: user_profile.openai_key,
|
|
||||||
encryption_sign,
|
|
||||||
authenticator: user_profile.authenticator.into(),
|
|
||||||
workspace_id: user_profile.workspace_id,
|
|
||||||
stability_ai_key: user_profile.stability_ai_key,
|
|
||||||
ai_model: user_profile.ai_model,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(ProtoBuf_Enum, Eq, PartialEq, Debug, Clone)]
|
|
||||||
pub enum EncryptionTypePB {
|
|
||||||
NoEncryption = 0,
|
|
||||||
Symmetric = 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for EncryptionTypePB {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::NoEncryption
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,48 +0,0 @@
|
|||||||
use crate::entities::*;
|
|
||||||
use crate::manager::UserManager;
|
|
||||||
use flowy_error::{FlowyError, FlowyResult};
|
|
||||||
use lib_dispatch::prelude::{data_result_ok, AFPluginData, AFPluginState, DataResult};
|
|
||||||
use lib_infra::box_any::BoxAny;
|
|
||||||
use std::rc::{Rc, Weak};
|
|
||||||
|
|
||||||
#[tracing::instrument(level = "debug", skip(data, manager), err)]
|
|
||||||
pub async fn oauth_sign_in_handler(
|
|
||||||
data: AFPluginData<OauthSignInPB>,
|
|
||||||
manager: AFPluginState<Weak<UserManager>>,
|
|
||||||
) -> DataResult<UserProfilePB, FlowyError> {
|
|
||||||
let manager = upgrade_manager(manager)?;
|
|
||||||
let params = data.into_inner();
|
|
||||||
let user_profile = manager.sign_up(BoxAny::new(params.map)).await?;
|
|
||||||
data_result_ok(user_profile.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tracing::instrument(level = "debug", skip(data, manager), err)]
|
|
||||||
pub async fn add_user_handler(
|
|
||||||
data: AFPluginData<AddUserPB>,
|
|
||||||
manager: AFPluginState<Weak<UserManager>>,
|
|
||||||
) -> Result<(), FlowyError> {
|
|
||||||
let manager = upgrade_manager(manager)?;
|
|
||||||
let params = data.into_inner();
|
|
||||||
manager.add_user(¶ms.email, ¶ms.password).await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tracing::instrument(level = "debug", skip(data, manager), err)]
|
|
||||||
pub async fn sign_in_with_password_handler(
|
|
||||||
data: AFPluginData<UserSignInPB>,
|
|
||||||
manager: AFPluginState<Weak<UserManager>>,
|
|
||||||
) -> DataResult<UserProfilePB, FlowyError> {
|
|
||||||
let manager = upgrade_manager(manager)?;
|
|
||||||
let params = data.into_inner();
|
|
||||||
let user_profile = manager
|
|
||||||
.sign_in_with_password(¶ms.email, ¶ms.password)
|
|
||||||
.await?;
|
|
||||||
data_result_ok(UserProfilePB::from(user_profile))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn upgrade_manager(manager: AFPluginState<Weak<UserManager>>) -> FlowyResult<Rc<UserManager>> {
|
|
||||||
let manager = manager
|
|
||||||
.upgrade()
|
|
||||||
.ok_or(FlowyError::internal().with_context("The user session is already drop"))?;
|
|
||||||
Ok(manager)
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
use crate::event_handler::*;
|
|
||||||
use crate::manager::UserManager;
|
|
||||||
use flowy_derive::{Flowy_Event, ProtoBuf_Enum};
|
|
||||||
use lib_dispatch::prelude::AFPlugin;
|
|
||||||
use std::rc::Weak;
|
|
||||||
use strum_macros::Display;
|
|
||||||
|
|
||||||
#[rustfmt::skip]
|
|
||||||
pub fn init(user_manager: Weak<UserManager>) -> AFPlugin {
|
|
||||||
AFPlugin::new()
|
|
||||||
.name("Flowy-User")
|
|
||||||
.state(user_manager)
|
|
||||||
.event(UserWasmEvent::OauthSignIn, oauth_sign_in_handler)
|
|
||||||
.event(UserWasmEvent::AddUser, add_user_handler)
|
|
||||||
.event(UserWasmEvent::SignInPassword, sign_in_with_password_handler)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Display, Hash, ProtoBuf_Enum, Flowy_Event)]
|
|
||||||
#[event_err = "FlowyError"]
|
|
||||||
pub enum UserWasmEvent {
|
|
||||||
#[event(input = "OauthSignInPB", output = "UserProfilePB")]
|
|
||||||
OauthSignIn = 0,
|
|
||||||
|
|
||||||
#[event(input = "AddUserPB")]
|
|
||||||
AddUser = 1,
|
|
||||||
|
|
||||||
#[event(input = "UserSignInPB", output = "UserProfilePB")]
|
|
||||||
SignInPassword = 2,
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
pub mod authenticate_user;
|
|
||||||
mod define;
|
|
||||||
pub mod entities;
|
|
||||||
mod event_handler;
|
|
||||||
pub mod event_map;
|
|
||||||
pub mod manager;
|
|
||||||
mod protobuf;
|
|
@ -1,205 +0,0 @@
|
|||||||
use crate::authenticate_user::AuthenticateUser;
|
|
||||||
use crate::define::{user_profile_key, user_workspace_key, AF_USER_SESSION_KEY};
|
|
||||||
use af_persistence::store::{AppFlowyWASMStore, IndexddbStore};
|
|
||||||
use anyhow::Context;
|
|
||||||
use collab::core::collab::DataSource;
|
|
||||||
use collab_entity::CollabType;
|
|
||||||
use collab_integrate::collab_builder::{AppFlowyCollabBuilder, CollabBuilderConfig};
|
|
||||||
use collab_integrate::{CollabKVDB, MutexCollab};
|
|
||||||
use collab_user::core::{MutexUserAwareness, UserAwareness};
|
|
||||||
use flowy_error::{ErrorCode, FlowyError, FlowyResult};
|
|
||||||
use flowy_user_pub::cloud::{UserCloudConfig, UserCloudServiceProvider};
|
|
||||||
use flowy_user_pub::entities::{
|
|
||||||
user_awareness_object_id, AuthResponse, Authenticator, UserAuthResponse, UserProfile,
|
|
||||||
UserWorkspace,
|
|
||||||
};
|
|
||||||
use flowy_user_pub::session::Session;
|
|
||||||
use lib_infra::box_any::BoxAny;
|
|
||||||
use lib_infra::future::Fut;
|
|
||||||
use std::rc::Rc;
|
|
||||||
use std::sync::{Arc, Mutex, Weak};
|
|
||||||
use tracing::{error, instrument, trace};
|
|
||||||
|
|
||||||
pub trait UserCallback {
|
|
||||||
fn did_init(
|
|
||||||
&self,
|
|
||||||
user_id: i64,
|
|
||||||
cloud_config: &Option<UserCloudConfig>,
|
|
||||||
user_workspace: &UserWorkspace,
|
|
||||||
device_id: &str,
|
|
||||||
) -> Fut<FlowyResult<()>>;
|
|
||||||
fn did_sign_in(&self, uid: i64, workspace: &UserWorkspace, device_id: &str) -> FlowyResult<()>;
|
|
||||||
fn did_sign_up(
|
|
||||||
&self,
|
|
||||||
is_new_user: bool,
|
|
||||||
user_profile: &UserProfile,
|
|
||||||
user_workspace: &UserWorkspace,
|
|
||||||
device_id: &str,
|
|
||||||
) -> Fut<FlowyResult<()>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct UserManager {
|
|
||||||
device_id: String,
|
|
||||||
pub(crate) store: Rc<AppFlowyWASMStore>,
|
|
||||||
pub(crate) cloud_services: Rc<dyn UserCloudServiceProvider>,
|
|
||||||
pub(crate) collab_builder: Weak<AppFlowyCollabBuilder>,
|
|
||||||
pub(crate) authenticate_user: Rc<AuthenticateUser>,
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub(crate) user_awareness: Rc<Mutex<Option<MutexUserAwareness>>>,
|
|
||||||
pub(crate) collab_db: Arc<CollabKVDB>,
|
|
||||||
|
|
||||||
user_callbacks: Vec<Rc<dyn UserCallback>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UserManager {
|
|
||||||
pub async fn new(
|
|
||||||
device_id: &str,
|
|
||||||
store: Rc<AppFlowyWASMStore>,
|
|
||||||
cloud_services: Rc<dyn UserCloudServiceProvider>,
|
|
||||||
authenticate_user: Rc<AuthenticateUser>,
|
|
||||||
collab_builder: Weak<AppFlowyCollabBuilder>,
|
|
||||||
) -> Result<Self, FlowyError> {
|
|
||||||
let device_id = device_id.to_string();
|
|
||||||
let store = Rc::new(AppFlowyWASMStore::new().await?);
|
|
||||||
let collab_db = Arc::new(CollabKVDB::new().await?);
|
|
||||||
Ok(Self {
|
|
||||||
device_id,
|
|
||||||
cloud_services,
|
|
||||||
collab_builder,
|
|
||||||
store,
|
|
||||||
authenticate_user,
|
|
||||||
user_callbacks: vec![],
|
|
||||||
user_awareness: Rc::new(Default::default()),
|
|
||||||
collab_db,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn sign_up(&self, params: BoxAny) -> FlowyResult<UserProfile> {
|
|
||||||
let auth_service = self.cloud_services.get_user_service()?;
|
|
||||||
let response: AuthResponse = auth_service.sign_up(params).await?;
|
|
||||||
let new_user_profile = UserProfile::from((&response, &Authenticator::AppFlowyCloud));
|
|
||||||
let new_session = Session::from(&response);
|
|
||||||
|
|
||||||
self.prepare_collab(&new_session);
|
|
||||||
self
|
|
||||||
.save_auth_data(&response, &new_user_profile, &new_session)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
for callback in self.user_callbacks.iter() {
|
|
||||||
if let Err(e) = callback
|
|
||||||
.did_sign_up(
|
|
||||||
response.is_new_user,
|
|
||||||
&new_user_profile,
|
|
||||||
&new_session.user_workspace,
|
|
||||||
&self.device_id,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
error!("Failed to call did_sign_up callback: {:?}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(nathan): send notification
|
|
||||||
// send_auth_state_notification(AuthStateChangedPB {
|
|
||||||
// state: AuthStatePB::AuthStateSignIn,
|
|
||||||
// message: "Sign in success".to_string(),
|
|
||||||
// });
|
|
||||||
Ok(new_user_profile)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn add_user(&self, email: &str, password: &str) -> Result<(), FlowyError> {
|
|
||||||
let auth_service = self.cloud_services.get_user_service()?;
|
|
||||||
auth_service.create_user(email, password).await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn sign_in_with_password(
|
|
||||||
&self,
|
|
||||||
email: &str,
|
|
||||||
password: &str,
|
|
||||||
) -> Result<UserProfile, FlowyError> {
|
|
||||||
let auth_service = self.cloud_services.get_user_service()?;
|
|
||||||
let user_profile = auth_service.sign_in_with_password(email, password).await?;
|
|
||||||
Ok(user_profile)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn prepare_collab(&self, session: &Session) {
|
|
||||||
let collab_builder = self.collab_builder.upgrade().unwrap();
|
|
||||||
collab_builder.initialize(session.user_workspace.id.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[instrument(level = "info", skip_all, err)]
|
|
||||||
async fn save_auth_data(
|
|
||||||
&self,
|
|
||||||
response: &impl UserAuthResponse,
|
|
||||||
user_profile: &UserProfile,
|
|
||||||
session: &Session,
|
|
||||||
) -> Result<(), FlowyError> {
|
|
||||||
let uid = user_profile.uid;
|
|
||||||
let user_profile = user_profile.clone();
|
|
||||||
let session = session.clone();
|
|
||||||
let user_workspace = response.user_workspaces().to_vec();
|
|
||||||
self
|
|
||||||
.store
|
|
||||||
.begin_write_transaction(|store| {
|
|
||||||
Box::pin(async move {
|
|
||||||
store.set(&user_workspace_key(uid), &user_workspace).await?;
|
|
||||||
store.set(AF_USER_SESSION_KEY, session).await?;
|
|
||||||
store.set(&user_profile_key(uid), user_profile).await?;
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn save_user_session(&self, session: &Session) -> FlowyResult<()> {
|
|
||||||
self.store.set(AF_USER_SESSION_KEY, session).await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn save_user_workspaces(
|
|
||||||
&self,
|
|
||||||
uid: i64,
|
|
||||||
user_workspaces: &[UserWorkspace],
|
|
||||||
) -> FlowyResult<()> {
|
|
||||||
self
|
|
||||||
.store
|
|
||||||
.set(&user_workspace_key(uid), &user_workspaces.to_vec())
|
|
||||||
.await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn save_user_profile(&self, user_profile: &UserProfile) -> FlowyResult<()> {
|
|
||||||
let uid = user_profile.uid;
|
|
||||||
self.store.set(&user_profile_key(uid), user_profile).await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn collab_for_user_awareness(
|
|
||||||
&self,
|
|
||||||
uid: i64,
|
|
||||||
object_id: &str,
|
|
||||||
collab_db: Weak<CollabKVDB>,
|
|
||||||
raw_data: Vec<u8>,
|
|
||||||
) -> Result<Arc<MutexCollab>, FlowyError> {
|
|
||||||
let collab_builder = self.collab_builder.upgrade().ok_or(FlowyError::new(
|
|
||||||
ErrorCode::Internal,
|
|
||||||
"Unexpected error: collab builder is not available",
|
|
||||||
))?;
|
|
||||||
let collab = collab_builder
|
|
||||||
.build(
|
|
||||||
uid,
|
|
||||||
object_id,
|
|
||||||
CollabType::UserAwareness,
|
|
||||||
DataSource::DocStateV1(raw_data),
|
|
||||||
collab_db,
|
|
||||||
CollabBuilderConfig::default().sync_enable(true),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.context("Build collab for user awareness failed")?;
|
|
||||||
Ok(collab)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,689 +0,0 @@
|
|||||||
// This file is generated by rust-protobuf 2.28.0. Do not edit
|
|
||||||
// @generated
|
|
||||||
|
|
||||||
// https://github.com/rust-lang/rust-clippy/issues/702
|
|
||||||
#![allow(unknown_lints)]
|
|
||||||
#![allow(clippy::all)]
|
|
||||||
|
|
||||||
#![allow(unused_attributes)]
|
|
||||||
#![cfg_attr(rustfmt, rustfmt::skip)]
|
|
||||||
|
|
||||||
#![allow(box_pointers)]
|
|
||||||
#![allow(dead_code)]
|
|
||||||
#![allow(missing_docs)]
|
|
||||||
#![allow(non_camel_case_types)]
|
|
||||||
#![allow(non_snake_case)]
|
|
||||||
#![allow(non_upper_case_globals)]
|
|
||||||
#![allow(trivial_casts)]
|
|
||||||
#![allow(unused_imports)]
|
|
||||||
#![allow(unused_results)]
|
|
||||||
//! Generated file from `auth.proto`
|
|
||||||
|
|
||||||
/// Generated files are compatible only with the same version
|
|
||||||
/// of protobuf runtime.
|
|
||||||
// const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_2_28_0;
|
|
||||||
|
|
||||||
#[derive(PartialEq,Clone,Default)]
|
|
||||||
pub struct OauthSignInPB {
|
|
||||||
// message fields
|
|
||||||
pub map: ::std::collections::HashMap<::std::string::String, ::std::string::String>,
|
|
||||||
pub authenticator: AuthenticatorPB,
|
|
||||||
// special fields
|
|
||||||
pub unknown_fields: ::protobuf::UnknownFields,
|
|
||||||
pub cached_size: ::protobuf::CachedSize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> ::std::default::Default for &'a OauthSignInPB {
|
|
||||||
fn default() -> &'a OauthSignInPB {
|
|
||||||
<OauthSignInPB as ::protobuf::Message>::default_instance()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl OauthSignInPB {
|
|
||||||
pub fn new() -> OauthSignInPB {
|
|
||||||
::std::default::Default::default()
|
|
||||||
}
|
|
||||||
|
|
||||||
// repeated .OauthSignInPB.MapEntry map = 1;
|
|
||||||
|
|
||||||
|
|
||||||
pub fn get_map(&self) -> &::std::collections::HashMap<::std::string::String, ::std::string::String> {
|
|
||||||
&self.map
|
|
||||||
}
|
|
||||||
pub fn clear_map(&mut self) {
|
|
||||||
self.map.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Param is passed by value, moved
|
|
||||||
pub fn set_map(&mut self, v: ::std::collections::HashMap<::std::string::String, ::std::string::String>) {
|
|
||||||
self.map = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mutable pointer to the field.
|
|
||||||
pub fn mut_map(&mut self) -> &mut ::std::collections::HashMap<::std::string::String, ::std::string::String> {
|
|
||||||
&mut self.map
|
|
||||||
}
|
|
||||||
|
|
||||||
// Take field
|
|
||||||
pub fn take_map(&mut self) -> ::std::collections::HashMap<::std::string::String, ::std::string::String> {
|
|
||||||
::std::mem::replace(&mut self.map, ::std::collections::HashMap::new())
|
|
||||||
}
|
|
||||||
|
|
||||||
// .AuthenticatorPB authenticator = 2;
|
|
||||||
|
|
||||||
|
|
||||||
pub fn get_authenticator(&self) -> AuthenticatorPB {
|
|
||||||
self.authenticator
|
|
||||||
}
|
|
||||||
pub fn clear_authenticator(&mut self) {
|
|
||||||
self.authenticator = AuthenticatorPB::Local;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Param is passed by value, moved
|
|
||||||
pub fn set_authenticator(&mut self, v: AuthenticatorPB) {
|
|
||||||
self.authenticator = v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ::protobuf::Message for OauthSignInPB {
|
|
||||||
fn is_initialized(&self) -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> {
|
|
||||||
while !is.eof()? {
|
|
||||||
let (field_number, wire_type) = is.read_tag_unpack()?;
|
|
||||||
match field_number {
|
|
||||||
1 => {
|
|
||||||
::protobuf::rt::read_map_into::<::protobuf::types::ProtobufTypeString, ::protobuf::types::ProtobufTypeString>(wire_type, is, &mut self.map)?;
|
|
||||||
},
|
|
||||||
2 => {
|
|
||||||
::protobuf::rt::read_proto3_enum_with_unknown_fields_into(wire_type, is, &mut self.authenticator, 2, &mut self.unknown_fields)?
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
::std::result::Result::Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute sizes of nested messages
|
|
||||||
#[allow(unused_variables)]
|
|
||||||
fn compute_size(&self) -> u32 {
|
|
||||||
let mut my_size = 0;
|
|
||||||
my_size += ::protobuf::rt::compute_map_size::<::protobuf::types::ProtobufTypeString, ::protobuf::types::ProtobufTypeString>(1, &self.map);
|
|
||||||
if self.authenticator != AuthenticatorPB::Local {
|
|
||||||
my_size += ::protobuf::rt::enum_size(2, self.authenticator);
|
|
||||||
}
|
|
||||||
my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
|
|
||||||
self.cached_size.set(my_size);
|
|
||||||
my_size
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> {
|
|
||||||
::protobuf::rt::write_map_with_cached_sizes::<::protobuf::types::ProtobufTypeString, ::protobuf::types::ProtobufTypeString>(1, &self.map, os)?;
|
|
||||||
if self.authenticator != AuthenticatorPB::Local {
|
|
||||||
os.write_enum(2, ::protobuf::ProtobufEnum::value(&self.authenticator))?;
|
|
||||||
}
|
|
||||||
os.write_unknown_fields(self.get_unknown_fields())?;
|
|
||||||
::std::result::Result::Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_cached_size(&self) -> u32 {
|
|
||||||
self.cached_size.get()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_unknown_fields(&self) -> &::protobuf::UnknownFields {
|
|
||||||
&self.unknown_fields
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields {
|
|
||||||
&mut self.unknown_fields
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_any(&self) -> &dyn (::std::any::Any) {
|
|
||||||
self as &dyn (::std::any::Any)
|
|
||||||
}
|
|
||||||
fn as_any_mut(&mut self) -> &mut dyn (::std::any::Any) {
|
|
||||||
self as &mut dyn (::std::any::Any)
|
|
||||||
}
|
|
||||||
fn into_any(self: ::std::boxed::Box<Self>) -> ::std::boxed::Box<dyn (::std::any::Any)> {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor {
|
|
||||||
Self::descriptor_static()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new() -> OauthSignInPB {
|
|
||||||
OauthSignInPB::new()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
|
|
||||||
static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT;
|
|
||||||
descriptor.get(|| {
|
|
||||||
let mut fields = ::std::vec::Vec::new();
|
|
||||||
fields.push(::protobuf::reflect::accessor::make_map_accessor::<_, ::protobuf::types::ProtobufTypeString, ::protobuf::types::ProtobufTypeString>(
|
|
||||||
"map",
|
|
||||||
|m: &OauthSignInPB| { &m.map },
|
|
||||||
|m: &mut OauthSignInPB| { &mut m.map },
|
|
||||||
));
|
|
||||||
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeEnum<AuthenticatorPB>>(
|
|
||||||
"authenticator",
|
|
||||||
|m: &OauthSignInPB| { &m.authenticator },
|
|
||||||
|m: &mut OauthSignInPB| { &mut m.authenticator },
|
|
||||||
));
|
|
||||||
::protobuf::reflect::MessageDescriptor::new_pb_name::<OauthSignInPB>(
|
|
||||||
"OauthSignInPB",
|
|
||||||
fields,
|
|
||||||
file_descriptor_proto()
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn default_instance() -> &'static OauthSignInPB {
|
|
||||||
static instance: ::protobuf::rt::LazyV2<OauthSignInPB> = ::protobuf::rt::LazyV2::INIT;
|
|
||||||
instance.get(OauthSignInPB::new)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ::protobuf::Clear for OauthSignInPB {
|
|
||||||
fn clear(&mut self) {
|
|
||||||
self.map.clear();
|
|
||||||
self.authenticator = AuthenticatorPB::Local;
|
|
||||||
self.unknown_fields.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ::std::fmt::Debug for OauthSignInPB {
|
|
||||||
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
|
||||||
::protobuf::text_format::fmt(self, f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ::protobuf::reflect::ProtobufValue for OauthSignInPB {
|
|
||||||
fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
|
|
||||||
::protobuf::reflect::ReflectValueRef::Message(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(PartialEq,Clone,Default)]
|
|
||||||
pub struct AddUserPB {
|
|
||||||
// message fields
|
|
||||||
pub email: ::std::string::String,
|
|
||||||
pub password: ::std::string::String,
|
|
||||||
// special fields
|
|
||||||
pub unknown_fields: ::protobuf::UnknownFields,
|
|
||||||
pub cached_size: ::protobuf::CachedSize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> ::std::default::Default for &'a AddUserPB {
|
|
||||||
fn default() -> &'a AddUserPB {
|
|
||||||
<AddUserPB as ::protobuf::Message>::default_instance()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AddUserPB {
|
|
||||||
pub fn new() -> AddUserPB {
|
|
||||||
::std::default::Default::default()
|
|
||||||
}
|
|
||||||
|
|
||||||
// string email = 1;
|
|
||||||
|
|
||||||
|
|
||||||
pub fn get_email(&self) -> &str {
|
|
||||||
&self.email
|
|
||||||
}
|
|
||||||
pub fn clear_email(&mut self) {
|
|
||||||
self.email.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Param is passed by value, moved
|
|
||||||
pub fn set_email(&mut self, v: ::std::string::String) {
|
|
||||||
self.email = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mutable pointer to the field.
|
|
||||||
// If field is not initialized, it is initialized with default value first.
|
|
||||||
pub fn mut_email(&mut self) -> &mut ::std::string::String {
|
|
||||||
&mut self.email
|
|
||||||
}
|
|
||||||
|
|
||||||
// Take field
|
|
||||||
pub fn take_email(&mut self) -> ::std::string::String {
|
|
||||||
::std::mem::replace(&mut self.email, ::std::string::String::new())
|
|
||||||
}
|
|
||||||
|
|
||||||
// string password = 2;
|
|
||||||
|
|
||||||
|
|
||||||
pub fn get_password(&self) -> &str {
|
|
||||||
&self.password
|
|
||||||
}
|
|
||||||
pub fn clear_password(&mut self) {
|
|
||||||
self.password.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Param is passed by value, moved
|
|
||||||
pub fn set_password(&mut self, v: ::std::string::String) {
|
|
||||||
self.password = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mutable pointer to the field.
|
|
||||||
// If field is not initialized, it is initialized with default value first.
|
|
||||||
pub fn mut_password(&mut self) -> &mut ::std::string::String {
|
|
||||||
&mut self.password
|
|
||||||
}
|
|
||||||
|
|
||||||
// Take field
|
|
||||||
pub fn take_password(&mut self) -> ::std::string::String {
|
|
||||||
::std::mem::replace(&mut self.password, ::std::string::String::new())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ::protobuf::Message for AddUserPB {
|
|
||||||
fn is_initialized(&self) -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> {
|
|
||||||
while !is.eof()? {
|
|
||||||
let (field_number, wire_type) = is.read_tag_unpack()?;
|
|
||||||
match field_number {
|
|
||||||
1 => {
|
|
||||||
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.email)?;
|
|
||||||
},
|
|
||||||
2 => {
|
|
||||||
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.password)?;
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
::std::result::Result::Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute sizes of nested messages
|
|
||||||
#[allow(unused_variables)]
|
|
||||||
fn compute_size(&self) -> u32 {
|
|
||||||
let mut my_size = 0;
|
|
||||||
if !self.email.is_empty() {
|
|
||||||
my_size += ::protobuf::rt::string_size(1, &self.email);
|
|
||||||
}
|
|
||||||
if !self.password.is_empty() {
|
|
||||||
my_size += ::protobuf::rt::string_size(2, &self.password);
|
|
||||||
}
|
|
||||||
my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
|
|
||||||
self.cached_size.set(my_size);
|
|
||||||
my_size
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> {
|
|
||||||
if !self.email.is_empty() {
|
|
||||||
os.write_string(1, &self.email)?;
|
|
||||||
}
|
|
||||||
if !self.password.is_empty() {
|
|
||||||
os.write_string(2, &self.password)?;
|
|
||||||
}
|
|
||||||
os.write_unknown_fields(self.get_unknown_fields())?;
|
|
||||||
::std::result::Result::Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_cached_size(&self) -> u32 {
|
|
||||||
self.cached_size.get()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_unknown_fields(&self) -> &::protobuf::UnknownFields {
|
|
||||||
&self.unknown_fields
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields {
|
|
||||||
&mut self.unknown_fields
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_any(&self) -> &dyn (::std::any::Any) {
|
|
||||||
self as &dyn (::std::any::Any)
|
|
||||||
}
|
|
||||||
fn as_any_mut(&mut self) -> &mut dyn (::std::any::Any) {
|
|
||||||
self as &mut dyn (::std::any::Any)
|
|
||||||
}
|
|
||||||
fn into_any(self: ::std::boxed::Box<Self>) -> ::std::boxed::Box<dyn (::std::any::Any)> {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor {
|
|
||||||
Self::descriptor_static()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new() -> AddUserPB {
|
|
||||||
AddUserPB::new()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
|
|
||||||
static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT;
|
|
||||||
descriptor.get(|| {
|
|
||||||
let mut fields = ::std::vec::Vec::new();
|
|
||||||
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
|
|
||||||
"email",
|
|
||||||
|m: &AddUserPB| { &m.email },
|
|
||||||
|m: &mut AddUserPB| { &mut m.email },
|
|
||||||
));
|
|
||||||
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
|
|
||||||
"password",
|
|
||||||
|m: &AddUserPB| { &m.password },
|
|
||||||
|m: &mut AddUserPB| { &mut m.password },
|
|
||||||
));
|
|
||||||
::protobuf::reflect::MessageDescriptor::new_pb_name::<AddUserPB>(
|
|
||||||
"AddUserPB",
|
|
||||||
fields,
|
|
||||||
file_descriptor_proto()
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn default_instance() -> &'static AddUserPB {
|
|
||||||
static instance: ::protobuf::rt::LazyV2<AddUserPB> = ::protobuf::rt::LazyV2::INIT;
|
|
||||||
instance.get(AddUserPB::new)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ::protobuf::Clear for AddUserPB {
|
|
||||||
fn clear(&mut self) {
|
|
||||||
self.email.clear();
|
|
||||||
self.password.clear();
|
|
||||||
self.unknown_fields.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ::std::fmt::Debug for AddUserPB {
|
|
||||||
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
|
||||||
::protobuf::text_format::fmt(self, f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ::protobuf::reflect::ProtobufValue for AddUserPB {
|
|
||||||
fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
|
|
||||||
::protobuf::reflect::ReflectValueRef::Message(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(PartialEq,Clone,Default)]
|
|
||||||
pub struct UserSignInPB {
|
|
||||||
// message fields
|
|
||||||
pub email: ::std::string::String,
|
|
||||||
pub password: ::std::string::String,
|
|
||||||
// special fields
|
|
||||||
pub unknown_fields: ::protobuf::UnknownFields,
|
|
||||||
pub cached_size: ::protobuf::CachedSize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> ::std::default::Default for &'a UserSignInPB {
|
|
||||||
fn default() -> &'a UserSignInPB {
|
|
||||||
<UserSignInPB as ::protobuf::Message>::default_instance()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UserSignInPB {
|
|
||||||
pub fn new() -> UserSignInPB {
|
|
||||||
::std::default::Default::default()
|
|
||||||
}
|
|
||||||
|
|
||||||
// string email = 1;
|
|
||||||
|
|
||||||
|
|
||||||
pub fn get_email(&self) -> &str {
|
|
||||||
&self.email
|
|
||||||
}
|
|
||||||
pub fn clear_email(&mut self) {
|
|
||||||
self.email.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Param is passed by value, moved
|
|
||||||
pub fn set_email(&mut self, v: ::std::string::String) {
|
|
||||||
self.email = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mutable pointer to the field.
|
|
||||||
// If field is not initialized, it is initialized with default value first.
|
|
||||||
pub fn mut_email(&mut self) -> &mut ::std::string::String {
|
|
||||||
&mut self.email
|
|
||||||
}
|
|
||||||
|
|
||||||
// Take field
|
|
||||||
pub fn take_email(&mut self) -> ::std::string::String {
|
|
||||||
::std::mem::replace(&mut self.email, ::std::string::String::new())
|
|
||||||
}
|
|
||||||
|
|
||||||
// string password = 2;
|
|
||||||
|
|
||||||
|
|
||||||
pub fn get_password(&self) -> &str {
|
|
||||||
&self.password
|
|
||||||
}
|
|
||||||
pub fn clear_password(&mut self) {
|
|
||||||
self.password.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Param is passed by value, moved
|
|
||||||
pub fn set_password(&mut self, v: ::std::string::String) {
|
|
||||||
self.password = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mutable pointer to the field.
|
|
||||||
// If field is not initialized, it is initialized with default value first.
|
|
||||||
pub fn mut_password(&mut self) -> &mut ::std::string::String {
|
|
||||||
&mut self.password
|
|
||||||
}
|
|
||||||
|
|
||||||
// Take field
|
|
||||||
pub fn take_password(&mut self) -> ::std::string::String {
|
|
||||||
::std::mem::replace(&mut self.password, ::std::string::String::new())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ::protobuf::Message for UserSignInPB {
|
|
||||||
fn is_initialized(&self) -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> {
|
|
||||||
while !is.eof()? {
|
|
||||||
let (field_number, wire_type) = is.read_tag_unpack()?;
|
|
||||||
match field_number {
|
|
||||||
1 => {
|
|
||||||
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.email)?;
|
|
||||||
},
|
|
||||||
2 => {
|
|
||||||
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.password)?;
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
::std::result::Result::Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute sizes of nested messages
|
|
||||||
#[allow(unused_variables)]
|
|
||||||
fn compute_size(&self) -> u32 {
|
|
||||||
let mut my_size = 0;
|
|
||||||
if !self.email.is_empty() {
|
|
||||||
my_size += ::protobuf::rt::string_size(1, &self.email);
|
|
||||||
}
|
|
||||||
if !self.password.is_empty() {
|
|
||||||
my_size += ::protobuf::rt::string_size(2, &self.password);
|
|
||||||
}
|
|
||||||
my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
|
|
||||||
self.cached_size.set(my_size);
|
|
||||||
my_size
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> {
|
|
||||||
if !self.email.is_empty() {
|
|
||||||
os.write_string(1, &self.email)?;
|
|
||||||
}
|
|
||||||
if !self.password.is_empty() {
|
|
||||||
os.write_string(2, &self.password)?;
|
|
||||||
}
|
|
||||||
os.write_unknown_fields(self.get_unknown_fields())?;
|
|
||||||
::std::result::Result::Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_cached_size(&self) -> u32 {
|
|
||||||
self.cached_size.get()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_unknown_fields(&self) -> &::protobuf::UnknownFields {
|
|
||||||
&self.unknown_fields
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields {
|
|
||||||
&mut self.unknown_fields
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_any(&self) -> &dyn (::std::any::Any) {
|
|
||||||
self as &dyn (::std::any::Any)
|
|
||||||
}
|
|
||||||
fn as_any_mut(&mut self) -> &mut dyn (::std::any::Any) {
|
|
||||||
self as &mut dyn (::std::any::Any)
|
|
||||||
}
|
|
||||||
fn into_any(self: ::std::boxed::Box<Self>) -> ::std::boxed::Box<dyn (::std::any::Any)> {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor {
|
|
||||||
Self::descriptor_static()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new() -> UserSignInPB {
|
|
||||||
UserSignInPB::new()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
|
|
||||||
static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT;
|
|
||||||
descriptor.get(|| {
|
|
||||||
let mut fields = ::std::vec::Vec::new();
|
|
||||||
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
|
|
||||||
"email",
|
|
||||||
|m: &UserSignInPB| { &m.email },
|
|
||||||
|m: &mut UserSignInPB| { &mut m.email },
|
|
||||||
));
|
|
||||||
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
|
|
||||||
"password",
|
|
||||||
|m: &UserSignInPB| { &m.password },
|
|
||||||
|m: &mut UserSignInPB| { &mut m.password },
|
|
||||||
));
|
|
||||||
::protobuf::reflect::MessageDescriptor::new_pb_name::<UserSignInPB>(
|
|
||||||
"UserSignInPB",
|
|
||||||
fields,
|
|
||||||
file_descriptor_proto()
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn default_instance() -> &'static UserSignInPB {
|
|
||||||
static instance: ::protobuf::rt::LazyV2<UserSignInPB> = ::protobuf::rt::LazyV2::INIT;
|
|
||||||
instance.get(UserSignInPB::new)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ::protobuf::Clear for UserSignInPB {
|
|
||||||
fn clear(&mut self) {
|
|
||||||
self.email.clear();
|
|
||||||
self.password.clear();
|
|
||||||
self.unknown_fields.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ::std::fmt::Debug for UserSignInPB {
|
|
||||||
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
|
||||||
::protobuf::text_format::fmt(self, f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ::protobuf::reflect::ProtobufValue for UserSignInPB {
|
|
||||||
fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
|
|
||||||
::protobuf::reflect::ReflectValueRef::Message(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone,PartialEq,Eq,Debug,Hash)]
|
|
||||||
pub enum AuthenticatorPB {
|
|
||||||
Local = 0,
|
|
||||||
Supabase = 1,
|
|
||||||
AppFlowyCloud = 2,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ::protobuf::ProtobufEnum for AuthenticatorPB {
|
|
||||||
fn value(&self) -> i32 {
|
|
||||||
*self as i32
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_i32(value: i32) -> ::std::option::Option<AuthenticatorPB> {
|
|
||||||
match value {
|
|
||||||
0 => ::std::option::Option::Some(AuthenticatorPB::Local),
|
|
||||||
1 => ::std::option::Option::Some(AuthenticatorPB::Supabase),
|
|
||||||
2 => ::std::option::Option::Some(AuthenticatorPB::AppFlowyCloud),
|
|
||||||
_ => ::std::option::Option::None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn values() -> &'static [Self] {
|
|
||||||
static values: &'static [AuthenticatorPB] = &[
|
|
||||||
AuthenticatorPB::Local,
|
|
||||||
AuthenticatorPB::Supabase,
|
|
||||||
AuthenticatorPB::AppFlowyCloud,
|
|
||||||
];
|
|
||||||
values
|
|
||||||
}
|
|
||||||
|
|
||||||
fn enum_descriptor_static() -> &'static ::protobuf::reflect::EnumDescriptor {
|
|
||||||
static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::EnumDescriptor> = ::protobuf::rt::LazyV2::INIT;
|
|
||||||
descriptor.get(|| {
|
|
||||||
::protobuf::reflect::EnumDescriptor::new_pb_name::<AuthenticatorPB>("AuthenticatorPB", file_descriptor_proto())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ::std::marker::Copy for AuthenticatorPB {
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ::std::default::Default for AuthenticatorPB {
|
|
||||||
fn default() -> Self {
|
|
||||||
AuthenticatorPB::Local
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ::protobuf::reflect::ProtobufValue for AuthenticatorPB {
|
|
||||||
fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
|
|
||||||
::protobuf::reflect::ReflectValueRef::Enum(::protobuf::ProtobufEnum::descriptor(self))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static file_descriptor_proto_data: &'static [u8] = b"\
|
|
||||||
\n\nauth.proto\"\xaa\x01\n\rOauthSignInPB\x12)\n\x03map\x18\x01\x20\x03(\
|
|
||||||
\x0b2\x17.OauthSignInPB.MapEntryR\x03map\x126\n\rauthenticator\x18\x02\
|
|
||||||
\x20\x01(\x0e2\x10.AuthenticatorPBR\rauthenticator\x1a6\n\x08MapEntry\
|
|
||||||
\x12\x10\n\x03key\x18\x01\x20\x01(\tR\x03key\x12\x14\n\x05value\x18\x02\
|
|
||||||
\x20\x01(\tR\x05value:\x028\x01\"=\n\tAddUserPB\x12\x14\n\x05email\x18\
|
|
||||||
\x01\x20\x01(\tR\x05email\x12\x1a\n\x08password\x18\x02\x20\x01(\tR\x08p\
|
|
||||||
assword\"@\n\x0cUserSignInPB\x12\x14\n\x05email\x18\x01\x20\x01(\tR\x05e\
|
|
||||||
mail\x12\x1a\n\x08password\x18\x02\x20\x01(\tR\x08password*=\n\x0fAuthen\
|
|
||||||
ticatorPB\x12\t\n\x05Local\x10\0\x12\x0c\n\x08Supabase\x10\x01\x12\x11\n\
|
|
||||||
\rAppFlowyCloud\x10\x02b\x06proto3\
|
|
||||||
";
|
|
||||||
|
|
||||||
static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;
|
|
||||||
|
|
||||||
fn parse_descriptor_proto() -> ::protobuf::descriptor::FileDescriptorProto {
|
|
||||||
::protobuf::Message::parse_from_bytes(file_descriptor_proto_data).unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn file_descriptor_proto() -> &'static ::protobuf::descriptor::FileDescriptorProto {
|
|
||||||
file_descriptor_proto_lazy.get(|| {
|
|
||||||
parse_descriptor_proto()
|
|
||||||
})
|
|
||||||
}
|
|
@ -1,95 +0,0 @@
|
|||||||
// This file is generated by rust-protobuf 2.28.0. Do not edit
|
|
||||||
// @generated
|
|
||||||
|
|
||||||
// https://github.com/rust-lang/rust-clippy/issues/702
|
|
||||||
#![allow(unknown_lints)]
|
|
||||||
#![allow(clippy::all)]
|
|
||||||
|
|
||||||
#![allow(unused_attributes)]
|
|
||||||
#![cfg_attr(rustfmt, rustfmt::skip)]
|
|
||||||
|
|
||||||
#![allow(box_pointers)]
|
|
||||||
#![allow(dead_code)]
|
|
||||||
#![allow(missing_docs)]
|
|
||||||
#![allow(non_camel_case_types)]
|
|
||||||
#![allow(non_snake_case)]
|
|
||||||
#![allow(non_upper_case_globals)]
|
|
||||||
#![allow(trivial_casts)]
|
|
||||||
#![allow(unused_imports)]
|
|
||||||
#![allow(unused_results)]
|
|
||||||
//! Generated file from `event_map.proto`
|
|
||||||
|
|
||||||
/// Generated files are compatible only with the same version
|
|
||||||
/// of protobuf runtime.
|
|
||||||
// const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_2_28_0;
|
|
||||||
|
|
||||||
#[derive(Clone,PartialEq,Eq,Debug,Hash)]
|
|
||||||
pub enum UserWasmEvent {
|
|
||||||
OauthSignIn = 0,
|
|
||||||
AddUser = 1,
|
|
||||||
SignInPassword = 2,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ::protobuf::ProtobufEnum for UserWasmEvent {
|
|
||||||
fn value(&self) -> i32 {
|
|
||||||
*self as i32
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_i32(value: i32) -> ::std::option::Option<UserWasmEvent> {
|
|
||||||
match value {
|
|
||||||
0 => ::std::option::Option::Some(UserWasmEvent::OauthSignIn),
|
|
||||||
1 => ::std::option::Option::Some(UserWasmEvent::AddUser),
|
|
||||||
2 => ::std::option::Option::Some(UserWasmEvent::SignInPassword),
|
|
||||||
_ => ::std::option::Option::None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn values() -> &'static [Self] {
|
|
||||||
static values: &'static [UserWasmEvent] = &[
|
|
||||||
UserWasmEvent::OauthSignIn,
|
|
||||||
UserWasmEvent::AddUser,
|
|
||||||
UserWasmEvent::SignInPassword,
|
|
||||||
];
|
|
||||||
values
|
|
||||||
}
|
|
||||||
|
|
||||||
fn enum_descriptor_static() -> &'static ::protobuf::reflect::EnumDescriptor {
|
|
||||||
static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::EnumDescriptor> = ::protobuf::rt::LazyV2::INIT;
|
|
||||||
descriptor.get(|| {
|
|
||||||
::protobuf::reflect::EnumDescriptor::new_pb_name::<UserWasmEvent>("UserWasmEvent", file_descriptor_proto())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ::std::marker::Copy for UserWasmEvent {
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ::std::default::Default for UserWasmEvent {
|
|
||||||
fn default() -> Self {
|
|
||||||
UserWasmEvent::OauthSignIn
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ::protobuf::reflect::ProtobufValue for UserWasmEvent {
|
|
||||||
fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
|
|
||||||
::protobuf::reflect::ReflectValueRef::Enum(::protobuf::ProtobufEnum::descriptor(self))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static file_descriptor_proto_data: &'static [u8] = b"\
|
|
||||||
\n\x0fevent_map.proto*A\n\rUserWasmEvent\x12\x0f\n\x0bOauthSignIn\x10\0\
|
|
||||||
\x12\x0b\n\x07AddUser\x10\x01\x12\x12\n\x0eSignInPassword\x10\x02b\x06pr\
|
|
||||||
oto3\
|
|
||||||
";
|
|
||||||
|
|
||||||
static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;
|
|
||||||
|
|
||||||
fn parse_descriptor_proto() -> ::protobuf::descriptor::FileDescriptorProto {
|
|
||||||
::protobuf::Message::parse_from_bytes(file_descriptor_proto_data).unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn file_descriptor_proto() -> &'static ::protobuf::descriptor::FileDescriptorProto {
|
|
||||||
file_descriptor_proto_lazy.get(|| {
|
|
||||||
parse_descriptor_proto()
|
|
||||||
})
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
#![cfg_attr(rustfmt, rustfmt::skip)]
|
|
||||||
#![allow(ambiguous_glob_reexports)]
|
|
||||||
// Auto-generated, do not edit
|
|
||||||
|
|
||||||
mod event_map;
|
|
||||||
pub use event_map::*;
|
|
||||||
|
|
||||||
mod auth;
|
|
||||||
pub use auth::*;
|
|
||||||
|
|
||||||
mod user;
|
|
||||||
pub use user::*;
|
|
@ -1,661 +0,0 @@
|
|||||||
// This file is generated by rust-protobuf 2.28.0. Do not edit
|
|
||||||
// @generated
|
|
||||||
|
|
||||||
// https://github.com/rust-lang/rust-clippy/issues/702
|
|
||||||
#![allow(unknown_lints)]
|
|
||||||
#![allow(clippy::all)]
|
|
||||||
|
|
||||||
#![allow(unused_attributes)]
|
|
||||||
#![cfg_attr(rustfmt, rustfmt::skip)]
|
|
||||||
|
|
||||||
#![allow(box_pointers)]
|
|
||||||
#![allow(dead_code)]
|
|
||||||
#![allow(missing_docs)]
|
|
||||||
#![allow(non_camel_case_types)]
|
|
||||||
#![allow(non_snake_case)]
|
|
||||||
#![allow(non_upper_case_globals)]
|
|
||||||
#![allow(trivial_casts)]
|
|
||||||
#![allow(unused_imports)]
|
|
||||||
#![allow(unused_results)]
|
|
||||||
//! Generated file from `user.proto`
|
|
||||||
|
|
||||||
/// Generated files are compatible only with the same version
|
|
||||||
/// of protobuf runtime.
|
|
||||||
// const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_2_28_0;
|
|
||||||
|
|
||||||
#[derive(PartialEq,Clone,Default)]
|
|
||||||
pub struct UserProfilePB {
|
|
||||||
// message fields
|
|
||||||
pub id: i64,
|
|
||||||
pub email: ::std::string::String,
|
|
||||||
pub name: ::std::string::String,
|
|
||||||
pub token: ::std::string::String,
|
|
||||||
pub icon_url: ::std::string::String,
|
|
||||||
pub openai_key: ::std::string::String,
|
|
||||||
pub authenticator: super::auth::AuthenticatorPB,
|
|
||||||
pub encryption_sign: ::std::string::String,
|
|
||||||
pub workspace_id: ::std::string::String,
|
|
||||||
pub stability_ai_key: ::std::string::String,
|
|
||||||
pub ai_model: ::std::string::String,
|
|
||||||
// special fields
|
|
||||||
pub unknown_fields: ::protobuf::UnknownFields,
|
|
||||||
pub cached_size: ::protobuf::CachedSize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> ::std::default::Default for &'a UserProfilePB {
|
|
||||||
fn default() -> &'a UserProfilePB {
|
|
||||||
<UserProfilePB as ::protobuf::Message>::default_instance()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UserProfilePB {
|
|
||||||
pub fn new() -> UserProfilePB {
|
|
||||||
::std::default::Default::default()
|
|
||||||
}
|
|
||||||
|
|
||||||
// int64 id = 1;
|
|
||||||
|
|
||||||
|
|
||||||
pub fn get_id(&self) -> i64 {
|
|
||||||
self.id
|
|
||||||
}
|
|
||||||
pub fn clear_id(&mut self) {
|
|
||||||
self.id = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Param is passed by value, moved
|
|
||||||
pub fn set_id(&mut self, v: i64) {
|
|
||||||
self.id = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
// string email = 2;
|
|
||||||
|
|
||||||
|
|
||||||
pub fn get_email(&self) -> &str {
|
|
||||||
&self.email
|
|
||||||
}
|
|
||||||
pub fn clear_email(&mut self) {
|
|
||||||
self.email.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Param is passed by value, moved
|
|
||||||
pub fn set_email(&mut self, v: ::std::string::String) {
|
|
||||||
self.email = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mutable pointer to the field.
|
|
||||||
// If field is not initialized, it is initialized with default value first.
|
|
||||||
pub fn mut_email(&mut self) -> &mut ::std::string::String {
|
|
||||||
&mut self.email
|
|
||||||
}
|
|
||||||
|
|
||||||
// Take field
|
|
||||||
pub fn take_email(&mut self) -> ::std::string::String {
|
|
||||||
::std::mem::replace(&mut self.email, ::std::string::String::new())
|
|
||||||
}
|
|
||||||
|
|
||||||
// string name = 3;
|
|
||||||
|
|
||||||
|
|
||||||
pub fn get_name(&self) -> &str {
|
|
||||||
&self.name
|
|
||||||
}
|
|
||||||
pub fn clear_name(&mut self) {
|
|
||||||
self.name.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Param is passed by value, moved
|
|
||||||
pub fn set_name(&mut self, v: ::std::string::String) {
|
|
||||||
self.name = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mutable pointer to the field.
|
|
||||||
// If field is not initialized, it is initialized with default value first.
|
|
||||||
pub fn mut_name(&mut self) -> &mut ::std::string::String {
|
|
||||||
&mut self.name
|
|
||||||
}
|
|
||||||
|
|
||||||
// Take field
|
|
||||||
pub fn take_name(&mut self) -> ::std::string::String {
|
|
||||||
::std::mem::replace(&mut self.name, ::std::string::String::new())
|
|
||||||
}
|
|
||||||
|
|
||||||
// string token = 4;
|
|
||||||
|
|
||||||
|
|
||||||
pub fn get_token(&self) -> &str {
|
|
||||||
&self.token
|
|
||||||
}
|
|
||||||
pub fn clear_token(&mut self) {
|
|
||||||
self.token.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Param is passed by value, moved
|
|
||||||
pub fn set_token(&mut self, v: ::std::string::String) {
|
|
||||||
self.token = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mutable pointer to the field.
|
|
||||||
// If field is not initialized, it is initialized with default value first.
|
|
||||||
pub fn mut_token(&mut self) -> &mut ::std::string::String {
|
|
||||||
&mut self.token
|
|
||||||
}
|
|
||||||
|
|
||||||
// Take field
|
|
||||||
pub fn take_token(&mut self) -> ::std::string::String {
|
|
||||||
::std::mem::replace(&mut self.token, ::std::string::String::new())
|
|
||||||
}
|
|
||||||
|
|
||||||
// string icon_url = 5;
|
|
||||||
|
|
||||||
|
|
||||||
pub fn get_icon_url(&self) -> &str {
|
|
||||||
&self.icon_url
|
|
||||||
}
|
|
||||||
pub fn clear_icon_url(&mut self) {
|
|
||||||
self.icon_url.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Param is passed by value, moved
|
|
||||||
pub fn set_icon_url(&mut self, v: ::std::string::String) {
|
|
||||||
self.icon_url = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mutable pointer to the field.
|
|
||||||
// If field is not initialized, it is initialized with default value first.
|
|
||||||
pub fn mut_icon_url(&mut self) -> &mut ::std::string::String {
|
|
||||||
&mut self.icon_url
|
|
||||||
}
|
|
||||||
|
|
||||||
// Take field
|
|
||||||
pub fn take_icon_url(&mut self) -> ::std::string::String {
|
|
||||||
::std::mem::replace(&mut self.icon_url, ::std::string::String::new())
|
|
||||||
}
|
|
||||||
|
|
||||||
// string openai_key = 6;
|
|
||||||
|
|
||||||
|
|
||||||
pub fn get_openai_key(&self) -> &str {
|
|
||||||
&self.openai_key
|
|
||||||
}
|
|
||||||
pub fn clear_openai_key(&mut self) {
|
|
||||||
self.openai_key.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Param is passed by value, moved
|
|
||||||
pub fn set_openai_key(&mut self, v: ::std::string::String) {
|
|
||||||
self.openai_key = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mutable pointer to the field.
|
|
||||||
// If field is not initialized, it is initialized with default value first.
|
|
||||||
pub fn mut_openai_key(&mut self) -> &mut ::std::string::String {
|
|
||||||
&mut self.openai_key
|
|
||||||
}
|
|
||||||
|
|
||||||
// Take field
|
|
||||||
pub fn take_openai_key(&mut self) -> ::std::string::String {
|
|
||||||
::std::mem::replace(&mut self.openai_key, ::std::string::String::new())
|
|
||||||
}
|
|
||||||
|
|
||||||
// .AuthenticatorPB authenticator = 7;
|
|
||||||
|
|
||||||
|
|
||||||
pub fn get_authenticator(&self) -> super::auth::AuthenticatorPB {
|
|
||||||
self.authenticator
|
|
||||||
}
|
|
||||||
pub fn clear_authenticator(&mut self) {
|
|
||||||
self.authenticator = super::auth::AuthenticatorPB::Local;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Param is passed by value, moved
|
|
||||||
pub fn set_authenticator(&mut self, v: super::auth::AuthenticatorPB) {
|
|
||||||
self.authenticator = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
// string encryption_sign = 8;
|
|
||||||
|
|
||||||
|
|
||||||
pub fn get_encryption_sign(&self) -> &str {
|
|
||||||
&self.encryption_sign
|
|
||||||
}
|
|
||||||
pub fn clear_encryption_sign(&mut self) {
|
|
||||||
self.encryption_sign.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Param is passed by value, moved
|
|
||||||
pub fn set_encryption_sign(&mut self, v: ::std::string::String) {
|
|
||||||
self.encryption_sign = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mutable pointer to the field.
|
|
||||||
// If field is not initialized, it is initialized with default value first.
|
|
||||||
pub fn mut_encryption_sign(&mut self) -> &mut ::std::string::String {
|
|
||||||
&mut self.encryption_sign
|
|
||||||
}
|
|
||||||
|
|
||||||
// Take field
|
|
||||||
pub fn take_encryption_sign(&mut self) -> ::std::string::String {
|
|
||||||
::std::mem::replace(&mut self.encryption_sign, ::std::string::String::new())
|
|
||||||
}
|
|
||||||
|
|
||||||
// string workspace_id = 9;
|
|
||||||
|
|
||||||
|
|
||||||
pub fn get_workspace_id(&self) -> &str {
|
|
||||||
&self.workspace_id
|
|
||||||
}
|
|
||||||
pub fn clear_workspace_id(&mut self) {
|
|
||||||
self.workspace_id.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Param is passed by value, moved
|
|
||||||
pub fn set_workspace_id(&mut self, v: ::std::string::String) {
|
|
||||||
self.workspace_id = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mutable pointer to the field.
|
|
||||||
// If field is not initialized, it is initialized with default value first.
|
|
||||||
pub fn mut_workspace_id(&mut self) -> &mut ::std::string::String {
|
|
||||||
&mut self.workspace_id
|
|
||||||
}
|
|
||||||
|
|
||||||
// Take field
|
|
||||||
pub fn take_workspace_id(&mut self) -> ::std::string::String {
|
|
||||||
::std::mem::replace(&mut self.workspace_id, ::std::string::String::new())
|
|
||||||
}
|
|
||||||
|
|
||||||
// string stability_ai_key = 10;
|
|
||||||
|
|
||||||
|
|
||||||
pub fn get_stability_ai_key(&self) -> &str {
|
|
||||||
&self.stability_ai_key
|
|
||||||
}
|
|
||||||
pub fn clear_stability_ai_key(&mut self) {
|
|
||||||
self.stability_ai_key.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Param is passed by value, moved
|
|
||||||
pub fn set_stability_ai_key(&mut self, v: ::std::string::String) {
|
|
||||||
self.stability_ai_key = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mutable pointer to the field.
|
|
||||||
// If field is not initialized, it is initialized with default value first.
|
|
||||||
pub fn mut_stability_ai_key(&mut self) -> &mut ::std::string::String {
|
|
||||||
&mut self.stability_ai_key
|
|
||||||
}
|
|
||||||
|
|
||||||
// Take field
|
|
||||||
pub fn take_stability_ai_key(&mut self) -> ::std::string::String {
|
|
||||||
::std::mem::replace(&mut self.stability_ai_key, ::std::string::String::new())
|
|
||||||
}
|
|
||||||
|
|
||||||
// string ai_model = 11;
|
|
||||||
|
|
||||||
|
|
||||||
pub fn get_ai_model(&self) -> &str {
|
|
||||||
&self.ai_model
|
|
||||||
}
|
|
||||||
pub fn clear_ai_model(&mut self) {
|
|
||||||
self.ai_model.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Param is passed by value, moved
|
|
||||||
pub fn set_ai_model(&mut self, v: ::std::string::String) {
|
|
||||||
self.ai_model = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mutable pointer to the field.
|
|
||||||
// If field is not initialized, it is initialized with default value first.
|
|
||||||
pub fn mut_ai_model(&mut self) -> &mut ::std::string::String {
|
|
||||||
&mut self.ai_model
|
|
||||||
}
|
|
||||||
|
|
||||||
// Take field
|
|
||||||
pub fn take_ai_model(&mut self) -> ::std::string::String {
|
|
||||||
::std::mem::replace(&mut self.ai_model, ::std::string::String::new())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ::protobuf::Message for UserProfilePB {
|
|
||||||
fn is_initialized(&self) -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> {
|
|
||||||
while !is.eof()? {
|
|
||||||
let (field_number, wire_type) = is.read_tag_unpack()?;
|
|
||||||
match field_number {
|
|
||||||
1 => {
|
|
||||||
if wire_type != ::protobuf::wire_format::WireTypeVarint {
|
|
||||||
return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type));
|
|
||||||
}
|
|
||||||
let tmp = is.read_int64()?;
|
|
||||||
self.id = tmp;
|
|
||||||
},
|
|
||||||
2 => {
|
|
||||||
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.email)?;
|
|
||||||
},
|
|
||||||
3 => {
|
|
||||||
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.name)?;
|
|
||||||
},
|
|
||||||
4 => {
|
|
||||||
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.token)?;
|
|
||||||
},
|
|
||||||
5 => {
|
|
||||||
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.icon_url)?;
|
|
||||||
},
|
|
||||||
6 => {
|
|
||||||
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.openai_key)?;
|
|
||||||
},
|
|
||||||
7 => {
|
|
||||||
::protobuf::rt::read_proto3_enum_with_unknown_fields_into(wire_type, is, &mut self.authenticator, 7, &mut self.unknown_fields)?
|
|
||||||
},
|
|
||||||
8 => {
|
|
||||||
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.encryption_sign)?;
|
|
||||||
},
|
|
||||||
9 => {
|
|
||||||
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.workspace_id)?;
|
|
||||||
},
|
|
||||||
10 => {
|
|
||||||
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.stability_ai_key)?;
|
|
||||||
},
|
|
||||||
11 => {
|
|
||||||
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.ai_model)?;
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
::std::result::Result::Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute sizes of nested messages
|
|
||||||
#[allow(unused_variables)]
|
|
||||||
fn compute_size(&self) -> u32 {
|
|
||||||
let mut my_size = 0;
|
|
||||||
if self.id != 0 {
|
|
||||||
my_size += ::protobuf::rt::value_size(1, self.id, ::protobuf::wire_format::WireTypeVarint);
|
|
||||||
}
|
|
||||||
if !self.email.is_empty() {
|
|
||||||
my_size += ::protobuf::rt::string_size(2, &self.email);
|
|
||||||
}
|
|
||||||
if !self.name.is_empty() {
|
|
||||||
my_size += ::protobuf::rt::string_size(3, &self.name);
|
|
||||||
}
|
|
||||||
if !self.token.is_empty() {
|
|
||||||
my_size += ::protobuf::rt::string_size(4, &self.token);
|
|
||||||
}
|
|
||||||
if !self.icon_url.is_empty() {
|
|
||||||
my_size += ::protobuf::rt::string_size(5, &self.icon_url);
|
|
||||||
}
|
|
||||||
if !self.openai_key.is_empty() {
|
|
||||||
my_size += ::protobuf::rt::string_size(6, &self.openai_key);
|
|
||||||
}
|
|
||||||
if self.authenticator != super::auth::AuthenticatorPB::Local {
|
|
||||||
my_size += ::protobuf::rt::enum_size(7, self.authenticator);
|
|
||||||
}
|
|
||||||
if !self.encryption_sign.is_empty() {
|
|
||||||
my_size += ::protobuf::rt::string_size(8, &self.encryption_sign);
|
|
||||||
}
|
|
||||||
if !self.workspace_id.is_empty() {
|
|
||||||
my_size += ::protobuf::rt::string_size(9, &self.workspace_id);
|
|
||||||
}
|
|
||||||
if !self.stability_ai_key.is_empty() {
|
|
||||||
my_size += ::protobuf::rt::string_size(10, &self.stability_ai_key);
|
|
||||||
}
|
|
||||||
if !self.ai_model.is_empty() {
|
|
||||||
my_size += ::protobuf::rt::string_size(11, &self.ai_model);
|
|
||||||
}
|
|
||||||
my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
|
|
||||||
self.cached_size.set(my_size);
|
|
||||||
my_size
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> {
|
|
||||||
if self.id != 0 {
|
|
||||||
os.write_int64(1, self.id)?;
|
|
||||||
}
|
|
||||||
if !self.email.is_empty() {
|
|
||||||
os.write_string(2, &self.email)?;
|
|
||||||
}
|
|
||||||
if !self.name.is_empty() {
|
|
||||||
os.write_string(3, &self.name)?;
|
|
||||||
}
|
|
||||||
if !self.token.is_empty() {
|
|
||||||
os.write_string(4, &self.token)?;
|
|
||||||
}
|
|
||||||
if !self.icon_url.is_empty() {
|
|
||||||
os.write_string(5, &self.icon_url)?;
|
|
||||||
}
|
|
||||||
if !self.openai_key.is_empty() {
|
|
||||||
os.write_string(6, &self.openai_key)?;
|
|
||||||
}
|
|
||||||
if self.authenticator != super::auth::AuthenticatorPB::Local {
|
|
||||||
os.write_enum(7, ::protobuf::ProtobufEnum::value(&self.authenticator))?;
|
|
||||||
}
|
|
||||||
if !self.encryption_sign.is_empty() {
|
|
||||||
os.write_string(8, &self.encryption_sign)?;
|
|
||||||
}
|
|
||||||
if !self.workspace_id.is_empty() {
|
|
||||||
os.write_string(9, &self.workspace_id)?;
|
|
||||||
}
|
|
||||||
if !self.stability_ai_key.is_empty() {
|
|
||||||
os.write_string(10, &self.stability_ai_key)?;
|
|
||||||
}
|
|
||||||
if !self.ai_model.is_empty() {
|
|
||||||
os.write_string(11, &self.ai_model)?;
|
|
||||||
}
|
|
||||||
os.write_unknown_fields(self.get_unknown_fields())?;
|
|
||||||
::std::result::Result::Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_cached_size(&self) -> u32 {
|
|
||||||
self.cached_size.get()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_unknown_fields(&self) -> &::protobuf::UnknownFields {
|
|
||||||
&self.unknown_fields
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields {
|
|
||||||
&mut self.unknown_fields
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_any(&self) -> &dyn (::std::any::Any) {
|
|
||||||
self as &dyn (::std::any::Any)
|
|
||||||
}
|
|
||||||
fn as_any_mut(&mut self) -> &mut dyn (::std::any::Any) {
|
|
||||||
self as &mut dyn (::std::any::Any)
|
|
||||||
}
|
|
||||||
fn into_any(self: ::std::boxed::Box<Self>) -> ::std::boxed::Box<dyn (::std::any::Any)> {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor {
|
|
||||||
Self::descriptor_static()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new() -> UserProfilePB {
|
|
||||||
UserProfilePB::new()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
|
|
||||||
static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT;
|
|
||||||
descriptor.get(|| {
|
|
||||||
let mut fields = ::std::vec::Vec::new();
|
|
||||||
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeInt64>(
|
|
||||||
"id",
|
|
||||||
|m: &UserProfilePB| { &m.id },
|
|
||||||
|m: &mut UserProfilePB| { &mut m.id },
|
|
||||||
));
|
|
||||||
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
|
|
||||||
"email",
|
|
||||||
|m: &UserProfilePB| { &m.email },
|
|
||||||
|m: &mut UserProfilePB| { &mut m.email },
|
|
||||||
));
|
|
||||||
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
|
|
||||||
"name",
|
|
||||||
|m: &UserProfilePB| { &m.name },
|
|
||||||
|m: &mut UserProfilePB| { &mut m.name },
|
|
||||||
));
|
|
||||||
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
|
|
||||||
"token",
|
|
||||||
|m: &UserProfilePB| { &m.token },
|
|
||||||
|m: &mut UserProfilePB| { &mut m.token },
|
|
||||||
));
|
|
||||||
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
|
|
||||||
"icon_url",
|
|
||||||
|m: &UserProfilePB| { &m.icon_url },
|
|
||||||
|m: &mut UserProfilePB| { &mut m.icon_url },
|
|
||||||
));
|
|
||||||
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
|
|
||||||
"openai_key",
|
|
||||||
|m: &UserProfilePB| { &m.openai_key },
|
|
||||||
|m: &mut UserProfilePB| { &mut m.openai_key },
|
|
||||||
));
|
|
||||||
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeEnum<super::auth::AuthenticatorPB>>(
|
|
||||||
"authenticator",
|
|
||||||
|m: &UserProfilePB| { &m.authenticator },
|
|
||||||
|m: &mut UserProfilePB| { &mut m.authenticator },
|
|
||||||
));
|
|
||||||
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
|
|
||||||
"encryption_sign",
|
|
||||||
|m: &UserProfilePB| { &m.encryption_sign },
|
|
||||||
|m: &mut UserProfilePB| { &mut m.encryption_sign },
|
|
||||||
));
|
|
||||||
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
|
|
||||||
"workspace_id",
|
|
||||||
|m: &UserProfilePB| { &m.workspace_id },
|
|
||||||
|m: &mut UserProfilePB| { &mut m.workspace_id },
|
|
||||||
));
|
|
||||||
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
|
|
||||||
"stability_ai_key",
|
|
||||||
|m: &UserProfilePB| { &m.stability_ai_key },
|
|
||||||
|m: &mut UserProfilePB| { &mut m.stability_ai_key },
|
|
||||||
));
|
|
||||||
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
|
|
||||||
"ai_model",
|
|
||||||
|m: &UserProfilePB| { &m.ai_model },
|
|
||||||
|m: &mut UserProfilePB| { &mut m.ai_model },
|
|
||||||
));
|
|
||||||
::protobuf::reflect::MessageDescriptor::new_pb_name::<UserProfilePB>(
|
|
||||||
"UserProfilePB",
|
|
||||||
fields,
|
|
||||||
file_descriptor_proto()
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn default_instance() -> &'static UserProfilePB {
|
|
||||||
static instance: ::protobuf::rt::LazyV2<UserProfilePB> = ::protobuf::rt::LazyV2::INIT;
|
|
||||||
instance.get(UserProfilePB::new)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ::protobuf::Clear for UserProfilePB {
|
|
||||||
fn clear(&mut self) {
|
|
||||||
self.id = 0;
|
|
||||||
self.email.clear();
|
|
||||||
self.name.clear();
|
|
||||||
self.token.clear();
|
|
||||||
self.icon_url.clear();
|
|
||||||
self.openai_key.clear();
|
|
||||||
self.authenticator = super::auth::AuthenticatorPB::Local;
|
|
||||||
self.encryption_sign.clear();
|
|
||||||
self.workspace_id.clear();
|
|
||||||
self.stability_ai_key.clear();
|
|
||||||
self.ai_model.clear();
|
|
||||||
self.unknown_fields.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ::std::fmt::Debug for UserProfilePB {
|
|
||||||
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
|
||||||
::protobuf::text_format::fmt(self, f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ::protobuf::reflect::ProtobufValue for UserProfilePB {
|
|
||||||
fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
|
|
||||||
::protobuf::reflect::ReflectValueRef::Message(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone,PartialEq,Eq,Debug,Hash)]
|
|
||||||
pub enum EncryptionTypePB {
|
|
||||||
NoEncryption = 0,
|
|
||||||
Symmetric = 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ::protobuf::ProtobufEnum for EncryptionTypePB {
|
|
||||||
fn value(&self) -> i32 {
|
|
||||||
*self as i32
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_i32(value: i32) -> ::std::option::Option<EncryptionTypePB> {
|
|
||||||
match value {
|
|
||||||
0 => ::std::option::Option::Some(EncryptionTypePB::NoEncryption),
|
|
||||||
1 => ::std::option::Option::Some(EncryptionTypePB::Symmetric),
|
|
||||||
_ => ::std::option::Option::None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn values() -> &'static [Self] {
|
|
||||||
static values: &'static [EncryptionTypePB] = &[
|
|
||||||
EncryptionTypePB::NoEncryption,
|
|
||||||
EncryptionTypePB::Symmetric,
|
|
||||||
];
|
|
||||||
values
|
|
||||||
}
|
|
||||||
|
|
||||||
fn enum_descriptor_static() -> &'static ::protobuf::reflect::EnumDescriptor {
|
|
||||||
static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::EnumDescriptor> = ::protobuf::rt::LazyV2::INIT;
|
|
||||||
descriptor.get(|| {
|
|
||||||
::protobuf::reflect::EnumDescriptor::new_pb_name::<EncryptionTypePB>("EncryptionTypePB", file_descriptor_proto())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ::std::marker::Copy for EncryptionTypePB {
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ::std::default::Default for EncryptionTypePB {
|
|
||||||
fn default() -> Self {
|
|
||||||
EncryptionTypePB::NoEncryption
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ::protobuf::reflect::ProtobufValue for EncryptionTypePB {
|
|
||||||
fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
|
|
||||||
::protobuf::reflect::ReflectValueRef::Enum(::protobuf::ProtobufEnum::descriptor(self))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static file_descriptor_proto_data: &'static [u8] = b"\
|
|
||||||
\n\nuser.proto\x1a\nauth.proto\"\xe2\x02\n\rUserProfilePB\x12\x0e\n\x02i\
|
|
||||||
d\x18\x01\x20\x01(\x03R\x02id\x12\x14\n\x05email\x18\x02\x20\x01(\tR\x05\
|
|
||||||
email\x12\x12\n\x04name\x18\x03\x20\x01(\tR\x04name\x12\x14\n\x05token\
|
|
||||||
\x18\x04\x20\x01(\tR\x05token\x12\x19\n\x08icon_url\x18\x05\x20\x01(\tR\
|
|
||||||
\x07iconUrl\x12\x1d\n\nopenai_key\x18\x06\x20\x01(\tR\topenaiKey\x126\n\
|
|
||||||
\rauthenticator\x18\x07\x20\x01(\x0e2\x10.AuthenticatorPBR\rauthenticato\
|
|
||||||
r\x12'\n\x0fencryption_sign\x18\x08\x20\x01(\tR\x0eencryptionSign\x12!\n\
|
|
||||||
\x0cworkspace_id\x18\t\x20\x01(\tR\x0bworkspaceId\x12(\n\x10stability_ai\
|
|
||||||
_key\x18\n\x20\x01(\tR\x0estabilityAiKey\x12\x19\n\x08ai_model\x18\x0b\
|
|
||||||
\x20\x01(\tR\x07aiModel*3\n\x10EncryptionTypePB\x12\x10\n\x0cNoEncryptio\
|
|
||||||
n\x10\0\x12\r\n\tSymmetric\x10\x01b\x06proto3\
|
|
||||||
";
|
|
||||||
|
|
||||||
static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;
|
|
||||||
|
|
||||||
fn parse_descriptor_proto() -> ::protobuf::descriptor::FileDescriptorProto {
|
|
||||||
::protobuf::Message::parse_from_bytes(file_descriptor_proto_data).unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn file_descriptor_proto() -> &'static ::protobuf::descriptor::FileDescriptorProto {
|
|
||||||
file_descriptor_proto_lazy.get(|| {
|
|
||||||
parse_descriptor_proto()
|
|
||||||
})
|
|
||||||
}
|
|
@ -1,58 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "af-wasm"
|
|
||||||
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]
|
|
||||||
wasm-bindgen = { version = "0.2.89" }
|
|
||||||
lazy_static = "1.4.0"
|
|
||||||
lib-dispatch = { workspace = true, features = ["use_serde"] }
|
|
||||||
parking_lot.workspace = true
|
|
||||||
tracing.workspace = true
|
|
||||||
tracing-core = { version = "0.1.32" }
|
|
||||||
tracing-wasm = "0.2.1"
|
|
||||||
serde.workspace = true
|
|
||||||
collab-integrate = { workspace = true }
|
|
||||||
tokio-stream.workspace = true
|
|
||||||
|
|
||||||
af-user.workspace = true
|
|
||||||
af-persistence.workspace = true
|
|
||||||
flowy-notification = { workspace = true, features = ["web_ts"] }
|
|
||||||
flowy-user-pub = { workspace = true }
|
|
||||||
flowy-server = { workspace = true }
|
|
||||||
flowy-server-pub = { workspace = true }
|
|
||||||
flowy-error = { workspace = true, features = ["impl_from_dispatch_error", "web_ts"] }
|
|
||||||
flowy-document = { workspace = true, features = ["web_ts"] }
|
|
||||||
flowy-folder = { workspace = true, features = ["web_ts"] }
|
|
||||||
lib-infra = { workspace = true }
|
|
||||||
collab = { workspace = true }
|
|
||||||
web-sys = "0.3"
|
|
||||||
wasm-bindgen-futures.workspace = true
|
|
||||||
uuid.workspace = true
|
|
||||||
serde-wasm-bindgen.workspace = true
|
|
||||||
js-sys = "0.3.67"
|
|
||||||
anyhow = "1.0"
|
|
||||||
|
|
||||||
# `wee_alloc` is a tiny allocator for wasm that is only ~1K in code size
|
|
||||||
# compared to the default allocator's ~10K. However, it is slower than the default
|
|
||||||
# allocator, so it's not enabled by default.
|
|
||||||
wee_alloc = { version = "0.4.2", optional = true }
|
|
||||||
|
|
||||||
# The `console_error_panic_hook` crate provides better debugging of panics by
|
|
||||||
# logging them with `console.error`. This is great for development, but requires
|
|
||||||
# all the `std::fmt` and `std::panicking` infrastructure, so it's only enabled
|
|
||||||
# in debug mode.
|
|
||||||
[target."cfg(debug_assertions)".dependencies]
|
|
||||||
console_error_panic_hook = "0.1.5"
|
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
wasm-bindgen-test = "0.3.40"
|
|
||||||
tokio = { version = "1.0", features = ["sync"] }
|
|
||||||
|
|
||||||
[features]
|
|
||||||
#default = ["wee_alloc"]
|
|
||||||
localhost_dev = []
|
|
@ -1,89 +0,0 @@
|
|||||||
use crate::deps_resolve::document_deps::DocumentDepsResolver;
|
|
||||||
use crate::deps_resolve::folder_deps::FolderDepsResolver;
|
|
||||||
use crate::integrate::server::ServerProviderWASM;
|
|
||||||
use af_persistence::store::AppFlowyWASMStore;
|
|
||||||
use af_user::authenticate_user::AuthenticateUser;
|
|
||||||
use af_user::manager::UserManager;
|
|
||||||
use collab_integrate::collab_builder::{AppFlowyCollabBuilder, WorkspaceCollabIntegrate};
|
|
||||||
use flowy_document::manager::DocumentManager;
|
|
||||||
use flowy_error::FlowyResult;
|
|
||||||
use flowy_folder::manager::FolderManager;
|
|
||||||
use flowy_server_pub::af_cloud_config::AFCloudConfiguration;
|
|
||||||
use flowy_storage::ObjectStorageService;
|
|
||||||
use lib_dispatch::prelude::AFPluginDispatcher;
|
|
||||||
use lib_dispatch::runtime::AFPluginRuntime;
|
|
||||||
use std::rc::Rc;
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
pub struct AppFlowyWASMCore {
|
|
||||||
pub collab_builder: Arc<AppFlowyCollabBuilder>,
|
|
||||||
pub event_dispatcher: Rc<AFPluginDispatcher>,
|
|
||||||
pub user_manager: Rc<UserManager>,
|
|
||||||
pub folder_manager: Rc<FolderManager>,
|
|
||||||
pub document_manager: Rc<DocumentManager>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AppFlowyWASMCore {
|
|
||||||
pub async fn new(device_id: &str, cloud_config: AFCloudConfiguration) -> FlowyResult<Self> {
|
|
||||||
let runtime = Arc::new(AFPluginRuntime::new().unwrap());
|
|
||||||
let server_provider = Rc::new(ServerProviderWASM::new(device_id, cloud_config));
|
|
||||||
let store = Rc::new(AppFlowyWASMStore::new().await?);
|
|
||||||
let auth_user = Rc::new(AuthenticateUser::new(store.clone()).await?);
|
|
||||||
let collab_builder = Arc::new(AppFlowyCollabBuilder::new(
|
|
||||||
device_id.to_string(),
|
|
||||||
server_provider.clone(),
|
|
||||||
WorkspaceCollabIntegrateImpl(auth_user.clone()),
|
|
||||||
));
|
|
||||||
|
|
||||||
let document_manager = DocumentDepsResolver::resolve(
|
|
||||||
Rc::downgrade(&auth_user),
|
|
||||||
collab_builder.clone(),
|
|
||||||
server_provider.clone(),
|
|
||||||
Rc::downgrade(&(server_provider.clone() as Rc<dyn ObjectStorageService>)),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
let folder_manager = FolderDepsResolver::resolve(
|
|
||||||
Rc::downgrade(&auth_user),
|
|
||||||
document_manager.clone(),
|
|
||||||
collab_builder.clone(),
|
|
||||||
server_provider.clone(),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
let user_manager = Rc::new(
|
|
||||||
UserManager::new(
|
|
||||||
device_id,
|
|
||||||
store,
|
|
||||||
server_provider.clone(),
|
|
||||||
auth_user,
|
|
||||||
Arc::downgrade(&collab_builder),
|
|
||||||
)
|
|
||||||
.await?,
|
|
||||||
);
|
|
||||||
|
|
||||||
let event_dispatcher = Rc::new(AFPluginDispatcher::new(
|
|
||||||
runtime,
|
|
||||||
vec![af_user::event_map::init(Rc::downgrade(&user_manager))],
|
|
||||||
));
|
|
||||||
Ok(Self {
|
|
||||||
collab_builder,
|
|
||||||
event_dispatcher,
|
|
||||||
user_manager,
|
|
||||||
folder_manager,
|
|
||||||
document_manager,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct WorkspaceCollabIntegrateImpl(Rc<AuthenticateUser>);
|
|
||||||
impl WorkspaceCollabIntegrate for WorkspaceCollabIntegrateImpl {
|
|
||||||
fn workspace_id(&self) -> Result<String, anyhow::Error> {
|
|
||||||
let workspace_id = self.0.workspace_id()?;
|
|
||||||
Ok(workspace_id)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn device_id(&self) -> Result<String, anyhow::Error> {
|
|
||||||
Ok("fake device id".to_string())
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
use crate::integrate::server::ServerProviderWASM;
|
|
||||||
use af_user::authenticate_user::AuthenticateUser;
|
|
||||||
use collab_integrate::collab_builder::AppFlowyCollabBuilder;
|
|
||||||
use flowy_document::manager::DocumentManager;
|
|
||||||
use flowy_storage::ObjectStorageService;
|
|
||||||
use std::rc::{Rc, Weak};
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
pub struct DocumentDepsResolver;
|
|
||||||
impl DocumentDepsResolver {
|
|
||||||
pub async fn resolve(
|
|
||||||
authenticate_user: Weak<AuthenticateUser>,
|
|
||||||
collab_builder: Arc<AppFlowyCollabBuilder>,
|
|
||||||
server_provider: Rc<ServerProviderWASM>,
|
|
||||||
storage_service: Weak<dyn ObjectStorageService>,
|
|
||||||
) -> Rc<DocumentManager> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
use crate::integrate::server::ServerProviderWASM;
|
|
||||||
use af_user::authenticate_user::AuthenticateUser;
|
|
||||||
use collab_integrate::collab_builder::AppFlowyCollabBuilder;
|
|
||||||
use flowy_document::manager::DocumentManager;
|
|
||||||
use flowy_folder::manager::FolderManager;
|
|
||||||
use std::rc::{Rc, Weak};
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
pub struct FolderDepsResolver;
|
|
||||||
|
|
||||||
impl FolderDepsResolver {
|
|
||||||
pub async fn resolve(
|
|
||||||
authenticate_user: Weak<AuthenticateUser>,
|
|
||||||
document_manager: Rc<DocumentManager>,
|
|
||||||
collab_builder: Arc<AppFlowyCollabBuilder>,
|
|
||||||
server_provider: Rc<ServerProviderWASM>,
|
|
||||||
) -> Rc<FolderManager> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,2 +0,0 @@
|
|||||||
pub(crate) mod document_deps;
|
|
||||||
pub(crate) mod folder_deps;
|
|
@ -1 +0,0 @@
|
|||||||
pub mod server;
|
|
@ -1,126 +0,0 @@
|
|||||||
use collab::preclude::CollabPlugin;
|
|
||||||
use collab_integrate::collab_builder::{
|
|
||||||
CollabCloudPluginProvider, CollabPluginProviderContext, CollabPluginProviderType,
|
|
||||||
};
|
|
||||||
use flowy_error::FlowyError;
|
|
||||||
use flowy_server::af_cloud::AppFlowyCloudServer;
|
|
||||||
use flowy_server::AppFlowyServer;
|
|
||||||
use flowy_server_pub::af_cloud_config::AFCloudConfiguration;
|
|
||||||
use flowy_storage::{ObjectIdentity, ObjectStorageService, ObjectValue};
|
|
||||||
use flowy_user_pub::cloud::{UserCloudService, UserCloudServiceProvider};
|
|
||||||
use flowy_user_pub::entities::{Authenticator, UserTokenState};
|
|
||||||
use lib_infra::future::{to_fut, Fut, FutureResult};
|
|
||||||
use parking_lot::RwLock;
|
|
||||||
use std::rc::Rc;
|
|
||||||
use std::sync::Arc;
|
|
||||||
use tokio_stream::wrappers::WatchStream;
|
|
||||||
use tracing::{info, warn};
|
|
||||||
|
|
||||||
pub struct ServerProviderWASM {
|
|
||||||
device_id: String,
|
|
||||||
config: AFCloudConfiguration,
|
|
||||||
server: RwLock<Option<Rc<dyn AppFlowyServer>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ServerProviderWASM {
|
|
||||||
pub fn new(device_id: &str, config: AFCloudConfiguration) -> Self {
|
|
||||||
info!("Server config: {}", config);
|
|
||||||
Self {
|
|
||||||
device_id: device_id.to_string(),
|
|
||||||
server: RwLock::new(Default::default()),
|
|
||||||
config,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_server(&self) -> Rc<dyn AppFlowyServer> {
|
|
||||||
let server = self.server.read().as_ref().cloned();
|
|
||||||
match server {
|
|
||||||
Some(server) => server,
|
|
||||||
None => {
|
|
||||||
let server = Rc::new(AppFlowyCloudServer::new(
|
|
||||||
self.config.clone(),
|
|
||||||
true,
|
|
||||||
self.device_id.clone(),
|
|
||||||
"0.0.1",
|
|
||||||
));
|
|
||||||
*self.server.write() = Some(server.clone());
|
|
||||||
server
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CollabCloudPluginProvider for ServerProviderWASM {
|
|
||||||
fn provider_type(&self) -> CollabPluginProviderType {
|
|
||||||
CollabPluginProviderType::AppFlowyCloud
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_plugins(&self, _context: CollabPluginProviderContext) -> Vec<Box<dyn CollabPlugin>> {
|
|
||||||
vec![]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_sync_enabled(&self) -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UserCloudServiceProvider for ServerProviderWASM {
|
|
||||||
fn set_token(&self, token: &str) -> Result<(), FlowyError> {
|
|
||||||
self.get_server().set_token(token)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_ai_model(&self, ai_model: &str) -> Result<(), FlowyError> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn subscribe_token_state(&self) -> Option<WatchStream<UserTokenState>> {
|
|
||||||
self.get_server().subscribe_token_state()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_enable_sync(&self, _uid: i64, _enable_sync: bool) {
|
|
||||||
warn!("enable sync is not supported in wasm")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_user_authenticator(&self, _authenticator: &Authenticator) {
|
|
||||||
warn!("set user authenticator is not supported in wasm")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_user_authenticator(&self) -> Authenticator {
|
|
||||||
Authenticator::AppFlowyCloud
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_network_reachable(&self, _reachable: bool) {
|
|
||||||
warn!("set network reachable is not supported in wasm")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_encrypt_secret(&self, _secret: String) {
|
|
||||||
warn!("set encrypt secret is not supported in wasm")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_user_service(&self) -> Result<Arc<dyn UserCloudService>, FlowyError> {
|
|
||||||
Ok(self.get_server().user_service())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn service_url(&self) -> String {
|
|
||||||
self.config.base_url.clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ObjectStorageService for ServerProviderWASM {
|
|
||||||
fn get_object_url(&self, object_id: ObjectIdentity) -> FutureResult<String, FlowyError> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn put_object(&self, url: String, object_value: ObjectValue) -> FutureResult<(), FlowyError> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn delete_object(&self, url: String) -> FutureResult<(), FlowyError> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_object(&self, url: String) -> FutureResult<ObjectValue, FlowyError> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,170 +0,0 @@
|
|||||||
use crate::notification::TSNotificationSender;
|
|
||||||
use flowy_notification::{register_notification_sender, unregister_all_notification_sender};
|
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
pub mod core;
|
|
||||||
mod deps_resolve;
|
|
||||||
mod integrate;
|
|
||||||
pub mod notification;
|
|
||||||
|
|
||||||
use crate::core::AppFlowyWASMCore;
|
|
||||||
use lazy_static::lazy_static;
|
|
||||||
use lib_dispatch::prelude::{
|
|
||||||
AFPluginDispatcher, AFPluginEventResponse, AFPluginRequest, StatusCode,
|
|
||||||
};
|
|
||||||
|
|
||||||
use flowy_server_pub::af_cloud_config::AFCloudConfiguration;
|
|
||||||
use tracing::{error, info};
|
|
||||||
use wasm_bindgen::prelude::wasm_bindgen;
|
|
||||||
use wasm_bindgen::JsValue;
|
|
||||||
use wasm_bindgen_futures::{future_to_promise, js_sys};
|
|
||||||
|
|
||||||
lazy_static! {
|
|
||||||
static ref APPFLOWY_CORE: RefCellAppFlowyCore = RefCellAppFlowyCore::new();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "wee_alloc")]
|
|
||||||
#[global_allocator]
|
|
||||||
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
|
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
extern "C" {
|
|
||||||
#[wasm_bindgen(js_namespace = console)]
|
|
||||||
pub fn log(s: &str);
|
|
||||||
#[wasm_bindgen(js_namespace = window)]
|
|
||||||
fn onFlowyNotify(event_name: &str, payload: JsValue);
|
|
||||||
}
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub fn init_tracing_log() {
|
|
||||||
tracing_wasm::set_as_global_default();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub fn init_wasm_core() -> js_sys::Promise {
|
|
||||||
// It's disabled in release mode so it doesn't bloat up the file size.
|
|
||||||
#[cfg(debug_assertions)]
|
|
||||||
console_error_panic_hook::set_once();
|
|
||||||
|
|
||||||
#[cfg(feature = "localhost_dev")]
|
|
||||||
let config = AFCloudConfiguration {
|
|
||||||
base_url: "http://localhost".to_string(),
|
|
||||||
ws_base_url: "ws://localhost/ws/v1".to_string(),
|
|
||||||
gotrue_url: "http://localhost/gotrue".to_string(),
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(not(feature = "localhost_dev"))]
|
|
||||||
let config = AFCloudConfiguration {
|
|
||||||
base_url: "https://beta.appflowy.cloud".to_string(),
|
|
||||||
ws_base_url: "wss://beta.appflowy.cloud/ws/v1".to_string(),
|
|
||||||
gotrue_url: "https://beta.appflowy.cloud/gotrue".to_string(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let future = async move {
|
|
||||||
if let Ok(core) = AppFlowyWASMCore::new("device_id", config).await {
|
|
||||||
*APPFLOWY_CORE.0.borrow_mut() = Some(core);
|
|
||||||
info!("🔥🔥🔥Initialized AppFlowyWASMCore");
|
|
||||||
} else {
|
|
||||||
error!("Failed to initialize AppFlowyWASMCore")
|
|
||||||
}
|
|
||||||
Ok(JsValue::from_str(""))
|
|
||||||
};
|
|
||||||
future_to_promise(future)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub fn async_event(name: String, payload: Vec<u8>) -> js_sys::Promise {
|
|
||||||
if let Some(dispatcher) = APPFLOWY_CORE.dispatcher() {
|
|
||||||
let future = async move {
|
|
||||||
let request = WasmRequest::new(name, payload);
|
|
||||||
let event_resp =
|
|
||||||
AFPluginDispatcher::boxed_async_send_with_callback(dispatcher.as_ref(), request, |_| {
|
|
||||||
Box::pin(async {})
|
|
||||||
})
|
|
||||||
.await;
|
|
||||||
|
|
||||||
let response = WasmResponse::from(event_resp);
|
|
||||||
serde_wasm_bindgen::to_value(&response).map_err(error_response)
|
|
||||||
};
|
|
||||||
|
|
||||||
future_to_promise(future)
|
|
||||||
} else {
|
|
||||||
future_to_promise(async { Err(JsValue::from_str("Dispatcher is not initialized")) })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub fn register_listener() {
|
|
||||||
unregister_all_notification_sender();
|
|
||||||
register_notification_sender(TSNotificationSender::new());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn on_event(event_name: &str, args: JsValue) {
|
|
||||||
onFlowyNotify(event_name, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct RefCellAppFlowyCore(RefCell<Option<AppFlowyWASMCore>>);
|
|
||||||
|
|
||||||
/// safety:
|
|
||||||
/// In a WebAssembly, implement the Sync for RefCellAppFlowyCore is safety
|
|
||||||
/// since WASM currently operates in a single-threaded environment.
|
|
||||||
unsafe impl Sync for RefCellAppFlowyCore {}
|
|
||||||
|
|
||||||
impl RefCellAppFlowyCore {
|
|
||||||
fn new() -> Self {
|
|
||||||
Self(RefCell::new(None))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dispatcher(&self) -> Option<Rc<AFPluginDispatcher>> {
|
|
||||||
self
|
|
||||||
.0
|
|
||||||
.borrow()
|
|
||||||
.as_ref()
|
|
||||||
.map(|core| core.event_dispatcher.clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn error_response(error: serde_wasm_bindgen::Error) -> JsValue {
|
|
||||||
error!("Error: {}", error);
|
|
||||||
serde_wasm_bindgen::to_value(&WasmResponse::error(error.to_string())).unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct WasmRequest {
|
|
||||||
name: String,
|
|
||||||
payload: Vec<u8>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WasmRequest {
|
|
||||||
pub fn new(name: String, payload: Vec<u8>) -> Self {
|
|
||||||
Self { name, payload }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<WasmRequest> for AFPluginRequest {
|
|
||||||
fn from(request: WasmRequest) -> Self {
|
|
||||||
AFPluginRequest::new(request.name).payload(request.payload)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(serde::Serialize)]
|
|
||||||
pub struct WasmResponse {
|
|
||||||
pub code: i8,
|
|
||||||
pub payload: Vec<u8>,
|
|
||||||
}
|
|
||||||
impl WasmResponse {
|
|
||||||
pub fn error(msg: String) -> Self {
|
|
||||||
Self {
|
|
||||||
code: StatusCode::Err as i8,
|
|
||||||
payload: msg.into_bytes(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<AFPluginEventResponse> for WasmResponse {
|
|
||||||
fn from(response: AFPluginEventResponse) -> Self {
|
|
||||||
Self {
|
|
||||||
code: response.status_code as i8,
|
|
||||||
payload: response.payload.to_vec(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
use flowy_notification::entities::SubscribeObject;
|
|
||||||
use flowy_notification::NotificationSender;
|
|
||||||
|
|
||||||
pub const AF_NOTIFICATION: &str = "af-notification";
|
|
||||||
|
|
||||||
pub struct TSNotificationSender {}
|
|
||||||
|
|
||||||
impl TSNotificationSender {
|
|
||||||
pub(crate) fn new() -> Self {
|
|
||||||
TSNotificationSender {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NotificationSender for TSNotificationSender {
|
|
||||||
fn send_subject(&self, _subject: SubscribeObject) -> Result<(), String> {
|
|
||||||
// on_event(AF_NOTIFICATION, serde_wasm_bindgen::to_value(&subject).unwrap_or(JsValue::UNDEFINED));
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,4 +0,0 @@
|
|||||||
use wasm_bindgen_test::wasm_bindgen_test_configure;
|
|
||||||
wasm_bindgen_test_configure!(run_in_browser);
|
|
||||||
mod user;
|
|
||||||
pub(crate) mod util;
|
|
@ -1,10 +0,0 @@
|
|||||||
use crate::util::tester::{unique_email, WASMEventTester};
|
|
||||||
use wasm_bindgen_test::wasm_bindgen_test;
|
|
||||||
|
|
||||||
#[wasm_bindgen_test]
|
|
||||||
async fn sign_up_event_test() {
|
|
||||||
let tester = WASMEventTester::new().await;
|
|
||||||
let email = unique_email();
|
|
||||||
let user_profile = tester.sign_in_with_email(&email).await.unwrap();
|
|
||||||
assert_eq!(user_profile.email, email);
|
|
||||||
}
|
|
@ -1 +0,0 @@
|
|||||||
mod event_test;
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user