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
e51b0f90de
11
CHANGELOG.md
11
CHANGELOG.md
@ -1,4 +1,15 @@
|
||||
# Release Notes
|
||||
## Version 0.6.2 - 01/07/2024
|
||||
### New Features
|
||||
### Bug Fixes
|
||||
|
||||
## Version 0.6.1 - 22/06/2024
|
||||
### New Features
|
||||
- Introduced the "Space" feature to help you organize your pages more efficiently.
|
||||
### Bug Fixes
|
||||
- Resolved shortcut conflicts on the board page.
|
||||
- Resolved an issue where underscores could cause the editor to freeze.
|
||||
|
||||
## Version 0.6.0 - 19/06/2024
|
||||
### New Features
|
||||
- Introduced the "Space" feature to help you organize your pages more efficiently.
|
||||
|
@ -26,7 +26,7 @@ CARGO_MAKE_EXTEND_WORKSPACE_MAKEFILE = true
|
||||
CARGO_MAKE_CRATE_FS_NAME = "dart_ffi"
|
||||
CARGO_MAKE_CRATE_NAME = "dart-ffi"
|
||||
LIB_NAME = "dart_ffi"
|
||||
APPFLOWY_VERSION = "0.6.1"
|
||||
APPFLOWY_VERSION = "0.6.2"
|
||||
FLUTTER_DESKTOP_FEATURES = "dart"
|
||||
PRODUCT_NAME = "AppFlowy"
|
||||
MACOSX_DEPLOYMENT_TARGET = "11.0"
|
||||
|
@ -1,3 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/plugins/ai_chat/chat_page.dart';
|
||||
import 'package:appflowy/plugins/util.dart';
|
||||
@ -7,7 +9,6 @@ import 'package:appflowy/workspace/presentation/home/home_stack.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/tab_bar_item.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/view_title_bar.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class AIChatPluginBuilder extends PluginBuilder {
|
||||
@ -65,6 +66,13 @@ class AIChatPagePlugin extends Plugin {
|
||||
_viewInfoBloc = ViewInfoBloc(view: notifier.view)
|
||||
..add(const ViewInfoEvent.started());
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_viewInfoBloc.close();
|
||||
notifier.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
class AIChatPagePluginWidgetBuilder extends PluginWidgetBuilder
|
||||
|
@ -1,9 +1,10 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:appflowy/plugins/base/emoji/emoji_picker_header.dart';
|
||||
import 'package:appflowy/plugins/base/emoji/emoji_search_bar.dart';
|
||||
import 'package:appflowy/plugins/base/emoji/emoji_skin_tone.dart';
|
||||
import 'package:flowy_infra/size.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_emoji_mart/flutter_emoji_mart.dart';
|
||||
|
||||
// use a global value to store the selected emoji to prevent reloading every time.
|
||||
@ -37,9 +38,7 @@ class _FlowyEmojiPickerState extends State<FlowyEmojiPicker> {
|
||||
EmojiData.builtIn().then(
|
||||
(value) {
|
||||
kCachedEmojiData = value;
|
||||
setState(() {
|
||||
emojiData = value;
|
||||
});
|
||||
setState(() => emojiData = value);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:appflowy/plugins/database/application/field/field_controller.dart';
|
||||
import 'package:appflowy/plugins/database/application/view/view_cache.dart';
|
||||
import 'package:appflowy/plugins/database/domain/database_view_service.dart';
|
||||
@ -12,7 +14,6 @@ import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:appflowy_result/appflowy_result.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'defines.dart';
|
||||
import 'row/row_cache.dart';
|
||||
@ -223,6 +224,7 @@ class DatabaseController {
|
||||
_databaseCallbacks.clear();
|
||||
_groupCallbacks.clear();
|
||||
_layoutCallbacks.clear();
|
||||
_isLoading.dispose();
|
||||
}
|
||||
|
||||
Future<void> _loadGroups() async {
|
||||
|
@ -1,3 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart';
|
||||
@ -19,12 +21,12 @@ import 'package:flowy_infra/size.dart';
|
||||
import 'package:flowy_infra/theme_extension.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
import '../../application/row/row_controller.dart';
|
||||
import '../../widgets/row/row_detail.dart';
|
||||
|
||||
import 'calendar_day.dart';
|
||||
import 'layout/sizes.dart';
|
||||
import 'toolbar/calendar_setting_bar.dart';
|
||||
@ -90,12 +92,11 @@ class _CalendarPageState extends State<CalendarPage> {
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_calendarState = GlobalKey<MonthViewState>();
|
||||
_calendarBloc = CalendarBloc(
|
||||
databaseController: widget.databaseController,
|
||||
)..add(const CalendarEvent.initial());
|
||||
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
@ -378,13 +379,7 @@ class UnscheduledEventsButton extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _UnscheduledEventsButtonState extends State<UnscheduledEventsButton> {
|
||||
late final PopoverController _popoverController;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_popoverController = PopoverController();
|
||||
}
|
||||
final PopoverController _popoverController = PopoverController();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -30,6 +30,12 @@ class CalendarLayoutSetting extends StatefulWidget {
|
||||
class _CalendarLayoutSettingState extends State<CalendarLayoutSetting> {
|
||||
final PopoverMutex popoverMutex = PopoverMutex();
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
popoverMutex.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
|
@ -1,18 +1,19 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/database/grid/application/filter/checkbox_filter_editor_bloc.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/pop_up_action.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/checkbox_filter.pbenum.dart';
|
||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/checkbox_filter.pbenum.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import '../condition_button.dart';
|
||||
import '../disclosure_button.dart';
|
||||
import '../filter_info.dart';
|
||||
|
||||
import 'choicechip.dart';
|
||||
|
||||
class CheckboxFilterChoicechip extends StatefulWidget {
|
||||
@ -30,9 +31,9 @@ class _CheckboxFilterChoicechipState extends State<CheckboxFilterChoicechip> {
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
bloc = CheckboxFilterEditorBloc(filterInfo: widget.filterInfo)
|
||||
..add(const CheckboxFilterEditorEvent.initial());
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
@ -82,6 +83,12 @@ class CheckboxFilterEditor extends StatefulWidget {
|
||||
class _CheckboxFilterEditorState extends State<CheckboxFilterEditor> {
|
||||
final popoverMutex = PopoverMutex();
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
popoverMutex.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider.value(
|
||||
|
@ -1,12 +1,14 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/database/grid/application/filter/checklist_filter_bloc.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/pop_up_action.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/checklist_filter.pbenum.dart';
|
||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/checklist_filter.pbenum.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import '../../condition_button.dart';
|
||||
import '../../disclosure_button.dart';
|
||||
import '../../filter_info.dart';
|
||||
@ -23,20 +25,20 @@ class ChecklistFilterChoicechip extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _ChecklistFilterChoicechipState extends State<ChecklistFilterChoicechip> {
|
||||
late ChecklistFilterEditorBloc bloc;
|
||||
late PopoverMutex popoverMutex;
|
||||
late final ChecklistFilterEditorBloc bloc;
|
||||
final PopoverMutex popoverMutex = PopoverMutex();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
popoverMutex = PopoverMutex();
|
||||
super.initState();
|
||||
bloc = ChecklistFilterEditorBloc(filterInfo: widget.filterInfo);
|
||||
bloc.add(const ChecklistFilterEditorEvent.initial());
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
bloc.close();
|
||||
popoverMutex.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/database/grid/application/filter/number_filter_editor_bloc.dart';
|
||||
@ -6,12 +8,12 @@ import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import '../condition_button.dart';
|
||||
import '../disclosure_button.dart';
|
||||
import '../filter_info.dart';
|
||||
|
||||
import 'choicechip.dart';
|
||||
|
||||
class NumberFilterChoiceChip extends StatefulWidget {
|
||||
@ -64,6 +66,12 @@ class NumberFilterEditor extends StatefulWidget {
|
||||
class _NumberFilterEditorState extends State<NumberFilterEditor> {
|
||||
final popoverMutex = PopoverMutex();
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
popoverMutex.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<NumberFilterEditorBloc, NumberFilterEditorState>(
|
||||
|
@ -1,14 +1,16 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:appflowy/plugins/database/grid/application/filter/select_option_filter_bloc.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/select_option_filter.pb.dart';
|
||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import '../../disclosure_button.dart';
|
||||
import '../../filter_info.dart';
|
||||
import '../choicechip.dart';
|
||||
|
||||
import 'condition_list.dart';
|
||||
import 'option_list.dart';
|
||||
import 'select_option_loader.dart';
|
||||
@ -29,6 +31,7 @@ class _SelectOptionFilterChoicechipState
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
if (widget.filterInfo.fieldInfo.fieldType == FieldType.SingleSelect) {
|
||||
bloc = SelectOptionFilterEditorBloc(
|
||||
filterInfo: widget.filterInfo,
|
||||
@ -43,7 +46,6 @@ class _SelectOptionFilterChoicechipState
|
||||
);
|
||||
}
|
||||
bloc.add(const SelectOptionFilterEditorEvent.initial());
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
@ -90,6 +92,12 @@ class SelectOptionFilterEditor extends StatefulWidget {
|
||||
class _SelectOptionFilterEditorState extends State<SelectOptionFilterEditor> {
|
||||
final popoverMutex = PopoverMutex();
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
popoverMutex.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider.value(
|
||||
|
@ -1,3 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/database/grid/application/filter/text_filter_editor_bloc.dart';
|
||||
@ -6,12 +8,12 @@ import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import '../condition_button.dart';
|
||||
import '../disclosure_button.dart';
|
||||
import '../filter_info.dart';
|
||||
|
||||
import 'choicechip.dart';
|
||||
|
||||
class TextFilterChoicechip extends StatelessWidget {
|
||||
@ -72,6 +74,12 @@ class TextFilterEditor extends StatefulWidget {
|
||||
class _TextFilterEditorState extends State<TextFilterEditor> {
|
||||
final popoverMutex = PopoverMutex();
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
popoverMutex.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<TextFilterEditorBloc, TextFilterEditorState>(
|
||||
|
@ -1,3 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/database/grid/application/filter/time_filter_editor_bloc.dart';
|
||||
@ -6,12 +8,12 @@ import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import '../condition_button.dart';
|
||||
import '../disclosure_button.dart';
|
||||
import '../filter_info.dart';
|
||||
|
||||
import 'choicechip.dart';
|
||||
|
||||
class TimeFilterChoiceChip extends StatefulWidget {
|
||||
@ -64,6 +66,12 @@ class TimeFilterEditor extends StatefulWidget {
|
||||
class _TimeFilterEditorState extends State<TimeFilterEditor> {
|
||||
final popoverMutex = PopoverMutex();
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
popoverMutex.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<TimeFilterEditorBloc, TimeFilterEditorState>(
|
||||
|
@ -1,3 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/database/application/field/field_info.dart';
|
||||
@ -10,7 +12,6 @@ import 'package:flowy_infra_ui/style_widget/scrolling/styled_list.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/text_field.dart';
|
||||
import 'package:flowy_infra_ui/widget/spacing.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import '../../../../application/field/field_controller.dart';
|
||||
@ -35,15 +36,15 @@ class GridCreateFilterList extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _GridCreateFilterListState extends State<GridCreateFilterList> {
|
||||
late GridCreateFilterBloc editBloc;
|
||||
late final GridCreateFilterBloc editBloc;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
editBloc = GridCreateFilterBloc(
|
||||
viewId: widget.viewId,
|
||||
fieldController: widget.fieldController,
|
||||
)..add(const GridCreateFilterEvent.initial());
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -1,3 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/database/application/field/field_controller.dart';
|
||||
@ -5,9 +7,7 @@ import 'package:appflowy/plugins/database/grid/application/filter/filter_menu_bl
|
||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra/theme_extension.dart';
|
||||
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import 'create_filter_list.dart';
|
||||
@ -77,13 +77,7 @@ class AddFilterButton extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _AddFilterButtonState extends State<AddFilterButton> {
|
||||
late PopoverController popoverController;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
popoverController = PopoverController();
|
||||
super.initState();
|
||||
}
|
||||
final PopoverController popoverController = PopoverController();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -1,3 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/plugins/database/application/field/field_cell_bloc.dart';
|
||||
import 'package:appflowy/plugins/database/application/field/field_controller.dart';
|
||||
@ -9,7 +11,6 @@ import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
import 'package:flowy_infra/theme_extension.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import '../../layout/sizes.dart';
|
||||
@ -41,13 +42,12 @@ class GridFieldCell extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _GridFieldCellState extends State<GridFieldCell> {
|
||||
final PopoverController popoverController = PopoverController();
|
||||
late final FieldCellBloc _bloc;
|
||||
late PopoverController popoverController;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
popoverController = PopoverController();
|
||||
_bloc = FieldCellBloc(viewId: widget.viewId, fieldInfo: widget.fieldInfo);
|
||||
if (widget.isEditing) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
|
||||
|
@ -1,5 +1,7 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/database/grid/application/sort/sort_editor_bloc.dart';
|
||||
@ -7,10 +9,8 @@ import 'package:appflowy/plugins/database/grid/presentation/layout/sizes.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/sort_entities.pbenum.dart';
|
||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
|
||||
import 'package:flowy_infra/theme_extension.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import 'create_sort_list.dart';
|
||||
@ -28,6 +28,12 @@ class SortEditor extends StatefulWidget {
|
||||
class _SortEditorState extends State<SortEditor> {
|
||||
final popoverMutex = PopoverMutex();
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
popoverMutex.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<SortEditorBloc, SortEditorState>(
|
||||
|
@ -1,6 +1,10 @@
|
||||
import 'dart:collection';
|
||||
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/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/database/application/cell/bloc/select_option_cell_editor_bloc.dart';
|
||||
@ -10,14 +14,12 @@ import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra/theme_extension.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import '../../grid/presentation/layout/sizes.dart';
|
||||
import '../../grid/presentation/widgets/common/type_option_separator.dart';
|
||||
import '../field/type_option_editor/select/select_option_editor.dart';
|
||||
|
||||
import 'extension.dart';
|
||||
import 'select_option_text_field.dart';
|
||||
|
||||
@ -314,13 +316,7 @@ class _SelectOptionCell extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _SelectOptionCellState extends State<_SelectOptionCell> {
|
||||
late PopoverController _popoverController;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_popoverController = PopoverController();
|
||||
super.initState();
|
||||
}
|
||||
final _popoverController = PopoverController();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -370,13 +370,7 @@ class FieldDetailsEditor extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _FieldDetailsEditorState extends State<FieldDetailsEditor> {
|
||||
late PopoverMutex popoverMutex;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
popoverMutex = PopoverMutex();
|
||||
super.initState();
|
||||
}
|
||||
final PopoverMutex popoverMutex = PopoverMutex();
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
@ -575,10 +569,7 @@ class _FieldNameTextFieldState extends State<FieldNameTextField> {
|
||||
}
|
||||
|
||||
class SwitchFieldButton extends StatefulWidget {
|
||||
const SwitchFieldButton({
|
||||
super.key,
|
||||
required this.popoverMutex,
|
||||
});
|
||||
const SwitchFieldButton({super.key, required this.popoverMutex});
|
||||
|
||||
final PopoverMutex popoverMutex;
|
||||
|
||||
|
@ -1,5 +1,8 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/database/application/cell/cell_controller.dart';
|
||||
@ -18,13 +21,12 @@ import 'package:collection/collection.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra/theme_extension.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import 'accessory/cell_accessory.dart';
|
||||
import '../cell/editable_cell_builder.dart';
|
||||
|
||||
import 'accessory/cell_accessory.dart';
|
||||
|
||||
/// Display the row properties in a list. Only used in [RowDetailPage].
|
||||
class RowPropertyList extends StatelessWidget {
|
||||
const RowPropertyList({
|
||||
@ -363,15 +365,9 @@ class CreateRowFieldButton extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _CreateRowFieldButtonState extends State<CreateRowFieldButton> {
|
||||
late PopoverController popoverController;
|
||||
final PopoverController popoverController = PopoverController();
|
||||
FieldPB? createdField;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
popoverController = PopoverController();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AppFlowyPopover(
|
||||
|
@ -1,3 +1,5 @@
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
import 'package:appflowy/plugins/database/application/database_controller.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/layout/sizes.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/setting/database_setting_action.dart';
|
||||
@ -6,7 +8,6 @@ import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/scrolling/styled_list.dart';
|
||||
import 'package:flowy_infra_ui/widget/spacing.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
class DatabaseSettingsList extends StatefulWidget {
|
||||
const DatabaseSettingsList({
|
||||
@ -21,7 +22,13 @@ class DatabaseSettingsList extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _DatabaseSettingsListState extends State<DatabaseSettingsList> {
|
||||
late final PopoverMutex popoverMutex = PopoverMutex();
|
||||
final PopoverMutex popoverMutex = PopoverMutex();
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
popoverMutex.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -1,5 +1,7 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/plugins/database/application/field/field_controller.dart';
|
||||
import 'package:appflowy/plugins/database/application/field/field_info.dart';
|
||||
@ -13,7 +15,6 @@ import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flowy_infra/theme_extension.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class DatabasePropertyList extends StatefulWidget {
|
||||
@ -43,6 +44,12 @@ class _DatabasePropertyListState extends State<DatabasePropertyList> {
|
||||
)..add(const DatabasePropertyEvent.initial());
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_popoverMutex.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider<DatabasePropertyBloc>.value(
|
||||
|
@ -1,3 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/show_mobile_bottom_sheet.dart';
|
||||
@ -25,7 +27,6 @@ import 'package:collection/collection.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:fixnum/fixnum.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:nanoid/non_secure.dart';
|
||||
|
||||
@ -64,6 +65,12 @@ class _MentionDateBlockState extends State<MentionDateBlock> {
|
||||
late bool _includeTime = widget.includeTime;
|
||||
late DateTime? parsedDate = DateTime.tryParse(widget.date);
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
mutex.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (parsedDate == null) {
|
||||
|
@ -85,12 +85,32 @@ class InitAppWindowTask extends LaunchTask with WindowListener {
|
||||
Future<void> onWindowMaximize() async {
|
||||
super.onWindowMaximize();
|
||||
await windowSizeManager.setWindowMaximized(true);
|
||||
await windowSizeManager.setPosition(Offset.zero);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> onWindowUnmaximize() async {
|
||||
super.onWindowUnmaximize();
|
||||
await windowSizeManager.setWindowMaximized(false);
|
||||
|
||||
final position = await windowManager.getPosition();
|
||||
return windowSizeManager.setPosition(position);
|
||||
}
|
||||
|
||||
@override
|
||||
void onWindowEnterFullScreen() async {
|
||||
super.onWindowEnterFullScreen();
|
||||
await windowSizeManager.setWindowMaximized(true);
|
||||
await windowSizeManager.setPosition(Offset.zero);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> onWindowLeaveFullScreen() async {
|
||||
super.onWindowLeaveFullScreen();
|
||||
await windowSizeManager.setWindowMaximized(false);
|
||||
|
||||
final position = await windowManager.getPosition();
|
||||
return windowSizeManager.setPosition(position);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -109,7 +109,7 @@ class UserBackendService implements IUserBackendService {
|
||||
return UserEventOpenWorkspace(payload).send();
|
||||
}
|
||||
|
||||
Future<FlowyResult<WorkspacePB, FlowyError>> getCurrentWorkspace() {
|
||||
static Future<FlowyResult<WorkspacePB, FlowyError>> getCurrentWorkspace() {
|
||||
return FolderEventReadCurrentWorkspace().send().then((result) {
|
||||
return result.fold(
|
||||
(workspace) => FlowyResult.success(workspace),
|
||||
|
@ -67,8 +67,9 @@ class SettingsAIBloc extends Bloc<SettingsAIEvent, SettingsAIState> {
|
||||
bool? disableSearchIndexing,
|
||||
AIModelPB? model,
|
||||
}) {
|
||||
final payload =
|
||||
UpdateUserWorkspaceSettingPB(workspaceId: userProfile.workspaceId);
|
||||
final payload = UpdateUserWorkspaceSettingPB(
|
||||
workspaceId: userProfile.workspaceId,
|
||||
);
|
||||
if (disableSearchIndexing != null) {
|
||||
payload.disableSearchIndexing = disableSearchIndexing;
|
||||
}
|
||||
@ -82,17 +83,16 @@ class SettingsAIBloc extends Bloc<SettingsAIEvent, SettingsAIState> {
|
||||
FlowyResult<UserProfilePB, FlowyError> userProfileOrFailed,
|
||||
) =>
|
||||
userProfileOrFailed.fold(
|
||||
(newUserProfile) =>
|
||||
add(SettingsAIEvent.didReceiveUserProfile(newUserProfile)),
|
||||
(profile) => add(SettingsAIEvent.didReceiveUserProfile(profile)),
|
||||
(err) => Log.error(err),
|
||||
);
|
||||
|
||||
void _loadUserWorkspaceSetting() {
|
||||
final payload = UserWorkspaceIdPB(workspaceId: userProfile.workspaceId);
|
||||
UserEventGetWorkspaceSetting(payload).send().then((result) {
|
||||
result.fold((settins) {
|
||||
result.fold((settings) {
|
||||
if (!isClosed) {
|
||||
add(SettingsAIEvent.didLoadAISetting(settins));
|
||||
add(SettingsAIEvent.didLoadAISetting(settings));
|
||||
}
|
||||
}, (err) {
|
||||
Log.error(err);
|
||||
|
@ -117,6 +117,10 @@ class SettingsPlanBloc extends Bloc<SettingsPlanEvent, SettingsPlanState> {
|
||||
);
|
||||
},
|
||||
cancelSubscription: () async {
|
||||
final newState = state
|
||||
.mapOrNull(ready: (state) => state)
|
||||
?.copyWith(downgradeProcessing: true);
|
||||
emit(newState ?? state);
|
||||
await _userService.cancelSubscription(workspaceId);
|
||||
add(const SettingsPlanEvent.started());
|
||||
},
|
||||
@ -174,5 +178,6 @@ class SettingsPlanState with _$SettingsPlanState {
|
||||
required WorkspaceSubscriptionPB subscription,
|
||||
required BillingPortalPB? billingPortal,
|
||||
@Default(false) bool showSuccessDialog,
|
||||
@Default(false) bool downgradeProcessing,
|
||||
}) = _Ready;
|
||||
}
|
||||
|
@ -1,8 +1,20 @@
|
||||
import 'package:appflowy_backend/protobuf/flowy-user/workspace.pb.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
final _storageNumberFormat = NumberFormat()
|
||||
..maximumFractionDigits = 2
|
||||
..minimumFractionDigits = 0;
|
||||
|
||||
extension PresentableUsage on WorkspaceUsagePB {
|
||||
String get totalBlobInGb =>
|
||||
(totalBlobBytesLimit.toInt() / 1024 / 1024 / 1024).round().toString();
|
||||
|
||||
/// We use [NumberFormat] to format the current blob in GB.
|
||||
///
|
||||
/// Where the [totalBlobBytes] is the total blob bytes in bytes.
|
||||
/// And [NumberFormat.maximumFractionDigits] is set to 2.
|
||||
/// And [NumberFormat.minimumFractionDigits] is set to 0.
|
||||
///
|
||||
String get currentBlobInGb =>
|
||||
(totalBlobBytes.toInt() / 1024 / 1024 / 1024).round().toString();
|
||||
_storageNumberFormat.format(totalBlobBytes.toInt() / 1024 / 1024 / 1024);
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ class WorkspaceSettingsBloc
|
||||
|
||||
try {
|
||||
final currentWorkspace =
|
||||
await _userService!.getCurrentWorkspace().getOrThrow();
|
||||
await UserBackendService.getCurrentWorkspace().getOrThrow();
|
||||
|
||||
final workspaces =
|
||||
await _userService!.getWorkspaces().getOrThrow();
|
||||
|
@ -297,12 +297,11 @@ class SpaceBloc extends Bloc<SpaceEvent, SpaceState> {
|
||||
if (currentSpace == null) {
|
||||
return;
|
||||
}
|
||||
await ViewBackendService.duplicate(
|
||||
view: currentSpace,
|
||||
openAfterDuplicate: false,
|
||||
includeChildren: true,
|
||||
);
|
||||
add(const SpaceEvent.didReceiveSpaceUpdate());
|
||||
final newSpace = await _duplicateSpace(currentSpace);
|
||||
// open the duplicated space
|
||||
if (newSpace != null) {
|
||||
add(SpaceEvent.open(newSpace));
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
@ -602,6 +601,40 @@ class SpaceBloc extends Bloc<SpaceEvent, SpaceState> {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Future<ViewPB?> _duplicateSpace(ViewPB space) async {
|
||||
// if the space is not duplicated, try to create a new space
|
||||
final icon = space.icon.value.isNotEmpty
|
||||
? space.icon.value
|
||||
: builtInSpaceIcons.first;
|
||||
final iconColor = space.spaceIconColor ?? builtInSpaceColors.first;
|
||||
final newSpace = await _createSpace(
|
||||
name: '${space.name} (copy)',
|
||||
icon: icon,
|
||||
iconColor: iconColor,
|
||||
permission: space.spacePermission,
|
||||
);
|
||||
|
||||
if (newSpace == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (final view in space.childViews) {
|
||||
unawaited(
|
||||
ViewBackendService.duplicate(
|
||||
view: view,
|
||||
openAfterDuplicate: true,
|
||||
includeChildren: true,
|
||||
parentViewId: newSpace.id,
|
||||
suffix: '',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Log.info('Space duplicated: $newSpace');
|
||||
add(const SpaceEvent.didReceiveSpaceUpdate());
|
||||
return newSpace;
|
||||
}
|
||||
}
|
||||
|
||||
@freezed
|
||||
|
@ -1,5 +1,3 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/shared/feature_flags.dart';
|
||||
import 'package:appflowy/user/application/user_listener.dart';
|
||||
@ -12,6 +10,7 @@ import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
|
||||
import 'package:appflowy_result/appflowy_result.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:protobuf/protobuf.dart';
|
||||
@ -406,7 +405,7 @@ class UserWorkspaceBloc extends Bloc<UserWorkspaceEvent, UserWorkspaceState> {
|
||||
)> _fetchWorkspaces() async {
|
||||
try {
|
||||
final currentWorkspace =
|
||||
await _userService.getCurrentWorkspace().getOrThrow();
|
||||
await UserBackendService.getCurrentWorkspace().getOrThrow();
|
||||
final workspaces = await _userService.getWorkspaces().getOrThrow();
|
||||
if (workspaces.isEmpty) {
|
||||
workspaces.add(convertWorkspacePBToUserWorkspace(currentWorkspace));
|
||||
|
@ -141,11 +141,22 @@ class ViewBackendService {
|
||||
required bool openAfterDuplicate,
|
||||
// should include children views
|
||||
required bool includeChildren,
|
||||
String? parentViewId,
|
||||
String? suffix,
|
||||
}) {
|
||||
final payload = DuplicateViewPayloadPB.create()
|
||||
..viewId = view.id
|
||||
..openAfterDuplicate = openAfterDuplicate
|
||||
..includeChildren = includeChildren;
|
||||
|
||||
if (parentViewId != null) {
|
||||
payload.parentViewId = parentViewId;
|
||||
}
|
||||
|
||||
if (suffix != null) {
|
||||
payload.suffix = suffix;
|
||||
}
|
||||
|
||||
return FolderEventDuplicateView(payload).send();
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,7 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy/startup/tasks/app_window_size_manager.dart';
|
||||
import 'package:appflowy/workspace/application/home/home_setting_bloc.dart';
|
||||
@ -9,7 +11,6 @@ import 'package:appflowy/workspace/application/tabs/tabs_bloc.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/shared/sidebar_setting.dart';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-user/user_profile.pb.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hotkey_manager/hotkey_manager.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:scaled_app/scaled_app.dart';
|
||||
@ -186,14 +187,12 @@ class _HomeHotKeysState extends State<HomeHotKeys> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
_registerHotKeys(context);
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
|
||||
_registerHotKeys(context);
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,8 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:appflowy/workspace/application/tabs/tabs_bloc.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/home_sizes.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/tabs/flowy_tab.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class TabsManager extends StatefulWidget {
|
||||
|
@ -1,3 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy/user/application/notification_filter/notification_filter_bloc.dart';
|
||||
import 'package:appflowy/user/application/reminder/reminder_bloc.dart';
|
||||
@ -9,7 +11,6 @@ import 'package:appflowy/workspace/presentation/notifications/widgets/notificati
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-user/reminder.pb.dart';
|
||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class NotificationDialog extends StatefulWidget {
|
||||
@ -44,7 +45,7 @@ class _NotificationDialogState extends State<NotificationDialog>
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_mutex.close();
|
||||
_mutex.dispose();
|
||||
_controller.removeListener(_updateState);
|
||||
_controller.dispose();
|
||||
super.dispose();
|
||||
|
@ -1,3 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
@ -10,18 +12,27 @@ import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra/theme_extension.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class NotificationButton extends StatelessWidget {
|
||||
const NotificationButton({
|
||||
super.key,
|
||||
});
|
||||
class NotificationButton extends StatefulWidget {
|
||||
const NotificationButton({super.key});
|
||||
|
||||
@override
|
||||
State<NotificationButton> createState() => _NotificationButtonState();
|
||||
}
|
||||
|
||||
class _NotificationButtonState extends State<NotificationButton> {
|
||||
final mutex = PopoverMutex();
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
mutex.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final views = context.watch<SidebarSectionsBloc>().state.section.views;
|
||||
final mutex = PopoverMutex();
|
||||
|
||||
return BlocProvider<ReminderBloc>.value(
|
||||
value: getIt<ReminderBloc>(),
|
||||
|
@ -1,3 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_configuration.dart';
|
||||
@ -12,7 +14,6 @@ import 'package:fixnum/fixnum.dart';
|
||||
import 'package:flowy_infra/size.dart';
|
||||
import 'package:flowy_infra/theme_extension.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class NotificationItem extends StatefulWidget {
|
||||
@ -71,6 +72,12 @@ class _NotificationItemState extends State<NotificationItem> {
|
||||
infoString = _buildInfoString();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
mutex.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
String _buildInfoString() {
|
||||
String scheduledString =
|
||||
_scheduledString(widget.scheduled, widget.includeTime);
|
||||
|
@ -0,0 +1,115 @@
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/user/application/user_service.dart';
|
||||
import 'package:appflowy/workspace/application/view/view_ext.dart';
|
||||
import 'package:appflowy/workspace/application/view/view_service.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/shared/settings_category.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/shared/single_setting_action.dart';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:appflowy_result/appflowy_result.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class FixDataWidget extends StatelessWidget {
|
||||
const FixDataWidget({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SettingsCategory(
|
||||
title: LocaleKeys.settings_manageDataPage_data_fixYourData.tr(),
|
||||
children: [
|
||||
SingleSettingAction(
|
||||
labelMaxLines: 4,
|
||||
label: LocaleKeys.settings_manageDataPage_data_fixYourDataDescription
|
||||
.tr(),
|
||||
buttonLabel: LocaleKeys.settings_manageDataPage_data_fixButton.tr(),
|
||||
onPressed: () {
|
||||
FixDataManager.checkWorkspaceHealth(dryRun: true);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class FixDataManager {
|
||||
static Future<void> checkWorkspaceHealth({
|
||||
required bool dryRun,
|
||||
}) async {
|
||||
try {
|
||||
final currentWorkspace =
|
||||
await UserBackendService.getCurrentWorkspace().getOrThrow();
|
||||
// get all the views in the workspace
|
||||
final result = await ViewBackendService.getAllViews().getOrThrow();
|
||||
final allViews = result.items;
|
||||
|
||||
// dump all the views in the workspace
|
||||
dumpViews('all views', allViews);
|
||||
|
||||
// get the workspace
|
||||
final workspaces = allViews.where(
|
||||
(e) => e.parentViewId == '' && e.id == currentWorkspace.id,
|
||||
);
|
||||
dumpViews('workspaces', workspaces.toList());
|
||||
|
||||
if (workspaces.length != 1) {
|
||||
Log.error('Failed to fix workspace: workspace not found');
|
||||
// there should be only one workspace
|
||||
return;
|
||||
}
|
||||
|
||||
final workspace = workspaces.first;
|
||||
|
||||
// check the health of the spaces
|
||||
await checkSpaceHealth(workspace: workspace, allViews: allViews);
|
||||
|
||||
// add other checks here
|
||||
// ...
|
||||
} catch (e) {
|
||||
Log.error('Failed to fix space relation: $e');
|
||||
}
|
||||
}
|
||||
|
||||
static Future<void> checkSpaceHealth({
|
||||
required ViewPB workspace,
|
||||
required List<ViewPB> allViews,
|
||||
bool dryRun = true,
|
||||
}) async {
|
||||
try {
|
||||
final workspaceChildViews =
|
||||
await ViewBackendService.getChildViews(viewId: workspace.id)
|
||||
.getOrThrow();
|
||||
final workspaceChildViewIds =
|
||||
workspaceChildViews.map((e) => e.id).toSet();
|
||||
final spaces = allViews.where((e) => e.isSpace).toList();
|
||||
|
||||
//
|
||||
for (final space in spaces) {
|
||||
// the space is the top level view, so its parent view id should be the workspace id
|
||||
// and the workspace should have the space in its child views
|
||||
if (space.parentViewId != workspace.id ||
|
||||
!workspaceChildViewIds.contains(space.id)) {
|
||||
Log.info('found an issue: space is not in the workspace: $space');
|
||||
if (!dryRun) {
|
||||
// move the space to the workspace if it is not in the workspace
|
||||
await ViewBackendService.moveViewV2(
|
||||
viewId: space.id,
|
||||
newParentId: workspace.id,
|
||||
prevViewId: null,
|
||||
);
|
||||
}
|
||||
workspaceChildViewIds.add(space.id);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
Log.error('Failed to check space health: $e');
|
||||
}
|
||||
}
|
||||
|
||||
static void dumpViews(String prefix, List<ViewPB> views) {
|
||||
for (int i = 0; i < views.length; i++) {
|
||||
final view = views[i];
|
||||
Log.info('$prefix $i: $view)');
|
||||
}
|
||||
}
|
||||
}
|
@ -1,13 +1,13 @@
|
||||
import 'package:appflowy/workspace/presentation/settings/shared/af_dropdown_menu_entry.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/shared/settings_dropdown.dart';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/workspace/application/settings/ai/settings_ai_bloc.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/shared/af_dropdown_menu_entry.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/shared/settings_body.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/shared/settings_dropdown.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/toggle/toggle.dart';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||
import 'package:flowy_infra_ui/widget/spacing.dart';
|
||||
@ -153,11 +153,9 @@ class _AISearchToggle extends StatelessWidget {
|
||||
} else {
|
||||
return Toggle(
|
||||
value: state.enableSearchIndexing,
|
||||
onChanged: (_) {
|
||||
context.read<SettingsAIBloc>().add(
|
||||
const SettingsAIEvent.toggleAISearch(),
|
||||
);
|
||||
},
|
||||
onChanged: (_) => context
|
||||
.read<SettingsAIBloc>()
|
||||
.add(const SettingsAIEvent.toggleAISearch()),
|
||||
);
|
||||
}
|
||||
},
|
||||
|
@ -1,8 +1,5 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:appflowy/core/helpers/url_launcher.dart';
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
@ -13,6 +10,7 @@ import 'package:appflowy/util/theme_extension.dart';
|
||||
import 'package:appflowy/workspace/application/settings/setting_file_importer_bloc.dart';
|
||||
import 'package:appflowy/workspace/application/settings/settings_location_cubit.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/toast.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/pages/fix_data_widget.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/shared/setting_action.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/shared/settings_alert_dialog.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/shared/settings_body.dart';
|
||||
@ -29,6 +27,8 @@ import 'package:flowy_infra_ui/style_widget/button.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||
import 'package:flowy_infra_ui/widget/spacing.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
|
||||
@ -110,7 +110,10 @@ class SettingsManageDataView extends StatelessWidget {
|
||||
if (kDebugMode) ...[
|
||||
SettingsCategory(
|
||||
title: LocaleKeys.settings_files_exportData.tr(),
|
||||
children: const [SettingsExportFileWidget()],
|
||||
children: const [
|
||||
SettingsExportFileWidget(),
|
||||
FixDataWidget(),
|
||||
],
|
||||
),
|
||||
],
|
||||
SettingsCategory(
|
||||
|
@ -47,7 +47,7 @@ class _SettingsPlanComparisonDialogState
|
||||
Widget build(BuildContext context) {
|
||||
final isLM = Theme.of(context).isLightMode;
|
||||
|
||||
return BlocListener<SettingsPlanBloc, SettingsPlanState>(
|
||||
return BlocConsumer<SettingsPlanBloc, SettingsPlanState>(
|
||||
listener: (context, state) {
|
||||
final readyState = state.mapOrNull(ready: (state) => state);
|
||||
|
||||
@ -82,7 +82,7 @@ class _SettingsPlanComparisonDialogState
|
||||
currentSubscription = readyState.subscription;
|
||||
});
|
||||
},
|
||||
child: FlowyDialog(
|
||||
builder: (context, state) => FlowyDialog(
|
||||
constraints: const BoxConstraints(maxWidth: 784, minWidth: 674),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
@ -185,7 +185,16 @@ class _SettingsPlanComparisonDialogState
|
||||
canDowngrade:
|
||||
currentSubscription.subscriptionPlan !=
|
||||
SubscriptionPlanPB.None,
|
||||
currentCanceled: currentSubscription.hasCanceled,
|
||||
currentCanceled: currentSubscription.hasCanceled ||
|
||||
(context
|
||||
.watch<SettingsPlanBloc>()
|
||||
.state
|
||||
.mapOrNull(
|
||||
loading: (_) => true,
|
||||
ready: (state) =>
|
||||
state.downgradeProcessing,
|
||||
) ??
|
||||
false),
|
||||
onSelected: () async {
|
||||
if (currentSubscription.subscriptionPlan ==
|
||||
SubscriptionPlanPB.None ||
|
||||
@ -484,8 +493,9 @@ class _ActionButton extends StatelessWidget {
|
||||
cursor: onPressed != null
|
||||
? SystemMouseCursors.click
|
||||
: MouseCursor.defer,
|
||||
child: _drawGradientBorder(
|
||||
child: _drawBorder(
|
||||
isLM: isLM,
|
||||
isUpgrade: isUpgrade,
|
||||
child: Container(
|
||||
height: 36,
|
||||
width: 148,
|
||||
@ -496,9 +506,7 @@ class _ActionButton extends StatelessWidget {
|
||||
border: Border.all(color: Colors.transparent),
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
),
|
||||
child: Center(
|
||||
child: _drawText(label, isLM),
|
||||
),
|
||||
child: Center(child: _drawText(label, isLM, isUpgrade)),
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -509,13 +517,13 @@ class _ActionButton extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _drawText(String text, bool isLM) {
|
||||
Widget _drawText(String text, bool isLM, bool isUpgrade) {
|
||||
final child = FlowyText(
|
||||
text,
|
||||
fontSize: 14,
|
||||
lineHeight: 1.2,
|
||||
fontWeight: useGradientBorder ? FontWeight.w600 : FontWeight.w500,
|
||||
color: const Color(0xFFC49BEC),
|
||||
color: isUpgrade ? const Color(0xFFC49BEC) : null,
|
||||
);
|
||||
|
||||
if (!useGradientBorder || !isLM) {
|
||||
@ -536,18 +544,25 @@ class _ActionButton extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _drawGradientBorder({required bool isLM, required Widget child}) {
|
||||
Widget _drawBorder({
|
||||
required bool isLM,
|
||||
required bool isUpgrade,
|
||||
required Widget child,
|
||||
}) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(2),
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
transform: const GradientRotation(-1.2),
|
||||
stops: const [0.4, 1],
|
||||
colors: [
|
||||
isLM ? const Color(0xFF251D37) : const Color(0xFF7459AD),
|
||||
isLM ? const Color(0xFF7547C0) : const Color(0xFFDDC8FF),
|
||||
],
|
||||
),
|
||||
gradient: isUpgrade
|
||||
? LinearGradient(
|
||||
transform: const GradientRotation(-1.2),
|
||||
stops: const [0.4, 1],
|
||||
colors: [
|
||||
isLM ? const Color(0xFF251D37) : const Color(0xFF7459AD),
|
||||
isLM ? const Color(0xFF7547C0) : const Color(0xFFDDC8FF),
|
||||
],
|
||||
)
|
||||
: null,
|
||||
border: isUpgrade ? null : Border.all(),
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: child,
|
||||
|
@ -80,11 +80,30 @@ class SettingsPlanView extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class _CurrentPlanBox extends StatelessWidget {
|
||||
class _CurrentPlanBox extends StatefulWidget {
|
||||
const _CurrentPlanBox({required this.subscription});
|
||||
|
||||
final WorkspaceSubscriptionPB subscription;
|
||||
|
||||
@override
|
||||
State<_CurrentPlanBox> createState() => _CurrentPlanBoxState();
|
||||
}
|
||||
|
||||
class _CurrentPlanBoxState extends State<_CurrentPlanBox> {
|
||||
late SettingsPlanBloc planBloc;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
planBloc = context.read<SettingsPlanBloc>();
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
planBloc = context.read<SettingsPlanBloc>();
|
||||
super.didChangeDependencies();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Stack(
|
||||
@ -105,13 +124,13 @@ class _CurrentPlanBox extends StatelessWidget {
|
||||
children: [
|
||||
const VSpace(4),
|
||||
FlowyText.semibold(
|
||||
subscription.label,
|
||||
widget.subscription.label,
|
||||
fontSize: 24,
|
||||
color: AFThemeExtension.of(context).strongText,
|
||||
),
|
||||
const VSpace(8),
|
||||
FlowyText.regular(
|
||||
subscription.info,
|
||||
widget.subscription.info,
|
||||
fontSize: 16,
|
||||
color: AFThemeExtension.of(context).strongText,
|
||||
maxLines: 3,
|
||||
@ -124,10 +143,10 @@ class _CurrentPlanBox extends StatelessWidget {
|
||||
onPressed: () => _openPricingDialog(
|
||||
context,
|
||||
context.read<SettingsPlanBloc>().workspaceId,
|
||||
subscription,
|
||||
widget.subscription,
|
||||
),
|
||||
),
|
||||
if (subscription.hasCanceled) ...[
|
||||
if (widget.subscription.hasCanceled) ...[
|
||||
const VSpace(12),
|
||||
FlowyText(
|
||||
LocaleKeys
|
||||
@ -149,10 +168,10 @@ class _CurrentPlanBox extends StatelessWidget {
|
||||
separatorBuilder: () => const VSpace(4),
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
..._getPros(subscription.subscriptionPlan).map(
|
||||
..._getPros(widget.subscription.subscriptionPlan).map(
|
||||
(s) => _ProConItem(label: s),
|
||||
),
|
||||
..._getCons(subscription.subscriptionPlan).map(
|
||||
..._getCons(widget.subscription.subscriptionPlan).map(
|
||||
(s) => _ProConItem(label: s, isPro: false),
|
||||
),
|
||||
],
|
||||
@ -185,7 +204,7 @@ class _CurrentPlanBox extends StatelessWidget {
|
||||
String _canceledDate(BuildContext context) {
|
||||
final appearance = context.read<AppearanceSettingsCubit>().state;
|
||||
return appearance.dateFormat.formatDate(
|
||||
subscription.canceledAt.toDateTime(),
|
||||
widget.subscription.canceledAt.toDateTime(),
|
||||
true,
|
||||
appearance.timeFormat,
|
||||
);
|
||||
@ -199,7 +218,7 @@ class _CurrentPlanBox extends StatelessWidget {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (_) => BlocProvider<SettingsPlanBloc>.value(
|
||||
value: context.read<SettingsPlanBloc>(),
|
||||
value: planBloc,
|
||||
child: SettingsPlanComparisonDialog(
|
||||
workspaceId: workspaceId,
|
||||
subscription: subscription,
|
||||
@ -224,6 +243,7 @@ class _CurrentPlanBox extends StatelessWidget {
|
||||
LocaleKeys.settings_planPage_planUsage_currentPlan_freeProFour.tr(),
|
||||
LocaleKeys.settings_planPage_planUsage_currentPlan_freeProFive.tr(),
|
||||
];
|
||||
|
||||
List<String> _freeCons() => [
|
||||
LocaleKeys.settings_planPage_planUsage_currentPlan_freeConOne.tr(),
|
||||
LocaleKeys.settings_planPage_planUsage_currentPlan_freeConTwo.tr(),
|
||||
@ -242,6 +262,7 @@ class _CurrentPlanBox extends StatelessWidget {
|
||||
LocaleKeys.settings_planPage_planUsage_currentPlan_professionalProFive
|
||||
.tr(),
|
||||
];
|
||||
|
||||
List<String> _proCons() => [
|
||||
LocaleKeys.settings_planPage_planUsage_currentPlan_professionalConOne
|
||||
.tr(),
|
||||
@ -314,6 +335,11 @@ class _PlanUsageSummary extends StatelessWidget {
|
||||
Expanded(
|
||||
child: _UsageBox(
|
||||
title: LocaleKeys.settings_planPage_planUsage_storageLabel.tr(),
|
||||
replacementText: subscription.subscriptionPlan ==
|
||||
SubscriptionPlanPB.Pro
|
||||
? LocaleKeys.settings_planPage_planUsage_storageUnlimited
|
||||
.tr()
|
||||
: null,
|
||||
label: LocaleKeys.settings_planPage_planUsage_storageUsage.tr(
|
||||
args: [
|
||||
usage.currentBlobInGb,
|
||||
@ -372,12 +398,16 @@ class _UsageBox extends StatelessWidget {
|
||||
required this.title,
|
||||
required this.label,
|
||||
required this.value,
|
||||
this.replacementText,
|
||||
});
|
||||
|
||||
final String title;
|
||||
final String label;
|
||||
final double value;
|
||||
|
||||
/// Replaces the progress indicator if not null
|
||||
final String? replacementText;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
@ -388,7 +418,21 @@ class _UsageBox extends StatelessWidget {
|
||||
fontSize: 11,
|
||||
color: AFThemeExtension.of(context).secondaryTextColor,
|
||||
),
|
||||
_PlanProgressIndicator(label: label, progress: value),
|
||||
if (replacementText != null) ...[
|
||||
Row(
|
||||
children: [
|
||||
Flexible(
|
||||
child: FlowyText.medium(
|
||||
replacementText!,
|
||||
fontSize: 11,
|
||||
color: AFThemeExtension.of(context).secondaryTextColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
] else ...[
|
||||
_PlanProgressIndicator(label: label, progress: value),
|
||||
],
|
||||
],
|
||||
);
|
||||
}
|
||||
|
@ -1,11 +1,12 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/base/emoji/emoji_picker.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/base/selectable_svg_widget.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/decoration.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
SelectionMenuItem emojiMenuItem = SelectionMenuItem(
|
||||
getName: LocaleKeys.document_plugins_emoji.tr,
|
||||
@ -85,8 +86,8 @@ class EmojiSelectionMenu extends StatefulWidget {
|
||||
class _EmojiSelectionMenuState extends State<EmojiSelectionMenu> {
|
||||
@override
|
||||
void initState() {
|
||||
HardwareKeyboard.instance.addHandler(_handleGlobalKeyEvent);
|
||||
super.initState();
|
||||
HardwareKeyboard.instance.addHandler(_handleGlobalKeyEvent);
|
||||
}
|
||||
|
||||
bool _handleGlobalKeyEvent(KeyEvent event) {
|
||||
@ -95,9 +96,8 @@ class _EmojiSelectionMenuState extends State<EmojiSelectionMenu> {
|
||||
//triggers on esc
|
||||
widget.onExit();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@override
|
||||
@ -106,17 +106,10 @@ class _EmojiSelectionMenuState extends State<EmojiSelectionMenu> {
|
||||
super.deactivate();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FlowyEmojiPicker(
|
||||
onEmojiSelected: (_, emoji) {
|
||||
widget.onSubmitted(emoji);
|
||||
},
|
||||
onEmojiSelected: (_, emoji) => widget.onSubmitted(emoji),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'emoji_picker.dart';
|
||||
import 'emoji_picker_builder.dart';
|
||||
import 'models/emoji_category_models.dart';
|
||||
@ -29,8 +31,10 @@ class DefaultEmojiPickerViewState extends State<DefaultEmojiPickerView>
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
var initCategory = widget.state.emojiCategoryGroupList.indexWhere(
|
||||
(element) => element.category == widget.config.initCategory,
|
||||
super.initState();
|
||||
|
||||
int initCategory = widget.state.emojiCategoryGroupList.indexWhere(
|
||||
(el) => el.category == widget.config.initCategory,
|
||||
);
|
||||
if (initCategory == -1) {
|
||||
initCategory = 0;
|
||||
@ -42,27 +46,23 @@ class DefaultEmojiPickerViewState extends State<DefaultEmojiPickerView>
|
||||
);
|
||||
_pageController = PageController(initialPage: initCategory);
|
||||
_emojiFocusNode.requestFocus();
|
||||
|
||||
_emojiController.addListener(() {
|
||||
final String query = _emojiController.text.toLowerCase();
|
||||
if (query.isEmpty) {
|
||||
searchEmojiList.emoji.clear();
|
||||
_pageController!.jumpToPage(
|
||||
_tabController!.index,
|
||||
);
|
||||
_pageController!.jumpToPage(_tabController!.index);
|
||||
} else {
|
||||
searchEmojiList.emoji.clear();
|
||||
for (final element in widget.state.emojiCategoryGroupList) {
|
||||
searchEmojiList.emoji.addAll(
|
||||
element.emoji.where((item) {
|
||||
return item.name.toLowerCase().contains(query);
|
||||
}).toList(),
|
||||
element.emoji
|
||||
.where((item) => item.name.toLowerCase().contains(query))
|
||||
.toList(),
|
||||
);
|
||||
}
|
||||
}
|
||||
setState(() {});
|
||||
});
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
@ -81,25 +81,17 @@ class DefaultEmojiPickerViewState extends State<DefaultEmojiPickerView>
|
||||
type: MaterialType.transparency,
|
||||
child: IconButton(
|
||||
padding: const EdgeInsets.only(bottom: 2),
|
||||
icon: Icon(
|
||||
Icons.backspace,
|
||||
color: widget.config.backspaceColor,
|
||||
),
|
||||
onPressed: () {
|
||||
widget.state.onBackspacePressed!();
|
||||
},
|
||||
icon: Icon(Icons.backspace, color: widget.config.backspaceColor),
|
||||
onPressed: () => widget.state.onBackspacePressed!(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
bool isEmojiSearching() {
|
||||
final bool result =
|
||||
searchEmojiList.emoji.isNotEmpty || _emojiController.text.isNotEmpty;
|
||||
|
||||
return result;
|
||||
}
|
||||
bool isEmojiSearching() =>
|
||||
searchEmojiList.emoji.isNotEmpty || _emojiController.text.isNotEmpty;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -213,15 +205,9 @@ class DefaultEmojiPickerViewState extends State<DefaultEmojiPickerView>
|
||||
required Widget child,
|
||||
}) {
|
||||
if (widget.config.buttonMode == ButtonMode.MATERIAL) {
|
||||
return InkWell(
|
||||
onTap: onPressed,
|
||||
child: child,
|
||||
);
|
||||
return InkWell(onTap: onPressed, child: child);
|
||||
}
|
||||
return GestureDetector(
|
||||
onTap: onPressed,
|
||||
child: child,
|
||||
);
|
||||
return GestureDetector(onTap: onPressed, child: child);
|
||||
}
|
||||
|
||||
Widget _buildPage(double emojiSize, EmojiCategoryGroup emojiCategoryGroup) {
|
||||
@ -275,9 +261,7 @@ class DefaultEmojiPickerViewState extends State<DefaultEmojiPickerView>
|
||||
child: FittedBox(
|
||||
child: Text(
|
||||
emoji.emoji,
|
||||
style: TextStyle(
|
||||
fontSize: emojiSize,
|
||||
),
|
||||
style: TextStyle(fontSize: emojiSize),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -1,3 +1,7 @@
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import 'package:appflowy/core/helpers/url_launcher.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/workspace/application/settings/supabase_cloud_setting_bloc.dart';
|
||||
@ -15,9 +19,6 @@ import 'package:flowy_infra/theme_extension.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flowy_infra_ui/widget/error_page.dart';
|
||||
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class SettingSupabaseCloudView extends StatelessWidget {
|
||||
@ -265,13 +266,7 @@ class SupabaseInput extends StatefulWidget {
|
||||
}
|
||||
|
||||
class SupabaseInputState extends State<SupabaseInput> {
|
||||
late TextEditingController _controller;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_controller = TextEditingController(text: widget.url);
|
||||
}
|
||||
late final _controller = TextEditingController(text: widget.url);
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
|
@ -29,6 +29,12 @@ class _DateTimeSettingState extends State<DateTimeSetting> {
|
||||
final timeSettingPopoverMutex = PopoverMutex();
|
||||
String? overlayIdentifier;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
timeSettingPopoverMutex.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final List<Widget> children = [
|
||||
|
@ -129,11 +129,6 @@ class NavigatorAlertDialog extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _CreateFlowyAlertDialog extends State<NavigatorAlertDialog> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return StyledDialog(
|
||||
|
@ -1,7 +1,8 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:styled_widget/styled_widget.dart';
|
||||
|
||||
class PopoverActionList<T extends PopoverAction> extends StatefulWidget {
|
||||
@ -42,12 +43,12 @@ class PopoverActionList<T extends PopoverAction> extends StatefulWidget {
|
||||
|
||||
class _PopoverActionListState<T extends PopoverAction>
|
||||
extends State<PopoverActionList<T>> {
|
||||
late PopoverController popoverController;
|
||||
final PopoverController popoverController = PopoverController();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
popoverController = PopoverController();
|
||||
super.initState();
|
||||
void dispose() {
|
||||
popoverController.close();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -1,7 +1,8 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:appflowy/workspace/application/view/view_listener.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ViewTabBarItem extends StatefulWidget {
|
||||
const ViewTabBarItem({super.key, required this.view});
|
||||
@ -37,7 +38,5 @@ class _ViewTabBarItemState extends State<ViewTabBarItem> {
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FlowyText.medium(view.name);
|
||||
}
|
||||
Widget build(BuildContext context) => FlowyText.medium(view.name);
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import 'package:appflowy_backend/appflowy_backend.dart';
|
||||
|
||||
void main() {
|
||||
@ -36,21 +37,15 @@ class _MyAppState extends State<MyApp> {
|
||||
// message was in flight, we want to discard the reply rather than calling
|
||||
// setState to update our non-existent appearance.
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_platformVersion = platformVersion;
|
||||
});
|
||||
setState(() => _platformVersion = platformVersion);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
home: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Plugin example app'),
|
||||
),
|
||||
body: Center(
|
||||
child: Text('Running on: $_platformVersion\n'),
|
||||
),
|
||||
appBar: AppBar(title: const Text('Plugin example app')),
|
||||
body: Center(child: Text('Running on: $_platformVersion\n')),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
import 'package:appflowy_popover/src/layout.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import 'package:appflowy_popover/src/layout.dart';
|
||||
|
||||
import 'mask.dart';
|
||||
import 'mutex.dart';
|
||||
|
||||
@ -177,9 +178,7 @@ class PopoverState extends State<Popover> {
|
||||
_rootEntry.addEntry(context, this, newEntry, widget.asBarrier);
|
||||
}
|
||||
|
||||
void close({
|
||||
bool notify = true,
|
||||
}) {
|
||||
void close({bool notify = true}) {
|
||||
if (_rootEntry.contains(this)) {
|
||||
_rootEntry.removeEntry(this);
|
||||
if (notify) {
|
||||
@ -286,9 +285,7 @@ class PopoverContainer extends StatefulWidget {
|
||||
if (context is StatefulElement && context.state is PopoverContainerState) {
|
||||
return context.state as PopoverContainerState;
|
||||
}
|
||||
final PopoverContainerState? result =
|
||||
context.findAncestorStateOfType<PopoverContainerState>();
|
||||
return result!;
|
||||
return context.findAncestorStateOfType<PopoverContainerState>()!;
|
||||
}
|
||||
|
||||
static PopoverContainerState? maybeOf(BuildContext context) {
|
||||
|
@ -2,10 +2,11 @@
|
||||
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flowy_infra_ui/src/flowy_overlay/layout.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import 'package:flowy_infra_ui/src/flowy_overlay/layout.dart';
|
||||
|
||||
/// Specifies how overlay are anchored to the SourceWidget
|
||||
enum AnchorDirection {
|
||||
// Corner aligned with a corner of the SourceWidget
|
||||
@ -341,18 +342,17 @@ class FlowyOverlayState extends State<FlowyOverlay> {
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_keyboardShortcutBindings.addAll({
|
||||
LogicalKeySet(LogicalKeyboardKey.escape): (identifier) {
|
||||
remove(identifier);
|
||||
},
|
||||
});
|
||||
super.initState();
|
||||
_keyboardShortcutBindings.addAll({
|
||||
LogicalKeySet(LogicalKeyboardKey.escape): (identifier) =>
|
||||
remove(identifier),
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final overlays = _overlayList.map((item) {
|
||||
var widget = item.widget;
|
||||
Widget widget = item.widget;
|
||||
|
||||
// requestFocus will cause the children weird focus behaviors.
|
||||
// item.focusNode.requestFocus();
|
||||
@ -390,15 +390,11 @@ class FlowyOverlayState extends State<FlowyOverlay> {
|
||||
return MaterialApp(
|
||||
theme: Theme.of(context),
|
||||
debugShowCheckedModeBanner: false,
|
||||
home: Stack(
|
||||
children: children..addAll(overlays),
|
||||
),
|
||||
home: Stack(children: children..addAll(overlays)),
|
||||
);
|
||||
}
|
||||
|
||||
void _handleTapOnBackground() {
|
||||
removeAll();
|
||||
}
|
||||
void _handleTapOnBackground() => removeAll();
|
||||
|
||||
Widget? _renderBackground(List<Widget> overlays) {
|
||||
Widget? child;
|
||||
|
@ -36,13 +36,7 @@ class StyledListView extends StatefulWidget {
|
||||
|
||||
/// State is public so this can easily be controlled externally
|
||||
class StyledListViewState extends State<StyledListView> {
|
||||
late ScrollController scrollController;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
scrollController = ScrollController();
|
||||
super.initState();
|
||||
}
|
||||
final scrollController = ScrollController();
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
@ -50,15 +44,6 @@ class StyledListViewState extends State<StyledListView> {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(StyledListView oldWidget) {
|
||||
if (oldWidget.itemCount != widget.itemCount ||
|
||||
oldWidget.itemExtent != widget.itemExtent) {
|
||||
setState(() {});
|
||||
}
|
||||
super.didUpdateWidget(oldWidget);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final contentSize = (widget.itemCount ?? 0.0) * (widget.itemExtent ?? 00.0);
|
||||
@ -75,7 +60,7 @@ class StyledListViewState extends State<StyledListView> {
|
||||
controller: scrollController,
|
||||
itemExtent: widget.itemExtent,
|
||||
itemCount: widget.itemCount,
|
||||
itemBuilder: (c, i) => widget.itemBuilder(c, i),
|
||||
itemBuilder: widget.itemBuilder,
|
||||
),
|
||||
);
|
||||
return listContent;
|
||||
|
@ -137,7 +137,6 @@ class FlowyTextFieldState extends State<FlowyTextField> {
|
||||
void _onSubmitted(String text) {
|
||||
widget.onSubmitted?.call(text);
|
||||
if (widget.autoClearWhenDone) {
|
||||
// using `controller.clear()` instead of `controller.text = ''` which will crash on Windows.
|
||||
controller.clear();
|
||||
}
|
||||
}
|
||||
@ -154,7 +153,7 @@ class FlowyTextFieldState extends State<FlowyTextField> {
|
||||
_onChanged(text);
|
||||
}
|
||||
},
|
||||
onSubmitted: (text) => _onSubmitted(text),
|
||||
onSubmitted: _onSubmitted,
|
||||
onEditingComplete: widget.onEditingComplete,
|
||||
minLines: 1,
|
||||
maxLines: widget.maxLines,
|
||||
|
@ -1,10 +1,11 @@
|
||||
import 'dart:async';
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:flowy_infra/size.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import 'package:flowy_infra/size.dart';
|
||||
|
||||
class FlowyFormTextInput extends StatelessWidget {
|
||||
static EdgeInsets kDefaultTextInputPadding =
|
||||
EdgeInsets.only(bottom: Insets.sm, top: 4);
|
||||
@ -162,10 +163,12 @@ class StyledSearchTextInputState extends State<StyledSearchTextInput> {
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_controller =
|
||||
widget.controller ?? TextEditingController(text: widget.initialValue);
|
||||
_focusNode = FocusNode(
|
||||
debugLabel: widget.label ?? '',
|
||||
debugLabel: widget.label,
|
||||
canRequestFocus: true,
|
||||
onKeyEvent: (node, event) {
|
||||
if (event.logicalKey == LogicalKeyboardKey.escape) {
|
||||
widget.onEditingCancel?.call();
|
||||
@ -173,7 +176,6 @@ class StyledSearchTextInputState extends State<StyledSearchTextInput> {
|
||||
}
|
||||
return KeyEventResult.ignored;
|
||||
},
|
||||
canRequestFocus: true,
|
||||
);
|
||||
// Listen for focus out events
|
||||
_focusNode
|
||||
@ -182,7 +184,6 @@ class StyledSearchTextInputState extends State<StyledSearchTextInput> {
|
||||
if (widget.autoFocus ?? false) {
|
||||
scheduleMicrotask(() => _focusNode.requestFocus());
|
||||
}
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
@ -292,8 +293,10 @@ class ThinUnderlineBorder extends InputBorder {
|
||||
bool get isOutline => false;
|
||||
|
||||
@override
|
||||
UnderlineInputBorder copyWith(
|
||||
{BorderSide? borderSide, BorderRadius? borderRadius}) {
|
||||
UnderlineInputBorder copyWith({
|
||||
BorderSide? borderSide,
|
||||
BorderRadius? borderRadius,
|
||||
}) {
|
||||
return UnderlineInputBorder(
|
||||
borderSide: borderSide ?? this.borderSide,
|
||||
borderRadius: borderRadius ?? this.borderRadius,
|
||||
@ -301,14 +304,12 @@ class ThinUnderlineBorder extends InputBorder {
|
||||
}
|
||||
|
||||
@override
|
||||
EdgeInsetsGeometry get dimensions {
|
||||
return EdgeInsets.only(bottom: borderSide.width);
|
||||
}
|
||||
EdgeInsetsGeometry get dimensions =>
|
||||
EdgeInsets.only(bottom: borderSide.width);
|
||||
|
||||
@override
|
||||
UnderlineInputBorder scale(double t) {
|
||||
return UnderlineInputBorder(borderSide: borderSide.scale(t));
|
||||
}
|
||||
UnderlineInputBorder scale(double t) =>
|
||||
UnderlineInputBorder(borderSide: borderSide.scale(t));
|
||||
|
||||
@override
|
||||
Path getInnerPath(Rect rect, {TextDirection? textDirection}) {
|
||||
|
@ -1,9 +1,10 @@
|
||||
import 'package:flowy_infra/size.dart';
|
||||
import 'package:flowy_infra_ui/widget/rounded_button.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flowy_infra/time/duration.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import 'package:flowy_infra/size.dart';
|
||||
import 'package:flowy_infra/time/duration.dart';
|
||||
import 'package:flowy_infra_ui/widget/rounded_button.dart';
|
||||
|
||||
class RoundedInputField extends StatefulWidget {
|
||||
final String? hintText;
|
||||
final bool obscureText;
|
||||
@ -60,33 +61,26 @@ class RoundedInputField extends StatefulWidget {
|
||||
|
||||
class _RoundedInputFieldState extends State<RoundedInputField> {
|
||||
String inputText = "";
|
||||
bool obscuteText = false;
|
||||
bool obscureText = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
obscuteText = widget.obscureText;
|
||||
if (widget.controller != null) {
|
||||
inputText = widget.controller!.text;
|
||||
} else {
|
||||
inputText = widget.initialValue ?? "";
|
||||
}
|
||||
|
||||
super.initState();
|
||||
obscureText = widget.obscureText;
|
||||
inputText = widget.controller != null
|
||||
? widget.controller!.text
|
||||
: widget.initialValue ?? "";
|
||||
}
|
||||
|
||||
String? _suffixText() {
|
||||
if (widget.maxLength != null) {
|
||||
return ' ${widget.controller!.text.length}/${widget.maxLength}';
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
String? _suffixText() => widget.maxLength != null
|
||||
? ' ${widget.controller!.text.length}/${widget.maxLength}'
|
||||
: null;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var borderColor =
|
||||
Color borderColor =
|
||||
widget.normalBorderColor ?? Theme.of(context).colorScheme.outline;
|
||||
var focusBorderColor =
|
||||
Color focusBorderColor =
|
||||
widget.focusBorderColor ?? Theme.of(context).colorScheme.primary;
|
||||
|
||||
if (widget.errorText.isNotEmpty) {
|
||||
@ -122,7 +116,7 @@ class _RoundedInputFieldState extends State<RoundedInputField> {
|
||||
},
|
||||
cursorColor:
|
||||
widget.cursorColor ?? Theme.of(context).colorScheme.primary,
|
||||
obscureText: obscuteText,
|
||||
obscureText: obscureText,
|
||||
style: widget.style ?? Theme.of(context).textTheme.bodyMedium,
|
||||
decoration: InputDecoration(
|
||||
contentPadding: widget.contentPadding,
|
||||
@ -134,17 +128,11 @@ class _RoundedInputFieldState extends State<RoundedInputField> {
|
||||
suffixText: _suffixText(),
|
||||
counterText: "",
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: borderColor,
|
||||
width: 1.0,
|
||||
),
|
||||
borderSide: BorderSide(color: borderColor, width: 1.0),
|
||||
borderRadius: Corners.s10Border,
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: focusBorderColor,
|
||||
width: 1.0,
|
||||
),
|
||||
borderSide: BorderSide(color: focusBorderColor, width: 1.0),
|
||||
borderRadius: Corners.s10Border,
|
||||
),
|
||||
suffixIcon: obscureIcon(),
|
||||
@ -186,19 +174,11 @@ class _RoundedInputFieldState extends State<RoundedInputField> {
|
||||
}
|
||||
|
||||
assert(widget.obscureIcon != null && widget.obscureHideIcon != null);
|
||||
Widget? icon;
|
||||
if (obscuteText) {
|
||||
icon = widget.obscureIcon!;
|
||||
} else {
|
||||
icon = widget.obscureHideIcon!;
|
||||
}
|
||||
final icon = obscureText ? widget.obscureIcon! : widget.obscureHideIcon!;
|
||||
|
||||
return RoundedImageButton(
|
||||
size: iconWidth,
|
||||
press: () {
|
||||
obscuteText = !obscuteText;
|
||||
setState(() {});
|
||||
},
|
||||
press: () => setState(() => obscureText = !obscureText),
|
||||
child: icon,
|
||||
);
|
||||
}
|
||||
|
@ -53,11 +53,11 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "."
|
||||
ref: "64c0be8"
|
||||
resolved-ref: "64c0be88a113c2eece5512701527e7d11b8c9239"
|
||||
ref: e8ee051
|
||||
resolved-ref: e8ee051719eded6621ccdc2722f696411c020209
|
||||
url: "https://github.com/AppFlowy-IO/appflowy-editor.git"
|
||||
source: git
|
||||
version: "2.5.1"
|
||||
version: "3.0.0"
|
||||
appflowy_editor_plugins:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -15,7 +15,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
|
||||
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
|
||||
# Read more about iOS versioning at
|
||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||
version: 0.6.1
|
||||
version: 0.6.2
|
||||
|
||||
environment:
|
||||
flutter: ">=3.22.0"
|
||||
@ -186,7 +186,7 @@ dependency_overrides:
|
||||
appflowy_editor:
|
||||
git:
|
||||
url: https://github.com/AppFlowy-IO/appflowy-editor.git
|
||||
ref: "64c0be8"
|
||||
ref: "e8ee051"
|
||||
|
||||
appflowy_editor_plugins:
|
||||
git:
|
||||
|
@ -59,7 +59,7 @@ class AppFlowyUnitTest {
|
||||
|
||||
WorkspacePB get currentWorkspace => workspace;
|
||||
Future<void> _loadWorkspace() async {
|
||||
final result = await userService.getCurrentWorkspace();
|
||||
final result = await UserBackendService.getCurrentWorkspace();
|
||||
result.fold(
|
||||
(value) => workspace = value,
|
||||
(error) {
|
||||
|
@ -410,17 +410,6 @@
|
||||
"pleaseInputYourStabilityAIKey": "يرجى إدخال رمز Stability AI الخاص بك",
|
||||
"clickToLogout": "انقر لتسجيل خروج المستخدم الحالي"
|
||||
},
|
||||
"shortcuts": {
|
||||
"shortcutsLabel": "الاختصارات",
|
||||
"command": "امر",
|
||||
"keyBinding": "ربط المفاتيح",
|
||||
"addNewCommand": "إضافة أمر جديد",
|
||||
"updateShortcutStep": "اضغط على مجموعة المفاتيح المطلوبة ثم اضغط على ENTER",
|
||||
"shortcutIsAlreadyUsed": "هذا الاختصار مستخدم بالفعل لـ: {conflict}",
|
||||
"resetToDefault": "إعادة التعيين إلى روابط المفاتيح الافتراضية",
|
||||
"couldNotLoadErrorMsg": "تعذر تحميل الاختصارات، حاول مرة أخرى",
|
||||
"couldNotSaveErrorMsg": "تعذر حفظ الاختصارات، حاول مرة أخرى"
|
||||
},
|
||||
"mobile": {
|
||||
"personalInfo": "معلومات شخصية",
|
||||
"username": "اسم المستخدم",
|
||||
@ -435,6 +424,17 @@
|
||||
"userprofileErrorDescription": "يرجى محاولة تسجيل الخروج وتسجيل الدخول مرة أخرى للتحقق مما إذا كانت المشكلة لا تزال قائمة.",
|
||||
"selectLayout": "حدد الشكل",
|
||||
"selectStartingDay": "اختر يوم البدء"
|
||||
},
|
||||
"shortcuts": {
|
||||
"shortcutsLabel": "الاختصارات",
|
||||
"command": "امر",
|
||||
"keyBinding": "ربط المفاتيح",
|
||||
"addNewCommand": "إضافة أمر جديد",
|
||||
"updateShortcutStep": "اضغط على مجموعة المفاتيح المطلوبة ثم اضغط على ENTER",
|
||||
"shortcutIsAlreadyUsed": "هذا الاختصار مستخدم بالفعل لـ: {conflict}",
|
||||
"resetToDefault": "إعادة التعيين إلى روابط المفاتيح الافتراضية",
|
||||
"couldNotLoadErrorMsg": "تعذر تحميل الاختصارات، حاول مرة أخرى",
|
||||
"couldNotSaveErrorMsg": "تعذر حفظ الاختصارات، حاول مرة أخرى"
|
||||
}
|
||||
},
|
||||
"grid": {
|
||||
|
@ -385,10 +385,6 @@
|
||||
"selectAnIcon": "Seleccioneu una icona",
|
||||
"pleaseInputYourOpenAIKey": "si us plau, introduïu la vostra clau OpenAI"
|
||||
},
|
||||
"shortcuts": {
|
||||
"command": "Comandament",
|
||||
"addNewCommand": "Afegeix una comanda nova"
|
||||
},
|
||||
"mobile": {
|
||||
"personalInfo": "Informació personal",
|
||||
"username": "Nom d'usuari",
|
||||
@ -402,6 +398,10 @@
|
||||
"userprofileError": "No s'ha pogut carregar el perfil d'usuari",
|
||||
"selectStartingDay": "Seleccioneu el dia d'inici",
|
||||
"version": "Versió"
|
||||
},
|
||||
"shortcuts": {
|
||||
"command": "Comandament",
|
||||
"addNewCommand": "Afegeix una comanda nova"
|
||||
}
|
||||
},
|
||||
"grid": {
|
||||
|
@ -484,17 +484,6 @@
|
||||
"pleaseInputYourStabilityAIKey": "تکایە جێگیری کلیلی AI ـەکەت بنووسە",
|
||||
"clickToLogout": "بۆ دەرچوون لە بەکارهێنەری ئێستا کلیک بکە"
|
||||
},
|
||||
"shortcuts": {
|
||||
"shortcutsLabel": "کورتە ڕێگاکان",
|
||||
"command": "فەرمان",
|
||||
"keyBinding": "کورتکراوەکانی تەختەکلیل",
|
||||
"addNewCommand": "زیاد کردنی فەرمانێکی نوێ",
|
||||
"updateShortcutStep": "تێکەڵەی کلیلی دڵخواز داگرە و ENTER داگرە",
|
||||
"shortcutIsAlreadyUsed": "ئەم کورتە ڕێگایە پێشتر بۆ: {conflict} بەکارهاتووە.",
|
||||
"resetToDefault": "گەڕاندنەوە بۆ کلیلەکانی بنهڕهت",
|
||||
"couldNotLoadErrorMsg": "کورتە ڕێگاکان نەتوانرا باربکرێن، تکایە دووبارە هەوڵبدەرەوە",
|
||||
"couldNotSaveErrorMsg": "کورتە ڕێگاکان نەتوانرا پاشەکەوت بکرێن، تکایە دووبارە هەوڵبدەرەوە"
|
||||
},
|
||||
"mobile": {
|
||||
"personalInfo": "زانیاری کەسی",
|
||||
"username": "ناوی بەکارهێنەر",
|
||||
@ -511,6 +500,17 @@
|
||||
"selectLayout": "نەخشە هەڵبژێرە",
|
||||
"selectStartingDay": "ڕۆژی دەستپێکردنەکەت هەڵبژێرە",
|
||||
"version": "وەشان"
|
||||
},
|
||||
"shortcuts": {
|
||||
"shortcutsLabel": "کورتە ڕێگاکان",
|
||||
"command": "فەرمان",
|
||||
"keyBinding": "کورتکراوەکانی تەختەکلیل",
|
||||
"addNewCommand": "زیاد کردنی فەرمانێکی نوێ",
|
||||
"updateShortcutStep": "تێکەڵەی کلیلی دڵخواز داگرە و ENTER داگرە",
|
||||
"shortcutIsAlreadyUsed": "ئەم کورتە ڕێگایە پێشتر بۆ: {conflict} بەکارهاتووە.",
|
||||
"resetToDefault": "گەڕاندنەوە بۆ کلیلەکانی بنهڕهت",
|
||||
"couldNotLoadErrorMsg": "کورتە ڕێگاکان نەتوانرا باربکرێن، تکایە دووبارە هەوڵبدەرەوە",
|
||||
"couldNotSaveErrorMsg": "کورتە ڕێگاکان نەتوانرا پاشەکەوت بکرێن، تکایە دووبارە هەوڵبدەرەوە"
|
||||
}
|
||||
},
|
||||
"grid": {
|
||||
|
@ -382,17 +382,6 @@
|
||||
"pleaseInputYourStabilityAIKey": "Prosím vložte svůj Stability AI klíč",
|
||||
"clickToLogout": "Klin"
|
||||
},
|
||||
"shortcuts": {
|
||||
"shortcutsLabel": "Klávesové zkratky",
|
||||
"command": "Příkaz",
|
||||
"keyBinding": "Přiřazená klávesa",
|
||||
"addNewCommand": "Přidat nový příkaz",
|
||||
"updateShortcutStep": "Stiskněte požadovanou kombinaci kláves a stiskněte ENTER",
|
||||
"shortcutIsAlreadyUsed": "Tato zkratka je již použita pro: @@",
|
||||
"resetToDefault": "Obnovit výchozí klávesové zkratky",
|
||||
"couldNotLoadErrorMsg": "Nepodařilo se načíst klávesové zkratky, zkuste to znovu",
|
||||
"couldNotSaveErrorMsg": "Nepodařilo seuložit klávesové zkta"
|
||||
},
|
||||
"mobile": {
|
||||
"personalInfo": "Osobní informace",
|
||||
"username": "Uživatelské jméno",
|
||||
@ -405,6 +394,17 @@
|
||||
"userAgreement": "Uživatels",
|
||||
"userprofileError": "Nepodařilo se načíst uživatelský profil",
|
||||
"userprofileErrorDescription": "Prosím zkuste se odhlásit a znovu přihlásit a zkontrolujte, zda problém přetrvává"
|
||||
},
|
||||
"shortcuts": {
|
||||
"shortcutsLabel": "Klávesové zkratky",
|
||||
"command": "Příkaz",
|
||||
"keyBinding": "Přiřazená klávesa",
|
||||
"addNewCommand": "Přidat nový příkaz",
|
||||
"updateShortcutStep": "Stiskněte požadovanou kombinaci kláves a stiskněte ENTER",
|
||||
"shortcutIsAlreadyUsed": "Tato zkratka je již použita pro: @@",
|
||||
"resetToDefault": "Obnovit výchozí klávesové zkratky",
|
||||
"couldNotLoadErrorMsg": "Nepodařilo se načíst klávesové zkratky, zkuste to znovu",
|
||||
"couldNotSaveErrorMsg": "Nepodařilo seuložit klávesové zkta"
|
||||
}
|
||||
},
|
||||
"grid": {
|
||||
|
@ -149,8 +149,8 @@
|
||||
"newBoardText": "Neues Board",
|
||||
"chat": {
|
||||
"newChat": "Neuer Chat",
|
||||
"inputMessageHint": "Nachricht an AppFlowy AI",
|
||||
"unsupportedCloudPrompt": "Diese Funktion ist nur bei Verwendung der AppFlowy Cloud verfügbar",
|
||||
"inputMessageHint": "Nachricht an @:appName AI",
|
||||
"unsupportedCloudPrompt": "Diese Funktion ist nur bei Verwendung der @:appName Cloud verfügbar",
|
||||
"relatedQuestion": "Verwandt",
|
||||
"serverUnavailable": "Dienst vorübergehend nicht verfügbar. Bitte versuche es später erneut.",
|
||||
"aiServerUnavailable": "Beim Generieren einer Antwort ist ein Fehler aufgetreten.",
|
||||
@ -279,7 +279,7 @@
|
||||
"Spaces": "Gemeinsam genutzte Bereiche",
|
||||
"public": "Öffentlich",
|
||||
"clickToHidePublic": "Hier klicken, um den öffentlichen Bereich auszublenden.\nHier erstellte Seiten sind für jedes Mitglied sichtbar.",
|
||||
"addAPageToPublic": "Eine Seite zum öffentlichen Bereich hinzufügen."
|
||||
"addAPageToPublic": "Eine Seite zur öffentlichen Domäne hinzufügen."
|
||||
},
|
||||
"notifications": {
|
||||
"export": {
|
||||
@ -375,6 +375,11 @@
|
||||
"change": "E-Mail ändern"
|
||||
}
|
||||
},
|
||||
"login": {
|
||||
"title": "Kontoanmeldung",
|
||||
"loginLabel": "Anmeldung",
|
||||
"logoutLabel": "Ausloggen"
|
||||
},
|
||||
"keys": {
|
||||
"title": "KI API-Schlüssel",
|
||||
"openAILabel": "OpenAI API-Schlüssel",
|
||||
@ -384,11 +389,6 @@
|
||||
"stabilityAITooltip": "Der für die KI-Modelle zu verwendende Stability API-Schlüssel",
|
||||
"stabilityAIHint": "Stability API-Schlüssel eingeben"
|
||||
},
|
||||
"login": {
|
||||
"title": "Kontoanmeldung",
|
||||
"loginLabel": "Anmeldung",
|
||||
"logoutLabel": "Ausloggen"
|
||||
},
|
||||
"description": "Passe dein Profil an, verwalte deine Sicherheitseinstellungen und KI API-Schlüssel oder melde dich bei deinem Konto an."
|
||||
},
|
||||
"workspacePage": {
|
||||
@ -521,21 +521,22 @@
|
||||
},
|
||||
"errorPage": {
|
||||
"message": "Shortcuts konnten nicht geladen werden: {}",
|
||||
"howToFix": "Bitte versuchen Sie es erneut. Wenn das Problem weiterhin besteht, melden Sie es bitte auf GitHub."
|
||||
"howToFix": "Bitte versuche es erneut. Wenn das Problem weiterhin besteht, melde es bitte auf GitHub."
|
||||
},
|
||||
"resetDialog": {
|
||||
"title": "Shortcuts zurücksetzen",
|
||||
"description": "Dies wird alle Ihre Shortcuts auf die Standardeinstellungen zurücksetzen, dies kann nicht rückgängig gemacht werden, sind Sie sicher, dass Sie fortfahren möchten?",
|
||||
"description": "Dies wird alle deine Shortcuts auf die Standardeinstellungen zurücksetzen, dies kann nicht rückgängig gemacht werden. Bist du dir sicher, dass du fortfahren möchtest?",
|
||||
"buttonLabel": "Zurücksetzen"
|
||||
},
|
||||
"conflictDialog": {
|
||||
"title": "{} ist derzeit in Verwendung",
|
||||
"descriptionPrefix": "Diese Tastenkombination wird derzeit verwendet von ",
|
||||
"descriptionSuffix": ". Wenn Sie diese Tastaturbelegung ersetzen, wird sie aus {} entfernt.",
|
||||
"descriptionSuffix": ". Wenn du diese Tastaturbelegung ersetzt, wird sie aus {} entfernt.",
|
||||
"confirmLabel": "Weiter"
|
||||
},
|
||||
"editTooltip": "Zum Starten der Bearbeitung der Tastaturbelegung drücken.",
|
||||
"keybindings": {
|
||||
"toggleToDoList": "Aufgabenliste ein-/ausblenden",
|
||||
"insertNewParagraphInCodeblock": "Neuen Absatz einfügen",
|
||||
"pasteInCodeblock": "In Codeblock einfügen",
|
||||
"selectAllCodeblock": "Alles auswählen",
|
||||
@ -567,26 +568,146 @@
|
||||
"moveCursorEnd": "Cursor an das Zeilenende bewegen",
|
||||
"moveCursorRightWord": "Cursor ein Wort nach rechts bewegen",
|
||||
"moveCursorRightSelect": "Auswählen und Cursor nach rechts bewegen",
|
||||
"moveCursorEndSelect": "Auswählen und Cursor an das Zeilenende bewegen"
|
||||
"moveCursorEndSelect": "Auswählen und Cursor an das Zeilenende bewegen",
|
||||
"moveCursorRightWordSelect": "Markiere das Wort und bewege den Cursor ein Wort nach rechts",
|
||||
"moveCursorUp": "Cursor nach oben bewegen",
|
||||
"moveCursorTopSelect": "Auswählen und Cursor zum Anfang bewegen",
|
||||
"moveCursorTop": "Cursor zum Anfang bewegen",
|
||||
"moveCursorUpSelect": "Auswählen und Cursor nach oben bewegen",
|
||||
"moveCursorBottomSelect": "Auswählen und Cursor ans Ende bewegen",
|
||||
"moveCursorBottom": "Cursor ans Ende bewegen",
|
||||
"moveCursorDown": "Cursor nach unten bewegen",
|
||||
"moveCursorDownSelect": "Auswählen und Cursor nach unten bewegen",
|
||||
"home": "Zum Anfang scrollen",
|
||||
"end": "Zum Ende scrollen",
|
||||
"toggleBold": "Fett ein-/ausschalten",
|
||||
"toggleItalic": "Kursivschrift ein-/ausschalten",
|
||||
"toggleUnderline": "Unterstreichung ein-/ausschalten",
|
||||
"toggleStrikethrough": "Durchgestrichen ein-/ausschalten",
|
||||
"toggleCode": "Inline-Code ein-/ausschalten",
|
||||
"toggleHighlight": "Hervorhebung ein-/ausschalten",
|
||||
"showLinkMenu": "Linkmenü anzeigen",
|
||||
"openInlineLink": "Inline-Link öffnen",
|
||||
"openLinks": "Alle ausgewählten Links öffnen",
|
||||
"indent": "Einzug",
|
||||
"outdent": "Ausrücken",
|
||||
"exit": "Bearbeitung beenden",
|
||||
"pageUp": "Eine Seite nach oben scrollen",
|
||||
"pageDown": "Eine Seite nach unten scrollen",
|
||||
"selectAll": "Alles auswählen",
|
||||
"pasteWithoutFormatting": "Inhalt ohne Formatierung einfügen",
|
||||
"showEmojiPicker": "Emoji-Auswahl anzeigen",
|
||||
"enterInTableCell": "Zeilenumbruch in Tabelle hinzufügen",
|
||||
"leftInTableCell": "In der Tabelle eine Zelle nach links verschieben",
|
||||
"rightInTableCell": "In der Tabelle eine Zelle nach rechts verschieben",
|
||||
"upInTableCell": "In der Tabelle eine Zelle nach oben verschieben",
|
||||
"downInTableCell": "In der Tabelle eine Zelle nach unten verschieben",
|
||||
"tabInTableCell": "Zur nächsten verfügbaren Zelle in der Tabelle gehen",
|
||||
"shiftTabInTableCell": "Zur zuvor verfügbaren Zelle in der Tabelle gehen",
|
||||
"backSpaceInTableCell": "Am Anfang der Zelle anhalten"
|
||||
},
|
||||
"commands": {
|
||||
"codeBlockNewParagraph": "Füge einen neuen Absatz neben dem Codeblock ein",
|
||||
"codeBlockIndentLines": "Füge am Zeilenanfang im Codeblock zwei Leerzeichen ein",
|
||||
"codeBlockOutdentLines": "Lösche zwei Leerzeichen am Zeilenanfang im Codeblock",
|
||||
"codeBlockAddTwoSpaces": "Einfügen von zwei Leerzeichen an der Cursorposition im Codeblock",
|
||||
"codeBlockSelectAll": "Wähle den gesamten Inhalt innerhalb eines Codeblocks aus",
|
||||
"codeBlockPasteText": "Text in Codeblock einfügen",
|
||||
"textAlignLeft": "Text nach links ausrichten",
|
||||
"textAlignCenter": "Text nach rechts ausrichten"
|
||||
"textAlignCenter": "Text nach rechts ausrichten",
|
||||
"textAlignRight": "Text rechtsbündig ausrichten"
|
||||
},
|
||||
"couldNotLoadErrorMsg": "Konnte keine Shortcuts laden, versuchen Sie es erneut",
|
||||
"couldNotSaveErrorMsg": "Shortcuts konnten nicht gespeichert werden, versuchen Sie es erneut"
|
||||
"couldNotLoadErrorMsg": "Konnte keine Shortcuts laden, versuche es erneut",
|
||||
"couldNotSaveErrorMsg": "Shortcuts konnten nicht gespeichert werden, versuche es erneut"
|
||||
},
|
||||
"aiPage": {
|
||||
"title": "KI-Einstellungen",
|
||||
"menuLabel": "KI-Einstellungen",
|
||||
"keys": {
|
||||
"enableAISearchTitle": "KI-Suche",
|
||||
"aiSettingsDescription": "Wähle oder konfiguriere KI-Modelle, die in @:appName verwendet werden. Für eine optimale Leistung empfehlen wir die Verwendung der Standardmodelloptionen",
|
||||
"loginToEnableAIFeature": "KI-Funktionen werden erst nach der Anmeldung bei @:appName Cloud aktiviert. Wenn du kein @:appName-Konto hast, gehe zu „Mein Konto“, um dich zu registrieren",
|
||||
"llmModel": "Sprachmodell",
|
||||
"title": "KI-API-Schlüssel",
|
||||
"openAILabel": "OpenAI API-Schlüssel",
|
||||
"openAITooltip": "Du findest deinen geheimen API-Schlüssel auf der API-Schlüsselseite",
|
||||
"openAIHint": "Gebe deinen OpenAI API-Schlüssel ein",
|
||||
"stabilityAILabel": "Stability API-Schlüssel",
|
||||
"stabilityAITooltip": "Dein Stability API-Schlüssel, der zur Authentifizierung deiner Anfragen verwendet wird",
|
||||
"stabilityAIHint": "Gebe deinen Stability API-Schlüssel ein"
|
||||
}
|
||||
},
|
||||
"planPage": {
|
||||
"menuLabel": "Plan",
|
||||
"title": "Tarifplan",
|
||||
"planUsage": {
|
||||
"title": "Zusammenfassung der Plannutzung",
|
||||
"storageLabel": "Speicher",
|
||||
"storageUsage": "{} von {} GB"
|
||||
"storageUsage": "{} von {} GB",
|
||||
"collaboratorsLabel": "Gastmitarbeiter",
|
||||
"collaboratorsUsage": "{} von {}",
|
||||
"aiResponseLabel": "KI-Antworten",
|
||||
"aiResponseUsage": "{} von {}",
|
||||
"proBadge": "Pro",
|
||||
"memberProToggle": "Unbegrenzte Mitgliederzahl",
|
||||
"guestCollabToggle": "10 Gastmitarbeiter",
|
||||
"aiCredit": {
|
||||
"title": "@:appName KI-Guthaben hinzufügen",
|
||||
"price": "5 $",
|
||||
"priceDescription": "für 1.000 Credits",
|
||||
"purchase": "Kauf von KI",
|
||||
"info": "Füge 1.000 KI-Credits pro Arbeitsbereich hinzu und integriere anpassbare KI nahtlos in deinen Arbeitsablauf für intelligentere, schnellere Ergebnisse mit bis zu:",
|
||||
"infoItemOne": "10.000 Antworten pro Datenbank",
|
||||
"infoItemTwo": "1.000 Antworten pro Arbeitsbereich"
|
||||
},
|
||||
"currentPlan": {
|
||||
"bannerLabel": "Derzeitiger Plan",
|
||||
"freeTitle": "Kostenfrei",
|
||||
"proTitle": "Pro",
|
||||
"teamTitle": "Team",
|
||||
"freeInfo": "Perfekt für Einzelpersonen oder kleine Teams mit bis zu 3 Mitgliedern.",
|
||||
"proInfo": "Perfekt für kleine und mittlere Teams mit bis zu 10 Mitgliedern.",
|
||||
"teamInfo": "Perfekt für alle produktiven und gut organisierten Teams.",
|
||||
"upgrade": "Vergleichen &\n Upgraden",
|
||||
"freeProOne": "Gemeinsamer Arbeitsbereich",
|
||||
"freeProTwo": "Bis zu 3 Mitglieder (inkl. Eigentümer)",
|
||||
"freeProThree": "Unbegrenzte Anzahl an Gästen (nur anzeigen)",
|
||||
"freeProFour": "Speicher 5 GB",
|
||||
"freeProFive": "30 Tage Änderungshistorie",
|
||||
"freeConOne": "Gastmitarbeiter (Bearbeitungszugriff)",
|
||||
"freeConTwo": "Unbegrenzter Speicherplatz",
|
||||
"freeConThree": "6 Monate Änderungshistorie",
|
||||
"professionalProOne": "Gemeinsamer Arbeitsbereich",
|
||||
"professionalProTwo": "Unbegrenzte Mitgliederzahl",
|
||||
"professionalProThree": "Unbegrenzte Anzahl an Gästen (nur anzeigen)",
|
||||
"professionalProFour": "Unbegrenzter Speicherplatz",
|
||||
"professionalProFive": "6 Monate Änderungshistorie",
|
||||
"professionalConOne": "Unbegrenzte Anzahl an Gastmitarbeitern (Bearbeitungszugriff)",
|
||||
"professionalConTwo": "Unbegrenzte KI-Antworten",
|
||||
"professionalConThree": "1 Jahr Änderungshistorie",
|
||||
"canceledInfo": "Dein Plan wurde gekündigt und du wirst am {} auf den kostenlosen Plan herabgestuft."
|
||||
},
|
||||
"deal": {
|
||||
"bannerLabel": "Neujahrsangebot!",
|
||||
"title": "Erweiter dein Team!",
|
||||
"info": "Upgraden und 10 % auf Pro- und Team-Pläne sparen! Steiger die Produktivität deines Arbeitsplatzes mit leistungsstarken neuen Funktionen, einschließlich @:appName KI.",
|
||||
"viewPlans": "Pläne anzeigen"
|
||||
}
|
||||
}
|
||||
},
|
||||
"billingPage": {
|
||||
"menuLabel": "Abrechnung",
|
||||
"title": "Abrechnung",
|
||||
"plan": {
|
||||
"title": "Plan",
|
||||
"freeLabel": "Kostenfrei",
|
||||
"proLabel": "Pro",
|
||||
"planButtonLabel": "Plan ändern",
|
||||
"billingPeriod": "Abrechnungszeitraum",
|
||||
"periodButtonLabel": "Zeitraum bearbeiten"
|
||||
},
|
||||
"paymentDetails": {
|
||||
"title": "Zahlungsdetails",
|
||||
"methodLabel": "Zahlungsmethode",
|
||||
"methodButtonLabel": "Zahlungsmethode bearbeiten"
|
||||
}
|
||||
@ -598,7 +719,7 @@
|
||||
"actions": {
|
||||
"upgrade": "Upgrade",
|
||||
"downgrade": "Downgrade",
|
||||
"downgradeDisabledTooltip": "Sie werden am Ende des Abrechnungszeitraums automatisch downgegradet",
|
||||
"downgradeDisabledTooltip": "Du wirst am Ende des Abrechnungszeitraums automatisch herabgestuft",
|
||||
"current": "Aktuell"
|
||||
},
|
||||
"freePlan": {
|
||||
@ -609,6 +730,7 @@
|
||||
},
|
||||
"proPlan": {
|
||||
"title": "Professionell",
|
||||
"description": "Ein Ort für kleine Gruppen zum Planen und Organisieren.",
|
||||
"price": "$10 /Monat",
|
||||
"priceInfo": "jährlich abgerechnet"
|
||||
},
|
||||
@ -644,12 +766,12 @@
|
||||
"itemEight": "10.000 monatlich"
|
||||
},
|
||||
"paymentSuccess": {
|
||||
"title": "Sie sind jetzt im {} Plan!",
|
||||
"description": "Ihre Zahlung wurde erfolgreich verarbeitet und Ihr Plan wurde auf AppFlowy {} aktualisiert. Sie können die Details Ihres Plans auf der Seite \"Plan\" einsehen"
|
||||
"title": "Du bist jetzt im {} Plan!",
|
||||
"description": "Deine Zahlung wurde erfolgreich verarbeitet und dein Plan wurde auf @:appName {} aktualisiert. Du kannst die Details deines Plans auf der Seite \"Plan\" einsehen"
|
||||
},
|
||||
"downgradeDialog": {
|
||||
"title": "Sind Sie sicher, dass Sie Ihren Plan downgraden wollen?",
|
||||
"description": "Wenn Sie Ihren Plan herabstufen, kehren Sie zum kostenlosen Plan zurück. Mitglieder können den Zugang zu Arbeitsbereichen verlieren und Sie müssen möglicherweise Speicherplatz freigeben, um die Speichergrenzen des kostenlosen Tarifs einzuhalten.",
|
||||
"title": "Bist du sicher, dass du deinen Plan herabstufen willst?",
|
||||
"description": "Wenn du deinen Plan herabstufst, kehrst du zum kostenfreien Plan zurück. Mitglieder können den Zugang zu Arbeitsbereichen verlieren und du musst möglicherweise Speicherplatz freigeben, um die Speichergrenzen des kostenfreien Tarifs einzuhalten.",
|
||||
"downgradeLabel": "Downgrade Plan"
|
||||
}
|
||||
},
|
||||
@ -664,7 +786,7 @@
|
||||
"notifications": "Benachrichtigungen",
|
||||
"open": "Einstellungen öffnen",
|
||||
"logout": "Abmelden",
|
||||
"logoutPrompt": "Wollen sie sich wirklich Abmelden?",
|
||||
"logoutPrompt": "Willst du dich wirklich abmelden?",
|
||||
"selfEncryptionLogoutPrompt": "Willst du dich wirklich Abmelden? Bitte stelle sicher, dass der Encryption Secret Code kopiert wurde.",
|
||||
"syncSetting": "Sync Einstellung",
|
||||
"cloudSettings": "Cloud Einstellungen",
|
||||
@ -816,7 +938,7 @@
|
||||
"failedToAddMember": "Mitglied konnte nicht hinzugefügt werden!",
|
||||
"addMemberSuccess": "Mitglied erfolgreich hinzugefügt",
|
||||
"removeMember": "Mitglied entfernen",
|
||||
"areYouSureToRemoveMember": "Möchten Sie dieses Mitglied wirklich entfernen?",
|
||||
"areYouSureToRemoveMember": "Möchtest du dieses Mitglied wirklich entfernen?",
|
||||
"inviteMemberSuccess": "Die Einladung wurde erfolgreich versendet",
|
||||
"failedToInviteMember": "Das Einladen des Mitglieds ist fehlgeschlagen"
|
||||
}
|
||||
@ -828,7 +950,7 @@
|
||||
"doubleTapToCopy": "Zweimal tippen, um den Pfad zu kopieren",
|
||||
"restoreLocation": "@:appName-Standardpfad wiederherstellen",
|
||||
"customizeLocation": "Einen anderen Ordner öffnen",
|
||||
"restartApp": "Bitte starten Sie die App neu, damit die Änderungen wirksam werden.",
|
||||
"restartApp": "Bitte starte die App neu, damit die Änderungen wirksam werden.",
|
||||
"exportDatabase": "Datenbank exportieren",
|
||||
"selectFiles": "Dateien auswählen, die exportiert werden sollen",
|
||||
"selectAll": "Alle auswählen",
|
||||
@ -911,7 +1033,7 @@
|
||||
}
|
||||
},
|
||||
"grid": {
|
||||
"deleteView": "Möchten Sie diese Ansicht wirklich löschen?",
|
||||
"deleteView": "Möchtest du diese Ansicht wirklich löschen?",
|
||||
"createView": "Neu",
|
||||
"title": {
|
||||
"placeholder": "Unbenannt"
|
||||
@ -1089,8 +1211,8 @@
|
||||
"cannotFindCreatableField": "Es konnte kein geeignetes Feld zum Sortieren gefunden werden",
|
||||
"deleteAllSorts": "Alle Sortierungen entfernen",
|
||||
"addSort": "Sortierung hinzufügen",
|
||||
"removeSorting": "Möchten Sie die Sortierung entfernen?",
|
||||
"fieldInUse": "Sie sortieren bereits nach diesem Feld"
|
||||
"removeSorting": "Möchtest du die Sortierung entfernen?",
|
||||
"fieldInUse": "Du sortierst bereits nach diesem Feld"
|
||||
},
|
||||
"row": {
|
||||
"duplicate": "Duplikat",
|
||||
@ -1103,8 +1225,8 @@
|
||||
"action": "Aktion",
|
||||
"add": "Klicken, um unten hinzuzufügen",
|
||||
"drag": "Ziehen, um zu verschieben",
|
||||
"deleteRowPrompt": "Sind Sie sicher, dass Sie diese Zeile löschen wollen? Diese Aktion kann nicht rückgängig gemacht werden",
|
||||
"deleteCardPrompt": "Sind Sie sicher, dass Sie diese Karte löschen wollen? Diese Aktion kann nicht rückgängig gemacht werden",
|
||||
"deleteRowPrompt": "Bist du sicher, dass du diese Zeile löschen willst? Diese Aktion kann nicht rückgängig gemacht werden",
|
||||
"deleteCardPrompt": "Bist du sicher, dass du diese Karte löschen willst? Diese Aktion kann nicht rückgängig gemacht werden",
|
||||
"dragAndClick": "Ziehen, um zu verschieben. Klicke, um das Menü zu öffnen",
|
||||
"insertRecordAbove": "Füge Datensatz oben ein",
|
||||
"insertRecordBelow": "Füge Datensatz unten ein",
|
||||
@ -1133,7 +1255,7 @@
|
||||
},
|
||||
"checklist": {
|
||||
"taskHint": "Aufgbenbeschreibbung",
|
||||
"addNew": "Fügen Sie einen Artikel hinzu",
|
||||
"addNew": "Füge eine Aufgabe hinzu",
|
||||
"submitNewTask": "Erstellen",
|
||||
"hideComplete": "Blende abgeschlossene Aufgaben aus",
|
||||
"showComplete": "Zeige alle Aufgaben"
|
||||
@ -1141,7 +1263,7 @@
|
||||
"url": {
|
||||
"launch": "Im Browser öffnen",
|
||||
"copy": "Webadresse kopieren",
|
||||
"textFieldHint": "Geben Sie eine URL ein",
|
||||
"textFieldHint": "Gebe eine URL ein",
|
||||
"copiedNotification": "In die Zwischenablage kopiert!"
|
||||
},
|
||||
"relation": {
|
||||
@ -1220,7 +1342,8 @@
|
||||
"smartEditCouldNotFetchResult": "Das Ergebnis konnte nicht von OpenAI abgerufen werden",
|
||||
"smartEditCouldNotFetchKey": "Der OpenAI-Schlüssel konnte nicht abgerufen werden",
|
||||
"smartEditDisabled": "OpenAI in den Einstellungen verbinden",
|
||||
"discardResponse": "Möchten Sie die KI-Antworten verwerfen?",
|
||||
"appflowyAIEditDisabled": "Melde dich an, um KI-Funktionen zu aktivieren",
|
||||
"discardResponse": "Möchtest du die KI-Antworten verwerfen?",
|
||||
"createInlineMathEquation": "Formel erstellen",
|
||||
"fonts": "Schriftarten",
|
||||
"insertDate": "Datum einfügen",
|
||||
@ -1254,7 +1377,7 @@
|
||||
"addIcon": "Symbol hinzufügen",
|
||||
"changeIcon": "Symbol wechseln",
|
||||
"coverRemoveAlert": "Nach dem Löschen wird es aus dem Titelbild entfernt.",
|
||||
"alertDialogConfirmation": "Sicher, dass Sie weitermachen wollen?"
|
||||
"alertDialogConfirmation": "Sicher, dass du weitermachen willst?"
|
||||
},
|
||||
"mathEquation": {
|
||||
"name": "Mathematische Formel",
|
||||
@ -1288,7 +1411,7 @@
|
||||
"convertToLink": "Konvertieren zum eingebetteten Link"
|
||||
},
|
||||
"outline": {
|
||||
"addHeadingToCreateOutline": "Fügen Sie Überschriften hinzu, um ein Inhaltsverzeichnis zu erstellen.",
|
||||
"addHeadingToCreateOutline": "Füge Überschriften hinzu, um ein Inhaltsverzeichnis zu erstellen.",
|
||||
"noMatchHeadings": "Keine passenden Überschriften gefunden."
|
||||
},
|
||||
"table": {
|
||||
@ -1329,7 +1452,7 @@
|
||||
"placeholder": "Inhaltsverzeichnis"
|
||||
},
|
||||
"textBlock": {
|
||||
"placeholder": "Geben Sie „/“ für Inhaltsblöcke ein"
|
||||
"placeholder": "Gebe „/“ für Inhaltsblöcke ein"
|
||||
},
|
||||
"title": {
|
||||
"placeholder": "Ohne Titel"
|
||||
@ -1457,6 +1580,7 @@
|
||||
"ungroupedButtonTooltip": "Enthält Karten, die keiner Gruppe zugeordnet sind",
|
||||
"ungroupedItemsTitle": "Klicke, um dem Board hinzuzufügen",
|
||||
"groupBy": "Gruppiert nach",
|
||||
"groupCondition": "Gruppenbedingung",
|
||||
"referencedBoardPrefix": "Sicht von",
|
||||
"notesTooltip": "Notizen vorhanden",
|
||||
"mobile": {
|
||||
@ -1511,7 +1635,7 @@
|
||||
"other": "{count} Ereignisse ohne Datum"
|
||||
},
|
||||
"unscheduledEventsTitle": "Ungeplante Events",
|
||||
"clickToAdd": "Klicken Sie, um es zum Kalender hinzuzufügen",
|
||||
"clickToAdd": "Klicken zum hinzufügen im Kalender",
|
||||
"name": "Kalendereinstellungen",
|
||||
"clickToOpen": "Hier klicken, um den Eintrag zu öffnen"
|
||||
},
|
||||
@ -1521,7 +1645,7 @@
|
||||
},
|
||||
"errorDialog": {
|
||||
"title": "@:appName-Fehler",
|
||||
"howToFixFallback": "Wir entschuldigen uns für die Unannehmlichkeiten! Reiche auf unserer GitHub-Seite ein Problem ein, das Ihren Fehler beschreibt.",
|
||||
"howToFixFallback": "Wir entschuldigen uns für die Unannehmlichkeiten! Reiche auf unserer GitHub-Seite ein Problem ein, das deinen Fehler beschreibt.",
|
||||
"github": "Auf GitHub ansehen"
|
||||
},
|
||||
"search": {
|
||||
@ -1538,8 +1662,8 @@
|
||||
},
|
||||
"unSupportBlock": "Die aktuelle Version unterstützt diesen Block nicht.",
|
||||
"views": {
|
||||
"deleteContentTitle": "Möchten Sie den {pageType} wirklich löschen?",
|
||||
"deleteContentCaption": "Wenn Sie diesen {pageType} löschen, können Sie ihn aus dem Papierkorb wiederherstellen."
|
||||
"deleteContentTitle": "Möchtest du den {pageType} wirklich löschen?",
|
||||
"deleteContentCaption": "Wenn du diesen {pageType} löschst, kannst du ihn aus dem Papierkorb wiederherstellen."
|
||||
},
|
||||
"colors": {
|
||||
"custom": "Individuell",
|
||||
@ -1674,7 +1798,7 @@
|
||||
},
|
||||
"error": {
|
||||
"weAreSorry": "Das tut uns leid",
|
||||
"loadingViewError": "Wir haben Schwierigkeiten diese Ansicht zu laden. Bitte prüfe die Internetverbindung, lade die App neu und zögere Sie nicht, dass Team zu kontaktieren, falls das Problem weiterhin besteht."
|
||||
"loadingViewError": "Wir haben Schwierigkeiten diese Ansicht zu laden. Bitte prüfe die Internetverbindung, lade die App neu und zögere nicht, das Team zu kontaktieren, falls das Problem weiterhin besteht."
|
||||
},
|
||||
"editor": {
|
||||
"bold": "Fett",
|
||||
@ -1904,37 +2028,39 @@
|
||||
"betaLabel": "BETA",
|
||||
"betaTooltip": "Wir unterstützen derzeit nur die Suche nach Seiten",
|
||||
"fromTrashHint": "Aus dem Mülleimer",
|
||||
"noResultsHint": "Wir haben nicht gefunden, wonach Sie gesucht haben. Versuchen Sie, nach einem anderen Begriff zu suchen.",
|
||||
"noResultsHint": "Wir haben nicht gefunden, wonach du gesucht hast. Versuche nach einem anderen Begriff zu suchen.",
|
||||
"clearSearchTooltip": "Suchfeld löschen"
|
||||
},
|
||||
"space": {
|
||||
"delete": "Löschen",
|
||||
"deleteConfirmation": "Löschen:",
|
||||
"deleteConfirmationDescription": "Alle Seiten innerhalb dieses Space werden gelöscht und in den Papierkorb verschoben.",
|
||||
"rename": "Space umbennen",
|
||||
"rename": "Domäne umbennen",
|
||||
"changeIcon": "Symbol ändern",
|
||||
"manage": "Space verwalten",
|
||||
"addNewSpace": "Space erstellen",
|
||||
"manage": "Domäne verwalten",
|
||||
"addNewSpace": "Domäne erstellen",
|
||||
"collapseAllSubPages": "Alle Unterseiten einklappen",
|
||||
"createNewSpace": "Einen neuen Space schaffen",
|
||||
"createSpaceDescription": "Erstellen Sie mehrere öffentliche und private Bereiche, um Ihre Arbeit besser zu organisieren.",
|
||||
"spaceName": "Space name",
|
||||
"createNewSpace": "Eine neue Domäne erstellen",
|
||||
"createSpaceDescription": "Erstellen mehrere öffentliche und private Domänen, um deine Arbeit besser zu organisieren.",
|
||||
"spaceName": "Name der Domäne",
|
||||
"permission": "Berechtigung",
|
||||
"publicPermission": "Öffentlich",
|
||||
"publicPermissionDescription": "Alle Mitglieder des Arbeitsbereichs mit Vollzugriff",
|
||||
"privatePermission": "Privat",
|
||||
"privatePermissionDescription": "Nur Sie haben Zugang zu diesem Space",
|
||||
"privatePermissionDescription": "Nur du hast Zugang zu dieser Domäne",
|
||||
"spaceIconBackground": "Hintergrundfarbe",
|
||||
"spaceIcon": "Symbol",
|
||||
"dangerZone": "Gefahrenzone",
|
||||
"unableToDeleteLastSpace": "Der letzte Space kann nicht gelöscht werden",
|
||||
"unableToDeleteSpaceNotCreatedByYou": "Von anderen erstellte Spaces können nicht gelöscht werden",
|
||||
"enableSpacesForYourWorkspace": "Spaces für Ihren Arbeitsbereich aktivieren",
|
||||
"title": "Spaces",
|
||||
"unableToDeleteLastSpace": "Die letzte Domäne kann nicht gelöscht werden",
|
||||
"unableToDeleteSpaceNotCreatedByYou": "Von anderen erstellte Domänen können nicht gelöscht werden",
|
||||
"enableSpacesForYourWorkspace": "Domänen für deinen Arbeitsbereich aktivieren",
|
||||
"title": "Domänen",
|
||||
"defaultSpaceName": "Allgemein",
|
||||
"upgradeSpaceTitle": "Spaces freigeben",
|
||||
"upgradeSpaceDescription": "Erstellen Sie mehrere öffentliche und private Spaces, um Ihren Arbeitsbereich besser zu organisieren.",
|
||||
"upgradeSpaceTitle": "Domänen aktivieren",
|
||||
"upgradeSpaceDescription": "Erstelle mehrere öffentliche und private Domänen, um deinen Arbeitsbereich besser zu organisieren.",
|
||||
"upgrade": "Update",
|
||||
"upgradeYourSpace": "Mehrere Spaces erstellen"
|
||||
"upgradeYourSpace": "Mehrere Domänen erstellen",
|
||||
"quicklySwitch": "Schnell zur nächsten Domäne wechseln",
|
||||
"duplicate": "Domäne duplizieren"
|
||||
}
|
||||
}
|
@ -154,8 +154,8 @@
|
||||
"newBoardText": "New board",
|
||||
"chat": {
|
||||
"newChat": "AI Chat",
|
||||
"inputMessageHint": "Message AppFlowy AI",
|
||||
"unsupportedCloudPrompt": "This feature is only available when using AppFlowy Cloud",
|
||||
"inputMessageHint": "Message @:appName AI",
|
||||
"unsupportedCloudPrompt": "This feature is only available when using @:appName Cloud",
|
||||
"relatedQuestion": "Related",
|
||||
"serverUnavailable": "Service Temporarily Unavailable. Please try again later.",
|
||||
"aiServerUnavailable": "🌈 Uh-oh! 🌈. A unicorn ate our response. Please retry!",
|
||||
@ -496,6 +496,11 @@
|
||||
"description": "Clearing the cache will cause images and fonts to be re-downloaded on load. This action will not remove or modify your data.",
|
||||
"successHint": "Cache cleared!"
|
||||
}
|
||||
},
|
||||
"data": {
|
||||
"fixYourData": "Fix your data",
|
||||
"fixButton": "Fix",
|
||||
"fixYourDataDescription": "If you're experiencing issues with your data, you can try to fix it here."
|
||||
}
|
||||
},
|
||||
"shortcutsPage": {
|
||||
@ -579,7 +584,7 @@
|
||||
"indent": "Indent",
|
||||
"outdent": "Outdent",
|
||||
"exit": "Exit editing",
|
||||
"pageUp": "Scroll on page up",
|
||||
"pageUp": "Scroll one page up",
|
||||
"pageDown": "Scroll one page down",
|
||||
"selectAll": "Select all",
|
||||
"pasteWithoutFormatting": "Paste content without formatting",
|
||||
@ -612,8 +617,8 @@
|
||||
"menuLabel": "AI Settings",
|
||||
"keys": {
|
||||
"enableAISearchTitle": "AI Search",
|
||||
"aiSettingsDescription": "Select or configure Ai models used on AppFlowy. For best performance we recommend using the default model options",
|
||||
"loginToEnableAIFeature": "AI features are only enabled after logging in with AppFlowy Cloud. If you don't have an AppFlowy account, go to 'My Account' to sign up",
|
||||
"aiSettingsDescription": "Select or configure Ai models used on @:appName. For best performance we recommend using the default model options",
|
||||
"loginToEnableAIFeature": "AI features are only enabled after logging in with @:appName Cloud. If you don't have an @:appName account, go to 'My Account' to sign up",
|
||||
"llmModel": "Language Model",
|
||||
"title": "AI API Keys",
|
||||
"openAILabel": "OpenAI API key",
|
||||
@ -638,8 +643,9 @@
|
||||
"proBadge": "Pro",
|
||||
"memberProToggle": "Unlimited members",
|
||||
"guestCollabToggle": "10 guest collaborators",
|
||||
"storageUnlimited": "Unlimited storage with your Pro Plan",
|
||||
"aiCredit": {
|
||||
"title": "Add AppFlowy AI Credit",
|
||||
"title": "Add @:appName AI Credit",
|
||||
"price": "5$",
|
||||
"priceDescription": "for 1,000 credits",
|
||||
"purchase": "Purchase AI",
|
||||
@ -659,7 +665,7 @@
|
||||
"freeProOne": "Collaborative workspace",
|
||||
"freeProTwo": "Up to 3 members (incl. owner)",
|
||||
"freeProThree": "Unlimited guests (view-only)",
|
||||
"freeProFour": "Storage 5gb",
|
||||
"freeProFour": "Storage 5GB",
|
||||
"freeProFive": "30 day revision history",
|
||||
"freeConOne": "Guest collaborators (edit access)",
|
||||
"freeConTwo": "Unlimited storage",
|
||||
@ -677,7 +683,7 @@
|
||||
"deal": {
|
||||
"bannerLabel": "New year deal!",
|
||||
"title": "Grow your team!",
|
||||
"info": "Upgrade and save 10% off Pro and Team plans! Boost your workspace productivity with powerful new features including Appflowy Ai.",
|
||||
"info": "Upgrade and save 10% off Pro and Team plans! Boost your workspace productivity with powerful new features including @:appName AI.",
|
||||
"viewPlans": "View plans"
|
||||
}
|
||||
}
|
||||
@ -756,7 +762,7 @@
|
||||
},
|
||||
"paymentSuccess": {
|
||||
"title": "You are now on the {} plan!",
|
||||
"description": "Your payment has been successfully processed and your plan is upgraded to AppFlowy {}. You can view your plan details on the Plan page"
|
||||
"description": "Your payment has been successfully processed and your plan is upgraded to @:appName {}. You can view your plan details on the Plan page"
|
||||
},
|
||||
"downgradeDialog": {
|
||||
"title": "Are you sure you want to downgrade your plan?",
|
||||
@ -2033,4 +2039,4 @@
|
||||
"reportPage": "Report page",
|
||||
"databaseHasNotBeenPublished": "This database hasn't been published yet"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -320,7 +320,6 @@
|
||||
"accountPage": {
|
||||
"menuLabel": "Mi cuenta",
|
||||
"title": "Mi cuenta",
|
||||
"description": "Personaliza tu perfil, administra la seguridad de la cuenta y las claves API de IA, o inicia sesión en tu cuenta.",
|
||||
"general": {
|
||||
"title": "Nombre de cuenta e imagen de perfil",
|
||||
"changeProfilePicture": "Cambiar"
|
||||
@ -331,6 +330,11 @@
|
||||
"change": "Cambiar email"
|
||||
}
|
||||
},
|
||||
"login": {
|
||||
"title": "Inicio de sesión en la cuenta",
|
||||
"loginLabel": "Inicio de sesión",
|
||||
"logoutLabel": "Cerrar sesión"
|
||||
},
|
||||
"keys": {
|
||||
"title": "Claves API de IA",
|
||||
"openAILabel": "Clave API de OpenAI",
|
||||
@ -340,11 +344,7 @@
|
||||
"stabilityAITooltip": "La clave API de Stability que se utilizará en los modelos de IA",
|
||||
"stabilityAIHint": "Ingresa tu clave API de Stability"
|
||||
},
|
||||
"login": {
|
||||
"title": "Inicio de sesión en la cuenta",
|
||||
"loginLabel": "Inicio de sesión",
|
||||
"logoutLabel": "Cerrar sesión"
|
||||
}
|
||||
"description": "Personaliza tu perfil, administra la seguridad de la cuenta y las claves API de IA, o inicia sesión en tu cuenta."
|
||||
},
|
||||
"menu": {
|
||||
"appearance": "Apariencia",
|
||||
@ -555,6 +555,23 @@
|
||||
"pleaseInputYourStabilityAIKey": "por favor ingrese su clave de estabilidad AI",
|
||||
"clickToLogout": "Haga clic para cerrar la sesión del usuario actual."
|
||||
},
|
||||
"mobile": {
|
||||
"personalInfo": "Informacion personal",
|
||||
"username": "Nombre de usuario",
|
||||
"usernameEmptyError": "El nombre de usuario no puede estar vacío",
|
||||
"about": "Acerca de",
|
||||
"pushNotifications": "Notificaciones ",
|
||||
"support": "Soporte",
|
||||
"joinDiscord": "Únete a nosotros en Discord",
|
||||
"privacyPolicy": "política de privacidad",
|
||||
"userAgreement": "Acuerdo del Usuario",
|
||||
"termsAndConditions": "Términos y condiciones",
|
||||
"userprofileError": "No se pudo cargar el perfil de usuario",
|
||||
"userprofileErrorDescription": "Intente cerrar sesión y volver a iniciarla para comprobar si el problema persiste.",
|
||||
"selectLayout": "Seleccionar diseño",
|
||||
"selectStartingDay": "Seleccione el día de inicio",
|
||||
"version": "Versión"
|
||||
},
|
||||
"shortcuts": {
|
||||
"shortcutsLabel": "Atajos",
|
||||
"command": "Commando",
|
||||
@ -575,23 +592,6 @@
|
||||
"textAlignCenter": "Alinear el texto al centro",
|
||||
"textAlignRight": "Alinear el texto a la derecha"
|
||||
}
|
||||
},
|
||||
"mobile": {
|
||||
"personalInfo": "Informacion personal",
|
||||
"username": "Nombre de usuario",
|
||||
"usernameEmptyError": "El nombre de usuario no puede estar vacío",
|
||||
"about": "Acerca de",
|
||||
"pushNotifications": "Notificaciones ",
|
||||
"support": "Soporte",
|
||||
"joinDiscord": "Únete a nosotros en Discord",
|
||||
"privacyPolicy": "política de privacidad",
|
||||
"userAgreement": "Acuerdo del Usuario",
|
||||
"termsAndConditions": "Términos y condiciones",
|
||||
"userprofileError": "No se pudo cargar el perfil de usuario",
|
||||
"userprofileErrorDescription": "Intente cerrar sesión y volver a iniciarla para comprobar si el problema persiste.",
|
||||
"selectLayout": "Seleccionar diseño",
|
||||
"selectStartingDay": "Seleccione el día de inicio",
|
||||
"version": "Versión"
|
||||
}
|
||||
},
|
||||
"grid": {
|
||||
|
@ -432,17 +432,6 @@
|
||||
"pleaseInputYourStabilityAIKey": "Veuillez saisir votre clé de Stability AI",
|
||||
"clickToLogout": "Cliquez pour déconnecter l'utilisateur actuel"
|
||||
},
|
||||
"shortcuts": {
|
||||
"shortcutsLabel": "Raccourcis",
|
||||
"command": "Commande",
|
||||
"keyBinding": "Racourcis clavier",
|
||||
"addNewCommand": "Ajouter une Nouvelle Commande",
|
||||
"updateShortcutStep": "Appuyez sur la combinaison de touches souhaitée et appuyez sur ENTER",
|
||||
"shortcutIsAlreadyUsed": "Ce raccourci est déjà utilisé pour : {conflict}",
|
||||
"resetToDefault": "Réinitialiser les raccourcis clavier par défaut",
|
||||
"couldNotLoadErrorMsg": "Impossible de charger les raccourcis, réessayez",
|
||||
"couldNotSaveErrorMsg": "Impossible d'enregistrer les raccourcis. Réessayez"
|
||||
},
|
||||
"mobile": {
|
||||
"personalInfo": "Informations personnelles",
|
||||
"username": "Nom d'utilisateur",
|
||||
@ -459,6 +448,17 @@
|
||||
"selectLayout": "Sélectionner la mise en page",
|
||||
"selectStartingDay": "Sélectionnez le jour de début",
|
||||
"version": "Version"
|
||||
},
|
||||
"shortcuts": {
|
||||
"shortcutsLabel": "Raccourcis",
|
||||
"command": "Commande",
|
||||
"keyBinding": "Racourcis clavier",
|
||||
"addNewCommand": "Ajouter une Nouvelle Commande",
|
||||
"updateShortcutStep": "Appuyez sur la combinaison de touches souhaitée et appuyez sur ENTER",
|
||||
"shortcutIsAlreadyUsed": "Ce raccourci est déjà utilisé pour : {conflict}",
|
||||
"resetToDefault": "Réinitialiser les raccourcis clavier par défaut",
|
||||
"couldNotLoadErrorMsg": "Impossible de charger les raccourcis, réessayez",
|
||||
"couldNotSaveErrorMsg": "Impossible d'enregistrer les raccourcis. Réessayez"
|
||||
}
|
||||
},
|
||||
"grid": {
|
||||
|
@ -622,6 +622,23 @@
|
||||
"pleaseInputYourStabilityAIKey": "Veuillez saisir votre clé de Stability AI",
|
||||
"clickToLogout": "Cliquez pour déconnecter l'utilisateur actuel"
|
||||
},
|
||||
"mobile": {
|
||||
"personalInfo": "Informations personnelles",
|
||||
"username": "Nom d'utilisateur",
|
||||
"usernameEmptyError": "Le nom d'utilisateur ne peut pas être vide",
|
||||
"about": "À propos",
|
||||
"pushNotifications": "Notifications push",
|
||||
"support": "Support",
|
||||
"joinDiscord": "Rejoignez-nous sur Discord",
|
||||
"privacyPolicy": "Politique de Confidentialité",
|
||||
"userAgreement": "Accord de l'utilisateur",
|
||||
"termsAndConditions": "Termes et conditions",
|
||||
"userprofileError": "Échec du chargement du profil utilisateur",
|
||||
"userprofileErrorDescription": "Veuillez essayer de vous déconnecter et de vous reconnecter pour vérifier si le problème persiste.",
|
||||
"selectLayout": "Sélectionner la mise en page",
|
||||
"selectStartingDay": "Sélectionnez le jour de début",
|
||||
"version": "Version"
|
||||
},
|
||||
"shortcuts": {
|
||||
"shortcutsLabel": "Raccourcis",
|
||||
"command": "Commande",
|
||||
@ -642,23 +659,6 @@
|
||||
"textAlignRight": "Aligner le texte à droite",
|
||||
"codeBlockDeleteTwoSpaces": "Supprimez deux espaces au début de la ligne dans le bloc de code"
|
||||
}
|
||||
},
|
||||
"mobile": {
|
||||
"personalInfo": "Informations personnelles",
|
||||
"username": "Nom d'utilisateur",
|
||||
"usernameEmptyError": "Le nom d'utilisateur ne peut pas être vide",
|
||||
"about": "À propos",
|
||||
"pushNotifications": "Notifications push",
|
||||
"support": "Support",
|
||||
"joinDiscord": "Rejoignez-nous sur Discord",
|
||||
"privacyPolicy": "Politique de Confidentialité",
|
||||
"userAgreement": "Accord de l'utilisateur",
|
||||
"termsAndConditions": "Termes et conditions",
|
||||
"userprofileError": "Échec du chargement du profil utilisateur",
|
||||
"userprofileErrorDescription": "Veuillez essayer de vous déconnecter et de vous reconnecter pour vérifier si le problème persiste.",
|
||||
"selectLayout": "Sélectionner la mise en page",
|
||||
"selectStartingDay": "Sélectionnez le jour de début",
|
||||
"version": "Version"
|
||||
}
|
||||
},
|
||||
"grid": {
|
||||
|
@ -380,17 +380,6 @@
|
||||
"pleaseInputYourStabilityAIKey": "Masukkan kunci Stability AI anda",
|
||||
"clickToLogout": "Klik untuk keluar dari pengguna saat ini"
|
||||
},
|
||||
"shortcuts": {
|
||||
"shortcutsLabel": "Pintasan",
|
||||
"command": "Perintah",
|
||||
"keyBinding": "Keybindin",
|
||||
"addNewCommand": "Tambah Perintah Baru",
|
||||
"updateShortcutStep": "Tekan kombinasi tombol yang diinginkan dan tekan ENTER",
|
||||
"shortcutIsAlreadyUsed": "Pintasan ini sudah digunakan untuk: {conflict}",
|
||||
"resetToDefault": "Mengatur ulang ke keybinding default",
|
||||
"couldNotLoadErrorMsg": "Tidak dapat memuat pintasan, Coba lagi",
|
||||
"couldNotSaveErrorMsg": "Tidak dapat menyimpan pintasan, Coba lagi"
|
||||
},
|
||||
"mobile": {
|
||||
"personalInfo": "Informasi pribadi",
|
||||
"username": "Nama Pengguna",
|
||||
@ -401,6 +390,17 @@
|
||||
"joinDiscord": "Bergabunglah dengan kami di Discord",
|
||||
"privacyPolicy": "Kebijakan Privasi",
|
||||
"userAgreement": "Perjanjian Pengguna"
|
||||
},
|
||||
"shortcuts": {
|
||||
"shortcutsLabel": "Pintasan",
|
||||
"command": "Perintah",
|
||||
"keyBinding": "Keybindin",
|
||||
"addNewCommand": "Tambah Perintah Baru",
|
||||
"updateShortcutStep": "Tekan kombinasi tombol yang diinginkan dan tekan ENTER",
|
||||
"shortcutIsAlreadyUsed": "Pintasan ini sudah digunakan untuk: {conflict}",
|
||||
"resetToDefault": "Mengatur ulang ke keybinding default",
|
||||
"couldNotLoadErrorMsg": "Tidak dapat memuat pintasan, Coba lagi",
|
||||
"couldNotSaveErrorMsg": "Tidak dapat menyimpan pintasan, Coba lagi"
|
||||
}
|
||||
},
|
||||
"grid": {
|
||||
|
@ -431,17 +431,6 @@
|
||||
"pleaseInputYourStabilityAIKey": "per favore inserisci la tua chiave Stability AI",
|
||||
"clickToLogout": "Fare clic per disconnettere l'utente corrente"
|
||||
},
|
||||
"shortcuts": {
|
||||
"shortcutsLabel": "Scorciatoie",
|
||||
"command": "Comando",
|
||||
"keyBinding": "Combinazione",
|
||||
"addNewCommand": "Aggiungi un nuovo comando",
|
||||
"updateShortcutStep": "Premi la combinazione di tasti e poi premi INVIO",
|
||||
"shortcutIsAlreadyUsed": "Questa scorciatoia è già usata per: {conflict}",
|
||||
"resetToDefault": "Ripristina le combinazioni di default",
|
||||
"couldNotLoadErrorMsg": "Impossibile caricare le scorciatoie, Riprova.",
|
||||
"couldNotSaveErrorMsg": "Impossibile salvare le scorciatoie, Riprova."
|
||||
},
|
||||
"mobile": {
|
||||
"personalInfo": "Informazione personale",
|
||||
"username": "Nome utente",
|
||||
@ -458,6 +447,17 @@
|
||||
"selectLayout": "Seleziona disposizione",
|
||||
"selectStartingDay": "Seleziona il giorno di inizio",
|
||||
"version": "Versione"
|
||||
},
|
||||
"shortcuts": {
|
||||
"shortcutsLabel": "Scorciatoie",
|
||||
"command": "Comando",
|
||||
"keyBinding": "Combinazione",
|
||||
"addNewCommand": "Aggiungi un nuovo comando",
|
||||
"updateShortcutStep": "Premi la combinazione di tasti e poi premi INVIO",
|
||||
"shortcutIsAlreadyUsed": "Questa scorciatoia è già usata per: {conflict}",
|
||||
"resetToDefault": "Ripristina le combinazioni di default",
|
||||
"couldNotLoadErrorMsg": "Impossibile caricare le scorciatoie, Riprova.",
|
||||
"couldNotSaveErrorMsg": "Impossibile salvare le scorciatoie, Riprova."
|
||||
}
|
||||
},
|
||||
"grid": {
|
||||
|
@ -345,13 +345,6 @@
|
||||
"selectAnIcon": "アイコンを選択してください",
|
||||
"pleaseInputYourOpenAIKey": "OpenAI キーを入力してください"
|
||||
},
|
||||
"shortcuts": {
|
||||
"shortcutsLabel": "ショートカット",
|
||||
"command": "コマンド",
|
||||
"keyBinding": "キーバインディング",
|
||||
"addNewCommand": "新しいコマンドを追加",
|
||||
"resetToDefault": "キーバインディングをデフォルトに戻す"
|
||||
},
|
||||
"mobile": {
|
||||
"username": "ユーザー名",
|
||||
"usernameEmptyError": "ユーザー名は空白にはできません",
|
||||
@ -361,6 +354,13 @@
|
||||
"userAgreement": "ユーザー同意",
|
||||
"termsAndConditions": "利用規約",
|
||||
"version": "バージョン"
|
||||
},
|
||||
"shortcuts": {
|
||||
"shortcutsLabel": "ショートカット",
|
||||
"command": "コマンド",
|
||||
"keyBinding": "キーバインディング",
|
||||
"addNewCommand": "新しいコマンドを追加",
|
||||
"resetToDefault": "キーバインディングをデフォルトに戻す"
|
||||
}
|
||||
},
|
||||
"grid": {
|
||||
|
@ -425,6 +425,23 @@
|
||||
"pleaseInputYourStabilityAIKey": "insira sua chave Stability AI",
|
||||
"clickToLogout": "Clique para sair do usuário atual"
|
||||
},
|
||||
"mobile": {
|
||||
"personalInfo": "Informações pessoais",
|
||||
"username": "Nome de usuário",
|
||||
"usernameEmptyError": "O nome de usuário não pode ficar vazio",
|
||||
"about": "Sobre",
|
||||
"pushNotifications": "Notificações via push",
|
||||
"support": "Apoiar",
|
||||
"joinDiscord": "Junte-se a nós no Discord",
|
||||
"privacyPolicy": "Política de Privacidade",
|
||||
"userAgreement": "Termo de Acordo do Usuário",
|
||||
"termsAndConditions": "Termos e Condições",
|
||||
"userprofileError": "Falha ao carregar o perfil do usuário",
|
||||
"userprofileErrorDescription": "Tente sair e fazer login novamente para verificar se o problema ainda persiste.",
|
||||
"selectLayout": "Selecione o layout",
|
||||
"selectStartingDay": "Selecione o dia de início",
|
||||
"version": "Versão"
|
||||
},
|
||||
"shortcuts": {
|
||||
"shortcutsLabel": "Atalhos",
|
||||
"command": "Comando",
|
||||
@ -445,23 +462,6 @@
|
||||
"textAlignRight": "Alinhar texto à direita",
|
||||
"codeBlockDeleteTwoSpaces": "Bloco de código: excluir dois espaços no início da linha"
|
||||
}
|
||||
},
|
||||
"mobile": {
|
||||
"personalInfo": "Informações pessoais",
|
||||
"username": "Nome de usuário",
|
||||
"usernameEmptyError": "O nome de usuário não pode ficar vazio",
|
||||
"about": "Sobre",
|
||||
"pushNotifications": "Notificações via push",
|
||||
"support": "Apoiar",
|
||||
"joinDiscord": "Junte-se a nós no Discord",
|
||||
"privacyPolicy": "Política de Privacidade",
|
||||
"userAgreement": "Termo de Acordo do Usuário",
|
||||
"termsAndConditions": "Termos e Condições",
|
||||
"userprofileError": "Falha ao carregar o perfil do usuário",
|
||||
"userprofileErrorDescription": "Tente sair e fazer login novamente para verificar se o problema ainda persiste.",
|
||||
"selectLayout": "Selecione o layout",
|
||||
"selectStartingDay": "Selecione o dia de início",
|
||||
"version": "Versão"
|
||||
}
|
||||
},
|
||||
"grid": {
|
||||
|
@ -443,17 +443,6 @@
|
||||
"pleaseInputYourStabilityAIKey": "Пожалуйста, введите свой токен Stability AI",
|
||||
"clickToLogout": "Нажмите, чтобы выйти из текущего аккаунта"
|
||||
},
|
||||
"shortcuts": {
|
||||
"shortcutsLabel": "Горячие клавиши",
|
||||
"command": "Команда",
|
||||
"keyBinding": "Привязка клавиш",
|
||||
"addNewCommand": "Добавить новую команду",
|
||||
"updateShortcutStep": "Нажмите нужную комбинацию клавиш и нажмите Enter.",
|
||||
"shortcutIsAlreadyUsed": "Это сочетание клавиш уже используется для: {conflict}",
|
||||
"resetToDefault": "Сбросить к стандартным сочетаниям клавиш",
|
||||
"couldNotLoadErrorMsg": "Не удалось загрузить горячие клавиши, попробуйте снова",
|
||||
"couldNotSaveErrorMsg": "Не удалось сохранить горячие клавиши, попробуйте снова"
|
||||
},
|
||||
"mobile": {
|
||||
"personalInfo": "Личная информация",
|
||||
"username": "Имя пользователя",
|
||||
@ -470,6 +459,17 @@
|
||||
"selectLayout": "Выбрать раскладку",
|
||||
"selectStartingDay": "Выбрать день начала",
|
||||
"version": "Версия"
|
||||
},
|
||||
"shortcuts": {
|
||||
"shortcutsLabel": "Горячие клавиши",
|
||||
"command": "Команда",
|
||||
"keyBinding": "Привязка клавиш",
|
||||
"addNewCommand": "Добавить новую команду",
|
||||
"updateShortcutStep": "Нажмите нужную комбинацию клавиш и нажмите Enter.",
|
||||
"shortcutIsAlreadyUsed": "Это сочетание клавиш уже используется для: {conflict}",
|
||||
"resetToDefault": "Сбросить к стандартным сочетаниям клавиш",
|
||||
"couldNotLoadErrorMsg": "Не удалось загрузить горячие клавиши, попробуйте снова",
|
||||
"couldNotSaveErrorMsg": "Не удалось сохранить горячие клавиши, попробуйте снова"
|
||||
}
|
||||
},
|
||||
"grid": {
|
||||
|
@ -332,12 +332,12 @@
|
||||
"change": "Email Değiş"
|
||||
}
|
||||
},
|
||||
"keys": {
|
||||
"openAIHint": "OpenAI API Anahtarını Gir"
|
||||
},
|
||||
"login": {
|
||||
"loginLabel": "Giriş Yap",
|
||||
"logoutLabel": "Çıkış Yap"
|
||||
},
|
||||
"keys": {
|
||||
"openAIHint": "OpenAI API Anahtarını Gir"
|
||||
}
|
||||
},
|
||||
"workspacePage": {
|
||||
@ -557,6 +557,23 @@
|
||||
"pleaseInputYourStabilityAIKey": "Lütfen Stability AI anahtarınızı girin",
|
||||
"clickToLogout": "Geçerli kullanıcıdan çıkış yapmak için tıklayın"
|
||||
},
|
||||
"mobile": {
|
||||
"personalInfo": "Kişisel Bilgiler",
|
||||
"username": "Kullanıcı Adı",
|
||||
"usernameEmptyError": "Kullanıcı adı boş olamaz",
|
||||
"about": "Hakkında",
|
||||
"pushNotifications": "Anında Bildirimler",
|
||||
"support": "Destek",
|
||||
"joinDiscord": "Discord'da bize katılın",
|
||||
"privacyPolicy": "Gizlilik Politikası",
|
||||
"userAgreement": "Kullanıcı Sözleşmesi",
|
||||
"termsAndConditions": "Şartlar ve Koşullar",
|
||||
"userprofileError": "Kullanıcı profili yüklenemedi",
|
||||
"userprofileErrorDescription": "Lütfen sorunun devam edip etmediğini kontrol etmek için çıkış yapıp tekrar giriş yapmayı deneyin.",
|
||||
"selectLayout": "Düzen seçin",
|
||||
"selectStartingDay": "Başlangıç gününü seçin",
|
||||
"version": "Sürüm"
|
||||
},
|
||||
"shortcuts": {
|
||||
"shortcutsLabel": "Kısayollar",
|
||||
"command": "Komut",
|
||||
@ -579,23 +596,6 @@
|
||||
"textAlignRight": "Metni sağa hizala",
|
||||
"codeBlockDeleteTwoSpaces": "Kod bloğunda satır başındaki iki boşluğu sil"
|
||||
}
|
||||
},
|
||||
"mobile": {
|
||||
"personalInfo": "Kişisel Bilgiler",
|
||||
"username": "Kullanıcı Adı",
|
||||
"usernameEmptyError": "Kullanıcı adı boş olamaz",
|
||||
"about": "Hakkında",
|
||||
"pushNotifications": "Anında Bildirimler",
|
||||
"support": "Destek",
|
||||
"joinDiscord": "Discord'da bize katılın",
|
||||
"privacyPolicy": "Gizlilik Politikası",
|
||||
"userAgreement": "Kullanıcı Sözleşmesi",
|
||||
"termsAndConditions": "Şartlar ve Koşullar",
|
||||
"userprofileError": "Kullanıcı profili yüklenemedi",
|
||||
"userprofileErrorDescription": "Lütfen sorunun devam edip etmediğini kontrol etmek için çıkış yapıp tekrar giriş yapmayı deneyin.",
|
||||
"selectLayout": "Düzen seçin",
|
||||
"selectStartingDay": "Başlangıç gününü seçin",
|
||||
"version": "Sürüm"
|
||||
}
|
||||
},
|
||||
"grid": {
|
||||
|
@ -284,7 +284,7 @@
|
||||
"themeUpload": {
|
||||
"button": "Завантажити",
|
||||
"uploadTheme": "Завантажити тему",
|
||||
"description": "Завантажте свою власну тему AppFlowy, скориставшись кнопкою нижче.",
|
||||
"description": "Завантажте свою власну тему @:appName, скориставшись кнопкою нижче.",
|
||||
"loading": "Будь ласка, зачекайте, поки ми перевіряємо та завантажуємо вашу тему...",
|
||||
"uploadSuccess": "Вашу тему успішно завантажено",
|
||||
"deletionFailure": "Не вдалося видалити тему. Спробуйте видалити її вручну.",
|
||||
@ -315,7 +315,7 @@
|
||||
"defaultLocation": "Де зараз зберігаються ваші дані",
|
||||
"exportData": "Експортуйте свої дані",
|
||||
"doubleTapToCopy": "Подвійний натиск для копіювання шляху",
|
||||
"restoreLocation": "Відновити до шляху за замовчуванням AppFlowy",
|
||||
"restoreLocation": "Відновити до шляху за замовчуванням @:appName",
|
||||
"customizeLocation": "Відкрити іншу папку",
|
||||
"restartApp": "Будь ласка, перезапустіть програму для врахування змін.",
|
||||
"exportDatabase": "Експорт бази даних",
|
||||
@ -327,10 +327,10 @@
|
||||
"defineWhereYourDataIsStored": "Визначте, де зберігаються ваші дані",
|
||||
"open": "Відкрити",
|
||||
"openFolder": "Відкрити існуючу папку",
|
||||
"openFolderDesc": "Читати та записувати в вашу існуючу папку AppFlowy",
|
||||
"openFolderDesc": "Читати та записувати в вашу існуючу папку @:appName",
|
||||
"folderHintText": "ім'я папки",
|
||||
"location": "Створення нової папки",
|
||||
"locationDesc": "Оберіть ім'я для папки з даними AppFlowy",
|
||||
"locationDesc": "Оберіть ім'я для папки з даними @:appName",
|
||||
"browser": "Перегляд",
|
||||
"create": "Створити",
|
||||
"set": "Встановити",
|
||||
@ -341,7 +341,7 @@
|
||||
"change": "Змінити",
|
||||
"openLocationTooltips": "Відкрити інший каталог даних",
|
||||
"openCurrentDataFolder": "Відкрити поточний каталог даних",
|
||||
"recoverLocationTooltips": "Скинути до каталогу даних за замовчуванням AppFlowy",
|
||||
"recoverLocationTooltips": "Скинути до каталогу даних за замовчуванням @:appName",
|
||||
"exportFileSuccess": "Файл успішно експортовано!",
|
||||
"exportFileFail": "Помилка експорту файлу!",
|
||||
"export": "Експорт"
|
||||
@ -720,7 +720,7 @@
|
||||
"referencedCalendarPrefix": "Вид на"
|
||||
},
|
||||
"errorDialog": {
|
||||
"title": "Помилка в AppFlowy",
|
||||
"title": "Помилка в @:appName",
|
||||
"howToFixFallback": "Вибачте за незручності! Надішліть звіт про помилку на нашу сторінку GitHub, де ви опишіть свою помилку.",
|
||||
"github": "Переглянути на GitHub"
|
||||
},
|
||||
|
@ -386,7 +386,7 @@
|
||||
"themeUpload": {
|
||||
"button": "Tải lên",
|
||||
"uploadTheme": "Tải theme lên",
|
||||
"description": "Tải lên AppFlowy theme của riêng bạn bằng nút bên dưới.",
|
||||
"description": "Tải lên @:appName theme của riêng bạn bằng nút bên dưới.",
|
||||
"loading": "Vui lòng đợi trong khi chúng tôi xác thực và tải theme của bạn lên...",
|
||||
"uploadSuccess": "Theme của bạn đã được tải lên thành công",
|
||||
"deletionFailure": "Không xóa được theme. Hãy thử xóa nó bằng tay.",
|
||||
@ -469,11 +469,6 @@
|
||||
"pleaseInputYourStabilityAIKey": "vui lòng nhập khóa Stability AI của bạn",
|
||||
"clickToLogout": "Nhấn để đăng xuất"
|
||||
},
|
||||
"shortcuts": {
|
||||
"shortcutsLabel": "Phím tắt",
|
||||
"updateShortcutStep": "Nhấn tổ hợp phím mong muốn và nhấn ENTER",
|
||||
"shortcutIsAlreadyUsed": "Phím tắt này đã được sử dụng cho: {conflict}"
|
||||
},
|
||||
"mobile": {
|
||||
"personalInfo": "Thông tin cá nhân",
|
||||
"username": "Tên người dùng ",
|
||||
@ -490,6 +485,11 @@
|
||||
"selectLayout": "Chọn bố cục",
|
||||
"selectStartingDay": "Chọn ngày bắt đầu",
|
||||
"version": "Phiên bản"
|
||||
},
|
||||
"shortcuts": {
|
||||
"shortcutsLabel": "Phím tắt",
|
||||
"updateShortcutStep": "Nhấn tổ hợp phím mong muốn và nhấn ENTER",
|
||||
"shortcutIsAlreadyUsed": "Phím tắt này đã được sử dụng cho: {conflict}"
|
||||
}
|
||||
},
|
||||
"grid": {
|
||||
|
@ -154,7 +154,7 @@
|
||||
"newBoardText": "新建看板",
|
||||
"chat": {
|
||||
"newChat": "AI 对话",
|
||||
"unsupportedCloudPrompt": "该功能仅在使用 AppFlowy Cloud 时可用",
|
||||
"unsupportedCloudPrompt": "该功能仅在使用 @:appName Cloud 时可用",
|
||||
"relatedQuestion": "相关问题",
|
||||
"serverUnavailable": "服务暂时不可用,请稍后再试",
|
||||
"clickToRetry": "点击重试",
|
||||
@ -352,13 +352,17 @@
|
||||
"accountPage": {
|
||||
"menuLabel": "我的账户",
|
||||
"title": "我的账户",
|
||||
"description": "自定义您的简介,管理账户安全信息和 AI API keys,或登陆您的账户",
|
||||
"email": {
|
||||
"title": "邮箱",
|
||||
"actions": {
|
||||
"change": "更改邮箱"
|
||||
}
|
||||
},
|
||||
"login": {
|
||||
"title": "登录账户",
|
||||
"loginLabel": "登录",
|
||||
"logoutLabel": "退出登录"
|
||||
},
|
||||
"keys": {
|
||||
"title": "AI API 密钥",
|
||||
"openAILabel": "OpenAI API 密钥",
|
||||
@ -366,11 +370,7 @@
|
||||
"stabilityAILabel": "Stability API key",
|
||||
"stabilityAIHint": "输入你的 Stability API Key"
|
||||
},
|
||||
"login": {
|
||||
"title": "登录账户",
|
||||
"loginLabel": "登录",
|
||||
"logoutLabel": "退出登录"
|
||||
}
|
||||
"description": "自定义您的简介,管理账户安全信息和 AI API keys,或登陆您的账户"
|
||||
},
|
||||
"workspacePage": {
|
||||
"menuLabel": "工作区",
|
||||
@ -685,6 +685,23 @@
|
||||
"pleaseInputYourStabilityAIKey": "请输入您的 Stability AI 密钥",
|
||||
"clickToLogout": "点击退出当前用户"
|
||||
},
|
||||
"mobile": {
|
||||
"personalInfo": "个人信息",
|
||||
"username": "用户名",
|
||||
"usernameEmptyError": "用户名不能为空",
|
||||
"about": "关于",
|
||||
"pushNotifications": "推送通知",
|
||||
"support": "支持",
|
||||
"joinDiscord": "在 Discord 中加入我们",
|
||||
"privacyPolicy": "隐私政策",
|
||||
"userAgreement": "用户协议",
|
||||
"termsAndConditions": "条款和条件",
|
||||
"userprofileError": "无法加载用户配置文件",
|
||||
"userprofileErrorDescription": "请尝试注销并重新登录以检查问题是否仍然存在。",
|
||||
"selectLayout": "选择布局",
|
||||
"selectStartingDay": "选择开始日期",
|
||||
"version": "版本"
|
||||
},
|
||||
"shortcuts": {
|
||||
"shortcutsLabel": "快捷方式",
|
||||
"command": "指令",
|
||||
@ -705,23 +722,6 @@
|
||||
"textAlignRight": "右对齐文本",
|
||||
"codeBlockDeleteTwoSpaces": "删除代码块中行首的两个空格"
|
||||
}
|
||||
},
|
||||
"mobile": {
|
||||
"personalInfo": "个人信息",
|
||||
"username": "用户名",
|
||||
"usernameEmptyError": "用户名不能为空",
|
||||
"about": "关于",
|
||||
"pushNotifications": "推送通知",
|
||||
"support": "支持",
|
||||
"joinDiscord": "在 Discord 中加入我们",
|
||||
"privacyPolicy": "隐私政策",
|
||||
"userAgreement": "用户协议",
|
||||
"termsAndConditions": "条款和条件",
|
||||
"userprofileError": "无法加载用户配置文件",
|
||||
"userprofileErrorDescription": "请尝试注销并重新登录以检查问题是否仍然存在。",
|
||||
"selectLayout": "选择布局",
|
||||
"selectStartingDay": "选择开始日期",
|
||||
"version": "版本"
|
||||
}
|
||||
},
|
||||
"grid": {
|
||||
|
@ -318,7 +318,6 @@
|
||||
"accountPage": {
|
||||
"menuLabel": "我的帳號",
|
||||
"title": "我的帳號",
|
||||
"description": "自訂您的個人資料、管理帳戶安全性和 AI API 金鑰,或登入您的帳號",
|
||||
"general": {
|
||||
"title": "帳號名稱和個人資料圖片",
|
||||
"changeProfilePicture": "更改個人資料圖片"
|
||||
@ -329,6 +328,11 @@
|
||||
"change": "更改電子郵件"
|
||||
}
|
||||
},
|
||||
"login": {
|
||||
"title": "帳號登入",
|
||||
"loginLabel": "登入",
|
||||
"logoutLabel": "登出"
|
||||
},
|
||||
"keys": {
|
||||
"title": "AI API 金鑰",
|
||||
"openAILabel": "Open AI API 金鑰",
|
||||
@ -338,11 +342,7 @@
|
||||
"stabilityAITooltip": "以Stability API 金鑰使用AI 模型",
|
||||
"stabilityAIHint": "輸入您的Stability API 金鑰"
|
||||
},
|
||||
"login": {
|
||||
"title": "帳號登入",
|
||||
"loginLabel": "登入",
|
||||
"logoutLabel": "登出"
|
||||
}
|
||||
"description": "自訂您的個人資料、管理帳戶安全性和 AI API 金鑰,或登入您的帳號"
|
||||
},
|
||||
"menu": {
|
||||
"appearance": "外觀",
|
||||
@ -546,20 +546,6 @@
|
||||
"pleaseInputYourStabilityAIKey": "請輸入您的 Stability AI 金鑰",
|
||||
"clickToLogout": "點選以登出目前使用者"
|
||||
},
|
||||
"shortcuts": {
|
||||
"shortcutsLabel": "快捷鍵",
|
||||
"command": "指令",
|
||||
"keyBinding": "鍵盤綁定",
|
||||
"addNewCommand": "新增指令",
|
||||
"updateShortcutStep": "按下您想要的鍵盤組合並按下 ENTER",
|
||||
"shortcutIsAlreadyUsed": "此快捷鍵已被使用於:{conflict}",
|
||||
"resetToDefault": "重設為預設鍵盤綁定",
|
||||
"couldNotLoadErrorMsg": "無法載入快捷鍵,請再試一次",
|
||||
"couldNotSaveErrorMsg": "無法儲存快捷鍵,請再試一次",
|
||||
"commands": {
|
||||
"textAlignRight": "向右對齊文字"
|
||||
}
|
||||
},
|
||||
"mobile": {
|
||||
"personalInfo": "個人資料",
|
||||
"username": "使用者名稱",
|
||||
@ -576,6 +562,20 @@
|
||||
"selectLayout": "選擇版面配置",
|
||||
"selectStartingDay": "選擇一週的起始日",
|
||||
"version": "版本"
|
||||
},
|
||||
"shortcuts": {
|
||||
"shortcutsLabel": "快捷鍵",
|
||||
"command": "指令",
|
||||
"keyBinding": "鍵盤綁定",
|
||||
"addNewCommand": "新增指令",
|
||||
"updateShortcutStep": "按下您想要的鍵盤組合並按下 ENTER",
|
||||
"shortcutIsAlreadyUsed": "此快捷鍵已被使用於:{conflict}",
|
||||
"resetToDefault": "重設為預設鍵盤綁定",
|
||||
"couldNotLoadErrorMsg": "無法載入快捷鍵,請再試一次",
|
||||
"couldNotSaveErrorMsg": "無法儲存快捷鍵,請再試一次",
|
||||
"commands": {
|
||||
"textAlignRight": "向右對齊文字"
|
||||
}
|
||||
}
|
||||
},
|
||||
"grid": {
|
||||
|
@ -11,6 +11,7 @@ use flowy_document::entities::DocumentDataPB;
|
||||
use flowy_document::manager::DocumentManager;
|
||||
use flowy_document::parser::json::parser::JsonToDocumentParser;
|
||||
use flowy_error::FlowyError;
|
||||
use flowy_folder::entities::{CreateViewParams, ViewLayoutPB};
|
||||
use flowy_folder::manager::{FolderManager, FolderUser};
|
||||
use flowy_folder::share::ImportType;
|
||||
use flowy_folder::view_operation::{
|
||||
@ -183,17 +184,13 @@ impl FolderOperationHandler for DocumentFolderOperation {
|
||||
fn create_view_with_view_data(
|
||||
&self,
|
||||
user_id: i64,
|
||||
view_id: &str,
|
||||
_name: &str,
|
||||
data: Vec<u8>,
|
||||
layout: ViewLayout,
|
||||
_meta: HashMap<String, String>,
|
||||
params: CreateViewParams,
|
||||
) -> FutureResult<(), FlowyError> {
|
||||
debug_assert_eq!(layout, ViewLayout::Document);
|
||||
let view_id = view_id.to_string();
|
||||
debug_assert_eq!(params.layout, ViewLayoutPB::Document);
|
||||
let view_id = params.view_id.to_string();
|
||||
let manager = self.0.clone();
|
||||
FutureResult::new(async move {
|
||||
let data = DocumentDataPB::try_from(Bytes::from(data))?;
|
||||
let data = DocumentDataPB::try_from(Bytes::from(params.initial_data))?;
|
||||
manager
|
||||
.create_document(user_id, &view_id, Some(data.into()))
|
||||
.await?;
|
||||
@ -327,40 +324,43 @@ impl FolderOperationHandler for DatabaseFolderOperation {
|
||||
fn create_view_with_view_data(
|
||||
&self,
|
||||
_user_id: i64,
|
||||
view_id: &str,
|
||||
name: &str,
|
||||
data: Vec<u8>,
|
||||
layout: ViewLayout,
|
||||
meta: HashMap<String, String>,
|
||||
params: CreateViewParams,
|
||||
) -> FutureResult<(), FlowyError> {
|
||||
match CreateDatabaseExtParams::from_map(meta) {
|
||||
match CreateDatabaseExtParams::from_map(params.meta.clone()) {
|
||||
None => {
|
||||
let database_manager = self.0.clone();
|
||||
let view_id = view_id.to_string();
|
||||
let view_id = params.view_id.to_string();
|
||||
FutureResult::new(async move {
|
||||
database_manager
|
||||
.create_database_with_database_data(&view_id, data)
|
||||
.create_database_with_database_data(&view_id, params.initial_data)
|
||||
.await?;
|
||||
Ok(())
|
||||
})
|
||||
},
|
||||
Some(params) => {
|
||||
Some(database_params) => {
|
||||
let database_manager = self.0.clone();
|
||||
|
||||
let layout = match layout {
|
||||
ViewLayout::Board => DatabaseLayoutPB::Board,
|
||||
ViewLayout::Calendar => DatabaseLayoutPB::Calendar,
|
||||
ViewLayout::Grid => DatabaseLayoutPB::Grid,
|
||||
ViewLayout::Document | ViewLayout::Chat => {
|
||||
let layout = match params.layout {
|
||||
ViewLayoutPB::Board => DatabaseLayoutPB::Board,
|
||||
ViewLayoutPB::Calendar => DatabaseLayoutPB::Calendar,
|
||||
ViewLayoutPB::Grid => DatabaseLayoutPB::Grid,
|
||||
ViewLayoutPB::Document | ViewLayoutPB::Chat => {
|
||||
return FutureResult::new(async move { Err(FlowyError::not_support()) });
|
||||
},
|
||||
};
|
||||
let name = name.to_string();
|
||||
let database_view_id = view_id.to_string();
|
||||
let name = params.name.to_string();
|
||||
let database_view_id = params.view_id.to_string();
|
||||
let database_parent_view_id = params.parent_view_id.to_string();
|
||||
|
||||
FutureResult::new(async move {
|
||||
database_manager
|
||||
.create_linked_view(name, layout.into(), params.database_id, database_view_id)
|
||||
.create_linked_view(
|
||||
name,
|
||||
layout.into(),
|
||||
database_params.database_id,
|
||||
database_view_id,
|
||||
database_parent_view_id,
|
||||
)
|
||||
.await?;
|
||||
Ok(())
|
||||
})
|
||||
@ -529,11 +529,7 @@ impl FolderOperationHandler for ChatFolderOperation {
|
||||
fn create_view_with_view_data(
|
||||
&self,
|
||||
_user_id: i64,
|
||||
_view_id: &str,
|
||||
_name: &str,
|
||||
_data: Vec<u8>,
|
||||
_layout: ViewLayout,
|
||||
_meta: HashMap<String, String>,
|
||||
_params: CreateViewParams,
|
||||
) -> FutureResult<(), FlowyError> {
|
||||
FutureResult::new(async move { Err(FlowyError::not_support()) })
|
||||
}
|
||||
|
@ -323,18 +323,23 @@ impl DatabaseManager {
|
||||
layout: DatabaseLayout,
|
||||
database_id: String,
|
||||
database_view_id: String,
|
||||
database_parent_view_id: String,
|
||||
) -> FlowyResult<()> {
|
||||
let wdb = self.get_database_indexer().await?;
|
||||
let mut params = CreateViewParams::new(database_id.clone(), database_view_id, name, layout);
|
||||
if let Some(database) = wdb.get_database(&database_id).await {
|
||||
let (field, layout_setting) = DatabaseLayoutDepsResolver::new(database, layout)
|
||||
.resolve_deps_when_create_database_linked_view();
|
||||
let (field, layout_setting, field_settings_map) =
|
||||
DatabaseLayoutDepsResolver::new(database, layout)
|
||||
.resolve_deps_when_create_database_linked_view(&database_parent_view_id);
|
||||
if let Some(field) = field {
|
||||
params = params.with_deps_fields(vec![field], vec![default_field_settings_by_layout_map()]);
|
||||
}
|
||||
if let Some(layout_setting) = layout_setting {
|
||||
params = params.with_layout_setting(layout_setting);
|
||||
}
|
||||
if let Some(field_settings_map) = field_settings_map {
|
||||
params = params.with_field_settings_map(field_settings_map);
|
||||
}
|
||||
};
|
||||
wdb.create_database_linked_view(params).await?;
|
||||
Ok(())
|
||||
|
@ -1,6 +1,8 @@
|
||||
use collab_database::database::{gen_field_id, MutexDatabase};
|
||||
use collab_database::fields::Field;
|
||||
use collab_database::views::{DatabaseLayout, LayoutSetting, OrderObjectPosition};
|
||||
use collab_database::views::{
|
||||
DatabaseLayout, FieldSettingsByFieldIdMap, LayoutSetting, OrderObjectPosition,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::entities::FieldType;
|
||||
@ -28,23 +30,40 @@ impl DatabaseLayoutDepsResolver {
|
||||
|
||||
pub fn resolve_deps_when_create_database_linked_view(
|
||||
&self,
|
||||
) -> (Option<Field>, Option<LayoutSetting>) {
|
||||
view_id: &str,
|
||||
) -> (
|
||||
Option<Field>,
|
||||
Option<LayoutSetting>,
|
||||
Option<FieldSettingsByFieldIdMap>,
|
||||
) {
|
||||
match self.database_layout {
|
||||
DatabaseLayout::Grid => (None, None),
|
||||
DatabaseLayout::Grid => (None, None, None),
|
||||
DatabaseLayout::Board => {
|
||||
let layout_settings = BoardLayoutSetting::new().into();
|
||||
if !self
|
||||
|
||||
let field = if !self
|
||||
.database
|
||||
.lock()
|
||||
.get_fields(None)
|
||||
.into_iter()
|
||||
.any(|field| FieldType::from(field.field_type).can_be_group())
|
||||
{
|
||||
let select_field = self.create_select_field();
|
||||
(Some(select_field), Some(layout_settings))
|
||||
Some(self.create_select_field())
|
||||
} else {
|
||||
(None, Some(layout_settings))
|
||||
}
|
||||
None
|
||||
};
|
||||
|
||||
let field_settings_map = self.database.lock().get_field_settings(view_id, None);
|
||||
tracing::info!(
|
||||
"resolve_deps_when_create_database_linked_view {:?}",
|
||||
field_settings_map
|
||||
);
|
||||
|
||||
(
|
||||
field,
|
||||
Some(layout_settings),
|
||||
Some(field_settings_map.into()),
|
||||
)
|
||||
},
|
||||
DatabaseLayout::Calendar => {
|
||||
match self
|
||||
@ -56,12 +75,12 @@ impl DatabaseLayoutDepsResolver {
|
||||
{
|
||||
Some(field) => {
|
||||
let layout_setting = CalendarLayoutSetting::new(field.id).into();
|
||||
(None, Some(layout_setting))
|
||||
(None, Some(layout_setting), None)
|
||||
},
|
||||
None => {
|
||||
let date_field = self.create_date_field();
|
||||
let layout_setting = CalendarLayoutSetting::new(date_field.clone().id).into();
|
||||
(Some(date_field), Some(layout_setting))
|
||||
(Some(date_field), Some(layout_setting), None)
|
||||
},
|
||||
}
|
||||
},
|
||||
|
@ -584,6 +584,16 @@ pub struct DuplicateViewPayloadPB {
|
||||
|
||||
#[pb(index = 3)]
|
||||
pub include_children: bool,
|
||||
|
||||
// duplicate the view to the specified parent view.
|
||||
// if the parent_view_id is None, the view will be duplicated to the same parent view.
|
||||
#[pb(index = 4, one_of)]
|
||||
pub parent_view_id: Option<String>,
|
||||
|
||||
// The suffix of the duplicated view name.
|
||||
// If the suffix is None, the duplicated view will have the same name with (copy) suffix.
|
||||
#[pb(index = 5, one_of)]
|
||||
pub suffix: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -593,6 +603,10 @@ pub struct DuplicateViewParams {
|
||||
pub open_after_duplicate: bool,
|
||||
|
||||
pub include_children: bool,
|
||||
|
||||
pub parent_view_id: Option<String>,
|
||||
|
||||
pub suffix: Option<String>,
|
||||
}
|
||||
|
||||
impl TryInto<DuplicateViewParams> for DuplicateViewPayloadPB {
|
||||
@ -604,6 +618,8 @@ impl TryInto<DuplicateViewParams> for DuplicateViewPayloadPB {
|
||||
view_id,
|
||||
open_after_duplicate: self.open_after_duplicate,
|
||||
include_children: self.include_children,
|
||||
parent_view_id: self.parent_view_id,
|
||||
suffix: self.suffix,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -381,9 +381,8 @@ impl FolderManager {
|
||||
let view_layout: ViewLayout = params.layout.clone().into();
|
||||
let handler = self.get_handler(&view_layout)?;
|
||||
let user_id = self.user.user_id()?;
|
||||
let meta = params.meta.clone();
|
||||
|
||||
if meta.is_empty() && params.initial_data.is_empty() {
|
||||
if params.meta.is_empty() && params.initial_data.is_empty() {
|
||||
tracing::trace!("Create view with build-in data");
|
||||
handler
|
||||
.create_built_in_view(user_id, ¶ms.view_id, ¶ms.name, view_layout.clone())
|
||||
@ -391,14 +390,7 @@ impl FolderManager {
|
||||
} else {
|
||||
tracing::trace!("Create view with view data");
|
||||
handler
|
||||
.create_view_with_view_data(
|
||||
user_id,
|
||||
¶ms.view_id,
|
||||
¶ms.name,
|
||||
params.initial_data.clone(),
|
||||
view_layout.clone(),
|
||||
meta,
|
||||
)
|
||||
.create_view_with_view_data(user_id, params.clone())
|
||||
.await?;
|
||||
}
|
||||
|
||||
@ -754,12 +746,17 @@ impl FolderManager {
|
||||
let view = self
|
||||
.with_folder(|| None, |folder| folder.views.get_view(¶ms.view_id))
|
||||
.ok_or_else(|| FlowyError::record_not_found().with_context("Can't duplicate the view"))?;
|
||||
let parent_view_id = params
|
||||
.parent_view_id
|
||||
.clone()
|
||||
.unwrap_or(view.parent_view_id.clone());
|
||||
self
|
||||
.duplicate_view_with_parent_id(
|
||||
&view.id,
|
||||
&view.parent_view_id,
|
||||
&parent_view_id,
|
||||
params.open_after_duplicate,
|
||||
params.include_children,
|
||||
params.suffix,
|
||||
)
|
||||
.await
|
||||
}
|
||||
@ -774,6 +771,7 @@ impl FolderManager {
|
||||
parent_view_id: &str,
|
||||
open_after_duplicated: bool,
|
||||
include_children: bool,
|
||||
suffix: Option<String>,
|
||||
) -> Result<(), FlowyError> {
|
||||
if view_id == parent_view_id {
|
||||
return Err(FlowyError::new(
|
||||
@ -790,6 +788,7 @@ impl FolderManager {
|
||||
let mut is_source_view = true;
|
||||
// use a stack to duplicate the view and its children
|
||||
let mut stack = vec![(view_id.to_string(), parent_view_id.to_string())];
|
||||
let suffix = suffix.unwrap_or(" (copy)".to_string());
|
||||
|
||||
while let Some((current_view_id, current_parent_id)) = stack.pop() {
|
||||
let view = self
|
||||
@ -825,7 +824,7 @@ impl FolderManager {
|
||||
);
|
||||
|
||||
let name = if is_source_view {
|
||||
format!("{} (copy)", &view.name)
|
||||
format!("{}{}", &view.name, suffix)
|
||||
} else {
|
||||
view.name.clone()
|
||||
};
|
||||
|
@ -70,11 +70,7 @@ pub trait FolderOperationHandler {
|
||||
fn create_view_with_view_data(
|
||||
&self,
|
||||
user_id: i64,
|
||||
view_id: &str,
|
||||
name: &str,
|
||||
data: Vec<u8>,
|
||||
layout: ViewLayout,
|
||||
meta: HashMap<String, String>,
|
||||
params: CreateViewParams,
|
||||
) -> FutureResult<(), FlowyError>;
|
||||
|
||||
/// Create a view with the pre-defined data.
|
||||
|
Loading…
Reference in New Issue
Block a user