feat: use new api to get the workspace member role (#6783)

* chore: update to latest client api

* chore: merge with main

* chore: remove unneeded code

* fix: sqlite migration

* fix: cargo fmt

* feat: use new api to get the member role

---------

Co-authored-by: Lucas.Xu <lucas.xu@appflowy.io>
This commit is contained in:
Zack 2024-12-19 15:19:24 +08:00 committed by GitHub
parent dda3962249
commit ee96a44fef
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
24 changed files with 114 additions and 88 deletions

View File

@ -15,8 +15,11 @@ part 'local_ai_on_boarding_bloc.freezed.dart';
class LocalAIOnBoardingBloc class LocalAIOnBoardingBloc
extends Bloc<LocalAIOnBoardingEvent, LocalAIOnBoardingState> { extends Bloc<LocalAIOnBoardingEvent, LocalAIOnBoardingState> {
LocalAIOnBoardingBloc(this.userProfile, this.member, this.workspaceId) LocalAIOnBoardingBloc(
: super(const LocalAIOnBoardingState()) { this.userProfile,
this.currentWorkspaceMemberRole,
this.workspaceId,
) : super(const LocalAIOnBoardingState()) {
_userService = UserBackendService(userId: userProfile.id); _userService = UserBackendService(userId: userProfile.id);
_successListenable = getIt<SubscriptionSuccessListenable>(); _successListenable = getIt<SubscriptionSuccessListenable>();
_successListenable.addListener(_onPaymentSuccessful); _successListenable.addListener(_onPaymentSuccessful);
@ -36,7 +39,7 @@ class LocalAIOnBoardingBloc
} }
final UserProfilePB userProfile; final UserProfilePB userProfile;
final WorkspaceMemberPB member; final AFRolePB? currentWorkspaceMemberRole;
final String workspaceId; final String workspaceId;
late final IUserBackendService _userService; late final IUserBackendService _userService;
late final SubscriptionSuccessListenable _successListenable; late final SubscriptionSuccessListenable _successListenable;

View File

@ -14,13 +14,18 @@ class SettingsAIBloc extends Bloc<SettingsAIEvent, SettingsAIState> {
SettingsAIBloc( SettingsAIBloc(
this.userProfile, this.userProfile,
this.workspaceId, this.workspaceId,
WorkspaceMemberPB? member, AFRolePB? currentWorkspaceMemberRole,
) : _userListener = UserListener(userProfile: userProfile), ) : _userListener = UserListener(userProfile: userProfile),
_userService = UserBackendService(userId: userProfile.id), _userService = UserBackendService(userId: userProfile.id),
super(SettingsAIState(userProfile: userProfile, member: member)) { super(
SettingsAIState(
userProfile: userProfile,
currentWorkspaceMemberRole: currentWorkspaceMemberRole,
),
) {
_dispatch(); _dispatch();
if (member == null) { if (currentWorkspaceMemberRole == null) {
_userService.getWorkspaceMember().then((result) { _userService.getWorkspaceMember().then((result) {
result.fold( result.fold(
(member) { (member) {
@ -85,7 +90,7 @@ class SettingsAIBloc extends Bloc<SettingsAIEvent, SettingsAIState> {
); );
}, },
refreshMember: (member) { refreshMember: (member) {
emit(state.copyWith(member: member)); emit(state.copyWith(currentWorkspaceMemberRole: member.role));
}, },
); );
}); });
@ -152,7 +157,7 @@ class SettingsAIState with _$SettingsAIState {
const factory SettingsAIState({ const factory SettingsAIState({
required UserProfilePB userProfile, required UserProfilePB userProfile,
UseAISettingPB? aiSettings, UseAISettingPB? aiSettings,
WorkspaceMemberPB? member, AFRolePB? currentWorkspaceMemberRole,
@Default(true) bool enableSearchIndexing, @Default(true) bool enableSearchIndexing,
}) = _SettingsAIState; }) = _SettingsAIState;
} }

View File

@ -33,7 +33,7 @@ class SettingsDialogBloc
extends Bloc<SettingsDialogEvent, SettingsDialogState> { extends Bloc<SettingsDialogEvent, SettingsDialogState> {
SettingsDialogBloc( SettingsDialogBloc(
this.userProfile, this.userProfile,
this.workspaceMember, { this.currentWorkspaceMemberRole, {
SettingsPage? initPage, SettingsPage? initPage,
}) : _userListener = UserListener(userProfile: userProfile), }) : _userListener = UserListener(userProfile: userProfile),
super(SettingsDialogState.initial(userProfile, initPage)) { super(SettingsDialogState.initial(userProfile, initPage)) {
@ -41,7 +41,7 @@ class SettingsDialogBloc
} }
final UserProfilePB userProfile; final UserProfilePB userProfile;
final WorkspaceMemberPB? workspaceMember; final AFRolePB? currentWorkspaceMemberRole;
final UserListener _userListener; final UserListener _userListener;
@override @override
@ -57,8 +57,10 @@ class SettingsDialogBloc
initial: () async { initial: () async {
_userListener.start(onProfileUpdated: _profileUpdated); _userListener.start(onProfileUpdated: _profileUpdated);
final isBillingEnabled = final isBillingEnabled = await _isBillingEnabled(
await _isBillingEnabled(userProfile, workspaceMember); userProfile,
currentWorkspaceMemberRole,
);
if (isBillingEnabled) { if (isBillingEnabled) {
emit(state.copyWith(isBillingEnabled: true)); emit(state.copyWith(isBillingEnabled: true));
} }
@ -86,7 +88,7 @@ class SettingsDialogBloc
Future<bool> _isBillingEnabled( Future<bool> _isBillingEnabled(
UserProfilePB userProfile, [ UserProfilePB userProfile, [
WorkspaceMemberPB? member, AFRolePB? currentWorkspaceMemberRole,
]) async { ]) async {
if ([ if ([
AuthenticatorPB.Local, AuthenticatorPB.Local,
@ -94,7 +96,8 @@ class SettingsDialogBloc
return false; return false;
} }
if (member == null || member.role != AFRolePB.Owner) { if (currentWorkspaceMemberRole == null ||
currentWorkspaceMemberRole != AFRolePB.Owner) {
return false; return false;
} }

View File

@ -63,13 +63,6 @@ class UserWorkspaceBloc extends Bloc<UserWorkspaceEvent, UserWorkspaceState> {
actionResult: null, actionResult: null,
), ),
); );
/// We wait with fetching the workspace member as it may take some time,
/// to avoid blocking the UI from rendering (the sidebar).
final workspaceMemberResult =
await _userService.getWorkspaceMember();
final workspaceMember = workspaceMemberResult.toNullable();
emit(state.copyWith(currentWorkspaceMember: workspaceMember));
}, },
fetchWorkspaces: () async { fetchWorkspaces: () async {
final result = await _fetchWorkspaces(); final result = await _fetchWorkspaces();
@ -238,14 +231,6 @@ class UserWorkspaceBloc extends Bloc<UserWorkspaceEvent, UserWorkspaceState> {
), ),
), ),
); );
/// We wait with fetching the workspace member as it may take some time,
/// to avoid blocking the UI from rendering (the sidebar).
final workspaceMemberResult =
await _userService.getWorkspaceMember();
final workspaceMember = workspaceMemberResult.toNullable();
emit(state.copyWith(currentWorkspaceMember: workspaceMember));
}, },
renameWorkspace: (workspaceId, name) async { renameWorkspace: (workspaceId, name) async {
final result = final result =
@ -515,7 +500,6 @@ class UserWorkspaceState with _$UserWorkspaceState {
const factory UserWorkspaceState({ const factory UserWorkspaceState({
@Default(null) UserWorkspacePB? currentWorkspace, @Default(null) UserWorkspacePB? currentWorkspace,
@Default([]) List<UserWorkspacePB> workspaces, @Default([]) List<UserWorkspacePB> workspaces,
@Default(null) WorkspaceMemberPB? currentWorkspaceMember,
@Default(null) UserWorkspaceActionResult? actionResult, @Default(null) UserWorkspaceActionResult? actionResult,
@Default(false) bool isCollabWorkspaceOn, @Default(false) bool isCollabWorkspaceOn,
}) = _UserWorkspaceState; }) = _UserWorkspaceState;
@ -533,7 +517,6 @@ class UserWorkspaceState with _$UserWorkspaceState {
if (identical(this, other)) return true; if (identical(this, other)) return true;
return other is UserWorkspaceState && return other is UserWorkspaceState &&
other.currentWorkspaceMember == currentWorkspaceMember &&
other.currentWorkspace == currentWorkspace && other.currentWorkspace == currentWorkspace &&
_deepCollectionEquality.equals(other.workspaces, workspaces) && _deepCollectionEquality.equals(other.workspaces, workspaces) &&
identical(other.actionResult, actionResult); identical(other.actionResult, actionResult);

View File

@ -94,15 +94,15 @@ class SidebarToast extends StatelessWidget {
} }
final userWorkspaceBloc = context.read<UserWorkspaceBloc>(); final userWorkspaceBloc = context.read<UserWorkspaceBloc>();
final member = userWorkspaceBloc.state.currentWorkspaceMember; final role = userWorkspaceBloc.state.currentWorkspace?.role;
if (member == null) { if (role == null) {
return Log.error( return Log.error(
"Member is null. It should not happen. If you see this error, it's a bug", "Member is null. It should not happen. If you see this error, it's a bug",
); );
} }
// Only if the user is the workspace owner will we navigate to the plan page. // Only if the user is the workspace owner will we navigate to the plan page.
if (member.role.isOwner) { if (role.isOwner) {
showSettingsDialog( showSettingsDialog(
context, context,
userProfile, userProfile,

View File

@ -150,7 +150,7 @@ class SpaceMoreActionTypeWrapper extends CustomActionCell {
final isOwner = context final isOwner = context
.read<UserWorkspaceBloc?>() .read<UserWorkspaceBloc?>()
?.state ?.state
.currentWorkspaceMember .currentWorkspace
?.role ?.role
.isOwner ?? .isOwner ??
false; false;

View File

@ -1,21 +1,20 @@
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/shared/af_role_pb_extension.dart'; import 'package:appflowy/shared/af_role_pb_extension.dart';
import 'package:appflowy/shared/feature_flags.dart'; import 'package:appflowy/shared/feature_flags.dart';
import 'package:appflowy/workspace/application/settings/ai/local_ai_on_boarding_bloc.dart'; import 'package:appflowy/workspace/application/settings/ai/local_ai_on_boarding_bloc.dart';
import 'package:appflowy/workspace/application/settings/ai/settings_ai_bloc.dart';
import 'package:appflowy/workspace/presentation/settings/pages/setting_ai_view/local_ai_setting.dart'; import 'package:appflowy/workspace/presentation/settings/pages/setting_ai_view/local_ai_setting.dart';
import 'package:appflowy/workspace/presentation/settings/pages/setting_ai_view/model_selection.dart'; import 'package:appflowy/workspace/presentation/settings/pages/setting_ai_view/model_selection.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/setting_appflowy_cloud.dart';
import 'package:flowy_infra/theme_extension.dart';
import 'package:flowy_infra_ui/widget/spacing.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/workspace/application/settings/ai/settings_ai_bloc.dart';
import 'package:appflowy/workspace/presentation/settings/shared/settings_body.dart'; import 'package:appflowy/workspace/presentation/settings/shared/settings_body.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/setting_appflowy_cloud.dart';
import 'package:appflowy/workspace/presentation/widgets/toggle/toggle.dart'; import 'package:appflowy/workspace/presentation/widgets/toggle/toggle.dart';
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart'; import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/theme_extension.dart';
import 'package:flowy_infra_ui/style_widget/text.dart'; import 'package:flowy_infra_ui/style_widget/text.dart';
import 'package:flowy_infra_ui/widget/spacing.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
class AIFeatureOnlySupportedWhenUsingAppFlowyCloud extends StatelessWidget { class AIFeatureOnlySupportedWhenUsingAppFlowyCloud extends StatelessWidget {
@ -39,19 +38,20 @@ class SettingsAIView extends StatelessWidget {
const SettingsAIView({ const SettingsAIView({
super.key, super.key,
required this.userProfile, required this.userProfile,
required this.member, required this.currentWorkspaceMemberRole,
required this.workspaceId, required this.workspaceId,
}); });
final UserProfilePB userProfile; final UserProfilePB userProfile;
final WorkspaceMemberPB? member; final AFRolePB? currentWorkspaceMemberRole;
final String workspaceId; final String workspaceId;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocProvider<SettingsAIBloc>( return BlocProvider<SettingsAIBloc>(
create: (_) => SettingsAIBloc(userProfile, workspaceId, member) create: (_) =>
..add(const SettingsAIEvent.started()), SettingsAIBloc(userProfile, workspaceId, currentWorkspaceMemberRole)
..add(const SettingsAIEvent.started()),
child: BlocBuilder<SettingsAIBloc, SettingsAIState>( child: BlocBuilder<SettingsAIBloc, SettingsAIState>(
builder: (context, state) { builder: (context, state) {
final children = <Widget>[ final children = <Widget>[
@ -60,11 +60,11 @@ class SettingsAIView extends StatelessWidget {
children.add(const _AISearchToggle(value: false)); children.add(const _AISearchToggle(value: false));
if (state.member != null) { if (state.currentWorkspaceMemberRole != null) {
children.add( children.add(
_LocalAIOnBoarding( _LocalAIOnBoarding(
userProfile: userProfile, userProfile: userProfile,
member: state.member!, currentWorkspaceMemberRole: state.currentWorkspaceMemberRole!,
workspaceId: workspaceId, workspaceId: workspaceId,
), ),
); );
@ -129,11 +129,11 @@ class _AISearchToggle extends StatelessWidget {
class _LocalAIOnBoarding extends StatelessWidget { class _LocalAIOnBoarding extends StatelessWidget {
const _LocalAIOnBoarding({ const _LocalAIOnBoarding({
required this.userProfile, required this.userProfile,
required this.member, required this.currentWorkspaceMemberRole,
required this.workspaceId, required this.workspaceId,
}); });
final UserProfilePB userProfile; final UserProfilePB userProfile;
final WorkspaceMemberPB member; final AFRolePB? currentWorkspaceMemberRole;
final String workspaceId; final String workspaceId;
@override @override
@ -142,16 +142,18 @@ class _LocalAIOnBoarding extends StatelessWidget {
return BillingGateGuard( return BillingGateGuard(
builder: (context) { builder: (context) {
return BlocProvider( return BlocProvider(
create: (context) => create: (context) => LocalAIOnBoardingBloc(
LocalAIOnBoardingBloc(userProfile, member, workspaceId) userProfile,
..add(const LocalAIOnBoardingEvent.started()), currentWorkspaceMemberRole,
workspaceId,
)..add(const LocalAIOnBoardingEvent.started()),
child: BlocBuilder<LocalAIOnBoardingBloc, LocalAIOnBoardingState>( child: BlocBuilder<LocalAIOnBoardingBloc, LocalAIOnBoardingState>(
builder: (context, state) { builder: (context, state) {
// Show the local AI settings if the user has purchased the AI Local plan // Show the local AI settings if the user has purchased the AI Local plan
if (kDebugMode || state.isPurchaseAILocal) { if (kDebugMode || state.isPurchaseAILocal) {
return const LocalAISetting(); return const LocalAISetting();
} else { } else {
if (member.role.isOwner) { if (currentWorkspaceMemberRole?.isOwner ?? false) {
// Show the upgrade to AI Local plan button if the user has not purchased the AI Local plan // Show the upgrade to AI Local plan button if the user has not purchased the AI Local plan
return _UpgradeToAILocalPlan( return _UpgradeToAILocalPlan(
onTap: () { onTap: () {

View File

@ -1,7 +1,5 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter/material.dart';
import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/document/application/document_appearance_cubit.dart'; import 'package:appflowy/plugins/document/application/document_appearance_cubit.dart';
@ -45,6 +43,7 @@ import 'package:flowy_infra/theme.dart';
import 'package:flowy_infra/theme_extension.dart'; import 'package:flowy_infra/theme_extension.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flowy_infra_ui/style_widget/hover.dart'; import 'package:flowy_infra_ui/style_widget/hover.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:google_fonts/google_fonts.dart'; import 'package:google_fonts/google_fonts.dart';
@ -52,11 +51,11 @@ class SettingsWorkspaceView extends StatelessWidget {
const SettingsWorkspaceView({ const SettingsWorkspaceView({
super.key, super.key,
required this.userProfile, required this.userProfile,
this.workspaceMember, this.currentWorkspaceMemberRole,
}); });
final UserProfilePB userProfile; final UserProfilePB userProfile;
final WorkspaceMemberPB? workspaceMember; final AFRolePB? currentWorkspaceMemberRole;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -93,7 +92,11 @@ class SettingsWorkspaceView extends StatelessWidget {
SettingsCategory( SettingsCategory(
title: LocaleKeys.settings_workspacePage_workspaceName_title title: LocaleKeys.settings_workspacePage_workspaceName_title
.tr(), .tr(),
children: [_WorkspaceNameSetting(member: workspaceMember)], children: [
_WorkspaceNameSetting(
currentWorkspaceMemberRole: currentWorkspaceMemberRole,
),
],
), ),
const SettingsCategorySpacer(), const SettingsCategorySpacer(),
SettingsCategory( SettingsCategory(
@ -104,7 +107,7 @@ class SettingsWorkspaceView extends StatelessWidget {
.tr(), .tr(),
children: [ children: [
_WorkspaceIconSetting( _WorkspaceIconSetting(
enableEdit: workspaceMember?.role.isOwner ?? false, enableEdit: currentWorkspaceMemberRole?.isOwner ?? false,
workspace: state.workspace, workspace: state.workspace,
), ),
], ],
@ -185,14 +188,14 @@ class SettingsWorkspaceView extends StatelessWidget {
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
onPressed: () => showConfirmDialog( onPressed: () => showConfirmDialog(
context: context, context: context,
title: workspaceMember?.role.isOwner ?? false title: currentWorkspaceMemberRole?.isOwner ?? false
? LocaleKeys ? LocaleKeys
.settings_workspacePage_deleteWorkspacePrompt_title .settings_workspacePage_deleteWorkspacePrompt_title
.tr() .tr()
: LocaleKeys : LocaleKeys
.settings_workspacePage_leaveWorkspacePrompt_title .settings_workspacePage_leaveWorkspacePrompt_title
.tr(), .tr(),
description: workspaceMember?.role.isOwner ?? false description: currentWorkspaceMemberRole?.isOwner ?? false
? LocaleKeys ? LocaleKeys
.settings_workspacePage_deleteWorkspacePrompt_content .settings_workspacePage_deleteWorkspacePrompt_content
.tr() .tr()
@ -201,13 +204,13 @@ class SettingsWorkspaceView extends StatelessWidget {
.tr(), .tr(),
style: ConfirmPopupStyle.cancelAndOk, style: ConfirmPopupStyle.cancelAndOk,
onConfirm: () => context.read<WorkspaceSettingsBloc>().add( onConfirm: () => context.read<WorkspaceSettingsBloc>().add(
workspaceMember?.role.isOwner ?? false currentWorkspaceMemberRole?.isOwner ?? false
? const WorkspaceSettingsEvent.deleteWorkspace() ? const WorkspaceSettingsEvent.deleteWorkspace()
: const WorkspaceSettingsEvent.leaveWorkspace(), : const WorkspaceSettingsEvent.leaveWorkspace(),
), ),
), ),
buttonType: SingleSettingsButtonType.danger, buttonType: SingleSettingsButtonType.danger,
buttonLabel: workspaceMember?.role.isOwner ?? false buttonLabel: currentWorkspaceMemberRole?.isOwner ?? false
? LocaleKeys ? LocaleKeys
.settings_workspacePage_manageWorkspace_deleteWorkspace .settings_workspacePage_manageWorkspace_deleteWorkspace
.tr() .tr()
@ -225,9 +228,11 @@ class SettingsWorkspaceView extends StatelessWidget {
} }
class _WorkspaceNameSetting extends StatefulWidget { class _WorkspaceNameSetting extends StatefulWidget {
const _WorkspaceNameSetting({this.member}); const _WorkspaceNameSetting({
this.currentWorkspaceMemberRole,
});
final WorkspaceMemberPB? member; final AFRolePB? currentWorkspaceMemberRole;
@override @override
State<_WorkspaceNameSetting> createState() => _WorkspaceNameSettingState(); State<_WorkspaceNameSetting> createState() => _WorkspaceNameSettingState();
@ -255,7 +260,8 @@ class _WorkspaceNameSettingState extends State<_WorkspaceNameSetting> {
} }
}, },
builder: (_, state) { builder: (_, state) {
if (widget.member == null || !widget.member!.role.isOwner) { if (widget.currentWorkspaceMemberRole == null ||
!widget.currentWorkspaceMemberRole!.isOwner) {
return Padding( return Padding(
padding: const EdgeInsets.symmetric(vertical: 2.5), padding: const EdgeInsets.symmetric(vertical: 2.5),
child: FlowyText.regular( child: FlowyText.regular(

View File

@ -109,7 +109,7 @@ class _HomePageButton extends StatelessWidget {
final isOwner = context final isOwner = context
.watch<UserWorkspaceBloc>() .watch<UserWorkspaceBloc>()
.state .state
.currentWorkspaceMember .currentWorkspace
?.role ?.role
.isOwner ?? .isOwner ??
false; false;
@ -227,7 +227,7 @@ class _FreePlanUpgradeButton extends StatelessWidget {
final isOwner = context final isOwner = context
.watch<UserWorkspaceBloc>() .watch<UserWorkspaceBloc>()
.state .state
.currentWorkspaceMember .currentWorkspace
?.role ?.role
.isOwner ?? .isOwner ??
false; false;

View File

@ -82,7 +82,7 @@ class _DomainMoreActionState extends State<DomainMoreAction> {
final isOwner = context final isOwner = context
.watch<UserWorkspaceBloc>() .watch<UserWorkspaceBloc>()
.state .state
.currentWorkspaceMember .currentWorkspace
?.role ?.role
.isOwner ?? .isOwner ??
false; false;

View File

@ -57,7 +57,7 @@ class SettingsDialog extends StatelessWidget {
return BlocProvider<SettingsDialogBloc>( return BlocProvider<SettingsDialogBloc>(
create: (context) => SettingsDialogBloc( create: (context) => SettingsDialogBloc(
user, user,
context.read<UserWorkspaceBloc>().state.currentWorkspaceMember, context.read<UserWorkspaceBloc>().state.currentWorkspace?.role,
initPage: initPage, initPage: initPage,
)..add(const SettingsDialogEvent.initial()), )..add(const SettingsDialogEvent.initial()),
child: BlocBuilder<SettingsDialogBloc, SettingsDialogState>( child: BlocBuilder<SettingsDialogBloc, SettingsDialogState>(
@ -80,10 +80,6 @@ class SettingsDialog extends StatelessWidget {
currentPage: currentPage:
context.read<SettingsDialogBloc>().state.page, context.read<SettingsDialogBloc>().state.page,
isBillingEnabled: state.isBillingEnabled, isBillingEnabled: state.isBillingEnabled,
member: context
.read<UserWorkspaceBloc>()
.state
.currentWorkspaceMember,
), ),
), ),
Expanded( Expanded(
@ -98,7 +94,8 @@ class SettingsDialog extends StatelessWidget {
context context
.read<UserWorkspaceBloc>() .read<UserWorkspaceBloc>()
.state .state
.currentWorkspaceMember, .currentWorkspace
?.role,
), ),
), ),
], ],
@ -114,7 +111,7 @@ class SettingsDialog extends StatelessWidget {
String workspaceId, String workspaceId,
SettingsPage page, SettingsPage page,
UserProfilePB user, UserProfilePB user,
WorkspaceMemberPB? member, AFRolePB? currentWorkspaceMemberRole,
) { ) {
switch (page) { switch (page) {
case SettingsPage.account: case SettingsPage.account:
@ -126,7 +123,7 @@ class SettingsDialog extends StatelessWidget {
case SettingsPage.workspace: case SettingsPage.workspace:
return SettingsWorkspaceView( return SettingsWorkspaceView(
userProfile: user, userProfile: user,
workspaceMember: member, currentWorkspaceMemberRole: currentWorkspaceMemberRole,
); );
case SettingsPage.manageData: case SettingsPage.manageData:
return SettingsManageDataView(userProfile: user); return SettingsManageDataView(userProfile: user);
@ -140,7 +137,7 @@ class SettingsDialog extends StatelessWidget {
if (user.authenticator == AuthenticatorPB.AppFlowyCloud) { if (user.authenticator == AuthenticatorPB.AppFlowyCloud) {
return SettingsAIView( return SettingsAIView(
userProfile: user, userProfile: user,
member: member, currentWorkspaceMemberRole: currentWorkspaceMemberRole,
workspaceId: workspaceId, workspaceId: workspaceId,
); );
} else { } else {

View File

@ -16,14 +16,12 @@ class SettingsMenu extends StatelessWidget {
required this.currentPage, required this.currentPage,
required this.userProfile, required this.userProfile,
required this.isBillingEnabled, required this.isBillingEnabled,
this.member,
}); });
final Function changeSelectedPage; final Function changeSelectedPage;
final SettingsPage currentPage; final SettingsPage currentPage;
final UserProfilePB userProfile; final UserProfilePB userProfile;
final bool isBillingEnabled; final bool isBillingEnabled;
final WorkspaceMemberPB? member;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {

View File

@ -667,6 +667,7 @@ fn to_user_workspace(af_workspace: AFWorkspace) -> UserWorkspace {
workspace_database_id: af_workspace.database_storage_id.to_string(), workspace_database_id: af_workspace.database_storage_id.to_string(),
icon: af_workspace.icon, icon: af_workspace.icon,
member_count: af_workspace.member_count.unwrap_or(0), member_count: af_workspace.member_count.unwrap_or(0),
role: af_workspace.role.map(|r| r.into()),
} }
} }

View File

@ -34,7 +34,7 @@ impl UserCloudService for LocalServerUserAuthServiceImpl {
let params = params.unbox_or_error::<SignUpParams>()?; let params = params.unbox_or_error::<SignUpParams>()?;
let uid = ID_GEN.lock().await.next_id(); let uid = ID_GEN.lock().await.next_id();
let workspace_id = uuid::Uuid::new_v4().to_string(); let workspace_id = uuid::Uuid::new_v4().to_string();
let user_workspace = UserWorkspace::new(&workspace_id, uid); let user_workspace = UserWorkspace::new_local(&workspace_id, uid);
let user_name = if params.name.is_empty() { let user_name = if params.name.is_empty() {
DEFAULT_USER_NAME() DEFAULT_USER_NAME()
} else { } else {
@ -214,6 +214,7 @@ fn make_user_workspace() -> UserWorkspace {
created_at: Default::default(), created_at: Default::default(),
workspace_database_id: uuid::Uuid::new_v4().to_string(), workspace_database_id: uuid::Uuid::new_v4().to_string(),
icon: "".to_string(), icon: "".to_string(),
member_count: 0, member_count: 1,
role: None,
} }
} }

View File

@ -0,0 +1 @@
ALTER TABLE user_workspace_table DROP COLUMN role;

View File

@ -0,0 +1 @@
ALTER TABLE user_workspace_table ADD COLUMN role INT;

View File

@ -102,6 +102,7 @@ diesel::table! {
database_storage_id -> Text, database_storage_id -> Text,
icon -> Text, icon -> Text,
member_count -> BigInt, member_count -> BigInt,
role -> Nullable<Integer>,
} }
} }

View File

@ -2,6 +2,7 @@ use std::str::FromStr;
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
pub use client_api::entity::billing_dto::RecurringInterval; pub use client_api::entity::billing_dto::RecurringInterval;
use client_api::entity::AFRole;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_json::Value; use serde_json::Value;
use serde_repr::*; use serde_repr::*;
@ -145,10 +146,12 @@ pub struct UserWorkspace {
pub icon: String, pub icon: String,
#[serde(default)] #[serde(default)]
pub member_count: i64, pub member_count: i64,
#[serde(default)]
pub role: Option<Role>,
} }
impl UserWorkspace { impl UserWorkspace {
pub fn new(workspace_id: &str, _uid: i64) -> Self { pub fn new_local(workspace_id: &str, _uid: i64) -> Self {
Self { Self {
id: workspace_id.to_string(), id: workspace_id.to_string(),
name: "".to_string(), name: "".to_string(),
@ -156,6 +159,7 @@ impl UserWorkspace {
workspace_database_id: Uuid::new_v4().to_string(), workspace_database_id: Uuid::new_v4().to_string(),
icon: "".to_string(), icon: "".to_string(),
member_count: 1, member_count: 1,
role: None,
} }
} }
} }
@ -420,6 +424,16 @@ impl From<Role> for i32 {
} }
} }
impl From<AFRole> for Role {
fn from(value: AFRole) -> Self {
match value {
AFRole::Owner => Role::Owner,
AFRole::Member => Role::Member,
AFRole::Guest => Role::Guest,
}
}
}
pub struct WorkspaceMember { pub struct WorkspaceMember {
pub email: String, pub email: String,
pub role: Role, pub role: Role,

View File

@ -75,7 +75,8 @@ impl<'de> Visitor<'de> for SessionVisitor {
// For historical reasons, the database_storage_id is constructed by the user_id. // For historical reasons, the database_storage_id is constructed by the user_id.
workspace_database_id: STANDARD.encode(format!("{}:user:database", user_id)), workspace_database_id: STANDARD.encode(format!("{}:user:database", user_id)),
icon: "".to_owned(), icon: "".to_owned(),
member_count: 0, member_count: 1,
role: None,
}) })
} }
} }

View File

@ -11,6 +11,7 @@ use crate::entities::{AIModelPB, AuthenticatorPB};
use crate::errors::ErrorCode; use crate::errors::ErrorCode;
use super::parser::UserStabilityAIKey; use super::parser::UserStabilityAIKey;
use super::AFRolePB;
#[derive(Default, ProtoBuf)] #[derive(Default, ProtoBuf)]
pub struct UserTokenPB { pub struct UserTokenPB {
@ -237,6 +238,9 @@ pub struct UserWorkspacePB {
#[pb(index = 5)] #[pb(index = 5)]
pub member_count: i64, pub member_count: i64,
#[pb(index = 6, one_of)]
pub role: Option<AFRolePB>,
} }
impl From<UserWorkspace> for UserWorkspacePB { impl From<UserWorkspace> for UserWorkspacePB {
@ -247,6 +251,7 @@ impl From<UserWorkspace> for UserWorkspacePB {
created_at_timestamp: value.created_at.timestamp(), created_at_timestamp: value.created_at.timestamp(),
icon: value.icon, icon: value.icon,
member_count: value.member_count, member_count: value.member_count,
role: value.role.map(AFRolePB::from),
} }
} }
} }

View File

@ -147,7 +147,7 @@ pub struct UpdateWorkspaceMemberPB {
} }
// Workspace Role // Workspace Role
#[derive(ProtoBuf_Enum, Clone, Default)] #[derive(Debug, ProtoBuf_Enum, Clone, Default)]
pub enum AFRolePB { pub enum AFRolePB {
Owner = 0, Owner = 0,
Member = 1, Member = 1,

View File

@ -17,6 +17,7 @@ pub struct UserWorkspaceTable {
pub database_storage_id: String, pub database_storage_id: String,
pub icon: String, pub icon: String,
pub member_count: i64, pub member_count: i64,
pub role: Option<i32>,
} }
pub fn get_user_workspace_op(workspace_id: &str, mut conn: DBConnection) -> Option<UserWorkspace> { pub fn get_user_workspace_op(workspace_id: &str, mut conn: DBConnection) -> Option<UserWorkspace> {
@ -94,6 +95,7 @@ impl TryFrom<(i64, &UserWorkspace)> for UserWorkspaceTable {
database_storage_id: value.1.workspace_database_id.clone(), database_storage_id: value.1.workspace_database_id.clone(),
icon: value.1.icon.clone(), icon: value.1.icon.clone(),
member_count: value.1.member_count, member_count: value.1.member_count,
role: value.1.role.clone().map(|v| v as i32),
}) })
} }
} }
@ -110,6 +112,7 @@ impl From<UserWorkspaceTable> for UserWorkspace {
workspace_database_id: value.database_storage_id, workspace_database_id: value.database_storage_id,
icon: value.icon, icon: value.icon,
member_count: value.member_count, member_count: value.member_count,
role: value.role.map(|v| v.into()),
} }
} }
} }

View File

@ -730,6 +730,7 @@ pub fn save_all_user_workspaces(
user_workspace_table::database_storage_id.eq(&user_workspace.database_storage_id), user_workspace_table::database_storage_id.eq(&user_workspace.database_storage_id),
user_workspace_table::icon.eq(&user_workspace.icon), user_workspace_table::icon.eq(&user_workspace.icon),
user_workspace_table::member_count.eq(&user_workspace.member_count), user_workspace_table::member_count.eq(&user_workspace.member_count),
user_workspace_table::role.eq(&user_workspace.role),
)) ))
.execute(conn)?; .execute(conn)?;