mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: support private section (#4882)
This commit is contained in:
@ -33,18 +33,19 @@ class SidebarRootViewsBloc
|
||||
await event.when(
|
||||
initial: (userProfile, workspaceId) async {
|
||||
_initial(userProfile, workspaceId);
|
||||
await _fetchApps(emit);
|
||||
await _fetchRootViews(emit);
|
||||
},
|
||||
reset: (userProfile, workspaceId) async {
|
||||
await _listener?.stop();
|
||||
_initial(userProfile, workspaceId);
|
||||
await _fetchApps(emit);
|
||||
await _fetchRootViews(emit);
|
||||
},
|
||||
createRootView: (name, desc, index) async {
|
||||
final result = await _workspaceService.createApp(
|
||||
createRootView: (name, desc, index, section) async {
|
||||
final result = await _workspaceService.createView(
|
||||
name: name,
|
||||
desc: desc,
|
||||
index: index,
|
||||
viewSection: section,
|
||||
);
|
||||
result.fold(
|
||||
(view) => emit(state.copyWith(lastCreatedRootView: view)),
|
||||
@ -59,48 +60,59 @@ class SidebarRootViewsBloc
|
||||
);
|
||||
},
|
||||
didReceiveViews: (viewsOrFailure) async {
|
||||
emit(
|
||||
viewsOrFailure.fold(
|
||||
(views) => state.copyWith(
|
||||
views: views,
|
||||
successOrFailure: FlowyResult.success(null),
|
||||
),
|
||||
(err) =>
|
||||
state.copyWith(successOrFailure: FlowyResult.failure(err)),
|
||||
),
|
||||
);
|
||||
// emit(
|
||||
// viewsOrFailure.fold(
|
||||
// (views) => state.copyWith(
|
||||
// views: views,
|
||||
// successOrFailure: FlowyResult.success(null),
|
||||
// ),
|
||||
// (err) =>
|
||||
// state.copyWith(successOrFailure: FlowyResult.failure(err)),
|
||||
// ),
|
||||
// );
|
||||
},
|
||||
moveRootView: (int fromIndex, int toIndex) {
|
||||
if (state.views.length > fromIndex) {
|
||||
final view = state.views[fromIndex];
|
||||
// if (state.views.length > fromIndex) {
|
||||
// final view = state.views[fromIndex];
|
||||
|
||||
_workspaceService.moveApp(
|
||||
appId: view.id,
|
||||
fromIndex: fromIndex,
|
||||
toIndex: toIndex,
|
||||
);
|
||||
// _workspaceService.moveApp(
|
||||
// appId: view.id,
|
||||
// fromIndex: fromIndex,
|
||||
// toIndex: toIndex,
|
||||
// );
|
||||
|
||||
final views = List<ViewPB>.from(state.views);
|
||||
views.insert(toIndex, views.removeAt(fromIndex));
|
||||
emit(state.copyWith(views: views));
|
||||
}
|
||||
// final views = List<ViewPB>.from(state.views);
|
||||
// views.insert(toIndex, views.removeAt(fromIndex));
|
||||
// emit(state.copyWith(views: views));
|
||||
// }
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _fetchApps(Emitter<SidebarRootViewState> emit) async {
|
||||
final viewsOrError = await _workspaceService.getViews();
|
||||
emit(
|
||||
viewsOrError.fold(
|
||||
(views) => state.copyWith(views: views),
|
||||
(error) {
|
||||
Log.error(error);
|
||||
return state.copyWith(successOrFailure: FlowyResult.failure(error));
|
||||
},
|
||||
),
|
||||
);
|
||||
Future<void> _fetchRootViews(
|
||||
Emitter<SidebarRootViewState> emit,
|
||||
) async {
|
||||
try {
|
||||
final publicViews = await _workspaceService.getPublicViews().getOrThrow();
|
||||
final privateViews =
|
||||
await _workspaceService.getPrivateViews().getOrThrow();
|
||||
emit(
|
||||
state.copyWith(
|
||||
publicViews: publicViews,
|
||||
privateViews: privateViews,
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
Log.error(e);
|
||||
// TODO: handle error
|
||||
// emit(
|
||||
// state.copyWith(
|
||||
// successOrFailure: FlowyResult.failure(e),
|
||||
// ),
|
||||
// );
|
||||
}
|
||||
}
|
||||
|
||||
void _handleAppsOrFail(FlowyResult<List<ViewPB>, FlowyError> viewsOrFail) {
|
||||
@ -137,9 +149,12 @@ class SidebarRootViewsEvent with _$SidebarRootViewsEvent {
|
||||
String name, {
|
||||
String? desc,
|
||||
int? index,
|
||||
required ViewSectionPB viewSection,
|
||||
}) = _createRootView;
|
||||
const factory SidebarRootViewsEvent.moveRootView(int fromIndex, int toIndex) =
|
||||
_MoveRootView;
|
||||
const factory SidebarRootViewsEvent.moveRootView(
|
||||
int fromIndex,
|
||||
int toIndex,
|
||||
) = _MoveRootView;
|
||||
const factory SidebarRootViewsEvent.didReceiveViews(
|
||||
FlowyResult<List<ViewPB>, FlowyError> appsOrFail,
|
||||
) = _ReceiveApps;
|
||||
@ -148,13 +163,13 @@ class SidebarRootViewsEvent with _$SidebarRootViewsEvent {
|
||||
@freezed
|
||||
class SidebarRootViewState with _$SidebarRootViewState {
|
||||
const factory SidebarRootViewState({
|
||||
required List<ViewPB> views,
|
||||
@Default([]) List<ViewPB> privateViews,
|
||||
@Default([]) List<ViewPB> publicViews,
|
||||
required FlowyResult<void, FlowyError> successOrFailure,
|
||||
@Default(null) ViewPB? lastCreatedRootView,
|
||||
}) = _SidebarRootViewState;
|
||||
|
||||
factory SidebarRootViewState.initial() => SidebarRootViewState(
|
||||
views: [],
|
||||
successOrFailure: FlowyResult.success(null),
|
||||
);
|
||||
}
|
||||
|
@ -0,0 +1,261 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:appflowy/workspace/application/workspace/workspace_sections_listener.dart';
|
||||
import 'package:appflowy/workspace/application/workspace/workspace_service.dart';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-user/user_profile.pb.dart';
|
||||
import 'package:appflowy_result/appflowy_result.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'sidebar_sections_bloc.freezed.dart';
|
||||
|
||||
class SidebarSection {
|
||||
const SidebarSection({
|
||||
required this.publicViews,
|
||||
required this.privateViews,
|
||||
});
|
||||
|
||||
const SidebarSection.empty()
|
||||
: publicViews = const [],
|
||||
privateViews = const [];
|
||||
|
||||
final List<ViewPB> publicViews;
|
||||
final List<ViewPB> privateViews;
|
||||
|
||||
List<ViewPB> get views => publicViews + privateViews;
|
||||
|
||||
SidebarSection copyWith({
|
||||
List<ViewPB>? publicViews,
|
||||
List<ViewPB>? privateViews,
|
||||
}) {
|
||||
return SidebarSection(
|
||||
publicViews: publicViews ?? this.publicViews,
|
||||
privateViews: privateViews ?? this.privateViews,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// The [SidebarSectionsBloc] is responsible for
|
||||
/// managing the root views in different sections of the workspace.
|
||||
class SidebarSectionsBloc
|
||||
extends Bloc<SidebarSectionsEvent, SidebarSectionsState> {
|
||||
SidebarSectionsBloc() : super(SidebarSectionsState.initial()) {
|
||||
on<SidebarSectionsEvent>(
|
||||
(event, emit) async {
|
||||
await event.when(
|
||||
initial: (userProfile, workspaceId) async {
|
||||
_initial(userProfile, workspaceId);
|
||||
final sectionViews = await _getSectionViews();
|
||||
if (sectionViews != null) {
|
||||
emit(
|
||||
state.copyWith(
|
||||
section: sectionViews,
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
reset: (userProfile, workspaceId) async {
|
||||
_reset(userProfile, workspaceId);
|
||||
final sectionViews = await _getSectionViews();
|
||||
if (sectionViews != null) {
|
||||
emit(
|
||||
state.copyWith(
|
||||
section: sectionViews,
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
createRootViewInSection: (name, section, desc, index) async {
|
||||
final result = await _workspaceService.createView(
|
||||
name: name,
|
||||
viewSection: section,
|
||||
desc: desc,
|
||||
index: index,
|
||||
);
|
||||
result.fold(
|
||||
(view) => emit(
|
||||
state.copyWith(
|
||||
lastCreatedRootView: view,
|
||||
createRootViewResult: FlowyResult.success(null),
|
||||
),
|
||||
),
|
||||
(error) {
|
||||
Log.error('Failed to create root view: $error');
|
||||
emit(
|
||||
state.copyWith(
|
||||
createRootViewResult: FlowyResult.failure(error),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
receiveSectionViewsUpdate: (sectionViews) async {
|
||||
final section = sectionViews.section;
|
||||
switch (section) {
|
||||
case ViewSectionPB.Public:
|
||||
emit(
|
||||
state.copyWith(
|
||||
section: state.section.copyWith(
|
||||
publicViews: sectionViews.views,
|
||||
),
|
||||
),
|
||||
);
|
||||
case ViewSectionPB.Private:
|
||||
emit(
|
||||
state.copyWith(
|
||||
section: state.section.copyWith(
|
||||
privateViews: sectionViews.views,
|
||||
),
|
||||
),
|
||||
);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
},
|
||||
moveRootView: (fromIndex, toIndex, fromSection, toSection) async {
|
||||
final views = fromSection == ViewSectionPB.Public
|
||||
? List<ViewPB>.from(state.section.publicViews)
|
||||
: List<ViewPB>.from(state.section.privateViews);
|
||||
if (fromIndex < 0 || fromIndex >= views.length) {
|
||||
Log.error(
|
||||
'Invalid fromIndex: $fromIndex, maxIndex: ${views.length - 1}',
|
||||
);
|
||||
return;
|
||||
}
|
||||
final view = views[fromIndex];
|
||||
final result = await _workspaceService.moveView(
|
||||
viewId: view.id,
|
||||
fromIndex: fromIndex,
|
||||
toIndex: toIndex,
|
||||
);
|
||||
result.fold(
|
||||
(value) {
|
||||
views.insert(toIndex, views.removeAt(fromIndex));
|
||||
var newState = state;
|
||||
if (fromSection == ViewSectionPB.Public) {
|
||||
newState = newState.copyWith(
|
||||
section: newState.section.copyWith(publicViews: views),
|
||||
);
|
||||
} else if (fromSection == ViewSectionPB.Private) {
|
||||
newState = newState.copyWith(
|
||||
section: newState.section.copyWith(privateViews: views),
|
||||
);
|
||||
}
|
||||
emit(newState);
|
||||
},
|
||||
(error) {
|
||||
Log.error('Failed to move root view: $error');
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
late WorkspaceService _workspaceService;
|
||||
WorkspaceSectionsListener? _listener;
|
||||
|
||||
@override
|
||||
Future<void> close() async {
|
||||
await _listener?.stop();
|
||||
_listener = null;
|
||||
return super.close();
|
||||
}
|
||||
|
||||
ViewSectionPB? getViewSection(ViewPB view) {
|
||||
final publicViews = state.section.publicViews.map((e) => e.id);
|
||||
final privateViews = state.section.privateViews.map((e) => e.id);
|
||||
if (publicViews.contains(view.id)) {
|
||||
return ViewSectionPB.Public;
|
||||
} else if (privateViews.contains(view.id)) {
|
||||
return ViewSectionPB.Private;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Future<SidebarSection?> _getSectionViews() async {
|
||||
try {
|
||||
final publicViews = await _workspaceService.getPublicViews().getOrThrow();
|
||||
final privateViews =
|
||||
await _workspaceService.getPrivateViews().getOrThrow();
|
||||
return SidebarSection(
|
||||
publicViews: publicViews,
|
||||
privateViews: privateViews,
|
||||
);
|
||||
} catch (e) {
|
||||
Log.error('Failed to get section views: $e');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
void _initial(UserProfilePB userProfile, String workspaceId) {
|
||||
_workspaceService = WorkspaceService(workspaceId: workspaceId);
|
||||
|
||||
_listener = WorkspaceSectionsListener(
|
||||
user: userProfile,
|
||||
workspaceId: workspaceId,
|
||||
)..start(
|
||||
sectionChanged: (result) {
|
||||
if (!isClosed) {
|
||||
result.fold(
|
||||
(s) => add(SidebarSectionsEvent.receiveSectionViewsUpdate(s)),
|
||||
(f) => Log.error('Failed to receive section views: $f'),
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _reset(UserProfilePB userProfile, String workspaceId) {
|
||||
_listener?.stop();
|
||||
_listener = null;
|
||||
|
||||
_initial(userProfile, workspaceId);
|
||||
}
|
||||
}
|
||||
|
||||
@freezed
|
||||
class SidebarSectionsEvent with _$SidebarSectionsEvent {
|
||||
const factory SidebarSectionsEvent.initial(
|
||||
UserProfilePB userProfile,
|
||||
String workspaceId,
|
||||
) = _Initial;
|
||||
const factory SidebarSectionsEvent.reset(
|
||||
UserProfilePB userProfile,
|
||||
String workspaceId,
|
||||
) = _Reset;
|
||||
const factory SidebarSectionsEvent.createRootViewInSection({
|
||||
required String name,
|
||||
required ViewSectionPB viewSection,
|
||||
String? desc,
|
||||
int? index,
|
||||
}) = _CreateRootViewInSection;
|
||||
const factory SidebarSectionsEvent.moveRootView({
|
||||
required int fromIndex,
|
||||
required int toIndex,
|
||||
required ViewSectionPB fromSection,
|
||||
required ViewSectionPB toSection,
|
||||
}) = _MoveRootView;
|
||||
const factory SidebarSectionsEvent.receiveSectionViewsUpdate(
|
||||
SectionViewsPB sectionViews,
|
||||
) = _ReceiveSectionViewsUpdate;
|
||||
}
|
||||
|
||||
@freezed
|
||||
class SidebarSectionsState with _$SidebarSectionsState {
|
||||
const factory SidebarSectionsState({
|
||||
required SidebarSection section,
|
||||
@Default(null) ViewPB? lastCreatedRootView,
|
||||
FlowyResult<void, FlowyError>? createRootViewResult,
|
||||
}) = _SidebarSectionsState;
|
||||
|
||||
factory SidebarSectionsState.initial() => const SidebarSectionsState(
|
||||
section: SidebarSection.empty(),
|
||||
);
|
||||
}
|
@ -3,6 +3,7 @@ import 'dart:convert';
|
||||
import 'package:appflowy/core/config/kv.dart';
|
||||
import 'package:appflowy/core/config/kv_keys.dart';
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
@ -10,7 +11,19 @@ part 'folder_bloc.freezed.dart';
|
||||
|
||||
enum FolderCategoryType {
|
||||
favorite,
|
||||
personal,
|
||||
private,
|
||||
public;
|
||||
|
||||
ViewSectionPB get toViewSectionPB {
|
||||
switch (this) {
|
||||
case FolderCategoryType.private:
|
||||
return ViewSectionPB.Private;
|
||||
case FolderCategoryType.public:
|
||||
return ViewSectionPB.Public;
|
||||
case FolderCategoryType.favorite:
|
||||
throw UnimplementedError();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class FolderBloc extends Bloc<FolderEvent, FolderState> {
|
||||
|
@ -2,7 +2,7 @@ import 'package:appflowy/user/application/user_service.dart';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/code.pbenum.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-user/user_profile.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
|
||||
import 'package:appflowy_result/appflowy_result.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
@ -20,14 +20,20 @@ class UserWorkspaceBloc extends Bloc<UserWorkspaceEvent, UserWorkspaceState> {
|
||||
(event, emit) async {
|
||||
await event.when(
|
||||
initial: () async {
|
||||
// do nothing
|
||||
add(const FetchWorkspaces());
|
||||
},
|
||||
workspacesReceived: (workspaceId) async {},
|
||||
fetchWorkspaces: () async {
|
||||
final result = await _fetchWorkspaces();
|
||||
if (result != null) {
|
||||
final members = await _userService
|
||||
.getWorkspaceMembers(
|
||||
result.$1.workspaceId,
|
||||
)
|
||||
.fold((s) => s.items.length, (f) => -1);
|
||||
emit(
|
||||
state.copyWith(
|
||||
isCollaborativeWorkspace: members > 1,
|
||||
currentWorkspace: result.$1,
|
||||
workspaces: result.$2,
|
||||
),
|
||||
@ -258,7 +264,7 @@ class UserWorkspaceBloc extends Bloc<UserWorkspaceEvent, UserWorkspaceState> {
|
||||
workspaces.firstWhere((e) => e.workspaceId == currentWorkspace.id);
|
||||
return (currentWorkspaceInList, workspaces);
|
||||
} catch (e) {
|
||||
Log.error(e);
|
||||
Log.error('fetch workspace error: $e');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -292,6 +298,7 @@ class UserWorkspaceState with _$UserWorkspaceState {
|
||||
const factory UserWorkspaceState({
|
||||
required UserWorkspacePB? currentWorkspace,
|
||||
required List<UserWorkspacePB> workspaces,
|
||||
@Default(false) bool isCollaborativeWorkspace,
|
||||
@Default(null) FlowyResult<void, FlowyError>? createWorkspaceResult,
|
||||
@Default(null) FlowyResult<void, FlowyError>? deleteWorkspaceResult,
|
||||
@Default(null) FlowyResult<void, FlowyError>? openWorkspaceResult,
|
||||
|
@ -165,6 +165,8 @@ class ViewBloc extends Bloc<ViewEvent, ViewState> {
|
||||
viewId: value.from.id,
|
||||
newParentId: value.newParentId,
|
||||
prevViewId: value.prevId,
|
||||
fromSection: value.fromSection,
|
||||
toSection: value.toSection,
|
||||
);
|
||||
emit(
|
||||
result.fold(
|
||||
@ -184,8 +186,8 @@ class ViewBloc extends Bloc<ViewEvent, ViewState> {
|
||||
layoutType: e.layoutType,
|
||||
ext: {},
|
||||
openAfterCreate: e.openAfterCreated,
|
||||
section: e.section,
|
||||
);
|
||||
|
||||
emit(
|
||||
result.fold(
|
||||
(view) => state.copyWith(
|
||||
@ -353,12 +355,15 @@ class ViewEvent with _$ViewEvent {
|
||||
ViewPB from,
|
||||
String newParentId,
|
||||
String? prevId,
|
||||
ViewSectionPB? fromSection,
|
||||
ViewSectionPB? toSection,
|
||||
) = Move;
|
||||
const factory ViewEvent.createView(
|
||||
String name,
|
||||
ViewLayoutPB layoutType, {
|
||||
/// open the view after created
|
||||
@Default(true) bool openAfterCreated,
|
||||
required ViewSectionPB section,
|
||||
}) = CreateView;
|
||||
const factory ViewEvent.viewDidUpdate(
|
||||
FlowyResult<ViewPB, FlowyError> result,
|
||||
|
@ -37,6 +37,7 @@ class ViewBackendService {
|
||||
/// The [index] is the index of the view in the parent view.
|
||||
/// If the index is null, the view will be added to the end of the list.
|
||||
int? index,
|
||||
ViewSectionPB? section,
|
||||
}) {
|
||||
final payload = CreateViewPayloadPB.create()
|
||||
..parentViewId = parentViewId
|
||||
@ -58,6 +59,10 @@ class ViewBackendService {
|
||||
payload.index = index;
|
||||
}
|
||||
|
||||
if (section != null) {
|
||||
payload.section = section;
|
||||
}
|
||||
|
||||
return FolderEventCreateView(payload).send();
|
||||
}
|
||||
|
||||
@ -195,11 +200,15 @@ class ViewBackendService {
|
||||
required String viewId,
|
||||
required String newParentId,
|
||||
required String? prevViewId,
|
||||
ViewSectionPB? fromSection,
|
||||
ViewSectionPB? toSection,
|
||||
}) {
|
||||
final payload = MoveNestedViewPayloadPB(
|
||||
viewId: viewId,
|
||||
newParentId: newParentId,
|
||||
prevViewId: prevViewId,
|
||||
fromSection: fromSection,
|
||||
toSection: toSection,
|
||||
);
|
||||
|
||||
return FolderEventMoveNestedView(payload).send();
|
||||
|
@ -11,23 +11,28 @@ import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart'
|
||||
import 'package:appflowy_result/appflowy_result.dart';
|
||||
import 'package:flowy_infra/notifier.dart';
|
||||
|
||||
typedef AppListNotifyValue = FlowyResult<List<ViewPB>, FlowyError>;
|
||||
typedef RootViewsNotifyValue = FlowyResult<List<ViewPB>, FlowyError>;
|
||||
typedef WorkspaceNotifyValue = FlowyResult<WorkspacePB, FlowyError>;
|
||||
|
||||
/// The [WorkspaceListener] listens to the changes including the below:
|
||||
///
|
||||
/// - The root views of the workspace. (Not including the views are inside the root views)
|
||||
/// - The workspace itself.
|
||||
class WorkspaceListener {
|
||||
WorkspaceListener({required this.user, required this.workspaceId});
|
||||
|
||||
final UserProfilePB user;
|
||||
final String workspaceId;
|
||||
|
||||
PublishNotifier<AppListNotifyValue>? _appsChangedNotifier = PublishNotifier();
|
||||
PublishNotifier<RootViewsNotifyValue>? _appsChangedNotifier =
|
||||
PublishNotifier();
|
||||
PublishNotifier<WorkspaceNotifyValue>? _workspaceUpdatedNotifier =
|
||||
PublishNotifier();
|
||||
|
||||
FolderNotificationListener? _listener;
|
||||
|
||||
void start({
|
||||
void Function(AppListNotifyValue)? appsChanged,
|
||||
void Function(RootViewsNotifyValue)? appsChanged,
|
||||
void Function(WorkspaceNotifyValue)? onWorkspaceUpdated,
|
||||
}) {
|
||||
if (appsChanged != null) {
|
||||
|
@ -0,0 +1,68 @@
|
||||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:appflowy/core/notification/folder_notification.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/notification.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart'
|
||||
show UserProfilePB;
|
||||
import 'package:appflowy_result/appflowy_result.dart';
|
||||
import 'package:flowy_infra/notifier.dart';
|
||||
|
||||
typedef SectionNotifyValue = FlowyResult<SectionViewsPB, FlowyError>;
|
||||
|
||||
/// The [WorkspaceSectionsListener] listens to the changes including the below:
|
||||
///
|
||||
/// - The root views inside different section of the workspace. (Not including the views are inside the root views)
|
||||
/// depends on the section type(s).
|
||||
class WorkspaceSectionsListener {
|
||||
WorkspaceSectionsListener({
|
||||
required this.user,
|
||||
required this.workspaceId,
|
||||
});
|
||||
|
||||
final UserProfilePB user;
|
||||
final String workspaceId;
|
||||
|
||||
final _sectionNotifier = PublishNotifier<SectionNotifyValue>();
|
||||
late final FolderNotificationListener _listener;
|
||||
|
||||
void start({
|
||||
void Function(SectionNotifyValue)? sectionChanged,
|
||||
}) {
|
||||
if (sectionChanged != null) {
|
||||
_sectionNotifier.addPublishListener(sectionChanged);
|
||||
}
|
||||
|
||||
_listener = FolderNotificationListener(
|
||||
objectId: workspaceId,
|
||||
handler: _handleObservableType,
|
||||
);
|
||||
}
|
||||
|
||||
void _handleObservableType(
|
||||
FolderNotification ty,
|
||||
FlowyResult<Uint8List, FlowyError> result,
|
||||
) {
|
||||
switch (ty) {
|
||||
case FolderNotification.DidUpdateSectionViews:
|
||||
final FlowyResult<SectionViewsPB, FlowyError> value = result.fold(
|
||||
(s) => FlowyResult.success(
|
||||
SectionViewsPB.fromBuffer(s),
|
||||
),
|
||||
(f) => FlowyResult.failure(f),
|
||||
);
|
||||
_sectionNotifier.value = value;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> stop() async {
|
||||
_sectionNotifier.dispose();
|
||||
|
||||
await _listener.stop();
|
||||
}
|
||||
}
|
@ -2,9 +2,7 @@ import 'dart:async';
|
||||
|
||||
import 'package:appflowy_backend/dispatch/dispatch.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart'
|
||||
show CreateViewPayloadPB, MoveViewPayloadPB, ViewLayoutPB, ViewPB;
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/workspace.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart';
|
||||
import 'package:appflowy_result/appflowy_result.dart';
|
||||
|
||||
class WorkspaceService {
|
||||
@ -12,15 +10,18 @@ class WorkspaceService {
|
||||
|
||||
final String workspaceId;
|
||||
|
||||
Future<FlowyResult<ViewPB, FlowyError>> createApp({
|
||||
Future<FlowyResult<ViewPB, FlowyError>> createView({
|
||||
required String name,
|
||||
required ViewSectionPB viewSection,
|
||||
String? desc,
|
||||
int? index,
|
||||
}) {
|
||||
final payload = CreateViewPayloadPB.create()
|
||||
..parentViewId = workspaceId
|
||||
..name = name
|
||||
..layout = ViewLayoutPB.Document;
|
||||
// only allow document layout for the top-level views
|
||||
..layout = ViewLayoutPB.Document
|
||||
..section = viewSection;
|
||||
|
||||
if (desc != null) {
|
||||
payload.desc = desc;
|
||||
@ -37,8 +38,8 @@ class WorkspaceService {
|
||||
return FolderEventReadCurrentWorkspace().send();
|
||||
}
|
||||
|
||||
Future<FlowyResult<List<ViewPB>, FlowyError>> getViews() {
|
||||
final payload = WorkspaceIdPB.create()..value = workspaceId;
|
||||
Future<FlowyResult<List<ViewPB>, FlowyError>> getPublicViews() {
|
||||
final payload = GetWorkspaceViewPB.create()..value = workspaceId;
|
||||
return FolderEventReadWorkspaceViews(payload).send().then((result) {
|
||||
return result.fold(
|
||||
(views) => FlowyResult.success(views.items),
|
||||
@ -47,13 +48,23 @@ class WorkspaceService {
|
||||
});
|
||||
}
|
||||
|
||||
Future<FlowyResult<void, FlowyError>> moveApp({
|
||||
required String appId,
|
||||
Future<FlowyResult<List<ViewPB>, FlowyError>> getPrivateViews() {
|
||||
final payload = GetWorkspaceViewPB.create()..value = workspaceId;
|
||||
return FolderEventReadPrivateViews(payload).send().then((result) {
|
||||
return result.fold(
|
||||
(views) => FlowyResult.success(views.items),
|
||||
(error) => FlowyResult.failure(error),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Future<FlowyResult<void, FlowyError>> moveView({
|
||||
required String viewId,
|
||||
required int fromIndex,
|
||||
required int toIndex,
|
||||
}) {
|
||||
final payload = MoveViewPayloadPB.create()
|
||||
..viewId = appId
|
||||
..viewId = viewId
|
||||
..from = fromIndex
|
||||
..to = toIndex;
|
||||
|
||||
|
Reference in New Issue
Block a user