[flutter]: fix resize window bugs

This commit is contained in:
appflowy 2021-11-11 19:56:30 +08:00
parent 45bb157aa7
commit 5f4ee57c95
15 changed files with 180 additions and 133 deletions

View File

@ -62,7 +62,7 @@ class DocBloc extends Bloc<DocEvent, DocState> {
await _subscription?.cancel();
}
docManager.closeDoc();
// docManager.closeDoc();
return super.close();
}
@ -122,6 +122,10 @@ class DocBloc extends Bloc<DocEvent, DocState> {
}
Document _decodeJsonToDocument(String data) {
// String d = r'''
// [{"insert":"\n👋 Welcome to AppFlowy!\n"},{"insert":"\n","attributes":{"header":1}},{"insert":"Here are the basics\n"},{"insert":"lick anywhere and just start typing\n"},{"insert":"H","attributes":{"list":"unchecked"}},{"insert":"ighlight any text, and use the menu at the bottom to style your writing however you like\n"},{"insert":"C","attributes":{"list":"unchecked"}},{"insert":"lick + New Page button at the bottom of your sidebar to add a new page\n"},{"insert":"C","attributes":{"list":"unchecked"}},{"insert":"lick the + next to any page title in the sidebar to quickly add a new subpage\n"},{"insert":"\n","attributes":{"list":"unchecked"}},{"insert":"Have a question? \n"},{"insert":"lick the '?' at the bottom right for help and support.\n\nLike AppFlowy? Follow us:\n"},{"insert":"G","attributes":{"header":2}},{"insert":"ithub: https://github.com/AppFlowy-IO/appflowy\n"},{"insert":"T","attributes":{"blockquote":true}},{"insert":"witter: https://twitter.com/appflowy\n"},{"insert":"N","attributes":{"blockquote":true}},{"insert":"ewsletter: https://www.appflowy.io/blog\n"},{"retain":1,"attributes":{"blockquote":true}},{"insert":"\n"}]
// ''';
final json = jsonDecode(data);
final document = Document.fromJson(json);
return document;

View File

@ -56,6 +56,10 @@ class HomeStackNotifier extends ChangeNotifier {
HomeStackNotifier({HomeStackContext? context}) : stackContext = context ?? BlankStackContext();
set context(HomeStackContext context) {
if (stackContext.identifier == context.identifier) {
return;
}
stackContext.isUpdated.removeListener(notifyListeners);
stackContext.dispose();

View File

@ -1,7 +1,6 @@
import 'package:app_flowy/workspace/application/home/home_bloc.dart';
import 'package:app_flowy/workspace/application/home/home_listen_bloc.dart';
import 'package:app_flowy/workspace/domain/page_stack/page_stack.dart';
import 'package:app_flowy/workspace/presentation/stack_page/doc/doc_stack_page.dart';
import 'package:app_flowy/workspace/presentation/stack_page/home_stack.dart';
import 'package:app_flowy/workspace/presentation/widgets/float_bubble/question_bubble.dart';
import 'package:app_flowy/workspace/presentation/widgets/prelude.dart';
@ -18,18 +17,25 @@ import 'package:app_flowy/workspace/domain/view_ext.dart';
import 'home_layout.dart';
class HomeScreen extends StatelessWidget {
class HomeScreen extends StatefulWidget {
static GlobalKey<ScaffoldState> scaffoldKey = GlobalKey();
final UserProfile user;
final CurrentWorkspaceSetting workspaceSetting;
const HomeScreen(this.user, this.workspaceSetting, {Key? key}) : super(key: key);
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
View? initialView;
@override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider<HomeListenBloc>(
create: (context) => getIt<HomeListenBloc>(param1: user)..add(const HomeListenEvent.started()),
create: (context) => getIt<HomeListenBloc>(param1: widget.user)..add(const HomeListenEvent.started()),
),
BlocProvider<HomeBloc>(create: (context) => getIt<HomeBloc>()),
],
@ -94,22 +100,15 @@ class HomeScreen extends StatelessWidget {
homeBloc.add(HomeEvent.forceCollapse(isCollapsed));
});
final pageContext = PublishNotifier<HomeStackContext>();
pageContext.addPublishListener((pageContext) {
getIt<HomeStackManager>().switchStack(pageContext);
});
HomeStackContext? initialStackContext;
if (workspaceSetting.hasLatestView()) {
initialStackContext = workspaceSetting.latestView.stackContext();
if (initialView == null && widget.workspaceSetting.hasLatestView()) {
initialView = widget.workspaceSetting.latestView;
getIt<HomeStackManager>().switchStack(initialView!.stackContext());
}
HomeMenu homeMenu = HomeMenu(
user: user,
workspaceSetting: workspaceSetting,
user: widget.user,
workspaceSetting: widget.workspaceSetting,
collapsedNotifier: collapasedNotifier,
pageContext: pageContext,
initialStackContext: initialStackContext,
);
return FocusTraversalGroup(child: RepaintBoundary(child: homeMenu));

View File

@ -15,7 +15,7 @@ import 'widget/toolbar/tool_bar.dart';
class DocPage extends StatefulWidget {
final View view;
const DocPage({Key? key, required this.view}) : super(key: key);
DocPage({Key? key, required this.view}) : super(key: ValueKey(view.id));
@override
State<DocPage> createState() => _DocPageState();

View File

@ -21,10 +21,10 @@ import 'package:clipboard/clipboard.dart';
import 'doc_page.dart';
class DocStackContext extends HomeStackContext<String, ShareActionWrapper> {
class DocStackContext extends HomeStackContext<int, ShareActionWrapper> {
View _view;
late IViewListener _listener;
final ValueNotifier<String> _isUpdated = ValueNotifier<String>("");
final ValueNotifier<int> _isUpdated = ValueNotifier<int>(0);
DocStackContext({required View view, Key? key}) : _view = view {
_listener = getIt<IViewListener>(param1: view);
@ -32,7 +32,7 @@ class DocStackContext extends HomeStackContext<String, ShareActionWrapper> {
result.fold(
(newView) {
_view = newView;
_isUpdated.value = _view.name;
_isUpdated.value = _view.hashCode;
},
(error) {},
);
@ -59,7 +59,7 @@ class DocStackContext extends HomeStackContext<String, ShareActionWrapper> {
List<NavigationItem> get navigationItems => _makeNavigationItems();
@override
ValueNotifier<String> get isUpdated => _isUpdated;
ValueNotifier<int> get isUpdated => _isUpdated;
// List<NavigationItem> get navigationItems => naviStacks.map((stack) {
// return NavigationItemImpl(context: stack);
@ -111,6 +111,7 @@ class _DocLeftBarItemState extends State<DocLeftBarItem> {
final theme = context.watch<AppTheme>();
return IntrinsicWidth(
key: ValueKey(_controller.text),
child: TextField(
controller: _controller,
focusNode: _focusNode,

View File

@ -42,25 +42,17 @@ import 'widget/menu_trash.dart';
//
class HomeMenu extends StatelessWidget {
final PublishNotifier<HomeStackContext> _pageContext;
final PublishNotifier<bool> _collapsedNotifier;
final UserProfile user;
final CurrentWorkspaceSetting workspaceSetting;
HomeMenu({
const HomeMenu({
Key? key,
required this.user,
required this.workspaceSetting,
required PublishNotifier<bool> collapsedNotifier,
required PublishNotifier<HomeStackContext> pageContext,
HomeStackContext? initialStackContext,
}) : _pageContext = pageContext,
_collapsedNotifier = collapsedNotifier,
super(key: key) {
if (initialStackContext != null) {
pageContext.value = initialStackContext;
}
}
}) : _collapsedNotifier = collapsedNotifier,
super(key: key);
@override
Widget build(BuildContext context) {
@ -78,7 +70,9 @@ class HomeMenu extends StatelessWidget {
listeners: [
BlocListener<MenuBloc, MenuState>(
listenWhen: (p, c) => p.context != c.context,
listener: (context, state) => _pageContext.value = state.context,
listener: (context, state) {
getIt<HomeStackManager>().switchStack(state.context);
},
),
BlocListener<MenuBloc, MenuState>(
listenWhen: (p, c) => p.isCollapse != c.isCollapse,
@ -168,40 +162,40 @@ class HomeMenu extends StatelessWidget {
}
class MenuSharedState extends ChangeNotifier {
View? _view;
View? _forcedOpenView;
PublishNotifier<View> forcedOpenView = PublishNotifier();
ValueNotifier<View?> selectedView = ValueNotifier<View?>(null);
MenuSharedState({View? view}) : _view = view;
MenuSharedState({View? view}) {
if (view != null) {
selectedView.value = view;
}
void addForcedOpenViewListener(void Function(View) callback) {
super.addListener(() {
if (_forcedOpenView != null) {
callback(_forcedOpenView!);
}
forcedOpenView.addPublishListener((view) {
selectedView.value = view;
});
}
void addSelectedViewListener(void Function(View?) callback) {
super.addListener(() {
callback(_view);
});
}
// void addForcedOpenViewListener(void Function(View) callback) {
// super.addListener(() {
// if (_forcedOpenView != null) {
// callback(_forcedOpenView!);
// }
// });
// }
set forcedOpenView(View? view) {
if (_forcedOpenView != view) {
_forcedOpenView = view;
selectedView = view;
notifyListeners();
}
_forcedOpenView = null;
}
// void addSelectedViewListener(void Function(View?) callback) {
// super.addListener(() {
// callback(_view);
// });
// }
set selectedView(View? view) {
if (_view != view) {
_view = view;
notifyListeners();
}
}
// set forcedOpenView(View? view) {
// if (_forcedOpenView != view) {
// _forcedOpenView = view;
// selectedView = view;
// notifyListeners();
// }
// }
View? get selectedView => _view;
}

View File

@ -38,11 +38,11 @@ class _MenuAppState extends State<MenuApp> {
selector: (state) {
final menuState = Provider.of<MenuSharedState>(context, listen: false);
if (state.latestCreatedView != null) {
menuState.forcedOpenView = state.latestCreatedView;
menuState.forcedOpenView.value = state.latestCreatedView!;
}
notifier.views = state.views;
notifier.selectedView = menuState.selectedView;
notifier.selectedView = menuState.selectedView.value;
return notifier;
},
builder: (context, notifier) => ChangeNotifierProvider.value(
@ -88,7 +88,7 @@ class _MenuAppState extends State<MenuApp> {
return MultiProvider(
providers: [ChangeNotifierProvider.value(value: notifier)],
child: Consumer(builder: (context, AppDataNotifier notifier, child) {
return const ViewSection();
return ViewSection(appData: notifier);
}),
);
}
@ -112,13 +112,16 @@ class MenuAppSizes {
class AppDataNotifier extends ChangeNotifier {
List<View> _views = [];
View? _selectedView;
ExpandableController expandController = ExpandableController(initialExpanded: false);
AppDataNotifier();
set selectedView(View? selectedView) {
if (selectedView != null) {
final isExpanded = _views.contains(selectedView);
set selectedView(View? view) {
_selectedView = view;
if (view != null) {
final isExpanded = _views.contains(view);
if (expandController.expanded == false && expandController.expanded != isExpanded) {
// Workaround: Delay 150 milliseconds to make the smooth animation while expanding
Future.delayed(const Duration(milliseconds: 150), () {
@ -128,6 +131,8 @@ class AppDataNotifier extends ChangeNotifier {
}
}
View? get selectedView => _selectedView;
set views(List<View>? views) {
if (views == null) {
if (_views.isNotEmpty) {

View File

@ -13,20 +13,18 @@ import 'item.dart';
import 'package:async/async.dart';
class ViewSection extends StatelessWidget {
const ViewSection({Key? key}) : super(key: key);
final AppDataNotifier appData;
const ViewSection({Key? key, required this.appData}) : super(key: key);
@override
Widget build(BuildContext context) {
// The ViewSectionNotifier will be updated after AppDataNotifier changed passed by parent widget
return ChangeNotifierProxyProvider<AppDataNotifier, ViewSectionNotifier>(
create: (_) {
final views = Provider.of<AppDataNotifier>(context, listen: false).views;
final menuState = Provider.of<MenuSharedState>(context, listen: false);
return ViewSectionNotifier(
context: context,
views: views,
initialSelectedView: menuState.selectedView,
views: appData.views,
initialSelectedView: appData.selectedView,
);
},
update: (_, notifier, controller) => controller!..update(notifier),
@ -47,7 +45,7 @@ class ViewSection extends StatelessWidget {
isSelected: _isViewSelected(context, view.id),
onSelected: (view) {
context.read<ViewSectionNotifier>().selectedView = view;
Provider.of<MenuSharedState>(context, listen: false).selectedView = view;
Provider.of<MenuSharedState>(context, listen: false).selectedView.value = view;
},
).padding(vertical: 4),
)
@ -80,14 +78,14 @@ class ViewSectionNotifier with ChangeNotifier {
_selectedView = initialSelectedView {
final menuSharedState = Provider.of<MenuSharedState>(context, listen: false);
// The forcedOpenView will be the view after creating the new view
menuSharedState.addForcedOpenViewListener((forcedOpenView) {
menuSharedState.forcedOpenView.addPublishListener((forcedOpenView) {
selectedView = forcedOpenView;
});
menuSharedState.addSelectedViewListener((currentSelectedView) {
menuSharedState.selectedView.addListener(() {
// Cancel the selected view of this section by setting the selectedView to null
// that will notify the listener to refresh the ViewSection UI
if (currentSelectedView != _selectedView) {
if (menuSharedState.selectedView.value != _selectedView) {
selectedView = null;
}
});

View File

@ -18,7 +18,7 @@ class MenuTrash extends StatelessWidget {
height: 26,
child: InkWell(
onTap: () {
Provider.of<MenuSharedState>(context, listen: false).selectedView = null;
Provider.of<MenuSharedState>(context, listen: false).selectedView.value = null;
getIt<HomeStackManager>().switchStack(TrashStackContext());
},
child: _render(context),

View File

@ -4,17 +4,21 @@ class PublishNotifier<T> extends ChangeNotifier {
T? _value;
set value(T newValue) {
_value = newValue;
notifyListeners();
if (_value != newValue) {
_value = newValue;
notifyListeners();
}
}
T? get currentValue => _value;
void addPublishListener(void Function(T) callback) {
super.addListener(() {
if (_value != null) {
callback(_value!);
}
});
super.addListener(
() {
if (_value != null) {
callback(_value!);
}
},
);
}
}

View File

@ -226,7 +226,8 @@ impl ClientEditDoc {
let _ = rx.await.map_err(internal_error)??;
// update rev id
self.rev_manager.set_rev_id(server_rev_id.clone().into());
self.rev_manager
.update_rev_id_counter_value(server_rev_id.clone().into());
let (local_base_rev_id, local_rev_id) = self.rev_manager.next_rev_id();
// save the revision

View File

@ -37,7 +37,7 @@ impl RevisionManager {
pub async fn load_document(&mut self) -> DocResult<Delta> {
let doc = self.rev_store.fetch_document().await?;
self.set_rev_id(doc.rev_id);
self.update_rev_id_counter_value(doc.rev_id);
Ok(doc.delta()?)
}
@ -59,7 +59,7 @@ impl RevisionManager {
(cur, next)
}
pub fn set_rev_id(&self, rev_id: i64) { self.rev_id_counter.set(rev_id); }
pub fn update_rev_id_counter_value(&self, rev_id: i64) { self.rev_id_counter.set(rev_id); }
pub async fn mk_revisions(&self, range: RevisionRange) -> Result<Revision, DocError> {
debug_assert!(&range.doc_id == &self.doc_id);

View File

@ -6,9 +6,9 @@ use crate::{
};
use async_stream::stream;
use dashmap::DashMap;
use flowy_database::ConnectionPool;
use flowy_database::{ConnectionPool, SqliteConnection};
use flowy_infra::future::ResultFuture;
use flowy_ot::core::{Delta, OperationTransformable};
use flowy_ot::core::{Delta, Operation, OperationTransformable};
use futures::stream::StreamExt;
use std::{collections::VecDeque, sync::Arc, time::Duration};
use tokio::{
@ -22,7 +22,7 @@ pub struct RevisionStore {
revs_map: Arc<DashMap<i64, RevisionRecord>>,
pending_tx: PendingSender,
pending_revs: Arc<RwLock<VecDeque<PendingRevId>>>,
defer_save_oper: RwLock<Option<JoinHandle<()>>>,
defer_save: RwLock<Option<JoinHandle<()>>>,
server: Arc<dyn RevisionServer>,
}
@ -45,7 +45,7 @@ impl RevisionStore {
revs_map,
pending_revs,
pending_tx,
defer_save_oper: RwLock::new(None),
defer_save: RwLock::new(None),
server,
});
@ -94,7 +94,7 @@ impl RevisionStore {
}
async fn save_revisions(&self) {
if let Some(handler) = self.defer_save_oper.write().await.take() {
if let Some(handler) = self.defer_save.write().await.take() {
handler.abort();
}
@ -105,7 +105,7 @@ impl RevisionStore {
let revs_map = self.revs_map.clone();
let persistence = self.persistence.clone();
*self.defer_save_oper.write().await = Some(tokio::spawn(async move {
*self.defer_save.write().await = Some(tokio::spawn(async move {
tokio::time::sleep(Duration::from_millis(300)).await;
let ids = revs_map.iter().map(|kv| kv.key().clone()).collect::<Vec<i64>>();
let revisions_state = revs_map
@ -182,7 +182,7 @@ async fn fetch_from_local(doc_id: &str, persistence: Arc<Persistence>) -> DocRes
let doc_id = doc_id.to_owned();
spawn_blocking(move || {
let conn = &*persistence.pool.get().map_err(internal_error)?;
let revisions = persistence.rev_sql.read_rev_tables(&doc_id, None, conn)?;
let revisions = persistence.rev_sql.read_rev_tables(&doc_id, conn)?;
if revisions.is_empty() {
return Err(DocError::record_not_found().context("Local doesn't have this document"));
}
@ -190,7 +190,16 @@ async fn fetch_from_local(doc_id: &str, persistence: Arc<Persistence>) -> DocRes
let base_rev_id: RevId = revisions.last().unwrap().base_rev_id.into();
let rev_id: RevId = revisions.last().unwrap().rev_id.into();
let mut delta = Delta::new();
for revision in revisions {
let mut pre_rev_id = 0;
for (index, revision) in revisions.into_iter().enumerate() {
if cfg!(debug_assertions) {
if index == 0 {
pre_rev_id = revision.rev_id;
} else {
validate_rev_id(pre_rev_id, revision.rev_id);
}
}
match Delta::from_bytes(revision.delta_data) {
Ok(local_delta) => {
delta = delta.compose(&local_delta)?;
@ -200,13 +209,17 @@ async fn fetch_from_local(doc_id: &str, persistence: Arc<Persistence>) -> DocRes
},
}
}
if cfg!(debug_assertions) {
validate_delta(&doc_id, persistence, conn, &delta);
}
match delta.ops.last() {
None => {},
Some(op) => {
let data = op.get_data();
if !data.ends_with("\n") {
log::error!("The op must end with newline");
log::debug!("Invalid delta: {}", delta.to_json());
delta.ops.push(Operation::Insert("\n".into()))
}
},
}
@ -222,6 +235,37 @@ async fn fetch_from_local(doc_id: &str, persistence: Arc<Persistence>) -> DocRes
.map_err(internal_error)?
}
#[cfg(debug_assertions)]
fn validate_rev_id(current: i64, next: i64) {
if current >= next {
log::error!("The next revision id should be greater than the previous");
}
}
#[cfg(debug_assertions)]
fn validate_delta(doc_id: &str, persistence: Arc<Persistence>, conn: &SqliteConnection, delta: &Delta) {
if delta.ops.last().is_none() {
return;
}
let data = delta.ops.last().as_ref().unwrap().get_data();
if !data.ends_with("\n") {
log::error!("The op must end with newline");
let result = || {
let revisions = persistence.rev_sql.read_rev_tables(&doc_id, conn)?;
for revision in revisions {
let delta = Delta::from_bytes(revision.delta_data)?;
log::error!("Invalid revision: {}:{}", revision.rev_id, delta.to_json());
}
Ok::<(), DocError>(())
};
match result() {
Ok(_) => {},
Err(e) => log::error!("{}", e),
}
}
}
// fn update_revisions(&self) {
// let rev_ids = self
// .revs

View File

@ -4,12 +4,7 @@ use crate::{
sql_tables::{doc::RevTable, RevChangeset, RevState, RevTableType},
};
use diesel::update;
use flowy_database::{
insert_or_ignore_into,
prelude::*,
schema::rev_table::{columns::*, dsl, dsl::doc_id},
SqliteConnection,
};
use flowy_database::{insert_or_ignore_into, prelude::*, schema::rev_table::dsl, SqliteConnection};
pub struct RevTableSql {}
@ -25,12 +20,12 @@ impl RevTableSql {
.map(|(revision, new_state)| {
let rev_ty: RevTableType = revision.ty.into();
(
doc_id.eq(revision.doc_id),
base_rev_id.eq(revision.base_rev_id),
rev_id.eq(revision.rev_id),
data.eq(revision.delta_data),
state.eq(new_state),
ty.eq(rev_ty),
dsl::doc_id.eq(revision.doc_id),
dsl::base_rev_id.eq(revision.base_rev_id),
dsl::rev_id.eq(revision.rev_id),
dsl::data.eq(revision.delta_data),
dsl::state.eq(new_state),
dsl::ty.eq(rev_ty),
)
})
.collect::<Vec<_>>();
@ -42,24 +37,18 @@ impl RevTableSql {
#[allow(dead_code)]
pub(crate) fn update_rev_table(&self, changeset: RevChangeset, conn: &SqliteConnection) -> Result<(), DocError> {
let filter = dsl::rev_table
.filter(rev_id.eq(changeset.rev_id.as_ref()))
.filter(doc_id.eq(changeset.doc_id));
let _ = update(filter).set(state.eq(changeset.state)).execute(conn)?;
.filter(dsl::rev_id.eq(changeset.rev_id.as_ref()))
.filter(dsl::doc_id.eq(changeset.doc_id));
let _ = update(filter).set(dsl::state.eq(changeset.state)).execute(conn)?;
tracing::debug!("Set {} to {:?}", changeset.rev_id, changeset.state);
Ok(())
}
pub(crate) fn read_rev_tables(
&self,
did: &str,
rid: Option<i64>,
conn: &SqliteConnection,
) -> Result<Vec<Revision>, DocError> {
let mut filter = dsl::rev_table.filter(doc_id.eq(did)).order(rev_id.asc()).into_boxed();
if let Some(rid) = rid {
filter = filter.filter(rev_id.eq(rid))
}
pub(crate) fn read_rev_tables(&self, doc_id: &str, conn: &SqliteConnection) -> Result<Vec<Revision>, DocError> {
let filter = dsl::rev_table
.filter(dsl::doc_id.eq(doc_id))
.order(dsl::rev_id.asc())
.into_boxed();
let rev_tables = filter.load::<RevTable>(conn)?;
let revisions = rev_tables
.into_iter()
@ -70,11 +59,13 @@ impl RevTableSql {
pub(crate) fn read_rev_table(
&self,
did: &str,
rid: &i64,
doc_id: &str,
revision_id: &i64,
conn: &SqliteConnection,
) -> Result<Option<Revision>, DocError> {
let filter = dsl::rev_table.filter(doc_id.eq(did)).filter(rev_id.eq(rid));
let filter = dsl::rev_table
.filter(dsl::doc_id.eq(doc_id))
.filter(dsl::rev_id.eq(revision_id));
let result = filter.first::<RevTable>(conn);
if Err(diesel::NotFound) == result {
@ -91,10 +82,10 @@ impl RevTableSql {
conn: &SqliteConnection,
) -> Result<Vec<Revision>, DocError> {
let rev_tables = dsl::rev_table
.filter(rev_id.ge(range.start))
.filter(rev_id.le(range.end))
.filter(doc_id.eq(doc_id_s))
.order(rev_id.asc())
.filter(dsl::rev_id.ge(range.start))
.filter(dsl::rev_id.le(range.end))
.filter(dsl::doc_id.eq(doc_id_s))
.order(dsl::rev_id.asc())
.load::<RevTable>(conn)?;
let revisions = rev_tables
@ -111,7 +102,9 @@ impl RevTableSql {
rev_id_s: i64,
conn: &SqliteConnection,
) -> Result<(), DocError> {
let filter = dsl::rev_table.filter(rev_id.eq(rev_id_s)).filter(doc_id.eq(doc_id_s));
let filter = dsl::rev_table
.filter(dsl::rev_id.eq(rev_id_s))
.filter(dsl::doc_id.eq(doc_id_s));
let affected_row = diesel::delete(filter).execute(conn)?;
debug_assert_eq!(affected_row, 1);
Ok(())

View File

@ -128,7 +128,7 @@ impl ViewController {
pub(crate) async fn delete_view(&self, params: DocIdentifier) -> Result<(), WorkspaceError> {
if let Some(view_id) = KV::get_str(LATEST_VIEW_ID) {
if view_id == params.doc_id {
KV::remove(LATEST_VIEW_ID);
let _ = KV::remove(LATEST_VIEW_ID);
}
}
let _ = self.document.close(params).await?;