Skip to content

Commit ae0f71b

Browse files
committed
chore: return URLCellData & parse url from cell content
1 parent 9844c02 commit ae0f71b

File tree

9 files changed

+120
-48
lines changed

9 files changed

+120
-48
lines changed

frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cell_service.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
1010
import 'package:flowy_sdk/protobuf/flowy-grid/cell_entities.pb.dart';
1111
import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option.pb.dart';
1212
import 'package:flowy_sdk/protobuf/flowy-grid/selection_type_option.pb.dart';
13+
import 'package:flowy_sdk/protobuf/flowy-grid/url_type_option.pb.dart';
1314
import 'package:flutter/foundation.dart';
1415
import 'package:freezed_annotation/freezed_annotation.dart';
1516
import 'package:app_flowy/workspace/application/grid/cell/cell_listener.dart';

frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ part of 'cell_service.dart';
33
typedef GridCellContext = _GridCellContext<String, String>;
44
typedef GridSelectOptionCellContext = _GridCellContext<SelectOptionCellData, String>;
55
typedef GridDateCellContext = _GridCellContext<DateCellData, DateCalData>;
6-
typedef GridURLCellContext = _GridCellContext<Cell, String>;
6+
typedef GridURLCellContext = _GridCellContext<URLCellData, String>;
77

88
class GridCellContextBuilder {
99
final GridCellCache _cellCache;

frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/data_loader.dart

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,11 @@ class GridCellDataLoader<T> extends IGridCellDataLoader<T> {
5858
return fut.then(
5959
(result) => result.fold((Cell cell) {
6060
try {
61-
return parser.parserData(cell.data);
61+
if (cell.data.isEmpty) {
62+
return null;
63+
} else {
64+
return parser.parserData(cell.data);
65+
}
6266
} catch (e, s) {
6367
Log.error('$parser parser cellData failed, $e');
6468
Log.error('Stack trace \n $s');
@@ -105,29 +109,20 @@ class StringCellDataParser implements ICellDataParser<String> {
105109
class DateCellDataParser implements ICellDataParser<DateCellData> {
106110
@override
107111
DateCellData? parserData(List<int> data) {
108-
if (data.isEmpty) {
109-
return null;
110-
}
111112
return DateCellData.fromBuffer(data);
112113
}
113114
}
114115

115116
class SelectOptionCellDataParser implements ICellDataParser<SelectOptionCellData> {
116117
@override
117118
SelectOptionCellData? parserData(List<int> data) {
118-
if (data.isEmpty) {
119-
return null;
120-
}
121119
return SelectOptionCellData.fromBuffer(data);
122120
}
123121
}
124122

125-
class URLCellDataParser implements ICellDataParser<Cell> {
123+
class URLCellDataParser implements ICellDataParser<URLCellData> {
126124
@override
127-
Cell? parserData(List<int> data) {
128-
if (data.isEmpty) {
129-
return null;
130-
}
131-
return Cell.fromBuffer(data);
125+
URLCellData? parserData(List<int> data) {
126+
return URLCellData.fromBuffer(data);
132127
}
133128
}

frontend/app_flowy/lib/workspace/application/grid/cell/url_cell_bloc.dart

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' show Cell;
1+
import 'package:flowy_sdk/protobuf/flowy-grid/url_type_option.pb.dart';
22
import 'package:flutter_bloc/flutter_bloc.dart';
33
import 'package:freezed_annotation/freezed_annotation.dart';
44
import 'dart:async';
@@ -23,7 +23,7 @@ class URLCellBloc extends Bloc<URLCellEvent, URLCellState> {
2323
emit(state.copyWith(content: text));
2424
},
2525
didReceiveCellUpdate: (cellData) {
26-
emit(state.copyWith(content: cellData.content));
26+
emit(state.copyWith(content: cellData.content, url: cellData.url));
2727
},
2828
);
2929
},
@@ -54,7 +54,7 @@ class URLCellBloc extends Bloc<URLCellEvent, URLCellState> {
5454
@freezed
5555
class URLCellEvent with _$URLCellEvent {
5656
const factory URLCellEvent.initial() = _InitialCell;
57-
const factory URLCellEvent.didReceiveCellUpdate(Cell cell) = _DidReceiveCellUpdate;
57+
const factory URLCellEvent.didReceiveCellUpdate(URLCellData cell) = _DidReceiveCellUpdate;
5858
const factory URLCellEvent.updateText(String text) = _UpdateText;
5959
}
6060

@@ -67,6 +67,9 @@ class URLCellState with _$URLCellState {
6767

6868
factory URLCellState.initial(GridURLCellContext context) {
6969
final cellData = context.getCellData();
70-
return URLCellState(content: cellData?.content ?? "", url: "");
70+
return URLCellState(
71+
content: cellData?.content ?? "",
72+
url: cellData?.url ?? "",
73+
);
7174
}
7275
}

frontend/rust-lib/Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frontend/rust-lib/flowy-grid/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ serde = { version = "1.0", features = ["derive"] }
3535
serde_json = {version = "1.0"}
3636
serde_repr = "0.1"
3737
indexmap = {version = "1.8.1", features = ["serde"]}
38-
url = { version = "2"}
38+
fancy-regex = "0.10.0"
3939

4040
[dev-dependencies]
4141
flowy-test = { path = "../flowy-test" }

frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -627,7 +627,7 @@ mod tests {
627627
field_meta: &FieldMeta,
628628
) -> String {
629629
type_option
630-
.decode_cell_data(encoded_data, &FieldType::DateTime, &field_meta)
630+
.decode_cell_data(encoded_data, &FieldType::DateTime, field_meta)
631631
.unwrap()
632632
.parse::<DateCellData>()
633633
.unwrap()

frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -537,7 +537,7 @@ mod tests {
537537

538538
let data = SelectOptionCellContentChangeset::from_insert(&google_option.id).to_str();
539539
let cell_data = type_option.apply_changeset(data, None).unwrap();
540-
assert_single_select_options(cell_data, &type_option, &field_meta, vec![google_option.clone()]);
540+
assert_single_select_options(cell_data, &type_option, &field_meta, vec![google_option]);
541541

542542
// Invalid option id
543543
let cell_data = type_option
@@ -580,12 +580,12 @@ mod tests {
580580
cell_data,
581581
&type_option,
582582
&field_meta,
583-
vec![google_option.clone(), facebook_option.clone()],
583+
vec![google_option.clone(), facebook_option],
584584
);
585585

586586
let data = SelectOptionCellContentChangeset::from_insert(&google_option.id).to_str();
587587
let cell_data = type_option.apply_changeset(data, None).unwrap();
588-
assert_multi_select_options(cell_data, &type_option, &field_meta, vec![google_option.clone()]);
588+
assert_multi_select_options(cell_data, &type_option, &field_meta, vec![google_option]);
589589

590590
// Invalid option id
591591
let cell_data = type_option
@@ -612,7 +612,7 @@ mod tests {
612612
assert_eq!(
613613
expected,
614614
type_option
615-
.decode_cell_data(cell_data, &field_meta.field_type, &field_meta)
615+
.decode_cell_data(cell_data, &field_meta.field_type, field_meta)
616616
.unwrap()
617617
.parse::<SelectOptionCellData>()
618618
.unwrap()
@@ -629,7 +629,7 @@ mod tests {
629629
assert_eq!(
630630
expected,
631631
type_option
632-
.decode_cell_data(cell_data, &field_meta.field_type, &field_meta)
632+
.decode_cell_data(cell_data, &field_meta.field_type, field_meta)
633633
.unwrap()
634634
.parse::<SelectOptionCellData>()
635635
.unwrap()
Lines changed: 95 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
use crate::impl_type_option;
22
use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder};
3-
use crate::services::row::{CellContentChangeset, CellDataOperation, DecodedCellData};
3+
use crate::services::row::{CellContentChangeset, CellDataOperation, DecodedCellData, EncodedCellData};
44
use bytes::Bytes;
5+
use fancy_regex::Regex;
56
use flowy_derive::ProtoBuf;
6-
use flowy_error::{FlowyError, FlowyResult};
7+
use flowy_error::{internal_error, FlowyError, FlowyResult};
78
use flowy_grid_data_model::entities::{
89
CellMeta, FieldMeta, FieldType, TypeOptionDataDeserializer, TypeOptionDataEntry,
910
};
10-
11+
use lazy_static::lazy_static;
1112
use serde::{Deserialize, Serialize};
13+
use std::str::FromStr;
1214

1315
#[derive(Default)]
1416
pub struct URLTypeOptionBuilder(URLTypeOption);
@@ -32,30 +34,38 @@ pub struct URLTypeOption {
3234
}
3335
impl_type_option!(URLTypeOption, FieldType::URL);
3436

35-
impl CellDataOperation<String, String> for URLTypeOption {
37+
impl CellDataOperation<EncodedCellData<URLCellData>, String> for URLTypeOption {
3638
fn decode_cell_data<T>(
3739
&self,
3840
encoded_data: T,
3941
decoded_field_type: &FieldType,
4042
_field_meta: &FieldMeta,
4143
) -> FlowyResult<DecodedCellData>
4244
where
43-
T: Into<String>,
45+
T: Into<EncodedCellData<URLCellData>>,
4446
{
4547
if !decoded_field_type.is_url() {
4648
return Ok(DecodedCellData::default());
4749
}
48-
49-
let cell_data = encoded_data.into();
50-
Ok(DecodedCellData::from_content(cell_data))
50+
let cell_data = encoded_data.into().try_into_inner()?;
51+
DecodedCellData::try_from_bytes(cell_data)
5152
}
5253

5354
fn apply_changeset<C>(&self, changeset: C, _cell_meta: Option<CellMeta>) -> Result<String, FlowyError>
5455
where
5556
C: Into<CellContentChangeset>,
5657
{
5758
let changeset = changeset.into();
58-
Ok(changeset.to_string())
59+
let mut cell_data = URLCellData {
60+
url: "".to_string(),
61+
content: changeset.to_string(),
62+
};
63+
64+
if let Ok(Some(m)) = URL_REGEX.find(&changeset) {
65+
cell_data.url = m.as_str().to_string();
66+
}
67+
68+
cell_data.to_json()
5969
}
6070
}
6171

@@ -68,34 +78,97 @@ pub struct URLCellData {
6878
pub content: String,
6979
}
7080

81+
impl URLCellData {
82+
pub fn new(s: &str) -> Self {
83+
Self {
84+
url: "".to_string(),
85+
content: s.to_string(),
86+
}
87+
}
88+
89+
fn to_json(&self) -> FlowyResult<String> {
90+
serde_json::to_string(self).map_err(internal_error)
91+
}
92+
}
93+
94+
impl FromStr for URLCellData {
95+
type Err = FlowyError;
96+
97+
fn from_str(s: &str) -> Result<Self, Self::Err> {
98+
serde_json::from_str::<URLCellData>(s).map_err(internal_error)
99+
}
100+
}
101+
102+
lazy_static! {
103+
static ref URL_REGEX: Regex = Regex::new(
104+
"[(http(s)?):\\/\\/(www\\.)?a-zA-Z0-9@:%._\\+~#=]{2,256}\\.[a-z]{2,6}\\b([-a-zA-Z0-9@:%_\\+.~#?&//=]*)"
105+
)
106+
.unwrap();
107+
}
108+
71109
#[cfg(test)]
72110
mod tests {
73111
use crate::services::field::FieldBuilder;
74-
use crate::services::field::URLTypeOption;
75-
use crate::services::row::CellDataOperation;
112+
use crate::services::field::{URLCellData, URLTypeOption};
113+
use crate::services::row::{CellDataOperation, EncodedCellData};
76114
use flowy_grid_data_model::entities::{FieldMeta, FieldType};
77115

78116
#[test]
79-
fn url_type_option_format_test() {
117+
fn url_type_option_test_no_url() {
80118
let type_option = URLTypeOption::default();
81119
let field_type = FieldType::URL;
82120
let field_meta = FieldBuilder::from_field_type(&field_type).build();
83-
assert_equal(&type_option, "123", "123", &field_type, &field_meta);
121+
assert_changeset(&type_option, "123", &field_type, &field_meta, "123", "");
84122
}
85123

86-
fn assert_equal(
124+
#[test]
125+
fn url_type_option_test_contains_url() {
126+
let type_option = URLTypeOption::default();
127+
let field_type = FieldType::URL;
128+
let field_meta = FieldBuilder::from_field_type(&field_type).build();
129+
assert_changeset(
130+
&type_option,
131+
"AppFlowy website - https://www.appflowy.io",
132+
&field_type,
133+
&field_meta,
134+
"AppFlowy website - https://www.appflowy.io",
135+
"https://www.appflowy.io",
136+
);
137+
138+
assert_changeset(
139+
&type_option,
140+
"AppFlowy website appflowy.io",
141+
&field_type,
142+
&field_meta,
143+
"AppFlowy website appflowy.io",
144+
"appflowy.io",
145+
);
146+
}
147+
148+
fn assert_changeset(
87149
type_option: &URLTypeOption,
88150
cell_data: &str,
89-
expected_str: &str,
90151
field_type: &FieldType,
91152
field_meta: &FieldMeta,
153+
expected: &str,
154+
expected_url: &str,
92155
) {
93-
assert_eq!(
94-
type_option
95-
.decode_cell_data(cell_data, field_type, field_meta)
96-
.unwrap()
97-
.content,
98-
expected_str.to_owned()
99-
);
156+
let encoded_data = type_option.apply_changeset(cell_data, None).unwrap();
157+
let decode_cell_data = decode_cell_data(encoded_data, type_option, field_meta, field_type);
158+
assert_eq!(expected.to_owned(), decode_cell_data.content);
159+
assert_eq!(expected_url.to_owned(), decode_cell_data.url);
160+
}
161+
162+
fn decode_cell_data<T: Into<EncodedCellData<URLCellData>>>(
163+
encoded_data: T,
164+
type_option: &URLTypeOption,
165+
field_meta: &FieldMeta,
166+
field_type: &FieldType,
167+
) -> URLCellData {
168+
type_option
169+
.decode_cell_data(encoded_data, field_type, field_meta)
170+
.unwrap()
171+
.parse::<URLCellData>()
172+
.unwrap()
100173
}
101174
}

0 commit comments

Comments
 (0)