mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
chore: move responsibility of url valdiation to front-frontend (#5129)
* chore: move responsibility of url valdiation to frontend * chore: fix test * chore: fix tauri build
This commit is contained in:
parent
8947a89a24
commit
e9e483291e
@ -34,12 +34,7 @@ export interface CheckboxCell extends Cell {
|
||||
|
||||
export interface UrlCell extends Cell {
|
||||
fieldType: FieldType.URL;
|
||||
data: UrlCellData;
|
||||
}
|
||||
|
||||
export interface UrlCellData {
|
||||
url: string;
|
||||
content?: string;
|
||||
data: string;
|
||||
}
|
||||
|
||||
export interface SelectCell extends Cell {
|
||||
@ -126,10 +121,9 @@ export const pbToSelectCellData = (pb: SelectOptionCellDataPB): SelectCellData =
|
||||
};
|
||||
};
|
||||
|
||||
const pbToURLCellData = (pb: URLCellDataPB): UrlCellData => ({
|
||||
url: pb.url,
|
||||
content: pb.content,
|
||||
});
|
||||
const pbToURLCellData = (pb: URLCellDataPB): string => (
|
||||
pb.content
|
||||
);
|
||||
|
||||
export const pbToChecklistCellData = (pb: ChecklistCellDataPB): ChecklistCellData => ({
|
||||
selectedOptions: pb.selected_options.map(({ id }) => id),
|
||||
|
@ -16,7 +16,7 @@ function UrlCell({ field, cell, placeholder }: Props) {
|
||||
const cellRef = useRef<HTMLDivElement>(null);
|
||||
const { value, editing, updateCell, setEditing, setValue } = useInputCell(cell);
|
||||
const handleClick = useCallback(() => {
|
||||
setValue(cell.data.content || '');
|
||||
setValue(cell.data);
|
||||
setEditing(true);
|
||||
}, [cell, setEditing, setValue]);
|
||||
|
||||
@ -32,19 +32,17 @@ function UrlCell({ field, cell, placeholder }: Props) {
|
||||
);
|
||||
|
||||
const content = useMemo(() => {
|
||||
const str = cell.data.content;
|
||||
|
||||
if (str) {
|
||||
if (cell.data) {
|
||||
return (
|
||||
<a
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
openUrl(str);
|
||||
openUrl(cell.data);
|
||||
}}
|
||||
target={'_blank'}
|
||||
className={'cursor-pointer text-content-blue-400 underline'}
|
||||
>
|
||||
{str}
|
||||
{cell.data}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
@ -4,9 +4,6 @@ use flowy_derive::ProtoBuf;
|
||||
#[derive(Clone, Debug, Default, ProtoBuf)]
|
||||
pub struct URLCellDataPB {
|
||||
#[pb(index = 1)]
|
||||
pub url: String,
|
||||
|
||||
#[pb(index = 2)]
|
||||
pub content: String,
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
use std::cmp::Ordering;
|
||||
|
||||
use bytes::Bytes;
|
||||
use collab::core::any_map::AnyMapExtension;
|
||||
use collab_database::fields::{Field, TypeOptionData, TypeOptionDataBuilder};
|
||||
use collab_database::rows::{new_cell_builder, Cell};
|
||||
@ -9,9 +8,7 @@ use serde::{Deserialize, Serialize};
|
||||
use flowy_error::{FlowyError, FlowyResult};
|
||||
|
||||
use crate::entities::{FieldType, TextFilterPB};
|
||||
use crate::services::cell::{
|
||||
stringify_cell, CellDataChangeset, CellDataDecoder, CellProtobufBlobParser,
|
||||
};
|
||||
use crate::services::cell::{stringify_cell, CellDataChangeset, CellDataDecoder};
|
||||
use crate::services::field::type_options::util::ProtobufStr;
|
||||
use crate::services::field::{
|
||||
TypeOption, TypeOptionCellData, TypeOptionCellDataCompare, TypeOptionCellDataFilter,
|
||||
@ -146,39 +143,6 @@ impl TypeOptionCellDataCompare for RichTextTypeOption {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TextCellData(pub String);
|
||||
impl AsRef<str> for TextCellData {
|
||||
fn as_ref(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for TextCellData {
|
||||
type Target = String;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for TextCellData {
|
||||
fn to_string(&self) -> String {
|
||||
self.0.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TextCellDataParser();
|
||||
impl CellProtobufBlobParser for TextCellDataParser {
|
||||
type Object = TextCellData;
|
||||
fn parser(bytes: &Bytes) -> FlowyResult<Self::Object> {
|
||||
match String::from_utf8(bytes.to_vec()) {
|
||||
Ok(s) => Ok(TextCellData(s)),
|
||||
Err(_) => Ok(TextCellData("".to_owned())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone)]
|
||||
pub struct StrCellData(pub String);
|
||||
impl std::ops::Deref for StrCellData {
|
||||
|
@ -7,161 +7,26 @@ mod tests {
|
||||
use crate::services::field::FieldBuilder;
|
||||
use crate::services::field::URLTypeOption;
|
||||
|
||||
/// The expected_str will equal to the input string, but the expected_url will be empty if there's no
|
||||
/// http url in the input string.
|
||||
#[test]
|
||||
fn url_type_option_does_not_contain_url_test() {
|
||||
let type_option = URLTypeOption::default();
|
||||
let field_type = FieldType::URL;
|
||||
let field = FieldBuilder::from_field_type(field_type).build();
|
||||
assert_url(&type_option, "123", "123", "", &field);
|
||||
assert_url(&type_option, "", "", "", &field);
|
||||
}
|
||||
|
||||
/// The expected_str will equal to the input string, but the expected_url will not be empty
|
||||
/// if there's a http url in the input string.
|
||||
#[test]
|
||||
fn url_type_option_contains_url_test() {
|
||||
fn url_test() {
|
||||
let type_option = URLTypeOption::default();
|
||||
let field_type = FieldType::URL;
|
||||
let field = FieldBuilder::from_field_type(field_type).build();
|
||||
assert_url(
|
||||
&type_option,
|
||||
"AppFlowy website - https://www.appflowy.io",
|
||||
"AppFlowy website - https://www.appflowy.io",
|
||||
"https://www.appflowy.io/",
|
||||
&field,
|
||||
);
|
||||
|
||||
assert_url(
|
||||
&type_option,
|
||||
"AppFlowy website appflowy.io",
|
||||
"AppFlowy website appflowy.io",
|
||||
"https://appflowy.io",
|
||||
"https://www.appflowy.io",
|
||||
"https://www.appflowy.io",
|
||||
&field,
|
||||
);
|
||||
assert_url(&type_option, "123", "123", &field);
|
||||
assert_url(&type_option, "", "", &field);
|
||||
}
|
||||
|
||||
/// if there's a http url and some words following it in the input string.
|
||||
#[test]
|
||||
fn url_type_option_contains_url_with_string_after_test() {
|
||||
let type_option = URLTypeOption::default();
|
||||
let field_type = FieldType::URL;
|
||||
let field = FieldBuilder::from_field_type(field_type).build();
|
||||
assert_url(
|
||||
&type_option,
|
||||
"AppFlowy website - https://www.appflowy.io welcome!",
|
||||
"AppFlowy website - https://www.appflowy.io welcome!",
|
||||
"https://www.appflowy.io/",
|
||||
&field,
|
||||
);
|
||||
|
||||
assert_url(
|
||||
&type_option,
|
||||
"AppFlowy website appflowy.io welcome!",
|
||||
"AppFlowy website appflowy.io welcome!",
|
||||
"https://appflowy.io",
|
||||
&field,
|
||||
);
|
||||
}
|
||||
|
||||
/// if there's a http url and special words following it in the input string.
|
||||
#[test]
|
||||
fn url_type_option_contains_url_with_special_string_after_test() {
|
||||
let type_option = URLTypeOption::default();
|
||||
let field_type = FieldType::URL;
|
||||
let field = FieldBuilder::from_field_type(field_type).build();
|
||||
assert_url(
|
||||
&type_option,
|
||||
"AppFlowy website - https://www.appflowy.io!",
|
||||
"AppFlowy website - https://www.appflowy.io!",
|
||||
"https://www.appflowy.io/",
|
||||
&field,
|
||||
);
|
||||
|
||||
assert_url(
|
||||
&type_option,
|
||||
"AppFlowy website appflowy.io!",
|
||||
"AppFlowy website appflowy.io!",
|
||||
"https://appflowy.io",
|
||||
&field,
|
||||
);
|
||||
}
|
||||
|
||||
/// if there's a level4 url in the input string.
|
||||
#[test]
|
||||
fn level4_url_type_test() {
|
||||
let type_option = URLTypeOption::default();
|
||||
let field_type = FieldType::URL;
|
||||
let field = FieldBuilder::from_field_type(field_type).build();
|
||||
assert_url(
|
||||
&type_option,
|
||||
"test - https://tester.testgroup.appflowy.io",
|
||||
"test - https://tester.testgroup.appflowy.io",
|
||||
"https://tester.testgroup.appflowy.io/",
|
||||
&field,
|
||||
);
|
||||
|
||||
assert_url(
|
||||
&type_option,
|
||||
"test tester.testgroup.appflowy.io",
|
||||
"test tester.testgroup.appflowy.io",
|
||||
"https://tester.testgroup.appflowy.io",
|
||||
&field,
|
||||
);
|
||||
}
|
||||
|
||||
/// urls with different top level domains.
|
||||
#[test]
|
||||
fn different_top_level_domains_test() {
|
||||
let type_option = URLTypeOption::default();
|
||||
let field_type = FieldType::URL;
|
||||
let field = FieldBuilder::from_field_type(field_type).build();
|
||||
assert_url(
|
||||
&type_option,
|
||||
"appflowy - https://appflowy.com",
|
||||
"appflowy - https://appflowy.com",
|
||||
"https://appflowy.com/",
|
||||
&field,
|
||||
);
|
||||
|
||||
assert_url(
|
||||
&type_option,
|
||||
"appflowy - https://appflowy.top",
|
||||
"appflowy - https://appflowy.top",
|
||||
"https://appflowy.top/",
|
||||
&field,
|
||||
);
|
||||
|
||||
assert_url(
|
||||
&type_option,
|
||||
"appflowy - https://appflowy.net",
|
||||
"appflowy - https://appflowy.net",
|
||||
"https://appflowy.net/",
|
||||
&field,
|
||||
);
|
||||
|
||||
assert_url(
|
||||
&type_option,
|
||||
"appflowy - https://appflowy.edu",
|
||||
"appflowy - https://appflowy.edu",
|
||||
"https://appflowy.edu/",
|
||||
&field,
|
||||
);
|
||||
}
|
||||
|
||||
fn assert_url(
|
||||
type_option: &URLTypeOption,
|
||||
input_str: &str,
|
||||
expected_str: &str,
|
||||
expected_url: &str,
|
||||
_field: &Field,
|
||||
) {
|
||||
fn assert_url(type_option: &URLTypeOption, input_str: &str, expected_url: &str, _field: &Field) {
|
||||
let decode_cell_data = type_option
|
||||
.apply_changeset(input_str.to_owned(), None)
|
||||
.unwrap()
|
||||
.1;
|
||||
assert_eq!(expected_str.to_owned(), decode_cell_data.data);
|
||||
assert_eq!(expected_url.to_owned(), decode_cell_data.url);
|
||||
assert_eq!(expected_url.to_owned(), decode_cell_data.data);
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,11 @@
|
||||
use std::cmp::Ordering;
|
||||
|
||||
use collab::core::any_map::AnyMapExtension;
|
||||
use collab_database::fields::{TypeOptionData, TypeOptionDataBuilder};
|
||||
use collab_database::rows::Cell;
|
||||
use flowy_error::FlowyResult;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::entities::{TextFilterPB, URLCellDataPB};
|
||||
use crate::services::cell::{CellDataChangeset, CellDataDecoder};
|
||||
use crate::services::field::{
|
||||
@ -6,15 +14,6 @@ use crate::services::field::{
|
||||
};
|
||||
use crate::services::sort::SortCondition;
|
||||
|
||||
use collab::core::any_map::AnyMapExtension;
|
||||
use collab_database::fields::{TypeOptionData, TypeOptionDataBuilder};
|
||||
use collab_database::rows::Cell;
|
||||
use fancy_regex::Regex;
|
||||
use flowy_error::FlowyResult;
|
||||
use lazy_static::lazy_static;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::cmp::Ordering;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct URLTypeOption {
|
||||
pub url: String,
|
||||
@ -82,14 +81,7 @@ impl CellDataChangeset for URLTypeOption {
|
||||
changeset: <Self as TypeOption>::CellChangeset,
|
||||
_cell: Option<Cell>,
|
||||
) -> FlowyResult<(Cell, <Self as TypeOption>::CellData)> {
|
||||
let mut url = "".to_string();
|
||||
if let Ok(Some(m)) = URL_REGEX.find(&changeset) {
|
||||
url = auto_append_scheme(m.as_str());
|
||||
}
|
||||
let url_cell_data = URLCellData {
|
||||
url,
|
||||
data: changeset,
|
||||
};
|
||||
let url_cell_data = URLCellData { data: changeset };
|
||||
Ok((url_cell_data.clone().into(), url_cell_data))
|
||||
}
|
||||
}
|
||||
@ -124,26 +116,3 @@ impl TypeOptionCellDataCompare for URLTypeOption {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn auto_append_scheme(s: &str) -> String {
|
||||
// Only support https scheme by now
|
||||
match url::Url::parse(s) {
|
||||
Ok(url) => {
|
||||
if url.scheme() == "https" {
|
||||
url.into()
|
||||
} else {
|
||||
format!("https://{}", s)
|
||||
}
|
||||
},
|
||||
Err(_) => {
|
||||
format!("https://{}", s)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
@ -11,14 +11,12 @@ use crate::services::field::{TypeOptionCellData, CELL_DATA};
|
||||
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
|
||||
pub struct URLCellData {
|
||||
pub url: String,
|
||||
pub data: String,
|
||||
}
|
||||
|
||||
impl URLCellData {
|
||||
pub fn new(s: &str) -> Self {
|
||||
Self {
|
||||
url: "".to_string(),
|
||||
data: s.to_string(),
|
||||
}
|
||||
}
|
||||
@ -36,16 +34,14 @@ impl TypeOptionCellData for URLCellData {
|
||||
|
||||
impl From<&Cell> for URLCellData {
|
||||
fn from(cell: &Cell) -> Self {
|
||||
let url = cell.get_str_value("url").unwrap_or_default();
|
||||
let content = cell.get_str_value(CELL_DATA).unwrap_or_default();
|
||||
Self { url, data: content }
|
||||
let data = cell.get_str_value(CELL_DATA).unwrap_or_default();
|
||||
Self { data }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<URLCellData> for Cell {
|
||||
fn from(data: URLCellData) -> Self {
|
||||
new_cell_builder(FieldType::URL)
|
||||
.insert_str_value("url", data.url)
|
||||
.insert_str_value(CELL_DATA, data.data)
|
||||
.build()
|
||||
}
|
||||
@ -53,19 +49,13 @@ impl From<URLCellData> for Cell {
|
||||
|
||||
impl From<URLCellData> for URLCellDataPB {
|
||||
fn from(data: URLCellData) -> Self {
|
||||
Self {
|
||||
url: data.url,
|
||||
content: data.data,
|
||||
}
|
||||
Self { content: data.data }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<URLCellDataPB> for URLCellData {
|
||||
fn from(data: URLCellDataPB) -> Self {
|
||||
Self {
|
||||
url: data.url,
|
||||
data: data.content,
|
||||
}
|
||||
Self { data: data.content }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -54,7 +54,7 @@ impl GroupCustomize for URLGroupController {
|
||||
) -> FlowyResult<(Option<InsertedGroupPB>, Option<GroupPB>)> {
|
||||
// Just return if the group with this url already exists
|
||||
let mut inserted_group = None;
|
||||
if self.context.get_group(&_cell_data.url).is_none() {
|
||||
if self.context.get_group(&_cell_data.content).is_none() {
|
||||
let cell_data: URLCellData = _cell_data.clone().into();
|
||||
let group = Group::new(cell_data.data);
|
||||
let mut new_group = self.context.add_new_group(group)?;
|
||||
|
@ -110,7 +110,10 @@ async fn url_cell_data_test() {
|
||||
if let Some(cell) = row_cell.cell.as_ref() {
|
||||
let cell = URLCellData::from(cell);
|
||||
if i == 0 {
|
||||
assert_eq!(cell.url.as_str(), "https://www.appflowy.io/");
|
||||
assert_eq!(
|
||||
cell.data.as_str(),
|
||||
"AppFlowy website - https://www.appflowy.io"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user