mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: clear all cells (#4856)
* feat: clear all cells * fix: smaller dialog width * fix: clippy warning
This commit is contained in:
@ -62,6 +62,19 @@ class FieldBackendService {
|
|||||||
return DatabaseEventDeleteField(payload).send();
|
return DatabaseEventDeleteField(payload).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clear all data of all cells in a Field
|
||||||
|
static Future<FlowyResult<void, FlowyError>> clearField({
|
||||||
|
required String viewId,
|
||||||
|
required String fieldId,
|
||||||
|
}) {
|
||||||
|
final payload = ClearFieldPayloadPB(
|
||||||
|
viewId: viewId,
|
||||||
|
fieldId: fieldId,
|
||||||
|
);
|
||||||
|
|
||||||
|
return DatabaseEventClearField(payload).send();
|
||||||
|
}
|
||||||
|
|
||||||
/// Duplicate a field
|
/// Duplicate a field
|
||||||
static Future<FlowyResult<void, FlowyError>> duplicateField({
|
static Future<FlowyResult<void, FlowyError>> duplicateField({
|
||||||
required String viewId,
|
required String viewId,
|
||||||
|
@ -104,6 +104,8 @@ class _FieldEditorState extends State<FieldEditor> {
|
|||||||
VSpace(GridSize.typeOptionSeparatorHeight),
|
VSpace(GridSize.typeOptionSeparatorHeight),
|
||||||
_actionCell(FieldAction.duplicate),
|
_actionCell(FieldAction.duplicate),
|
||||||
VSpace(GridSize.typeOptionSeparatorHeight),
|
VSpace(GridSize.typeOptionSeparatorHeight),
|
||||||
|
_actionCell(FieldAction.clearData),
|
||||||
|
VSpace(GridSize.typeOptionSeparatorHeight),
|
||||||
_actionCell(FieldAction.delete),
|
_actionCell(FieldAction.delete),
|
||||||
],
|
],
|
||||||
).padding(all: 8.0),
|
).padding(all: 8.0),
|
||||||
@ -195,6 +197,7 @@ enum FieldAction {
|
|||||||
insertRight,
|
insertRight,
|
||||||
toggleVisibility,
|
toggleVisibility,
|
||||||
duplicate,
|
duplicate,
|
||||||
|
clearData,
|
||||||
delete;
|
delete;
|
||||||
|
|
||||||
Widget icon(FieldInfo fieldInfo, Color? color) {
|
Widget icon(FieldInfo fieldInfo, Color? color) {
|
||||||
@ -213,6 +216,8 @@ enum FieldAction {
|
|||||||
}
|
}
|
||||||
case FieldAction.duplicate:
|
case FieldAction.duplicate:
|
||||||
svgData = FlowySvgs.copy_s;
|
svgData = FlowySvgs.copy_s;
|
||||||
|
case FieldAction.clearData:
|
||||||
|
svgData = FlowySvgs.reload_s;
|
||||||
case FieldAction.delete:
|
case FieldAction.delete:
|
||||||
svgData = FlowySvgs.delete_s;
|
svgData = FlowySvgs.delete_s;
|
||||||
}
|
}
|
||||||
@ -241,6 +246,8 @@ enum FieldAction {
|
|||||||
}
|
}
|
||||||
case FieldAction.duplicate:
|
case FieldAction.duplicate:
|
||||||
return LocaleKeys.grid_field_duplicate.tr();
|
return LocaleKeys.grid_field_duplicate.tr();
|
||||||
|
case FieldAction.clearData:
|
||||||
|
return LocaleKeys.grid_field_clear.tr();
|
||||||
case FieldAction.delete:
|
case FieldAction.delete:
|
||||||
return LocaleKeys.grid_field_delete.tr();
|
return LocaleKeys.grid_field_delete.tr();
|
||||||
}
|
}
|
||||||
@ -273,6 +280,22 @@ enum FieldAction {
|
|||||||
fieldId: fieldInfo.id,
|
fieldId: fieldInfo.id,
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
case FieldAction.clearData:
|
||||||
|
NavigatorAlertDialog(
|
||||||
|
constraints: const BoxConstraints(
|
||||||
|
maxWidth: 250,
|
||||||
|
maxHeight: 260,
|
||||||
|
),
|
||||||
|
title: LocaleKeys.grid_field_clearFieldPromptMessage.tr(),
|
||||||
|
confirm: () {
|
||||||
|
FieldBackendService.clearField(
|
||||||
|
viewId: viewId,
|
||||||
|
fieldId: fieldInfo.id,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
).show(context);
|
||||||
|
PopoverContainer.of(context).close();
|
||||||
|
break;
|
||||||
case FieldAction.delete:
|
case FieldAction.delete:
|
||||||
NavigatorAlertDialog(
|
NavigatorAlertDialog(
|
||||||
title: LocaleKeys.grid_field_deleteFieldPromptMessage.tr(),
|
title: LocaleKeys.grid_field_deleteFieldPromptMessage.tr(),
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
import 'package:appflowy/startup/tasks/app_widget.dart';
|
import 'package:appflowy/startup/tasks/app_widget.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
@ -8,7 +10,6 @@ import 'package:flowy_infra_ui/widget/buttons/primary_button.dart';
|
|||||||
import 'package:flowy_infra_ui/widget/buttons/secondary_button.dart';
|
import 'package:flowy_infra_ui/widget/buttons/secondary_button.dart';
|
||||||
import 'package:flowy_infra_ui/widget/dialog/styled_dialogs.dart';
|
import 'package:flowy_infra_ui/widget/dialog/styled_dialogs.dart';
|
||||||
import 'package:flowy_infra_ui/widget/spacing.dart';
|
import 'package:flowy_infra_ui/widget/spacing.dart';
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
export 'package:flowy_infra_ui/widget/dialog/styled_dialogs.dart';
|
export 'package:flowy_infra_ui/widget/dialog/styled_dialogs.dart';
|
||||||
|
|
||||||
@ -114,12 +115,14 @@ class NavigatorAlertDialog extends StatefulWidget {
|
|||||||
this.cancel,
|
this.cancel,
|
||||||
this.confirm,
|
this.confirm,
|
||||||
this.hideCancelButton = false,
|
this.hideCancelButton = false,
|
||||||
|
this.constraints,
|
||||||
});
|
});
|
||||||
|
|
||||||
final String title;
|
final String title;
|
||||||
final void Function()? cancel;
|
final void Function()? cancel;
|
||||||
final void Function()? confirm;
|
final void Function()? confirm;
|
||||||
final bool hideCancelButton;
|
final bool hideCancelButton;
|
||||||
|
final BoxConstraints? constraints;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<NavigatorAlertDialog> createState() => _CreateFlowyAlertDialog();
|
State<NavigatorAlertDialog> createState() => _CreateFlowyAlertDialog();
|
||||||
@ -140,7 +143,8 @@ class _CreateFlowyAlertDialog extends State<NavigatorAlertDialog> {
|
|||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
...[
|
...[
|
||||||
ConstrainedBox(
|
ConstrainedBox(
|
||||||
constraints: const BoxConstraints(
|
constraints: widget.constraints ??
|
||||||
|
const BoxConstraints(
|
||||||
maxWidth: 400,
|
maxWidth: 400,
|
||||||
maxHeight: 260,
|
maxHeight: 260,
|
||||||
),
|
),
|
||||||
|
@ -620,6 +620,7 @@
|
|||||||
"insertRight": "Insert Right",
|
"insertRight": "Insert Right",
|
||||||
"duplicate": "Duplicate",
|
"duplicate": "Duplicate",
|
||||||
"delete": "Delete",
|
"delete": "Delete",
|
||||||
|
"clear": "Clear cells",
|
||||||
"textFieldName": "Text",
|
"textFieldName": "Text",
|
||||||
"checkboxFieldName": "Checkbox",
|
"checkboxFieldName": "Checkbox",
|
||||||
"dateFieldName": "Date",
|
"dateFieldName": "Date",
|
||||||
@ -660,6 +661,7 @@
|
|||||||
"editProperty": "Edit property",
|
"editProperty": "Edit property",
|
||||||
"newProperty": "New property",
|
"newProperty": "New property",
|
||||||
"deleteFieldPromptMessage": "Are you sure? This property will be deleted",
|
"deleteFieldPromptMessage": "Are you sure? This property will be deleted",
|
||||||
|
"clearFieldPromptMessage": "Are you sure? All cells in this column will be emptied",
|
||||||
"newColumn": "New Column",
|
"newColumn": "New Column",
|
||||||
"format": "Format",
|
"format": "Format",
|
||||||
"reminderOnDateTooltip": "This cell has a scheduled reminder",
|
"reminderOnDateTooltip": "This cell has a scheduled reminder",
|
||||||
|
@ -10,6 +10,7 @@ use strum_macros::{EnumCount as EnumCountMacro, EnumIter};
|
|||||||
|
|
||||||
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
|
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
|
||||||
use flowy_error::ErrorCode;
|
use flowy_error::ErrorCode;
|
||||||
|
use validator::Validate;
|
||||||
|
|
||||||
use crate::entities::parser::NotEmptyStr;
|
use crate::entities::parser::NotEmptyStr;
|
||||||
use crate::entities::position_entities::OrderObjectPositionPB;
|
use crate::entities::position_entities::OrderObjectPositionPB;
|
||||||
@ -620,6 +621,30 @@ impl TryInto<FieldIdParams> for DuplicateFieldPayloadPB {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default, ProtoBuf, Validate)]
|
||||||
|
pub struct ClearFieldPayloadPB {
|
||||||
|
#[pb(index = 1)]
|
||||||
|
#[validate(custom = "lib_infra::validator_fn::required_not_empty_str")]
|
||||||
|
pub field_id: String,
|
||||||
|
|
||||||
|
#[pb(index = 2)]
|
||||||
|
#[validate(custom = "lib_infra::validator_fn::required_not_empty_str")]
|
||||||
|
pub view_id: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryInto<FieldIdParams> for ClearFieldPayloadPB {
|
||||||
|
type Error = ErrorCode;
|
||||||
|
|
||||||
|
fn try_into(self) -> Result<FieldIdParams, Self::Error> {
|
||||||
|
let view_id = NotEmptyStr::parse(self.view_id).map_err(|_| ErrorCode::DatabaseIdIsEmpty)?;
|
||||||
|
let field_id = NotEmptyStr::parse(self.field_id).map_err(|_| ErrorCode::FieldIdIsEmpty)?;
|
||||||
|
Ok(FieldIdParams {
|
||||||
|
view_id: view_id.0,
|
||||||
|
field_id: field_id.0,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, ProtoBuf)]
|
#[derive(Debug, Clone, Default, ProtoBuf)]
|
||||||
pub struct DeleteFieldPayloadPB {
|
pub struct DeleteFieldPayloadPB {
|
||||||
#[pb(index = 1)]
|
#[pb(index = 1)]
|
||||||
|
@ -257,6 +257,20 @@ pub(crate) async fn delete_field_handler(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(level = "trace", skip(data, manager), err)]
|
||||||
|
pub(crate) async fn clear_field_handler(
|
||||||
|
data: AFPluginData<ClearFieldPayloadPB>,
|
||||||
|
manager: AFPluginState<Weak<DatabaseManager>>,
|
||||||
|
) -> Result<(), FlowyError> {
|
||||||
|
let manager = upgrade_manager(manager)?;
|
||||||
|
let params: FieldIdParams = data.into_inner().try_into()?;
|
||||||
|
let database_editor = manager.get_database_with_view_id(¶ms.view_id).await?;
|
||||||
|
database_editor
|
||||||
|
.clear_field(¶ms.view_id, ¶ms.field_id)
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[tracing::instrument(level = "debug", skip(data, manager), err)]
|
#[tracing::instrument(level = "debug", skip(data, manager), err)]
|
||||||
pub(crate) async fn switch_to_field_handler(
|
pub(crate) async fn switch_to_field_handler(
|
||||||
data: AFPluginData<UpdateFieldTypePayloadPB>,
|
data: AFPluginData<UpdateFieldTypePayloadPB>,
|
||||||
|
@ -27,6 +27,7 @@ pub fn init(database_manager: Weak<DatabaseManager>) -> AFPlugin {
|
|||||||
.event(DatabaseEvent::UpdateField, update_field_handler)
|
.event(DatabaseEvent::UpdateField, update_field_handler)
|
||||||
.event(DatabaseEvent::UpdateFieldTypeOption, update_field_type_option_handler)
|
.event(DatabaseEvent::UpdateFieldTypeOption, update_field_type_option_handler)
|
||||||
.event(DatabaseEvent::DeleteField, delete_field_handler)
|
.event(DatabaseEvent::DeleteField, delete_field_handler)
|
||||||
|
.event(DatabaseEvent::ClearField, clear_field_handler)
|
||||||
.event(DatabaseEvent::UpdateFieldType, switch_to_field_handler)
|
.event(DatabaseEvent::UpdateFieldType, switch_to_field_handler)
|
||||||
.event(DatabaseEvent::DuplicateField, duplicate_field_handler)
|
.event(DatabaseEvent::DuplicateField, duplicate_field_handler)
|
||||||
.event(DatabaseEvent::MoveField, move_field_handler)
|
.event(DatabaseEvent::MoveField, move_field_handler)
|
||||||
@ -161,6 +162,11 @@ pub enum DatabaseEvent {
|
|||||||
#[event(input = "DeleteFieldPayloadPB")]
|
#[event(input = "DeleteFieldPayloadPB")]
|
||||||
DeleteField = 14,
|
DeleteField = 14,
|
||||||
|
|
||||||
|
/// [ClearField] event is used to clear all Cells in a Field. [ClearFieldPayloadPB] is the context that
|
||||||
|
/// is used to clear the field from the Database.
|
||||||
|
#[event(input = "ClearFieldPayloadPB")]
|
||||||
|
ClearField = 15,
|
||||||
|
|
||||||
/// [UpdateFieldType] event is used to update the current Field's type.
|
/// [UpdateFieldType] event is used to update the current Field's type.
|
||||||
/// It will insert a new FieldTypeOptionData if the new FieldType doesn't exist before, otherwise
|
/// It will insert a new FieldTypeOptionData if the new FieldType doesn't exist before, otherwise
|
||||||
/// reuse the existing FieldTypeOptionData. You could check the [DatabaseRevisionPad] for more details.
|
/// reuse the existing FieldTypeOptionData. You could check the [DatabaseRevisionPad] for more details.
|
||||||
|
@ -361,6 +361,30 @@ impl DatabaseEditor {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn clear_field(&self, view_id: &str, field_id: &str) -> FlowyResult<()> {
|
||||||
|
let field_type: FieldType = self
|
||||||
|
.get_field(field_id)
|
||||||
|
.map(|field| field.field_type.into())
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
if matches!(
|
||||||
|
field_type,
|
||||||
|
FieldType::LastEditedTime | FieldType::CreatedTime
|
||||||
|
) {
|
||||||
|
return Err(FlowyError::new(
|
||||||
|
ErrorCode::Internal,
|
||||||
|
"Can not clear the field type of Last Edited Time or Created Time.",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let cells: Vec<RowCell> = self.get_cells_for_field(view_id, field_id).await;
|
||||||
|
for row_cell in cells {
|
||||||
|
self.clear_cell(view_id, row_cell.row_id, field_id).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Update the field type option data.
|
/// Update the field type option data.
|
||||||
/// Do nothing if the [TypeOptionData] is empty.
|
/// Do nothing if the [TypeOptionData] is empty.
|
||||||
pub async fn update_field_type_option(
|
pub async fn update_field_type_option(
|
||||||
@ -804,6 +828,37 @@ impl DatabaseEditor {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
self
|
||||||
|
.did_update_row(view_id, row_id, field_id, old_row)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn clear_cell(&self, view_id: &str, row_id: RowId, field_id: &str) -> FlowyResult<()> {
|
||||||
|
// Get the old row before updating the cell. It would be better to get the old cell
|
||||||
|
let old_row = { self.get_row_detail(view_id, &row_id) };
|
||||||
|
|
||||||
|
self.database.lock().update_row(&row_id, |row_update| {
|
||||||
|
row_update.update_cells(|cell_update| {
|
||||||
|
cell_update.clear(field_id);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
self
|
||||||
|
.did_update_row(view_id, row_id, field_id, old_row)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn did_update_row(
|
||||||
|
&self,
|
||||||
|
view_id: &str,
|
||||||
|
row_id: RowId,
|
||||||
|
field_id: &str,
|
||||||
|
old_row: Option<RowDetail>,
|
||||||
|
) {
|
||||||
let option_row = self.get_row_detail(view_id, &row_id);
|
let option_row = self.get_row_detail(view_id, &row_id);
|
||||||
if let Some(new_row_detail) = option_row {
|
if let Some(new_row_detail) = option_row {
|
||||||
for view in self.database_views.editors().await {
|
for view in self.database_views.editors().await {
|
||||||
@ -821,8 +876,6 @@ impl DatabaseEditor {
|
|||||||
self
|
self
|
||||||
.notify_update_row(view_id, row_id, vec![changeset])
|
.notify_update_row(view_id, row_id, vec![changeset])
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_auto_updated_fields_changesets(
|
pub fn get_auto_updated_fields_changesets(
|
||||||
|
Reference in New Issue
Block a user