Merge pull request #467 from AppFlowy-IO/fix_reorder_animation

Fix: reorder animation
This commit is contained in:
Nathan.fooo 2022-04-28 17:32:33 +08:00 committed by GitHub
commit f2cd3846f1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 278 additions and 1374 deletions

View File

@ -161,11 +161,12 @@ class AppViewDataContext extends ChangeNotifier {
final String appId;
final ValueNotifier<List<View>> _viewsNotifier = ValueNotifier([]);
final ValueNotifier<View?> _selectedViewNotifier = ValueNotifier(null);
VoidCallback? _menuSharedStateListener;
ExpandableController expandController = ExpandableController(initialExpanded: false);
AppViewDataContext({required this.appId}) {
_setLatestView(getIt<MenuSharedState>().latestOpenView);
getIt<MenuSharedState>().addLatestViewListener((view) {
_menuSharedStateListener = getIt<MenuSharedState>().addLatestViewListener((view) {
_setLatestView(view);
});
}
@ -234,4 +235,12 @@ class AppViewDataContext extends ChangeNotifier {
});
}
}
@override
void dispose() {
if (_menuSharedStateListener != null) {
getIt<MenuSharedState>().removeLatestViewListener(_menuSharedStateListener!);
}
super.dispose();
}
}

View File

@ -1,7 +1,9 @@
import 'dart:async';
import 'dart:collection';
import 'package:app_flowy/workspace/application/grid/cell/select_option_service.dart';
import 'package:dartz/dartz.dart';
import 'package:equatable/equatable.dart';
import 'package:flowy_sdk/dispatch/dispatch.dart';
import 'package:flowy_sdk/log.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
@ -10,13 +12,52 @@ import 'package:flowy_sdk/protobuf/flowy-grid/cell_entities.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/selection_type_option.pb.dart';
import 'package:flutter/foundation.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:app_flowy/workspace/application/grid/cell/cell_listener.dart';
import 'package:equatable/equatable.dart';
part 'cell_service.freezed.dart';
typedef GridDefaultCellContext = GridCellContext<Cell>;
typedef GridSelectOptionCellContext = GridCellContext<SelectOptionContext>;
class GridCellContextBuilder {
final GridCellCache _cellCache;
final GridCell _gridCell;
GridCellContextBuilder({
required GridCellCache cellCache,
required GridCell gridCell,
}) : _cellCache = cellCache,
_gridCell = gridCell;
GridCellContext build() {
switch (_gridCell.field.fieldType) {
case FieldType.Checkbox:
case FieldType.DateTime:
case FieldType.Number:
return GridDefaultCellContext(
gridCell: _gridCell,
cellCache: _cellCache,
cellDataLoader: DefaultCellDataLoader(gridCell: _gridCell, reloadOnCellChanged: true),
);
case FieldType.RichText:
return GridDefaultCellContext(
gridCell: _gridCell,
cellCache: _cellCache,
cellDataLoader: DefaultCellDataLoader(gridCell: _gridCell),
);
case FieldType.MultiSelect:
case FieldType.SingleSelect:
return GridSelectOptionCellContext(
gridCell: _gridCell,
cellCache: _cellCache,
cellDataLoader: SelectOptionCellDataLoader(gridCell: _gridCell),
);
default:
throw UnimplementedError;
}
}
}
// ignore: must_be_immutable
class GridCellContext<T> extends Equatable {
final GridCell gridCell;

View File

@ -27,14 +27,26 @@ class GridHeaderBloc extends Bloc<GridHeaderEvent, GridHeaderState> {
emit(state.copyWith(fields: value.fields));
},
moveField: (_MoveField value) async {
final result = await _fieldService.moveField(value.field.id, value.fromIndex, value.toIndex);
result.fold((l) {}, (err) => Log.error(err));
await _moveField(value, emit);
},
);
},
);
}
Future<void> _moveField(_MoveField value, Emitter<GridHeaderState> emit) async {
final fields = List<Field>.from(state.fields);
fields.insert(value.toIndex, fields.removeAt(value.fromIndex));
emit(state.copyWith(fields: fields));
final result = await _fieldService.moveField(
value.field.id,
value.fromIndex,
value.toIndex,
);
result.fold((l) {}, (err) => Log.error(err));
}
Future<void> _startListening() async {
fieldCache.addListener(
onChanged: (fields) => add(GridHeaderEvent.didReceiveFieldUpdate(fields)),

View File

@ -45,6 +45,9 @@ class MenuBloc extends Bloc<MenuEvent, MenuState> {
if (state.apps.length > value.fromIndex) {
final app = state.apps[value.fromIndex];
_workspaceService.moveApp(appId: app.id, fromIndex: value.fromIndex, toIndex: value.toIndex);
final apps = List<App>.from(state.apps);
apps.insert(value.toIndex, apps.removeAt(value.fromIndex));
emit(state.copyWith(apps: apps));
}
},
);

View File

@ -32,7 +32,7 @@ class ViewSectionBloc extends Bloc<ViewSectionEvent, ViewSectionState> {
emit(state.copyWith(views: value.views));
},
moveView: (_MoveView value) async {
await _moveView(value);
_moveView(value, emit);
},
);
});
@ -59,9 +59,13 @@ class ViewSectionBloc extends Bloc<ViewSectionEvent, ViewSectionState> {
}
}
Future<void> _moveView(_MoveView value) async {
Future<void> _moveView(_MoveView value, Emitter<ViewSectionState> emit) async {
if (value.fromIndex < state.views.length) {
final viewId = state.views[value.fromIndex].id;
final views = List<View>.from(state.views);
views.insert(value.toIndex, views.removeAt(value.fromIndex));
emit(state.copyWith(views: views));
final result = await _appService.moveView(
viewId: viewId,
fromIndex: value.fromIndex,

View File

@ -11,7 +11,7 @@ import 'section/section.dart';
class MenuApp extends StatefulWidget {
final App app;
MenuApp(this.app, {Key? key}) : super(key: ValueKey(app.hashCode));
const MenuApp(this.app, {Key? key}) : super(key: key);
@override
State<MenuApp> createState() => _MenuAppState();
@ -95,6 +95,11 @@ class _MenuAppState extends State<MenuApp> {
);
}
@override
void didUpdateWidget(covariant MenuApp oldWidget) {
super.didUpdateWidget(oldWidget);
}
@override
void dispose() {
viewDataContext.dispose();

View File

@ -107,7 +107,7 @@ class HomeMenu extends StatelessWidget {
child: ScrollConfiguration(
behavior: const ScrollBehavior().copyWith(scrollbars: false),
child: BlocSelector<MenuBloc, MenuState, List<Widget>>(
selector: (state) => state.apps.map((app) => MenuApp(app)).toList(),
selector: (state) => state.apps.map((app) => MenuApp(app, key: ValueKey(app.id))).toList(),
builder: (context, menuItems) {
return ReorderableListView.builder(
itemCount: menuItems.length,
@ -116,11 +116,18 @@ class HomeMenu extends StatelessWidget {
padding: EdgeInsets.only(bottom: 20.0 - MenuAppSizes.appVPadding),
child: MenuUser(user),
),
onReorder: (oldIndex, newIndex) => context.read<MenuBloc>().add(MenuEvent.moveApp(oldIndex, newIndex)),
onReorder: (oldIndex, newIndex) {
// Moving item1 from index 0 to index 1
// expect: oldIndex: 0, newIndex: 1
// receive: oldIndex: 0, newIndex: 2
// Workaround: if newIndex > oldIndex, we just minus one
int index = newIndex > oldIndex ? newIndex - 1 : newIndex;
context.read<MenuBloc>().add(MenuEvent.moveApp(oldIndex, index));
},
physics: StyledScrollPhysics(),
itemBuilder: (BuildContext context, int index) {
return ReorderableDragStartListener(
key: ValueKey(menuItems[index].hashCode),
key: ValueKey(menuItems[index].key),
index: index,
child: Padding(
padding: EdgeInsets.symmetric(vertical: MenuAppSizes.appVPadding / 2),

View File

@ -1,5 +1,4 @@
import 'package:app_flowy/workspace/application/grid/cell/cell_service.dart';
import 'package:app_flowy/workspace/application/grid/cell/select_option_service.dart';
import 'package:flowy_infra_ui/style_widget/hover.dart';
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' show FieldType;
import 'package:flutter/widgets.dart';
@ -12,49 +11,22 @@ import 'text_cell.dart';
GridCellWidget buildGridCellWidget(GridCell gridCell, GridCellCache cellCache, {GridCellStyle? style}) {
final key = ValueKey(gridCell.rowId + gridCell.field.id);
final cellContext = makeCellContext(gridCell, cellCache);
final cellContextBuilder = GridCellContextBuilder(gridCell: gridCell, cellCache: cellCache);
switch (gridCell.field.fieldType) {
case FieldType.Checkbox:
return CheckboxCell(cellContext: cellContext, key: key);
return CheckboxCell(cellContextBuilder: cellContextBuilder, key: key);
case FieldType.DateTime:
return DateCell(cellContext: cellContext, key: key);
case FieldType.MultiSelect:
return MultiSelectCell(cellContext: cellContext as GridSelectOptionCellContext, style: style, key: key);
case FieldType.Number:
return NumberCell(cellContext: cellContext, key: key);
case FieldType.RichText:
return GridTextCell(cellContext: cellContext, style: style, key: key);
return DateCell(cellContextBuilder: cellContextBuilder, key: key);
case FieldType.SingleSelect:
return SingleSelectCell(cellContext: cellContext as GridSelectOptionCellContext, style: style, key: key);
default:
throw UnimplementedError;
}
}
return SingleSelectCell(cellContextBuilder: cellContextBuilder, style: style, key: key);
case FieldType.MultiSelect:
return MultiSelectCell(cellContextBuilder: cellContextBuilder, style: style, key: key);
case FieldType.Number:
return NumberCell(cellContextBuilder: cellContextBuilder, key: key);
case FieldType.RichText:
return GridTextCell(cellContextBuilder: cellContextBuilder, style: style, key: key);
GridCellContext makeCellContext(GridCell gridCell, GridCellCache cellCache) {
switch (gridCell.field.fieldType) {
case FieldType.Checkbox:
case FieldType.DateTime:
case FieldType.Number:
return GridDefaultCellContext(
gridCell: gridCell,
cellCache: cellCache,
cellDataLoader: DefaultCellDataLoader(gridCell: gridCell, reloadOnCellChanged: true),
);
case FieldType.RichText:
return GridDefaultCellContext(
gridCell: gridCell,
cellCache: cellCache,
cellDataLoader: DefaultCellDataLoader(gridCell: gridCell),
);
case FieldType.MultiSelect:
case FieldType.SingleSelect:
return GridSelectOptionCellContext(
gridCell: gridCell,
cellCache: cellCache,
cellDataLoader: SelectOptionCellDataLoader(gridCell: gridCell),
);
default:
throw UnimplementedError;
}
@ -72,7 +44,16 @@ class BlankCell extends StatelessWidget {
abstract class GridCellWidget extends HoverWidget {
@override
final ValueNotifier<bool> onFocus = ValueNotifier<bool>(false);
final GridCellRequestFocusNotifier requestFocus = GridCellRequestFocusNotifier();
GridCellWidget({Key? key}) : super(key: key);
}
class GridCellRequestFocusNotifier extends ChangeNotifier {
void notify() {
notifyListeners();
}
}
abstract class GridCellStyle {}

View File

@ -1,3 +1,4 @@
import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/row/grid_row.dart';
import 'package:flowy_infra/theme.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
@ -31,17 +32,20 @@ class CellContainer extends StatelessWidget {
final GridCellWidget child;
final Widget? expander;
final double width;
final RegionStateNotifier rowStateNotifier;
const CellContainer({
Key? key,
required this.child,
required this.width,
required this.rowStateNotifier,
this.expander,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
return ChangeNotifierProxyProvider<RegionStateNotifier, CellStateNotifier>(
create: (_) => CellStateNotifier(),
update: (_, row, cell) => cell!..onEnter = row.onEnter,
child: Selector<CellStateNotifier, bool>(
selector: (context, notifier) => notifier.isFocus,
builder: (context, isFocus, _) {
@ -54,11 +58,15 @@ class CellContainer extends StatelessWidget {
container = _CellEnterRegion(child: container, expander: expander!);
}
return Container(
constraints: BoxConstraints(maxWidth: width),
decoration: _makeBoxDecoration(context, isFocus),
padding: GridSize.cellContentInsets,
child: container,
return GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () => child.requestFocus.notify(),
child: Container(
constraints: BoxConstraints(maxWidth: width),
decoration: _makeBoxDecoration(context, isFocus),
padding: GridSize.cellContentInsets,
child: container,
),
);
},
),

View File

@ -7,10 +7,9 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'cell_builder.dart';
class CheckboxCell extends GridCellWidget {
final GridCellContext cellContext;
final GridCellContextBuilder cellContextBuilder;
CheckboxCell({
required this.cellContext,
required this.cellContextBuilder,
Key? key,
}) : super(key: key);
@ -23,7 +22,9 @@ class _CheckboxCellState extends State<CheckboxCell> {
@override
void initState() {
_cellBloc = getIt<CheckboxCellBloc>(param1: widget.cellContext)..add(const CheckboxCellEvent.initial());
final cellContext = widget.cellContextBuilder.build();
_cellBloc = getIt<CheckboxCellBloc>(param1: cellContext)..add(const CheckboxCellEvent.initial());
_listenCellRequestFocus();
super.initState();
}
@ -48,9 +49,21 @@ class _CheckboxCellState extends State<CheckboxCell> {
);
}
@override
void didUpdateWidget(covariant CheckboxCell oldWidget) {
_listenCellRequestFocus();
super.didUpdateWidget(oldWidget);
}
@override
Future<void> dispose() async {
_cellBloc.close();
super.dispose();
}
void _listenCellRequestFocus() {
widget.requestFocus.addListener(() {
_cellBloc.add(const CheckboxCellEvent.select());
});
}
}

View File

@ -14,10 +14,10 @@ abstract class GridCellDelegate {
}
class DateCell extends GridCellWidget {
final GridCellContext cellContext;
final GridCellContextBuilder cellContextBuilder;
DateCell({
required this.cellContext,
required this.cellContextBuilder,
Key? key,
}) : super(key: key);
@ -30,7 +30,8 @@ class _DateCellState extends State<DateCell> {
@override
void initState() {
_cellBloc = getIt<DateCellBloc>(param1: widget.cellContext)..add(const DateCellEvent.initial());
final cellContext = widget.cellContextBuilder.build();
_cellBloc = getIt<DateCellBloc>(param1: cellContext)..add(const DateCellEvent.initial());
super.initState();
}

View File

@ -8,10 +8,10 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'cell_builder.dart';
class NumberCell extends GridCellWidget {
final GridCellContext cellContext;
final GridCellContextBuilder cellContextBuilder;
NumberCell({
required this.cellContext,
required this.cellContextBuilder,
Key? key,
}) : super(key: key);
@ -23,11 +23,13 @@ class _NumberCellState extends State<NumberCell> {
late NumberCellBloc _cellBloc;
late TextEditingController _controller;
late FocusNode _focusNode;
VoidCallback? _focusListener;
Timer? _delayOperation;
@override
void initState() {
_cellBloc = getIt<NumberCellBloc>(param1: widget.cellContext)..add(const NumberCellEvent.initial());
final cellContext = widget.cellContextBuilder.build();
_cellBloc = getIt<NumberCellBloc>(param1: cellContext)..add(const NumberCellEvent.initial());
_controller = TextEditingController(text: _cellBloc.state.content);
_focusNode = FocusNode();
_focusNode.addListener(() {
@ -39,6 +41,7 @@ class _NumberCellState extends State<NumberCell> {
@override
Widget build(BuildContext context) {
_listenCellRequestFocus(context);
return BlocProvider.value(
value: _cellBloc,
child: BlocConsumer<NumberCellBloc, NumberCellState>(
@ -67,6 +70,9 @@ class _NumberCellState extends State<NumberCell> {
@override
Future<void> dispose() async {
if (_focusListener != null) {
widget.requestFocus.removeListener(_focusListener!);
}
_delayOperation?.cancel();
_cellBloc.close();
_focusNode.dispose();
@ -88,4 +94,19 @@ class _NumberCellState extends State<NumberCell> {
});
}
}
void _listenCellRequestFocus(BuildContext context) {
if (_focusListener != null) {
widget.requestFocus.removeListener(_focusListener!);
}
focusListener() {
if (_focusNode.hasFocus == false && _focusNode.canRequestFocus) {
FocusScope.of(context).requestFocus(_focusNode);
}
}
_focusListener = focusListener;
widget.requestFocus.addListener(focusListener);
}
}

View File

@ -20,11 +20,11 @@ class SelectOptionCellStyle extends GridCellStyle {
}
class SingleSelectCell extends GridCellWidget {
final GridSelectOptionCellContext cellContext;
final GridCellContextBuilder cellContextBuilder;
late final SelectOptionCellStyle? cellStyle;
SingleSelectCell({
required this.cellContext,
required this.cellContextBuilder,
GridCellStyle? style,
Key? key,
}) : super(key: key) {
@ -45,7 +45,8 @@ class _SingleSelectCellState extends State<SingleSelectCell> {
@override
void initState() {
// Log.trace("init widget $hashCode");
_cellBloc = getIt<SelectionCellBloc>(param1: widget.cellContext)..add(const SelectionCellEvent.initial());
final cellContext = _buildCellContext();
_cellBloc = getIt<SelectionCellBloc>(param1: cellContext)..add(const SelectionCellEvent.initial());
super.initState();
}
@ -69,7 +70,7 @@ class _SingleSelectCellState extends State<SingleSelectCell> {
widget.onFocus.value = true;
SelectOptionCellEditor.show(
context,
widget.cellContext.clone(),
_buildCellContext(),
() => widget.onFocus.value = false,
);
},
@ -81,12 +82,8 @@ class _SingleSelectCellState extends State<SingleSelectCell> {
);
}
@override
void didUpdateWidget(covariant SingleSelectCell oldWidget) {
if (oldWidget.cellContext != widget.cellContext) {
// Log.trace("did update widget $hashCode");
}
super.didUpdateWidget(oldWidget);
GridSelectOptionCellContext _buildCellContext() {
return widget.cellContextBuilder.build() as GridSelectOptionCellContext;
}
@override
@ -99,11 +96,11 @@ class _SingleSelectCellState extends State<SingleSelectCell> {
//----------------------------------------------------------------
class MultiSelectCell extends GridCellWidget {
final GridSelectOptionCellContext cellContext;
final GridCellContextBuilder cellContextBuilder;
late final SelectOptionCellStyle? cellStyle;
MultiSelectCell({
required this.cellContext,
required this.cellContextBuilder,
GridCellStyle? style,
Key? key,
}) : super(key: key) {
@ -123,7 +120,8 @@ class _MultiSelectCellState extends State<MultiSelectCell> {
@override
void initState() {
_cellBloc = getIt<SelectionCellBloc>(param1: widget.cellContext)..add(const SelectionCellEvent.initial());
final cellContext = _buildCellContext();
_cellBloc = getIt<SelectionCellBloc>(param1: cellContext)..add(const SelectionCellEvent.initial());
super.initState();
}
@ -145,7 +143,7 @@ class _MultiSelectCellState extends State<MultiSelectCell> {
widget.onFocus.value = true;
SelectOptionCellEditor.show(
context,
widget.cellContext,
_buildCellContext(),
() => widget.onFocus.value = false,
);
},
@ -162,4 +160,8 @@ class _MultiSelectCellState extends State<MultiSelectCell> {
_cellBloc.close();
super.dispose();
}
GridSelectOptionCellContext _buildCellContext() {
return widget.cellContextBuilder.build() as GridSelectOptionCellContext;
}
}

View File

@ -14,10 +14,10 @@ class GridTextCellStyle extends GridCellStyle {
}
class GridTextCell extends GridCellWidget {
final GridCellContext cellContext;
final GridCellContextBuilder cellContextBuilder;
late final GridTextCellStyle? cellStyle;
GridTextCell({
required this.cellContext,
required this.cellContextBuilder,
GridCellStyle? style,
Key? key,
}) : super(key: key) {
@ -36,12 +36,13 @@ class _GridTextCellState extends State<GridTextCell> {
late TextCellBloc _cellBloc;
late TextEditingController _controller;
late FocusNode _focusNode;
VoidCallback? _focusListener;
Timer? _delayOperation;
@override
void initState() {
_cellBloc = getIt<TextCellBloc>(param1: widget.cellContext);
final cellContext = widget.cellContextBuilder.build();
_cellBloc = getIt<TextCellBloc>(param1: cellContext);
_cellBloc.add(const TextCellEvent.initial());
_controller = TextEditingController(text: _cellBloc.state.content);
_focusNode = FocusNode();
@ -49,11 +50,14 @@ class _GridTextCellState extends State<GridTextCell> {
widget.onFocus.value = _focusNode.hasFocus;
focusChanged();
});
super.initState();
}
@override
Widget build(BuildContext context) {
_listenCellRequestFocus(context);
return BlocProvider.value(
value: _cellBloc,
child: BlocConsumer<TextCellBloc, TextCellState>(
@ -83,8 +87,26 @@ class _GridTextCellState extends State<GridTextCell> {
);
}
void _listenCellRequestFocus(BuildContext context) {
if (_focusListener != null) {
widget.requestFocus.removeListener(_focusListener!);
}
focusListener() {
if (_focusNode.hasFocus == false && _focusNode.canRequestFocus) {
FocusScope.of(context).requestFocus(_focusNode);
}
}
_focusListener = focusListener;
widget.requestFocus.addListener(focusListener);
}
@override
Future<void> dispose() async {
if (_focusListener != null) {
widget.requestFocus.removeListener(_focusListener!);
}
_delayOperation?.cancel();
_cellBloc.close();
_focusNode.dispose();

View File

@ -88,7 +88,7 @@ class _RowLeading extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Consumer<_RegionStateNotifier>(
return Consumer<RegionStateNotifier>(
builder: (context, state, _) {
return SizedBox(width: GridSize.leadingHeaderPadding, child: state.onEnter ? _activeWidget() : null);
},
@ -164,13 +164,13 @@ class _RowCells extends StatelessWidget {
return Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: _makeCells(state.cellDataMap),
children: _makeCells(context, state.cellDataMap),
);
},
);
}
List<Widget> _makeCells(GridCellMap gridCellMap) {
List<Widget> _makeCells(BuildContext context, GridCellMap gridCellMap) {
return gridCellMap.values.map(
(gridCell) {
Widget? expander;
@ -181,6 +181,7 @@ class _RowCells extends StatelessWidget {
return CellContainer(
width: gridCell.field.width.toDouble(),
child: buildGridCellWidget(gridCell, cellCache),
rowStateNotifier: Provider.of<RegionStateNotifier>(context, listen: false),
expander: expander,
);
},
@ -188,7 +189,7 @@ class _RowCells extends StatelessWidget {
}
}
class _RegionStateNotifier extends ChangeNotifier {
class RegionStateNotifier extends ChangeNotifier {
bool _onEnter = false;
set onEnter(bool value) {
@ -226,11 +227,11 @@ class _RowEnterRegion extends StatefulWidget {
}
class _RowEnterRegionState extends State<_RowEnterRegion> {
late _RegionStateNotifier _rowStateNotifier;
late RegionStateNotifier _rowStateNotifier;
@override
void initState() {
_rowStateNotifier = _RegionStateNotifier();
_rowStateNotifier = RegionStateNotifier();
super.initState();
}

View File

@ -12,7 +12,7 @@ use flowy_database::{
schema::{view_table, view_table::dsl},
SqliteConnection,
};
use lib_infra::timestamp;
use lib_infra::util::timestamp;
pub struct ViewTableSql();
impl ViewTableSql {

View File

@ -265,7 +265,7 @@ use flowy_user::event_map::UserCloudService;
use flowy_user_data_model::entities::{
SignInParams, SignInResponse, SignUpParams, SignUpResponse, UpdateUserParams, UserProfile,
};
use lib_infra::{future::FutureResult, timestamp};
use lib_infra::{future::FutureResult, util::timestamp};
impl FolderCouldServiceV1 for LocalServer {
fn init(&self) {}

View File

@ -10,6 +10,8 @@ use crate::{
use flowy_folder_data_model::entities::{app::App, trash::Trash, view::View, workspace::Workspace};
use lib_ot::core::*;
use crate::errors::internal_error;
use lib_infra::util::move_vec_element;
use serde::{Deserialize, Serialize};
use std::sync::Arc;
@ -170,16 +172,12 @@ impl FolderPad {
}
#[tracing::instrument(level = "trace", skip(self), err)]
pub fn move_app(&mut self, app_id: &str, _from: usize, to: usize) -> CollaborateResult<Option<FolderChange>> {
pub fn move_app(&mut self, app_id: &str, from: usize, to: usize) -> CollaborateResult<Option<FolderChange>> {
let app = self.read_app(app_id)?;
self.with_workspace(&app.workspace_id, |workspace| {
match workspace.apps.iter().position(|app| app.id == app_id) {
None => Ok(None),
Some(index) => {
let app = workspace.apps.remove(index);
workspace.apps.insert(to, app);
Ok(Some(()))
}
match move_vec_element(&mut workspace.apps, |app| app.id == app_id, from, to).map_err(internal_error)? {
true => Ok(Some(())),
false => Ok(None),
}
})
}
@ -251,16 +249,12 @@ impl FolderPad {
}
#[tracing::instrument(level = "trace", skip(self), err)]
pub fn move_view(&mut self, view_id: &str, _from: usize, to: usize) -> CollaborateResult<Option<FolderChange>> {
pub fn move_view(&mut self, view_id: &str, from: usize, to: usize) -> CollaborateResult<Option<FolderChange>> {
let view = self.read_view(view_id)?;
self.with_app(&view.belong_to_id, |app| {
match app.belongings.iter().position(|view| view.id == view_id) {
None => Ok(None),
Some(index) => {
let view = app.belongings.remove(index);
app.belongings.insert(to, view);
Ok(Some(()))
}
match move_vec_element(&mut app.belongings, |view| view.id == view_id, from, to).map_err(internal_error)? {
true => Ok(Some(())),
false => Ok(None),
}
})
}

View File

@ -6,6 +6,7 @@ use flowy_grid_data_model::entities::{
gen_grid_id, FieldChangesetParams, FieldMeta, FieldOrder, FieldType, GridBlockMeta, GridBlockMetaChangeset,
GridMeta,
};
use lib_infra::util::move_vec_element;
use lib_ot::core::{OperationTransformable, PlainTextAttributes, PlainTextDelta, PlainTextDeltaBuilder};
use std::collections::HashMap;
use std::sync::Arc;
@ -208,20 +209,22 @@ impl GridMetaPad {
pub fn move_field(
&mut self,
field_id: &str,
_from_index: usize,
from_index: usize,
to_index: usize,
) -> CollaborateResult<Option<GridChangeset>> {
self.modify_grid(
|grid_meta| match grid_meta.fields.iter().position(|field| field.id == field_id) {
None => Ok(None),
Some(index) => {
// debug_assert_eq!(index, from_index);
let field_meta = grid_meta.fields.remove(index);
grid_meta.fields.insert(to_index, field_meta);
Ok(Some(()))
}
},
)
self.modify_grid(|grid_meta| {
match move_vec_element(
&mut grid_meta.fields,
|field| field.id == field_id,
from_index,
to_index,
)
.map_err(internal_error)?
{
true => Ok(Some(())),
false => Ok(None),
}
})
}
pub fn contain_field(&self, field_id: &str) -> bool {

View File

@ -1,8 +1,4 @@
pub mod code_gen;
pub mod future;
pub mod retry;
#[allow(dead_code)]
pub fn timestamp() -> i64 {
chrono::Utc::now().timestamp()
}
pub mod util;

View File

@ -0,0 +1,32 @@
pub fn move_vec_element<T, F>(
vec: &mut Vec<T>,
filter: F,
_from_index: usize,
mut to_index: usize,
) -> Result<bool, String>
where
F: FnMut(&T) -> bool,
{
match vec.iter().position(filter) {
None => Ok(false),
Some(index) => {
if vec.len() > to_index {
let removed_element = vec.remove(index);
vec.insert(to_index, removed_element);
Ok(true)
} else {
let msg = format!(
"Move element to invalid index: {}, current len: {}",
to_index,
vec.len()
);
Err(msg)
}
}
}
}
#[allow(dead_code)]
pub fn timestamp() -> i64 {
chrono::Utc::now().timestamp()
}