fix: some mobile search UI issues (#7845)

* fix: some mobile search UI issues

* fix: some icon colors

* fix: show 'Add Link' while creating a link on mobile

* fix: add tapping event for mobile embed link

* fix: bookmark can not open the non-http link

* fix: improve the behavior of links on mobile

* fix: switch workspace will refresh the reminders

* chore: remove unused import

* fix: test error
This commit is contained in:
Morn 2025-04-29 12:52:28 +08:00 committed by GitHub
parent 63aa5112ba
commit d28987d16d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 223 additions and 174 deletions

View File

@ -53,6 +53,7 @@ class _MobileBottomSheetEditLinkWidgetState
ViewPB? currentView;
bool showErrorText = false;
bool showRemoveLink = false;
String title = LocaleKeys.editor_editLink.tr();
AppFlowyThemeData get theme => AppFlowyTheme.of(context);
@ -73,6 +74,7 @@ class _MobileBottomSheetEditLinkWidgetState
)..searchRecentViews();
if (linkInfo.link.isEmpty) {
isShowingSearchResult = true;
title = LocaleKeys.toolbar_addLink.tr();
} else {
showRemoveLink = true;
textFocusNode.requestFocus();
@ -104,7 +106,7 @@ class _MobileBottomSheetEditLinkWidgetState
child: Column(
children: [
BottomSheetHeader(
title: LocaleKeys.editor_editLink.tr(),
title: title,
onClose: () => context.pop(),
confirmButton: FlowyTextButton(
LocaleKeys.button_done.tr(),

View File

@ -1,9 +1,11 @@
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/workspace/application/command_palette/command_palette_bloc.dart';
import 'package:appflowy/workspace/application/command_palette/search_result_ext.dart';
import 'package:appflowy/workspace/application/view/view_ext.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-search/result.pb.dart';
import 'package:appflowy_ui/appflowy_ui.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/widget/spacing.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
@ -20,12 +22,18 @@ class MobileSearchResultCell extends StatelessWidget {
final theme = AppFlowyTheme.of(context),
textColor = theme.textColorScheme.primary;
final commandPaletteState = context.read<CommandPaletteBloc>().state;
final displayName = item.displayName.isEmpty
? LocaleKeys.menuAppHeader_defaultNewPageName.tr()
: item.displayName;
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 12),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
buildIcon(theme),
SizedBox.square(
dimension: 24,
child: Center(child: buildIcon(theme)),
),
HSpace(12),
Flexible(
child: Column(
@ -35,7 +43,7 @@ class MobileSearchResultCell extends StatelessWidget {
maxLines: 1,
overflow: TextOverflow.ellipsis,
text: buildHighLightSpan(
content: item.displayName,
content: displayName,
normal: theme.textStyle.heading4.standard(color: textColor),
highlight: theme.textStyle.heading4
.standard(color: textColor)
@ -45,7 +53,7 @@ class MobileSearchResultCell extends StatelessWidget {
),
),
buildPath(commandPaletteState, theme),
buildSummary(theme),
...buildSummary(theme),
],
),
),
@ -84,24 +92,25 @@ class MobileSearchResultCell extends StatelessWidget {
);
}
Widget buildSummary(AppFlowyThemeData theme) {
if (item.content.isEmpty) {
return const SizedBox.shrink();
}
return RichText(
maxLines: 3,
overflow: TextOverflow.ellipsis,
text: buildHighLightSpan(
content: item.content,
normal: theme.textStyle.heading4
.standard(color: theme.textColorScheme.secondary),
highlight: theme.textStyle.heading4
.standard(color: theme.textColorScheme.primary)
.copyWith(
backgroundColor: theme.fillColorScheme.themeSelect,
),
List<Widget> buildSummary(AppFlowyThemeData theme) {
if (item.content.isEmpty) return [];
return [
VSpace(theme.spacing.m),
RichText(
maxLines: 3,
overflow: TextOverflow.ellipsis,
text: buildHighLightSpan(
content: item.content,
normal: theme.textStyle.heading4
.standard(color: theme.textColorScheme.secondary),
highlight: theme.textStyle.heading4
.standard(color: theme.textColorScheme.primary)
.copyWith(
backgroundColor: theme.fillColorScheme.themeSelect,
),
),
),
);
];
}
TextSpan buildHighLightSpan({

View File

@ -55,7 +55,7 @@ class _MobileSearchTextfieldState extends State<MobileSearchTextfield> {
Widget build(BuildContext context) {
final theme = AppFlowyTheme.of(context);
return SizedBox(
height: 40,
height: 42,
child: ValueListenableBuilder(
valueListenable: controller,
builder: (context, _, __) {
@ -84,12 +84,17 @@ class _MobileSearchTextfieldState extends State<MobileSearchTextfield> {
if (!hasFocus || !hasText) return SizedBox.shrink();
return GestureDetector(
onTap: () => focusNode.unfocus(),
child: Padding(
behavior: HitTestBehavior.opaque,
child: Container(
height: 42,
padding: EdgeInsets.only(left: 8),
child: Text(
LocaleKeys.button_cancel.tr(),
style: theme.textStyle.body
.standard(color: theme.textColorScheme.action),
child: Align(
alignment: Alignment.centerLeft,
child: Text(
LocaleKeys.button_cancel.tr(),
style: theme.textStyle.body
.standard(color: theme.textColorScheme.action),
),
),
),
);

View File

@ -2,6 +2,7 @@ import 'dart:math';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:appflowy_ui/appflowy_ui.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
@ -328,6 +329,7 @@ class _MobileSelectionMenuWidgetState extends State<MobileSelectionMenuWidget> {
}
Widget _buildNoResultsWidget(BuildContext context) {
final theme = AppFlowyTheme.of(context);
return DecoratedBox(
decoration: BoxDecoration(
color: Theme.of(context).cardColor,
@ -350,7 +352,10 @@ class _MobileSelectionMenuWidgetState extends State<MobileSelectionMenuWidget> {
child: Center(
child: Text(
LocaleKeys.inlineActions_noResults.tr(),
style: TextStyle(fontSize: 18.0, color: Color(0x801F2225)),
style: TextStyle(
fontSize: 18.0,
color: theme.textColorScheme.primary,
),
textAlign: TextAlign.center,
),
),

View File

@ -12,6 +12,7 @@ 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 'package:universal_platform/universal_platform.dart';
import 'link_embed_menu.dart';
@ -156,7 +157,9 @@ class LinkEmbedBlockComponentState
child: ValueListenableBuilder<bool>(
valueListenable: showActionsNotifier,
builder: (context, showActions, child) {
if (!showActions) return SizedBox.shrink();
if (!showActions || UniversalPlatform.isMobile) {
return SizedBox.shrink();
}
return LinkEmbedMenu(
editorState: context.read<EditorState>(),
node: node,
@ -189,11 +192,19 @@ class LinkEmbedBlockComponentState
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: ClipRRect(
borderRadius: const BorderRadius.vertical(top: Radius.circular(16)),
child: FlowyNetworkImage(
url: linkInfo.imageUrl ?? '',
width: MediaQuery.of(context).size.width,
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: !UniversalPlatform.isMobile
? null
: () =>
afLaunchUrlString(url, addingHttpSchemeWhenFailed: true),
child: ClipRRect(
borderRadius:
const BorderRadius.vertical(top: Radius.circular(16)),
child: FlowyNetworkImage(
url: linkInfo.imageUrl ?? '',
width: MediaQuery.of(context).size.width,
),
),
),
),
@ -258,46 +269,53 @@ class LinkEmbedBlockComponentState
child: CircularProgressIndicator.adaptive(),
),
)
: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SvgPicture.asset(
FlowySvgs.embed_error_xl.path,
),
VSpace(4),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: RichText(
maxLines: 1,
overflow: TextOverflow.ellipsis,
text: TextSpan(
children: [
TextSpan(
text: '$url ',
style: TextStyle(
color: textSceme.secondary,
fontSize: 14,
height: 20 / 14,
fontWeight: FontWeight.w700,
: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: !UniversalPlatform.isMobile
? null
: () =>
afLaunchUrlString(url, addingHttpSchemeWhenFailed: true),
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SvgPicture.asset(
FlowySvgs.embed_error_xl.path,
),
VSpace(4),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: RichText(
maxLines: 1,
overflow: TextOverflow.ellipsis,
text: TextSpan(
children: [
TextSpan(
text: '$url ',
style: TextStyle(
color: textSceme.secondary,
fontSize: 14,
height: 20 / 14,
fontWeight: FontWeight.w700,
),
),
),
TextSpan(
text: LocaleKeys
.document_plugins_linkPreview_linkPreviewMenu_unableToDisplay
.tr(),
style: TextStyle(
color: textSceme.secondary,
fontSize: 14,
height: 20 / 14,
fontWeight: FontWeight.w400,
TextSpan(
text: LocaleKeys
.document_plugins_linkPreview_linkPreviewMenu_unableToDisplay
.tr(),
style: TextStyle(
color: textSceme.secondary,
fontSize: 14,
height: 20 / 14,
fontWeight: FontWeight.w400,
),
),
),
],
],
),
),
),
),
],
],
),
),
);
}

View File

@ -122,7 +122,8 @@ class CustomLinkPreviewWidget extends StatelessWidget {
return MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
onTap: () => afLaunchUrlString(url),
behavior: HitTestBehavior.opaque,
onTap: () => afLaunchUrlString(url, addingHttpSchemeWhenFailed: true),
child: child,
),
);
@ -133,7 +134,8 @@ class CustomLinkPreviewWidget extends StatelessWidget {
editorState: context.read<EditorState>(),
extendActionWidgets: _buildExtendActionWidgets(context),
child: GestureDetector(
onTap: () => afLaunchUrlString(url),
behavior: HitTestBehavior.opaque,
onTap: () => afLaunchUrlString(url, addingHttpSchemeWhenFailed: true),
child: child,
),
);

View File

@ -4,6 +4,7 @@ import 'package:appflowy/plugins/document/presentation/editor_plugins/link_previ
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:appflowy_editor_plugins/appflowy_editor_plugins.dart';
import 'package:flutter/material.dart';
import 'package:universal_platform/universal_platform.dart';
import 'custom_link_preview.dart';
import 'default_selectable_mixin.dart';
@ -148,7 +149,7 @@ class CustomLinkPreviewBlockComponentState
child = Stack(
children: [
child,
if (showActions)
if (showActions && UniversalPlatform.isDesktopOrWeb)
Positioned(
top: 12,
right: 12,

View File

@ -13,6 +13,7 @@ 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/services.dart';
import 'package:universal_platform/universal_platform.dart';
import 'mention_link_error_preview.dart';
import 'mention_link_preview.dart';
@ -91,6 +92,10 @@ class _MentionLinkBlockState extends State<MentionLinkBlock> {
@override
Widget build(BuildContext context) {
final child = buildIconWithTitle(context);
if (UniversalPlatform.isMobile) return child;
return AppFlowyPopover(
key: ValueKey(showAtBottom),
controller: previewController,
@ -143,7 +148,12 @@ class _MentionLinkBlockState extends State<MentionLinkBlock> {
onRemoveLink: removeLink,
onOpenLink: openLink,
),
child: buildIconWithTitle(context),
child: MouseRegion(
cursor: SystemMouseCursors.click,
onEnter: onEnter,
onExit: onExit,
child: child,
),
);
}
@ -151,50 +161,44 @@ class _MentionLinkBlockState extends State<MentionLinkBlock> {
final theme = AppFlowyTheme.of(context);
final siteName = linkInfo.siteName, linkTitle = linkInfo.title ?? url;
return MouseRegion(
cursor: SystemMouseCursors.click,
onEnter: onEnter,
onExit: onExit,
child: GestureDetector(
onTap: () async {
await afLaunchUrlString(url, addingHttpSchemeWhenFailed: true);
},
child: FlowyHoverContainer(
style:
HoverStyle(hoverColor: Theme.of(context).colorScheme.secondary),
applyStyle: isHovering,
child: Row(
mainAxisSize: MainAxisSize.min,
key: key,
children: [
HSpace(2),
buildIcon(),
HSpace(4),
Flexible(
child: RichText(
overflow: TextOverflow.ellipsis,
text: TextSpan(
children: [
if (siteName != null) ...[
TextSpan(
text: siteName,
style: theme.textStyle.body
.standard(color: theme.textColorScheme.secondary),
),
WidgetSpan(child: HSpace(2)),
],
return GestureDetector(
onTap: () async {
await afLaunchUrlString(url, addingHttpSchemeWhenFailed: true);
},
child: FlowyHoverContainer(
style: HoverStyle(hoverColor: Theme.of(context).colorScheme.secondary),
applyStyle: isHovering,
child: Row(
mainAxisSize: MainAxisSize.min,
key: key,
children: [
HSpace(2),
buildIcon(),
HSpace(4),
Flexible(
child: RichText(
overflow: TextOverflow.ellipsis,
text: TextSpan(
children: [
if (siteName != null) ...[
TextSpan(
text: linkTitle,
text: siteName,
style: theme.textStyle.body
.standard(color: theme.textColorScheme.primary),
.standard(color: theme.textColorScheme.secondary),
),
WidgetSpan(child: HSpace(2)),
],
),
TextSpan(
text: linkTitle,
style: theme.textStyle.body
.standard(color: theme.textColorScheme.primary),
),
],
),
),
HSpace(2),
],
),
),
HSpace(2),
],
),
),
);

View File

@ -37,7 +37,6 @@ class ReminderBloc extends Bloc<ReminderEvent, ReminderState> {
_listener = AppLifecycleListener(
onResume: () {
if (!isClosed) {
add(const ReminderEvent.refresh());
add(const ReminderEvent.resetTimer());
}
},
@ -437,15 +436,11 @@ class ReminderBloc extends Bloc<ReminderEvent, ReminderState> {
Future<bool> checkReminderAvailable(
ReminderPB reminder,
Set<String> reminderIds, {
Set<String>? removedIds,
}) async {
Set<String> reminderIds,
) async {
/// blockId is null means no node
final blockId = reminder.meta[ReminderMetaKeys.blockId];
if (blockId == null) {
removedIds?.add(reminder.id);
return false;
}
if (blockId == null) return false;
/// check if schedule time is comming
final scheduledAt = reminder.scheduledAt.toDateTime();
@ -457,19 +452,13 @@ class ReminderBloc extends Bloc<ReminderEvent, ReminderState> {
final viewId = reminder.objectId;
final view =
await ViewBackendService.getView(viewId).fold((s) => s, (_) => null);
if (view == null) {
removedIds?.add(reminder.id);
return false;
}
if (view == null) return false;
/// check if document is not null
final document = await DocumentService()
.openDocument(documentId: viewId)
.fold((s) => s.toDocument(), (_) => null);
if (document == null) {
removedIds?.add(reminder.id);
return false;
}
if (document == null) return false;
Node? searchById(Node current, String id) {
if (current.id == id) {
return current;
@ -488,10 +477,7 @@ class ReminderBloc extends Bloc<ReminderEvent, ReminderState> {
/// check if node is not null
final node = searchById(document.root, blockId);
if (node == null) {
removedIds?.add(reminder.id);
return false;
}
if (node == null) return false;
final textInserts = node.delta?.whereType<TextInsert>();
if (textInserts == null) return false;
for (final text in textInserts) {
@ -502,8 +488,6 @@ class ReminderBloc extends Bloc<ReminderEvent, ReminderState> {
return true;
}
}
removedIds?.add(reminder.id);
return false;
}
@ -512,19 +496,11 @@ class ReminderBloc extends Bloc<ReminderEvent, ReminderState> {
) async {
final List<ReminderPB> availableReminders = [];
final reminderIds = reminders.map((e) => e.id).toSet();
final removedIds = <String>{};
for (final r in reminders) {
if (await checkReminderAvailable(
r,
reminderIds,
removedIds: removedIds,
)) {
if (await checkReminderAvailable(r, reminderIds)) {
availableReminders.add(r);
}
}
for (final id in removedIds) {
add(ReminderEvent.remove(reminderId: id));
}
return availableReminders;
}
}

View File

@ -1,5 +1,7 @@
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/shared/feature_flags.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/user/application/reminder/reminder_bloc.dart';
import 'package:appflowy/user/application/user_listener.dart';
import 'package:appflowy/user/application/user_service.dart';
import 'package:appflowy_backend/log.dart';
@ -285,6 +287,7 @@ class UserWorkspaceBloc extends Bloc<UserWorkspaceEvent, UserWorkspaceState> {
),
),
);
getIt<ReminderBloc>().add(ReminderEvent.started());
},
renameWorkspace: (workspaceId, name) async {
final result =

View File

@ -276,7 +276,7 @@ class DesktopHomeScreen extends StatelessWidget {
.animatedPanelX(
closeX: -layout.notificationPanelWidth,
isClosed: !layout.showNotificationPanel,
curve: Curves.easeInOut,
curve: Curves.easeOutQuad,
duration: layout.animDuration.inMilliseconds * 0.001,
)
.positioned(

View File

@ -6,6 +6,7 @@ import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/workspace/application/home/home_setting_bloc.dart';
import 'package:appflowy/workspace/application/menu/sidebar_sections_bloc.dart';
import 'package:appflowy/workspace/presentation/home/home_sizes.dart';
import 'package:appflowy_ui/appflowy_ui.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/style_widget/hover.dart';
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
@ -83,6 +84,7 @@ class SidebarTopMenu extends StatelessWidget {
),
],
);
final theme = AppFlowyTheme.of(context);
return ValueListenableBuilder(
valueListenable: isSidebarOnHover,
@ -97,10 +99,12 @@ class SidebarTopMenu extends StatelessWidget {
onPointerDown: (_) =>
context.read<HomeSettingBloc>().collapseMenu(),
child: FlowyHover(
child: Container(
child: SizedBox(
width: 24,
padding: const EdgeInsets.all(4),
child: const FlowySvg(FlowySvgs.hide_menu_s),
child: FlowySvg(
FlowySvgs.double_back_arrow_m,
color: theme.iconColorScheme.secondary,
),
),
),
),

View File

@ -4,6 +4,7 @@ import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/workspace/application/home/home_setting_bloc.dart';
import 'package:appflowy/workspace/presentation/home/home_stack.dart';
import 'package:appflowy_ui/appflowy_ui.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/size.dart';
import 'package:flowy_infra_ui/style_widget/icon_button.dart';
@ -81,6 +82,7 @@ class FlowyNavigation extends StatelessWidget {
),
],
);
final theme = AppFlowyTheme.of(context);
return Padding(
padding: const EdgeInsets.only(right: 8.0),
child: SizedBox(
@ -101,8 +103,10 @@ class FlowyNavigation extends StatelessWidget {
child: FlowyIconButton(
width: 24,
onPressed: () {},
iconPadding: const EdgeInsets.all(4),
icon: const FlowySvg(FlowySvgs.hide_menu_s),
icon: FlowySvg(
FlowySvgs.double_back_arrow_m,
color: theme.iconColorScheme.secondary,
),
),
),
),

View File

@ -99,32 +99,37 @@ class _NotificationPanelState extends State<NotificationPanel>
Widget buildTitle({
required BuildContext context,
required VoidCallback onHide,
}) =>
Container(
padding: const EdgeInsets.symmetric(horizontal: 16),
height: 24,
child: Row(
children: [
FlowyText.medium(
LocaleKeys.notificationHub_title.tr(),
fontSize: 16,
figmaLineHeight: 24,
}) {
final theme = AppFlowyTheme.of(context);
return Container(
padding: const EdgeInsets.symmetric(horizontal: 16),
height: 24,
child: Row(
children: [
FlowyText.medium(
LocaleKeys.notificationHub_title.tr(),
fontSize: 16,
figmaLineHeight: 24,
),
Spacer(),
FlowyIconButton(
width: 24,
icon: FlowySvg(
FlowySvgs.double_back_arrow_m,
color: theme.iconColorScheme.secondary,
),
Spacer(),
FlowyIconButton(
icon: FlowySvg(FlowySvgs.hide_menu_s),
width: 24,
richTooltipText: colappsedButtonTooltip(context),
onPressed: onHide,
iconPadding: const EdgeInsets.all(4),
),
HSpace(8),
buildMoreActionButton(context),
],
),
);
richTooltipText: colappsedButtonTooltip(context),
onPressed: onHide,
),
HSpace(8),
buildMoreActionButton(context),
],
),
);
}
Widget buildMoreActionButton(BuildContext context) {
final theme = AppFlowyTheme.of(context);
return AppFlowyPopover(
constraints: BoxConstraints.loose(const Size(240, 78)),
offset: const Offset(-24, 24),
@ -134,13 +139,15 @@ class _NotificationPanelState extends State<NotificationPanel>
onClose: () => keepEditorFocusNotifier.decrease(),
popupBuilder: (_) => buildMoreActions(),
child: FlowyIconButton(
icon: FlowySvg(FlowySvgs.three_dots_s),
width: 24,
icon: FlowySvg(
FlowySvgs.three_dots_m,
color: theme.iconColorScheme.secondary,
),
onPressed: () {
keepEditorFocusNotifier.increase();
moreActionController.show();
},
iconPadding: const EdgeInsets.all(4),
),
);
}

View File

@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.25 6.75L6 12L11.25 17.25" stroke="#171717" stroke-width="1.2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M17.4375 6.75L12.1875 12L17.4375 17.25" stroke="#171717" stroke-width="1.2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 407 B

View File

@ -0,0 +1,5 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.325 12.0004C8.325 12.746 7.72058 13.3504 6.975 13.3504C6.22942 13.3504 5.625 12.746 5.625 12.0004C5.625 11.2548 6.22942 10.6504 6.975 10.6504C7.72058 10.6504 8.325 11.2548 8.325 12.0004Z" fill="#171717"/>
<path d="M13.3499 12.0004C13.3499 12.746 12.7455 13.3504 11.9999 13.3504C11.2543 13.3504 10.6499 12.746 10.6499 12.0004C10.6499 11.2548 11.2543 10.6504 11.9999 10.6504C12.7455 10.6504 13.3499 11.2548 13.3499 12.0004Z" fill="#171717"/>
<path d="M18.375 12.0004C18.375 12.746 17.7706 13.3504 17.025 13.3504C16.2795 13.3504 15.675 12.746 15.675 12.0004C15.675 11.2548 16.2795 10.6504 17.025 10.6504C17.7706 10.6504 18.375 11.2548 18.375 12.0004Z" fill="#171717"/>
</svg>

After

Width:  |  Height:  |  Size: 781 B