chore: support open url with https scheme

This commit is contained in:
appflowy 2022-05-29 21:47:38 +08:00
parent ae0f71b5ee
commit 4a9627b31d
20 changed files with 388 additions and 184 deletions

View File

@ -108,7 +108,8 @@ class _GridCellContext<T, D> extends Equatable {
late final ValueNotifier<T?> _cellDataNotifier;
bool isListening = false;
VoidCallback? _onFieldChangedFn;
Timer? _delayOperation;
Timer? _loadDataOperation;
Timer? _saveDataOperation;
_GridCellContext({
required this.gridCell,
@ -138,7 +139,7 @@ class _GridCellContext<T, D> extends Equatable {
FieldType get fieldType => gridCell.field.fieldType;
VoidCallback? startListening({required void Function(T) onCellChanged}) {
VoidCallback? startListening({required void Function(T?) onCellChanged}) {
if (isListening) {
Log.error("Already started. It seems like you should call clone first");
return null;
@ -162,7 +163,7 @@ class _GridCellContext<T, D> extends Equatable {
}
onCellChangedFn() {
onCellChanged(_cellDataNotifier.value as T);
onCellChanged(_cellDataNotifier.value);
if (cellDataLoader.config.reloadOnCellChanged) {
_loadData();
@ -189,13 +190,26 @@ class _GridCellContext<T, D> extends Equatable {
return _fieldService.getFieldTypeOptionData(fieldType: fieldType);
}
Future<Option<FlowyError>> saveCellData(D data) {
return cellDataPersistence.save(data);
void saveCellData(D data, {bool deduplicate = false, void Function(Option<FlowyError>)? resultCallback}) async {
if (deduplicate) {
_loadDataOperation?.cancel();
_loadDataOperation = Timer(const Duration(milliseconds: 300), () async {
final result = await cellDataPersistence.save(data);
if (resultCallback != null) {
resultCallback(result);
}
});
} else {
final result = await cellDataPersistence.save(data);
if (resultCallback != null) {
resultCallback(result);
}
}
}
void _loadData() {
_delayOperation?.cancel();
_delayOperation = Timer(const Duration(milliseconds: 10), () {
_loadDataOperation?.cancel();
_loadDataOperation = Timer(const Duration(milliseconds: 10), () {
cellDataLoader.loadData().then((data) {
_cellDataNotifier.value = data;
cellCache.insert(GridCellCacheData(key: _cacheKey, object: data));
@ -204,7 +218,8 @@ class _GridCellContext<T, D> extends Equatable {
}
void dispose() {
_delayOperation?.cancel();
_loadDataOperation?.cancel();
_saveDataOperation?.cancel();
if (_onFieldChangedFn != null) {
cellCache.removeFieldListener(_cacheKey, _onFieldChangedFn!);

View File

@ -58,7 +58,7 @@ class CheckboxCellBloc extends Bloc<CheckboxCellEvent, CheckboxCellState> {
class CheckboxCellEvent with _$CheckboxCellEvent {
const factory CheckboxCellEvent.initial() = _Initial;
const factory CheckboxCellEvent.select() = _Selected;
const factory CheckboxCellEvent.didReceiveCellUpdate(String cellData) = _DidReceiveCellUpdate;
const factory CheckboxCellEvent.didReceiveCellUpdate(String? cellData) = _DidReceiveCellUpdate;
}
@freezed

View File

@ -37,7 +37,7 @@ class DateCalBloc extends Bloc<DateCalEvent, DateCalState> {
setFocusedDay: (focusedDay) {
emit(state.copyWith(focusedDay: focusedDay));
},
didReceiveCellUpdate: (DateCellData cellData) {
didReceiveCellUpdate: (DateCellData? cellData) {
final dateData = dateDataFromCellData(cellData);
final time = dateData.foldRight("", (dateData, previous) => dateData.time);
emit(state.copyWith(dateData: dateData, time: time));
@ -83,25 +83,26 @@ class DateCalBloc extends Bloc<DateCalEvent, DateCalState> {
return;
}
final result = await cellContext.saveCellData(newDateData);
result.fold(
() => emit(state.copyWith(
dateData: Some(newDateData),
timeFormatError: none(),
)),
(err) {
switch (ErrorCode.valueOf(err.code)!) {
case ErrorCode.InvalidDateTimeFormat:
emit(state.copyWith(
dateData: Some(newDateData),
timeFormatError: Some(timeFormatPrompt(err)),
));
break;
default:
Log.error(err);
}
},
);
cellContext.saveCellData(newDateData, resultCallback: (result) {
result.fold(
() => emit(state.copyWith(
dateData: Some(newDateData),
timeFormatError: none(),
)),
(err) {
switch (ErrorCode.valueOf(err.code)!) {
case ErrorCode.InvalidDateTimeFormat:
emit(state.copyWith(
dateData: Some(newDateData),
timeFormatError: Some(timeFormatPrompt(err)),
));
break;
default:
Log.error(err);
}
},
);
});
}
String timeFormatPrompt(FlowyError error) {
@ -183,7 +184,7 @@ class DateCalEvent with _$DateCalEvent {
const factory DateCalEvent.setDateFormat(DateFormat dateFormat) = _DateFormat;
const factory DateCalEvent.setIncludeTime(bool includeTime) = _IncludeTime;
const factory DateCalEvent.setTime(String time) = _Time;
const factory DateCalEvent.didReceiveCellUpdate(DateCellData data) = _DidReceiveCellUpdate;
const factory DateCalEvent.didReceiveCellUpdate(DateCellData? data) = _DidReceiveCellUpdate;
}
@freezed

View File

@ -16,7 +16,13 @@ class DateCellBloc extends Bloc<DateCellEvent, DateCellState> {
(event, emit) async {
event.when(
initial: () => _startListening(),
didReceiveCellUpdate: (DateCellData value) => emit(state.copyWith(data: Some(value))),
didReceiveCellUpdate: (DateCellData? cellData) {
if (cellData != null) {
emit(state.copyWith(data: Some(cellData)));
} else {
emit(state.copyWith(data: none()));
}
},
didReceiveFieldUpdate: (Field value) => emit(state.copyWith(field: value)),
);
},
@ -47,7 +53,7 @@ class DateCellBloc extends Bloc<DateCellEvent, DateCellState> {
@freezed
class DateCellEvent with _$DateCellEvent {
const factory DateCellEvent.initial() = _InitialCell;
const factory DateCellEvent.didReceiveCellUpdate(DateCellData data) = _DidReceiveCellUpdate;
const factory DateCellEvent.didReceiveCellUpdate(DateCellData? data) = _DidReceiveCellUpdate;
const factory DateCellEvent.didReceiveFieldUpdate(Field field) = _DidReceiveFieldUpdate;
}

View File

@ -19,7 +19,7 @@ class NumberCellBloc extends Bloc<NumberCellEvent, NumberCellState> {
_startListening();
},
didReceiveCellUpdate: (_DidReceiveCellUpdate value) {
emit(state.copyWith(content: value.cellContent));
emit(state.copyWith(content: value.cellContent ?? ""));
},
updateCell: (_UpdateCell value) async {
await _updateCellValue(value, emit);
@ -58,7 +58,7 @@ class NumberCellBloc extends Bloc<NumberCellEvent, NumberCellState> {
class NumberCellEvent with _$NumberCellEvent {
const factory NumberCellEvent.initial() = _Initial;
const factory NumberCellEvent.updateCell(String text) = _UpdateCell;
const factory NumberCellEvent.didReceiveCellUpdate(String cellContent) = _DidReceiveCellUpdate;
const factory NumberCellEvent.didReceiveCellUpdate(String? cellContent) = _DidReceiveCellUpdate;
}
@freezed

View File

@ -44,7 +44,7 @@ class SelectOptionCellBloc extends Bloc<SelectOptionCellEvent, SelectOptionCellS
onCellChanged: ((selectOptionContext) {
if (!isClosed) {
add(SelectOptionCellEvent.didReceiveOptions(
selectOptionContext.selectOptions,
selectOptionContext?.selectOptions ?? [],
));
}
}),

View File

@ -43,7 +43,7 @@ class TextCellBloc extends Bloc<TextCellEvent, TextCellState> {
_onCellChangedFn = cellContext.startListening(
onCellChanged: ((cellContent) {
if (!isClosed) {
add(TextCellEvent.didReceiveCellUpdate(cellContent));
add(TextCellEvent.didReceiveCellUpdate(cellContent ?? ""));
}
}),
);

View File

@ -18,12 +18,11 @@ class URLCellBloc extends Bloc<URLCellEvent, URLCellState> {
initial: () {
_startListening();
},
updateText: (text) {
cellContext.saveCellData(text);
emit(state.copyWith(content: text));
},
didReceiveCellUpdate: (cellData) {
emit(state.copyWith(content: cellData.content, url: cellData.url));
emit(state.copyWith(
content: cellData?.content ?? "",
url: cellData?.url ?? "",
));
},
);
},
@ -54,8 +53,7 @@ class URLCellBloc extends Bloc<URLCellEvent, URLCellState> {
@freezed
class URLCellEvent with _$URLCellEvent {
const factory URLCellEvent.initial() = _InitialCell;
const factory URLCellEvent.didReceiveCellUpdate(URLCellData cell) = _DidReceiveCellUpdate;
const factory URLCellEvent.updateText(String text) = _UpdateText;
const factory URLCellEvent.didReceiveCellUpdate(URLCellData? cell) = _DidReceiveCellUpdate;
}
@freezed

View File

@ -0,0 +1,73 @@
import 'package:flowy_sdk/protobuf/flowy-grid/url_type_option.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'dart:async';
import 'cell_service/cell_service.dart';
part 'url_cell_editor_bloc.freezed.dart';
class URLCellEditorBloc extends Bloc<URLCellEditorEvent, URLCellEditorState> {
final GridURLCellContext cellContext;
void Function()? _onCellChangedFn;
URLCellEditorBloc({
required this.cellContext,
}) : super(URLCellEditorState.initial(cellContext)) {
on<URLCellEditorEvent>(
(event, emit) async {
event.when(
initial: () {
_startListening();
},
updateText: (text) {
cellContext.saveCellData(text, deduplicate: true);
emit(state.copyWith(content: text));
},
didReceiveCellUpdate: (cellData) {
emit(state.copyWith(content: cellData?.content ?? ""));
},
);
},
);
}
@override
Future<void> close() async {
if (_onCellChangedFn != null) {
cellContext.removeListener(_onCellChangedFn!);
_onCellChangedFn = null;
}
cellContext.dispose();
return super.close();
}
void _startListening() {
_onCellChangedFn = cellContext.startListening(
onCellChanged: ((cellData) {
if (!isClosed) {
add(URLCellEditorEvent.didReceiveCellUpdate(cellData));
}
}),
);
}
}
@freezed
class URLCellEditorEvent with _$URLCellEditorEvent {
const factory URLCellEditorEvent.initial() = _InitialCell;
const factory URLCellEditorEvent.didReceiveCellUpdate(URLCellData? cell) = _DidReceiveCellUpdate;
const factory URLCellEditorEvent.updateText(String text) = _UpdateText;
}
@freezed
class URLCellEditorState with _$URLCellEditorState {
const factory URLCellEditorState({
required String content,
}) = _URLCellEditorState;
factory URLCellEditorState.initial(GridURLCellContext context) {
final cellData = context.getCellData();
return URLCellEditorState(
content: cellData?.content ?? "",
);
}
}

View File

@ -13,7 +13,7 @@ import 'date_cell/date_cell.dart';
import 'number_cell.dart';
import 'select_option_cell/select_option_cell.dart';
import 'text_cell.dart';
import 'url_cell.dart';
import 'url_cell/url_cell.dart';
GridCellWidget buildGridCellWidget(GridCell gridCell, GridCellCache cellCache, {GridCellStyle? style}) {
final key = ValueKey(gridCell.cellId());
@ -35,7 +35,6 @@ GridCellWidget buildGridCellWidget(GridCell gridCell, GridCellCache cellCache, {
return GridTextCell(cellContextBuilder: cellContextBuilder, style: style, key: key);
case FieldType.URL:
return GridURLCell(cellContextBuilder: cellContextBuilder, style: style, key: key);
}
throw UnimplementedError;
}
@ -151,7 +150,7 @@ class CellContainer extends StatelessWidget {
});
if (expander != null) {
container = _CellEnterRegion(child: container, expander: expander!);
container = CellEnterRegion(child: container, expander: expander!);
}
return GestureDetector(
@ -181,10 +180,10 @@ class CellContainer extends StatelessWidget {
}
}
class _CellEnterRegion extends StatelessWidget {
class CellEnterRegion extends StatelessWidget {
final Widget child;
final Widget expander;
const _CellEnterRegion({required this.child, required this.expander, Key? key}) : super(key: key);
const CellEnterRegion({required this.child, required this.expander, Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {

View File

@ -1,127 +0,0 @@
import 'dart:async';
import 'package:app_flowy/workspace/application/grid/cell/url_cell_bloc.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:app_flowy/workspace/application/grid/prelude.dart';
import 'cell_builder.dart';
class GridURLCellStyle extends GridCellStyle {
String? placeholder;
GridURLCellStyle({
this.placeholder,
});
}
class GridURLCell extends StatefulWidget with GridCellWidget {
final GridCellContextBuilder cellContextBuilder;
late final GridURLCellStyle? cellStyle;
GridURLCell({
required this.cellContextBuilder,
GridCellStyle? style,
Key? key,
}) : super(key: key) {
if (style != null) {
cellStyle = (style as GridURLCellStyle);
} else {
cellStyle = null;
}
}
@override
State<GridURLCell> createState() => _GridURLCellState();
}
class _GridURLCellState extends State<GridURLCell> {
late URLCellBloc _cellBloc;
late TextEditingController _controller;
late CellSingleFocusNode _focusNode;
Timer? _delayOperation;
@override
void initState() {
final cellContext = widget.cellContextBuilder.build() as GridURLCellContext;
_cellBloc = URLCellBloc(cellContext: cellContext);
_cellBloc.add(const URLCellEvent.initial());
_controller = TextEditingController(text: _cellBloc.state.content);
_focusNode = CellSingleFocusNode();
_listenFocusNode();
_listenRequestFocus(context);
super.initState();
}
@override
Widget build(BuildContext context) {
return BlocProvider.value(
value: _cellBloc,
child: BlocListener<URLCellBloc, URLCellState>(
listener: (context, state) {
if (_controller.text != state.content) {
_controller.text = state.content;
}
},
child: TextField(
controller: _controller,
focusNode: _focusNode,
onChanged: (value) => focusChanged(),
onEditingComplete: () => _focusNode.unfocus(),
maxLines: null,
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500),
decoration: InputDecoration(
contentPadding: EdgeInsets.zero,
border: InputBorder.none,
hintText: widget.cellStyle?.placeholder,
isDense: true,
),
),
),
);
}
@override
Future<void> dispose() async {
widget.requestFocus.removeAllListener();
_delayOperation?.cancel();
_cellBloc.close();
_focusNode.removeSingleListener();
_focusNode.dispose();
super.dispose();
}
@override
void didUpdateWidget(covariant GridURLCell oldWidget) {
if (oldWidget != widget) {
_listenFocusNode();
}
super.didUpdateWidget(oldWidget);
}
void _listenFocusNode() {
widget.onFocus.value = _focusNode.hasFocus;
_focusNode.setSingleListener(() {
widget.onFocus.value = _focusNode.hasFocus;
focusChanged();
});
}
void _listenRequestFocus(BuildContext context) {
widget.requestFocus.addListener(() {
if (_focusNode.hasFocus == false && _focusNode.canRequestFocus) {
FocusScope.of(context).requestFocus(_focusNode);
}
});
}
Future<void> focusChanged() async {
if (mounted) {
_delayOperation?.cancel();
_delayOperation = Timer(const Duration(milliseconds: 300), () {
if (_cellBloc.isClosed == false && _controller.text != _cellBloc.state.content) {
_cellBloc.add(URLCellEvent.updateText(_controller.text));
}
});
}
}
}

View File

@ -0,0 +1,96 @@
import 'package:app_flowy/workspace/application/grid/cell/cell_service/cell_service.dart';
import 'package:app_flowy/workspace/application/grid/cell/url_cell_editor_bloc.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:flutter_bloc/flutter_bloc.dart';
class URLCellEditor extends StatefulWidget {
final GridURLCellContext cellContext;
const URLCellEditor({required this.cellContext, Key? key}) : super(key: key);
@override
State<URLCellEditor> createState() => _URLCellEditorState();
static void show(
BuildContext context,
GridURLCellContext cellContext,
) {
FlowyOverlay.of(context).remove(identifier());
final editor = URLCellEditor(
cellContext: cellContext,
);
//
FlowyOverlay.of(context).insertWithAnchor(
widget: OverlayContainer(
child: SizedBox(width: 200, child: editor),
constraints: BoxConstraints.loose(const Size(300, 160)),
),
identifier: URLCellEditor.identifier(),
anchorContext: context,
anchorDirection: AnchorDirection.bottomWithCenterAligned,
);
}
static String identifier() {
return (URLCellEditor).toString();
}
}
class _URLCellEditorState extends State<URLCellEditor> {
late URLCellEditorBloc _cellBloc;
late TextEditingController _controller;
@override
void initState() {
_cellBloc = URLCellEditorBloc(cellContext: widget.cellContext);
_cellBloc.add(const URLCellEditorEvent.initial());
_controller = TextEditingController(text: _cellBloc.state.content);
super.initState();
}
@override
Widget build(BuildContext context) {
return BlocProvider.value(
value: _cellBloc,
child: BlocListener<URLCellEditorBloc, URLCellEditorState>(
listener: (context, state) {
if (_controller.text != state.content) {
_controller.text = state.content;
}
},
child: TextField(
autofocus: true,
controller: _controller,
onChanged: (value) => focusChanged(),
maxLines: null,
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500),
decoration: const InputDecoration(
contentPadding: EdgeInsets.zero,
border: InputBorder.none,
hintText: "",
isDense: true,
),
),
),
);
}
@override
Future<void> dispose() async {
_cellBloc.close();
super.dispose();
}
Future<void> focusChanged() async {
if (mounted) {
if (_cellBloc.isClosed == false && _controller.text != _cellBloc.state.content) {
_cellBloc.add(URLCellEditorEvent.updateText(_controller.text));
}
}
}
}

View File

@ -0,0 +1,131 @@
import 'dart:async';
import 'package:app_flowy/workspace/application/grid/cell/url_cell_bloc.dart';
import 'package:flowy_infra/image.dart';
import 'package:flowy_infra/theme.dart';
import 'package:flowy_infra_ui/style_widget/icon_button.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:app_flowy/workspace/application/grid/prelude.dart';
import 'package:url_launcher/url_launcher.dart';
import '../cell_builder.dart';
import 'cell_editor.dart';
class GridURLCellStyle extends GridCellStyle {
String? placeholder;
GridURLCellStyle({
this.placeholder,
});
}
class GridURLCell extends StatefulWidget with GridCellWidget {
final GridCellContextBuilder cellContextBuilder;
late final GridURLCellStyle? cellStyle;
GridURLCell({
required this.cellContextBuilder,
GridCellStyle? style,
Key? key,
}) : super(key: key) {
if (style != null) {
cellStyle = (style as GridURLCellStyle);
} else {
cellStyle = null;
}
}
@override
State<GridURLCell> createState() => _GridURLCellState();
}
class _GridURLCellState extends State<GridURLCell> {
late URLCellBloc _cellBloc;
@override
void initState() {
final cellContext = widget.cellContextBuilder.build() as GridURLCellContext;
_cellBloc = URLCellBloc(cellContext: cellContext);
_cellBloc.add(const URLCellEvent.initial());
_listenRequestFocus(context);
super.initState();
}
@override
Widget build(BuildContext context) {
final theme = context.watch<AppTheme>();
return BlocProvider.value(
value: _cellBloc,
child: BlocBuilder<URLCellBloc, URLCellState>(
builder: (context, state) {
final richText = RichText(
textAlign: TextAlign.left,
text: TextSpan(
text: state.content,
style: TextStyle(
color: theme.main2,
fontSize: 14,
decoration: TextDecoration.underline,
),
recognizer: _tapGesture(context),
),
);
return CellEnterRegion(
child: Align(alignment: Alignment.centerLeft, child: richText),
expander: _EditCellIndicator(onTap: () {}),
);
},
),
);
}
@override
Future<void> dispose() async {
widget.requestFocus.removeAllListener();
_cellBloc.close();
super.dispose();
}
TapGestureRecognizer _tapGesture(BuildContext context) {
final gesture = TapGestureRecognizer();
gesture.onTap = () async {
final url = context.read<URLCellBloc>().state.url;
await _openUrlOrEdit(url);
};
return gesture;
}
Future<void> _openUrlOrEdit(String url) async {
final uri = Uri.parse(url);
if (url.isNotEmpty && await canLaunchUrl(uri)) {
await launchUrl(uri);
} else {
final cellContext = widget.cellContextBuilder.build() as GridURLCellContext;
URLCellEditor.show(context, cellContext);
}
}
void _listenRequestFocus(BuildContext context) {
widget.requestFocus.addListener(() {
_openUrlOrEdit(_cellBloc.state.url);
});
}
}
class _EditCellIndicator extends StatelessWidget {
final VoidCallback onTap;
const _EditCellIndicator({required this.onTap, Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final theme = context.watch<AppTheme>();
return FlowyIconButton(
width: 26,
onPressed: onTap,
hoverColor: theme.hover,
radius: BorderRadius.circular(4),
iconPadding: const EdgeInsets.all(5),
icon: svgWidget("editor/edit", color: theme.iconColor),
);
}
}

View File

@ -209,9 +209,10 @@ class _CellExpander extends StatelessWidget {
return FittedBox(
fit: BoxFit.contain,
child: FlowyIconButton(
width: 26,
onPressed: onExpand,
iconPadding: const EdgeInsets.fromLTRB(6, 6, 6, 6),
fillColor: theme.surface,
iconPadding: const EdgeInsets.all(5),
radius: BorderRadius.circular(4),
icon: svgWidget("grid/expander", color: theme.main1),
),
);

View File

@ -4,7 +4,7 @@ import 'package:app_flowy/workspace/application/grid/row/row_detail_bloc.dart';
import 'package:app_flowy/workspace/application/grid/row/row_service.dart';
import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart';
import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/cell/prelude.dart';
import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/cell/url_cell.dart';
import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/url_cell.dart';
import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_cell.dart';
import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_editor.dart';
import 'package:flowy_infra/image.dart';

View File

@ -954,6 +954,7 @@ dependencies = [
"strum_macros",
"tokio",
"tracing",
"url",
]
[[package]]

View File

@ -36,6 +36,7 @@ serde_json = {version = "1.0"}
serde_repr = "0.1"
indexmap = {version = "1.8.1", features = ["serde"]}
fancy-regex = "0.10.0"
url = { version = "2"}
[dev-dependencies]
flowy-test = { path = "../flowy-test" }

View File

@ -207,8 +207,6 @@ impl CellDataOperation<String, String> for MultiSelectTypeOption {
return Ok(DecodedCellData::default());
}
tracing::info!("😁{}", self.options.len());
let encoded_data = encoded_data.into();
let select_options = select_option_ids(encoded_data)
.into_iter()

View File

@ -62,7 +62,19 @@ impl CellDataOperation<EncodedCellData<URLCellData>, String> for URLTypeOption {
};
if let Ok(Some(m)) = URL_REGEX.find(&changeset) {
cell_data.url = m.as_str().to_string();
// Only support https scheme by now
match url::Url::parse(m.as_str()) {
Ok(url) => {
if url.scheme() == "https" {
cell_data.url = url.into();
} else {
cell_data.url = format!("https://{}", m.as_str());
}
}
Err(_) => {
cell_data.url = format!("https://{}", m.as_str());
}
}
}
cell_data.to_json()
@ -132,7 +144,7 @@ mod tests {
&field_type,
&field_meta,
"AppFlowy website - https://www.appflowy.io",
"https://www.appflowy.io",
"https://www.appflowy.io/",
);
assert_changeset(
@ -141,7 +153,7 @@ mod tests {
&field_type,
&field_meta,
"AppFlowy website appflowy.io",
"appflowy.io",
"https://appflowy.io",
);
}

View File

@ -167,7 +167,6 @@ pub fn decode_cell_data<T: Into<String>>(
field_meta: &FieldMeta,
) -> FlowyResult<DecodedCellData> {
let encoded_data = encoded_data.into();
tracing::info!("😁{:?}", field_meta.type_options);
let get_cell_data = || {
let data = match t_field_type {
FieldType::RichText => field_meta