feat: support leaving workspace (#5007)

This commit is contained in:
Lucas.Xu 2024-03-29 19:01:43 +08:00 committed by GitHub
parent dc8f632e3e
commit 0e7b3c7db2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 75 additions and 6 deletions

View File

@ -5,12 +5,16 @@ import 'package:easy_localization/easy_localization.dart';
extension AFRolePBExtension on AFRolePB {
bool get isOwner => this == AFRolePB.Owner;
bool get isMember => this == AFRolePB.Member;
bool get canInvite => isOwner;
bool get canDelete => isOwner;
bool get canUpdate => isOwner;
bool get canLeave => this != AFRolePB.Owner;
String get description {
switch (this) {
case AFRolePB.Owner:

View File

@ -190,4 +190,11 @@ class UserBackendService {
..role = role;
return UserEventUpdateWorkspaceMember(data).send();
}
Future<FlowyResult<void, FlowyError>> leaveWorkspace(
String workspaceId,
) async {
final data = UserWorkspaceIdPB.create()..workspaceId = workspaceId;
return UserEventLeaveWorkspace(data).send();
}
}

View File

@ -218,6 +218,30 @@ class UserWorkspaceBloc extends Bloc<UserWorkspaceEvent, UserWorkspaceState> {
),
);
},
leaveWorkspace: (workspaceId) async {
final result = await _userService.leaveWorkspace(workspaceId);
final workspaces = result.fold(
(s) => state.workspaces
.where((e) => e.workspaceId != workspaceId)
.toList(),
(e) => state.workspaces,
);
result.onSuccess((_) {
// if leaving the current workspace, open the first workspace
if (state.currentWorkspace?.workspaceId == workspaceId) {
add(OpenWorkspace(workspaces.first.workspaceId));
}
});
emit(
state.copyWith(
workspaces: workspaces,
actionResult: UserWorkspaceActionResult(
actionType: UserWorkspaceActionType.leave,
result: result,
),
),
);
},
);
},
);
@ -270,6 +294,8 @@ class UserWorkspaceEvent with _$UserWorkspaceEvent {
String workspaceId,
String icon,
) = _UpdateWorkspaceIcon;
const factory UserWorkspaceEvent.leaveWorkspace(String workspaceId) =
LeaveWorkspace;
}
enum UserWorkspaceActionType {
@ -279,7 +305,8 @@ enum UserWorkspaceActionType {
open,
rename,
updateIcon,
fetchWorkspaces;
fetchWorkspaces,
leave;
}
class UserWorkspaceActionResult {

View File

@ -112,6 +112,7 @@ class SidebarWorkspace extends StatelessWidget {
break;
case UserWorkspaceActionType.none:
case UserWorkspaceActionType.fetchWorkspaces:
case UserWorkspaceActionType.leave:
message = null;
break;
}

View File

@ -1,6 +1,8 @@
import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/shared/af_role_pb_extension.dart';
import 'package:appflowy/workspace/application/user/user_workspace_bloc.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/members/workspace_member_bloc.dart';
import 'package:appflowy/workspace/presentation/widgets/dialogs.dart';
import 'package:appflowy/workspace/presentation/widgets/pop_up_action.dart';
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
@ -13,6 +15,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
enum WorkspaceMoreAction {
rename,
delete,
leave,
}
class WorkspaceMoreActionList extends StatelessWidget {
@ -25,9 +28,20 @@ class WorkspaceMoreActionList extends StatelessWidget {
@override
Widget build(BuildContext context) {
final myRole = context.read<WorkspaceMemberBloc>().state.myRole;
final actions = [];
if (myRole.isOwner) {
actions.add(WorkspaceMoreAction.rename);
actions.add(WorkspaceMoreAction.delete);
} else if (myRole.canLeave) {
actions.add(WorkspaceMoreAction.leave);
}
if (actions.isEmpty) {
return const SizedBox.shrink();
}
return PopoverActionList<_WorkspaceMoreActionWrapper>(
direction: PopoverDirection.bottomWithCenterAligned,
actions: WorkspaceMoreAction.values
actions: actions
.map((e) => _WorkspaceMoreActionWrapper(e, workspace))
.toList(),
buildChild: (controller) {
@ -92,6 +106,20 @@ class _WorkspaceMoreActionWrapper extends CustomActionCell {
);
},
).show(context);
case WorkspaceMoreAction.leave:
await showDialog(
context: context,
builder: (_) => NavigatorOkCancelDialog(
title: LocaleKeys.workspace_leaveCurrentWorkspace.tr(),
message: LocaleKeys.workspace_leaveCurrentWorkspacePrompt.tr(),
onOkPressed: () {
workspaceBloc.add(
UserWorkspaceEvent.leaveWorkspace(workspace.workspaceId),
);
},
okTitle: LocaleKeys.button_yes.tr(),
),
);
}
},
);
@ -103,6 +131,8 @@ class _WorkspaceMoreActionWrapper extends CustomActionCell {
return LocaleKeys.button_delete.tr();
case WorkspaceMoreAction.rename:
return LocaleKeys.button_rename.tr();
case WorkspaceMoreAction.leave:
return LocaleKeys.workspace_leaveCurrentWorkspace.tr();
}
}
}

View File

@ -1,6 +1,5 @@
import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/shared/af_role_pb_extension.dart';
import 'package:appflowy/workspace/application/user/user_workspace_bloc.dart';
import 'package:appflowy/workspace/presentation/home/menu/sidebar/workspace/_sidebar_workspace_actions.dart';
import 'package:appflowy/workspace/presentation/home/menu/sidebar/workspace/_sidebar_workspace_icon.dart';
@ -201,8 +200,7 @@ class WorkspaceMenuItem extends StatelessWidget {
return Row(
children: [
if (context.read<WorkspaceMemberBloc>().state.myRole.isOwner)
WorkspaceMoreActionList(workspace: workspace),
WorkspaceMoreActionList(workspace: workspace),
const FlowySvg(
FlowySvgs.blue_check_s,
),

View File

@ -79,7 +79,9 @@
"updateIconSuccess": "Updated workspace icon successfully",
"updateIconFailed": "Updated workspace icon failed",
"cannotDeleteTheOnlyWorkspace": "Cannot delete the only workspace",
"fetchWorkspacesFailed": "Failed to fetch workspaces"
"fetchWorkspacesFailed": "Failed to fetch workspaces",
"leaveCurrentWorkspace": "Leave workspace",
"leaveCurrentWorkspacePrompt": "Are you sure you want to leave the current workspace?"
},
"shareAction": {
"buttonText": "Share",