mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
fix: image url check for embed link (#4826)
* fix: image url check in for embed link * chore: move all patterns to shared * test: prefer enterText over manipulating widget
This commit is contained in:
parent
6e2caf3358
commit
f5cb8b6d25
@ -1,6 +1,9 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
import 'package:appflowy/plugins/base/emoji/emoji_picker.dart';
|
import 'package:appflowy/plugins/base/emoji/emoji_picker.dart';
|
||||||
import 'package:appflowy/plugins/base/emoji/emoji_skin_tone.dart';
|
import 'package:appflowy/plugins/base/emoji/emoji_skin_tone.dart';
|
||||||
@ -14,8 +17,6 @@ import 'package:appflowy/plugins/document/presentation/editor_plugins/image/embe
|
|||||||
import 'package:appflowy/plugins/inline_actions/widgets/inline_actions_handler.dart';
|
import 'package:appflowy/plugins/inline_actions/widgets/inline_actions_handler.dart';
|
||||||
import 'package:appflowy_editor/appflowy_editor.dart' hide Log;
|
import 'package:appflowy_editor/appflowy_editor.dart' hide Log;
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
import 'package:flutter_emoji_mart/flutter_emoji_mart.dart';
|
import 'package:flutter_emoji_mart/flutter_emoji_mart.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
@ -132,8 +133,7 @@ class EditorOperations {
|
|||||||
of: find.byType(EmbedImageUrlWidget),
|
of: find.byType(EmbedImageUrlWidget),
|
||||||
matching: find.byType(TextField),
|
matching: find.byType(TextField),
|
||||||
);
|
);
|
||||||
final textField = tester.widget<TextField>(imageUrlTextField);
|
await tester.enterText(imageUrlTextField, imageUrl);
|
||||||
textField.controller?.text = imageUrl;
|
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
await tester.tapButton(
|
await tester.tapButton(
|
||||||
find.descendant(
|
find.descendant(
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
import 'package:appflowy/mobile/presentation/base/app_bar.dart';
|
import 'package:appflowy/mobile/presentation/base/app_bar.dart';
|
||||||
import 'package:appflowy/mobile/presentation/widgets/flowy_mobile_search_text_field.dart';
|
import 'package:appflowy/mobile/presentation/widgets/flowy_mobile_search_text_field.dart';
|
||||||
import 'package:appflowy/mobile/presentation/widgets/widgets.dart';
|
import 'package:appflowy/mobile/presentation/widgets/widgets.dart';
|
||||||
|
import 'package:appflowy/util/google_font_family_extension.dart';
|
||||||
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
|
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
@ -23,9 +25,7 @@ class FontPickerScreen extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class LanguagePickerPage extends StatefulWidget {
|
class LanguagePickerPage extends StatefulWidget {
|
||||||
const LanguagePickerPage({
|
const LanguagePickerPage({super.key});
|
||||||
super.key,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<LanguagePickerPage> createState() => _LanguagePickerPageState();
|
State<LanguagePickerPage> createState() => _LanguagePickerPageState();
|
||||||
@ -52,6 +52,7 @@ class _LanguagePickerPageState extends State<LanguagePickerPage> {
|
|||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
child: Scrollbar(
|
child: Scrollbar(
|
||||||
child: ListView.builder(
|
child: ListView.builder(
|
||||||
|
itemCount: availableFonts.length + 1, // with search bar
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
if (index == 0) {
|
if (index == 0) {
|
||||||
// search bar
|
// search bar
|
||||||
@ -65,7 +66,8 @@ class _LanguagePickerPageState extends State<LanguagePickerPage> {
|
|||||||
setState(() {
|
setState(() {
|
||||||
availableFonts = _availableFonts
|
availableFonts = _availableFonts
|
||||||
.where(
|
.where(
|
||||||
(element) => parseFontFamilyName(element)
|
(font) => font
|
||||||
|
.parseFontFamilyName()
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.contains(keyword.toLowerCase()),
|
.contains(keyword.toLowerCase()),
|
||||||
)
|
)
|
||||||
@ -75,8 +77,9 @@ class _LanguagePickerPageState extends State<LanguagePickerPage> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final fontFamilyName = availableFonts[index - 1];
|
final fontFamilyName = availableFonts[index - 1];
|
||||||
final displayName = parseFontFamilyName(fontFamilyName);
|
final displayName = fontFamilyName.parseFontFamilyName();
|
||||||
return FlowyOptionTile.checkbox(
|
return FlowyOptionTile.checkbox(
|
||||||
text: displayName,
|
text: displayName,
|
||||||
isSelected: selectedFontFamilyName == fontFamilyName,
|
isSelected: selectedFontFamilyName == fontFamilyName,
|
||||||
@ -86,17 +89,9 @@ class _LanguagePickerPageState extends State<LanguagePickerPage> {
|
|||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
itemCount: availableFonts.length + 1, // with search bar
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
String parseFontFamilyName(String fontFamilyName) {
|
|
||||||
final camelCase = RegExp('(?<=[a-z])[A-Z]');
|
|
||||||
return fontFamilyName
|
|
||||||
.replaceAll('_regular', '')
|
|
||||||
.replaceAllMapped(camelCase, (m) => ' ${m.group(0)}');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ import 'package:appflowy/plugins/database/grid/application/calculations/field_ty
|
|||||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/calculations/calculation_selector.dart';
|
import 'package:appflowy/plugins/database/grid/presentation/widgets/calculations/calculation_selector.dart';
|
||||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/calculations/calculation_type_item.dart';
|
import 'package:appflowy/plugins/database/grid/presentation/widgets/calculations/calculation_type_item.dart';
|
||||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/calculations/remove_calculation_button.dart';
|
import 'package:appflowy/plugins/database/grid/presentation/widgets/calculations/remove_calculation_button.dart';
|
||||||
|
import 'package:appflowy/shared/patterns/common_patterns.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-database2/calculation_entities.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-database2/calculation_entities.pb.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pbenum.dart';
|
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pbenum.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-database2/number_entities.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-database2/number_entities.pb.dart';
|
||||||
@ -132,9 +133,8 @@ class _CalculateCellState extends State<CalculateCell> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String _withoutTrailingZeros(String value) {
|
String _withoutTrailingZeros(String value) {
|
||||||
final regex = RegExp(r'^(\d+(?:\.\d*?[1-9](?=0|\b))?)\.?0*$');
|
if (trailingZerosRegex.hasMatch(value)) {
|
||||||
if (regex.hasMatch(value)) {
|
final match = trailingZerosRegex.firstMatch(value)!;
|
||||||
final match = regex.firstMatch(value)!;
|
|
||||||
return match.group(1)!;
|
return match.group(1)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/copy_and_paste/editor_state_paste_node_extension.dart';
|
import 'package:appflowy/plugins/document/presentation/editor_plugins/copy_and_paste/editor_state_paste_node_extension.dart';
|
||||||
|
import 'package:appflowy/shared/patterns/common_patterns.dart';
|
||||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||||
|
|
||||||
RegExp _hrefRegex = RegExp(
|
|
||||||
r'https?://(?:www\.)?[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,}(?:/[^\s]*)?',
|
|
||||||
);
|
|
||||||
|
|
||||||
extension PasteFromPlainText on EditorState {
|
extension PasteFromPlainText on EditorState {
|
||||||
Future<void> pastePlainText(String plainText) async {
|
Future<void> pastePlainText(String plainText) async {
|
||||||
if (await pasteHtmlIfAvailable(plainText)) {
|
if (await pasteHtmlIfAvailable(plainText)) {
|
||||||
@ -23,7 +20,7 @@ extension PasteFromPlainText on EditorState {
|
|||||||
.map((e) {
|
.map((e) {
|
||||||
// parse the url content
|
// parse the url content
|
||||||
final Attributes attributes = {};
|
final Attributes attributes = {};
|
||||||
if (_hrefRegex.hasMatch(e)) {
|
if (hrefRegex.hasMatch(e)) {
|
||||||
attributes[AppFlowyRichTextKeys.href] = e;
|
attributes[AppFlowyRichTextKeys.href] = e;
|
||||||
}
|
}
|
||||||
return Delta()..insert(e, attributes: attributes);
|
return Delta()..insert(e, attributes: attributes);
|
||||||
@ -45,7 +42,7 @@ extension PasteFromPlainText on EditorState {
|
|||||||
if (selection == null ||
|
if (selection == null ||
|
||||||
!selection.isSingle ||
|
!selection.isSingle ||
|
||||||
selection.isCollapsed ||
|
selection.isCollapsed ||
|
||||||
!_hrefRegex.hasMatch(plainText)) {
|
!hrefRegex.hasMatch(plainText)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
|
import 'package:appflowy/shared/patterns/common_patterns.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
class EmbedImageUrlWidget extends StatefulWidget {
|
class EmbedImageUrlWidget extends StatefulWidget {
|
||||||
const EmbedImageUrlWidget({
|
const EmbedImageUrlWidget({
|
||||||
@ -16,6 +18,7 @@ class EmbedImageUrlWidget extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _EmbedImageUrlWidgetState extends State<EmbedImageUrlWidget> {
|
class _EmbedImageUrlWidgetState extends State<EmbedImageUrlWidget> {
|
||||||
|
bool isUrlValid = true;
|
||||||
String inputText = '';
|
String inputText = '';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -25,8 +28,15 @@ class _EmbedImageUrlWidgetState extends State<EmbedImageUrlWidget> {
|
|||||||
FlowyTextField(
|
FlowyTextField(
|
||||||
hintText: LocaleKeys.document_imageBlock_embedLink_placeholder.tr(),
|
hintText: LocaleKeys.document_imageBlock_embedLink_placeholder.tr(),
|
||||||
onChanged: (value) => inputText = value,
|
onChanged: (value) => inputText = value,
|
||||||
onEditingComplete: () => widget.onSubmit(inputText),
|
onEditingComplete: submit,
|
||||||
),
|
),
|
||||||
|
if (!isUrlValid) ...[
|
||||||
|
const VSpace(8),
|
||||||
|
FlowyText(
|
||||||
|
LocaleKeys.document_plugins_cover_invalidImageUrl.tr(),
|
||||||
|
color: Theme.of(context).colorScheme.error,
|
||||||
|
),
|
||||||
|
],
|
||||||
const VSpace(8),
|
const VSpace(8),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: 160,
|
width: 160,
|
||||||
@ -37,10 +47,20 @@ class _EmbedImageUrlWidgetState extends State<EmbedImageUrlWidget> {
|
|||||||
LocaleKeys.document_imageBlock_embedLink_label.tr(),
|
LocaleKeys.document_imageBlock_embedLink_label.tr(),
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
onTap: () => widget.onSubmit(inputText),
|
onTap: submit,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void submit() {
|
||||||
|
if (checkUrlValidity(inputText)) {
|
||||||
|
return widget.onSubmit(inputText);
|
||||||
|
}
|
||||||
|
|
||||||
|
setState(() => isUrlValid = false);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool checkUrlValidity(String url) => imgUrlRegex.hasMatch(url);
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:appflowy/mobile/presentation/setting/font/font_picker_screen.dart';
|
import 'package:appflowy/mobile/presentation/setting/font/font_picker_screen.dart';
|
||||||
import 'package:appflowy/plugins/document/application/document_appearance_cubit.dart';
|
import 'package:appflowy/plugins/document/application/document_appearance_cubit.dart';
|
||||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/mobile_toolbar_v3/_toolbar_theme.dart';
|
import 'package:appflowy/plugins/document/presentation/editor_plugins/mobile_toolbar_v3/_toolbar_theme.dart';
|
||||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart';
|
import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart';
|
||||||
import 'package:appflowy/util/google_font_family_extension.dart';
|
import 'package:appflowy/util/google_font_family_extension.dart';
|
||||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
const _trailingZerosPattern = r'^(\d+(?:\.\d*?[1-9](?=0|\b))?)\.?0*$';
|
||||||
|
final trailingZerosRegex = RegExp(_trailingZerosPattern);
|
||||||
|
|
||||||
|
const _hrefPattern =
|
||||||
|
r'https?://(?:www\.)?[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,}(?:/[^\s]*)?';
|
||||||
|
final hrefRegex = RegExp(_hrefPattern);
|
||||||
|
|
||||||
|
/// This pattern allows for both HTTP and HTTPS Scheme
|
||||||
|
/// It allows for query parameters
|
||||||
|
/// It only allows the following image extensions: .png, .jpg, .gif, .webm
|
||||||
|
///
|
||||||
|
const _imgUrlPattern =
|
||||||
|
r'(https?:\/\/)([^\s(["<,>/]*)(\/)[^\s[",><]*(.png|.jpg|.gif|.webm)(\?[^\s[",><]*)?';
|
||||||
|
final imgUrlRegex = RegExp(_imgUrlPattern);
|
||||||
|
|
||||||
|
const _appflowyCloudUrlPattern = r'^(https:\/\/)(.*)(\.appflowy\.cloud\/)(.*)';
|
||||||
|
final appflowyCloudUrlRegex = RegExp(_appflowyCloudUrlPattern);
|
||||||
|
|
||||||
|
const _camelCasePattern = '(?<=[a-z])[A-Z]';
|
||||||
|
final camelCaseRegex = RegExp(_camelCasePattern);
|
||||||
|
|
||||||
|
const _macOSVolumesPattern = '^/Volumes/[^/]+';
|
||||||
|
final macOSVolumesRegex = RegExp(_macOSVolumesPattern);
|
@ -0,0 +1,19 @@
|
|||||||
|
/// RegExp to match Twelve Hour formats
|
||||||
|
/// Source: https://stackoverflow.com/a/33906224
|
||||||
|
///
|
||||||
|
/// Matches eg: "05:05 PM", "5:50 Pm", "10:59 am", etc.
|
||||||
|
///
|
||||||
|
const _twelveHourTimePattern =
|
||||||
|
r'\b((1[0-2]|0?[1-9]):([0-5][0-9]) ([AaPp][Mm]))';
|
||||||
|
final twelveHourTimeRegex = RegExp(_twelveHourTimePattern);
|
||||||
|
bool isTwelveHourTime(String? time) => twelveHourTimeRegex.hasMatch(time ?? '');
|
||||||
|
|
||||||
|
/// RegExp to match Twenty Four Hour formats
|
||||||
|
/// Source: https://stackoverflow.com/a/7536768
|
||||||
|
///
|
||||||
|
/// Matches eg: "0:01", "04:59", "16:30", etc.
|
||||||
|
///
|
||||||
|
const _twentyFourHourtimePattern = r'^([0-1]?[0-9]|2[0-3]):[0-5][0-9]$';
|
||||||
|
final tewentyFourHourTimeRegex = RegExp(_twentyFourHourtimePattern);
|
||||||
|
bool isTwentyFourHourTime(String? time) =>
|
||||||
|
tewentyFourHourTimeRegex.hasMatch(time ?? '');
|
@ -1,7 +1,6 @@
|
|||||||
|
import 'package:appflowy/shared/patterns/common_patterns.dart';
|
||||||
|
|
||||||
extension GoogleFontsParser on String {
|
extension GoogleFontsParser on String {
|
||||||
String parseFontFamilyName() {
|
String parseFontFamilyName() => replaceAll('_regular', '')
|
||||||
final camelCase = RegExp('(?<=[a-z])[A-Z]');
|
.replaceAllMapped(camelCaseRegex, (m) => ' ${m.group(0)}');
|
||||||
return replaceAll('_regular', '')
|
|
||||||
.replaceAllMapped(camelCase, (m) => ' ${m.group(0)}');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,8 @@ import 'dart:io';
|
|||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import 'package:appflowy/shared/patterns/common_patterns.dart';
|
||||||
|
|
||||||
extension StringExtension on String {
|
extension StringExtension on String {
|
||||||
static const _specialCharacters = r'\/:*?"<>| ';
|
static const _specialCharacters = r'\/:*?"<>| ';
|
||||||
|
|
||||||
@ -31,8 +33,6 @@ extension StringExtension on String {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns if the string is a appflowy cloud url.
|
/// Returns true if the string is a appflowy cloud url.
|
||||||
bool get isAppFlowyCloudUrl {
|
bool get isAppFlowyCloudUrl => appflowyCloudUrlRegex.hasMatch(this);
|
||||||
return RegExp(r'^(https:\/\/)(.*)(\.appflowy\.cloud\/)(.*)').hasMatch(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
import 'package:appflowy/core/config/kv.dart';
|
import 'package:appflowy/core/config/kv.dart';
|
||||||
import 'package:appflowy/core/config/kv_keys.dart';
|
import 'package:appflowy/core/config/kv_keys.dart';
|
||||||
|
import 'package:appflowy/shared/patterns/common_patterns.dart';
|
||||||
import 'package:appflowy/startup/startup.dart';
|
import 'package:appflowy/startup/startup.dart';
|
||||||
import 'package:appflowy_backend/log.dart';
|
import 'package:appflowy_backend/log.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:path/path.dart' as p;
|
import 'package:path/path.dart' as p;
|
||||||
|
|
||||||
import '../../../startup/tasks/prelude.dart';
|
import '../../../startup/tasks/prelude.dart';
|
||||||
@ -26,7 +28,7 @@ class ApplicationDataStorage {
|
|||||||
|
|
||||||
if (Platform.isMacOS) {
|
if (Platform.isMacOS) {
|
||||||
// remove the prefix `/Volumes/*`
|
// remove the prefix `/Volumes/*`
|
||||||
path = path.replaceFirst(RegExp('^/Volumes/[^/]+'), '');
|
path = path.replaceFirst(macOSVolumesRegex, '');
|
||||||
} else if (Platform.isWindows) {
|
} else if (Platform.isWindows) {
|
||||||
path = path.replaceAll('/', '\\');
|
path = path.replaceAll('/', '\\');
|
||||||
}
|
}
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
/// RegExp to match Twelve Hour formats
|
|
||||||
/// Source: https://stackoverflow.com/a/33906224
|
|
||||||
///
|
|
||||||
/// Matches eg: "05:05 PM", "5:50 Pm", "10:59 am", etc.
|
|
||||||
///
|
|
||||||
final _twelveHourTimePattern =
|
|
||||||
RegExp(r'\b((1[0-2]|0?[1-9]):([0-5][0-9]) ([AaPp][Mm]))');
|
|
||||||
bool isTwelveHourTime(String? time) =>
|
|
||||||
_twelveHourTimePattern.hasMatch(time ?? '');
|
|
||||||
|
|
||||||
/// RegExp to match Twenty Four Hour formats
|
|
||||||
/// Source: https://stackoverflow.com/a/7536768
|
|
||||||
///
|
|
||||||
/// Matches eg: "0:01", "04:59", "16:30", etc.
|
|
||||||
///
|
|
||||||
final _twentyFourHourtimePattern = RegExp(r'^([0-1]?[0-9]|2[0-3]):[0-5][0-9]$');
|
|
||||||
bool isTwentyFourHourTime(String? time) =>
|
|
||||||
_twentyFourHourtimePattern.hasMatch(time ?? '');
|
|
@ -95,7 +95,7 @@ class _FontFamilyDropDownState extends State<FontFamilyDropDown> {
|
|||||||
return FlowySettingValueDropDown(
|
return FlowySettingValueDropDown(
|
||||||
popoverKey: ThemeFontFamilySetting.popoverKey,
|
popoverKey: ThemeFontFamilySetting.popoverKey,
|
||||||
popoverController: widget.popoverController,
|
popoverController: widget.popoverController,
|
||||||
currentValue: parseFontFamilyName(widget.currentFontFamily),
|
currentValue: widget.currentFontFamily.parseFontFamilyName(),
|
||||||
onClose: () {
|
onClose: () {
|
||||||
query.value = '';
|
query.value = '';
|
||||||
widget.onClose?.call();
|
widget.onClose?.call();
|
||||||
@ -162,18 +162,11 @@ class _FontFamilyDropDownState extends State<FontFamilyDropDown> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
String parseFontFamilyName(String fontFamilyName) {
|
|
||||||
final camelCase = RegExp('(?<=[a-z])[A-Z]');
|
|
||||||
return fontFamilyName
|
|
||||||
.replaceAll('_regular', '')
|
|
||||||
.replaceAllMapped(camelCase, (m) => ' ${m.group(0)}');
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _fontFamilyItemButton(
|
Widget _fontFamilyItemButton(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
TextStyle style,
|
TextStyle style,
|
||||||
) {
|
) {
|
||||||
final buttonFontFamily = parseFontFamilyName(style.fontFamily!);
|
final buttonFontFamily = style.fontFamily!.parseFontFamilyName();
|
||||||
|
|
||||||
return Tooltip(
|
return Tooltip(
|
||||||
message: buttonFontFamily,
|
message: buttonFontFamily,
|
||||||
@ -184,21 +177,19 @@ class _FontFamilyDropDownState extends State<FontFamilyDropDown> {
|
|||||||
child: FlowyButton(
|
child: FlowyButton(
|
||||||
onHover: (_) => FocusScope.of(context).unfocus(),
|
onHover: (_) => FocusScope.of(context).unfocus(),
|
||||||
text: FlowyText.medium(
|
text: FlowyText.medium(
|
||||||
parseFontFamilyName(style.fontFamily!),
|
buttonFontFamily,
|
||||||
fontFamily: style.fontFamily!,
|
fontFamily: style.fontFamily!,
|
||||||
),
|
),
|
||||||
rightIcon:
|
rightIcon:
|
||||||
buttonFontFamily == parseFontFamilyName(widget.currentFontFamily)
|
buttonFontFamily == widget.currentFontFamily.parseFontFamilyName()
|
||||||
? const FlowySvg(
|
? const FlowySvg(FlowySvgs.check_s)
|
||||||
FlowySvgs.check_s,
|
|
||||||
)
|
|
||||||
: null,
|
: null,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (widget.onFontFamilyChanged != null) {
|
if (widget.onFontFamilyChanged != null) {
|
||||||
widget.onFontFamilyChanged!(style.fontFamily!);
|
widget.onFontFamilyChanged!(style.fontFamily!);
|
||||||
} else {
|
} else {
|
||||||
final fontFamily = style.fontFamily!.parseFontFamilyName();
|
final fontFamily = style.fontFamily!.parseFontFamilyName();
|
||||||
if (parseFontFamilyName(widget.currentFontFamily) !=
|
if (widget.currentFontFamily.parseFontFamilyName() !=
|
||||||
buttonFontFamily) {
|
buttonFontFamily) {
|
||||||
context
|
context
|
||||||
.read<AppearanceSettingsCubit>()
|
.read<AppearanceSettingsCubit>()
|
||||||
|
Loading…
Reference in New Issue
Block a user