mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
[flutter]: fix resize window bugs
This commit is contained in:
parent
45bb157aa7
commit
5f4ee57c95
@ -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;
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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));
|
||||
|
@ -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();
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
});
|
||||
|
@ -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),
|
||||
|
@ -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!);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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(())
|
||||
|
@ -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?;
|
||||
|
Loading…
Reference in New Issue
Block a user