mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
chore: return URLCellData & parse url from cell content
This commit is contained in:
parent
9844c02cbc
commit
ae0f71b5ee
@ -10,6 +10,7 @@ import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/cell_entities.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/selection_type_option.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/url_type_option.pb.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:app_flowy/workspace/application/grid/cell/cell_listener.dart';
|
||||
|
@ -3,7 +3,7 @@ part of 'cell_service.dart';
|
||||
typedef GridCellContext = _GridCellContext<String, String>;
|
||||
typedef GridSelectOptionCellContext = _GridCellContext<SelectOptionCellData, String>;
|
||||
typedef GridDateCellContext = _GridCellContext<DateCellData, DateCalData>;
|
||||
typedef GridURLCellContext = _GridCellContext<Cell, String>;
|
||||
typedef GridURLCellContext = _GridCellContext<URLCellData, String>;
|
||||
|
||||
class GridCellContextBuilder {
|
||||
final GridCellCache _cellCache;
|
||||
|
@ -58,7 +58,11 @@ class GridCellDataLoader<T> extends IGridCellDataLoader<T> {
|
||||
return fut.then(
|
||||
(result) => result.fold((Cell cell) {
|
||||
try {
|
||||
return parser.parserData(cell.data);
|
||||
if (cell.data.isEmpty) {
|
||||
return null;
|
||||
} else {
|
||||
return parser.parserData(cell.data);
|
||||
}
|
||||
} catch (e, s) {
|
||||
Log.error('$parser parser cellData failed, $e');
|
||||
Log.error('Stack trace \n $s');
|
||||
@ -105,9 +109,6 @@ class StringCellDataParser implements ICellDataParser<String> {
|
||||
class DateCellDataParser implements ICellDataParser<DateCellData> {
|
||||
@override
|
||||
DateCellData? parserData(List<int> data) {
|
||||
if (data.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
return DateCellData.fromBuffer(data);
|
||||
}
|
||||
}
|
||||
@ -115,19 +116,13 @@ class DateCellDataParser implements ICellDataParser<DateCellData> {
|
||||
class SelectOptionCellDataParser implements ICellDataParser<SelectOptionCellData> {
|
||||
@override
|
||||
SelectOptionCellData? parserData(List<int> data) {
|
||||
if (data.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
return SelectOptionCellData.fromBuffer(data);
|
||||
}
|
||||
}
|
||||
|
||||
class URLCellDataParser implements ICellDataParser<Cell> {
|
||||
class URLCellDataParser implements ICellDataParser<URLCellData> {
|
||||
@override
|
||||
Cell? parserData(List<int> data) {
|
||||
if (data.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
return Cell.fromBuffer(data);
|
||||
URLCellData? parserData(List<int> data) {
|
||||
return URLCellData.fromBuffer(data);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' show Cell;
|
||||
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';
|
||||
@ -23,7 +23,7 @@ class URLCellBloc extends Bloc<URLCellEvent, URLCellState> {
|
||||
emit(state.copyWith(content: text));
|
||||
},
|
||||
didReceiveCellUpdate: (cellData) {
|
||||
emit(state.copyWith(content: cellData.content));
|
||||
emit(state.copyWith(content: cellData.content, url: cellData.url));
|
||||
},
|
||||
);
|
||||
},
|
||||
@ -54,7 +54,7 @@ class URLCellBloc extends Bloc<URLCellEvent, URLCellState> {
|
||||
@freezed
|
||||
class URLCellEvent with _$URLCellEvent {
|
||||
const factory URLCellEvent.initial() = _InitialCell;
|
||||
const factory URLCellEvent.didReceiveCellUpdate(Cell cell) = _DidReceiveCellUpdate;
|
||||
const factory URLCellEvent.didReceiveCellUpdate(URLCellData cell) = _DidReceiveCellUpdate;
|
||||
const factory URLCellEvent.updateText(String text) = _UpdateText;
|
||||
}
|
||||
|
||||
@ -67,6 +67,9 @@ class URLCellState with _$URLCellState {
|
||||
|
||||
factory URLCellState.initial(GridURLCellContext context) {
|
||||
final cellData = context.getCellData();
|
||||
return URLCellState(content: cellData?.content ?? "", url: "");
|
||||
return URLCellState(
|
||||
content: cellData?.content ?? "",
|
||||
url: cellData?.url ?? "",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
2
frontend/rust-lib/Cargo.lock
generated
2
frontend/rust-lib/Cargo.lock
generated
@ -928,6 +928,7 @@ dependencies = [
|
||||
"dart-notify",
|
||||
"dashmap",
|
||||
"diesel",
|
||||
"fancy-regex",
|
||||
"flowy-database",
|
||||
"flowy-derive",
|
||||
"flowy-error",
|
||||
@ -953,7 +954,6 @@ dependencies = [
|
||||
"strum_macros",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -35,7 +35,7 @@ serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = {version = "1.0"}
|
||||
serde_repr = "0.1"
|
||||
indexmap = {version = "1.8.1", features = ["serde"]}
|
||||
url = { version = "2"}
|
||||
fancy-regex = "0.10.0"
|
||||
|
||||
[dev-dependencies]
|
||||
flowy-test = { path = "../flowy-test" }
|
||||
|
@ -627,7 +627,7 @@ mod tests {
|
||||
field_meta: &FieldMeta,
|
||||
) -> String {
|
||||
type_option
|
||||
.decode_cell_data(encoded_data, &FieldType::DateTime, &field_meta)
|
||||
.decode_cell_data(encoded_data, &FieldType::DateTime, field_meta)
|
||||
.unwrap()
|
||||
.parse::<DateCellData>()
|
||||
.unwrap()
|
||||
|
@ -537,7 +537,7 @@ mod tests {
|
||||
|
||||
let data = SelectOptionCellContentChangeset::from_insert(&google_option.id).to_str();
|
||||
let cell_data = type_option.apply_changeset(data, None).unwrap();
|
||||
assert_single_select_options(cell_data, &type_option, &field_meta, vec![google_option.clone()]);
|
||||
assert_single_select_options(cell_data, &type_option, &field_meta, vec![google_option]);
|
||||
|
||||
// Invalid option id
|
||||
let cell_data = type_option
|
||||
@ -580,12 +580,12 @@ mod tests {
|
||||
cell_data,
|
||||
&type_option,
|
||||
&field_meta,
|
||||
vec![google_option.clone(), facebook_option.clone()],
|
||||
vec![google_option.clone(), facebook_option],
|
||||
);
|
||||
|
||||
let data = SelectOptionCellContentChangeset::from_insert(&google_option.id).to_str();
|
||||
let cell_data = type_option.apply_changeset(data, None).unwrap();
|
||||
assert_multi_select_options(cell_data, &type_option, &field_meta, vec![google_option.clone()]);
|
||||
assert_multi_select_options(cell_data, &type_option, &field_meta, vec![google_option]);
|
||||
|
||||
// Invalid option id
|
||||
let cell_data = type_option
|
||||
@ -612,7 +612,7 @@ mod tests {
|
||||
assert_eq!(
|
||||
expected,
|
||||
type_option
|
||||
.decode_cell_data(cell_data, &field_meta.field_type, &field_meta)
|
||||
.decode_cell_data(cell_data, &field_meta.field_type, field_meta)
|
||||
.unwrap()
|
||||
.parse::<SelectOptionCellData>()
|
||||
.unwrap()
|
||||
@ -629,7 +629,7 @@ mod tests {
|
||||
assert_eq!(
|
||||
expected,
|
||||
type_option
|
||||
.decode_cell_data(cell_data, &field_meta.field_type, &field_meta)
|
||||
.decode_cell_data(cell_data, &field_meta.field_type, field_meta)
|
||||
.unwrap()
|
||||
.parse::<SelectOptionCellData>()
|
||||
.unwrap()
|
||||
|
@ -1,14 +1,16 @@
|
||||
use crate::impl_type_option;
|
||||
use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder};
|
||||
use crate::services::row::{CellContentChangeset, CellDataOperation, DecodedCellData};
|
||||
use crate::services::row::{CellContentChangeset, CellDataOperation, DecodedCellData, EncodedCellData};
|
||||
use bytes::Bytes;
|
||||
use fancy_regex::Regex;
|
||||
use flowy_derive::ProtoBuf;
|
||||
use flowy_error::{FlowyError, FlowyResult};
|
||||
use flowy_error::{internal_error, FlowyError, FlowyResult};
|
||||
use flowy_grid_data_model::entities::{
|
||||
CellMeta, FieldMeta, FieldType, TypeOptionDataDeserializer, TypeOptionDataEntry,
|
||||
};
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::str::FromStr;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct URLTypeOptionBuilder(URLTypeOption);
|
||||
@ -32,7 +34,7 @@ pub struct URLTypeOption {
|
||||
}
|
||||
impl_type_option!(URLTypeOption, FieldType::URL);
|
||||
|
||||
impl CellDataOperation<String, String> for URLTypeOption {
|
||||
impl CellDataOperation<EncodedCellData<URLCellData>, String> for URLTypeOption {
|
||||
fn decode_cell_data<T>(
|
||||
&self,
|
||||
encoded_data: T,
|
||||
@ -40,14 +42,13 @@ impl CellDataOperation<String, String> for URLTypeOption {
|
||||
_field_meta: &FieldMeta,
|
||||
) -> FlowyResult<DecodedCellData>
|
||||
where
|
||||
T: Into<String>,
|
||||
T: Into<EncodedCellData<URLCellData>>,
|
||||
{
|
||||
if !decoded_field_type.is_url() {
|
||||
return Ok(DecodedCellData::default());
|
||||
}
|
||||
|
||||
let cell_data = encoded_data.into();
|
||||
Ok(DecodedCellData::from_content(cell_data))
|
||||
let cell_data = encoded_data.into().try_into_inner()?;
|
||||
DecodedCellData::try_from_bytes(cell_data)
|
||||
}
|
||||
|
||||
fn apply_changeset<C>(&self, changeset: C, _cell_meta: Option<CellMeta>) -> Result<String, FlowyError>
|
||||
@ -55,7 +56,16 @@ impl CellDataOperation<String, String> for URLTypeOption {
|
||||
C: Into<CellContentChangeset>,
|
||||
{
|
||||
let changeset = changeset.into();
|
||||
Ok(changeset.to_string())
|
||||
let mut cell_data = URLCellData {
|
||||
url: "".to_string(),
|
||||
content: changeset.to_string(),
|
||||
};
|
||||
|
||||
if let Ok(Some(m)) = URL_REGEX.find(&changeset) {
|
||||
cell_data.url = m.as_str().to_string();
|
||||
}
|
||||
|
||||
cell_data.to_json()
|
||||
}
|
||||
}
|
||||
|
||||
@ -68,34 +78,97 @@ pub struct URLCellData {
|
||||
pub content: String,
|
||||
}
|
||||
|
||||
impl URLCellData {
|
||||
pub fn new(s: &str) -> Self {
|
||||
Self {
|
||||
url: "".to_string(),
|
||||
content: s.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
fn to_json(&self) -> FlowyResult<String> {
|
||||
serde_json::to_string(self).map_err(internal_error)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for URLCellData {
|
||||
type Err = FlowyError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
serde_json::from_str::<URLCellData>(s).map_err(internal_error)
|
||||
}
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref URL_REGEX: Regex = Regex::new(
|
||||
"[(http(s)?):\\/\\/(www\\.)?a-zA-Z0-9@:%._\\+~#=]{2,256}\\.[a-z]{2,6}\\b([-a-zA-Z0-9@:%_\\+.~#?&//=]*)"
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::services::field::FieldBuilder;
|
||||
use crate::services::field::URLTypeOption;
|
||||
use crate::services::row::CellDataOperation;
|
||||
use crate::services::field::{URLCellData, URLTypeOption};
|
||||
use crate::services::row::{CellDataOperation, EncodedCellData};
|
||||
use flowy_grid_data_model::entities::{FieldMeta, FieldType};
|
||||
|
||||
#[test]
|
||||
fn url_type_option_format_test() {
|
||||
fn url_type_option_test_no_url() {
|
||||
let type_option = URLTypeOption::default();
|
||||
let field_type = FieldType::URL;
|
||||
let field_meta = FieldBuilder::from_field_type(&field_type).build();
|
||||
assert_equal(&type_option, "123", "123", &field_type, &field_meta);
|
||||
assert_changeset(&type_option, "123", &field_type, &field_meta, "123", "");
|
||||
}
|
||||
|
||||
fn assert_equal(
|
||||
type_option: &URLTypeOption,
|
||||
cell_data: &str,
|
||||
expected_str: &str,
|
||||
field_type: &FieldType,
|
||||
field_meta: &FieldMeta,
|
||||
) {
|
||||
assert_eq!(
|
||||
type_option
|
||||
.decode_cell_data(cell_data, field_type, field_meta)
|
||||
.unwrap()
|
||||
.content,
|
||||
expected_str.to_owned()
|
||||
#[test]
|
||||
fn url_type_option_test_contains_url() {
|
||||
let type_option = URLTypeOption::default();
|
||||
let field_type = FieldType::URL;
|
||||
let field_meta = FieldBuilder::from_field_type(&field_type).build();
|
||||
assert_changeset(
|
||||
&type_option,
|
||||
"AppFlowy website - https://www.appflowy.io",
|
||||
&field_type,
|
||||
&field_meta,
|
||||
"AppFlowy website - https://www.appflowy.io",
|
||||
"https://www.appflowy.io",
|
||||
);
|
||||
|
||||
assert_changeset(
|
||||
&type_option,
|
||||
"AppFlowy website appflowy.io",
|
||||
&field_type,
|
||||
&field_meta,
|
||||
"AppFlowy website appflowy.io",
|
||||
"appflowy.io",
|
||||
);
|
||||
}
|
||||
|
||||
fn assert_changeset(
|
||||
type_option: &URLTypeOption,
|
||||
cell_data: &str,
|
||||
field_type: &FieldType,
|
||||
field_meta: &FieldMeta,
|
||||
expected: &str,
|
||||
expected_url: &str,
|
||||
) {
|
||||
let encoded_data = type_option.apply_changeset(cell_data, None).unwrap();
|
||||
let decode_cell_data = decode_cell_data(encoded_data, type_option, field_meta, field_type);
|
||||
assert_eq!(expected.to_owned(), decode_cell_data.content);
|
||||
assert_eq!(expected_url.to_owned(), decode_cell_data.url);
|
||||
}
|
||||
|
||||
fn decode_cell_data<T: Into<EncodedCellData<URLCellData>>>(
|
||||
encoded_data: T,
|
||||
type_option: &URLTypeOption,
|
||||
field_meta: &FieldMeta,
|
||||
field_type: &FieldType,
|
||||
) -> URLCellData {
|
||||
type_option
|
||||
.decode_cell_data(encoded_data, field_type, field_meta)
|
||||
.unwrap()
|
||||
.parse::<URLCellData>()
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user