123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481 |
- use crate::entities::{GroupChangesetPB, GroupPB, InsertedGroupPB};
- use crate::services::field::RowSingleCellData;
- use crate::services::group::{
- default_group_setting, GeneratedGroupContext, Group, GroupData, GroupSetting,
- };
- use collab_database::fields::Field;
- use flowy_error::{FlowyError, FlowyResult};
- use indexmap::IndexMap;
- use lib_infra::future::Fut;
- use serde::de::DeserializeOwned;
- use serde::Serialize;
- use std::collections::HashMap;
- use std::fmt::Formatter;
- use std::marker::PhantomData;
- use std::sync::Arc;
- pub trait GroupSettingReader: Send + Sync + 'static {
- fn get_group_setting(&self, view_id: &str) -> Fut<Option<Arc<GroupSetting>>>;
- fn get_configuration_cells(&self, view_id: &str, field_id: &str) -> Fut<Vec<RowSingleCellData>>;
- }
- pub trait GroupSettingWriter: Send + Sync + 'static {
- fn save_configuration(&self, view_id: &str, group_setting: GroupSetting) -> Fut<FlowyResult<()>>;
- }
- impl<T> std::fmt::Display for GroupContext<T> {
- fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
- self.groups_map.iter().for_each(|(_, group)| {
- let _ = f.write_fmt(format_args!(
- "Group:{} has {} rows \n",
- group.id,
- group.rows.len()
- ));
- });
- Ok(())
- }
- }
- /// A [GroupContext] represents as the groups memory cache
- /// Each [GenericGroupController] has its own [GroupContext], the `context` has its own configuration
- /// that is restored from the disk.
- ///
- /// The `context` contains a list of [GroupData]s and the grouping [Field]
- pub struct GroupContext<C> {
- pub view_id: String,
- /// The group configuration restored from the disk.
- ///
- /// Uses the [GroupSettingReader] to read the configuration data from disk
- setting: Arc<GroupSetting>,
- configuration_phantom: PhantomData<C>,
- /// The grouping field
- field: Arc<Field>,
- /// Cache all the groups
- groups_map: IndexMap<String, GroupData>,
- /// A reader that implement the [GroupSettingReader] trait
- ///
- #[allow(dead_code)]
- reader: Arc<dyn GroupSettingReader>,
- /// A writer that implement the [GroupSettingWriter] trait is used to save the
- /// configuration to disk
- ///
- writer: Arc<dyn GroupSettingWriter>,
- }
- impl<C> GroupContext<C>
- where
- C: Serialize + DeserializeOwned,
- {
- #[tracing::instrument(level = "trace", skip_all, err)]
- pub async fn new(
- view_id: String,
- field: Arc<Field>,
- reader: Arc<dyn GroupSettingReader>,
- writer: Arc<dyn GroupSettingWriter>,
- ) -> FlowyResult<Self> {
- let setting = match reader.get_group_setting(&view_id).await {
- None => {
- let default_configuration = default_group_setting(&field);
- writer
- .save_configuration(&view_id, default_configuration.clone())
- .await?;
- Arc::new(default_configuration)
- },
- Some(setting) => setting,
- };
- Ok(Self {
- view_id,
- field,
- groups_map: IndexMap::new(),
- reader,
- writer,
- setting,
- configuration_phantom: PhantomData,
- })
- }
- /// Returns the no `status` group
- ///
- /// We take the `id` of the `field` as the no status group id
- pub(crate) fn get_no_status_group(&self) -> Option<&GroupData> {
- self.groups_map.get(&self.field.id)
- }
- pub(crate) fn get_mut_no_status_group(&mut self) -> Option<&mut GroupData> {
- self.groups_map.get_mut(&self.field.id)
- }
- pub(crate) fn groups(&self) -> Vec<&GroupData> {
- self.groups_map.values().collect()
- }
- pub(crate) fn get_mut_group(&mut self, group_id: &str) -> Option<&mut GroupData> {
- 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, &GroupData)> {
- 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 without `No status` group
- pub(crate) fn iter_mut_status_groups(&mut self, mut each: impl FnMut(&mut GroupData)) {
- self.groups_map.iter_mut().for_each(|(_, group)| {
- if group.id != self.field.id {
- each(group);
- }
- });
- }
- pub(crate) fn iter_mut_groups(&mut self, mut each: impl FnMut(&mut GroupData)) {
- self.groups_map.iter_mut().for_each(|(_, group)| {
- each(group);
- });
- }
- #[tracing::instrument(level = "trace", skip(self), err)]
- pub(crate) fn add_new_group(&mut self, group: Group) -> FlowyResult<InsertedGroupPB> {
- let group_data = GroupData::new(
- group.id.clone(),
- self.field.id.clone(),
- group.name.clone(),
- group.id.clone(),
- );
- self.groups_map.insert(group.id.clone(), group_data);
- let (index, group_data) = self.get_group(&group.id).unwrap();
- let insert_group = InsertedGroupPB {
- group: GroupPB::from(group_data.clone()),
- index: index as i32,
- };
- self.mut_configuration(|configuration| {
- configuration.groups.push(group);
- true
- })?;
- Ok(insert_group)
- }
- #[tracing::instrument(level = "trace", skip(self))]
- pub(crate) fn delete_group(&mut self, deleted_group_id: &str) -> FlowyResult<()> {
- self.groups_map.remove(deleted_group_id);
- self.mut_configuration(|configuration| {
- configuration
- .groups
- .retain(|group| group.id != deleted_group_id);
- true
- })?;
- Ok(())
- }
- pub(crate) fn move_group(&mut self, from_id: &str, to_id: &str) -> FlowyResult<()> {
- let from_index = self.groups_map.get_index_of(from_id);
- let to_index = self.groups_map.get_index_of(to_id);
- match (from_index, to_index) {
- (Some(from_index), Some(to_index)) => {
- self.groups_map.move_index(from_index, to_index);
- self.mut_configuration(|configuration| {
- let from_index = configuration
- .groups
- .iter()
- .position(|group| group.id == from_id);
- let to_index = configuration
- .groups
- .iter()
- .position(|group| group.id == to_id);
- if let (Some(from), Some(to)) = &(from_index, to_index) {
- tracing::trace!(
- "Move group from index:{:?} to index:{:?}",
- from_index,
- to_index
- );
- let group = configuration.groups.remove(*from);
- configuration.groups.insert(*to, group);
- }
- tracing::debug!(
- "Group order: {:?} ",
- configuration
- .groups
- .iter()
- .map(|group| group.name.clone())
- .collect::<Vec<String>>()
- .join(",")
- );
- from_index.is_some() && to_index.is_some()
- })?;
- Ok(())
- },
- _ => Err(FlowyError::record_not_found().context("Moving group failed. Groups are not exist")),
- }
- }
- /// Reset the memory cache of the groups and update the group configuration
- ///
- /// # Arguments
- ///
- /// * `generated_group_configs`: the generated groups contains a list of [GeneratedGroupConfig].
- ///
- /// Each [FieldType] can implement the [GroupGenerator] trait in order to generate different
- /// groups. For example, the FieldType::Checkbox has the [CheckboxGroupGenerator] that implements
- /// the [GroupGenerator] trait.
- ///
- /// Consider the passed-in generated_group_configs as new groups, the groups in the current
- /// [GroupConfigurationRevision] as old groups. The old groups and the new groups will be merged
- /// while keeping the order of the old groups.
- ///
- #[tracing::instrument(level = "trace", skip(self, generated_group_context), err)]
- pub(crate) fn init_groups(
- &mut self,
- generated_group_context: GeneratedGroupContext,
- ) -> FlowyResult<Option<GroupChangesetPB>> {
- let GeneratedGroupContext {
- no_status_group,
- group_configs,
- } = generated_group_context;
- let mut new_groups = vec![];
- let mut filter_content_map = HashMap::new();
- group_configs.into_iter().for_each(|generate_group| {
- filter_content_map.insert(
- generate_group.group.id.clone(),
- generate_group.filter_content,
- );
- new_groups.push(generate_group.group);
- });
- let mut old_groups = self.setting.groups.clone();
- // clear all the groups if grouping by a new field
- if self.setting.field_id != self.field.id {
- old_groups.clear();
- }
- // The `all_group_revs` is the combination of the new groups and old groups
- let MergeGroupResult {
- mut all_groups,
- new_groups,
- deleted_groups,
- } = merge_groups(no_status_group, old_groups, new_groups);
- let deleted_group_ids = deleted_groups
- .into_iter()
- .map(|group_rev| group_rev.id)
- .collect::<Vec<String>>();
- self.mut_configuration(|configuration| {
- let mut is_changed = !deleted_group_ids.is_empty();
- // Remove the groups
- configuration
- .groups
- .retain(|group| !deleted_group_ids.contains(&group.id));
- // Update/Insert new groups
- for group in &mut all_groups {
- match configuration
- .groups
- .iter()
- .position(|old_group_rev| old_group_rev.id == group.id)
- {
- None => {
- // Push the group to the end of the list if it doesn't exist in the group
- configuration.groups.push(group.clone());
- is_changed = true;
- },
- Some(pos) => {
- let mut old_group = configuration.groups.get_mut(pos).unwrap();
- // Take the old group setting
- group.visible = old_group.visible;
- if !is_changed {
- is_changed = is_group_changed(group, old_group);
- }
- // Consider the the name of the `group_rev` as the newest.
- old_group.name = group.name.clone();
- },
- }
- }
- is_changed
- })?;
- // Update the memory cache of the groups
- all_groups.into_iter().for_each(|group_rev| {
- let filter_content = filter_content_map
- .get(&group_rev.id)
- .cloned()
- .unwrap_or_else(|| "".to_owned());
- let group = GroupData::new(
- group_rev.id,
- self.field.id.clone(),
- group_rev.name,
- filter_content,
- );
- self.groups_map.insert(group.id.clone(), group);
- });
- let initial_groups = new_groups
- .into_iter()
- .flat_map(|group_rev| {
- let filter_content = filter_content_map.get(&group_rev.id)?;
- let group = GroupData::new(
- group_rev.id,
- self.field.id.clone(),
- group_rev.name,
- filter_content.clone(),
- );
- Some(GroupPB::from(group))
- })
- .collect();
- let changeset = GroupChangesetPB {
- view_id: self.view_id.clone(),
- initial_groups,
- deleted_groups: deleted_group_ids,
- update_groups: vec![],
- inserted_groups: vec![],
- };
- tracing::trace!("Group changeset: {:?}", changeset);
- if changeset.is_empty() {
- Ok(None)
- } else {
- Ok(Some(changeset))
- }
- }
- #[allow(dead_code)]
- pub(crate) async fn hide_group(&mut self, group_id: &str) -> FlowyResult<()> {
- self.mut_group_rev(group_id, |group_rev| {
- group_rev.visible = false;
- })?;
- Ok(())
- }
- #[allow(dead_code)]
- pub(crate) async fn show_group(&mut self, group_id: &str) -> FlowyResult<()> {
- self.mut_group_rev(group_id, |group_rev| {
- group_rev.visible = true;
- })?;
- Ok(())
- }
- pub(crate) async fn get_all_cells(&self) -> Vec<RowSingleCellData> {
- self
- .reader
- .get_configuration_cells(&self.view_id, &self.field.id)
- .await
- }
- fn mut_configuration(
- &mut self,
- mut_configuration_fn: impl FnOnce(&mut GroupSetting) -> bool,
- ) -> FlowyResult<()> {
- let configuration = Arc::make_mut(&mut self.setting);
- let is_changed = mut_configuration_fn(configuration);
- if is_changed {
- let configuration = (*self.setting).clone();
- let writer = self.writer.clone();
- let view_id = self.view_id.clone();
- tokio::spawn(async move {
- match writer.save_configuration(&view_id, configuration).await {
- Ok(_) => {},
- Err(e) => {
- tracing::error!("Save group configuration failed: {}", e);
- },
- }
- });
- }
- Ok(())
- }
- fn mut_group_rev(
- &mut self,
- group_id: &str,
- mut_groups_fn: impl Fn(&mut Group),
- ) -> FlowyResult<()> {
- self.mut_configuration(|configuration| {
- match configuration
- .groups
- .iter_mut()
- .find(|group| group.id == group_id)
- {
- None => false,
- Some(group) => {
- mut_groups_fn(group);
- true
- },
- }
- })
- }
- }
- /// Merge the new groups into old groups while keeping the order in the old groups
- ///
- fn merge_groups(
- no_status_group: Option<Group>,
- old_groups: Vec<Group>,
- new_groups: Vec<Group>,
- ) -> MergeGroupResult {
- let mut merge_result = MergeGroupResult::new();
- // group_map is a helper map is used to filter out the new groups.
- let mut new_group_map: IndexMap<String, Group> = IndexMap::new();
- new_groups.into_iter().for_each(|group_rev| {
- new_group_map.insert(group_rev.id.clone(), group_rev);
- });
- // The group is ordered in old groups. Add them before adding the new groups
- for old in old_groups {
- if let Some(new) = new_group_map.remove(&old.id) {
- merge_result.all_groups.push(new.clone());
- } else {
- merge_result.deleted_groups.push(old);
- }
- }
- // Find out the new groups
- let new_groups = new_group_map.into_values();
- for (_, group) in new_groups.into_iter().enumerate() {
- merge_result.all_groups.push(group.clone());
- merge_result.new_groups.push(group);
- }
- // The `No status` group index is initialized to 0
- if let Some(no_status_group) = no_status_group {
- merge_result.all_groups.insert(0, no_status_group);
- }
- merge_result
- }
- fn is_group_changed(new: &Group, old: &Group) -> bool {
- if new.name != old.name {
- return true;
- }
- false
- }
- struct MergeGroupResult {
- // Contains the new groups and the updated groups
- all_groups: Vec<Group>,
- new_groups: Vec<Group>,
- deleted_groups: Vec<Group>,
- }
- impl MergeGroupResult {
- fn new() -> Self {
- Self {
- all_groups: vec![],
- new_groups: vec![],
- deleted_groups: vec![],
- }
- }
- }
|