grid_view_editor.rs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602
  1. use crate::dart_notification::{send_dart_notification, GridNotification};
  2. use crate::entities::{
  3. CreateRowParams, DeleteFilterParams, DeleteGroupParams, GridFilterConfigurationPB, GridGroupConfigurationPB,
  4. GridLayout, GridLayoutPB, GridSettingPB, GroupChangesetPB, GroupPB, GroupViewChangesetPB, InsertFilterParams,
  5. InsertGroupParams, InsertedGroupPB, InsertedRowPB, MoveGroupParams, RepeatedGridFilterConfigurationPB,
  6. RepeatedGridGroupConfigurationPB, RowPB,
  7. };
  8. use crate::services::grid_editor_task::GridServiceTaskScheduler;
  9. use crate::services::grid_view_manager::{GridViewFieldDelegate, GridViewRowDelegate};
  10. use crate::services::group::{
  11. default_group_configuration, find_group_field, make_group_controller, GroupConfigurationReader,
  12. GroupConfigurationWriter, GroupController, MoveGroupRowContext,
  13. };
  14. use bytes::Bytes;
  15. use flowy_error::{FlowyError, FlowyResult};
  16. use flowy_grid_data_model::revision::{
  17. gen_grid_filter_id, FieldRevision, FieldTypeRevision, FilterConfigurationRevision, GroupConfigurationRevision,
  18. RowChangeset, RowRevision,
  19. };
  20. use flowy_revision::{
  21. RevisionCloudService, RevisionCompress, RevisionManager, RevisionObjectDeserializer, RevisionObjectSerializer,
  22. };
  23. use flowy_sync::client_grid::{GridViewRevisionChangeset, GridViewRevisionPad};
  24. use flowy_sync::entities::revision::Revision;
  25. use flowy_sync::util::make_operations_from_revisions;
  26. use lib_infra::future::{wrap_future, AFFuture, FutureResult};
  27. use lib_ot::core::EmptyAttributes;
  28. use std::future::Future;
  29. use std::sync::Arc;
  30. use tokio::sync::RwLock;
  31. #[allow(dead_code)]
  32. pub struct GridViewRevisionEditor {
  33. user_id: String,
  34. view_id: String,
  35. pad: Arc<RwLock<GridViewRevisionPad>>,
  36. rev_manager: Arc<RevisionManager>,
  37. field_delegate: Arc<dyn GridViewFieldDelegate>,
  38. row_delegate: Arc<dyn GridViewRowDelegate>,
  39. group_controller: Arc<RwLock<Box<dyn GroupController>>>,
  40. scheduler: Arc<dyn GridServiceTaskScheduler>,
  41. }
  42. impl GridViewRevisionEditor {
  43. #[tracing::instrument(level = "trace", skip_all, err)]
  44. pub(crate) async fn new(
  45. user_id: &str,
  46. token: &str,
  47. view_id: String,
  48. field_delegate: Arc<dyn GridViewFieldDelegate>,
  49. row_delegate: Arc<dyn GridViewRowDelegate>,
  50. scheduler: Arc<dyn GridServiceTaskScheduler>,
  51. mut rev_manager: RevisionManager,
  52. ) -> FlowyResult<Self> {
  53. let cloud = Arc::new(GridViewRevisionCloudService {
  54. token: token.to_owned(),
  55. });
  56. let view_revision_pad = rev_manager.load::<GridViewRevisionSerde>(Some(cloud)).await?;
  57. let pad = Arc::new(RwLock::new(view_revision_pad));
  58. let rev_manager = Arc::new(rev_manager);
  59. let group_controller = new_group_controller(
  60. user_id.to_owned(),
  61. view_id.clone(),
  62. pad.clone(),
  63. rev_manager.clone(),
  64. field_delegate.clone(),
  65. row_delegate.clone(),
  66. )
  67. .await?;
  68. let user_id = user_id.to_owned();
  69. Ok(Self {
  70. pad,
  71. user_id,
  72. view_id,
  73. rev_manager,
  74. scheduler,
  75. field_delegate,
  76. row_delegate,
  77. group_controller: Arc::new(RwLock::new(group_controller)),
  78. })
  79. }
  80. pub(crate) async fn duplicate_view_data(&self) -> FlowyResult<String> {
  81. let json_str = self.pad.read().await.json_str()?;
  82. Ok(json_str)
  83. }
  84. pub(crate) async fn will_create_view_row(&self, row_rev: &mut RowRevision, params: &CreateRowParams) {
  85. if params.group_id.is_none() {
  86. return;
  87. }
  88. let group_id = params.group_id.as_ref().unwrap();
  89. let _ = self
  90. .mut_group_controller(|group_controller, field_rev| {
  91. group_controller.will_create_row(row_rev, &field_rev, group_id);
  92. Ok(())
  93. })
  94. .await;
  95. }
  96. pub(crate) async fn did_create_view_row(&self, row_pb: &RowPB, params: &CreateRowParams) {
  97. // Send the group notification if the current view has groups
  98. match params.group_id.as_ref() {
  99. None => {}
  100. Some(group_id) => {
  101. let index = match params.start_row_id {
  102. None => Some(0),
  103. Some(_) => None,
  104. };
  105. self.group_controller.write().await.did_create_row(row_pb, group_id);
  106. let inserted_row = InsertedRowPB {
  107. row: row_pb.clone(),
  108. index,
  109. is_new: true,
  110. };
  111. let changeset = GroupChangesetPB::insert(group_id.clone(), vec![inserted_row]);
  112. self.notify_did_update_group(changeset).await;
  113. }
  114. }
  115. }
  116. #[tracing::instrument(level = "trace", skip_all)]
  117. pub(crate) async fn did_delete_view_row(&self, row_rev: &RowRevision) {
  118. // Send the group notification if the current view has groups;
  119. let changesets = self
  120. .mut_group_controller(|group_controller, field_rev| {
  121. group_controller.did_delete_delete_row(row_rev, &field_rev)
  122. })
  123. .await;
  124. tracing::trace!("Delete row in view changeset: {:?}", changesets);
  125. if let Some(changesets) = changesets {
  126. for changeset in changesets {
  127. self.notify_did_update_group(changeset).await;
  128. }
  129. }
  130. }
  131. pub(crate) async fn did_update_view_cell(&self, row_rev: &RowRevision) {
  132. let changesets = self
  133. .mut_group_controller(|group_controller, field_rev| {
  134. group_controller.did_update_group_row(row_rev, &field_rev)
  135. })
  136. .await;
  137. if let Some(changesets) = changesets {
  138. for changeset in changesets {
  139. self.notify_did_update_group(changeset).await;
  140. }
  141. }
  142. }
  143. pub(crate) async fn move_view_group_row(
  144. &self,
  145. row_rev: &RowRevision,
  146. row_changeset: &mut RowChangeset,
  147. to_group_id: &str,
  148. to_row_id: Option<String>,
  149. ) -> Vec<GroupChangesetPB> {
  150. let changesets = self
  151. .mut_group_controller(|group_controller, field_rev| {
  152. let move_row_context = MoveGroupRowContext {
  153. row_rev,
  154. row_changeset,
  155. field_rev: field_rev.as_ref(),
  156. to_group_id,
  157. to_row_id,
  158. };
  159. let changesets = group_controller.move_group_row(move_row_context)?;
  160. Ok(changesets)
  161. })
  162. .await;
  163. changesets.unwrap_or_default()
  164. }
  165. /// Only call once after grid view editor initialized
  166. #[tracing::instrument(level = "trace", skip(self))]
  167. pub(crate) async fn load_view_groups(&self) -> FlowyResult<Vec<GroupPB>> {
  168. let groups = self.group_controller.read().await.groups();
  169. tracing::trace!("Number of groups: {}", groups.len());
  170. Ok(groups.into_iter().map(GroupPB::from).collect())
  171. }
  172. #[tracing::instrument(level = "trace", skip(self), err)]
  173. pub(crate) async fn move_view_group(&self, params: MoveGroupParams) -> FlowyResult<()> {
  174. let _ = self
  175. .group_controller
  176. .write()
  177. .await
  178. .move_group(&params.from_group_id, &params.to_group_id)?;
  179. match self.group_controller.read().await.get_group(&params.from_group_id) {
  180. None => tracing::warn!("Can not find the group with id: {}", params.from_group_id),
  181. Some((index, group)) => {
  182. let inserted_group = InsertedGroupPB {
  183. group: GroupPB::from(group),
  184. index: index as i32,
  185. };
  186. let changeset = GroupViewChangesetPB {
  187. view_id: self.view_id.clone(),
  188. inserted_groups: vec![inserted_group],
  189. deleted_groups: vec![params.from_group_id.clone()],
  190. update_groups: vec![],
  191. new_groups: vec![],
  192. };
  193. self.notify_did_update_view(changeset).await;
  194. }
  195. }
  196. Ok(())
  197. }
  198. pub(crate) async fn group_id(&self) -> String {
  199. self.group_controller.read().await.field_id().to_owned()
  200. }
  201. pub(crate) async fn get_view_setting(&self) -> GridSettingPB {
  202. let field_revs = self.field_delegate.get_field_revs().await;
  203. let grid_setting = make_grid_setting(&*self.pad.read().await, &field_revs);
  204. grid_setting
  205. }
  206. pub(crate) async fn get_view_filters(&self) -> Vec<GridFilterConfigurationPB> {
  207. let field_revs = self.field_delegate.get_field_revs().await;
  208. match self.pad.read().await.get_all_filters(&field_revs) {
  209. None => vec![],
  210. Some(filters) => filters
  211. .into_values()
  212. .flatten()
  213. .map(|filter| GridFilterConfigurationPB::from(filter.as_ref()))
  214. .collect(),
  215. }
  216. }
  217. /// Initialize new group when grouping by a new field
  218. ///
  219. pub(crate) async fn initialize_new_group(&self, params: InsertGroupParams) -> FlowyResult<()> {
  220. if let Some(field_rev) = self.field_delegate.get_field_rev(&params.field_id).await {
  221. let _ = self
  222. .modify(|pad| {
  223. let configuration = default_group_configuration(&field_rev);
  224. let changeset = pad.insert_or_update_group_configuration(
  225. &params.field_id,
  226. &params.field_type_rev,
  227. configuration,
  228. )?;
  229. Ok(changeset)
  230. })
  231. .await?;
  232. }
  233. if self.group_controller.read().await.field_id() != params.field_id {
  234. let _ = self.group_by_view_field(&params.field_id).await?;
  235. self.notify_did_update_setting().await;
  236. }
  237. Ok(())
  238. }
  239. pub(crate) async fn delete_view_group(&self, params: DeleteGroupParams) -> FlowyResult<()> {
  240. self.modify(|pad| {
  241. let changeset = pad.delete_filter(&params.field_id, &params.field_type_rev, &params.group_id)?;
  242. Ok(changeset)
  243. })
  244. .await
  245. }
  246. pub(crate) async fn insert_view_filter(&self, params: InsertFilterParams) -> FlowyResult<()> {
  247. self.modify(|pad| {
  248. let filter_rev = FilterConfigurationRevision {
  249. id: gen_grid_filter_id(),
  250. field_id: params.field_id.clone(),
  251. condition: params.condition,
  252. content: params.content,
  253. };
  254. let changeset = pad.insert_filter(&params.field_id, &params.field_type_rev, filter_rev)?;
  255. Ok(changeset)
  256. })
  257. .await
  258. }
  259. pub(crate) async fn delete_view_filter(&self, delete_filter: DeleteFilterParams) -> FlowyResult<()> {
  260. self.modify(|pad| {
  261. let changeset = pad.delete_filter(
  262. &delete_filter.field_id,
  263. &delete_filter.field_type_rev,
  264. &delete_filter.filter_id,
  265. )?;
  266. Ok(changeset)
  267. })
  268. .await
  269. }
  270. #[tracing::instrument(level = "trace", skip_all, err)]
  271. pub(crate) async fn did_update_view_field(&self, _field_id: &str) -> FlowyResult<()> {
  272. // Do nothing
  273. Ok(())
  274. }
  275. ///
  276. ///
  277. /// # Arguments
  278. ///
  279. /// * `field_id`:
  280. ///
  281. #[tracing::instrument(level = "debug", skip_all, err)]
  282. pub(crate) async fn group_by_view_field(&self, field_id: &str) -> FlowyResult<()> {
  283. if let Some(field_rev) = self.field_delegate.get_field_rev(field_id).await {
  284. let new_group_controller = new_group_controller_with_field_rev(
  285. self.user_id.clone(),
  286. self.view_id.clone(),
  287. self.pad.clone(),
  288. self.rev_manager.clone(),
  289. field_rev,
  290. self.row_delegate.clone(),
  291. )
  292. .await?;
  293. let new_groups = new_group_controller.groups().into_iter().map(GroupPB::from).collect();
  294. *self.group_controller.write().await = new_group_controller;
  295. let changeset = GroupViewChangesetPB {
  296. view_id: self.view_id.clone(),
  297. new_groups,
  298. ..Default::default()
  299. };
  300. debug_assert!(!changeset.is_empty());
  301. if !changeset.is_empty() {
  302. send_dart_notification(&changeset.view_id, GridNotification::DidGroupByNewField)
  303. .payload(changeset)
  304. .send();
  305. }
  306. }
  307. Ok(())
  308. }
  309. async fn notify_did_update_setting(&self) {
  310. let setting = self.get_view_setting().await;
  311. send_dart_notification(&self.view_id, GridNotification::DidUpdateGridSetting)
  312. .payload(setting)
  313. .send();
  314. }
  315. pub async fn notify_did_update_group(&self, changeset: GroupChangesetPB) {
  316. send_dart_notification(&changeset.group_id, GridNotification::DidUpdateGroup)
  317. .payload(changeset)
  318. .send();
  319. }
  320. async fn notify_did_update_view(&self, changeset: GroupViewChangesetPB) {
  321. send_dart_notification(&self.view_id, GridNotification::DidUpdateGroupView)
  322. .payload(changeset)
  323. .send();
  324. }
  325. async fn modify<F>(&self, f: F) -> FlowyResult<()>
  326. where
  327. F: for<'a> FnOnce(&'a mut GridViewRevisionPad) -> FlowyResult<Option<GridViewRevisionChangeset>>,
  328. {
  329. let mut write_guard = self.pad.write().await;
  330. match f(&mut *write_guard)? {
  331. None => {}
  332. Some(change) => {
  333. let _ = apply_change(&self.user_id, self.rev_manager.clone(), change).await?;
  334. }
  335. }
  336. Ok(())
  337. }
  338. async fn mut_group_controller<F, T>(&self, f: F) -> Option<T>
  339. where
  340. F: FnOnce(&mut Box<dyn GroupController>, Arc<FieldRevision>) -> FlowyResult<T>,
  341. {
  342. let group_field_id = self.group_controller.read().await.field_id().to_owned();
  343. match self.field_delegate.get_field_rev(&group_field_id).await {
  344. None => None,
  345. Some(field_rev) => {
  346. let mut write_guard = self.group_controller.write().await;
  347. f(&mut write_guard, field_rev).ok()
  348. }
  349. }
  350. }
  351. #[allow(dead_code)]
  352. async fn async_mut_group_controller<F, O, T>(&self, f: F) -> Option<T>
  353. where
  354. F: FnOnce(Arc<RwLock<Box<dyn GroupController>>>, Arc<FieldRevision>) -> O,
  355. O: Future<Output = FlowyResult<T>> + Sync + 'static,
  356. {
  357. let group_field_id = self.group_controller.read().await.field_id().to_owned();
  358. match self.field_delegate.get_field_rev(&group_field_id).await {
  359. None => None,
  360. Some(field_rev) => {
  361. let _write_guard = self.group_controller.write().await;
  362. f(self.group_controller.clone(), field_rev).await.ok()
  363. }
  364. }
  365. }
  366. }
  367. async fn new_group_controller(
  368. user_id: String,
  369. view_id: String,
  370. view_rev_pad: Arc<RwLock<GridViewRevisionPad>>,
  371. rev_manager: Arc<RevisionManager>,
  372. field_delegate: Arc<dyn GridViewFieldDelegate>,
  373. row_delegate: Arc<dyn GridViewRowDelegate>,
  374. ) -> FlowyResult<Box<dyn GroupController>> {
  375. let configuration_reader = GroupConfigurationReaderImpl(view_rev_pad.clone());
  376. let field_revs = field_delegate.get_field_revs().await;
  377. let layout = view_rev_pad.read().await.layout();
  378. // Read the group field or find a new group field
  379. let field_rev = configuration_reader
  380. .get_configuration()
  381. .await
  382. .and_then(|configuration| {
  383. field_revs
  384. .iter()
  385. .find(|field_rev| field_rev.id == configuration.field_id)
  386. .cloned()
  387. })
  388. .unwrap_or_else(|| find_group_field(&field_revs, &layout).unwrap());
  389. new_group_controller_with_field_rev(user_id, view_id, view_rev_pad, rev_manager, field_rev, row_delegate).await
  390. }
  391. /// Returns a [GroupController]
  392. ///
  393. /// # Arguments
  394. ///
  395. /// * `user_id`:
  396. /// * `view_id`:
  397. /// * `view_rev_pad`:
  398. /// * `rev_manager`:
  399. /// * `field_rev`:
  400. /// * `row_delegate`:
  401. ///
  402. async fn new_group_controller_with_field_rev(
  403. user_id: String,
  404. view_id: String,
  405. view_rev_pad: Arc<RwLock<GridViewRevisionPad>>,
  406. rev_manager: Arc<RevisionManager>,
  407. field_rev: Arc<FieldRevision>,
  408. row_delegate: Arc<dyn GridViewRowDelegate>,
  409. ) -> FlowyResult<Box<dyn GroupController>> {
  410. let configuration_reader = GroupConfigurationReaderImpl(view_rev_pad.clone());
  411. let configuration_writer = GroupConfigurationWriterImpl {
  412. user_id,
  413. rev_manager,
  414. view_pad: view_rev_pad,
  415. };
  416. let row_revs = row_delegate.gv_row_revs().await;
  417. make_group_controller(view_id, field_rev, row_revs, configuration_reader, configuration_writer).await
  418. }
  419. async fn apply_change(
  420. user_id: &str,
  421. rev_manager: Arc<RevisionManager>,
  422. change: GridViewRevisionChangeset,
  423. ) -> FlowyResult<()> {
  424. let GridViewRevisionChangeset { operations: delta, md5 } = change;
  425. let (base_rev_id, rev_id) = rev_manager.next_rev_id_pair();
  426. let delta_data = delta.json_bytes();
  427. let revision = Revision::new(&rev_manager.object_id, base_rev_id, rev_id, delta_data, user_id, md5);
  428. let _ = rev_manager.add_local_revision(&revision).await?;
  429. Ok(())
  430. }
  431. struct GridViewRevisionCloudService {
  432. #[allow(dead_code)]
  433. token: String,
  434. }
  435. impl RevisionCloudService for GridViewRevisionCloudService {
  436. #[tracing::instrument(level = "trace", skip(self))]
  437. fn fetch_object(&self, _user_id: &str, _object_id: &str) -> FutureResult<Vec<Revision>, FlowyError> {
  438. FutureResult::new(async move { Ok(vec![]) })
  439. }
  440. }
  441. pub struct GridViewRevisionSerde();
  442. impl RevisionObjectDeserializer for GridViewRevisionSerde {
  443. type Output = GridViewRevisionPad;
  444. fn deserialize_revisions(object_id: &str, revisions: Vec<Revision>) -> FlowyResult<Self::Output> {
  445. let pad = GridViewRevisionPad::from_revisions(object_id, revisions)?;
  446. Ok(pad)
  447. }
  448. }
  449. impl RevisionObjectSerializer for GridViewRevisionSerde {
  450. fn serialize_revisions(revisions: Vec<Revision>) -> FlowyResult<Bytes> {
  451. let operations = make_operations_from_revisions::<EmptyAttributes>(revisions)?;
  452. Ok(operations.json_bytes())
  453. }
  454. }
  455. pub struct GridViewRevisionCompactor();
  456. impl RevisionCompress for GridViewRevisionCompactor {
  457. fn serialize_revisions(&self, revisions: Vec<Revision>) -> FlowyResult<Bytes> {
  458. GridViewRevisionSerde::serialize_revisions(revisions)
  459. }
  460. }
  461. struct GroupConfigurationReaderImpl(Arc<RwLock<GridViewRevisionPad>>);
  462. impl GroupConfigurationReader for GroupConfigurationReaderImpl {
  463. fn get_configuration(&self) -> AFFuture<Option<Arc<GroupConfigurationRevision>>> {
  464. let view_pad = self.0.clone();
  465. wrap_future(async move {
  466. let mut groups = view_pad.read().await.get_all_groups();
  467. if groups.is_empty() {
  468. None
  469. } else {
  470. debug_assert_eq!(groups.len(), 1);
  471. Some(groups.pop().unwrap())
  472. }
  473. })
  474. }
  475. }
  476. struct GroupConfigurationWriterImpl {
  477. user_id: String,
  478. rev_manager: Arc<RevisionManager>,
  479. view_pad: Arc<RwLock<GridViewRevisionPad>>,
  480. }
  481. impl GroupConfigurationWriter for GroupConfigurationWriterImpl {
  482. fn save_configuration(
  483. &self,
  484. field_id: &str,
  485. field_type: FieldTypeRevision,
  486. group_configuration: GroupConfigurationRevision,
  487. ) -> AFFuture<FlowyResult<()>> {
  488. let user_id = self.user_id.clone();
  489. let rev_manager = self.rev_manager.clone();
  490. let view_pad = self.view_pad.clone();
  491. let field_id = field_id.to_owned();
  492. wrap_future(async move {
  493. let changeset = view_pad.write().await.insert_or_update_group_configuration(
  494. &field_id,
  495. &field_type,
  496. group_configuration,
  497. )?;
  498. if let Some(changeset) = changeset {
  499. let _ = apply_change(&user_id, rev_manager, changeset).await?;
  500. }
  501. Ok(())
  502. })
  503. }
  504. }
  505. pub fn make_grid_setting(view_pad: &GridViewRevisionPad, field_revs: &[Arc<FieldRevision>]) -> GridSettingPB {
  506. let layout_type: GridLayout = view_pad.layout.clone().into();
  507. let filter_configurations = view_pad
  508. .get_all_filters(field_revs)
  509. .map(|filters_by_field_id| {
  510. filters_by_field_id
  511. .into_iter()
  512. .flat_map(|(_, v)| {
  513. let repeated_filter: RepeatedGridFilterConfigurationPB = v.into();
  514. repeated_filter.items
  515. })
  516. .collect::<Vec<GridFilterConfigurationPB>>()
  517. })
  518. .unwrap_or_default();
  519. let group_configurations = view_pad
  520. .get_groups_by_field_revs(field_revs)
  521. .map(|groups_by_field_id| {
  522. groups_by_field_id
  523. .into_iter()
  524. .flat_map(|(_, v)| {
  525. let repeated_group: RepeatedGridGroupConfigurationPB = v.into();
  526. repeated_group.items
  527. })
  528. .collect::<Vec<GridGroupConfigurationPB>>()
  529. })
  530. .unwrap_or_default();
  531. GridSettingPB {
  532. layouts: GridLayoutPB::all(),
  533. layout_type,
  534. filter_configurations: filter_configurations.into(),
  535. group_configurations: group_configurations.into(),
  536. }
  537. }
  538. #[cfg(test)]
  539. mod tests {
  540. use lib_ot::core::Delta;
  541. #[test]
  542. fn test() {
  543. let s1 = r#"[{"insert":"{\"view_id\":\"fTURELffPr\",\"grid_id\":\"fTURELffPr\",\"layout\":0,\"filters\":[],\"groups\":[]}"}]"#;
  544. let _delta_1 = Delta::from_json(s1).unwrap();
  545. let s2 = r#"[{"retain":195},{"insert":"{\\\"group_id\\\":\\\"wD9i\\\",\\\"visible\\\":true},{\\\"group_id\\\":\\\"xZtv\\\",\\\"visible\\\":true},{\\\"group_id\\\":\\\"tFV2\\\",\\\"visible\\\":true}"},{"retain":10}]"#;
  546. let _delta_2 = Delta::from_json(s2).unwrap();
  547. }
  548. }