[rust]: delete app

This commit is contained in:
appflowy 2021-10-30 17:19:50 +08:00
parent dd9456e7ff
commit 4966b03123
55 changed files with 867 additions and 448 deletions

View File

@ -1,5 +1,6 @@
import 'package:app_flowy/workspace/domain/i_app.dart';
import 'package:flowy_log/flowy_log.dart';
import 'package:flowy_sdk/protobuf/flowy-workspace/app_create.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-workspace/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-workspace/view_create.pb.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
@ -11,7 +12,7 @@ part 'app_bloc.freezed.dart';
class AppBloc extends Bloc<AppEvent, AppState> {
final IApp appManager;
final IAppListenr listener;
AppBloc({required this.appManager, required this.listener}) : super(AppState.initial());
AppBloc({required App app, required this.appManager, required this.listener}) : super(AppState.initial(app));
@override
Stream<AppState> mapEventToState(
@ -20,7 +21,6 @@ class AppBloc extends Bloc<AppEvent, AppState> {
yield* event.map(
initial: (e) async* {
listener.start(viewsChangeCallback: _handleViewsOrFail);
yield* _fetchViews();
},
createView: (CreateView value) async* {
@ -39,6 +39,20 @@ class AppBloc extends Bloc<AppEvent, AppState> {
didReceiveViews: (e) async* {
yield state.copyWith(views: e.views);
},
delete: (e) async* {
final result = await appManager.delete();
yield result.fold(
(l) => state.copyWith(successOrFailure: left(unit)),
(error) => state.copyWith(successOrFailure: right(error)),
);
},
rename: (e) async* {
final result = await appManager.rename(e.newName);
yield result.fold(
(l) => state.copyWith(successOrFailure: left(unit)),
(error) => state.copyWith(successOrFailure: right(error)),
);
},
);
}
@ -73,19 +87,23 @@ class AppBloc extends Bloc<AppEvent, AppState> {
class AppEvent with _$AppEvent {
const factory AppEvent.initial() = Initial;
const factory AppEvent.createView(String name, String desc, ViewType viewType) = CreateView;
const factory AppEvent.delete() = Delete;
const factory AppEvent.rename(String newName) = Rename;
const factory AppEvent.didReceiveViews(List<View> views) = ReceiveViews;
}
@freezed
class AppState with _$AppState {
const factory AppState({
required App app,
required bool isLoading,
required List<View>? views,
View? selectedView,
required Either<Unit, WorkspaceError> successOrFailure,
}) = _AppState;
factory AppState.initial() => AppState(
factory AppState.initial(App app) => AppState(
app: app,
isLoading: false,
views: null,
selectedView: null,

View File

@ -28,6 +28,16 @@ class _$AppEventTearOff {
);
}
Delete delete() {
return const Delete();
}
Rename rename(String newName) {
return Rename(
newName,
);
}
ReceiveViews didReceiveViews(List<View> views) {
return ReceiveViews(
views,
@ -45,6 +55,8 @@ mixin _$AppEvent {
required TResult Function() initial,
required TResult Function(String name, String desc, ViewType viewType)
createView,
required TResult Function() delete,
required TResult Function(String newName) rename,
required TResult Function(List<View> views) didReceiveViews,
}) =>
throw _privateConstructorUsedError;
@ -52,6 +64,8 @@ mixin _$AppEvent {
TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial,
TResult Function(String name, String desc, ViewType viewType)? createView,
TResult Function()? delete,
TResult Function(String newName)? rename,
TResult Function(List<View> views)? didReceiveViews,
required TResult orElse(),
}) =>
@ -60,6 +74,8 @@ mixin _$AppEvent {
TResult map<TResult extends Object?>({
required TResult Function(Initial value) initial,
required TResult Function(CreateView value) createView,
required TResult Function(Delete value) delete,
required TResult Function(Rename value) rename,
required TResult Function(ReceiveViews value) didReceiveViews,
}) =>
throw _privateConstructorUsedError;
@ -67,6 +83,8 @@ mixin _$AppEvent {
TResult maybeMap<TResult extends Object?>({
TResult Function(Initial value)? initial,
TResult Function(CreateView value)? createView,
TResult Function(Delete value)? delete,
TResult Function(Rename value)? rename,
TResult Function(ReceiveViews value)? didReceiveViews,
required TResult orElse(),
}) =>
@ -128,6 +146,8 @@ class _$Initial implements Initial {
required TResult Function() initial,
required TResult Function(String name, String desc, ViewType viewType)
createView,
required TResult Function() delete,
required TResult Function(String newName) rename,
required TResult Function(List<View> views) didReceiveViews,
}) {
return initial();
@ -138,6 +158,8 @@ class _$Initial implements Initial {
TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial,
TResult Function(String name, String desc, ViewType viewType)? createView,
TResult Function()? delete,
TResult Function(String newName)? rename,
TResult Function(List<View> views)? didReceiveViews,
required TResult orElse(),
}) {
@ -152,6 +174,8 @@ class _$Initial implements Initial {
TResult map<TResult extends Object?>({
required TResult Function(Initial value) initial,
required TResult Function(CreateView value) createView,
required TResult Function(Delete value) delete,
required TResult Function(Rename value) rename,
required TResult Function(ReceiveViews value) didReceiveViews,
}) {
return initial(this);
@ -162,6 +186,8 @@ class _$Initial implements Initial {
TResult maybeMap<TResult extends Object?>({
TResult Function(Initial value)? initial,
TResult Function(CreateView value)? createView,
TResult Function(Delete value)? delete,
TResult Function(Rename value)? rename,
TResult Function(ReceiveViews value)? didReceiveViews,
required TResult orElse(),
}) {
@ -264,6 +290,8 @@ class _$CreateView implements CreateView {
required TResult Function() initial,
required TResult Function(String name, String desc, ViewType viewType)
createView,
required TResult Function() delete,
required TResult Function(String newName) rename,
required TResult Function(List<View> views) didReceiveViews,
}) {
return createView(name, desc, viewType);
@ -274,6 +302,8 @@ class _$CreateView implements CreateView {
TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial,
TResult Function(String name, String desc, ViewType viewType)? createView,
TResult Function()? delete,
TResult Function(String newName)? rename,
TResult Function(List<View> views)? didReceiveViews,
required TResult orElse(),
}) {
@ -288,6 +318,8 @@ class _$CreateView implements CreateView {
TResult map<TResult extends Object?>({
required TResult Function(Initial value) initial,
required TResult Function(CreateView value) createView,
required TResult Function(Delete value) delete,
required TResult Function(Rename value) rename,
required TResult Function(ReceiveViews value) didReceiveViews,
}) {
return createView(this);
@ -298,6 +330,8 @@ class _$CreateView implements CreateView {
TResult maybeMap<TResult extends Object?>({
TResult Function(Initial value)? initial,
TResult Function(CreateView value)? createView,
TResult Function(Delete value)? delete,
TResult Function(Rename value)? rename,
TResult Function(ReceiveViews value)? didReceiveViews,
required TResult orElse(),
}) {
@ -320,6 +354,227 @@ abstract class CreateView implements AppEvent {
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $DeleteCopyWith<$Res> {
factory $DeleteCopyWith(Delete value, $Res Function(Delete) then) =
_$DeleteCopyWithImpl<$Res>;
}
/// @nodoc
class _$DeleteCopyWithImpl<$Res> extends _$AppEventCopyWithImpl<$Res>
implements $DeleteCopyWith<$Res> {
_$DeleteCopyWithImpl(Delete _value, $Res Function(Delete) _then)
: super(_value, (v) => _then(v as Delete));
@override
Delete get _value => super._value as Delete;
}
/// @nodoc
class _$Delete implements Delete {
const _$Delete();
@override
String toString() {
return 'AppEvent.delete()';
}
@override
bool operator ==(dynamic other) {
return identical(this, other) || (other is Delete);
}
@override
int get hashCode => runtimeType.hashCode;
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function() initial,
required TResult Function(String name, String desc, ViewType viewType)
createView,
required TResult Function() delete,
required TResult Function(String newName) rename,
required TResult Function(List<View> views) didReceiveViews,
}) {
return delete();
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial,
TResult Function(String name, String desc, ViewType viewType)? createView,
TResult Function()? delete,
TResult Function(String newName)? rename,
TResult Function(List<View> views)? didReceiveViews,
required TResult orElse(),
}) {
if (delete != null) {
return delete();
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(Initial value) initial,
required TResult Function(CreateView value) createView,
required TResult Function(Delete value) delete,
required TResult Function(Rename value) rename,
required TResult Function(ReceiveViews value) didReceiveViews,
}) {
return delete(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(Initial value)? initial,
TResult Function(CreateView value)? createView,
TResult Function(Delete value)? delete,
TResult Function(Rename value)? rename,
TResult Function(ReceiveViews value)? didReceiveViews,
required TResult orElse(),
}) {
if (delete != null) {
return delete(this);
}
return orElse();
}
}
abstract class Delete implements AppEvent {
const factory Delete() = _$Delete;
}
/// @nodoc
abstract class $RenameCopyWith<$Res> {
factory $RenameCopyWith(Rename value, $Res Function(Rename) then) =
_$RenameCopyWithImpl<$Res>;
$Res call({String newName});
}
/// @nodoc
class _$RenameCopyWithImpl<$Res> extends _$AppEventCopyWithImpl<$Res>
implements $RenameCopyWith<$Res> {
_$RenameCopyWithImpl(Rename _value, $Res Function(Rename) _then)
: super(_value, (v) => _then(v as Rename));
@override
Rename get _value => super._value as Rename;
@override
$Res call({
Object? newName = freezed,
}) {
return _then(Rename(
newName == freezed
? _value.newName
: newName // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
/// @nodoc
class _$Rename implements Rename {
const _$Rename(this.newName);
@override
final String newName;
@override
String toString() {
return 'AppEvent.rename(newName: $newName)';
}
@override
bool operator ==(dynamic other) {
return identical(this, other) ||
(other is Rename &&
(identical(other.newName, newName) ||
const DeepCollectionEquality().equals(other.newName, newName)));
}
@override
int get hashCode =>
runtimeType.hashCode ^ const DeepCollectionEquality().hash(newName);
@JsonKey(ignore: true)
@override
$RenameCopyWith<Rename> get copyWith =>
_$RenameCopyWithImpl<Rename>(this, _$identity);
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function() initial,
required TResult Function(String name, String desc, ViewType viewType)
createView,
required TResult Function() delete,
required TResult Function(String newName) rename,
required TResult Function(List<View> views) didReceiveViews,
}) {
return rename(newName);
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial,
TResult Function(String name, String desc, ViewType viewType)? createView,
TResult Function()? delete,
TResult Function(String newName)? rename,
TResult Function(List<View> views)? didReceiveViews,
required TResult orElse(),
}) {
if (rename != null) {
return rename(newName);
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(Initial value) initial,
required TResult Function(CreateView value) createView,
required TResult Function(Delete value) delete,
required TResult Function(Rename value) rename,
required TResult Function(ReceiveViews value) didReceiveViews,
}) {
return rename(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(Initial value)? initial,
TResult Function(CreateView value)? createView,
TResult Function(Delete value)? delete,
TResult Function(Rename value)? rename,
TResult Function(ReceiveViews value)? didReceiveViews,
required TResult orElse(),
}) {
if (rename != null) {
return rename(this);
}
return orElse();
}
}
abstract class Rename implements AppEvent {
const factory Rename(String newName) = _$Rename;
String get newName => throw _privateConstructorUsedError;
@JsonKey(ignore: true)
$RenameCopyWith<Rename> get copyWith => throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $ReceiveViewsCopyWith<$Res> {
factory $ReceiveViewsCopyWith(
@ -387,6 +642,8 @@ class _$ReceiveViews implements ReceiveViews {
required TResult Function() initial,
required TResult Function(String name, String desc, ViewType viewType)
createView,
required TResult Function() delete,
required TResult Function(String newName) rename,
required TResult Function(List<View> views) didReceiveViews,
}) {
return didReceiveViews(views);
@ -397,6 +654,8 @@ class _$ReceiveViews implements ReceiveViews {
TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial,
TResult Function(String name, String desc, ViewType viewType)? createView,
TResult Function()? delete,
TResult Function(String newName)? rename,
TResult Function(List<View> views)? didReceiveViews,
required TResult orElse(),
}) {
@ -411,6 +670,8 @@ class _$ReceiveViews implements ReceiveViews {
TResult map<TResult extends Object?>({
required TResult Function(Initial value) initial,
required TResult Function(CreateView value) createView,
required TResult Function(Delete value) delete,
required TResult Function(Rename value) rename,
required TResult Function(ReceiveViews value) didReceiveViews,
}) {
return didReceiveViews(this);
@ -421,6 +682,8 @@ class _$ReceiveViews implements ReceiveViews {
TResult maybeMap<TResult extends Object?>({
TResult Function(Initial value)? initial,
TResult Function(CreateView value)? createView,
TResult Function(Delete value)? delete,
TResult Function(Rename value)? rename,
TResult Function(ReceiveViews value)? didReceiveViews,
required TResult orElse(),
}) {
@ -445,11 +708,13 @@ class _$AppStateTearOff {
const _$AppStateTearOff();
_AppState call(
{required bool isLoading,
{required App app,
required bool isLoading,
required List<View>? views,
View? selectedView,
required Either<Unit, WorkspaceError> successOrFailure}) {
return _AppState(
app: app,
isLoading: isLoading,
views: views,
selectedView: selectedView,
@ -463,6 +728,7 @@ const $AppState = _$AppStateTearOff();
/// @nodoc
mixin _$AppState {
App get app => throw _privateConstructorUsedError;
bool get isLoading => throw _privateConstructorUsedError;
List<View>? get views => throw _privateConstructorUsedError;
View? get selectedView => throw _privateConstructorUsedError;
@ -479,7 +745,8 @@ abstract class $AppStateCopyWith<$Res> {
factory $AppStateCopyWith(AppState value, $Res Function(AppState) then) =
_$AppStateCopyWithImpl<$Res>;
$Res call(
{bool isLoading,
{App app,
bool isLoading,
List<View>? views,
View? selectedView,
Either<Unit, WorkspaceError> successOrFailure});
@ -495,12 +762,17 @@ class _$AppStateCopyWithImpl<$Res> implements $AppStateCopyWith<$Res> {
@override
$Res call({
Object? app = freezed,
Object? isLoading = freezed,
Object? views = freezed,
Object? selectedView = freezed,
Object? successOrFailure = freezed,
}) {
return _then(_value.copyWith(
app: app == freezed
? _value.app
: app // ignore: cast_nullable_to_non_nullable
as App,
isLoading: isLoading == freezed
? _value.isLoading
: isLoading // ignore: cast_nullable_to_non_nullable
@ -527,7 +799,8 @@ abstract class _$AppStateCopyWith<$Res> implements $AppStateCopyWith<$Res> {
__$AppStateCopyWithImpl<$Res>;
@override
$Res call(
{bool isLoading,
{App app,
bool isLoading,
List<View>? views,
View? selectedView,
Either<Unit, WorkspaceError> successOrFailure});
@ -544,12 +817,17 @@ class __$AppStateCopyWithImpl<$Res> extends _$AppStateCopyWithImpl<$Res>
@override
$Res call({
Object? app = freezed,
Object? isLoading = freezed,
Object? views = freezed,
Object? selectedView = freezed,
Object? successOrFailure = freezed,
}) {
return _then(_AppState(
app: app == freezed
? _value.app
: app // ignore: cast_nullable_to_non_nullable
as App,
isLoading: isLoading == freezed
? _value.isLoading
: isLoading // ignore: cast_nullable_to_non_nullable
@ -574,11 +852,14 @@ class __$AppStateCopyWithImpl<$Res> extends _$AppStateCopyWithImpl<$Res>
class _$_AppState implements _AppState {
const _$_AppState(
{required this.isLoading,
{required this.app,
required this.isLoading,
required this.views,
this.selectedView,
required this.successOrFailure});
@override
final App app;
@override
final bool isLoading;
@override
@ -590,13 +871,15 @@ class _$_AppState implements _AppState {
@override
String toString() {
return 'AppState(isLoading: $isLoading, views: $views, selectedView: $selectedView, successOrFailure: $successOrFailure)';
return 'AppState(app: $app, isLoading: $isLoading, views: $views, selectedView: $selectedView, successOrFailure: $successOrFailure)';
}
@override
bool operator ==(dynamic other) {
return identical(this, other) ||
(other is _AppState &&
(identical(other.app, app) ||
const DeepCollectionEquality().equals(other.app, app)) &&
(identical(other.isLoading, isLoading) ||
const DeepCollectionEquality()
.equals(other.isLoading, isLoading)) &&
@ -613,6 +896,7 @@ class _$_AppState implements _AppState {
@override
int get hashCode =>
runtimeType.hashCode ^
const DeepCollectionEquality().hash(app) ^
const DeepCollectionEquality().hash(isLoading) ^
const DeepCollectionEquality().hash(views) ^
const DeepCollectionEquality().hash(selectedView) ^
@ -626,11 +910,14 @@ class _$_AppState implements _AppState {
abstract class _AppState implements AppState {
const factory _AppState(
{required bool isLoading,
{required App app,
required bool isLoading,
required List<View>? views,
View? selectedView,
required Either<Unit, WorkspaceError> successOrFailure}) = _$_AppState;
@override
App get app => throw _privateConstructorUsedError;
@override
bool get isLoading => throw _privateConstructorUsedError;
@override

View File

@ -8,6 +8,10 @@ abstract class IApp {
Future<Either<List<View>, WorkspaceError>> getViews();
Future<Either<View, WorkspaceError>> createView({required String name, String? desc, required ViewType viewType});
Future<Either<Unit, WorkspaceError>> delete();
Future<Either<Unit, WorkspaceError>> rename(String newName);
}
abstract class IAppListenr {

View File

@ -1,12 +1,10 @@
import 'package:flowy_sdk/protobuf/flowy-workspace/protobuf.dart';
import 'package:dartz/dartz.dart';
typedef WorkspaceCreateAppCallback = void Function(Either<List<App>, WorkspaceError> appsOrFail);
typedef WorkspaceAppsChangedCallback = void Function(Either<List<App>, WorkspaceError> appsOrFail);
typedef WorkspaceUpdatedCallback = void Function(String name, String desc);
typedef WorkspaceDeleteAppCallback = void Function(Either<List<App>, WorkspaceError> appsOrFail);
abstract class IWorkspace {
Future<Either<App, WorkspaceError>> createApp({required String name, String? desc});
@ -14,7 +12,7 @@ abstract class IWorkspace {
}
abstract class IWorkspaceListener {
void start({WorkspaceCreateAppCallback? addAppCallback, WorkspaceUpdatedCallback? updatedCallback});
void start({WorkspaceAppsChangedCallback? addAppCallback, WorkspaceUpdatedCallback? updatedCallback});
Future<void> stop();
}

View File

@ -19,6 +19,7 @@ import 'package:app_flowy/workspace/infrastructure/repos/trash_repo.dart';
import 'package:app_flowy/workspace/infrastructure/repos/view_repo.dart';
import 'package:app_flowy/workspace/infrastructure/repos/workspace_repo.dart';
import 'package:flowy_sdk/protobuf/flowy-user/user_profile.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-workspace/app_create.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-workspace/view_create.pb.dart';
import 'package:get_it/get_it.dart';
@ -77,10 +78,11 @@ class HomeDepsResolver {
(user, _) => MenuUserBloc(getIt<IUser>(param1: user), getIt<IUserListener>(param1: user)));
// App
getIt.registerFactoryParam<AppBloc, String, void>(
(appId, _) => AppBloc(
appManager: getIt<IApp>(param1: appId),
listener: getIt<IAppListenr>(param1: appId),
getIt.registerFactoryParam<AppBloc, App, void>(
(app, _) => AppBloc(
app: app,
appManager: getIt<IApp>(param1: app.id),
listener: getIt<IAppListenr>(param1: app.id),
),
);

View File

@ -26,6 +26,16 @@ class IAppImpl extends IApp {
);
});
}
@override
Future<Either<Unit, workspace.WorkspaceError>> delete() {
return repo.delete();
}
@override
Future<Either<Unit, workspace.WorkspaceError>> rename(String newName) {
return repo.updateApp(name: newName);
}
}
class IAppListenerhImpl extends IAppListenr {

View File

@ -35,7 +35,7 @@ class IWorkspaceListenerImpl extends IWorkspaceListener {
});
@override
void start({WorkspaceCreateAppCallback? addAppCallback, WorkspaceUpdatedCallback? updatedCallback}) {
void start({WorkspaceAppsChangedCallback? addAppCallback, WorkspaceUpdatedCallback? updatedCallback}) {
repo.startListening(createApp: addAppCallback, update: updatedCallback);
}

View File

@ -6,7 +6,9 @@ import 'package:flowy_log/flowy_log.dart';
import 'package:flowy_sdk/dispatch/dispatch.dart';
import 'package:flowy_sdk/protobuf/flowy-dart-notify/subject.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-workspace/app_create.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-workspace/app_delete.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-workspace/app_query.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-workspace/app_update.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-workspace/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-workspace/observable.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-workspace/view_create.pb.dart';
@ -21,7 +23,7 @@ class AppRepository {
});
Future<Either<App, WorkspaceError>> getAppDesc() {
final request = QueryAppRequest.create()..appId = appId;
final request = QueryAppRequest.create()..appIds.add(appId);
return WorkspaceEventReadApp(request).send();
}
@ -37,7 +39,7 @@ class AppRepository {
}
Future<Either<List<View>, WorkspaceError>> getViews() {
final request = QueryAppRequest.create()..appId = appId;
final request = QueryAppRequest.create()..appIds.add(appId);
return WorkspaceEventReadApp(request).send().then((result) {
return result.fold(
@ -46,6 +48,20 @@ class AppRepository {
);
});
}
Future<Either<Unit, WorkspaceError>> delete() {
final request = QueryAppRequest.create()..appIds.add(appId);
return WorkspaceEventDeleteApp(request).send();
}
Future<Either<Unit, WorkspaceError>> updateApp({String? name}) {
UpdateAppRequest request = UpdateAppRequest.create()..appId = appId;
if (name != null) {
request.name = name;
}
return WorkspaceEventUpdateApp(request).send();
}
}
class AppListenerRepository {

View File

@ -64,8 +64,7 @@ class WorkspaceRepo {
class WorkspaceListenerRepo {
StreamSubscription<SubscribeObject>? _subscription;
WorkspaceCreateAppCallback? _createApp;
WorkspaceDeleteAppCallback? _deleteApp;
WorkspaceAppsChangedCallback? _appsChanged;
WorkspaceUpdatedCallback? _update;
late WorkspaceNotificationParser _parser;
final UserProfile user;
@ -77,12 +76,10 @@ class WorkspaceListenerRepo {
});
void startListening({
WorkspaceCreateAppCallback? createApp,
WorkspaceDeleteAppCallback? deleteApp,
WorkspaceAppsChangedCallback? appsChanged,
WorkspaceUpdatedCallback? update,
}) {
_createApp = createApp;
_deleteApp = deleteApp;
_appsChanged = appsChanged;
_update = update;
_parser = WorkspaceNotificationParser(
@ -108,23 +105,13 @@ class WorkspaceListenerRepo {
);
}
break;
case WorkspaceNotification.WorkspaceCreateApp:
if (_createApp != null) {
case WorkspaceNotification.WorkspaceAppsChanged:
if (_appsChanged != null) {
result.fold(
(payload) => _createApp!(
(payload) => _appsChanged!(
left(RepeatedApp.fromBuffer(payload).items),
),
(error) => _createApp!(right(error)),
);
}
break;
case WorkspaceNotification.WorkspaceDeleteApp:
if (_deleteApp != null) {
result.fold(
(payload) => _deleteApp!(
left(RepeatedApp.fromBuffer(payload).items),
),
(error) => _deleteApp!(right(error)),
(error) => _appsChanged!(right(error)),
);
}
break;

View File

@ -1,3 +1,5 @@
import 'package:app_flowy/workspace/domain/edit_action/app_edit.dart';
import 'package:app_flowy/workspace/presentation/widgets/dialogs.dart';
import 'package:expandable/expandable.dart';
import 'package:flowy_infra/flowy_icon_data_icons.dart';
import 'package:flowy_infra/theme.dart';
@ -10,6 +12,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:app_flowy/workspace/application/app/app_bloc.dart';
import 'package:styled_widget/styled_widget.dart';
import 'package:dartz/dartz.dart';
import '../menu_app.dart';
import 'add_button.dart';
@ -67,13 +70,15 @@ class MenuAppHeader extends StatelessWidget {
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
// Open the document
ExpandableController.of(context, rebuildOnChange: false, required: true)?.toggle();
},
onSecondaryTap: () {
AppDisclosureActions(onSelected: (action) {
print(action);
}).show(context, context, anchorDirection: AnchorDirection.bottomWithCenterAligned);
final actionList = AppDisclosureActions(onSelected: (action) => _handleAction(context, action));
actionList.show(
context,
context,
anchorDirection: AnchorDirection.bottomWithCenterAligned,
);
},
child: FlowyText.medium(
app.name,
@ -90,4 +95,24 @@ class MenuAppHeader extends StatelessWidget {
},
).padding(right: MenuAppSizes.headerPadding);
}
void _handleAction(BuildContext context, Option<AppDisclosureAction> action) {
action.fold(() {}, (action) {
switch (action) {
case AppDisclosureAction.rename:
TextFieldDialog(
title: 'Rename',
value: context.read<AppBloc>().state.app.name,
confirm: (newValue) {
context.read<AppBloc>().add(AppEvent.rename(newValue));
},
).show(context);
break;
case AppDisclosureAction.delete:
context.read<AppBloc>().add(const AppEvent.delete());
break;
}
});
}
}

View File

@ -23,7 +23,7 @@ class MenuApp extends MenuItem {
providers: [
BlocProvider<AppBloc>(
create: (context) {
final appBloc = getIt<AppBloc>(param1: app.id);
final appBloc = getIt<AppBloc>(param1: app);
appBloc.add(const AppEvent.initial());
return appBloc;
},

View File

@ -119,7 +119,7 @@ class WorkspaceEventCreateApp {
}
class WorkspaceEventDeleteApp {
DeleteAppRequest request;
QueryAppRequest request;
WorkspaceEventDeleteApp(this.request);
Future<Either<Unit, WorkspaceError>> send() {

View File

@ -11,22 +11,17 @@ import 'package:protobuf/protobuf.dart' as $pb;
class QueryAppRequest extends $pb.GeneratedMessage {
static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'QueryAppRequest', createEmptyInstance: create)
..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'appId')
..aOB(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'isTrash')
..pPS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'appIds')
..hasRequiredFields = false
;
QueryAppRequest._() : super();
factory QueryAppRequest({
$core.String? appId,
$core.bool? isTrash,
$core.Iterable<$core.String>? appIds,
}) {
final _result = create();
if (appId != null) {
_result.appId = appId;
}
if (isTrash != null) {
_result.isTrash = isTrash;
if (appIds != null) {
_result.appIds.addAll(appIds);
}
return _result;
}
@ -52,22 +47,7 @@ class QueryAppRequest extends $pb.GeneratedMessage {
static QueryAppRequest? _defaultInstance;
@$pb.TagNumber(1)
$core.String get appId => $_getSZ(0);
@$pb.TagNumber(1)
set appId($core.String v) { $_setString(0, v); }
@$pb.TagNumber(1)
$core.bool hasAppId() => $_has(0);
@$pb.TagNumber(1)
void clearAppId() => clearField(1);
@$pb.TagNumber(2)
$core.bool get isTrash => $_getBF(1);
@$pb.TagNumber(2)
set isTrash($core.bool v) { $_setBool(1, v); }
@$pb.TagNumber(2)
$core.bool hasIsTrash() => $_has(1);
@$pb.TagNumber(2)
void clearIsTrash() => clearField(2);
$core.List<$core.String> get appIds => $_getList(0);
}
class AppIdentifier extends $pb.GeneratedMessage {

View File

@ -12,13 +12,12 @@ import 'dart:typed_data' as $typed_data;
const QueryAppRequest$json = const {
'1': 'QueryAppRequest',
'2': const [
const {'1': 'app_id', '3': 1, '4': 1, '5': 9, '10': 'appId'},
const {'1': 'is_trash', '3': 2, '4': 1, '5': 8, '10': 'isTrash'},
const {'1': 'app_ids', '3': 1, '4': 3, '5': 9, '10': 'appIds'},
],
};
/// Descriptor for `QueryAppRequest`. Decode as a `google.protobuf.DescriptorProto`.
final $typed_data.Uint8List queryAppRequestDescriptor = $convert.base64Decode('Cg9RdWVyeUFwcFJlcXVlc3QSFQoGYXBwX2lkGAEgASgJUgVhcHBJZBIZCghpc190cmFzaBgCIAEoCFIHaXNUcmFzaA==');
final $typed_data.Uint8List queryAppRequestDescriptor = $convert.base64Decode('Cg9RdWVyeUFwcFJlcXVlc3QSFwoHYXBwX2lkcxgBIAMoCVIGYXBwSWRz');
@$core.Deprecated('Use appIdentifierDescriptor instead')
const AppIdentifier$json = const {
'1': 'AppIdentifier',

View File

@ -14,9 +14,8 @@ class WorkspaceNotification extends $pb.ProtobufEnum {
static const WorkspaceNotification UserCreateWorkspace = WorkspaceNotification._(10, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UserCreateWorkspace');
static const WorkspaceNotification UserDeleteWorkspace = WorkspaceNotification._(11, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UserDeleteWorkspace');
static const WorkspaceNotification WorkspaceUpdated = WorkspaceNotification._(12, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'WorkspaceUpdated');
static const WorkspaceNotification WorkspaceCreateApp = WorkspaceNotification._(13, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'WorkspaceCreateApp');
static const WorkspaceNotification WorkspaceDeleteApp = WorkspaceNotification._(14, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'WorkspaceDeleteApp');
static const WorkspaceNotification WorkspaceListUpdated = WorkspaceNotification._(15, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'WorkspaceListUpdated');
static const WorkspaceNotification WorkspaceListUpdated = WorkspaceNotification._(13, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'WorkspaceListUpdated');
static const WorkspaceNotification WorkspaceAppsChanged = WorkspaceNotification._(14, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'WorkspaceAppsChanged');
static const WorkspaceNotification AppUpdated = WorkspaceNotification._(21, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'AppUpdated');
static const WorkspaceNotification AppViewsChanged = WorkspaceNotification._(24, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'AppViewsChanged');
static const WorkspaceNotification ViewUpdated = WorkspaceNotification._(31, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ViewUpdated');
@ -28,9 +27,8 @@ class WorkspaceNotification extends $pb.ProtobufEnum {
UserCreateWorkspace,
UserDeleteWorkspace,
WorkspaceUpdated,
WorkspaceCreateApp,
WorkspaceDeleteApp,
WorkspaceListUpdated,
WorkspaceAppsChanged,
AppUpdated,
AppViewsChanged,
ViewUpdated,

View File

@ -16,9 +16,8 @@ const WorkspaceNotification$json = const {
const {'1': 'UserCreateWorkspace', '2': 10},
const {'1': 'UserDeleteWorkspace', '2': 11},
const {'1': 'WorkspaceUpdated', '2': 12},
const {'1': 'WorkspaceCreateApp', '2': 13},
const {'1': 'WorkspaceDeleteApp', '2': 14},
const {'1': 'WorkspaceListUpdated', '2': 15},
const {'1': 'WorkspaceListUpdated', '2': 13},
const {'1': 'WorkspaceAppsChanged', '2': 14},
const {'1': 'AppUpdated', '2': 21},
const {'1': 'AppViewsChanged', '2': 24},
const {'1': 'ViewUpdated', '2': 31},
@ -28,4 +27,4 @@ const WorkspaceNotification$json = const {
};
/// Descriptor for `WorkspaceNotification`. Decode as a `google.protobuf.EnumDescriptorProto`.
final $typed_data.Uint8List workspaceNotificationDescriptor = $convert.base64Decode('ChVXb3Jrc3BhY2VOb3RpZmljYXRpb24SCwoHVW5rbm93bhAAEhcKE1VzZXJDcmVhdGVXb3Jrc3BhY2UQChIXChNVc2VyRGVsZXRlV29ya3NwYWNlEAsSFAoQV29ya3NwYWNlVXBkYXRlZBAMEhYKEldvcmtzcGFjZUNyZWF0ZUFwcBANEhYKEldvcmtzcGFjZURlbGV0ZUFwcBAOEhgKFFdvcmtzcGFjZUxpc3RVcGRhdGVkEA8SDgoKQXBwVXBkYXRlZBAVEhMKD0FwcFZpZXdzQ2hhbmdlZBAYEg8KC1ZpZXdVcGRhdGVkEB8SFAoQVXNlclVuYXV0aG9yaXplZBBkEhEKDFRyYXNoVXBkYXRlZBDoBw==');
final $typed_data.Uint8List workspaceNotificationDescriptor = $convert.base64Decode('ChVXb3Jrc3BhY2VOb3RpZmljYXRpb24SCwoHVW5rbm93bhAAEhcKE1VzZXJDcmVhdGVXb3Jrc3BhY2UQChIXChNVc2VyRGVsZXRlV29ya3NwYWNlEAsSFAoQV29ya3NwYWNlVXBkYXRlZBAMEhgKFFdvcmtzcGFjZUxpc3RVcGRhdGVkEA0SGAoUV29ya3NwYWNlQXBwc0NoYW5nZWQQDhIOCgpBcHBVcGRhdGVkEBUSEwoPQXBwVmlld3NDaGFuZ2VkEBgSDwoLVmlld1VwZGF0ZWQQHxIUChBVc2VyVW5hdXRob3JpemVkEGQSEQoMVHJhc2hVcGRhdGVkEOgH');

View File

@ -12,10 +12,12 @@ import 'package:protobuf/protobuf.dart' as $pb;
class TrashType extends $pb.ProtobufEnum {
static const TrashType Unknown = TrashType._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Unknown');
static const TrashType View = TrashType._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'View');
static const TrashType App = TrashType._(2, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'App');
static const $core.List<TrashType> values = <TrashType> [
Unknown,
View,
App,
];
static final $core.Map<$core.int, TrashType> _byValue = $pb.ProtobufEnum.initByValue(values);

View File

@ -14,11 +14,12 @@ const TrashType$json = const {
'2': const [
const {'1': 'Unknown', '2': 0},
const {'1': 'View', '2': 1},
const {'1': 'App', '2': 2},
],
};
/// Descriptor for `TrashType`. Decode as a `google.protobuf.EnumDescriptorProto`.
final $typed_data.Uint8List trashTypeDescriptor = $convert.base64Decode('CglUcmFzaFR5cGUSCwoHVW5rbm93bhAAEggKBFZpZXcQAQ==');
final $typed_data.Uint8List trashTypeDescriptor = $convert.base64Decode('CglUcmFzaFR5cGUSCwoHVW5rbm93bhAAEggKBFZpZXcQARIHCgNBcHAQAg==');
@$core.Deprecated('Use trashIdentifiersDescriptor instead')
const TrashIdentifiers$json = const {
'1': 'TrashIdentifiers',

View File

@ -46,6 +46,20 @@ impl std::convert::Into<App> for AppTable {
}
}
impl std::convert::Into<Trash> for AppTable {
fn into(self) -> Trash {
Trash {
id: self.id.to_string(),
name: self.name,
modified_time: self.modified_time.timestamp(),
create_time: self.create_time.timestamp(),
ty: TrashType::App,
unknown_fields: Default::default(),
cached_size: Default::default(),
}
}
}
#[derive(Debug, Clone, sqlx::FromRow)]
pub struct ViewTable {
pub(crate) id: uuid::Uuid,

View File

@ -4,6 +4,7 @@ use crate::{
sqlx_ext::{map_sqlx_error, DBTransaction, SqlBuilder},
};
use crate::service::trash::read_trash_ids;
use chrono::Utc;
use flowy_net::errors::{invalid_params, ServerError};
use flowy_workspace::{
@ -44,6 +45,26 @@ pub(crate) async fn read_app(
app_id: Uuid,
user: &LoggedUser,
) -> Result<App, ServerError> {
let table = read_app_table(app_id, transaction).await?;
let read_trash_ids = read_trash_ids(user, transaction).await?;
if read_trash_ids.contains(&table.id.to_string()) {
return Err(ServerError::record_not_found());
}
let mut views = RepeatedView::default();
views.set_items(
read_view_belong_to_id(&table.id.to_string(), user, transaction as &mut DBTransaction<'_>)
.await?
.into(),
);
let mut app: App = table.into();
app.set_belongings(views);
Ok(app)
}
pub(crate) async fn read_app_table(app_id: Uuid, transaction: &mut DBTransaction<'_>) -> Result<AppTable, ServerError> {
let (sql, args) = SqlBuilder::select(APP_TABLE)
.add_field("*")
.and_where_eq("id", app_id)
@ -54,16 +75,7 @@ pub(crate) async fn read_app(
.await
.map_err(map_sqlx_error)?;
let mut views = RepeatedView::default();
views.set_items(
read_view_belong_to_id(user, transaction as &mut DBTransaction<'_>, &table.id.to_string())
.await?
.into(),
);
let mut app: App = table.into();
app.set_belongings(views);
Ok(app)
Ok(table)
}
pub(crate) async fn update_app(

View File

@ -1,6 +1,7 @@
use crate::{
entities::workspace::{TrashTable, TRASH_TABLE},
service::{
app::app::{delete_app, read_app_table},
user::LoggedUser,
view::{delete_view, read_view_table},
},
@ -82,16 +83,6 @@ pub(crate) async fn delete_trash(
.await
.map_err(map_sqlx_error)?;
match TrashType::from_i32(trash_table.ty) {
None => log::error!("Parser trash type with value: {} failed", trash_table.ty),
Some(ty) => match ty {
TrashType::Unknown => {},
TrashType::View => {
let _ = delete_view(transaction as &mut DBTransaction<'_>, vec![trash_table.id]).await;
},
},
}
let _ = delete_trash_targets(
transaction as &mut DBTransaction<'_>,
vec![(trash_table.id.clone(), trash_table.ty)],
@ -120,6 +111,9 @@ async fn delete_trash_targets(
TrashType::View => {
let _ = delete_view(transaction as &mut DBTransaction<'_>, vec![id]).await;
},
TrashType::App => {
let _ = delete_app(transaction as &mut DBTransaction<'_>, id).await;
},
},
}
}
@ -163,6 +157,9 @@ pub(crate) async fn read_trash(
TrashType::View => {
trash.push(read_view_table(table.id, transaction).await?.into());
},
TrashType::App => {
trash.push(read_app_table(table.id, transaction).await?.into());
},
},
}
}

View File

@ -100,7 +100,7 @@ pub(crate) async fn read_view(
let mut views = RepeatedView::default();
views.set_items(
read_view_belong_to_id(&user, transaction, &table.id.to_string())
read_view_belong_to_id(&table.id.to_string(), &user, transaction)
.await?
.into(),
);
@ -128,9 +128,9 @@ pub(crate) async fn read_view_table(
// transaction must be commit from caller
pub(crate) async fn read_view_belong_to_id<'c>(
id: &str,
user: &LoggedUser,
transaction: &mut DBTransaction<'_>,
id: &str,
) -> Result<Vec<View>, ServerError> {
// TODO: add index for app_table
let (sql, args) = SqlBuilder::select(VIEW_TABLE)

View File

@ -1,6 +1,6 @@
use crate::helper::*;
use flowy_workspace::entities::{
app::{AppIdentifier, DeleteAppParams, UpdateAppParams},
app::{AppIdentifier, UpdateAppParams},
trash::{TrashIdentifier, TrashIdentifiers, TrashType},
view::{UpdateViewParams, ViewIdentifier},
workspace::{CreateWorkspaceParams, DeleteWorkspaceParams, QueryWorkspaceParams, UpdateWorkspaceParams},
@ -123,7 +123,7 @@ async fn app_update() {
async fn app_delete() {
let test = AppTest::new().await;
let delete_params = DeleteAppParams {
let delete_params = AppIdentifier {
app_id: test.app.id.clone(),
};
test.server.delete_app(delete_params).await;

View File

@ -95,7 +95,7 @@ impl TestUserServer {
update_app_request(self.user_token(), params, &url).await.unwrap();
}
pub async fn delete_app(&self, params: DeleteAppParams) {
pub async fn delete_app(&self, params: AppIdentifier) {
let url = format!("{}/api/app", self.http_addr());
delete_app_request(self.user_token(), params, &url).await.unwrap();
}

View File

@ -25,8 +25,6 @@ pub fn category_from_str(type_str: &str) -> TypeCategory {
| "RepeatedApp"
| "UpdateAppRequest"
| "UpdateAppParams"
| "DeleteAppRequest"
| "DeleteAppParams"
| "UpdateWorkspaceRequest"
| "UpdateWorkspaceParams"
| "DeleteWorkspaceRequest"

View File

@ -172,7 +172,7 @@ pub async fn create_app(sdk: &FlowyTestSDK, name: &str, desc: &str, workspace_id
}
pub async fn delete_app(sdk: &FlowyTestSDK, app_id: &str) {
let delete_app_request = DeleteAppRequest {
let delete_app_request = AppIdentifier {
app_id: app_id.to_string(),
};

View File

@ -1,27 +0,0 @@
use crate::{entities::app::parser::AppId, errors::WorkspaceError};
use flowy_derive::ProtoBuf;
use std::convert::TryInto;
#[derive(Default, ProtoBuf)]
pub struct DeleteAppRequest {
#[pb(index = 1)]
pub app_id: String,
}
#[derive(Default, ProtoBuf, Clone)]
pub struct DeleteAppParams {
#[pb(index = 1)]
pub app_id: String,
}
impl TryInto<DeleteAppParams> for DeleteAppRequest {
type Error = WorkspaceError;
fn try_into(self) -> Result<DeleteAppParams, Self::Error> {
let app_id = AppId::parse(self.app_id)
.map_err(|e| WorkspaceError::app_id().context(e))?
.0;
Ok(DeleteAppParams { app_id })
}
}

View File

@ -5,19 +5,7 @@ use std::convert::TryInto;
#[derive(Default, ProtoBuf, Clone)]
pub struct QueryAppRequest {
#[pb(index = 1)]
pub app_id: String,
#[pb(index = 2)]
pub is_trash: bool,
}
impl QueryAppRequest {
pub fn new(app_id: &str) -> Self {
QueryAppRequest {
app_id: app_id.to_string(),
is_trash: false,
}
}
pub app_ids: Vec<String>,
}
#[derive(ProtoBuf, Default, Clone, Debug)]
@ -30,7 +18,6 @@ impl AppIdentifier {
pub fn new(app_id: &str) -> Self {
Self {
app_id: app_id.to_string(),
..Default::default()
}
}
}
@ -39,10 +26,15 @@ impl TryInto<AppIdentifier> for QueryAppRequest {
type Error = WorkspaceError;
fn try_into(self) -> Result<AppIdentifier, Self::Error> {
let app_id = AppId::parse(self.app_id)
.map_err(|e| WorkspaceError::app_id().context(e))?
.0;
debug_assert!(self.app_ids.len() == 1);
if self.app_ids.len() != 1 {
return Err(WorkspaceError::invalid_view_id().context("The len of app_ids should be equal to 1"));
}
let app_id = self.app_ids.first().unwrap().clone();
let app_id = AppId::parse(app_id)
.map_err(|e| WorkspaceError::invalid_app_id().context(e))?
.0;
Ok(AppIdentifier { app_id })
}
}

View File

@ -73,7 +73,7 @@ impl TryInto<UpdateAppParams> for UpdateAppRequest {
fn try_into(self) -> Result<UpdateAppParams, Self::Error> {
let app_id = AppId::parse(self.app_id)
.map_err(|e| WorkspaceError::app_id().context(e))?
.map_err(|e| WorkspaceError::invalid_app_id().context(e))?
.0;
let name = match self.name {

View File

@ -2,10 +2,8 @@ pub use app_create::*;
pub use app_update::*;
mod app_create;
mod app_delete;
mod app_query;
mod app_update;
pub mod parser;
pub use app_delete::*;
pub use app_query::*;

View File

@ -6,6 +6,7 @@ use std::fmt::Formatter;
pub enum TrashType {
Unknown = 0,
View = 1,
App = 2,
}
impl std::convert::TryFrom<i32> for TrashType {
@ -15,6 +16,7 @@ impl std::convert::TryFrom<i32> for TrashType {
match value {
0 => Ok(TrashType::Unknown),
1 => Ok(TrashType::View),
2 => Ok(TrashType::App),
_ => Err(format!("Invalid trash type: {}", value)),
}
}

View File

@ -98,7 +98,7 @@ impl TryInto<CreateViewParams> for CreateViewRequest {
.0;
let belong_to_id = AppId::parse(self.belong_to_id)
.map_err(|e| WorkspaceError::app_id().context(e))?
.map_err(|e| WorkspaceError::invalid_app_id().context(e))?
.0;
let thumbnail = match self.thumbnail {

View File

@ -42,7 +42,7 @@ impl WorkspaceError {
static_workspace_error!(color_style, ErrorCode::AppColorStyleInvalid);
static_workspace_error!(workspace_desc, ErrorCode::WorkspaceDescInvalid);
static_workspace_error!(app_name, ErrorCode::AppNameInvalid);
static_workspace_error!(app_id, ErrorCode::AppIdInvalid);
static_workspace_error!(invalid_app_id, ErrorCode::AppIdInvalid);
static_workspace_error!(view_name, ErrorCode::ViewNameInvalid);
static_workspace_error!(view_thumbnail, ErrorCode::ViewThumbnailInvalid);
static_workspace_error!(invalid_view_id, ErrorCode::ViewIdInvalid);

View File

@ -25,7 +25,7 @@ pub enum WorkspaceEvent {
#[event(input = "CreateAppRequest", output = "App")]
CreateApp = 101,
#[event(input = "DeleteAppRequest")]
#[event(input = "QueryAppRequest")]
DeleteApp = 102,
#[event(input = "QueryAppRequest", output = "App")]

View File

@ -1,17 +1,18 @@
use crate::{
entities::app::{
App,
AppIdentifier,
CreateAppParams,
CreateAppRequest,
DeleteAppParams,
DeleteAppRequest,
QueryAppRequest,
UpdateAppParams,
UpdateAppRequest,
entities::{
app::{
App,
AppIdentifier,
CreateAppParams,
CreateAppRequest,
QueryAppRequest,
UpdateAppParams,
UpdateAppRequest,
},
trash::Trash,
},
errors::WorkspaceError,
services::{AppController, ViewController},
services::{AppController, TrashCan, ViewController},
};
use flowy_dispatch::prelude::{data_result, Data, DataResult, Unit};
use std::{convert::TryInto, sync::Arc};
@ -27,13 +28,20 @@ pub(crate) async fn create_app_handler(
data_result(detail)
}
#[tracing::instrument(skip(data, controller))]
#[tracing::instrument(skip(data, controller, trash_can))]
pub(crate) async fn delete_app_handler(
data: Data<DeleteAppRequest>,
data: Data<QueryAppRequest>,
controller: Unit<Arc<AppController>>,
trash_can: Unit<Arc<TrashCan>>,
) -> Result<(), WorkspaceError> {
let params: DeleteAppParams = data.into_inner().try_into()?;
let _ = controller.delete_app(&params.app_id).await?;
let params: AppIdentifier = data.into_inner().try_into()?;
let trash = controller
.read_app_tables(vec![params.app_id])?
.into_iter()
.map(|view_table| view_table.into())
.collect::<Vec<Trash>>();
let _ = trash_can.add(trash).await?;
Ok(())
}

View File

@ -48,7 +48,12 @@ pub fn mk_workspace(
flowy_document,
));
let app_controller = Arc::new(AppController::new(user.clone(), database.clone(), server.clone()));
let app_controller = Arc::new(AppController::new(
user.clone(),
database.clone(),
trash_can.clone(),
server.clone(),
));
let workspace_controller = Arc::new(WorkspaceController::new(
user.clone(),

View File

@ -8,9 +8,8 @@ pub(crate) enum WorkspaceNotification {
UserCreateWorkspace = 10,
UserDeleteWorkspace = 11,
WorkspaceUpdated = 12,
WorkspaceCreateApp = 13,
WorkspaceDeleteApp = 14,
WorkspaceListUpdated = 15,
WorkspaceListUpdated = 13,
WorkspaceAppsChanged = 14,
AppUpdated = 21,
AppViewsChanged = 24,
ViewUpdated = 31,

View File

@ -26,8 +26,7 @@
#[derive(PartialEq,Clone,Default)]
pub struct QueryAppRequest {
// message fields
pub app_id: ::std::string::String,
pub is_trash: bool,
pub app_ids: ::protobuf::RepeatedField<::std::string::String>,
// special fields
pub unknown_fields: ::protobuf::UnknownFields,
pub cached_size: ::protobuf::CachedSize,
@ -44,45 +43,29 @@ impl QueryAppRequest {
::std::default::Default::default()
}
// string app_id = 1;
// repeated string app_ids = 1;
pub fn get_app_id(&self) -> &str {
&self.app_id
pub fn get_app_ids(&self) -> &[::std::string::String] {
&self.app_ids
}
pub fn clear_app_id(&mut self) {
self.app_id.clear();
pub fn clear_app_ids(&mut self) {
self.app_ids.clear();
}
// Param is passed by value, moved
pub fn set_app_id(&mut self, v: ::std::string::String) {
self.app_id = v;
pub fn set_app_ids(&mut self, v: ::protobuf::RepeatedField<::std::string::String>) {
self.app_ids = v;
}
// Mutable pointer to the field.
// If field is not initialized, it is initialized with default value first.
pub fn mut_app_id(&mut self) -> &mut ::std::string::String {
&mut self.app_id
pub fn mut_app_ids(&mut self) -> &mut ::protobuf::RepeatedField<::std::string::String> {
&mut self.app_ids
}
// Take field
pub fn take_app_id(&mut self) -> ::std::string::String {
::std::mem::replace(&mut self.app_id, ::std::string::String::new())
}
// bool is_trash = 2;
pub fn get_is_trash(&self) -> bool {
self.is_trash
}
pub fn clear_is_trash(&mut self) {
self.is_trash = false;
}
// Param is passed by value, moved
pub fn set_is_trash(&mut self, v: bool) {
self.is_trash = v;
pub fn take_app_ids(&mut self) -> ::protobuf::RepeatedField<::std::string::String> {
::std::mem::replace(&mut self.app_ids, ::protobuf::RepeatedField::new())
}
}
@ -96,14 +79,7 @@ impl ::protobuf::Message for QueryAppRequest {
let (field_number, wire_type) = is.read_tag_unpack()?;
match field_number {
1 => {
::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.app_id)?;
},
2 => {
if wire_type != ::protobuf::wire_format::WireTypeVarint {
return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type));
}
let tmp = is.read_bool()?;
self.is_trash = tmp;
::protobuf::rt::read_repeated_string_into(wire_type, is, &mut self.app_ids)?;
},
_ => {
::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
@ -117,24 +93,18 @@ impl ::protobuf::Message for QueryAppRequest {
#[allow(unused_variables)]
fn compute_size(&self) -> u32 {
let mut my_size = 0;
if !self.app_id.is_empty() {
my_size += ::protobuf::rt::string_size(1, &self.app_id);
}
if self.is_trash != false {
my_size += 2;
}
for value in &self.app_ids {
my_size += ::protobuf::rt::string_size(1, &value);
};
my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
self.cached_size.set(my_size);
my_size
}
fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> {
if !self.app_id.is_empty() {
os.write_string(1, &self.app_id)?;
}
if self.is_trash != false {
os.write_bool(2, self.is_trash)?;
}
for v in &self.app_ids {
os.write_string(1, &v)?;
};
os.write_unknown_fields(self.get_unknown_fields())?;
::std::result::Result::Ok(())
}
@ -173,15 +143,10 @@ impl ::protobuf::Message for QueryAppRequest {
static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT;
descriptor.get(|| {
let mut fields = ::std::vec::Vec::new();
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
"app_id",
|m: &QueryAppRequest| { &m.app_id },
|m: &mut QueryAppRequest| { &mut m.app_id },
));
fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeBool>(
"is_trash",
|m: &QueryAppRequest| { &m.is_trash },
|m: &mut QueryAppRequest| { &mut m.is_trash },
fields.push(::protobuf::reflect::accessor::make_repeated_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
"app_ids",
|m: &QueryAppRequest| { &m.app_ids },
|m: &mut QueryAppRequest| { &mut m.app_ids },
));
::protobuf::reflect::MessageDescriptor::new_pb_name::<QueryAppRequest>(
"QueryAppRequest",
@ -199,8 +164,7 @@ impl ::protobuf::Message for QueryAppRequest {
impl ::protobuf::Clear for QueryAppRequest {
fn clear(&mut self) {
self.app_id.clear();
self.is_trash = false;
self.app_ids.clear();
self.unknown_fields.clear();
}
}
@ -377,21 +341,18 @@ impl ::protobuf::reflect::ProtobufValue for AppIdentifier {
}
static file_descriptor_proto_data: &'static [u8] = b"\
\n\x0fapp_query.proto\"C\n\x0fQueryAppRequest\x12\x15\n\x06app_id\x18\
\x01\x20\x01(\tR\x05appId\x12\x19\n\x08is_trash\x18\x02\x20\x01(\x08R\
\x07isTrash\"&\n\rAppIdentifier\x12\x15\n\x06app_id\x18\x01\x20\x01(\tR\
\x05appIdJ\xe7\x01\n\x06\x12\x04\0\0\x08\x01\n\x08\n\x01\x0c\x12\x03\0\0\
\x12\n\n\n\x02\x04\0\x12\x04\x02\0\x05\x01\n\n\n\x03\x04\0\x01\x12\x03\
\x02\x08\x17\n\x0b\n\x04\x04\0\x02\0\x12\x03\x03\x04\x16\n\x0c\n\x05\x04\
\0\x02\0\x05\x12\x03\x03\x04\n\n\x0c\n\x05\x04\0\x02\0\x01\x12\x03\x03\
\x0b\x11\n\x0c\n\x05\x04\0\x02\0\x03\x12\x03\x03\x14\x15\n\x0b\n\x04\x04\
\0\x02\x01\x12\x03\x04\x04\x16\n\x0c\n\x05\x04\0\x02\x01\x05\x12\x03\x04\
\x04\x08\n\x0c\n\x05\x04\0\x02\x01\x01\x12\x03\x04\t\x11\n\x0c\n\x05\x04\
\0\x02\x01\x03\x12\x03\x04\x14\x15\n\n\n\x02\x04\x01\x12\x04\x06\0\x08\
\x01\n\n\n\x03\x04\x01\x01\x12\x03\x06\x08\x15\n\x0b\n\x04\x04\x01\x02\0\
\x12\x03\x07\x04\x16\n\x0c\n\x05\x04\x01\x02\0\x05\x12\x03\x07\x04\n\n\
\x0c\n\x05\x04\x01\x02\0\x01\x12\x03\x07\x0b\x11\n\x0c\n\x05\x04\x01\x02\
\0\x03\x12\x03\x07\x14\x15b\x06proto3\
\n\x0fapp_query.proto\"*\n\x0fQueryAppRequest\x12\x17\n\x07app_ids\x18\
\x01\x20\x03(\tR\x06appIds\"&\n\rAppIdentifier\x12\x15\n\x06app_id\x18\
\x01\x20\x01(\tR\x05appIdJ\xbe\x01\n\x06\x12\x04\0\0\x07\x01\n\x08\n\x01\
\x0c\x12\x03\0\0\x12\n\n\n\x02\x04\0\x12\x04\x02\0\x04\x01\n\n\n\x03\x04\
\0\x01\x12\x03\x02\x08\x17\n\x0b\n\x04\x04\0\x02\0\x12\x03\x03\x04\x20\n\
\x0c\n\x05\x04\0\x02\0\x04\x12\x03\x03\x04\x0c\n\x0c\n\x05\x04\0\x02\0\
\x05\x12\x03\x03\r\x13\n\x0c\n\x05\x04\0\x02\0\x01\x12\x03\x03\x14\x1b\n\
\x0c\n\x05\x04\0\x02\0\x03\x12\x03\x03\x1e\x1f\n\n\n\x02\x04\x01\x12\x04\
\x05\0\x07\x01\n\n\n\x03\x04\x01\x01\x12\x03\x05\x08\x15\n\x0b\n\x04\x04\
\x01\x02\0\x12\x03\x06\x04\x16\n\x0c\n\x05\x04\x01\x02\0\x05\x12\x03\x06\
\x04\n\n\x0c\n\x05\x04\x01\x02\0\x01\x12\x03\x06\x0b\x11\n\x0c\n\x05\x04\
\x01\x02\0\x03\x12\x03\x06\x14\x15b\x06proto3\
";
static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

View File

@ -29,9 +29,8 @@ pub enum WorkspaceNotification {
UserCreateWorkspace = 10,
UserDeleteWorkspace = 11,
WorkspaceUpdated = 12,
WorkspaceCreateApp = 13,
WorkspaceDeleteApp = 14,
WorkspaceListUpdated = 15,
WorkspaceListUpdated = 13,
WorkspaceAppsChanged = 14,
AppUpdated = 21,
AppViewsChanged = 24,
ViewUpdated = 31,
@ -50,9 +49,8 @@ impl ::protobuf::ProtobufEnum for WorkspaceNotification {
10 => ::std::option::Option::Some(WorkspaceNotification::UserCreateWorkspace),
11 => ::std::option::Option::Some(WorkspaceNotification::UserDeleteWorkspace),
12 => ::std::option::Option::Some(WorkspaceNotification::WorkspaceUpdated),
13 => ::std::option::Option::Some(WorkspaceNotification::WorkspaceCreateApp),
14 => ::std::option::Option::Some(WorkspaceNotification::WorkspaceDeleteApp),
15 => ::std::option::Option::Some(WorkspaceNotification::WorkspaceListUpdated),
13 => ::std::option::Option::Some(WorkspaceNotification::WorkspaceListUpdated),
14 => ::std::option::Option::Some(WorkspaceNotification::WorkspaceAppsChanged),
21 => ::std::option::Option::Some(WorkspaceNotification::AppUpdated),
24 => ::std::option::Option::Some(WorkspaceNotification::AppViewsChanged),
31 => ::std::option::Option::Some(WorkspaceNotification::ViewUpdated),
@ -68,9 +66,8 @@ impl ::protobuf::ProtobufEnum for WorkspaceNotification {
WorkspaceNotification::UserCreateWorkspace,
WorkspaceNotification::UserDeleteWorkspace,
WorkspaceNotification::WorkspaceUpdated,
WorkspaceNotification::WorkspaceCreateApp,
WorkspaceNotification::WorkspaceDeleteApp,
WorkspaceNotification::WorkspaceListUpdated,
WorkspaceNotification::WorkspaceAppsChanged,
WorkspaceNotification::AppUpdated,
WorkspaceNotification::AppViewsChanged,
WorkspaceNotification::ViewUpdated,
@ -104,40 +101,37 @@ impl ::protobuf::reflect::ProtobufValue for WorkspaceNotification {
}
static file_descriptor_proto_data: &'static [u8] = b"\
\n\x10observable.proto*\x95\x02\n\x15WorkspaceNotification\x12\x0b\n\x07\
\n\x10observable.proto*\xff\x01\n\x15WorkspaceNotification\x12\x0b\n\x07\
Unknown\x10\0\x12\x17\n\x13UserCreateWorkspace\x10\n\x12\x17\n\x13UserDe\
leteWorkspace\x10\x0b\x12\x14\n\x10WorkspaceUpdated\x10\x0c\x12\x16\n\
\x12WorkspaceCreateApp\x10\r\x12\x16\n\x12WorkspaceDeleteApp\x10\x0e\x12\
\x18\n\x14WorkspaceListUpdated\x10\x0f\x12\x0e\n\nAppUpdated\x10\x15\x12\
\x13\n\x0fAppViewsChanged\x10\x18\x12\x0f\n\x0bViewUpdated\x10\x1f\x12\
\x14\n\x10UserUnauthorized\x10d\x12\x11\n\x0cTrashUpdated\x10\xe8\x07J\
\x96\x04\n\x06\x12\x04\0\0\x0f\x01\n\x08\n\x01\x0c\x12\x03\0\0\x12\n\n\n\
\x02\x05\0\x12\x04\x02\0\x0f\x01\n\n\n\x03\x05\0\x01\x12\x03\x02\x05\x1a\
\n\x0b\n\x04\x05\0\x02\0\x12\x03\x03\x04\x10\n\x0c\n\x05\x05\0\x02\0\x01\
\x12\x03\x03\x04\x0b\n\x0c\n\x05\x05\0\x02\0\x02\x12\x03\x03\x0e\x0f\n\
\x0b\n\x04\x05\0\x02\x01\x12\x03\x04\x04\x1d\n\x0c\n\x05\x05\0\x02\x01\
\x01\x12\x03\x04\x04\x17\n\x0c\n\x05\x05\0\x02\x01\x02\x12\x03\x04\x1a\
\x1c\n\x0b\n\x04\x05\0\x02\x02\x12\x03\x05\x04\x1d\n\x0c\n\x05\x05\0\x02\
\x02\x01\x12\x03\x05\x04\x17\n\x0c\n\x05\x05\0\x02\x02\x02\x12\x03\x05\
\x1a\x1c\n\x0b\n\x04\x05\0\x02\x03\x12\x03\x06\x04\x1a\n\x0c\n\x05\x05\0\
\x02\x03\x01\x12\x03\x06\x04\x14\n\x0c\n\x05\x05\0\x02\x03\x02\x12\x03\
\x06\x17\x19\n\x0b\n\x04\x05\0\x02\x04\x12\x03\x07\x04\x1c\n\x0c\n\x05\
\x05\0\x02\x04\x01\x12\x03\x07\x04\x16\n\x0c\n\x05\x05\0\x02\x04\x02\x12\
\x03\x07\x19\x1b\n\x0b\n\x04\x05\0\x02\x05\x12\x03\x08\x04\x1c\n\x0c\n\
\x05\x05\0\x02\x05\x01\x12\x03\x08\x04\x16\n\x0c\n\x05\x05\0\x02\x05\x02\
\x12\x03\x08\x19\x1b\n\x0b\n\x04\x05\0\x02\x06\x12\x03\t\x04\x1e\n\x0c\n\
\x05\x05\0\x02\x06\x01\x12\x03\t\x04\x18\n\x0c\n\x05\x05\0\x02\x06\x02\
\x12\x03\t\x1b\x1d\n\x0b\n\x04\x05\0\x02\x07\x12\x03\n\x04\x14\n\x0c\n\
\x05\x05\0\x02\x07\x01\x12\x03\n\x04\x0e\n\x0c\n\x05\x05\0\x02\x07\x02\
\x12\x03\n\x11\x13\n\x0b\n\x04\x05\0\x02\x08\x12\x03\x0b\x04\x19\n\x0c\n\
\x05\x05\0\x02\x08\x01\x12\x03\x0b\x04\x13\n\x0c\n\x05\x05\0\x02\x08\x02\
\x12\x03\x0b\x16\x18\n\x0b\n\x04\x05\0\x02\t\x12\x03\x0c\x04\x15\n\x0c\n\
\x05\x05\0\x02\t\x01\x12\x03\x0c\x04\x0f\n\x0c\n\x05\x05\0\x02\t\x02\x12\
\x03\x0c\x12\x14\n\x0b\n\x04\x05\0\x02\n\x12\x03\r\x04\x1b\n\x0c\n\x05\
\x05\0\x02\n\x01\x12\x03\r\x04\x14\n\x0c\n\x05\x05\0\x02\n\x02\x12\x03\r\
\x17\x1a\n\x0b\n\x04\x05\0\x02\x0b\x12\x03\x0e\x04\x18\n\x0c\n\x05\x05\0\
\x02\x0b\x01\x12\x03\x0e\x04\x10\n\x0c\n\x05\x05\0\x02\x0b\x02\x12\x03\
\x0e\x13\x17b\x06proto3\
leteWorkspace\x10\x0b\x12\x14\n\x10WorkspaceUpdated\x10\x0c\x12\x18\n\
\x14WorkspaceListUpdated\x10\r\x12\x18\n\x14WorkspaceAppsChanged\x10\x0e\
\x12\x0e\n\nAppUpdated\x10\x15\x12\x13\n\x0fAppViewsChanged\x10\x18\x12\
\x0f\n\x0bViewUpdated\x10\x1f\x12\x14\n\x10UserUnauthorized\x10d\x12\x11\
\n\x0cTrashUpdated\x10\xe8\x07J\xed\x03\n\x06\x12\x04\0\0\x0e\x01\n\x08\
\n\x01\x0c\x12\x03\0\0\x12\n\n\n\x02\x05\0\x12\x04\x02\0\x0e\x01\n\n\n\
\x03\x05\0\x01\x12\x03\x02\x05\x1a\n\x0b\n\x04\x05\0\x02\0\x12\x03\x03\
\x04\x10\n\x0c\n\x05\x05\0\x02\0\x01\x12\x03\x03\x04\x0b\n\x0c\n\x05\x05\
\0\x02\0\x02\x12\x03\x03\x0e\x0f\n\x0b\n\x04\x05\0\x02\x01\x12\x03\x04\
\x04\x1d\n\x0c\n\x05\x05\0\x02\x01\x01\x12\x03\x04\x04\x17\n\x0c\n\x05\
\x05\0\x02\x01\x02\x12\x03\x04\x1a\x1c\n\x0b\n\x04\x05\0\x02\x02\x12\x03\
\x05\x04\x1d\n\x0c\n\x05\x05\0\x02\x02\x01\x12\x03\x05\x04\x17\n\x0c\n\
\x05\x05\0\x02\x02\x02\x12\x03\x05\x1a\x1c\n\x0b\n\x04\x05\0\x02\x03\x12\
\x03\x06\x04\x1a\n\x0c\n\x05\x05\0\x02\x03\x01\x12\x03\x06\x04\x14\n\x0c\
\n\x05\x05\0\x02\x03\x02\x12\x03\x06\x17\x19\n\x0b\n\x04\x05\0\x02\x04\
\x12\x03\x07\x04\x1e\n\x0c\n\x05\x05\0\x02\x04\x01\x12\x03\x07\x04\x18\n\
\x0c\n\x05\x05\0\x02\x04\x02\x12\x03\x07\x1b\x1d\n\x0b\n\x04\x05\0\x02\
\x05\x12\x03\x08\x04\x1e\n\x0c\n\x05\x05\0\x02\x05\x01\x12\x03\x08\x04\
\x18\n\x0c\n\x05\x05\0\x02\x05\x02\x12\x03\x08\x1b\x1d\n\x0b\n\x04\x05\0\
\x02\x06\x12\x03\t\x04\x14\n\x0c\n\x05\x05\0\x02\x06\x01\x12\x03\t\x04\
\x0e\n\x0c\n\x05\x05\0\x02\x06\x02\x12\x03\t\x11\x13\n\x0b\n\x04\x05\0\
\x02\x07\x12\x03\n\x04\x19\n\x0c\n\x05\x05\0\x02\x07\x01\x12\x03\n\x04\
\x13\n\x0c\n\x05\x05\0\x02\x07\x02\x12\x03\n\x16\x18\n\x0b\n\x04\x05\0\
\x02\x08\x12\x03\x0b\x04\x15\n\x0c\n\x05\x05\0\x02\x08\x01\x12\x03\x0b\
\x04\x0f\n\x0c\n\x05\x05\0\x02\x08\x02\x12\x03\x0b\x12\x14\n\x0b\n\x04\
\x05\0\x02\t\x12\x03\x0c\x04\x1b\n\x0c\n\x05\x05\0\x02\t\x01\x12\x03\x0c\
\x04\x14\n\x0c\n\x05\x05\0\x02\t\x02\x12\x03\x0c\x17\x1a\n\x0b\n\x04\x05\
\0\x02\n\x12\x03\r\x04\x18\n\x0c\n\x05\x05\0\x02\n\x01\x12\x03\r\x04\x10\
\n\x0c\n\x05\x05\0\x02\n\x02\x12\x03\r\x13\x17b\x06proto3\
";
static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

View File

@ -886,6 +886,7 @@ impl ::protobuf::reflect::ProtobufValue for RepeatedTrash {
pub enum TrashType {
Unknown = 0,
View = 1,
App = 2,
}
impl ::protobuf::ProtobufEnum for TrashType {
@ -897,6 +898,7 @@ impl ::protobuf::ProtobufEnum for TrashType {
match value {
0 => ::std::option::Option::Some(TrashType::Unknown),
1 => ::std::option::Option::Some(TrashType::View),
2 => ::std::option::Option::Some(TrashType::App),
_ => ::std::option::Option::None
}
}
@ -905,6 +907,7 @@ impl ::protobuf::ProtobufEnum for TrashType {
static values: &'static [TrashType] = &[
TrashType::Unknown,
TrashType::View,
TrashType::App,
];
values
}
@ -942,47 +945,49 @@ static file_descriptor_proto_data: &'static [u8] = b"\
me\x18\x03\x20\x01(\x03R\x0cmodifiedTime\x12\x1f\n\x0bcreate_time\x18\
\x04\x20\x01(\x03R\ncreateTime\x12\x1a\n\x02ty\x18\x05\x20\x01(\x0e2\n.T\
rashTypeR\x02ty\"-\n\rRepeatedTrash\x12\x1c\n\x05items\x18\x01\x20\x03(\
\x0b2\x06.TrashR\x05items*\"\n\tTrashType\x12\x0b\n\x07Unknown\x10\0\x12\
\x08\n\x04View\x10\x01J\x9e\x06\n\x06\x12\x04\0\0\x17\x01\n\x08\n\x01\
\x0c\x12\x03\0\0\x12\n\n\n\x02\x04\0\x12\x04\x02\0\x05\x01\n\n\n\x03\x04\
\0\x01\x12\x03\x02\x08\x18\n\x0b\n\x04\x04\0\x02\0\x12\x03\x03\x04'\n\
\x0c\n\x05\x04\0\x02\0\x04\x12\x03\x03\x04\x0c\n\x0c\n\x05\x04\0\x02\0\
\x06\x12\x03\x03\r\x1c\n\x0c\n\x05\x04\0\x02\0\x01\x12\x03\x03\x1d\"\n\
\x0c\n\x05\x04\0\x02\0\x03\x12\x03\x03%&\n\x0b\n\x04\x04\0\x02\x01\x12\
\x03\x04\x04\x18\n\x0c\n\x05\x04\0\x02\x01\x05\x12\x03\x04\x04\x08\n\x0c\
\n\x05\x04\0\x02\x01\x01\x12\x03\x04\t\x13\n\x0c\n\x05\x04\0\x02\x01\x03\
\x12\x03\x04\x16\x17\n\n\n\x02\x04\x01\x12\x04\x06\0\t\x01\n\n\n\x03\x04\
\x01\x01\x12\x03\x06\x08\x17\n\x0b\n\x04\x04\x01\x02\0\x12\x03\x07\x04\
\x12\n\x0c\n\x05\x04\x01\x02\0\x05\x12\x03\x07\x04\n\n\x0c\n\x05\x04\x01\
\x02\0\x01\x12\x03\x07\x0b\r\n\x0c\n\x05\x04\x01\x02\0\x03\x12\x03\x07\
\x10\x11\n\x0b\n\x04\x04\x01\x02\x01\x12\x03\x08\x04\x15\n\x0c\n\x05\x04\
\x01\x02\x01\x06\x12\x03\x08\x04\r\n\x0c\n\x05\x04\x01\x02\x01\x01\x12\
\x03\x08\x0e\x10\n\x0c\n\x05\x04\x01\x02\x01\x03\x12\x03\x08\x13\x14\n\n\
\n\x02\x04\x02\x12\x04\n\0\x10\x01\n\n\n\x03\x04\x02\x01\x12\x03\n\x08\r\
\n\x0b\n\x04\x04\x02\x02\0\x12\x03\x0b\x04\x12\n\x0c\n\x05\x04\x02\x02\0\
\x05\x12\x03\x0b\x04\n\n\x0c\n\x05\x04\x02\x02\0\x01\x12\x03\x0b\x0b\r\n\
\x0c\n\x05\x04\x02\x02\0\x03\x12\x03\x0b\x10\x11\n\x0b\n\x04\x04\x02\x02\
\x01\x12\x03\x0c\x04\x14\n\x0c\n\x05\x04\x02\x02\x01\x05\x12\x03\x0c\x04\
\n\n\x0c\n\x05\x04\x02\x02\x01\x01\x12\x03\x0c\x0b\x0f\n\x0c\n\x05\x04\
\x02\x02\x01\x03\x12\x03\x0c\x12\x13\n\x0b\n\x04\x04\x02\x02\x02\x12\x03\
\r\x04\x1c\n\x0c\n\x05\x04\x02\x02\x02\x05\x12\x03\r\x04\t\n\x0c\n\x05\
\x04\x02\x02\x02\x01\x12\x03\r\n\x17\n\x0c\n\x05\x04\x02\x02\x02\x03\x12\
\x03\r\x1a\x1b\n\x0b\n\x04\x04\x02\x02\x03\x12\x03\x0e\x04\x1a\n\x0c\n\
\x05\x04\x02\x02\x03\x05\x12\x03\x0e\x04\t\n\x0c\n\x05\x04\x02\x02\x03\
\x01\x12\x03\x0e\n\x15\n\x0c\n\x05\x04\x02\x02\x03\x03\x12\x03\x0e\x18\
\x19\n\x0b\n\x04\x04\x02\x02\x04\x12\x03\x0f\x04\x15\n\x0c\n\x05\x04\x02\
\x02\x04\x06\x12\x03\x0f\x04\r\n\x0c\n\x05\x04\x02\x02\x04\x01\x12\x03\
\x0f\x0e\x10\n\x0c\n\x05\x04\x02\x02\x04\x03\x12\x03\x0f\x13\x14\n\n\n\
\x02\x04\x03\x12\x04\x11\0\x13\x01\n\n\n\x03\x04\x03\x01\x12\x03\x11\x08\
\x15\n\x0b\n\x04\x04\x03\x02\0\x12\x03\x12\x04\x1d\n\x0c\n\x05\x04\x03\
\x02\0\x04\x12\x03\x12\x04\x0c\n\x0c\n\x05\x04\x03\x02\0\x06\x12\x03\x12\
\r\x12\n\x0c\n\x05\x04\x03\x02\0\x01\x12\x03\x12\x13\x18\n\x0c\n\x05\x04\
\x03\x02\0\x03\x12\x03\x12\x1b\x1c\n\n\n\x02\x05\0\x12\x04\x14\0\x17\x01\
\n\n\n\x03\x05\0\x01\x12\x03\x14\x05\x0e\n\x0b\n\x04\x05\0\x02\0\x12\x03\
\x15\x04\x10\n\x0c\n\x05\x05\0\x02\0\x01\x12\x03\x15\x04\x0b\n\x0c\n\x05\
\x05\0\x02\0\x02\x12\x03\x15\x0e\x0f\n\x0b\n\x04\x05\0\x02\x01\x12\x03\
\x16\x04\r\n\x0c\n\x05\x05\0\x02\x01\x01\x12\x03\x16\x04\x08\n\x0c\n\x05\
\x05\0\x02\x01\x02\x12\x03\x16\x0b\x0cb\x06proto3\
\x0b2\x06.TrashR\x05items*+\n\tTrashType\x12\x0b\n\x07Unknown\x10\0\x12\
\x08\n\x04View\x10\x01\x12\x07\n\x03App\x10\x02J\xc7\x06\n\x06\x12\x04\0\
\0\x18\x01\n\x08\n\x01\x0c\x12\x03\0\0\x12\n\n\n\x02\x04\0\x12\x04\x02\0\
\x05\x01\n\n\n\x03\x04\0\x01\x12\x03\x02\x08\x18\n\x0b\n\x04\x04\0\x02\0\
\x12\x03\x03\x04'\n\x0c\n\x05\x04\0\x02\0\x04\x12\x03\x03\x04\x0c\n\x0c\
\n\x05\x04\0\x02\0\x06\x12\x03\x03\r\x1c\n\x0c\n\x05\x04\0\x02\0\x01\x12\
\x03\x03\x1d\"\n\x0c\n\x05\x04\0\x02\0\x03\x12\x03\x03%&\n\x0b\n\x04\x04\
\0\x02\x01\x12\x03\x04\x04\x18\n\x0c\n\x05\x04\0\x02\x01\x05\x12\x03\x04\
\x04\x08\n\x0c\n\x05\x04\0\x02\x01\x01\x12\x03\x04\t\x13\n\x0c\n\x05\x04\
\0\x02\x01\x03\x12\x03\x04\x16\x17\n\n\n\x02\x04\x01\x12\x04\x06\0\t\x01\
\n\n\n\x03\x04\x01\x01\x12\x03\x06\x08\x17\n\x0b\n\x04\x04\x01\x02\0\x12\
\x03\x07\x04\x12\n\x0c\n\x05\x04\x01\x02\0\x05\x12\x03\x07\x04\n\n\x0c\n\
\x05\x04\x01\x02\0\x01\x12\x03\x07\x0b\r\n\x0c\n\x05\x04\x01\x02\0\x03\
\x12\x03\x07\x10\x11\n\x0b\n\x04\x04\x01\x02\x01\x12\x03\x08\x04\x15\n\
\x0c\n\x05\x04\x01\x02\x01\x06\x12\x03\x08\x04\r\n\x0c\n\x05\x04\x01\x02\
\x01\x01\x12\x03\x08\x0e\x10\n\x0c\n\x05\x04\x01\x02\x01\x03\x12\x03\x08\
\x13\x14\n\n\n\x02\x04\x02\x12\x04\n\0\x10\x01\n\n\n\x03\x04\x02\x01\x12\
\x03\n\x08\r\n\x0b\n\x04\x04\x02\x02\0\x12\x03\x0b\x04\x12\n\x0c\n\x05\
\x04\x02\x02\0\x05\x12\x03\x0b\x04\n\n\x0c\n\x05\x04\x02\x02\0\x01\x12\
\x03\x0b\x0b\r\n\x0c\n\x05\x04\x02\x02\0\x03\x12\x03\x0b\x10\x11\n\x0b\n\
\x04\x04\x02\x02\x01\x12\x03\x0c\x04\x14\n\x0c\n\x05\x04\x02\x02\x01\x05\
\x12\x03\x0c\x04\n\n\x0c\n\x05\x04\x02\x02\x01\x01\x12\x03\x0c\x0b\x0f\n\
\x0c\n\x05\x04\x02\x02\x01\x03\x12\x03\x0c\x12\x13\n\x0b\n\x04\x04\x02\
\x02\x02\x12\x03\r\x04\x1c\n\x0c\n\x05\x04\x02\x02\x02\x05\x12\x03\r\x04\
\t\n\x0c\n\x05\x04\x02\x02\x02\x01\x12\x03\r\n\x17\n\x0c\n\x05\x04\x02\
\x02\x02\x03\x12\x03\r\x1a\x1b\n\x0b\n\x04\x04\x02\x02\x03\x12\x03\x0e\
\x04\x1a\n\x0c\n\x05\x04\x02\x02\x03\x05\x12\x03\x0e\x04\t\n\x0c\n\x05\
\x04\x02\x02\x03\x01\x12\x03\x0e\n\x15\n\x0c\n\x05\x04\x02\x02\x03\x03\
\x12\x03\x0e\x18\x19\n\x0b\n\x04\x04\x02\x02\x04\x12\x03\x0f\x04\x15\n\
\x0c\n\x05\x04\x02\x02\x04\x06\x12\x03\x0f\x04\r\n\x0c\n\x05\x04\x02\x02\
\x04\x01\x12\x03\x0f\x0e\x10\n\x0c\n\x05\x04\x02\x02\x04\x03\x12\x03\x0f\
\x13\x14\n\n\n\x02\x04\x03\x12\x04\x11\0\x13\x01\n\n\n\x03\x04\x03\x01\
\x12\x03\x11\x08\x15\n\x0b\n\x04\x04\x03\x02\0\x12\x03\x12\x04\x1d\n\x0c\
\n\x05\x04\x03\x02\0\x04\x12\x03\x12\x04\x0c\n\x0c\n\x05\x04\x03\x02\0\
\x06\x12\x03\x12\r\x12\n\x0c\n\x05\x04\x03\x02\0\x01\x12\x03\x12\x13\x18\
\n\x0c\n\x05\x04\x03\x02\0\x03\x12\x03\x12\x1b\x1c\n\n\n\x02\x05\0\x12\
\x04\x14\0\x18\x01\n\n\n\x03\x05\0\x01\x12\x03\x14\x05\x0e\n\x0b\n\x04\
\x05\0\x02\0\x12\x03\x15\x04\x10\n\x0c\n\x05\x05\0\x02\0\x01\x12\x03\x15\
\x04\x0b\n\x0c\n\x05\x05\0\x02\0\x02\x12\x03\x15\x0e\x0f\n\x0b\n\x04\x05\
\0\x02\x01\x12\x03\x16\x04\r\n\x0c\n\x05\x05\0\x02\x01\x01\x12\x03\x16\
\x04\x08\n\x0c\n\x05\x05\0\x02\x01\x02\x12\x03\x16\x0b\x0c\n\x0b\n\x04\
\x05\0\x02\x02\x12\x03\x17\x04\x0c\n\x0c\n\x05\x05\0\x02\x02\x01\x12\x03\
\x17\x04\x07\n\x0c\n\x05\x05\0\x02\x02\x02\x12\x03\x17\n\x0bb\x06proto3\
";
static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

View File

@ -1,8 +1,7 @@
syntax = "proto3";
message QueryAppRequest {
string app_id = 1;
bool is_trash = 2;
repeated string app_ids = 1;
}
message AppIdentifier {
string app_id = 1;

View File

@ -5,9 +5,8 @@ enum WorkspaceNotification {
UserCreateWorkspace = 10;
UserDeleteWorkspace = 11;
WorkspaceUpdated = 12;
WorkspaceCreateApp = 13;
WorkspaceDeleteApp = 14;
WorkspaceListUpdated = 15;
WorkspaceListUpdated = 13;
WorkspaceAppsChanged = 14;
AppUpdated = 21;
AppViewsChanged = 24;
ViewUpdated = 31;

View File

@ -21,4 +21,5 @@ message RepeatedTrash {
enum TrashType {
Unknown = 0;
View = 1;
App = 2;
}

View File

@ -1,25 +1,43 @@
use crate::{
entities::app::{App, CreateAppParams, *},
entities::{
app::{App, CreateAppParams, *},
trash::TrashType,
},
errors::*,
module::{WorkspaceDatabase, WorkspaceUser},
notify::*,
services::{helper::spawn, server::Server},
services::{helper::spawn, server::Server, TrashCan, TrashEvent},
sql_tables::app::{AppTable, AppTableChangeset, AppTableSql},
};
use flowy_database::SqliteConnection;
use std::sync::Arc;
use futures::{FutureExt, StreamExt};
use std::{collections::HashSet, sync::Arc};
pub(crate) struct AppController {
user: Arc<dyn WorkspaceUser>,
database: Arc<dyn WorkspaceDatabase>,
trash_can: Arc<TrashCan>,
server: Server,
}
impl AppController {
pub(crate) fn new(user: Arc<dyn WorkspaceUser>, database: Arc<dyn WorkspaceDatabase>, server: Server) -> Self {
Self { user, database, server }
pub(crate) fn new(
user: Arc<dyn WorkspaceUser>,
database: Arc<dyn WorkspaceDatabase>,
trash_can: Arc<TrashCan>,
server: Server,
) -> Self {
Self {
user,
database,
trash_can,
server,
}
}
pub fn init(&self) -> Result<(), WorkspaceError> {
self.listen_trash_can_event();
Ok(())
}
#[tracing::instrument(level = "debug", skip(self), err)]
@ -29,10 +47,7 @@ impl AppController {
conn.immediate_transaction::<_, WorkspaceError, _>(|| {
let _ = self.save_app(app.clone(), &*conn)?;
let apps = self.read_local_apps(&app.workspace_id, &*conn)?;
send_dart_notification(&app.workspace_id, WorkspaceNotification::WorkspaceCreateApp)
.payload(apps)
.send();
let _ = notify_app_num_changed(&app.workspace_id, self.trash_can.clone(), conn)?;
Ok(())
})?;
@ -46,33 +61,18 @@ impl AppController {
}
pub(crate) async fn read_app(&self, params: AppIdentifier) -> Result<App, WorkspaceError> {
let app_table = AppTableSql::read_app(&params.app_id, &*self.database.db_connection()?)?;
let conn = self.database.db_connection()?;
let app_table = AppTableSql::read_app(&params.app_id, &*conn)?;
let trash_ids = self.trash_can.trash_ids(&conn)?;
if trash_ids.contains(&app_table.id) {
return Err(WorkspaceError::record_not_found());
}
let _ = self.read_app_on_server(params)?;
Ok(app_table.into())
}
#[tracing::instrument(level = "debug", skip(self), err)]
pub(crate) async fn delete_app(&self, app_id: &str) -> Result<(), WorkspaceError> {
let conn = &*self.database.db_connection()?;
conn.immediate_transaction::<_, WorkspaceError, _>(|| {
let app = AppTableSql::delete_app(app_id, &*conn)?;
let apps = self.read_local_apps(&app.workspace_id, &*conn)?;
send_dart_notification(&app.workspace_id, WorkspaceNotification::WorkspaceDeleteApp)
.payload(apps)
.send();
Ok(())
})?;
let _ = self.delete_app_on_server(app_id);
Ok(())
}
fn read_local_apps(&self, workspace_id: &str, conn: &SqliteConnection) -> Result<RepeatedApp, WorkspaceError> {
let app_tables = AppTableSql::read_apps(workspace_id, false, conn)?;
let apps = app_tables.into_iter().map(|table| table.into()).collect::<Vec<App>>();
Ok(RepeatedApp { items: apps })
}
pub(crate) async fn update_app(&self, params: UpdateAppParams) -> Result<(), WorkspaceError> {
let changeset = AppTableChangeset::new(params.clone());
let app_id = changeset.id.clone();
@ -89,6 +89,19 @@ impl AppController {
let _ = self.update_app_on_server(params)?;
Ok(())
}
pub(crate) fn read_app_tables(&self, ids: Vec<String>) -> Result<Vec<AppTable>, WorkspaceError> {
let conn = &*self.database.db_connection()?;
let mut app_tables = vec![];
conn.immediate_transaction::<_, WorkspaceError, _>(|| {
for app_id in ids {
app_tables.push(AppTableSql::read_app(&app_id, conn)?);
}
Ok(())
})?;
Ok(app_tables)
}
}
impl AppController {
@ -115,37 +128,6 @@ impl AppController {
Ok(())
}
#[tracing::instrument(level = "debug", skip(self), err)]
fn delete_app_on_server(&self, app_id: &str) -> Result<(), WorkspaceError> {
let token = self.user.token()?;
let server = self.server.clone();
let params = DeleteAppParams {
app_id: app_id.to_string(),
};
spawn(async move {
match server.delete_app(&token, params).await {
Ok(_) => {},
Err(e) => {
// TODO: retry?
log::error!("Delete app failed: {:?}", e);
},
}
});
// let action = RetryAction::new(self.server.clone(), self.user.clone(), move
// |token, server| { let params = params.clone();
// async move {
// match server.delete_app(&token, params).await {
// Ok(_) => {},
// Err(e) => log::error!("Delete app failed: {:?}", e),
// }
// Ok::<(), WorkspaceError>(())
// }
// });
//
// spawn_retry(500, 3, action);
Ok(())
}
#[tracing::instrument(level = "debug", skip(self), err)]
fn read_app_on_server(&self, params: AppIdentifier) -> Result<(), WorkspaceError> {
let token = self.user.token()?;
@ -175,4 +157,137 @@ impl AppController {
});
Ok(())
}
fn listen_trash_can_event(&self) {
let mut rx = self.trash_can.subscribe();
let database = self.database.clone();
let trash_can = self.trash_can.clone();
let _ = tokio::spawn(async move {
loop {
let mut stream = Box::pin(rx.recv().into_stream().filter_map(|result| async move {
match result {
Ok(event) => event.select(TrashType::App),
Err(_e) => None,
}
}));
match stream.next().await {
Some(event) => handle_trash_event(database.clone(), trash_can.clone(), event).await,
None => {},
}
}
});
}
}
async fn handle_trash_event(database: Arc<dyn WorkspaceDatabase>, trash_can: Arc<TrashCan>, event: TrashEvent) {
let db_result = database.db_connection();
match event {
TrashEvent::NewTrash(identifiers, ret) | TrashEvent::Putback(identifiers, ret) => {
let result = || {
let conn = &*db_result?;
let _ = conn.immediate_transaction::<_, WorkspaceError, _>(|| {
for identifier in identifiers.items {
let app_table = AppTableSql::read_app(&identifier.id, conn)?;
let _ = notify_app_num_changed(&app_table.workspace_id, trash_can.clone(), conn)?;
}
Ok(())
})?;
Ok::<(), WorkspaceError>(())
};
let _ = ret.send(result()).await;
},
TrashEvent::Delete(identifiers, ret) => {
let result = || {
let conn = &*db_result?;
let _ = conn.immediate_transaction::<_, WorkspaceError, _>(|| {
let mut notify_ids = HashSet::new();
for identifier in identifiers.items {
let app_table = AppTableSql::read_app(&identifier.id, conn)?;
let _ = AppTableSql::delete_app(&identifier.id, conn)?;
notify_ids.insert(app_table.workspace_id);
}
for notify_id in notify_ids {
let _ = notify_app_num_changed(&notify_id, trash_can.clone(), conn)?;
}
Ok(())
})?;
Ok::<(), WorkspaceError>(())
};
let _ = ret.send(result()).await;
},
}
}
#[tracing::instrument(skip(workspace_id, trash_can, conn), err)]
fn notify_app_num_changed(
workspace_id: &str,
trash_can: Arc<TrashCan>,
conn: &SqliteConnection,
) -> WorkspaceResult<()> {
let repeated_app = read_local_workspace_apps(workspace_id, trash_can, conn)?;
send_dart_notification(workspace_id, WorkspaceNotification::WorkspaceAppsChanged)
.payload(repeated_app)
.send();
Ok(())
}
fn read_local_workspace_apps(
workspace_id: &str,
trash_can: Arc<TrashCan>,
conn: &SqliteConnection,
) -> Result<RepeatedApp, WorkspaceError> {
let mut app_tables = AppTableSql::read_workspace_apps(workspace_id, false, conn)?;
let trash_ids = trash_can.trash_ids(conn)?;
app_tables.retain(|app_table| !trash_ids.contains(&app_table.id));
let apps = app_tables.into_iter().map(|table| table.into()).collect::<Vec<App>>();
Ok(RepeatedApp { items: apps })
}
// #[tracing::instrument(level = "debug", skip(self), err)]
// pub(crate) async fn delete_app(&self, app_id: &str) -> Result<(),
// WorkspaceError> { let conn = &*self.database.db_connection()?;
// conn.immediate_transaction::<_, WorkspaceError, _>(|| {
// let app = AppTableSql::delete_app(app_id, &*conn)?;
// let apps = self.read_local_apps(&app.workspace_id, &*conn)?;
// send_dart_notification(&app.workspace_id,
// WorkspaceNotification::WorkspaceDeleteApp) .payload(apps)
// .send();
// Ok(())
// })?;
//
// let _ = self.delete_app_on_server(app_id);
// Ok(())
// }
//
// #[tracing::instrument(level = "debug", skip(self), err)]
// fn delete_app_on_server(&self, app_id: &str) -> Result<(), WorkspaceError> {
// let token = self.user.token()?;
// let server = self.server.clone();
// let params = DeleteAppParams {
// app_id: app_id.to_string(),
// };
// spawn(async move {
// match server.delete_app(&token, params).await {
// Ok(_) => {},
// Err(e) => {
// // TODO: retry?
// log::error!("Delete app failed: {:?}", e);
// },
// }
// });
// // let action = RetryAction::new(self.server.clone(), self.user.clone(),
// move // |token, server| { let params = params.clone();
// // async move {
// // match server.delete_app(&token, params).await {
// // Ok(_) => {},
// // Err(e) => log::error!("Delete app failed: {:?}", e),
// // }
// // Ok::<(), WorkspaceError>(())
// // }
// // });
// //
// // spawn_retry(500, 3, action);
// Ok(())
// }

View File

@ -8,7 +8,7 @@ pub use server_api_mock::*;
use crate::{
entities::{
app::{App, AppIdentifier, CreateAppParams, DeleteAppParams, UpdateAppParams},
app::{App, AppIdentifier, CreateAppParams, UpdateAppParams},
trash::{RepeatedTrash, TrashIdentifiers},
view::{CreateViewParams, UpdateViewParams, View, ViewIdentifier, ViewIdentifiers},
workspace::{
@ -58,7 +58,7 @@ pub trait WorkspaceServerAPI {
fn update_app(&self, token: &str, params: UpdateAppParams) -> ResultFuture<(), WorkspaceError>;
fn delete_app(&self, token: &str, params: DeleteAppParams) -> ResultFuture<(), WorkspaceError>;
fn delete_app(&self, token: &str, params: AppIdentifier) -> ResultFuture<(), WorkspaceError>;
// Trash
fn create_trash(&self, token: &str, params: TrashIdentifiers) -> ResultFuture<(), WorkspaceError>;

View File

@ -1,6 +1,6 @@
use crate::{
entities::{
app::{App, AppIdentifier, CreateAppParams, DeleteAppParams, UpdateAppParams},
app::{App, AppIdentifier, CreateAppParams, UpdateAppParams},
trash::{RepeatedTrash, TrashIdentifiers},
view::{CreateViewParams, UpdateViewParams, View, ViewIdentifier, ViewIdentifiers},
workspace::{
@ -97,7 +97,7 @@ impl WorkspaceServerAPI for WorkspaceServer {
ResultFuture::new(async move { update_app_request(&token, params, &url).await })
}
fn delete_app(&self, token: &str, params: DeleteAppParams) -> ResultFuture<(), WorkspaceError> {
fn delete_app(&self, token: &str, params: AppIdentifier) -> ResultFuture<(), WorkspaceError> {
let token = token.to_owned();
let url = self.config.app_url();
ResultFuture::new(async move { delete_app_request(&token, params, &url).await })
@ -214,7 +214,7 @@ pub async fn update_app_request(token: &str, params: UpdateAppParams, url: &str)
Ok(())
}
pub async fn delete_app_request(token: &str, params: DeleteAppParams, url: &str) -> Result<(), WorkspaceError> {
pub async fn delete_app_request(token: &str, params: AppIdentifier, url: &str) -> Result<(), WorkspaceError> {
let _ = request_builder()
.delete(&url.to_owned())
.header(HEADER_TOKEN, token)

View File

@ -1,6 +1,6 @@
use crate::{
entities::{
app::{App, AppIdentifier, CreateAppParams, DeleteAppParams, RepeatedApp, UpdateAppParams},
app::{App, AppIdentifier, CreateAppParams, RepeatedApp, UpdateAppParams},
trash::{RepeatedTrash, TrashIdentifiers},
view::{CreateViewParams, RepeatedView, UpdateViewParams, View, ViewIdentifier, ViewIdentifiers},
workspace::{
@ -104,7 +104,7 @@ impl WorkspaceServerAPI for WorkspaceServerMock {
ResultFuture::new(async { Ok(()) })
}
fn delete_app(&self, _token: &str, _params: DeleteAppParams) -> ResultFuture<(), WorkspaceError> {
fn delete_app(&self, _token: &str, _params: AppIdentifier) -> ResultFuture<(), WorkspaceError> {
ResultFuture::new(async { Ok(()) })
}

View File

@ -64,10 +64,8 @@ impl ViewController {
conn.immediate_transaction::<_, WorkspaceError, _>(|| {
let _ = self.save_view(view.clone(), conn)?;
let repeated_view = read_belonging_view(&view.belong_to_id, trash_can, &conn)?;
send_dart_notification(&view.belong_to_id, WorkspaceNotification::AppViewsChanged)
.payload(repeated_view)
.send();
let _ = notify_view_num_changed(&view.belong_to_id, trash_can, &conn)?;
Ok(())
})?;
@ -145,7 +143,7 @@ impl ViewController {
pub(crate) async fn read_views_belong_to(&self, belong_to_id: &str) -> Result<RepeatedView, WorkspaceError> {
// TODO: read from server
let conn = self.database.db_connection()?;
let repeated_view = read_belonging_view(belong_to_id, self.trash_can.clone(), &conn)?;
let repeated_view = read_local_belonging_view(belong_to_id, self.trash_can.clone(), &conn)?;
Ok(repeated_view)
}
@ -309,7 +307,7 @@ fn notify_view_num_changed(
trash_can: Arc<TrashCan>,
conn: &SqliteConnection,
) -> WorkspaceResult<()> {
let repeated_view = read_belonging_view(belong_to_id, trash_can.clone(), conn)?;
let repeated_view = read_local_belonging_view(belong_to_id, trash_can.clone(), conn)?;
tracing::Span::current().record("view_count", &format!("{}", repeated_view.len()).as_str());
send_dart_notification(&belong_to_id, WorkspaceNotification::AppViewsChanged)
.payload(repeated_view)
@ -317,13 +315,19 @@ fn notify_view_num_changed(
Ok(())
}
fn read_belonging_view(
fn read_local_belonging_view(
belong_to_id: &str,
trash_can: Arc<TrashCan>,
conn: &SqliteConnection,
) -> WorkspaceResult<RepeatedView> {
let mut repeated_view = ViewTableSql::read_views(belong_to_id, conn)?;
let mut view_tables = ViewTableSql::read_views(belong_to_id, conn)?;
let trash_ids = trash_can.trash_ids(conn)?;
repeated_view.retain(|view| !trash_ids.contains(&view.id));
Ok(repeated_view)
view_tables.retain(|view_table| !trash_ids.contains(&view_table.id));
let views = view_tables
.into_iter()
.map(|view_table| view_table.into())
.collect::<Vec<View>>();
Ok(RepeatedView { items: views })
}

View File

@ -47,6 +47,7 @@ impl WorkspaceController {
pub fn init(&self) -> Result<(), WorkspaceError> {
let _ = self.trash_can.init()?;
let _ = self.view_controller.init()?;
let _ = self.app_controller.init()?;
Ok(())
}

View File

@ -29,16 +29,11 @@ impl AppTableSql {
pub(crate) fn read_app(app_id: &str, conn: &SqliteConnection) -> Result<AppTable, WorkspaceError> {
let filter = dsl::app_table.filter(app_table::id.eq(app_id)).into_boxed();
// if let Some(is_trash) = is_trash {
// filter = filter.filter(app_table::is_trash.eq(is_trash));
// }
let app_table = filter.first::<AppTable>(conn)?;
Ok(app_table)
}
pub(crate) fn read_apps(
pub(crate) fn read_workspace_apps(
workspace_id: &str,
is_trash: bool,
conn: &SqliteConnection,

View File

@ -8,6 +8,7 @@ use crate::{
use diesel::sql_types::Binary;
use flowy_database::schema::app_table;
use crate::entities::trash::{Trash, TrashType};
use serde::{Deserialize, Serialize, __private::TryFrom};
use std::convert::TryInto;
@ -44,6 +45,18 @@ impl AppTable {
}
}
impl std::convert::Into<Trash> for AppTable {
fn into(self) -> Trash {
Trash {
id: self.id,
name: self.name,
modified_time: self.modified_time,
create_time: self.create_time,
ty: TrashType::App,
}
}
}
#[derive(Clone, PartialEq, Serialize, Deserialize, Debug, Default, FromSqlRow, AsExpression)]
#[sql_type = "Binary"]
pub(crate) struct ColorStyleCol {

View File

@ -61,6 +61,7 @@ impl std::convert::From<TrashTable> for TrashTableChangeset {
pub(crate) enum SqlTrashType {
Unknown = 0,
View = 1,
App = 2,
}
impl std::convert::From<i32> for SqlTrashType {
@ -68,6 +69,7 @@ impl std::convert::From<i32> for SqlTrashType {
match value {
0 => SqlTrashType::Unknown,
1 => SqlTrashType::View,
2 => SqlTrashType::App,
_o => SqlTrashType::Unknown,
}
}
@ -80,6 +82,7 @@ impl std::convert::Into<TrashType> for SqlTrashType {
match self {
SqlTrashType::Unknown => TrashType::Unknown,
SqlTrashType::View => TrashType::View,
SqlTrashType::App => TrashType::App,
}
}
}
@ -89,6 +92,7 @@ impl std::convert::From<TrashType> for SqlTrashType {
match ty {
TrashType::Unknown => SqlTrashType::Unknown,
TrashType::View => SqlTrashType::View,
TrashType::App => SqlTrashType::App,
}
}
}

View File

@ -1,5 +1,4 @@
use crate::{
entities::view::{RepeatedView, View},
errors::WorkspaceError,
sql_tables::view::{ViewTable, ViewTableChangeset},
};
@ -39,18 +38,13 @@ impl ViewTableSql {
}
// belong_to_id will be the app_id or view_id.
pub(crate) fn read_views(belong_to_id: &str, conn: &SqliteConnection) -> Result<RepeatedView, WorkspaceError> {
pub(crate) fn read_views(belong_to_id: &str, conn: &SqliteConnection) -> Result<Vec<ViewTable>, WorkspaceError> {
let view_tables = dsl::view_table
.filter(view_table::belong_to_id.eq(belong_to_id))
.into_boxed()
.load::<ViewTable>(conn)?;
let views = view_tables
.into_iter()
.map(|view_table| view_table.into())
.collect::<Vec<View>>();
Ok(RepeatedView { items: views })
Ok(view_tables)
}
pub(crate) fn update_view(changeset: ViewTableChangeset, conn: &SqliteConnection) -> Result<(), WorkspaceError> {

View File

@ -6,14 +6,18 @@ use flowy_workspace::entities::{app::QueryAppRequest, view::*};
async fn app_delete() {
let test = AppTest::new().await;
delete_app(&test.sdk, &test.app.id).await;
let query = QueryAppRequest::new(&test.app.id);
let query = QueryAppRequest {
app_ids: vec![test.app.id.clone()],
};
let _ = read_app(&test.sdk, query).await;
}
#[tokio::test]
async fn app_read() {
let test = AppTest::new().await;
let query = QueryAppRequest::new(&test.app.id);
let query = QueryAppRequest {
app_ids: vec![test.app.id.clone()],
};
let app_from_db = read_app(&test.sdk, query).await;
assert_eq!(app_from_db, test.app);
}
@ -40,7 +44,9 @@ async fn app_create_with_view() {
let view_a = create_view_with_request(&test.sdk, request_a).await;
let view_b = create_view_with_request(&test.sdk, request_b).await;
let query = QueryAppRequest::new(&test.app.id);
let query = QueryAppRequest {
app_ids: vec![test.app.id.clone()],
};
let view_from_db = read_app(&test.sdk, query).await;
assert_eq!(view_from_db.belongings[0], view_a);

View File

@ -9,7 +9,7 @@ use flowy_workspace::entities::{
#[should_panic]
async fn view_delete() {
let test = FlowyTest::setup();
let _ = test.init_user();
let _ = test.init_user().await;
let test = ViewTest::new(&test).await;
test.delete_views(vec![test.view.id.clone()]).await;
@ -22,7 +22,7 @@ async fn view_delete() {
#[tokio::test]
async fn view_delete_then_putback() {
let test = FlowyTest::setup();
let _ = test.init_user();
let _ = test.init_user().await;
let test = ViewTest::new(&test).await;
test.delete_views(vec![test.view.id.clone()]).await;
@ -45,7 +45,7 @@ async fn view_delete_then_putback() {
#[tokio::test]
async fn view_delete_all() {
let test = FlowyTest::setup();
let _ = test.init_user();
let _ = test.init_user().await;
let test = ViewTest::new(&test).await;
let view1 = test.view.clone();
@ -53,7 +53,9 @@ async fn view_delete_all() {
let view3 = create_view(&test.sdk, &test.app.id).await;
let view_ids = vec![view1.id.clone(), view2.id.clone(), view3.id.clone()];
let query = QueryAppRequest::new(&test.app.id);
let query = QueryAppRequest {
app_ids: vec![test.app.id.clone()],
};
let app = read_app(&test.sdk, query.clone()).await;
assert_eq!(app.belongings.len(), view_ids.len());
test.delete_views(view_ids.clone()).await;
@ -65,7 +67,7 @@ async fn view_delete_all() {
#[tokio::test]
async fn view_delete_all_permanent() {
let test = FlowyTest::setup();
let _ = test.init_user();
let _ = test.init_user().await;
let test = ViewTest::new(&test).await;
let view1 = test.view.clone();
@ -74,7 +76,9 @@ async fn view_delete_all_permanent() {
let view_ids = vec![view1.id.clone(), view2.id.clone()];
test.delete_views_permanent(view_ids).await;
let query = QueryAppRequest::new(&test.app.id);
let query = QueryAppRequest {
app_ids: vec![test.app.id.clone()],
};
assert_eq!(read_app(&test.sdk, query).await.belongings.len(), 0);
assert_eq!(read_trash(&test.sdk).await.len(), 0);
}