mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
Merge pull request #1073 from AppFlowy-IO/feat/merge_release_0052
Feat/merge release 0052
This commit is contained in:
@ -22,7 +22,7 @@ CARGO_MAKE_EXTEND_WORKSPACE_MAKEFILE = true
|
|||||||
CARGO_MAKE_CRATE_FS_NAME = "dart_ffi"
|
CARGO_MAKE_CRATE_FS_NAME = "dart_ffi"
|
||||||
CARGO_MAKE_CRATE_NAME = "dart-ffi"
|
CARGO_MAKE_CRATE_NAME = "dart-ffi"
|
||||||
LIB_NAME = "dart_ffi"
|
LIB_NAME = "dart_ffi"
|
||||||
CURRENT_APP_VERSION = "0.0.5.1"
|
CURRENT_APP_VERSION = "0.0.5.2"
|
||||||
FEATURES = "flutter"
|
FEATURES = "flutter"
|
||||||
PRODUCT_NAME = "AppFlowy"
|
PRODUCT_NAME = "AppFlowy"
|
||||||
# CRATE_TYPE: https://doc.rust-lang.org/reference/linkage.html
|
# CRATE_TYPE: https://doc.rust-lang.org/reference/linkage.html
|
||||||
|
@ -187,35 +187,31 @@ class _BoardContentState extends State<BoardContent> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildFooter(BuildContext context, AppFlowyGroupData columnData) {
|
Widget _buildFooter(BuildContext context, AppFlowyGroupData columnData) {
|
||||||
final boardCustomData = columnData.customData as BoardCustomData;
|
// final boardCustomData = columnData.customData as BoardCustomData;
|
||||||
final group = boardCustomData.group;
|
// final group = boardCustomData.group;
|
||||||
|
|
||||||
if (group.isDefault) {
|
return AppFlowyGroupFooter(
|
||||||
return const SizedBox();
|
icon: SizedBox(
|
||||||
} else {
|
height: 20,
|
||||||
return AppFlowyGroupFooter(
|
width: 20,
|
||||||
icon: SizedBox(
|
child: svgWidget(
|
||||||
height: 20,
|
"home/add",
|
||||||
width: 20,
|
color: context.read<AppTheme>().iconColor,
|
||||||
child: svgWidget(
|
|
||||||
"home/add",
|
|
||||||
color: context.read<AppTheme>().iconColor,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
title: FlowyText.medium(
|
),
|
||||||
LocaleKeys.board_column_create_new_card.tr(),
|
title: FlowyText.medium(
|
||||||
fontSize: 14,
|
LocaleKeys.board_column_create_new_card.tr(),
|
||||||
color: context.read<AppTheme>().textColor,
|
fontSize: 14,
|
||||||
),
|
color: context.read<AppTheme>().textColor,
|
||||||
height: 50,
|
),
|
||||||
margin: config.footerPadding,
|
height: 50,
|
||||||
onAddButtonClick: () {
|
margin: config.footerPadding,
|
||||||
context.read<BoardBloc>().add(
|
onAddButtonClick: () {
|
||||||
BoardEvent.createBottomRow(columnData.id),
|
context.read<BoardBloc>().add(
|
||||||
);
|
BoardEvent.createBottomRow(columnData.id),
|
||||||
},
|
);
|
||||||
);
|
},
|
||||||
}
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildCard(
|
Widget _buildCard(
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import 'package:app_flowy/plugins/grid/application/row/row_action_sheet_bloc.dart';
|
import 'package:app_flowy/plugins/grid/application/row/row_action_sheet_bloc.dart';
|
||||||
import 'package:app_flowy/workspace/presentation/widgets/dialogs.dart';
|
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:app_flowy/generated/locale_keys.g.dart';
|
import 'package:app_flowy/generated/locale_keys.g.dart';
|
||||||
import 'package:flowy_infra/image.dart';
|
import 'package:flowy_infra/image.dart';
|
||||||
@ -151,14 +150,9 @@ extension _RowActionExtension on _RowAction {
|
|||||||
.add(const RowActionSheetEvent.duplicateRow());
|
.add(const RowActionSheetEvent.duplicateRow());
|
||||||
break;
|
break;
|
||||||
case _RowAction.delete:
|
case _RowAction.delete:
|
||||||
NavigatorAlertDialog(
|
context
|
||||||
title: LocaleKeys.grid_field_deleteFieldPromptMessage.tr(),
|
.read<RowActionSheetBloc>()
|
||||||
confirm: () {
|
.add(const RowActionSheetEvent.deleteRow());
|
||||||
context
|
|
||||||
.read<RowActionSheetBloc>()
|
|
||||||
.add(const RowActionSheetEvent.deleteRow());
|
|
||||||
},
|
|
||||||
).show(context);
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -78,7 +78,7 @@ impl FolderMigration {
|
|||||||
|
|
||||||
let folder = FolderPad::new(workspaces, trash)?;
|
let folder = FolderPad::new(workspaces, trash)?;
|
||||||
KV::set_bool(&key, true);
|
KV::set_bool(&key, true);
|
||||||
tracing::trace!("Run folder v1 migration");
|
tracing::info!("Run folder v1 migration");
|
||||||
Ok(Some(folder))
|
Ok(Some(folder))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,11 +89,10 @@ impl FolderMigration {
|
|||||||
}
|
}
|
||||||
let _ = self.migration_folder_rev_struct(folder_id).await?;
|
let _ = self.migration_folder_rev_struct(folder_id).await?;
|
||||||
KV::set_bool(&key, true);
|
KV::set_bool(&key, true);
|
||||||
tracing::trace!("Run folder v2 migration");
|
// tracing::info!("Run folder v2 migration");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub async fn run_v3_migration(&self, folder_id: &FolderId) -> FlowyResult<()> {
|
pub async fn run_v3_migration(&self, folder_id: &FolderId) -> FlowyResult<()> {
|
||||||
let key = migration_flag_key(&self.user_id, V3_MIGRATION);
|
let key = migration_flag_key(&self.user_id, V3_MIGRATION);
|
||||||
if KV::get_bool(&key) {
|
if KV::get_bool(&key) {
|
||||||
@ -101,7 +100,7 @@ impl FolderMigration {
|
|||||||
}
|
}
|
||||||
let _ = self.migration_folder_rev_struct(folder_id).await?;
|
let _ = self.migration_folder_rev_struct(folder_id).await?;
|
||||||
KV::set_bool(&key, true);
|
KV::set_bool(&key, true);
|
||||||
tracing::trace!("Run folder v3 migration");
|
tracing::info!("Run folder v3 migration");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,8 +101,7 @@ impl FolderPersistence {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let _ = migrations.run_v2_migration(folder_id).await?;
|
let _ = migrations.run_v2_migration(folder_id).await?;
|
||||||
|
let _ = migrations.run_v3_migration(folder_id).await?;
|
||||||
// let _ = migrations.run_v3_migration(folder_id).await?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,6 +57,7 @@ impl GridBlockManager {
|
|||||||
Ok(self.get_block_editor(&block_id).await?)
|
Ok(self.get_block_editor(&block_id).await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(level = "trace", skip(self, start_row_id), err)]
|
||||||
pub(crate) async fn create_row(&self, row_rev: RowRevision, start_row_id: Option<String>) -> FlowyResult<i32> {
|
pub(crate) async fn create_row(&self, row_rev: RowRevision, start_row_id: Option<String>) -> FlowyResult<i32> {
|
||||||
let block_id = row_rev.block_id.clone();
|
let block_id = row_rev.block_id.clone();
|
||||||
let _ = self.persistence.insert(&row_rev.block_id, &row_rev.id)?;
|
let _ = self.persistence.insert(&row_rev.block_id, &row_rev.id)?;
|
||||||
|
@ -29,6 +29,7 @@ impl TypeOptionBuilder for RichTextTypeOptionBuilder {
|
|||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, ProtoBuf)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, ProtoBuf)]
|
||||||
pub struct RichTextTypeOptionPB {
|
pub struct RichTextTypeOptionPB {
|
||||||
#[pb(index = 1)]
|
#[pb(index = 1)]
|
||||||
|
#[serde(default)]
|
||||||
data: String, //It's not used yet
|
data: String, //It's not used yet
|
||||||
}
|
}
|
||||||
impl_type_option!(RichTextTypeOptionPB, FieldType::RichText);
|
impl_type_option!(RichTextTypeOptionPB, FieldType::RichText);
|
||||||
|
@ -420,6 +420,7 @@ impl GridRevisionEditor {
|
|||||||
|
|
||||||
pub async fn delete_row(&self, row_id: &str) -> FlowyResult<()> {
|
pub async fn delete_row(&self, row_id: &str) -> FlowyResult<()> {
|
||||||
let row_rev = self.block_manager.delete_row(row_id).await?;
|
let row_rev = self.block_manager.delete_row(row_id).await?;
|
||||||
|
tracing::trace!("Did delete row:{:?}", row_rev);
|
||||||
if let Some(row_rev) = row_rev {
|
if let Some(row_rev) = row_rev {
|
||||||
self.view_manager.did_delete_row(row_rev).await;
|
self.view_manager.did_delete_row(row_rev).await;
|
||||||
}
|
}
|
||||||
|
@ -109,6 +109,7 @@ impl GridViewRevisionEditor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(level = "trace", skip_all)]
|
||||||
pub(crate) async fn did_delete_row(&self, row_rev: &RowRevision) {
|
pub(crate) async fn did_delete_row(&self, row_rev: &RowRevision) {
|
||||||
// Send the group notification if the current view has groups;
|
// Send the group notification if the current view has groups;
|
||||||
let changesets = self
|
let changesets = self
|
||||||
@ -116,6 +117,7 @@ impl GridViewRevisionEditor {
|
|||||||
.await;
|
.await;
|
||||||
|
|
||||||
if let Some(changesets) = changesets {
|
if let Some(changesets) = changesets {
|
||||||
|
tracing::trace!("{:?}", changesets);
|
||||||
for changeset in changesets {
|
for changeset in changesets {
|
||||||
self.notify_did_update_group(changeset).await;
|
self.notify_did_update_group(changeset).await;
|
||||||
}
|
}
|
||||||
|
@ -92,17 +92,33 @@ where
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the groups without the default group
|
pub(crate) fn get_default_group(&self) -> &Group {
|
||||||
pub(crate) fn concrete_groups(&self) -> Vec<&Group> {
|
|
||||||
self.groups_map.values().collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn default_group(&self) -> &Group {
|
|
||||||
&self.default_group
|
&self.default_group
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_mut_default_group(&mut self) -> &mut Group {
|
||||||
|
&mut self.default_group
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the groups without the default group
|
||||||
|
pub(crate) fn groups(&self) -> Vec<&Group> {
|
||||||
|
self.groups_map.values().collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_mut_group(&mut self, group_id: &str) -> Option<&mut Group> {
|
||||||
|
self.groups_map.get_mut(group_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the index and group specified by the group_id
|
||||||
|
pub(crate) fn get_group(&self, group_id: &str) -> Option<(usize, &Group)> {
|
||||||
|
match (self.groups_map.get_index_of(group_id), self.groups_map.get(group_id)) {
|
||||||
|
(Some(index), Some(group)) => Some((index, group)),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Iterate mut the groups. The default group will be the last one that get mutated.
|
/// Iterate mut the groups. The default group will be the last one that get mutated.
|
||||||
pub(crate) fn iter_mut_groups(&mut self, mut each: impl FnMut(&mut Group)) {
|
pub(crate) fn iter_mut_all_groups(&mut self, mut each: impl FnMut(&mut Group)) {
|
||||||
self.groups_map.iter_mut().for_each(|(_, group)| {
|
self.groups_map.iter_mut().for_each(|(_, group)| {
|
||||||
each(group);
|
each(group);
|
||||||
});
|
});
|
||||||
@ -253,22 +269,6 @@ where
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_mut_default_group(&mut self) -> &mut Group {
|
|
||||||
&mut self.default_group
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn get_mut_group(&mut self, group_id: &str) -> Option<&mut Group> {
|
|
||||||
self.groups_map.get_mut(group_id)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the index and group specified by the group_id
|
|
||||||
pub(crate) fn get_group(&self, group_id: &str) -> Option<(usize, &Group)> {
|
|
||||||
match (self.groups_map.get_index_of(group_id), self.groups_map.get(group_id)) {
|
|
||||||
(Some(index), Some(group)) => Some((index, group)),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn save_configuration(&self) -> FlowyResult<()> {
|
pub fn save_configuration(&self) -> FlowyResult<()> {
|
||||||
let configuration = (&*self.configuration).clone();
|
let configuration = (&*self.configuration).clone();
|
||||||
let writer = self.writer.clone();
|
let writer = self.writer.clone();
|
||||||
|
@ -183,11 +183,11 @@ where
|
|||||||
|
|
||||||
fn groups(&self) -> Vec<Group> {
|
fn groups(&self) -> Vec<Group> {
|
||||||
if self.use_default_group() {
|
if self.use_default_group() {
|
||||||
let mut groups: Vec<Group> = self.group_ctx.concrete_groups().into_iter().cloned().collect();
|
let mut groups: Vec<Group> = self.group_ctx.groups().into_iter().cloned().collect();
|
||||||
groups.push(self.group_ctx.default_group().clone());
|
groups.push(self.group_ctx.get_default_group().clone());
|
||||||
groups
|
groups
|
||||||
} else {
|
} else {
|
||||||
self.group_ctx.concrete_groups().into_iter().cloned().collect()
|
self.group_ctx.groups().into_iter().cloned().collect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -208,7 +208,7 @@ where
|
|||||||
let mut grouped_rows: Vec<GroupedRow> = vec![];
|
let mut grouped_rows: Vec<GroupedRow> = vec![];
|
||||||
let cell_bytes = decode_any_cell_data(cell_rev.data, field_rev);
|
let cell_bytes = decode_any_cell_data(cell_rev.data, field_rev);
|
||||||
let cell_data = cell_bytes.parser::<P>()?;
|
let cell_data = cell_bytes.parser::<P>()?;
|
||||||
for group in self.group_ctx.concrete_groups() {
|
for group in self.group_ctx.groups() {
|
||||||
if self.can_group(&group.filter_content, &cell_data) {
|
if self.can_group(&group.filter_content, &cell_data) {
|
||||||
grouped_rows.push(GroupedRow {
|
grouped_rows.push(GroupedRow {
|
||||||
row: row_rev.into(),
|
row: row_rev.into(),
|
||||||
@ -264,12 +264,17 @@ where
|
|||||||
row_rev: &RowRevision,
|
row_rev: &RowRevision,
|
||||||
field_rev: &FieldRevision,
|
field_rev: &FieldRevision,
|
||||||
) -> FlowyResult<Vec<GroupChangesetPB>> {
|
) -> FlowyResult<Vec<GroupChangesetPB>> {
|
||||||
|
// if the cell_rev is none, then the row must be crated from the default group.
|
||||||
if let Some(cell_rev) = row_rev.cells.get(&self.field_id) {
|
if let Some(cell_rev) = row_rev.cells.get(&self.field_id) {
|
||||||
let cell_bytes = decode_any_cell_data(cell_rev.data.clone(), field_rev);
|
let cell_bytes = decode_any_cell_data(cell_rev.data.clone(), field_rev);
|
||||||
let cell_data = cell_bytes.parser::<P>()?;
|
let cell_data = cell_bytes.parser::<P>()?;
|
||||||
Ok(self.remove_row_if_match(row_rev, &cell_data))
|
Ok(self.remove_row_if_match(row_rev, &cell_data))
|
||||||
} else {
|
} else {
|
||||||
Ok(vec![])
|
let group = self.group_ctx.get_default_group();
|
||||||
|
Ok(vec![GroupChangesetPB::delete(
|
||||||
|
group.id.clone(),
|
||||||
|
vec![row_rev.id.clone()],
|
||||||
|
)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ impl GroupAction for CheckboxGroupController {
|
|||||||
|
|
||||||
fn add_row_if_match(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec<GroupChangesetPB> {
|
fn add_row_if_match(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec<GroupChangesetPB> {
|
||||||
let mut changesets = vec![];
|
let mut changesets = vec![];
|
||||||
self.group_ctx.iter_mut_groups(|group| {
|
self.group_ctx.iter_mut_all_groups(|group| {
|
||||||
let mut changeset = GroupChangesetPB::new(group.id.clone());
|
let mut changeset = GroupChangesetPB::new(group.id.clone());
|
||||||
let is_contained = group.contains_row(&row_rev.id);
|
let is_contained = group.contains_row(&row_rev.id);
|
||||||
if group.id == CHECK && cell_data.is_check() {
|
if group.id == CHECK && cell_data.is_check() {
|
||||||
@ -63,7 +63,7 @@ impl GroupAction for CheckboxGroupController {
|
|||||||
|
|
||||||
fn remove_row_if_match(&mut self, row_rev: &RowRevision, _cell_data: &Self::CellDataType) -> Vec<GroupChangesetPB> {
|
fn remove_row_if_match(&mut self, row_rev: &RowRevision, _cell_data: &Self::CellDataType) -> Vec<GroupChangesetPB> {
|
||||||
let mut changesets = vec![];
|
let mut changesets = vec![];
|
||||||
self.group_ctx.iter_mut_groups(|group| {
|
self.group_ctx.iter_mut_all_groups(|group| {
|
||||||
let mut changeset = GroupChangesetPB::new(group.id.clone());
|
let mut changeset = GroupChangesetPB::new(group.id.clone());
|
||||||
if group.contains_row(&row_rev.id) {
|
if group.contains_row(&row_rev.id) {
|
||||||
changeset.deleted_rows.push(row_rev.id.clone());
|
changeset.deleted_rows.push(row_rev.id.clone());
|
||||||
@ -79,7 +79,7 @@ impl GroupAction for CheckboxGroupController {
|
|||||||
|
|
||||||
fn move_row(&mut self, _cell_data: &Self::CellDataType, mut context: MoveGroupRowContext) -> Vec<GroupChangesetPB> {
|
fn move_row(&mut self, _cell_data: &Self::CellDataType, mut context: MoveGroupRowContext) -> Vec<GroupChangesetPB> {
|
||||||
let mut group_changeset = vec![];
|
let mut group_changeset = vec![];
|
||||||
self.group_ctx.iter_mut_groups(|group| {
|
self.group_ctx.iter_mut_all_groups(|group| {
|
||||||
if let Some(changeset) = move_group_row(group, &mut context) {
|
if let Some(changeset) = move_group_row(group, &mut context) {
|
||||||
group_changeset.push(changeset);
|
group_changeset.push(changeset);
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ impl GroupAction for MultiSelectGroupController {
|
|||||||
|
|
||||||
fn add_row_if_match(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec<GroupChangesetPB> {
|
fn add_row_if_match(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec<GroupChangesetPB> {
|
||||||
let mut changesets = vec![];
|
let mut changesets = vec![];
|
||||||
self.group_ctx.iter_mut_groups(|group| {
|
self.group_ctx.iter_mut_all_groups(|group| {
|
||||||
if let Some(changeset) = add_select_option_row(group, cell_data, row_rev) {
|
if let Some(changeset) = add_select_option_row(group, cell_data, row_rev) {
|
||||||
changesets.push(changeset);
|
changesets.push(changeset);
|
||||||
}
|
}
|
||||||
@ -38,7 +38,7 @@ impl GroupAction for MultiSelectGroupController {
|
|||||||
|
|
||||||
fn remove_row_if_match(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec<GroupChangesetPB> {
|
fn remove_row_if_match(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec<GroupChangesetPB> {
|
||||||
let mut changesets = vec![];
|
let mut changesets = vec![];
|
||||||
self.group_ctx.iter_mut_groups(|group| {
|
self.group_ctx.iter_mut_all_groups(|group| {
|
||||||
if let Some(changeset) = remove_select_option_row(group, cell_data, row_rev) {
|
if let Some(changeset) = remove_select_option_row(group, cell_data, row_rev) {
|
||||||
changesets.push(changeset);
|
changesets.push(changeset);
|
||||||
}
|
}
|
||||||
@ -48,7 +48,7 @@ impl GroupAction for MultiSelectGroupController {
|
|||||||
|
|
||||||
fn move_row(&mut self, _cell_data: &Self::CellDataType, mut context: MoveGroupRowContext) -> Vec<GroupChangesetPB> {
|
fn move_row(&mut self, _cell_data: &Self::CellDataType, mut context: MoveGroupRowContext) -> Vec<GroupChangesetPB> {
|
||||||
let mut group_changeset = vec![];
|
let mut group_changeset = vec![];
|
||||||
self.group_ctx.iter_mut_groups(|group| {
|
self.group_ctx.iter_mut_all_groups(|group| {
|
||||||
if let Some(changeset) = move_group_row(group, &mut context) {
|
if let Some(changeset) = move_group_row(group, &mut context) {
|
||||||
group_changeset.push(changeset);
|
group_changeset.push(changeset);
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ impl GroupAction for SingleSelectGroupController {
|
|||||||
|
|
||||||
fn add_row_if_match(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec<GroupChangesetPB> {
|
fn add_row_if_match(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec<GroupChangesetPB> {
|
||||||
let mut changesets = vec![];
|
let mut changesets = vec![];
|
||||||
self.group_ctx.iter_mut_groups(|group| {
|
self.group_ctx.iter_mut_all_groups(|group| {
|
||||||
if let Some(changeset) = add_select_option_row(group, cell_data, row_rev) {
|
if let Some(changeset) = add_select_option_row(group, cell_data, row_rev) {
|
||||||
changesets.push(changeset);
|
changesets.push(changeset);
|
||||||
}
|
}
|
||||||
@ -38,7 +38,7 @@ impl GroupAction for SingleSelectGroupController {
|
|||||||
|
|
||||||
fn remove_row_if_match(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec<GroupChangesetPB> {
|
fn remove_row_if_match(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec<GroupChangesetPB> {
|
||||||
let mut changesets = vec![];
|
let mut changesets = vec![];
|
||||||
self.group_ctx.iter_mut_groups(|group| {
|
self.group_ctx.iter_mut_all_groups(|group| {
|
||||||
if let Some(changeset) = remove_select_option_row(group, cell_data, row_rev) {
|
if let Some(changeset) = remove_select_option_row(group, cell_data, row_rev) {
|
||||||
changesets.push(changeset);
|
changesets.push(changeset);
|
||||||
}
|
}
|
||||||
@ -48,7 +48,7 @@ impl GroupAction for SingleSelectGroupController {
|
|||||||
|
|
||||||
fn move_row(&mut self, _cell_data: &Self::CellDataType, mut context: MoveGroupRowContext) -> Vec<GroupChangesetPB> {
|
fn move_row(&mut self, _cell_data: &Self::CellDataType, mut context: MoveGroupRowContext) -> Vec<GroupChangesetPB> {
|
||||||
let mut group_changeset = vec![];
|
let mut group_changeset = vec![];
|
||||||
self.group_ctx.iter_mut_groups(|group| {
|
self.group_ctx.iter_mut_all_groups(|group| {
|
||||||
if let Some(changeset) = move_group_row(group, &mut context) {
|
if let Some(changeset) = move_group_row(group, &mut context) {
|
||||||
group_changeset.push(changeset);
|
group_changeset.push(changeset);
|
||||||
}
|
}
|
||||||
|
@ -17,10 +17,13 @@ pub struct AppRevision {
|
|||||||
|
|
||||||
pub belongings: Vec<ViewRevision>,
|
pub belongings: Vec<ViewRevision>,
|
||||||
|
|
||||||
|
#[serde(default)]
|
||||||
pub version: i64,
|
pub version: i64,
|
||||||
|
|
||||||
|
#[serde(default)]
|
||||||
pub modified_time: i64,
|
pub modified_time: i64,
|
||||||
|
|
||||||
|
#[serde(default)]
|
||||||
pub create_time: i64,
|
pub create_time: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,9 +1,76 @@
|
|||||||
use crate::revision::{TrashRevision, WorkspaceRevision};
|
use crate::revision::{TrashRevision, WorkspaceRevision};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::de::{MapAccess, Visitor};
|
||||||
|
use serde::{de, Deserialize, Deserializer, Serialize};
|
||||||
|
use std::fmt;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize, Serialize, Clone, Eq, PartialEq)]
|
#[derive(Debug, Default, Serialize, Clone, Eq, PartialEq)]
|
||||||
pub struct FolderRevision {
|
pub struct FolderRevision {
|
||||||
pub workspaces: Vec<Arc<WorkspaceRevision>>,
|
pub workspaces: Vec<Arc<WorkspaceRevision>>,
|
||||||
pub trash: Vec<Arc<TrashRevision>>,
|
pub trash: Vec<Arc<TrashRevision>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for FolderRevision {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
struct FolderVisitor<'a>(&'a mut Option<FolderRevision>);
|
||||||
|
impl<'de, 'a> Visitor<'de> for FolderVisitor<'a> {
|
||||||
|
type Value = ();
|
||||||
|
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
formatter.write_str("Expect struct FolderRevision")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
|
||||||
|
where
|
||||||
|
A: MapAccess<'de>,
|
||||||
|
{
|
||||||
|
let f = |map: &mut A,
|
||||||
|
workspaces: &mut Option<Vec<WorkspaceRevision>>,
|
||||||
|
trash: &mut Option<Vec<TrashRevision>>| match map.next_key::<String>()
|
||||||
|
{
|
||||||
|
Ok(Some(key)) => {
|
||||||
|
if key == "workspaces" && workspaces.is_none() {
|
||||||
|
*workspaces = Some(map.next_value::<Vec<WorkspaceRevision>>().ok()?);
|
||||||
|
}
|
||||||
|
if key == "trash" && trash.is_none() {
|
||||||
|
*trash = Some(map.next_value::<Vec<TrashRevision>>().ok()?);
|
||||||
|
}
|
||||||
|
Some(())
|
||||||
|
}
|
||||||
|
Ok(None) => None,
|
||||||
|
Err(_e) => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut workspaces: Option<Vec<WorkspaceRevision>> = None;
|
||||||
|
let mut trash: Option<Vec<TrashRevision>> = None;
|
||||||
|
while f(&mut map, &mut workspaces, &mut trash).is_some() {
|
||||||
|
if workspaces.is_some() && trash.is_some() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*self.0 = Some(FolderRevision {
|
||||||
|
workspaces: workspaces.unwrap_or_default().into_iter().map(Arc::new).collect(),
|
||||||
|
trash: trash.unwrap_or_default().into_iter().map(Arc::new).collect(),
|
||||||
|
});
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut folder_rev: Option<FolderRevision> = None;
|
||||||
|
const FIELDS: &[&str] = &["workspaces", "trash"];
|
||||||
|
let _ = serde::Deserializer::deserialize_struct(
|
||||||
|
deserializer,
|
||||||
|
"FolderRevision",
|
||||||
|
FIELDS,
|
||||||
|
FolderVisitor(&mut folder_rev),
|
||||||
|
);
|
||||||
|
|
||||||
|
match folder_rev {
|
||||||
|
None => Err(de::Error::missing_field("workspaces or trash")),
|
||||||
|
Some(folder_rev) => Ok(folder_rev),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -8,8 +8,10 @@ pub struct TrashRevision {
|
|||||||
|
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
|
||||||
|
#[serde(default)]
|
||||||
pub modified_time: i64,
|
pub modified_time: i64,
|
||||||
|
|
||||||
|
#[serde(default)]
|
||||||
pub create_time: i64,
|
pub create_time: i64,
|
||||||
|
|
||||||
pub ty: TrashTypeRevision,
|
pub ty: TrashTypeRevision,
|
||||||
|
@ -9,7 +9,6 @@ pub fn gen_view_id() -> String {
|
|||||||
pub struct ViewRevision {
|
pub struct ViewRevision {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
|
|
||||||
// Maybe app_id or vi
|
|
||||||
#[serde(rename = "belong_to_id")]
|
#[serde(rename = "belong_to_id")]
|
||||||
pub app_id: String,
|
pub app_id: String,
|
||||||
|
|
||||||
@ -24,8 +23,10 @@ pub struct ViewRevision {
|
|||||||
|
|
||||||
pub belongings: Vec<ViewRevision>,
|
pub belongings: Vec<ViewRevision>,
|
||||||
|
|
||||||
|
#[serde(default)]
|
||||||
pub modified_time: i64,
|
pub modified_time: i64,
|
||||||
|
|
||||||
|
#[serde(default)]
|
||||||
pub create_time: i64,
|
pub create_time: i64,
|
||||||
|
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
@ -14,7 +14,9 @@ pub struct WorkspaceRevision {
|
|||||||
|
|
||||||
pub apps: Vec<AppRevision>,
|
pub apps: Vec<AppRevision>,
|
||||||
|
|
||||||
|
#[serde(default)]
|
||||||
pub modified_time: i64,
|
pub modified_time: i64,
|
||||||
|
|
||||||
|
#[serde(default)]
|
||||||
pub create_time: i64,
|
pub create_time: i64,
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ use flowy_folder_data_model::revision::{AppRevision, FolderRevision, TrashRevisi
|
|||||||
use lib_infra::util::move_vec_element;
|
use lib_infra::util::move_vec_element;
|
||||||
use lib_ot::core::*;
|
use lib_ot::core::*;
|
||||||
|
|
||||||
|
use serde::Deserialize;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
@ -44,7 +45,9 @@ impl FolderPad {
|
|||||||
pub fn from_delta(delta: FolderDelta) -> CollaborateResult<Self> {
|
pub fn from_delta(delta: FolderDelta) -> CollaborateResult<Self> {
|
||||||
// TODO: Reconvert from history if delta.to_str() failed.
|
// TODO: Reconvert from history if delta.to_str() failed.
|
||||||
let content = delta.content()?;
|
let content = delta.content()?;
|
||||||
let folder_rev: FolderRevision = serde_json::from_str(&content).map_err(|e| {
|
let mut deserializer = serde_json::Deserializer::from_reader(content.as_bytes());
|
||||||
|
|
||||||
|
let folder_rev = FolderRevision::deserialize(&mut deserializer).map_err(|e| {
|
||||||
tracing::error!("Deserialize folder from {} failed", content);
|
tracing::error!("Deserialize folder from {} failed", content);
|
||||||
return CollaborateError::internal().context(format!("Deserialize delta to folder failed: {}", e));
|
return CollaborateError::internal().context(format!("Deserialize delta to folder failed: {}", e));
|
||||||
})?;
|
})?;
|
||||||
@ -455,6 +458,7 @@ mod tests {
|
|||||||
#![allow(clippy::all)]
|
#![allow(clippy::all)]
|
||||||
use crate::{client_folder::folder_pad::FolderPad, entities::folder::FolderDelta};
|
use crate::{client_folder::folder_pad::FolderPad, entities::folder::FolderDelta};
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
use flowy_folder_data_model::revision::{
|
use flowy_folder_data_model::revision::{
|
||||||
AppRevision, FolderRevision, TrashRevision, ViewRevision, WorkspaceRevision,
|
AppRevision, FolderRevision, TrashRevision, ViewRevision, WorkspaceRevision,
|
||||||
@ -478,6 +482,20 @@ mod tests {
|
|||||||
assert_eq!(folder, folder_from_delta);
|
assert_eq!(folder, folder_from_delta);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn folder_deserialize_invalid_json_test() {
|
||||||
|
for json in vec![
|
||||||
|
// No timestamp
|
||||||
|
r#"{"workspaces":[{"id":"1","name":"first workspace","desc":"","apps":[]}],"trash":[]}"#,
|
||||||
|
// Trailing characters
|
||||||
|
r#"{"workspaces":[{"id":"1","name":"first workspace","desc":"","apps":[]}],"trash":[]}123"#,
|
||||||
|
] {
|
||||||
|
let mut deserializer = serde_json::Deserializer::from_reader(json.as_bytes());
|
||||||
|
let folder_rev = FolderRevision::deserialize(&mut deserializer).unwrap();
|
||||||
|
assert_eq!(folder_rev.workspaces.first().as_ref().unwrap().name, "first workspace");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn folder_update_workspace() {
|
fn folder_update_workspace() {
|
||||||
let (mut folder, initial_delta, workspace) = test_folder();
|
let (mut folder, initial_delta, workspace) = test_folder();
|
||||||
|
@ -23,6 +23,8 @@ impl std::ops::Deref for GridViewRevisionPad {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl GridViewRevisionPad {
|
impl GridViewRevisionPad {
|
||||||
|
// For the moment, the view_id is equal to grid_id. The grid_id represents the database id.
|
||||||
|
// A database can be referenced by multiple views.
|
||||||
pub fn new(grid_id: String, view_id: String) -> Self {
|
pub fn new(grid_id: String, view_id: String) -> Self {
|
||||||
let view = Arc::new(GridViewRevision::new(grid_id, view_id));
|
let view = Arc::new(GridViewRevision::new(grid_id, view_id));
|
||||||
let json = serde_json::to_string(&view).unwrap();
|
let json = serde_json::to_string(&view).unwrap();
|
||||||
@ -30,11 +32,14 @@ impl GridViewRevisionPad {
|
|||||||
Self { view, delta }
|
Self { view, delta }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_delta(delta: Delta) -> CollaborateResult<Self> {
|
pub fn from_delta(view_id: &str, delta: Delta) -> CollaborateResult<Self> {
|
||||||
|
if delta.is_empty() {
|
||||||
|
return Ok(GridViewRevisionPad::new(view_id.to_owned(), view_id.to_owned()));
|
||||||
|
}
|
||||||
let s = delta.content()?;
|
let s = delta.content()?;
|
||||||
let view: GridViewRevision = serde_json::from_str(&s).map_err(|e| {
|
let view: GridViewRevision = serde_json::from_str(&s).map_err(|e| {
|
||||||
let msg = format!("Deserialize delta to GridViewRevision failed: {}", e);
|
let msg = format!("Deserialize delta to GridViewRevision failed: {}", e);
|
||||||
tracing::error!("{}", s);
|
tracing::error!("parsing json: {}", s);
|
||||||
CollaborateError::internal().context(msg)
|
CollaborateError::internal().context(msg)
|
||||||
})?;
|
})?;
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
@ -43,9 +48,9 @@ impl GridViewRevisionPad {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_revisions(_grid_id: &str, revisions: Vec<Revision>) -> CollaborateResult<Self> {
|
pub fn from_revisions(view_id: &str, revisions: Vec<Revision>) -> CollaborateResult<Self> {
|
||||||
let delta: Delta = make_text_delta_from_revisions(revisions)?;
|
let delta: Delta = make_text_delta_from_revisions(revisions)?;
|
||||||
Self::from_delta(delta)
|
Self::from_delta(view_id, delta)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_groups_by_field_revs(&self, field_revs: &[Arc<FieldRevision>]) -> Option<GroupConfigurationsByFieldId> {
|
pub fn get_groups_by_field_revs(&self, field_revs: &[Arc<FieldRevision>]) -> Option<GroupConfigurationsByFieldId> {
|
||||||
|
Reference in New Issue
Block a user