Merge branch 'main' into workspace-invite

This commit is contained in:
Zack Fu Zi Xiang 2024-03-08 13:44:38 +08:00
commit 0aa4aa956d
No known key found for this signature in database
25 changed files with 225 additions and 131 deletions

View File

@ -257,7 +257,7 @@
"label": "AF: Tauri UI Dev",
"type": "shell",
"isBackground": true,
"command": "pnpm run tauri:dev",
"command": "pnpm run sync:i18n && pnpm run dev",
"options": {
"cwd": "${workspaceFolder}/appflowy_tauri"
}

View File

@ -148,7 +148,7 @@ class _MobileViewPageState extends State<MobileViewPage> {
context,
showDragHandle: true,
showDivider: false,
backgroundColor: Theme.of(context).colorScheme.surface,
backgroundColor: Theme.of(context).colorScheme.background,
builder: (_) => _buildViewPageBottomSheet(context),
);
},

View File

@ -2,7 +2,6 @@ import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/mobile/presentation/widgets/flowy_mobile_quick_action_button.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/widget/separated_flex.dart';
import 'package:flutter/material.dart';
enum MobileViewItemBottomSheetBodyAction {
@ -26,12 +25,8 @@ class MobileViewItemBottomSheetBody extends StatelessWidget {
@override
Widget build(BuildContext context) {
return SeparatedColumn(
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
separatorBuilder: () => const Divider(
height: 8.5,
thickness: 0.5,
),
children: [
MobileQuickActionButton(
text: LocaleKeys.button_rename.tr(),
@ -40,6 +35,7 @@ class MobileViewItemBottomSheetBody extends StatelessWidget {
MobileViewItemBottomSheetBodyAction.rename,
),
),
_divider(),
MobileQuickActionButton(
text: isFavorite
? LocaleKeys.button_removeFromFavorites.tr()
@ -54,6 +50,7 @@ class MobileViewItemBottomSheetBody extends StatelessWidget {
: MobileViewItemBottomSheetBodyAction.addToFavorites,
),
),
_divider(),
MobileQuickActionButton(
text: LocaleKeys.button_duplicate.tr(),
icon: FlowySvgs.m_duplicate_s,
@ -61,6 +58,7 @@ class MobileViewItemBottomSheetBody extends StatelessWidget {
MobileViewItemBottomSheetBodyAction.duplicate,
),
),
_divider(),
MobileQuickActionButton(
text: LocaleKeys.button_delete.tr(),
textColor: Theme.of(context).colorScheme.error,
@ -70,7 +68,13 @@ class MobileViewItemBottomSheetBody extends StatelessWidget {
MobileViewItemBottomSheetBodyAction.delete,
),
),
_divider(),
],
);
}
Widget _divider() => const Divider(
height: 8.5,
thickness: 0.5,
);
}

View File

@ -4,7 +4,6 @@ import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart';
import 'package:appflowy/mobile/presentation/widgets/flowy_mobile_quick_action_button.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
enum MobileViewBottomSheetBodyAction {
@ -85,12 +84,8 @@ class MobileViewBottomSheetBody extends StatelessWidget {
@override
Widget build(BuildContext context) {
final isFavorite = view.isFavorite;
return SeparatedColumn(
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
separatorBuilder: () => const Divider(
height: 8.5,
thickness: 0.5,
),
children: [
MobileQuickActionButton(
text: LocaleKeys.button_rename.tr(),
@ -99,6 +94,7 @@ class MobileViewBottomSheetBody extends StatelessWidget {
MobileViewBottomSheetBodyAction.rename,
),
),
_divider(),
MobileQuickActionButton(
text: isFavorite
? LocaleKeys.button_removeFromFavorites.tr()
@ -113,6 +109,7 @@ class MobileViewBottomSheetBody extends StatelessWidget {
: MobileViewBottomSheetBodyAction.addToFavorites,
),
),
_divider(),
MobileQuickActionButton(
text: LocaleKeys.button_duplicate.tr(),
icon: FlowySvgs.m_duplicate_s,
@ -120,6 +117,7 @@ class MobileViewBottomSheetBody extends StatelessWidget {
MobileViewBottomSheetBodyAction.duplicate,
),
),
_divider(),
MobileQuickActionButton(
text: LocaleKeys.button_delete.tr(),
textColor: Theme.of(context).colorScheme.error,
@ -129,7 +127,13 @@ class MobileViewBottomSheetBody extends StatelessWidget {
MobileViewBottomSheetBodyAction.delete,
),
),
_divider(),
],
);
}
Widget _divider() => const Divider(
height: 8.5,
thickness: 0.5,
);
}

View File

@ -54,7 +54,7 @@ enum MobilePaneActionType {
context,
showDragHandle: true,
showDivider: false,
backgroundColor: Theme.of(context).colorScheme.surface,
backgroundColor: Theme.of(context).colorScheme.background,
useRootNavigator: true,
builder: (context) {
return MultiBlocProvider(

View File

@ -150,6 +150,7 @@ class _MobileRowDetailPageState extends State<MobileRowDetailPage> {
icon: FlowySvgs.m_delete_m,
iconColor: Theme.of(context).colorScheme.error,
),
const Divider(height: 8.5, thickness: 0.5),
],
),
);

View File

@ -40,7 +40,7 @@ Future<FieldType?> showFieldTypeGridBottomSheet(
showCloseButton: true,
elevation: 20,
title: title,
backgroundColor: Theme.of(context).colorScheme.surface,
backgroundColor: Theme.of(context).colorScheme.background,
enableDraggableScrollable: true,
builder: (context) {
final typeOptionMenuItemValue = mobileSupportedFieldTypes
@ -94,9 +94,6 @@ void mobileCreateFieldWorkflow(
if (optionValues != null) {
await optionValues.create(viewId: viewId, position: position);
}
if (context.mounted) {
context.pop();
}
}
/// Used to edit a field.

View File

@ -262,11 +262,9 @@ class _SortItem extends StatelessWidget {
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 12.0),
child: Expanded(
child: FlowyText.medium(
LocaleKeys.grid_sort_by.tr(),
fontSize: 15,
),
child: FlowyText.medium(
LocaleKeys.grid_sort_by.tr(),
fontSize: 15,
),
),
const VSpace(10),
@ -407,6 +405,8 @@ class _SortDetailContent extends StatelessWidget {
final SortInfo? sortInfo;
bool get isCreatingNewSort => sortInfo == null;
@override
Widget build(BuildContext context) {
return Column(
@ -417,7 +417,7 @@ class _SortDetailContent extends StatelessWidget {
padding: const EdgeInsets.symmetric(horizontal: 16),
child: DefaultTabController(
length: 2,
initialIndex: sortInfo == null
initialIndex: isCreatingNewSort
? 0
: sortInfo!.sortPB.condition == SortConditionPB.Ascending
? 0
@ -489,30 +489,40 @@ class _SortDetailContent extends StatelessWidget {
child: BlocBuilder<SortEditorBloc, SortEditorState>(
builder: (context, state) {
final fields = state.allFields
.where(
(field) =>
field.canCreateSort ||
sortInfo != null && sortInfo!.fieldId == field.id,
)
.where((field) => field.canCreateSort || field.hasSort)
.toList();
return ListView.builder(
itemCount: fields.length,
itemBuilder: (context, index) {
final fieldInfo = fields[index];
final isSelected = sortInfo == null
final isSelected = isCreatingNewSort
? context
.watch<MobileSortEditorCubit>()
.state
.newSortFieldId ==
fieldInfo.id
: sortInfo!.fieldId == fieldInfo.id;
final enabled = fieldInfo.canCreateSort ||
isCreatingNewSort && !fieldInfo.hasSort ||
!isCreatingNewSort && sortInfo!.fieldId == fieldInfo.id;
return FlowyOptionTile.checkbox(
text: fieldInfo.field.name,
isSelected: isSelected,
textColor: enabled ? null : Theme.of(context).disabledColor,
showTopBorder: false,
onTap: () {
if (!isSelected) {
if (isSelected) {
return;
}
if (enabled) {
_changeFieldId(context, fieldInfo.id);
} else {
Fluttertoast.showToast(
msg: LocaleKeys.grid_sort_fieldInUse.tr(),
gravity: ToastGravity.BOTTOM,
);
}
},
);
@ -526,7 +536,7 @@ class _SortDetailContent extends StatelessWidget {
}
void _changeCondition(BuildContext context, SortConditionPB newCondition) {
if (sortInfo == null) {
if (isCreatingNewSort) {
context.read<MobileSortEditorCubit>().changeSortCondition(newCondition);
} else {
context.read<SortEditorBloc>().add(
@ -539,7 +549,7 @@ class _SortDetailContent extends StatelessWidget {
}
void _changeFieldId(BuildContext context, String newFieldId) {
if (sortInfo == null) {
if (isCreatingNewSort) {
context.read<MobileSortEditorCubit>().changeFieldId(newFieldId);
} else {
context.read<SortEditorBloc>().add(

View File

@ -183,6 +183,7 @@ class MobileDatabaseViewListButton extends StatelessWidget {
showMobileBottomSheet(
context,
showDragHandle: true,
backgroundColor: Theme.of(context).colorScheme.background,
builder: (_) {
return BlocProvider<ViewBloc>(
create: (_) =>

View File

@ -7,6 +7,7 @@ import 'package:appflowy/mobile/presentation/widgets/widgets.dart';
import 'package:appflowy/shared/appflowy_cache_manager.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/util/share_log_files.dart';
import 'package:appflowy/workspace/presentation/home/toast.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
@ -71,9 +72,14 @@ class SupportSettingGroup extends StatelessWidget {
maxLines: 4,
),
actionButtonTitle: LocaleKeys.button_yes.tr(),
actionButtonColor: Theme.of(context).colorScheme.error,
onActionButtonPressed: () async {
await getIt<FlowyCacheManager>().clearAllCache();
if (context.mounted) {
showSnackBarMessage(
context,
LocaleKeys.settings_files_clearCacheSuccess.tr(),
);
}
},
);
},

View File

@ -13,6 +13,7 @@ enum FlowyOptionTileType {
class FlowyOptionTile extends StatelessWidget {
const FlowyOptionTile._({
super.key,
required this.type,
this.showTopBorder = true,
this.showBottomBorder = true,
@ -88,9 +89,11 @@ class FlowyOptionTile extends StatelessWidget {
}
factory FlowyOptionTile.checkbox({
Key? key,
required String text,
required bool isSelected,
required VoidCallback? onTap,
Color? textColor,
Widget? leftIcon,
Widget? content,
bool showTopBorder = true,
@ -99,9 +102,11 @@ class FlowyOptionTile extends StatelessWidget {
Color? backgroundColor,
}) {
return FlowyOptionTile._(
key: key,
type: FlowyOptionTileType.checkbox,
isSelected: isSelected,
text: text,
textColor: textColor,
content: content,
onTap: onTap,
fontFamily: fontFamily,

View File

@ -337,6 +337,7 @@ class FieldController {
...changeset.insertSorts.map((sort) => sort.sort.fieldId),
...changeset.updateSorts.map((sort) => sort.fieldId),
...changeset.deleteSorts.map((sort) => sort.fieldId),
...?_sortNotifier?.sorts.map((sort) => sort.fieldId),
]);
final newFieldInfos = [...fieldInfos];
@ -367,8 +368,8 @@ class FieldController {
insertSortFromChangeset(newSortInfos, changeset);
updateSortFromChangeset(newSortInfos, changeset);
_sortNotifier?.sorts = newSortInfos;
updateFieldInfos(newSortInfos, changeset);
_sortNotifier?.sorts = newSortInfos;
},
(err) => Log.error(err),
);

View File

@ -32,7 +32,7 @@ class _CalculationSelectorState extends State<CalculationSelector> {
onEnter: (_) => _setHovering(true),
onExit: (_) => _setHovering(false),
child: AnimatedOpacity(
duration: const Duration(milliseconds: 200),
duration: const Duration(milliseconds: 100),
opacity: widget.isSelected || _isHovering ? 1 : 0,
child: FlowyButton(
radius: BorderRadius.zero,

View File

@ -370,9 +370,6 @@ class _FieldDetailsEditorState extends State<FieldDetailsEditor> {
Widget _addDeleteFieldButton() {
return BlocBuilder<FieldEditorBloc, FieldEditorState>(
builder: (context, state) {
if (state.field.isPrimary) {
return const SizedBox.shrink();
}
return Padding(
padding: const EdgeInsets.fromLTRB(8.0, 4.0, 8.0, 0),
child: FieldActionCell(
@ -389,9 +386,6 @@ class _FieldDetailsEditorState extends State<FieldDetailsEditor> {
Widget _addDuplicateFieldButton() {
return BlocBuilder<FieldEditorBloc, FieldEditorState>(
builder: (context, state) {
if (state.field.isPrimary) {
return const SizedBox.shrink();
}
return Padding(
padding: const EdgeInsets.fromLTRB(8.0, 4.0, 8.0, 0),
child: FieldActionCell(
@ -533,41 +527,52 @@ class _SwitchFieldButtonState extends State<SwitchFieldButton> {
@override
Widget build(BuildContext context) {
return SizedBox(
height: GridSize.popoverItemHeight,
child: AppFlowyPopover(
constraints: BoxConstraints.loose(const Size(460, 540)),
triggerActions: PopoverTriggerFlags.hover,
mutex: widget.popoverMutex,
controller: _popoverController,
offset: const Offset(8, 0),
margin: const EdgeInsets.all(8),
popupBuilder: (BuildContext popoverContext) {
return FieldTypeList(
onSelectField: (newFieldType) {
context
.read<FieldEditorBloc>()
.add(FieldEditorEvent.switchFieldType(newFieldType));
return BlocBuilder<FieldEditorBloc, FieldEditorState>(
builder: (context, state) {
final bool isPrimary = state.field.isPrimary;
return SizedBox(
height: GridSize.popoverItemHeight,
child: AppFlowyPopover(
constraints: BoxConstraints.loose(const Size(460, 540)),
triggerActions: isPrimary ? 0 : PopoverTriggerFlags.hover,
mutex: widget.popoverMutex,
controller: _popoverController,
offset: const Offset(8, 0),
margin: const EdgeInsets.all(8),
popupBuilder: (BuildContext popoverContext) {
return FieldTypeList(
onSelectField: (newFieldType) {
context
.read<FieldEditorBloc>()
.add(FieldEditorEvent.switchFieldType(newFieldType));
},
);
},
);
},
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: _buildMoreButton(context),
),
),
);
}
Widget _buildMoreButton(BuildContext context) {
final bloc = context.read<FieldEditorBloc>();
return FlowyButton(
onTap: () => _popoverController.show(),
text: FlowyText.medium(
bloc.state.field.fieldType.i18n,
),
leftIcon: FlowySvg(bloc.state.field.fieldType.svgData),
rightIcon: const FlowySvg(FlowySvgs.more_s),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: FlowyButton(
onTap: () {
if (!isPrimary) {
_popoverController.show();
}
},
text: FlowyText.medium(
state.field.fieldType.i18n,
color: isPrimary ? Theme.of(context).disabledColor : null,
),
leftIcon: FlowySvg(
state.field.fieldType.svgData,
color: isPrimary ? Theme.of(context).disabledColor : null,
),
rightIcon: FlowySvg(
FlowySvgs.more_s,
color: isPrimary ? Theme.of(context).disabledColor : null,
),
),
),
),
);
},
);
}
}

View File

@ -1,11 +1,12 @@
import 'package:appflowy/plugins/database/grid/presentation/layout/sizes.dart';
import 'package:appflowy/plugins/database/widgets/row/cells/cell_container.dart';
import 'package:flutter/material.dart';
import 'package:appflowy/plugins/database/application/cell/bloc/checklist_cell_bloc.dart';
import 'package:appflowy/plugins/database/grid/presentation/layout/sizes.dart';
import 'package:appflowy/plugins/database/widgets/cell_editor/checklist_cell_editor.dart';
import 'package:appflowy/plugins/database/widgets/cell_editor/checklist_progress_bar.dart';
import 'package:appflowy/plugins/database/widgets/row/cells/cell_container.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 '../editable_cell_skeleton/checklist.dart';
@ -25,6 +26,7 @@ class DesktopGridChecklistCellSkin extends IEditableChecklistCellSkin {
constraints: BoxConstraints.loose(const Size(360, 400)),
direction: PopoverDirection.bottomWithLeftAligned,
triggerActions: PopoverTriggerFlags.none,
skipTraversal: true,
popupBuilder: (BuildContext popoverContext) {
WidgetsBinding.instance.addPostFrameCallback((_) {
cellContainerNotifier.isFocus = true;

View File

@ -1,7 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.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';
import 'package:appflowy/plugins/database/application/cell/bloc/url_cell_bloc.dart';
@ -177,21 +176,11 @@ class _VisitURLAccessoryState extends State<_VisitURLAccessory>
}
@override
bool enable() {
return widget.cellDataNotifier.value.isNotEmpty;
}
bool enable() => widget.cellDataNotifier.value.isNotEmpty;
@override
void onTap() {
final content = widget.cellDataNotifier.value;
if (content.isEmpty) {
return;
}
final shouldAddScheme =
!['http', 'https'].any((pattern) => content.startsWith(pattern));
final url = shouldAddScheme ? 'http://$content' : content;
afLaunchUrlString(url);
}
void onTap() =>
openUrlCellLink(widget.cellDataNotifier.value);
}
class _URLAccessoryIconContainer extends StatelessWidget {

View File

@ -1,5 +1,6 @@
import 'dart:async';
import 'package:appflowy/core/helpers/url_launcher.dart';
import 'package:appflowy/plugins/database/application/cell/cell_controller.dart';
import 'package:appflowy/plugins/database/application/cell/cell_controller_builder.dart';
import 'package:appflowy/plugins/database/application/database_controller.dart';
@ -15,6 +16,9 @@ import '../desktop_row_detail/desktop_row_detail_url_cell.dart';
import '../mobile_grid/mobile_grid_url_cell.dart';
import '../mobile_row_detail/mobile_row_detail_url_cell.dart';
const regexUrl =
r"[(http(s)?):\/\/(www\.)?a-zA-Z0-9@:._\+-~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:_\+.~#?&\/\/=]*)";
abstract class IEditableURLCellSkin {
const IEditableURLCellSkin();
@ -130,3 +134,24 @@ class _GridURLCellState extends GridEditableTextCell<EditableURLCell> {
@override
String? onCopy() => cellBloc.state.content;
}
void openUrlCellLink(String content) {
if (RegExp(regexUrl).hasMatch(content)) {
const linkPrefix = [
'http://',
'https://',
'file://',
'ftp://',
'ftps://',
'mailto:',
];
final shouldAddScheme =
!linkPrefix.any((pattern) => content.startsWith(pattern));
final url = shouldAddScheme ? 'https://$content' : content;
afLaunchUrlString(url);
} else {
afLaunchUrlString(
"https://www.google.com/search?q=${Uri.encodeComponent(content)}",
);
}
}

View File

@ -1,6 +1,9 @@
import 'dart:async';
import 'dart:io';
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/cell_controller_builder.dart';
@ -11,11 +14,10 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/size.dart';
import 'package:flowy_infra/theme_extension.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../application/cell/bloc/checklist_cell_bloc.dart';
import 'checklist_progress_bar.dart';
class ChecklistCellEditor extends StatefulWidget {
@ -189,7 +191,9 @@ class _ChecklistItemState extends State<ChecklistItem> {
void didUpdateWidget(ChecklistItem oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.task.data.name != oldWidget.task.data.name) {
final selection = _textController.selection;
_textController.text = widget.task.data.name;
_textController.selection = selection;
}
}
@ -234,6 +238,10 @@ class _ChecklistItemState extends State<ChecklistItem> {
else
const SingleActivator(LogicalKeyboardKey.enter, control: true):
const _SelectTaskIntent(),
const SingleActivator(LogicalKeyboardKey.arrowUp):
const PreviousFocusIntent(),
const SingleActivator(LogicalKeyboardKey.arrowDown):
const NextFocusIntent(),
},
descendantsAreTraversable: false,
child: Container(
@ -261,15 +269,24 @@ class _ChecklistItemState extends State<ChecklistItem> {
),
Expanded(
child: Shortcuts(
shortcuts: const {
SingleActivator(LogicalKeyboardKey.space):
DoNothingAndStopPropagationIntent(),
SingleActivator(LogicalKeyboardKey.delete):
DoNothingAndStopPropagationIntent(),
SingleActivator(LogicalKeyboardKey.enter):
DoNothingAndStopPropagationIntent(),
SingleActivator(LogicalKeyboardKey.escape):
_EndEditingTaskIntent(),
shortcuts: {
const SingleActivator(LogicalKeyboardKey.space):
const DoNothingAndStopPropagationIntent(),
const SingleActivator(LogicalKeyboardKey.delete):
const DoNothingAndStopPropagationIntent(),
if (Platform.isMacOS)
LogicalKeySet(
LogicalKeyboardKey.fn,
LogicalKeyboardKey.backspace,
): const DoNothingAndStopPropagationIntent(),
const SingleActivator(LogicalKeyboardKey.enter):
const DoNothingAndStopPropagationIntent(),
const SingleActivator(LogicalKeyboardKey.escape):
const _EndEditingTaskIntent(),
const SingleActivator(LogicalKeyboardKey.arrowUp):
const DoNothingAndStopPropagationIntent(),
const SingleActivator(LogicalKeyboardKey.arrowDown):
const DoNothingAndStopPropagationIntent(),
},
child: TextField(
controller: _textController,
@ -345,12 +362,11 @@ class NewTaskItem extends StatefulWidget {
}
class _NewTaskItemState extends State<NewTaskItem> {
late final TextEditingController _textEditingController;
final _textEditingController = TextEditingController();
@override
void initState() {
super.initState();
_textEditingController = TextEditingController();
if (widget.focusNode.canRequestFocus) {
widget.focusNode.requestFocus();
}
@ -385,15 +401,13 @@ class _NewTaskItemState extends State<NewTaskItem> {
hintText: LocaleKeys.grid_checklist_addNew.tr(),
),
onSubmitted: (taskDescription) {
if (taskDescription.trim().isNotEmpty) {
context.read<ChecklistCellBloc>().add(
ChecklistCellEvent.createNewTask(
taskDescription.trim(),
),
);
if (taskDescription.isNotEmpty) {
context
.read<ChecklistCellBloc>()
.add(ChecklistCellEvent.createNewTask(taskDescription));
_textEditingController.clear();
}
widget.focusNode.requestFocus();
_textEditingController.clear();
},
onChanged: (value) => setState(() {}),
),
@ -409,16 +423,17 @@ class _NewTaskItemState extends State<NewTaskItem> {
: Theme.of(context).colorScheme.primaryContainer,
fontColor: Theme.of(context).colorScheme.onPrimary,
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3),
onPressed: () {
final text = _textEditingController.text.trim();
if (text.isNotEmpty) {
context.read<ChecklistCellBloc>().add(
ChecklistCellEvent.createNewTask(text),
);
}
widget.focusNode.requestFocus();
_textEditingController.clear();
},
onPressed: _textEditingController.text.isEmpty
? null
: () {
context.read<ChecklistCellBloc>().add(
ChecklistCellEvent.createNewTask(
_textEditingController.text,
),
);
widget.focusNode.requestFocus();
_textEditingController.clear();
},
),
],
),

View File

@ -150,6 +150,7 @@ void _showEditSortPanelFromToolbar(
showDragHandle: true,
showDivider: false,
useSafeArea: false,
backgroundColor: Theme.of(context).colorScheme.background,
builder: (_) {
return BlocProvider.value(
value: context.read<SortEditorBloc>(),

View File

@ -86,6 +86,13 @@ class ImagePlaceholderState extends State<ImagePlaceholder> {
popupBuilder: (context) {
return UploadImageMenu(
limitMaximumImageSize: !_isLocalMode(),
supportTypes: const [
UploadImageType.local,
UploadImageType.url,
UploadImageType.unsplash,
UploadImageType.openAI,
UploadImageType.stabilityAI,
],
onSelectedLocalImage: (path) {
controller.close();
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {

View File

@ -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';
@ -90,6 +91,8 @@ class Popover extends StatefulWidget {
/// the conflict won't be resolve by using Listener, we want these two gestures exclusive.
final PopoverClickHandler clickHandler;
final bool skipTraversal;
/// The content area of the popover.
final Widget child;
@ -110,6 +113,7 @@ class Popover extends StatefulWidget {
this.canClose,
this.asBarrier = false,
this.clickHandler = PopoverClickHandler.listener,
this.skipTraversal = false,
});
@override
@ -158,6 +162,7 @@ class PopoverState extends State<Popover> {
popupBuilder: widget.popupBuilder,
onClose: () => close(),
onCloseAll: () => _removeRootOverlay(),
skipTraversal: widget.skipTraversal,
),
);
@ -263,6 +268,7 @@ class PopoverContainer extends StatefulWidget {
final EdgeInsets windowPadding;
final void Function() onClose;
final void Function() onCloseAll;
final bool skipTraversal;
const PopoverContainer({
super.key,
@ -273,6 +279,7 @@ class PopoverContainer extends StatefulWidget {
required this.windowPadding,
required this.onClose,
required this.onCloseAll,
required this.skipTraversal,
});
@override
@ -293,6 +300,7 @@ class PopoverContainerState extends State<PopoverContainer> {
Widget build(BuildContext context) {
return Focus(
autofocus: true,
skipTraversal: widget.skipTraversal,
child: CustomSingleChildLayout(
delegate: PopoverLayoutDelegate(
direction: widget.direction,

View File

@ -26,6 +26,10 @@ class AppFlowyPopover extends StatelessWidget {
/// the conflict won't be resolve by using Listener, we want these two gestures exclusive.
final PopoverClickHandler clickHandler;
/// If true the popover will not participate in focus traversal.
///
final bool skipTraversal;
const AppFlowyPopover({
super.key,
required this.child,
@ -43,6 +47,7 @@ class AppFlowyPopover extends StatelessWidget {
this.windowPadding = const EdgeInsets.all(8.0),
this.decoration,
this.clickHandler = PopoverClickHandler.listener,
this.skipTraversal = false,
});
@override
@ -58,6 +63,7 @@ class AppFlowyPopover extends StatelessWidget {
windowPadding: windowPadding,
offset: offset,
clickHandler: clickHandler,
skipTraversal: skipTraversal,
popupBuilder: (context) {
return _PopoverContainer(
constraints: constraints,

View File

@ -1,12 +1,13 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flowy_infra/size.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/flowy_tooltip.dart';
import 'package:flowy_infra_ui/widget/ignore_parent_gesture.dart';
import 'package:flowy_infra_ui/widget/spacing.dart';
import 'package:flutter/material.dart';
class FlowyButton extends StatelessWidget {
final Widget text;
@ -213,6 +214,7 @@ class FlowyTextButton extends StatelessWidget {
);
child = RawMaterialButton(
focusNode: FocusNode(skipTraversal: onPressed == null),
hoverElevation: 0,
highlightElevation: 0,
shape: RoundedRectangleBorder(borderRadius: radius ?? Corners.s6Border),
@ -237,6 +239,10 @@ class FlowyTextButton extends StatelessWidget {
);
}
if (onPressed == null) {
child = ExcludeFocus(child: child);
}
return child;
}
}

View File

@ -1101,10 +1101,10 @@ packages:
dependency: transitive
description:
name: markdown
sha256: "1b134d9f8ff2da15cb298efe6cd8b7d2a78958c1b00384ebcbdf13fe340a6c90"
sha256: ef2a1298144e3f985cc736b22e0ccdaf188b5b3970648f2d9dc13efd1d9df051
url: "https://pub.dev"
source: hosted
version: "7.2.1"
version: "7.2.2"
matcher:
dependency: transitive
description:

View File

@ -463,7 +463,7 @@
"exportFileFail": "Export file failed!",
"export": "Export",
"clearCache": "Clear cache",
"clearCacheDesc": "Clear the cache including the images, fonts, and other temporary files. This will not delete your data.",
"clearCacheDesc": "If you encounter issues with images not loading or fonts not displaying correctly, try clearing your cache. This action will not remove your user data.",
"areYouSureToClearCache": "Are you sure to clear the cache?",
"clearCacheSuccess": "Cache cleared successfully!"
},
@ -679,7 +679,8 @@
"cannotFindCreatableField": "Cannot find a suitable field to sort by",
"deleteAllSorts": "Delete all sorts",
"addSort": "Add new sort",
"removeSorting": "Would you like to remove sorting?"
"removeSorting": "Would you like to remove sorting?",
"fieldInUse": "You are already sorting by this field"
},
"row": {
"duplicate": "Duplicate",