mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2026-01-08 13:21:10 +00:00
feat: enable shared section on mobile (#8020)
* feat: enable shared section on mobile * feat: update mobile view item * feat: shared with me section on mobile
This commit is contained in:
parent
5598689dba
commit
11200a5b3d
@ -0,0 +1,91 @@
|
||||
import 'package:appflowy/features/shared_section/data/repositories/rust_shared_pages_repository_impl.dart';
|
||||
import 'package:appflowy/features/shared_section/logic/shared_section_bloc.dart';
|
||||
import 'package:appflowy/features/shared_section/presentation/widgets/m_shared_page_list.dart';
|
||||
import 'package:appflowy/features/shared_section/presentation/widgets/m_shared_section_header.dart';
|
||||
import 'package:appflowy/features/shared_section/presentation/widgets/refresh_button.dart';
|
||||
import 'package:appflowy/features/shared_section/presentation/widgets/shared_section_error.dart';
|
||||
import 'package:appflowy/features/shared_section/presentation/widgets/shared_section_loading.dart';
|
||||
import 'package:appflowy/mobile/application/mobile_router.dart';
|
||||
import 'package:appflowy/shared/icon_emoji_picker/tab.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/home_sizes.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';
|
||||
|
||||
class MSharedSection extends StatelessWidget {
|
||||
const MSharedSection({
|
||||
super.key,
|
||||
required this.workspaceId,
|
||||
});
|
||||
|
||||
final String workspaceId;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final repository = RustSharePagesRepositoryImpl();
|
||||
|
||||
return BlocProvider(
|
||||
create: (_) => SharedSectionBloc(
|
||||
workspaceId: workspaceId,
|
||||
repository: repository,
|
||||
enablePolling: true,
|
||||
)..add(const SharedSectionInitEvent()),
|
||||
child: BlocBuilder<SharedSectionBloc, SharedSectionState>(
|
||||
builder: (context, state) {
|
||||
if (state.isLoading) {
|
||||
return const SharedSectionLoading();
|
||||
}
|
||||
|
||||
if (state.errorMessage.isNotEmpty) {
|
||||
return SharedSectionError(errorMessage: state.errorMessage);
|
||||
}
|
||||
|
||||
// hide the shared section if there are no shared pages
|
||||
if (state.sharedPages.isEmpty) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const VSpace(HomeSpaceViewSizes.mVerticalPadding),
|
||||
|
||||
// Shared header
|
||||
MSharedSectionHeader(),
|
||||
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: HomeSpaceViewSizes.mHorizontalPadding,
|
||||
),
|
||||
child: MSharedPageList(
|
||||
sharedPages: state.sharedPages,
|
||||
onSelected: (view) {
|
||||
context.pushView(
|
||||
view,
|
||||
tabs: [
|
||||
PickerTabType.emoji,
|
||||
PickerTabType.icon,
|
||||
PickerTabType.custom,
|
||||
].map((e) => e.name).toList(),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
|
||||
// Refresh button, for debugging only
|
||||
if (kDebugMode)
|
||||
RefreshSharedSectionButton(
|
||||
onTap: () {
|
||||
context.read<SharedSectionBloc>().add(
|
||||
const SharedSectionEvent.refresh(),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
import 'package:appflowy/features/shared_section/data/repositories/rust_shared_pages_repository_impl.dart';
|
||||
import 'package:appflowy/features/shared_section/data/repositories/local_shared_pages_repository_impl.dart';
|
||||
import 'package:appflowy/features/shared_section/logic/shared_section_bloc.dart';
|
||||
import 'package:appflowy/features/shared_section/presentation/widgets/refresh_button.dart';
|
||||
import 'package:appflowy/features/shared_section/presentation/widgets/shared_pages_list.dart';
|
||||
import 'package:appflowy/features/shared_section/presentation/widgets/shared_page_list.dart';
|
||||
import 'package:appflowy/features/shared_section/presentation/widgets/shared_section_error.dart';
|
||||
import 'package:appflowy/features/shared_section/presentation/widgets/shared_section_header.dart';
|
||||
import 'package:appflowy/features/shared_section/presentation/widgets/shared_section_loading.dart';
|
||||
@ -30,7 +30,8 @@ class SharedSection extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final repository = RustSharePagesRepositoryImpl();
|
||||
// final repository = RustSharePagesRepositoryImpl();
|
||||
final repository = LocalSharedPagesRepositoryImpl();
|
||||
|
||||
return BlocProvider(
|
||||
create: (_) => SharedSectionBloc(
|
||||
@ -68,7 +69,7 @@ class SharedSection extends StatelessWidget {
|
||||
|
||||
// Shared pages list
|
||||
if (state.isExpanded)
|
||||
SharedPagesList(
|
||||
SharedPageList(
|
||||
sharedPages: state.sharedPages,
|
||||
onSetEditing: (context, value) {
|
||||
context.read<ViewBloc>().add(ViewEvent.setIsEditing(value));
|
||||
|
||||
@ -0,0 +1,38 @@
|
||||
import 'package:appflowy/features/shared_section/models/shared_page.dart';
|
||||
import 'package:appflowy/mobile/presentation/page_item/mobile_view_item.dart';
|
||||
import 'package:appflowy/workspace/application/sidebar/folder/folder_bloc.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/home_sizes.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// Shared pages on mobile
|
||||
class MSharedPageList extends StatelessWidget {
|
||||
const MSharedPageList({
|
||||
super.key,
|
||||
required this.sharedPages,
|
||||
required this.onSelected,
|
||||
});
|
||||
|
||||
final SharedPages sharedPages;
|
||||
final ViewItemOnSelected onSelected;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: sharedPages.map((sharedPage) {
|
||||
final view = sharedPage.view;
|
||||
return MobileViewItem(
|
||||
key: ValueKey(view.id),
|
||||
spaceType: FolderSpaceType.public,
|
||||
isFirstChild: view.id == sharedPages.first.view.id,
|
||||
view: view,
|
||||
level: 0,
|
||||
isDraggable: false, // disable draggable for shared pages
|
||||
leftPadding: HomeSpaceViewSizes.leftPadding,
|
||||
isFeedback: false,
|
||||
onSelected: onSelected,
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/home_sizes.dart';
|
||||
import 'package:appflowy_ui/appflowy_ui.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||
import 'package:flowy_infra_ui/widget/spacing.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class MSharedSectionHeader extends StatelessWidget {
|
||||
const MSharedSectionHeader({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = AppFlowyTheme.of(context);
|
||||
return SizedBox(
|
||||
height: 48,
|
||||
child: Row(
|
||||
children: [
|
||||
const HSpace(HomeSpaceViewSizes.mHorizontalPadding),
|
||||
FlowySvg(
|
||||
FlowySvgs.shared_with_me_m,
|
||||
color: theme.badgeColorScheme.color13Thick2,
|
||||
),
|
||||
const HSpace(10.0),
|
||||
FlowyText.medium(
|
||||
LocaleKeys.shareSection_shared.tr(),
|
||||
lineHeight: 1.15,
|
||||
fontSize: 16.0,
|
||||
),
|
||||
const HSpace(HomeSpaceViewSizes.mHorizontalPadding),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -9,8 +9,9 @@ import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class SharedPagesList extends StatelessWidget {
|
||||
const SharedPagesList({
|
||||
/// Shared pages on desktop
|
||||
class SharedPageList extends StatelessWidget {
|
||||
const SharedPageList({
|
||||
super.key,
|
||||
required this.sharedPages,
|
||||
required this.onAction,
|
||||
@ -0,0 +1,41 @@
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy_ui/appflowy_ui.dart';
|
||||
import 'package:flowy_infra_ui/widget/spacing.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class SharedSectionEmpty extends StatelessWidget {
|
||||
const SharedSectionEmpty({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = AppFlowyTheme.of(context);
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
FlowySvg(
|
||||
FlowySvgs.empty_shared_section_m,
|
||||
color: theme.iconColorScheme.tertiary,
|
||||
),
|
||||
const VSpace(12),
|
||||
Text(
|
||||
'Nothing shared with you',
|
||||
style: theme.textStyle.heading3.enhanced(
|
||||
color: theme.textColorScheme.secondary,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const VSpace(4),
|
||||
Text(
|
||||
'Pages shared with you will show here',
|
||||
style: theme.textStyle.heading4.standard(
|
||||
color: theme.textColorScheme.tertiary,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const VSpace(kBottomNavigationBarHeight + 60.0),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -239,7 +239,7 @@ class _HomePageState extends State<_HomePage> {
|
||||
),
|
||||
),
|
||||
],
|
||||
child: MobileSpaceTab(
|
||||
child: MobileHomePageTab(
|
||||
userProfile: widget.userProfile,
|
||||
),
|
||||
),
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import 'package:appflowy/features/shared_section/presentation/m_shared_section.dart';
|
||||
import 'package:appflowy/features/workspace/logic/workspace_bloc.dart';
|
||||
import 'package:appflowy/mobile/application/mobile_router.dart';
|
||||
import 'package:appflowy/mobile/presentation/home/favorite_folder/favorite_space.dart';
|
||||
@ -23,8 +24,8 @@ import 'ai_bubble_button.dart';
|
||||
|
||||
final ValueNotifier<int> mobileCreateNewAIChatNotifier = ValueNotifier(0);
|
||||
|
||||
class MobileSpaceTab extends StatefulWidget {
|
||||
const MobileSpaceTab({
|
||||
class MobileHomePageTab extends StatefulWidget {
|
||||
const MobileHomePageTab({
|
||||
super.key,
|
||||
required this.userProfile,
|
||||
});
|
||||
@ -32,10 +33,10 @@ class MobileSpaceTab extends StatefulWidget {
|
||||
final UserProfilePB userProfile;
|
||||
|
||||
@override
|
||||
State<MobileSpaceTab> createState() => _MobileSpaceTabState();
|
||||
State<MobileHomePageTab> createState() => _MobileHomePageTabState();
|
||||
}
|
||||
|
||||
class _MobileSpaceTabState extends State<MobileSpaceTab>
|
||||
class _MobileHomePageTabState extends State<MobileHomePageTab>
|
||||
with SingleTickerProviderStateMixin {
|
||||
TabController? tabController;
|
||||
|
||||
@ -178,6 +179,18 @@ class _MobileSpaceTabState extends State<MobileSpaceTab>
|
||||
);
|
||||
case MobileSpaceTabType.favorites:
|
||||
return MobileFavoriteSpace(userProfile: widget.userProfile);
|
||||
case MobileSpaceTabType.shared:
|
||||
final workspaceId = context
|
||||
.read<UserWorkspaceBloc>()
|
||||
.state
|
||||
.currentWorkspace
|
||||
?.workspaceId;
|
||||
if (workspaceId == null) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
return MSharedSection(
|
||||
workspaceId: workspaceId,
|
||||
);
|
||||
}
|
||||
}).toList();
|
||||
}
|
||||
|
||||
@ -15,7 +15,8 @@ enum MobileSpaceTabType {
|
||||
// DO NOT CHANGE THE ORDER
|
||||
spaces,
|
||||
recent,
|
||||
favorites;
|
||||
favorites,
|
||||
shared;
|
||||
|
||||
String get tr {
|
||||
switch (this) {
|
||||
@ -25,6 +26,8 @@ enum MobileSpaceTabType {
|
||||
return LocaleKeys.sideBar_Spaces.tr();
|
||||
case MobileSpaceTabType.favorites:
|
||||
return LocaleKeys.sideBar_favoriteSpace.tr();
|
||||
case MobileSpaceTabType.shared:
|
||||
return 'Shared';
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -89,6 +92,9 @@ class SpaceOrderBloc extends Bloc<SpaceOrderEvent, SpaceOrderState> {
|
||||
if (order.isEmpty) {
|
||||
return MobileSpaceTabType.values;
|
||||
}
|
||||
if (!order.contains(MobileSpaceTabType.shared.index)) {
|
||||
order.add(MobileSpaceTabType.shared.index);
|
||||
}
|
||||
return order
|
||||
.map((e) => MobileSpaceTabType.values[e])
|
||||
.cast<MobileSpaceTabType>()
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import 'package:appflowy/core/config/kv.dart';
|
||||
import 'package:appflowy/features/share_tab/data/models/share_access_level.dart';
|
||||
import 'package:appflowy/features/shared_section/models/shared_page.dart';
|
||||
import 'package:appflowy/features/shared_section/presentation/widgets/shared_pages_list.dart';
|
||||
import 'package:appflowy/features/shared_section/presentation/widgets/shared_page_list.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/menu_shared_state.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@ -47,7 +47,7 @@ void main() {
|
||||
await tester.pumpWidget(
|
||||
WidgetTestWrapper(
|
||||
child: SingleChildScrollView(
|
||||
child: SharedPagesList(
|
||||
child: SharedPageList(
|
||||
sharedPages: sharedPages,
|
||||
onAction: (action, view, data) {},
|
||||
onSelected: (context, view) {},
|
||||
@ -59,7 +59,7 @@ void main() {
|
||||
);
|
||||
expect(find.text('Page 1'), findsOneWidget);
|
||||
expect(find.text('Page 2'), findsOneWidget);
|
||||
expect(find.byType(SharedPagesList), findsOneWidget);
|
||||
expect(find.byType(SharedPageList), findsOneWidget);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@ -0,0 +1,3 @@
|
||||
<svg width="45" height="44" viewBox="0 0 45 44" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M29.1316 17.2632C32.271 17.2632 34.8158 14.7182 34.8158 11.5789C34.8158 8.43965 32.271 5.89474 29.1316 5.89474M34.8158 26.7368C38.1395 27.4657 40.5 29.3116 40.5 31.4737C40.5 33.4239 38.5793 35.1171 35.7632 35.965M10.1842 11.5789C10.1842 13.589 10.9827 15.5167 12.404 16.9381C13.8254 18.3594 15.7531 19.1579 17.7632 19.1579C19.7732 19.1579 21.701 18.3594 23.1223 16.9381C24.5436 15.5167 25.3421 13.589 25.3421 11.5789C25.3421 9.56889 24.5436 7.64115 23.1223 6.21982C21.701 4.79849 19.7732 4 17.7632 4C15.7531 4 13.8254 4.79849 12.404 6.21982C10.9827 7.64115 10.1842 9.56889 10.1842 11.5789ZM4.5 32.4211C4.5 34.4311 5.89736 36.3589 8.38469 37.7802C10.872 39.2015 14.2455 40 17.7632 40C21.2808 40 24.6543 39.2015 27.1416 37.7802C29.629 36.3589 31.0263 34.4311 31.0263 32.4211C31.0263 30.411 29.629 28.4833 27.1416 27.0619C24.6543 25.6406 21.2808 24.8421 17.7632 24.8421C14.2455 24.8421 10.872 25.6406 8.38469 27.0619C5.89736 28.4833 4.5 30.411 4.5 32.4211Z" stroke="#B5BBD3" stroke-width="2" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
Loading…
x
Reference in New Issue
Block a user