mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
fix: launch review issues (#4960)
* fix: i18n for multi select condition list * fix: lookup url cell content to assert validity * fix: compromise checkmark color
This commit is contained in:
parent
27ff5f07ab
commit
99ee60a60d
@ -1,4 +1,5 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:appflowy/plugins/database/application/cell/cell_controller_builder.dart';
|
import 'package:appflowy/plugins/database/application/cell/cell_controller_builder.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-database2/url_entities.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-database2/url_entities.pb.dart';
|
||||||
@ -29,15 +30,17 @@ class URLCellBloc extends Bloc<URLCellEvent, URLCellState> {
|
|||||||
void _dispatch() {
|
void _dispatch() {
|
||||||
on<URLCellEvent>(
|
on<URLCellEvent>(
|
||||||
(event, emit) async {
|
(event, emit) async {
|
||||||
event.when(
|
await event.when(
|
||||||
initial: () {
|
initial: () {
|
||||||
_startListening();
|
_startListening();
|
||||||
},
|
},
|
||||||
didReceiveCellUpdate: (cellData) {
|
didReceiveCellUpdate: (cellData) async {
|
||||||
|
final content = cellData?.content ?? "";
|
||||||
|
final isValid = await isUrlValid(content);
|
||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
content: cellData?.content ?? "",
|
content: content,
|
||||||
url: cellData?.url ?? "",
|
isValid: isValid,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -58,6 +61,35 @@ class URLCellBloc extends Bloc<URLCellEvent, URLCellState> {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<bool> isUrlValid(String content) async {
|
||||||
|
if (content.isEmpty) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// check protocol is provided
|
||||||
|
const linkPrefix = [
|
||||||
|
'http://',
|
||||||
|
'https://',
|
||||||
|
'file://',
|
||||||
|
'ftp://',
|
||||||
|
'ftps://',
|
||||||
|
'mailto:',
|
||||||
|
];
|
||||||
|
final shouldAddScheme =
|
||||||
|
!linkPrefix.any((pattern) => content.startsWith(pattern));
|
||||||
|
final url = shouldAddScheme ? 'http://$content' : content;
|
||||||
|
|
||||||
|
// get hostname and check validity
|
||||||
|
final uri = Uri.parse(url);
|
||||||
|
final hostName = uri.host;
|
||||||
|
await InternetAddress.lookup(hostName);
|
||||||
|
} catch (_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@freezed
|
@freezed
|
||||||
@ -72,14 +104,14 @@ class URLCellEvent with _$URLCellEvent {
|
|||||||
class URLCellState with _$URLCellState {
|
class URLCellState with _$URLCellState {
|
||||||
const factory URLCellState({
|
const factory URLCellState({
|
||||||
required String content,
|
required String content,
|
||||||
required String url,
|
required bool isValid,
|
||||||
}) = _URLCellState;
|
}) = _URLCellState;
|
||||||
|
|
||||||
factory URLCellState.initial(URLCellController context) {
|
factory URLCellState.initial(URLCellController context) {
|
||||||
final cellData = context.getCellData();
|
final cellData = context.getCellData();
|
||||||
return URLCellState(
|
return URLCellState(
|
||||||
content: cellData?.content ?? "",
|
content: cellData?.content ?? "",
|
||||||
url: cellData?.url ?? "",
|
isValid: true,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -96,9 +96,9 @@ extension SelectOptionFilterConditionPBExtension
|
|||||||
SelectOptionFilterConditionPB.OptionIsNot =>
|
SelectOptionFilterConditionPB.OptionIsNot =>
|
||||||
LocaleKeys.grid_selectOptionFilter_isNot.tr(),
|
LocaleKeys.grid_selectOptionFilter_isNot.tr(),
|
||||||
SelectOptionFilterConditionPB.OptionContains =>
|
SelectOptionFilterConditionPB.OptionContains =>
|
||||||
LocaleKeys.grid_selectOptionFilter_isNot.tr(),
|
LocaleKeys.grid_selectOptionFilter_contains.tr(),
|
||||||
SelectOptionFilterConditionPB.OptionDoesNotContain =>
|
SelectOptionFilterConditionPB.OptionDoesNotContain =>
|
||||||
LocaleKeys.grid_selectOptionFilter_isNot.tr(),
|
LocaleKeys.grid_selectOptionFilter_doesNotContain.tr(),
|
||||||
SelectOptionFilterConditionPB.OptionIsEmpty =>
|
SelectOptionFilterConditionPB.OptionIsEmpty =>
|
||||||
LocaleKeys.grid_selectOptionFilter_isEmpty.tr(),
|
LocaleKeys.grid_selectOptionFilter_isEmpty.tr(),
|
||||||
SelectOptionFilterConditionPB.OptionIsNotEmpty =>
|
SelectOptionFilterConditionPB.OptionIsNotEmpty =>
|
||||||
|
@ -138,9 +138,8 @@ class _DatabaseList extends StatelessWidget {
|
|||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
rightIcon: meta.databaseId == currentDatabaseId
|
rightIcon: meta.databaseId == currentDatabaseId
|
||||||
? FlowySvg(
|
? const FlowySvg(
|
||||||
FlowySvgs.check_s,
|
FlowySvgs.check_s,
|
||||||
color: Theme.of(context).colorScheme.primary,
|
|
||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
),
|
),
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
@ -24,9 +25,6 @@ import '../desktop_row_detail/desktop_row_detail_url_cell.dart';
|
|||||||
import '../mobile_grid/mobile_grid_url_cell.dart';
|
import '../mobile_grid/mobile_grid_url_cell.dart';
|
||||||
import '../mobile_row_detail/mobile_row_detail_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 {
|
abstract class IEditableURLCellSkin {
|
||||||
const IEditableURLCellSkin();
|
const IEditableURLCellSkin();
|
||||||
|
|
||||||
@ -134,8 +132,8 @@ class _GridURLCellState extends GridEditableTextCell<EditableURLCell> {
|
|||||||
Future<void> focusChanged() async {
|
Future<void> focusChanged() async {
|
||||||
if (mounted &&
|
if (mounted &&
|
||||||
!cellBloc.isClosed &&
|
!cellBloc.isClosed &&
|
||||||
cellBloc.state.content != _textEditingController.text.trim()) {
|
cellBloc.state.content != _textEditingController.text) {
|
||||||
cellBloc.add(URLCellEvent.updateURL(_textEditingController.text.trim()));
|
cellBloc.add(URLCellEvent.updateURL(_textEditingController.text));
|
||||||
}
|
}
|
||||||
return super.focusChanged();
|
return super.focusChanged();
|
||||||
}
|
}
|
||||||
@ -169,6 +167,9 @@ class MobileURLEditor extends StatelessWidget {
|
|||||||
textStyle: Theme.of(context).textTheme.bodyMedium,
|
textStyle: Theme.of(context).textTheme.bodyMedium,
|
||||||
keyboardType: TextInputType.url,
|
keyboardType: TextInputType.url,
|
||||||
hintTextConstraints: const BoxConstraints(maxHeight: 52),
|
hintTextConstraints: const BoxConstraints(maxHeight: 52),
|
||||||
|
error: context.watch<URLCellBloc>().state.isValid
|
||||||
|
? null
|
||||||
|
: const SizedBox.shrink(),
|
||||||
onChanged: (_) {
|
onChanged: (_) {
|
||||||
if (textEditingController.value.composing.isCollapsed) {
|
if (textEditingController.value.composing.isCollapsed) {
|
||||||
context
|
context
|
||||||
@ -212,8 +213,11 @@ class MobileURLEditor extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void openUrlCellLink(String content) {
|
void openUrlCellLink(String content) async {
|
||||||
if (RegExp(regexUrl).hasMatch(content)) {
|
String url = "";
|
||||||
|
|
||||||
|
try {
|
||||||
|
// check protocol is provided
|
||||||
const linkPrefix = [
|
const linkPrefix = [
|
||||||
'http://',
|
'http://',
|
||||||
'https://',
|
'https://',
|
||||||
@ -224,11 +228,15 @@ void openUrlCellLink(String content) {
|
|||||||
];
|
];
|
||||||
final shouldAddScheme =
|
final shouldAddScheme =
|
||||||
!linkPrefix.any((pattern) => content.startsWith(pattern));
|
!linkPrefix.any((pattern) => content.startsWith(pattern));
|
||||||
final url = shouldAddScheme ? 'https://$content' : content;
|
url = shouldAddScheme ? 'http://$content' : content;
|
||||||
afLaunchUrlString(url);
|
|
||||||
} else {
|
// get hostname and check validity
|
||||||
afLaunchUrlString(
|
final uri = Uri.parse(url);
|
||||||
"https://www.google.com/search?q=${Uri.encodeComponent(content)}",
|
final hostName = uri.host;
|
||||||
);
|
await InternetAddress.lookup(hostName);
|
||||||
|
} catch (_) {
|
||||||
|
url = "https://www.google.com/search?q=${Uri.encodeComponent(content)}";
|
||||||
|
} finally {
|
||||||
|
await afLaunchUrlString(url);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,9 +50,8 @@ class RelationCellEditor extends StatelessWidget {
|
|||||||
rightIcon: cellState.rows
|
rightIcon: cellState.rows
|
||||||
.map((e) => e.rowId)
|
.map((e) => e.rowId)
|
||||||
.contains(row.rowId)
|
.contains(row.rowId)
|
||||||
? FlowySvg(
|
? const FlowySvg(
|
||||||
FlowySvgs.check_s,
|
FlowySvgs.check_s,
|
||||||
color: Theme.of(context).primaryColor,
|
|
||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
onTap: () => context
|
onTap: () => context
|
||||||
|
@ -20,6 +20,7 @@ class FlowyTextField extends StatefulWidget {
|
|||||||
final bool submitOnLeave;
|
final bool submitOnLeave;
|
||||||
final Duration? debounceDuration;
|
final Duration? debounceDuration;
|
||||||
final String? errorText;
|
final String? errorText;
|
||||||
|
final Widget? error;
|
||||||
final int? maxLines;
|
final int? maxLines;
|
||||||
final bool showCounter;
|
final bool showCounter;
|
||||||
final Widget? prefixIcon;
|
final Widget? prefixIcon;
|
||||||
@ -50,6 +51,7 @@ class FlowyTextField extends StatefulWidget {
|
|||||||
this.submitOnLeave = false,
|
this.submitOnLeave = false,
|
||||||
this.debounceDuration,
|
this.debounceDuration,
|
||||||
this.errorText,
|
this.errorText,
|
||||||
|
this.error,
|
||||||
this.maxLines = 1,
|
this.maxLines = 1,
|
||||||
this.showCounter = true,
|
this.showCounter = true,
|
||||||
this.prefixIcon,
|
this.prefixIcon,
|
||||||
@ -176,6 +178,7 @@ class FlowyTextFieldState extends State<FlowyTextField> {
|
|||||||
isDense: false,
|
isDense: false,
|
||||||
hintText: widget.hintText,
|
hintText: widget.hintText,
|
||||||
errorText: widget.errorText,
|
errorText: widget.errorText,
|
||||||
|
error: widget.error,
|
||||||
errorStyle: Theme.of(context)
|
errorStyle: Theme.of(context)
|
||||||
.textTheme
|
.textTheme
|
||||||
.bodySmall!
|
.bodySmall!
|
||||||
|
Loading…
Reference in New Issue
Block a user