mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2025-10-02 11:53:17 +00:00
feat: display joined at and user avatar in members page (#7809)
* feat: new settings page design * feat: support display member join at and avatar * chore: bump version 0.9.0 * chore: update translations
This commit is contained in:
parent
4626c18ced
commit
1977cf6637
@ -26,7 +26,7 @@ CARGO_MAKE_EXTEND_WORKSPACE_MAKEFILE = true
|
|||||||
CARGO_MAKE_CRATE_FS_NAME = "dart_ffi"
|
CARGO_MAKE_CRATE_FS_NAME = "dart_ffi"
|
||||||
CARGO_MAKE_CRATE_NAME = "dart-ffi"
|
CARGO_MAKE_CRATE_NAME = "dart-ffi"
|
||||||
LIB_NAME = "dart_ffi"
|
LIB_NAME = "dart_ffi"
|
||||||
APPFLOWY_VERSION = "0.8.9"
|
APPFLOWY_VERSION = "0.9.0"
|
||||||
FLUTTER_DESKTOP_FEATURES = "dart"
|
FLUTTER_DESKTOP_FEATURES = "dart"
|
||||||
PRODUCT_NAME = "AppFlowy"
|
PRODUCT_NAME = "AppFlowy"
|
||||||
MACOSX_DEPLOYMENT_TARGET = "11.0"
|
MACOSX_DEPLOYMENT_TARGET = "11.0"
|
||||||
|
@ -27,6 +27,7 @@ import 'package:appflowy/workspace/presentation/settings/widgets/web_url_hint_wi
|
|||||||
import 'package:appflowy/workspace/presentation/widgets/dialogs.dart';
|
import 'package:appflowy/workspace/presentation/widgets/dialogs.dart';
|
||||||
import 'package:appflowy_backend/log.dart';
|
import 'package:appflowy_backend/log.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
|
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
|
||||||
|
import 'package:appflowy_ui/appflowy_ui.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@ -60,6 +61,7 @@ class SettingsDialog extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final width = MediaQuery.of(context).size.width * 0.6;
|
final width = MediaQuery.of(context).size.width * 0.6;
|
||||||
|
final theme = AppFlowyTheme.of(context);
|
||||||
return BlocProvider<SettingsDialogBloc>(
|
return BlocProvider<SettingsDialogBloc>(
|
||||||
create: (context) => SettingsDialogBloc(
|
create: (context) => SettingsDialogBloc(
|
||||||
user,
|
user,
|
||||||
@ -72,12 +74,12 @@ class SettingsDialog extends StatelessWidget {
|
|||||||
constraints: const BoxConstraints(minWidth: 564),
|
constraints: const BoxConstraints(minWidth: 564),
|
||||||
child: ScaffoldMessenger(
|
child: ScaffoldMessenger(
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: theme.backgroundColorScheme.primary,
|
||||||
body: Row(
|
body: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: 200,
|
width: 204,
|
||||||
child: SettingsMenu(
|
child: SettingsMenu(
|
||||||
userProfile: user,
|
userProfile: user,
|
||||||
changeSelectedPage: (index) => context
|
changeSelectedPage: (index) => context
|
||||||
@ -88,6 +90,10 @@ class SettingsDialog extends StatelessWidget {
|
|||||||
isBillingEnabled: state.isBillingEnabled,
|
isBillingEnabled: state.isBillingEnabled,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
AFDivider(
|
||||||
|
axis: Axis.vertical,
|
||||||
|
color: theme.borderColorScheme.primary,
|
||||||
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: getSettingsView(
|
child: getSettingsView(
|
||||||
context
|
context
|
||||||
|
@ -88,17 +88,18 @@ class _Description extends StatelessWidget {
|
|||||||
await showConfirmDialog(
|
await showConfirmDialog(
|
||||||
context: context,
|
context: context,
|
||||||
style: ConfirmPopupStyle.cancelAndOk,
|
style: ConfirmPopupStyle.cancelAndOk,
|
||||||
title: 'Reset the invite link?',
|
title: LocaleKeys.settings_appearance_members_resetInviteLink.tr(),
|
||||||
description:
|
description: LocaleKeys
|
||||||
'Resetting will deactivate the current link for all space members and generate a new one. The old link will no longer be available.',
|
.settings_appearance_members_resetInviteLinkDescription
|
||||||
confirmLabel: 'Reset',
|
.tr(),
|
||||||
|
confirmLabel: LocaleKeys.settings_appearance_members_reset.tr(),
|
||||||
onConfirm: () {
|
onConfirm: () {
|
||||||
context.read<WorkspaceMemberBloc>().add(
|
context.read<WorkspaceMemberBloc>().add(
|
||||||
const WorkspaceMemberEvent.generateInviteLink(),
|
const WorkspaceMemberEvent.generateInviteLink(),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
confirmButtonBuilder: (_) => AFFilledTextButton.destructive(
|
confirmButtonBuilder: (_) => AFFilledTextButton.destructive(
|
||||||
text: 'Reset',
|
text: LocaleKeys.settings_appearance_members_reset.tr(),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
context.read<WorkspaceMemberBloc>().add(
|
context.read<WorkspaceMemberBloc>().add(
|
||||||
const WorkspaceMemberEvent.generateInviteLink(),
|
const WorkspaceMemberEvent.generateInviteLink(),
|
||||||
@ -145,7 +146,8 @@ class _CopyLinkButton extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
showToastNotification(
|
showToastNotification(
|
||||||
message: LocaleKeys.shareAction_copyLinkFailed.tr(),
|
message: 'You haven\'t generated an invite link yet.',
|
||||||
|
type: ToastificationType.error,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -10,6 +10,7 @@ import 'package:appflowy/workspace/presentation/settings/widgets/members/inivita
|
|||||||
import 'package:appflowy/workspace/presentation/settings/widgets/members/workspace_member_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/dialogs.dart';
|
||||||
import 'package:appflowy/workspace/presentation/widgets/pop_up_action.dart';
|
import 'package:appflowy/workspace/presentation/widgets/pop_up_action.dart';
|
||||||
|
import 'package:appflowy/workspace/presentation/widgets/user_avatar.dart';
|
||||||
import 'package:appflowy_backend/log.dart';
|
import 'package:appflowy_backend/log.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-error/code.pbenum.dart';
|
import 'package:appflowy_backend/protobuf/flowy-error/code.pbenum.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
|
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
|
||||||
@ -318,6 +319,7 @@ class _MemberListHeader extends StatelessWidget {
|
|||||||
return Row(
|
return Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
|
flex: 4,
|
||||||
child: Text(
|
child: Text(
|
||||||
LocaleKeys.settings_appearance_members_user.tr(),
|
LocaleKeys.settings_appearance_members_user.tr(),
|
||||||
style: theme.textStyle.body.standard(
|
style: theme.textStyle.body.standard(
|
||||||
@ -326,6 +328,7 @@ class _MemberListHeader extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
|
flex: 2,
|
||||||
child: Text(
|
child: Text(
|
||||||
LocaleKeys.settings_appearance_members_role.tr(),
|
LocaleKeys.settings_appearance_members_role.tr(),
|
||||||
style: theme.textStyle.body.standard(
|
style: theme.textStyle.body.standard(
|
||||||
@ -334,6 +337,7 @@ class _MemberListHeader extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
|
flex: 3,
|
||||||
child: Text(
|
child: Text(
|
||||||
LocaleKeys.settings_accountPage_email_title.tr(),
|
LocaleKeys.settings_accountPage_email_title.tr(),
|
||||||
style: theme.textStyle.body.standard(
|
style: theme.textStyle.body.standard(
|
||||||
@ -364,14 +368,39 @@ class _MemberItem extends StatelessWidget {
|
|||||||
return Row(
|
return Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
flex: 4,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
UserAvatar(
|
||||||
|
iconUrl: member.avatarUrl,
|
||||||
|
name: member.name,
|
||||||
|
size: 24,
|
||||||
|
fontSize: 12,
|
||||||
|
emojiFontSize: 20,
|
||||||
|
),
|
||||||
|
HSpace(8),
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
member.name,
|
member.name,
|
||||||
style: theme.textStyle.body.enhanced(
|
style: theme.textStyle.body.enhanced(
|
||||||
color: theme.textColorScheme.primary,
|
color: theme.textColorScheme.primary,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
Text(
|
||||||
|
_formatJoinedDate(member.joinedAt.toInt()),
|
||||||
|
style: theme.textStyle.caption.standard(
|
||||||
|
color: theme.textColorScheme.secondary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
|
flex: 2,
|
||||||
child: member.role.isOwner || !myRole.canUpdate
|
child: member.role.isOwner || !myRole.canUpdate
|
||||||
? Text(
|
? Text(
|
||||||
member.role.description,
|
member.role.description,
|
||||||
@ -384,6 +413,7 @@ class _MemberItem extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
|
flex: 3,
|
||||||
child: FlowyTooltip(
|
child: FlowyTooltip(
|
||||||
message: member.email,
|
message: member.email,
|
||||||
child: Text(
|
child: Text(
|
||||||
@ -403,6 +433,11 @@ class _MemberItem extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String _formatJoinedDate(int joinedAt) {
|
||||||
|
final date = DateTime.fromMillisecondsSinceEpoch(joinedAt * 1000);
|
||||||
|
return 'Joined on ${DateFormat('MMM d, y').format(date)}';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum _MemberMoreAction {
|
enum _MemberMoreAction {
|
||||||
@ -428,7 +463,7 @@ class _MemberMoreActionList extends StatelessWidget {
|
|||||||
return FlowyButton(
|
return FlowyButton(
|
||||||
useIntrinsicWidth: true,
|
useIntrinsicWidth: true,
|
||||||
text: const FlowySvg(
|
text: const FlowySvg(
|
||||||
FlowySvgs.three_dots_vertical_s,
|
FlowySvgs.three_dots_s,
|
||||||
),
|
),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
controller.show();
|
controller.show();
|
||||||
|
@ -4,6 +4,7 @@ import 'package:appflowy/shared/feature_flags.dart';
|
|||||||
import 'package:appflowy/workspace/application/settings/settings_dialog_bloc.dart';
|
import 'package:appflowy/workspace/application/settings/settings_dialog_bloc.dart';
|
||||||
import 'package:appflowy/workspace/presentation/settings/widgets/settings_menu_element.dart';
|
import 'package:appflowy/workspace/presentation/settings/widgets/settings_menu_element.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
|
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
|
||||||
|
import 'package:appflowy_ui/appflowy_ui.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
@ -25,28 +26,27 @@ class SettingsMenu extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final theme = AppFlowyTheme.of(context);
|
||||||
// Column > Expanded for full size no matter the content
|
// Column > Expanded for full size no matter the content
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Container(
|
child: DecoratedBox(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 8) +
|
|
||||||
const EdgeInsets.only(left: 8, right: 4),
|
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Theme.of(context).colorScheme.surfaceContainerHighest,
|
color: theme.backgroundColorScheme.secondary,
|
||||||
borderRadius: const BorderRadiusDirectional.only(
|
borderRadius: const BorderRadiusDirectional.only(
|
||||||
topStart: Radius.circular(8),
|
topStart: Radius.circular(8),
|
||||||
bottomStart: Radius.circular(8),
|
bottomStart: Radius.circular(8),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
// Right padding is added to make the scrollbar centered
|
padding: EdgeInsets.symmetric(
|
||||||
// in the space between the menu and the content
|
vertical: 24,
|
||||||
padding: const EdgeInsets.only(right: 4) +
|
horizontal: theme.spacing.l,
|
||||||
const EdgeInsets.symmetric(vertical: 16),
|
),
|
||||||
physics: const ClampingScrollPhysics(),
|
physics: const ClampingScrollPhysics(),
|
||||||
child: SeparatedColumn(
|
child: SeparatedColumn(
|
||||||
separatorBuilder: () => const VSpace(16),
|
separatorBuilder: () => VSpace(theme.spacing.xs),
|
||||||
children: [
|
children: [
|
||||||
SettingsMenuElement(
|
SettingsMenuElement(
|
||||||
page: SettingsPage.account,
|
page: SettingsPage.account,
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
import 'package:appflowy/workspace/application/settings/settings_dialog_bloc.dart';
|
import 'package:appflowy/workspace/application/settings/settings_dialog_bloc.dart';
|
||||||
import 'package:flowy_infra/size.dart';
|
import 'package:appflowy_ui/appflowy_ui.dart';
|
||||||
import 'package:flowy_infra/theme_extension.dart';
|
import 'package:flowy_infra_ui/widget/spacing.dart';
|
||||||
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
|
||||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class SettingsMenuElement extends StatelessWidget {
|
class SettingsMenuElement extends StatelessWidget {
|
||||||
@ -23,42 +21,67 @@ class SettingsMenuElement extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return FlowyHover(
|
final theme = AppFlowyTheme.of(context);
|
||||||
isSelected: () => page == selectedPage,
|
return AFBaseButton(
|
||||||
resetHoverOnRebuild: false,
|
|
||||||
style: HoverStyle(
|
|
||||||
hoverColor: AFThemeExtension.of(context).greyHover,
|
|
||||||
borderRadius: BorderRadius.circular(4),
|
|
||||||
),
|
|
||||||
builder: (_, isHovering) => ListTile(
|
|
||||||
dense: true,
|
|
||||||
leading: iconWidget(
|
|
||||||
isHovering || page == selectedPage
|
|
||||||
? Theme.of(context).colorScheme.onSurface
|
|
||||||
: AFThemeExtension.of(context).textColor,
|
|
||||||
),
|
|
||||||
onTap: () => changeSelectedPage(page),
|
onTap: () => changeSelectedPage(page),
|
||||||
selected: page == selectedPage,
|
padding: EdgeInsets.all(theme.spacing.m),
|
||||||
selectedColor: Theme.of(context).colorScheme.onSurface,
|
borderRadius: theme.borderRadius.m,
|
||||||
selectedTileColor: Theme.of(context).colorScheme.primary,
|
borderColor: (_, __, ___, ____) => theme.fillColorScheme.transparent,
|
||||||
shape: RoundedRectangleBorder(
|
backgroundColor: (_, isHovering, __) {
|
||||||
borderRadius: BorderRadius.circular(5),
|
if (isHovering) {
|
||||||
),
|
return theme.fillColorScheme.primaryAlpha5;
|
||||||
minLeadingWidth: 0,
|
} else if (page == selectedPage) {
|
||||||
title: FlowyText.medium(
|
return theme.fillColorScheme.themeSelect;
|
||||||
|
}
|
||||||
|
return theme.fillColorScheme.transparent;
|
||||||
|
},
|
||||||
|
builder: (_, __, ___) {
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
icon,
|
||||||
|
HSpace(theme.spacing.m),
|
||||||
|
Text(
|
||||||
label,
|
label,
|
||||||
fontSize: FontSizes.s14,
|
style: theme.textStyle.body.standard(
|
||||||
overflow: TextOverflow.ellipsis,
|
color: theme.textColorScheme.primary,
|
||||||
color: page == selectedPage
|
|
||||||
? Theme.of(context).colorScheme.onSurface
|
|
||||||
: null,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget iconWidget(Color color) => IconTheme(
|
// return FlowyHover(
|
||||||
data: IconThemeData(color: color),
|
// isSelected: () => page == selectedPage,
|
||||||
child: icon,
|
// resetHoverOnRebuild: false,
|
||||||
);
|
// style: HoverStyle(
|
||||||
|
// hoverColor: AFThemeExtension.of(context).greyHover,
|
||||||
|
// borderRadius: BorderRadius.circular(4),
|
||||||
|
// ),
|
||||||
|
// builder: (_, isHovering) => ListTile(
|
||||||
|
// dense: true,
|
||||||
|
// leading: iconWidget(
|
||||||
|
// isHovering || page == selectedPage
|
||||||
|
// ? Theme.of(context).colorScheme.onSurface
|
||||||
|
// : AFThemeExtension.of(context).textColor,
|
||||||
|
// ),
|
||||||
|
// onTap: () => changeSelectedPage(page),
|
||||||
|
// selected: page == selectedPage,
|
||||||
|
// selectedColor: Theme.of(context).colorScheme.onSurface,
|
||||||
|
// selectedTileColor: Theme.of(context).colorScheme.primary,
|
||||||
|
// shape: RoundedRectangleBorder(
|
||||||
|
// borderRadius: BorderRadius.circular(5),
|
||||||
|
// ),
|
||||||
|
// minLeadingWidth: 0,
|
||||||
|
// title: FlowyText.medium(
|
||||||
|
// label,
|
||||||
|
// fontSize: FontSizes.s14,
|
||||||
|
// overflow: TextOverflow.ellipsis,
|
||||||
|
// color: page == selectedPage
|
||||||
|
// ? Theme.of(context).colorScheme.onSurface
|
||||||
|
// : null,
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// );
|
||||||
}
|
}
|
||||||
|
@ -17,12 +17,14 @@ class UserAvatar extends StatelessWidget {
|
|||||||
required this.fontSize,
|
required this.fontSize,
|
||||||
this.isHovering = false,
|
this.isHovering = false,
|
||||||
this.decoration,
|
this.decoration,
|
||||||
|
this.emojiFontSize,
|
||||||
});
|
});
|
||||||
|
|
||||||
final String iconUrl;
|
final String iconUrl;
|
||||||
final String name;
|
final String name;
|
||||||
final double size;
|
final double size;
|
||||||
final double fontSize;
|
final double fontSize;
|
||||||
|
final double? emojiFontSize;
|
||||||
final Decoration? decoration;
|
final Decoration? decoration;
|
||||||
|
|
||||||
// If true, a border will be applied on top of the avatar
|
// If true, a border will be applied on top of the avatar
|
||||||
@ -127,7 +129,10 @@ class UserAvatar extends StatelessWidget {
|
|||||||
FlowySvgData('emoji/$iconUrl'),
|
FlowySvgData('emoji/$iconUrl'),
|
||||||
blendMode: null,
|
blendMode: null,
|
||||||
)
|
)
|
||||||
: FlowyText.emoji(iconUrl, fontSize: fontSize),
|
: FlowyText.emoji(
|
||||||
|
iconUrl,
|
||||||
|
fontSize: emojiFontSize ?? fontSize,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -4,7 +4,7 @@ description: Bring projects, wikis, and teams together with AI. AppFlowy is an
|
|||||||
your data. The best open source alternative to Notion.
|
your data. The best open source alternative to Notion.
|
||||||
publish_to: "none"
|
publish_to: "none"
|
||||||
|
|
||||||
version: 0.8.9
|
version: 0.9.0
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
flutter: ">=3.27.4"
|
flutter: ">=3.27.4"
|
||||||
|
@ -1353,7 +1353,7 @@
|
|||||||
"generateANewLink": "generate a new link",
|
"generateANewLink": "generate a new link",
|
||||||
"inviteMemberByEmail": "Invite member by email",
|
"inviteMemberByEmail": "Invite member by email",
|
||||||
"inviteMemberHintText": "Invite by email",
|
"inviteMemberHintText": "Invite by email",
|
||||||
"resetInviteLink": "Reset the invite link",
|
"resetInviteLink": "Reset the invite link?",
|
||||||
"resetInviteLinkDescription": "Resetting will deactivate the current link for all space members and generate a new one. The previous link can only be managed through the",
|
"resetInviteLinkDescription": "Resetting will deactivate the current link for all space members and generate a new one. The previous link can only be managed through the",
|
||||||
"adminPanel": "Admin Panel",
|
"adminPanel": "Admin Panel",
|
||||||
"reset": "Reset",
|
"reset": "Reset",
|
||||||
|
24
frontend/rust-lib/Cargo.lock
generated
24
frontend/rust-lib/Cargo.lock
generated
@ -493,7 +493,7 @@ checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "app-error"
|
name = "app-error"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=6c6f1c5cc3ce2161c247f63f6132361da8dc0a32#6c6f1c5cc3ce2161c247f63f6132361da8dc0a32"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=954c323#954c32332487f5e17a7fb5be0bc339db1cb00e17"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bincode",
|
"bincode",
|
||||||
@ -513,7 +513,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "appflowy-ai-client"
|
name = "appflowy-ai-client"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=6c6f1c5cc3ce2161c247f63f6132361da8dc0a32#6c6f1c5cc3ce2161c247f63f6132361da8dc0a32"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=954c323#954c32332487f5e17a7fb5be0bc339db1cb00e17"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bytes",
|
"bytes",
|
||||||
@ -1159,7 +1159,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "client-api"
|
name = "client-api"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=6c6f1c5cc3ce2161c247f63f6132361da8dc0a32#6c6f1c5cc3ce2161c247f63f6132361da8dc0a32"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=954c323#954c32332487f5e17a7fb5be0bc339db1cb00e17"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"again",
|
"again",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
@ -1214,7 +1214,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "client-api-entity"
|
name = "client-api-entity"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=6c6f1c5cc3ce2161c247f63f6132361da8dc0a32#6c6f1c5cc3ce2161c247f63f6132361da8dc0a32"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=954c323#954c32332487f5e17a7fb5be0bc339db1cb00e17"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"collab-entity",
|
"collab-entity",
|
||||||
"collab-rt-entity",
|
"collab-rt-entity",
|
||||||
@ -1227,7 +1227,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "client-websocket"
|
name = "client-websocket"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=6c6f1c5cc3ce2161c247f63f6132361da8dc0a32#6c6f1c5cc3ce2161c247f63f6132361da8dc0a32"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=954c323#954c32332487f5e17a7fb5be0bc339db1cb00e17"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
@ -1499,7 +1499,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-rt-entity"
|
name = "collab-rt-entity"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=6c6f1c5cc3ce2161c247f63f6132361da8dc0a32#6c6f1c5cc3ce2161c247f63f6132361da8dc0a32"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=954c323#954c32332487f5e17a7fb5be0bc339db1cb00e17"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bincode",
|
"bincode",
|
||||||
@ -1521,7 +1521,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-rt-protocol"
|
name = "collab-rt-protocol"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=6c6f1c5cc3ce2161c247f63f6132361da8dc0a32#6c6f1c5cc3ce2161c247f63f6132361da8dc0a32"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=954c323#954c32332487f5e17a7fb5be0bc339db1cb00e17"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -1969,7 +1969,7 @@ checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "database-entity"
|
name = "database-entity"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=6c6f1c5cc3ce2161c247f63f6132361da8dc0a32#6c6f1c5cc3ce2161c247f63f6132361da8dc0a32"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=954c323#954c32332487f5e17a7fb5be0bc339db1cb00e17"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bincode",
|
"bincode",
|
||||||
"bytes",
|
"bytes",
|
||||||
@ -3426,7 +3426,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "gotrue"
|
name = "gotrue"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=6c6f1c5cc3ce2161c247f63f6132361da8dc0a32#6c6f1c5cc3ce2161c247f63f6132361da8dc0a32"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=954c323#954c32332487f5e17a7fb5be0bc339db1cb00e17"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"getrandom 0.2.10",
|
"getrandom 0.2.10",
|
||||||
@ -3441,7 +3441,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "gotrue-entity"
|
name = "gotrue-entity"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=6c6f1c5cc3ce2161c247f63f6132361da8dc0a32#6c6f1c5cc3ce2161c247f63f6132361da8dc0a32"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=954c323#954c32332487f5e17a7fb5be0bc339db1cb00e17"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"app-error",
|
"app-error",
|
||||||
"jsonwebtoken",
|
"jsonwebtoken",
|
||||||
@ -4065,7 +4065,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "infra"
|
name = "infra"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=6c6f1c5cc3ce2161c247f63f6132361da8dc0a32#6c6f1c5cc3ce2161c247f63f6132361da8dc0a32"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=954c323#954c32332487f5e17a7fb5be0bc339db1cb00e17"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bytes",
|
"bytes",
|
||||||
@ -6643,7 +6643,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "shared-entity"
|
name = "shared-entity"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=6c6f1c5cc3ce2161c247f63f6132361da8dc0a32#6c6f1c5cc3ce2161c247f63f6132361da8dc0a32"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=954c323#954c32332487f5e17a7fb5be0bc339db1cb00e17"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"app-error",
|
"app-error",
|
||||||
|
@ -108,8 +108,8 @@ af-local-ai = { version = "0.1" }
|
|||||||
# Run the script.add_workspace_members:
|
# Run the script.add_workspace_members:
|
||||||
# scripts/tool/update_client_api_rev.sh new_rev_id
|
# scripts/tool/update_client_api_rev.sh new_rev_id
|
||||||
# ⚠️⚠️⚠️️
|
# ⚠️⚠️⚠️️
|
||||||
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "6c6f1c5cc3ce2161c247f63f6132361da8dc0a32" }
|
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "954c323" }
|
||||||
client-api-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "6c6f1c5cc3ce2161c247f63f6132361da8dc0a32" }
|
client-api-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "954c323" }
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
opt-level = 0
|
opt-level = 0
|
||||||
|
@ -71,6 +71,7 @@ pub fn from_af_workspace_member(member: AFWorkspaceMember) -> WorkspaceMember {
|
|||||||
role: from_af_role(member.role),
|
role: from_af_role(member.role),
|
||||||
name: member.name,
|
name: member.name,
|
||||||
avatar_url: member.avatar_url,
|
avatar_url: member.avatar_url,
|
||||||
|
joined_at: member.joined_at.map(|dt| dt.timestamp()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -248,6 +248,7 @@ impl UserCloudService for LocalServerUserServiceImpl {
|
|||||||
uid,
|
uid,
|
||||||
workspace_id: workspace_id.to_string(),
|
workspace_id: workspace_id.to_string(),
|
||||||
updated_at: chrono::Utc::now().naive_utc(),
|
updated_at: chrono::Utc::now().naive_utc(),
|
||||||
|
joined_at: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let member = WorkspaceMember::from(row.clone());
|
let member = WorkspaceMember::from(row.clone());
|
||||||
|
@ -0,0 +1,3 @@
|
|||||||
|
-- This file should undo anything in `up.sql`
|
||||||
|
ALTER TABLE workspace_members_table
|
||||||
|
DROP COLUMN joined_at;
|
@ -0,0 +1,3 @@
|
|||||||
|
-- Your SQL goes here
|
||||||
|
ALTER TABLE workspace_members_table
|
||||||
|
ADD COLUMN joined_at BIGINT DEFAULT NULL;
|
@ -120,6 +120,7 @@ diesel::table! {
|
|||||||
uid -> BigInt,
|
uid -> BigInt,
|
||||||
workspace_id -> Text,
|
workspace_id -> Text,
|
||||||
updated_at -> Timestamp,
|
updated_at -> Timestamp,
|
||||||
|
joined_at -> Nullable<BigInt>,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -370,6 +370,7 @@ pub struct WorkspaceMember {
|
|||||||
pub role: Role,
|
pub role: Role,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub avatar_url: Option<String>,
|
pub avatar_url: Option<String>,
|
||||||
|
pub joined_at: Option<i64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// represent the user awareness object id for the workspace.
|
/// represent the user awareness object id for the workspace.
|
||||||
|
@ -16,6 +16,7 @@ pub struct WorkspaceMemberTable {
|
|||||||
pub uid: i64,
|
pub uid: i64,
|
||||||
pub workspace_id: String,
|
pub workspace_id: String,
|
||||||
pub updated_at: chrono::NaiveDateTime,
|
pub updated_at: chrono::NaiveDateTime,
|
||||||
|
pub joined_at: Option<i64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<WorkspaceMemberTable> for WorkspaceMember {
|
impl From<WorkspaceMemberTable> for WorkspaceMember {
|
||||||
@ -25,6 +26,7 @@ impl From<WorkspaceMemberTable> for WorkspaceMember {
|
|||||||
role: Role::from(value.role),
|
role: Role::from(value.role),
|
||||||
name: value.name,
|
name: value.name,
|
||||||
avatar_url: value.avatar_url,
|
avatar_url: value.avatar_url,
|
||||||
|
joined_at: value.joined_at,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -111,6 +111,7 @@ pub fn insert_local_workspace(
|
|||||||
uid,
|
uid,
|
||||||
workspace_id: workspace_id.to_string(),
|
workspace_id: workspace_id.to_string(),
|
||||||
updated_at: chrono::Utc::now().naive_utc(),
|
updated_at: chrono::Utc::now().naive_utc(),
|
||||||
|
joined_at: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
upsert_user_workspace(uid, AuthType::Local, user_workspace.clone(), conn)?;
|
upsert_user_workspace(uid, AuthType::Local, user_workspace.clone(), conn)?;
|
||||||
|
@ -24,6 +24,9 @@ pub struct WorkspaceMemberPB {
|
|||||||
|
|
||||||
#[pb(index = 4, one_of)]
|
#[pb(index = 4, one_of)]
|
||||||
pub avatar_url: Option<String>,
|
pub avatar_url: Option<String>,
|
||||||
|
|
||||||
|
#[pb(index = 5, one_of)]
|
||||||
|
pub joined_at: Option<i64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<WorkspaceMember> for WorkspaceMemberPB {
|
impl From<WorkspaceMember> for WorkspaceMemberPB {
|
||||||
@ -33,6 +36,7 @@ impl From<WorkspaceMember> for WorkspaceMemberPB {
|
|||||||
name: value.name,
|
name: value.name,
|
||||||
role: value.role.into(),
|
role: value.role.into(),
|
||||||
avatar_url: value.avatar_url,
|
avatar_url: value.avatar_url,
|
||||||
|
joined_at: value.joined_at,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -673,6 +673,7 @@ impl UserManager {
|
|||||||
role: member_record.role.into(),
|
role: member_record.role.into(),
|
||||||
name: member_record.name,
|
name: member_record.name,
|
||||||
avatar_url: member_record.avatar_url,
|
avatar_url: member_record.avatar_url,
|
||||||
|
joined_at: member_record.joined_at,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -703,6 +704,7 @@ impl UserManager {
|
|||||||
uid,
|
uid,
|
||||||
workspace_id: workspace_id.to_string(),
|
workspace_id: workspace_id.to_string(),
|
||||||
updated_at: Utc::now().naive_utc(),
|
updated_at: Utc::now().naive_utc(),
|
||||||
|
joined_at: member.joined_at,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut db = self.authenticate_user.get_sqlite_connection(uid)?;
|
let mut db = self.authenticate_user.get_sqlite_connection(uid)?;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user