mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
chore: polish ui (#5883)
* chore: polish ui * chore: clippy * chore: fmt
This commit is contained in:
@ -175,7 +175,7 @@ SPEC CHECKSUMS:
|
|||||||
file_picker: 09aa5ec1ab24135ccd7a1621c46c84134bfd6655
|
file_picker: 09aa5ec1ab24135ccd7a1621c46c84134bfd6655
|
||||||
flowy_infra_ui: 0455e1fa8c51885aa1437848e361e99419f34ebc
|
flowy_infra_ui: 0455e1fa8c51885aa1437848e361e99419f34ebc
|
||||||
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
||||||
fluttertoast: e9a18c7be5413da53898f660530c56f35edfba9c
|
fluttertoast: 723e187574b149e68e63ca4d39b837586b903cfa
|
||||||
image_gallery_saver: cb43cc43141711190510e92c460eb1655cd343cb
|
image_gallery_saver: cb43cc43141711190510e92c460eb1655cd343cb
|
||||||
image_picker_ios: 99dfe1854b4fa34d0364e74a78448a0151025425
|
image_picker_ios: 99dfe1854b4fa34d0364e74a78448a0151025425
|
||||||
integration_test: ce0a3ffa1de96d1a89ca0ac26fca7ea18a749ef4
|
integration_test: ce0a3ffa1de96d1a89ca0ac26fca7ea18a749ef4
|
||||||
@ -197,4 +197,4 @@ SPEC CHECKSUMS:
|
|||||||
|
|
||||||
PODFILE CHECKSUM: d0d9b4ff572d8695c38eb3f9b490f55cdfc57eca
|
PODFILE CHECKSUM: d0d9b4ff572d8695c38eb3f9b490f55cdfc57eca
|
||||||
|
|
||||||
COCOAPODS: 1.15.2
|
COCOAPODS: 1.11.3
|
||||||
|
@ -2,10 +2,12 @@ import 'dart:async';
|
|||||||
|
|
||||||
import 'package:appflowy/workspace/application/view/view_ext.dart';
|
import 'package:appflowy/workspace/application/view/view_ext.dart';
|
||||||
import 'package:appflowy/workspace/application/view/view_service.dart';
|
import 'package:appflowy/workspace/application/view/view_service.dart';
|
||||||
|
import 'package:appflowy_backend/log.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||||
import 'package:bloc/bloc.dart';
|
import 'package:bloc/bloc.dart';
|
||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
|
||||||
import 'chat_input_action_control.dart';
|
import 'chat_input_action_control.dart';
|
||||||
@ -43,34 +45,28 @@ class ChatInputActionBloc
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
refreshViews: (List<ViewPB> views) {
|
refreshViews: (List<ViewPB> views) {
|
||||||
|
final List<ViewActionPage> pages = _filterPages(
|
||||||
|
views,
|
||||||
|
state.selectedPages,
|
||||||
|
state.filter,
|
||||||
|
);
|
||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
views: views,
|
views: views,
|
||||||
pages: views.map((v) => ViewActionPage(view: v)).toList(),
|
pages: pages,
|
||||||
|
indicator: const ChatActionMenuIndicator.ready(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
filter: (String filter) {
|
filter: (String filter) {
|
||||||
final List<ViewActionPage> pages = [];
|
Log.debug("Filter chat input pages: $filter");
|
||||||
if (filter.isEmpty) {
|
final List<ViewActionPage> pages = _filterPages(
|
||||||
pages.addAll(state.views.map((v) => ViewActionPage(view: v)));
|
state.views,
|
||||||
} else {
|
state.selectedPages,
|
||||||
pages.addAll(
|
filter,
|
||||||
state.views
|
|
||||||
.where(
|
|
||||||
(v) => v.name.toLowerCase().contains(
|
|
||||||
filter.toLowerCase(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.map(
|
|
||||||
(v) => ViewActionPage(view: v),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
|
||||||
pages.retainWhere((view) {
|
emit(state.copyWith(pages: pages, filter: filter));
|
||||||
return !state.selectedPages.contains(view);
|
|
||||||
});
|
|
||||||
emit(state.copyWith(pages: pages));
|
|
||||||
},
|
},
|
||||||
handleKeyEvent: (PhysicalKeyboardKey physicalKey) {
|
handleKeyEvent: (PhysicalKeyboardKey physicalKey) {
|
||||||
emit(
|
emit(
|
||||||
@ -81,8 +77,14 @@ class ChatInputActionBloc
|
|||||||
},
|
},
|
||||||
addPage: (ChatInputActionPage page) {
|
addPage: (ChatInputActionPage page) {
|
||||||
if (!state.selectedPages.any((p) => p.pageId == page.pageId)) {
|
if (!state.selectedPages.any((p) => p.pageId == page.pageId)) {
|
||||||
|
final List<ViewActionPage> pages = _filterPages(
|
||||||
|
state.views,
|
||||||
|
state.selectedPages,
|
||||||
|
state.filter,
|
||||||
|
);
|
||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
|
pages: pages,
|
||||||
selectedPages: [...state.selectedPages, page],
|
selectedPages: [...state.selectedPages, page],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -93,9 +95,11 @@ class ChatInputActionBloc
|
|||||||
List.from(state.selectedPages);
|
List.from(state.selectedPages);
|
||||||
selectedPages.retainWhere((t) => !text.contains(t.title));
|
selectedPages.retainWhere((t) => !text.contains(t.title));
|
||||||
|
|
||||||
final allPages =
|
final List<ViewActionPage> allPages = _filterPages(
|
||||||
state.views.map((v) => ViewActionPage(view: v)).toList();
|
state.views,
|
||||||
allPages.retainWhere((view) => !selectedPages.contains(view));
|
state.selectedPages,
|
||||||
|
state.filter,
|
||||||
|
);
|
||||||
|
|
||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
@ -108,6 +112,32 @@ class ChatInputActionBloc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<ViewActionPage> _filterPages(
|
||||||
|
List<ViewPB> views,
|
||||||
|
List<ChatInputActionPage> selectedPages,
|
||||||
|
String filter,
|
||||||
|
) {
|
||||||
|
final pages = views
|
||||||
|
.map(
|
||||||
|
(v) => ViewActionPage(view: v),
|
||||||
|
)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
pages.retainWhere((page) {
|
||||||
|
return !selectedPages.contains(page);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (filter.isEmpty) {
|
||||||
|
return pages;
|
||||||
|
}
|
||||||
|
|
||||||
|
return pages
|
||||||
|
.where(
|
||||||
|
(v) => v.title.toLowerCase().contains(filter.toLowerCase()),
|
||||||
|
)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
class ViewActionPage extends ChatInputActionPage {
|
class ViewActionPage extends ChatInputActionPage {
|
||||||
ViewActionPage({required this.view});
|
ViewActionPage({required this.view});
|
||||||
|
|
||||||
@ -124,6 +154,9 @@ class ViewActionPage extends ChatInputActionPage {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
dynamic get page => view;
|
dynamic get page => view;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget get icon => view.defaultIcon();
|
||||||
}
|
}
|
||||||
|
|
||||||
@freezed
|
@freezed
|
||||||
@ -146,7 +179,10 @@ class ChatInputActionState with _$ChatInputActionState {
|
|||||||
@Default([]) List<ViewPB> views,
|
@Default([]) List<ViewPB> views,
|
||||||
@Default([]) List<ChatInputActionPage> pages,
|
@Default([]) List<ChatInputActionPage> pages,
|
||||||
@Default([]) List<ChatInputActionPage> selectedPages,
|
@Default([]) List<ChatInputActionPage> selectedPages,
|
||||||
|
@Default("") String filter,
|
||||||
ChatInputKeyboardEvent? keyboardKey,
|
ChatInputKeyboardEvent? keyboardKey,
|
||||||
|
@Default(ChatActionMenuIndicator.loading())
|
||||||
|
ChatActionMenuIndicator indicator,
|
||||||
}) = _ChatInputActionState;
|
}) = _ChatInputActionState;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -159,3 +195,9 @@ class ChatInputKeyboardEvent extends Equatable {
|
|||||||
@override
|
@override
|
||||||
List<Object?> get props => [timestamp];
|
List<Object?> get props => [timestamp];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
class ChatActionMenuIndicator with _$ChatActionMenuIndicator {
|
||||||
|
const factory ChatActionMenuIndicator.ready() = _Ready;
|
||||||
|
const factory ChatActionMenuIndicator.loading() = _Loading;
|
||||||
|
}
|
||||||
|
@ -9,6 +9,7 @@ abstract class ChatInputActionPage extends Equatable {
|
|||||||
String get title;
|
String get title;
|
||||||
String get pageId;
|
String get pageId;
|
||||||
dynamic get page;
|
dynamic get page;
|
||||||
|
Widget get icon;
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef ChatInputMetadata = Map<String, ChatInputActionPage>;
|
typedef ChatInputMetadata = Map<String, ChatInputActionPage>;
|
||||||
@ -26,10 +27,9 @@ class ChatInputActionControl extends ChatActionHandler {
|
|||||||
final String chatId;
|
final String chatId;
|
||||||
|
|
||||||
// Private attributes
|
// Private attributes
|
||||||
bool _isShowActionMenu = false;
|
|
||||||
String _atText = "";
|
String _atText = "";
|
||||||
String _prevText = "";
|
String _prevText = "";
|
||||||
bool _didLoadViews = false;
|
String _showMenuText = "";
|
||||||
|
|
||||||
// Getter
|
// Getter
|
||||||
List<String> get tags =>
|
List<String> get tags =>
|
||||||
@ -43,12 +43,12 @@ class ChatInputActionControl extends ChatActionHandler {
|
|||||||
void handleKeyEvent(KeyEvent event) {
|
void handleKeyEvent(KeyEvent event) {
|
||||||
// ignore: deprecated_member_use
|
// ignore: deprecated_member_use
|
||||||
if (event is KeyDownEvent || event is RawKeyDownEvent) {
|
if (event is KeyDownEvent || event is RawKeyDownEvent) {
|
||||||
commandBloc.add(ChatInputActionEvent.handleKeyEvent(event.physicalKey));
|
_commandBloc.add(ChatInputActionEvent.handleKeyEvent(event.physicalKey));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool canHandleKeyEvent(KeyEvent event) {
|
bool canHandleKeyEvent(KeyEvent event) {
|
||||||
return _isShowActionMenu &&
|
return _showMenuText.isNotEmpty &&
|
||||||
<PhysicalKeyboardKey>{
|
<PhysicalKeyboardKey>{
|
||||||
PhysicalKeyboardKey.arrowDown,
|
PhysicalKeyboardKey.arrowDown,
|
||||||
PhysicalKeyboardKey.arrowUp,
|
PhysicalKeyboardKey.arrowUp,
|
||||||
@ -58,35 +58,29 @@ class ChatInputActionControl extends ChatActionHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void dispose() {
|
void dispose() {
|
||||||
commandBloc.close();
|
_commandBloc.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onSelected(ChatInputActionPage page) {
|
void onSelected(ChatInputActionPage page) {
|
||||||
_atText = "";
|
|
||||||
_isShowActionMenu = false;
|
|
||||||
|
|
||||||
_commandBloc.add(ChatInputActionEvent.addPage(page));
|
_commandBloc.add(ChatInputActionEvent.addPage(page));
|
||||||
textController.text =
|
textController.text = "$_showMenuText${page.title}";
|
||||||
"${textController.text.replaceAll(_atText, '')}${page.title}";
|
|
||||||
_prevText = textController.text;
|
onExit();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onExit() {
|
void onExit() {
|
||||||
_atText = "";
|
_atText = "";
|
||||||
_isShowActionMenu = false;
|
_showMenuText = "";
|
||||||
_didLoadViews = false;
|
_prevText = "";
|
||||||
commandBloc.add(const ChatInputActionEvent.filter(""));
|
_commandBloc.add(const ChatInputActionEvent.filter(""));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onEnter() {
|
void onEnter() {
|
||||||
if (!_didLoadViews) {
|
_commandBloc.add(const ChatInputActionEvent.started());
|
||||||
_didLoadViews = true;
|
_showMenuText = textController.text;
|
||||||
commandBloc.add(const ChatInputActionEvent.started());
|
|
||||||
}
|
|
||||||
_isShowActionMenu = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -134,16 +128,15 @@ class ChatInputActionControl extends ChatActionHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If the action menu is shown, filter the views
|
// If the action menu is shown, filter the views
|
||||||
if (_isShowActionMenu) {
|
if (_showMenuText.isNotEmpty) {
|
||||||
// before filter the views, remove the first character '@' if it exists
|
if (text.length >= _showMenuText.length) {
|
||||||
if (inputText.startsWith("@")) {
|
final filterText = inputText.substring(_showMenuText.length);
|
||||||
final filter = inputText.substring(1);
|
_commandBloc.add(ChatInputActionEvent.filter(filterText));
|
||||||
commandBloc.add(ChatInputActionEvent.filter(filter));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the text change from "xxx @"" to "xxx", which means user delete the @, we should hide the action menu
|
// If the text change from "xxx @"" to "xxx", which means user delete the @, we should hide the action menu
|
||||||
if (_atText.isNotEmpty && !inputText.contains(_atText)) {
|
if (_atText.isNotEmpty && !inputText.contains(_atText)) {
|
||||||
commandBloc.add(
|
_commandBloc.add(
|
||||||
const ChatInputActionEvent.handleKeyEvent(PhysicalKeyboardKey.escape),
|
const ChatInputActionEvent.handleKeyEvent(PhysicalKeyboardKey.escape),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -489,6 +489,7 @@ class _ChatContentPageState extends State<_ChatContentPage> {
|
|||||||
child: BlocBuilder<ChatInputStateBloc, ChatInputStateState>(
|
child: BlocBuilder<ChatInputStateBloc, ChatInputStateState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
// Show different hint text based on the AI type
|
// Show different hint text based on the AI type
|
||||||
|
final aiType = state.aiType;
|
||||||
final hintText = state.aiType.when(
|
final hintText = state.aiType.when(
|
||||||
appflowyAI: () => LocaleKeys.chat_inputMessageHint.tr(),
|
appflowyAI: () => LocaleKeys.chat_inputMessageHint.tr(),
|
||||||
localAI: () => LocaleKeys.chat_inputLocalAIMessageHint.tr(),
|
localAI: () => LocaleKeys.chat_inputLocalAIMessageHint.tr(),
|
||||||
@ -500,6 +501,7 @@ class _ChatContentPageState extends State<_ChatContentPage> {
|
|||||||
selector: (state) => state.streamingStatus,
|
selector: (state) => state.streamingStatus,
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
return ChatInput(
|
return ChatInput(
|
||||||
|
aiType: aiType,
|
||||||
chatId: widget.view.id,
|
chatId: widget.view.id,
|
||||||
onSendPressed: (message) {
|
onSendPressed: (message) {
|
||||||
context.read<ChatBloc>().add(
|
context.read<ChatBloc>().add(
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import 'package:appflowy/plugins/ai_chat/application/chat_input_bloc.dart';
|
||||||
import 'package:appflowy/plugins/ai_chat/presentation/chat_input_action_menu.dart';
|
import 'package:appflowy/plugins/ai_chat/presentation/chat_input_action_menu.dart';
|
||||||
import 'package:appflowy/plugins/ai_chat/application/chat_input_action_control.dart';
|
import 'package:appflowy/plugins/ai_chat/application/chat_input_action_control.dart';
|
||||||
import 'package:extended_text_field/extended_text_field.dart';
|
import 'package:extended_text_field/extended_text_field.dart';
|
||||||
@ -26,6 +27,7 @@ class ChatInput extends StatefulWidget {
|
|||||||
required this.isStreaming,
|
required this.isStreaming,
|
||||||
required this.onStopStreaming,
|
required this.onStopStreaming,
|
||||||
required this.hintText,
|
required this.hintText,
|
||||||
|
required this.aiType,
|
||||||
});
|
});
|
||||||
|
|
||||||
final bool? isAttachmentUploading;
|
final bool? isAttachmentUploading;
|
||||||
@ -36,6 +38,7 @@ class ChatInput extends StatefulWidget {
|
|||||||
final String chatId;
|
final String chatId;
|
||||||
final bool isStreaming;
|
final bool isStreaming;
|
||||||
final String hintText;
|
final String hintText;
|
||||||
|
final AIType aiType;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<ChatInput> createState() => _ChatInputState();
|
State<ChatInput> createState() => _ChatInputState();
|
||||||
@ -117,7 +120,9 @@ class _ChatInputState extends State<ChatInput> {
|
|||||||
Expanded(child: _inputTextField(textPadding)),
|
Expanded(child: _inputTextField(textPadding)),
|
||||||
|
|
||||||
// TODO(lucas): support mobile
|
// TODO(lucas): support mobile
|
||||||
if (PlatformExtension.isDesktop) _atButton(buttonPadding),
|
if (PlatformExtension.isDesktop &&
|
||||||
|
widget.aiType == const AIType.appflowyAI())
|
||||||
|
_atButton(buttonPadding),
|
||||||
_sendButton(buttonPadding),
|
_sendButton(buttonPadding),
|
||||||
const HSpace(14),
|
const HSpace(14),
|
||||||
],
|
],
|
||||||
@ -169,6 +174,7 @@ class _ChatInputState extends State<ChatInput> {
|
|||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
border: InputBorder.none,
|
border: InputBorder.none,
|
||||||
hintText: widget.hintText,
|
hintText: widget.hintText,
|
||||||
|
focusedBorder: InputBorder.none,
|
||||||
hintStyle: TextStyle(
|
hintStyle: TextStyle(
|
||||||
color: AFThemeExtension.of(context).textColor.withOpacity(0.5),
|
color: AFThemeExtension.of(context).textColor.withOpacity(0.5),
|
||||||
),
|
),
|
||||||
@ -190,6 +196,10 @@ class _ChatInputState extends State<ChatInput> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _handleOnTextChange(BuildContext context, String text) {
|
void _handleOnTextChange(BuildContext context, String text) {
|
||||||
|
if (widget.aiType != const AIType.appflowyAI()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (PlatformExtension.isDesktop) {
|
if (PlatformExtension.isDesktop) {
|
||||||
if (_inputActionControl.onTextChanged(text)) {
|
if (_inputActionControl.onTextChanged(text)) {
|
||||||
ChatActionsMenu(
|
ChatActionsMenu(
|
||||||
|
@ -78,6 +78,8 @@ class ChatActionsMenu {
|
|||||||
),
|
),
|
||||||
maxHeight,
|
maxHeight,
|
||||||
);
|
);
|
||||||
|
final isLoading =
|
||||||
|
state.indicator == const ChatActionMenuIndicator.loading();
|
||||||
|
|
||||||
return Stack(
|
return Stack(
|
||||||
children: [
|
children: [
|
||||||
@ -106,6 +108,7 @@ class ChatActionsMenu {
|
|||||||
vertical: 2,
|
vertical: 2,
|
||||||
),
|
),
|
||||||
child: ActionList(
|
child: ActionList(
|
||||||
|
isLoading: isLoading,
|
||||||
handler: handler,
|
handler: handler,
|
||||||
onDismiss: () => dismiss(),
|
onDismiss: () => dismiss(),
|
||||||
pages: state.pages,
|
pages: state.pages,
|
||||||
@ -149,6 +152,7 @@ class _ActionItem extends StatelessWidget {
|
|||||||
borderRadius: BorderRadius.circular(4.0),
|
borderRadius: BorderRadius.circular(4.0),
|
||||||
),
|
),
|
||||||
child: FlowyButton(
|
child: FlowyButton(
|
||||||
|
leftIcon: item.icon,
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 6),
|
margin: const EdgeInsets.symmetric(horizontal: 6),
|
||||||
iconPadding: 10.0,
|
iconPadding: 10.0,
|
||||||
text: FlowyText.regular(
|
text: FlowyText.regular(
|
||||||
@ -166,11 +170,13 @@ class ActionList extends StatefulWidget {
|
|||||||
required this.handler,
|
required this.handler,
|
||||||
required this.onDismiss,
|
required this.onDismiss,
|
||||||
required this.pages,
|
required this.pages,
|
||||||
|
required this.isLoading,
|
||||||
});
|
});
|
||||||
|
|
||||||
final ChatActionHandler handler;
|
final ChatActionHandler handler;
|
||||||
final VoidCallback? onDismiss;
|
final VoidCallback? onDismiss;
|
||||||
final List<ChatInputActionPage> pages;
|
final List<ChatInputActionPage> pages;
|
||||||
|
final bool isLoading;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<ActionList> createState() => _ActionListState();
|
State<ActionList> createState() => _ActionListState();
|
||||||
@ -223,13 +229,22 @@ class _ActionListState extends State<ActionList> {
|
|||||||
child: ListView(
|
child: ListView(
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
controller: _scrollController,
|
controller: _scrollController,
|
||||||
padding: const EdgeInsets.all(8),
|
padding: const EdgeInsets.all(4),
|
||||||
children: _buildPages(),
|
children: _buildPages(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Widget> _buildPages() {
|
List<Widget> _buildPages() {
|
||||||
|
if (widget.isLoading) {
|
||||||
|
return [
|
||||||
|
SizedBox(
|
||||||
|
height: _noPageHeight.toDouble(),
|
||||||
|
child: const Center(child: CircularProgressIndicator.adaptive()),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
if (widget.pages.isEmpty) {
|
if (widget.pages.isEmpty) {
|
||||||
return [
|
return [
|
||||||
SizedBox(
|
SizedBox(
|
||||||
|
@ -30,13 +30,13 @@ impl Stream for LocalAIStreamAdaptor {
|
|||||||
|
|
||||||
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||||
let this = self.project();
|
let this = self.project();
|
||||||
return match ready!(this.stream.as_mut().poll_next(cx)) {
|
match ready!(this.stream.as_mut().poll_next(cx)) {
|
||||||
Some(Ok(bytes)) => match String::from_utf8(bytes.to_vec()) {
|
Some(Ok(bytes)) => match String::from_utf8(bytes.to_vec()) {
|
||||||
Ok(s) => Poll::Ready(Some(Ok(QuestionStreamValue::Answer { value: s }))),
|
Ok(s) => Poll::Ready(Some(Ok(QuestionStreamValue::Answer { value: s }))),
|
||||||
Err(err) => Poll::Ready(Some(Err(FlowyError::internal().with_context(err)))),
|
Err(err) => Poll::Ready(Some(Err(FlowyError::internal().with_context(err)))),
|
||||||
},
|
},
|
||||||
Some(Err(err)) => Poll::Ready(Some(Err(FlowyError::local_ai().with_context(err)))),
|
Some(Err(err)) => Poll::Ready(Some(Err(FlowyError::local_ai().with_context(err)))),
|
||||||
None => Poll::Ready(None),
|
None => Poll::Ready(None),
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ use crate::manager_observer::{
|
|||||||
ChildViewChangeReason,
|
ChildViewChangeReason,
|
||||||
};
|
};
|
||||||
use crate::notification::{
|
use crate::notification::{
|
||||||
send_notification, send_workspace_setting_notification, FolderNotification,
|
send_current_workspace_notification, send_notification, FolderNotification,
|
||||||
};
|
};
|
||||||
use crate::publish_util::{generate_publish_name, view_pb_to_publish_view};
|
use crate::publish_util::{generate_publish_name, view_pb_to_publish_view};
|
||||||
use crate::share::{ImportParams, ImportValue};
|
use crate::share::{ImportParams, ImportValue};
|
||||||
@ -978,7 +978,11 @@ impl FolderManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let workspace_id = self.user.workspace_id()?;
|
let workspace_id = self.user.workspace_id()?;
|
||||||
send_workspace_setting_notification(workspace_id, view);
|
let setting = WorkspaceSettingPB {
|
||||||
|
workspace_id,
|
||||||
|
latest_view: view,
|
||||||
|
};
|
||||||
|
send_current_workspace_notification(FolderNotification::DidUpdateWorkspaceSetting, setting);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,8 +2,6 @@ use flowy_derive::ProtoBuf_Enum;
|
|||||||
use flowy_notification::NotificationBuilder;
|
use flowy_notification::NotificationBuilder;
|
||||||
use lib_dispatch::prelude::ToBytes;
|
use lib_dispatch::prelude::ToBytes;
|
||||||
|
|
||||||
use crate::entities::{ViewPB, WorkspaceSettingPB};
|
|
||||||
|
|
||||||
const FOLDER_OBSERVABLE_SOURCE: &str = "Workspace";
|
const FOLDER_OBSERVABLE_SOURCE: &str = "Workspace";
|
||||||
|
|
||||||
#[derive(ProtoBuf_Enum, Debug, Default)]
|
#[derive(ProtoBuf_Enum, Debug, Default)]
|
||||||
@ -78,20 +76,8 @@ pub(crate) fn send_notification(id: &str, ty: FolderNotification) -> Notificatio
|
|||||||
/// The [CURRENT_WORKSPACE] represents as the current workspace that opened by the
|
/// The [CURRENT_WORKSPACE] represents as the current workspace that opened by the
|
||||||
/// user. Only one workspace can be opened at a time.
|
/// user. Only one workspace can be opened at a time.
|
||||||
const CURRENT_WORKSPACE: &str = "current-workspace";
|
const CURRENT_WORKSPACE: &str = "current-workspace";
|
||||||
pub(crate) fn send_workspace_notification<T: ToBytes>(ty: FolderNotification, payload: T) {
|
pub(crate) fn send_current_workspace_notification<T: ToBytes>(ty: FolderNotification, payload: T) {
|
||||||
send_notification(CURRENT_WORKSPACE, ty)
|
send_notification(CURRENT_WORKSPACE, ty)
|
||||||
.payload(payload)
|
.payload(payload)
|
||||||
.send();
|
.send();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn send_workspace_setting_notification(
|
|
||||||
workspace_id: String,
|
|
||||||
latest_view: Option<ViewPB>,
|
|
||||||
) -> Option<()> {
|
|
||||||
let setting = WorkspaceSettingPB {
|
|
||||||
workspace_id,
|
|
||||||
latest_view,
|
|
||||||
};
|
|
||||||
send_workspace_notification(FolderNotification::DidUpdateWorkspaceSetting, setting);
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
Reference in New Issue
Block a user