🐛 Fix bugs
This commit is contained in:
@@ -1306,5 +1306,6 @@
|
||||
"activities": "Activities",
|
||||
"presenceTypeGaming": "Playing",
|
||||
"presenceTypeMusic": "Listening to Music",
|
||||
"presenceTypeWorkout": "Working out"
|
||||
"presenceTypeWorkout": "Working out",
|
||||
"articleCompose": "Compose Article"
|
||||
}
|
||||
|
||||
@@ -50,6 +50,7 @@ import 'package:island/screens/creators/publishers_form.dart';
|
||||
import 'package:island/screens/creators/webfeed/webfeed_list.dart';
|
||||
import 'package:island/screens/poll/poll_editor.dart';
|
||||
import 'package:island/screens/posts/compose.dart';
|
||||
import 'package:island/screens/posts/compose_article.dart';
|
||||
import 'package:island/screens/posts/post_detail.dart';
|
||||
import 'package:island/screens/posts/publisher_profile.dart';
|
||||
import 'package:island/screens/auth/login.dart';
|
||||
@@ -106,11 +107,19 @@ final routerProvider = Provider<GoRouter>((ref) {
|
||||
routes: [
|
||||
// Standalone routes without bottom navigation
|
||||
GoRoute(
|
||||
name: 'postEdit',
|
||||
path: '/posts/:id/edit',
|
||||
name: 'articleCompose',
|
||||
path: '/articles/compose',
|
||||
builder:
|
||||
(context, state) => ArticleComposeScreen(
|
||||
initialState: state.extra as PostComposeInitialState?,
|
||||
),
|
||||
),
|
||||
GoRoute(
|
||||
name: 'articleEdit',
|
||||
path: '/articles/:id/edit',
|
||||
builder: (context, state) {
|
||||
final id = state.pathParameters['id']!;
|
||||
return PostEditScreen(id: id);
|
||||
return ArticleEditScreen(id: id);
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
|
||||
@@ -177,8 +177,14 @@ class PublisherSelector extends StatelessWidget {
|
||||
iconStyleData: IconStyleData(
|
||||
icon: Icon(Icons.arrow_drop_down),
|
||||
iconSize: 19,
|
||||
iconEnabledColor: Theme.of(context).appBarTheme.foregroundColor!,
|
||||
iconDisabledColor: Theme.of(context).appBarTheme.foregroundColor!,
|
||||
iconEnabledColor:
|
||||
isWideScreen(context)
|
||||
? null
|
||||
: Theme.of(context).appBarTheme.foregroundColor!,
|
||||
iconDisabledColor:
|
||||
isWideScreen(context)
|
||||
? null
|
||||
: Theme.of(context).appBarTheme.foregroundColor!,
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -561,6 +567,7 @@ class CreatorHubScreen extends HookConsumerWidget {
|
||||
? Column(
|
||||
spacing: 8,
|
||||
children: [
|
||||
const SizedBox.shrink(),
|
||||
PublisherSelector(
|
||||
currentPublisher: currentPublisher.value,
|
||||
publishersMenu: publishersMenu,
|
||||
|
||||
@@ -16,6 +16,7 @@ import 'package:island/services/responsive.dart';
|
||||
import 'package:island/widgets/app_scaffold.dart';
|
||||
import 'package:island/models/post.dart';
|
||||
import 'package:island/widgets/check_in.dart';
|
||||
import 'package:island/widgets/navigation/fab_menu.dart';
|
||||
import 'package:island/widgets/post/post_featured.dart';
|
||||
import 'package:island/widgets/post/post_item.dart';
|
||||
import 'package:island/widgets/post/compose_card.dart';
|
||||
@@ -72,6 +73,22 @@ class ExploreScreen extends HookConsumerWidget {
|
||||
final tabController = useTabController(initialLength: 3);
|
||||
final currentFilter = useState<String?>(null);
|
||||
|
||||
useEffect(() {
|
||||
// Set FAB type to chat
|
||||
final fabMenuNotifier = ref.read(fabMenuTypeProvider.notifier);
|
||||
Future(() {
|
||||
fabMenuNotifier.state = FabMenuType.compose;
|
||||
});
|
||||
return () {
|
||||
// Clean up: reset FAB type to main
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (fabMenuNotifier.state == FabMenuType.compose) {
|
||||
fabMenuNotifier.state = FabMenuType.main;
|
||||
}
|
||||
});
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() {
|
||||
void listener() {
|
||||
switch (tabController.index) {
|
||||
@@ -703,7 +720,9 @@ class ActivityListNotifier extends _$ActivityListNotifier
|
||||
fetch(cursor: null);
|
||||
|
||||
@override
|
||||
Future<CursorPagingData<SnTimelineEvent>> fetch({required String? cursor}) async {
|
||||
Future<CursorPagingData<SnTimelineEvent>> fetch({
|
||||
required String? cursor,
|
||||
}) async {
|
||||
final client = ref.read(apiClientProvider);
|
||||
final take = 20;
|
||||
|
||||
|
||||
@@ -1,27 +1,6 @@
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:island/models/file.dart';
|
||||
import 'package:island/models/post.dart';
|
||||
import 'package:island/screens/creators/publishers_form.dart';
|
||||
import 'package:island/screens/posts/compose_article.dart';
|
||||
import 'package:island/screens/posts/post_detail.dart';
|
||||
import 'package:island/services/compose_storage_db.dart';
|
||||
import 'package:island/widgets/app_scaffold.dart';
|
||||
import 'package:island/widgets/content/cloud_files.dart';
|
||||
import 'package:island/widgets/post/compose_attachments.dart';
|
||||
import 'package:island/widgets/post/compose_form_fields.dart';
|
||||
import 'package:island/widgets/post/compose_info_banner.dart';
|
||||
import 'package:island/widgets/post/compose_settings_sheet.dart';
|
||||
import 'package:island/widgets/post/compose_shared.dart';
|
||||
import 'package:island/widgets/post/compose_toolbar.dart';
|
||||
import 'package:island/widgets/post/post_item.dart';
|
||||
import 'package:island/widgets/post/publishers_modal.dart';
|
||||
import 'package:material_symbols_icons/symbols.dart';
|
||||
import 'package:styled_widget/styled_widget.dart';
|
||||
|
||||
part 'compose.freezed.dart';
|
||||
part 'compose.g.dart';
|
||||
@@ -41,358 +20,3 @@ sealed class PostComposeInitialState with _$PostComposeInitialState {
|
||||
factory PostComposeInitialState.fromJson(Map<String, dynamic> json) =>
|
||||
_$PostComposeInitialStateFromJson(json);
|
||||
}
|
||||
|
||||
class PostEditScreen extends HookConsumerWidget {
|
||||
final String id;
|
||||
const PostEditScreen({super.key, required this.id});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final post = ref.watch(postProvider(id));
|
||||
return post.when(
|
||||
data: (post) => PostComposeScreen(originalPost: post),
|
||||
loading:
|
||||
() => AppScaffold(
|
||||
isNoBackground: false,
|
||||
appBar: AppBar(leading: const PageBackButton()),
|
||||
body: const Center(child: CircularProgressIndicator()),
|
||||
),
|
||||
error:
|
||||
(e, _) => AppScaffold(
|
||||
isNoBackground: false,
|
||||
appBar: AppBar(leading: const PageBackButton()),
|
||||
body: Text('Error: $e', textAlign: TextAlign.center),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class PostComposeScreen extends HookConsumerWidget {
|
||||
final SnPost? originalPost;
|
||||
final int? type;
|
||||
final PostComposeInitialState? initialState;
|
||||
const PostComposeScreen({
|
||||
super.key,
|
||||
this.type,
|
||||
this.initialState,
|
||||
this.originalPost,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
// Determine the compose type: auto-detect from edited post or use query parameter
|
||||
final composeType = originalPost?.type ?? type ?? 0;
|
||||
final repliedPost = initialState?.replyingTo ?? originalPost?.repliedPost;
|
||||
final forwardedPost =
|
||||
initialState?.forwardingTo ?? originalPost?.forwardedPost;
|
||||
|
||||
// If type is 1 (article), return ArticleComposeScreen
|
||||
if (composeType == 1) {
|
||||
return ArticleComposeScreen(originalPost: originalPost);
|
||||
}
|
||||
|
||||
// When editing, preserve the original replied/forwarded post references
|
||||
final effectiveRepliedPost = repliedPost ?? originalPost?.repliedPost;
|
||||
final effectiveForwardedPost = forwardedPost ?? originalPost?.forwardedPost;
|
||||
|
||||
final publishers = ref.watch(publishersManagedProvider);
|
||||
final state = useMemoized(
|
||||
() => ComposeLogic.createState(
|
||||
originalPost: originalPost,
|
||||
forwardedPost: effectiveForwardedPost,
|
||||
repliedPost: effectiveRepliedPost,
|
||||
postType: 0, // Regular post type
|
||||
),
|
||||
[originalPost, effectiveForwardedPost, effectiveRepliedPost],
|
||||
);
|
||||
|
||||
// Add a listener to the entire state to trigger rebuilds
|
||||
final stateNotifier = useMemoized(
|
||||
() => Listenable.merge([
|
||||
state.titleController,
|
||||
state.descriptionController,
|
||||
state.contentController,
|
||||
state.visibility,
|
||||
state.attachments,
|
||||
state.attachmentProgress,
|
||||
state.currentPublisher,
|
||||
state.submitting,
|
||||
]),
|
||||
[state],
|
||||
);
|
||||
useListenable(stateNotifier);
|
||||
|
||||
// Start auto-save when component mounts
|
||||
useEffect(() {
|
||||
if (originalPost == null) {
|
||||
// Only auto-save for new posts, not edits
|
||||
state.startAutoSave(ref);
|
||||
}
|
||||
return () => state.stopAutoSave();
|
||||
}, [state]);
|
||||
|
||||
// Initialize publisher once when data is available
|
||||
useEffect(() {
|
||||
if (publishers.value?.isNotEmpty ?? false) {
|
||||
if (state.currentPublisher.value == null) {
|
||||
// If no publisher is set, use the first available one
|
||||
state.currentPublisher.value = publishers.value!.first;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}, [publishers]);
|
||||
|
||||
// Load initial state if provided (for sharing functionality)
|
||||
useEffect(() {
|
||||
if (initialState != null) {
|
||||
state.titleController.text = initialState!.title ?? '';
|
||||
state.descriptionController.text = initialState!.description ?? '';
|
||||
state.contentController.text = initialState!.content ?? '';
|
||||
if (initialState!.visibility != null) {
|
||||
state.visibility.value = initialState!.visibility!;
|
||||
}
|
||||
if (initialState!.attachments.isNotEmpty) {
|
||||
state.attachments.value = List.from(initialState!.attachments);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}, [initialState]);
|
||||
|
||||
// Load draft if available (only for new posts without initial state)
|
||||
useEffect(() {
|
||||
if (originalPost == null &&
|
||||
effectiveForwardedPost == null &&
|
||||
effectiveRepliedPost == null &&
|
||||
initialState == null) {
|
||||
// Try to load the most recent draft
|
||||
final drafts = ref.read(composeStorageNotifierProvider);
|
||||
if (drafts.isNotEmpty) {
|
||||
final mostRecentDraft = drafts.values.reduce(
|
||||
(a, b) =>
|
||||
(a.updatedAt ?? DateTime(0)).isAfter(b.updatedAt ?? DateTime(0))
|
||||
? a
|
||||
: b,
|
||||
);
|
||||
|
||||
// Only load if the draft has meaningful content
|
||||
if (mostRecentDraft.content?.isNotEmpty == true ||
|
||||
mostRecentDraft.title?.isNotEmpty == true) {
|
||||
state.titleController.text = mostRecentDraft.title ?? '';
|
||||
state.descriptionController.text =
|
||||
mostRecentDraft.description ?? '';
|
||||
state.contentController.text = mostRecentDraft.content ?? '';
|
||||
state.visibility.value = mostRecentDraft.visibility;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}, []);
|
||||
|
||||
// Dispose state when widget is disposed
|
||||
useEffect(() {
|
||||
return () {
|
||||
state.stopAutoSave();
|
||||
ComposeLogic.dispose(state);
|
||||
};
|
||||
}, []);
|
||||
|
||||
// Helper methods
|
||||
|
||||
void showSettingsSheet() {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
builder: (context) => ComposeSettingsSheet(state: state),
|
||||
);
|
||||
}
|
||||
|
||||
return PopScope(
|
||||
onPopInvoked: (_) {
|
||||
if (originalPost == null) {
|
||||
ComposeLogic.saveDraft(ref, state);
|
||||
}
|
||||
},
|
||||
child: AppScaffold(
|
||||
isNoBackground: false,
|
||||
appBar: AppBar(
|
||||
leading: const PageBackButton(),
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: const Icon(Symbols.settings),
|
||||
onPressed: showSettingsSheet,
|
||||
tooltip: 'postSettings'.tr(),
|
||||
),
|
||||
IconButton(
|
||||
onPressed:
|
||||
state.submitting.value
|
||||
? null
|
||||
: () => ComposeLogic.performAction(
|
||||
ref,
|
||||
state,
|
||||
context,
|
||||
originalPost: originalPost,
|
||||
repliedPost: repliedPost,
|
||||
forwardedPost: forwardedPost,
|
||||
),
|
||||
icon:
|
||||
state.submitting.value
|
||||
? SizedBox(
|
||||
width: 28,
|
||||
height: 28,
|
||||
child: const CircularProgressIndicator(
|
||||
color: Colors.white,
|
||||
strokeWidth: 2.5,
|
||||
),
|
||||
).center()
|
||||
: Icon(
|
||||
originalPost != null ? Symbols.edit : Symbols.upload,
|
||||
),
|
||||
),
|
||||
const Gap(8),
|
||||
],
|
||||
),
|
||||
body: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Reply/Forward info section
|
||||
ComposeInfoBanner(
|
||||
originalPost: originalPost,
|
||||
replyingTo: repliedPost,
|
||||
forwardingTo: forwardedPost,
|
||||
onReferencePostTap: (context, post) {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
backgroundColor: Colors.transparent,
|
||||
builder:
|
||||
(context) => DraggableScrollableSheet(
|
||||
initialChildSize: 0.7,
|
||||
maxChildSize: 0.9,
|
||||
minChildSize: 0.5,
|
||||
builder:
|
||||
(context, scrollController) => Container(
|
||||
decoration: BoxDecoration(
|
||||
color:
|
||||
Theme.of(context).scaffoldBackgroundColor,
|
||||
borderRadius: const BorderRadius.vertical(
|
||||
top: Radius.circular(16),
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
width: 40,
|
||||
height: 4,
|
||||
margin: const EdgeInsets.symmetric(
|
||||
vertical: 8,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color:
|
||||
Theme.of(context).colorScheme.outline,
|
||||
borderRadius: BorderRadius.circular(2),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: SingleChildScrollView(
|
||||
controller: scrollController,
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: PostItem(item: post),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
// Main content area
|
||||
Expanded(
|
||||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 560),
|
||||
child: Row(
|
||||
spacing: 12,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Publisher profile picture
|
||||
GestureDetector(
|
||||
child: ProfilePictureWidget(
|
||||
fileId: state.currentPublisher.value?.picture?.id,
|
||||
radius: 20,
|
||||
fallbackIcon:
|
||||
state.currentPublisher.value == null
|
||||
? Symbols.question_mark
|
||||
: null,
|
||||
),
|
||||
onTap: () {
|
||||
showModalBottomSheet(
|
||||
isScrollControlled: true,
|
||||
context: context,
|
||||
builder: (context) => const PublisherModal(),
|
||||
).then((value) {
|
||||
if (value != null) {
|
||||
state.currentPublisher.value = value;
|
||||
}
|
||||
});
|
||||
},
|
||||
).padding(top: 16),
|
||||
|
||||
// Post content form
|
||||
Expanded(
|
||||
child: KeyboardListener(
|
||||
focusNode: FocusNode(),
|
||||
onKeyEvent:
|
||||
(event) => ComposeLogic.handleKeyPress(
|
||||
event,
|
||||
state,
|
||||
ref,
|
||||
context,
|
||||
originalPost: originalPost,
|
||||
repliedPost: repliedPost,
|
||||
forwardedPost: forwardedPost,
|
||||
),
|
||||
child: SingleChildScrollView(
|
||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
ComposeFormFields(
|
||||
state: state,
|
||||
showPublisherAvatar: false,
|
||||
onPublisherTap: () {
|
||||
showModalBottomSheet(
|
||||
isScrollControlled: true,
|
||||
context: context,
|
||||
builder:
|
||||
(context) => const PublisherModal(),
|
||||
).then((value) {
|
||||
if (value != null) {
|
||||
state.currentPublisher.value = value;
|
||||
}
|
||||
});
|
||||
},
|
||||
),
|
||||
const Gap(8),
|
||||
ComposeAttachments(
|
||||
state: state,
|
||||
isCompact: false,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
).padding(horizontal: 16),
|
||||
).alignment(Alignment.topCenter),
|
||||
),
|
||||
|
||||
// Bottom toolbar
|
||||
ComposeToolbar(state: state, originalPost: originalPost),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:island/models/file.dart';
|
||||
import 'package:island/models/post.dart';
|
||||
import 'package:island/screens/creators/publishers_form.dart';
|
||||
import 'package:island/screens/posts/compose.dart';
|
||||
import 'package:island/screens/posts/post_detail.dart';
|
||||
import 'package:island/services/compose_storage_db.dart';
|
||||
import 'package:island/services/responsive.dart';
|
||||
@@ -49,8 +50,9 @@ class ArticleEditScreen extends HookConsumerWidget {
|
||||
|
||||
class ArticleComposeScreen extends HookConsumerWidget {
|
||||
final SnPost? originalPost;
|
||||
final PostComposeInitialState? initialState;
|
||||
|
||||
const ArticleComposeScreen({super.key, this.originalPost});
|
||||
const ArticleComposeScreen({super.key, this.originalPost, this.initialState});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
@@ -100,9 +102,25 @@ class ArticleComposeScreen extends HookConsumerWidget {
|
||||
return null;
|
||||
}, [publishers]);
|
||||
|
||||
// Load initial state if provided (for sharing functionality)
|
||||
useEffect(() {
|
||||
if (initialState != null) {
|
||||
state.titleController.text = initialState!.title ?? '';
|
||||
state.descriptionController.text = initialState!.description ?? '';
|
||||
state.contentController.text = initialState!.content ?? '';
|
||||
if (initialState!.visibility != null) {
|
||||
state.visibility.value = initialState!.visibility!;
|
||||
}
|
||||
if (initialState!.attachments.isNotEmpty) {
|
||||
state.attachments.value = List.from(initialState!.attachments);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}, [initialState]);
|
||||
|
||||
// Load draft if available (only for new articles)
|
||||
useEffect(() {
|
||||
if (originalPost == null) {
|
||||
if (originalPost == null && initialState == null) {
|
||||
// Try to load the most recent article draft
|
||||
final drafts = ref.read(composeStorageNotifierProvider);
|
||||
if (drafts.isNotEmpty) {
|
||||
@@ -199,6 +217,7 @@ class ArticleComposeScreen extends HookConsumerWidget {
|
||||
border: Border.all(color: colorScheme.outline.withOpacity(0.3)),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
margin: const EdgeInsets.symmetric(vertical: 8),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@@ -219,7 +238,12 @@ class ArticleComposeScreen extends HookConsumerWidget {
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(child: widgetItem),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
child: widgetItem,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
@@ -246,7 +270,7 @@ class ArticleComposeScreen extends HookConsumerWidget {
|
||||
}
|
||||
});
|
||||
},
|
||||
),
|
||||
).padding(top: 16),
|
||||
|
||||
// Attachments preview
|
||||
ValueListenableBuilder<List<UniversalFile>>(
|
||||
|
||||
@@ -108,13 +108,21 @@ class PostActionButtons extends HookConsumerWidget {
|
||||
final editButtons = <Widget>[
|
||||
FilledButton.tonal(
|
||||
onPressed: () {
|
||||
context.pushNamed('postEdit', pathParameters: {'id': post.id}).then(
|
||||
(value) {
|
||||
if (value != null) {
|
||||
if (post.type == 1) {
|
||||
context
|
||||
.pushNamed('articleEdit', pathParameters: {'id': post.id})
|
||||
.then((value) {
|
||||
if (value != null) {
|
||||
onRefresh?.call();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
PostComposeSheet.show(context, originalPost: post).then((value) {
|
||||
if (value == true) {
|
||||
onRefresh?.call();
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
},
|
||||
style: FilledButton.styleFrom(
|
||||
shape: const RoundedRectangleBorder(
|
||||
|
||||
@@ -51,33 +51,33 @@ class TabsScreen extends HookConsumerWidget {
|
||||
final destinations = [
|
||||
NavigationDestination(
|
||||
label: 'explore'.tr(),
|
||||
icon: const Icon(Symbols.explore),
|
||||
icon: const Icon(Symbols.explore_rounded),
|
||||
),
|
||||
NavigationDestination(
|
||||
label: 'chat'.tr(),
|
||||
icon: const Icon(Symbols.chat_rounded),
|
||||
icon: const Icon(Symbols.forum_rounded),
|
||||
),
|
||||
NavigationDestination(
|
||||
label: 'realms'.tr(),
|
||||
icon: const Icon(Symbols.group),
|
||||
icon: const Icon(Symbols.group_rounded),
|
||||
),
|
||||
NavigationDestination(
|
||||
label: 'account'.tr(),
|
||||
icon: Badge.count(
|
||||
count: notificationUnreadCount.value ?? 0,
|
||||
isLabelVisible: (notificationUnreadCount.value ?? 0) > 0,
|
||||
child: const Icon(Symbols.account_circle),
|
||||
child: const Icon(Symbols.person_rounded),
|
||||
),
|
||||
),
|
||||
if (wideScreen)
|
||||
NavigationDestination(
|
||||
label: 'creatorHub'.tr(),
|
||||
icon: const Icon(Symbols.ink_pen),
|
||||
icon: const Icon(Symbols.design_services_rounded),
|
||||
),
|
||||
if (wideScreen)
|
||||
NavigationDestination(
|
||||
label: 'developerHub'.tr(),
|
||||
icon: const Icon(Symbols.data_object),
|
||||
icon: const Icon(Symbols.data_object_rounded),
|
||||
),
|
||||
];
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import 'package:island/widgets/alert.dart';
|
||||
import 'package:island/widgets/post/compose_sheet.dart';
|
||||
import 'package:material_symbols_icons/symbols.dart';
|
||||
|
||||
enum FabMenuType { main, chat, realm }
|
||||
enum FabMenuType { main, compose, chat, realm }
|
||||
|
||||
/// Global state provider for FAB menu type
|
||||
final fabMenuTypeProvider = StateProvider<FabMenuType>(
|
||||
@@ -69,7 +69,7 @@ class FabMenu extends HookConsumerWidget {
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
||||
leading: const Icon(Symbols.notifications),
|
||||
trailing: Badge(
|
||||
label: Text(notificationCount.toString()),
|
||||
label: Text(notificationCount.value.toString()),
|
||||
isLabelVisible: notificationCount.value! > 0,
|
||||
),
|
||||
title: Text('notifications').tr(),
|
||||
@@ -88,6 +88,38 @@ class FabMenu extends HookConsumerWidget {
|
||||
];
|
||||
|
||||
switch (fabType) {
|
||||
case FabMenuType.compose:
|
||||
icon = Symbols.create;
|
||||
useRootNavigator = false;
|
||||
menuContent = Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Gap(24),
|
||||
ListTile(
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
||||
leading: const Icon(Symbols.post_add_rounded),
|
||||
title: Text('postCompose').tr(),
|
||||
onTap: () async {
|
||||
Navigator.of(context).pop();
|
||||
await PostComposeSheet.show(context);
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
||||
leading: const Icon(Symbols.article),
|
||||
title: Text('articleCompose').tr(),
|
||||
onTap: () async {
|
||||
Navigator.of(context).pop();
|
||||
GoRouter.of(context).pushNamed('articleCompose');
|
||||
},
|
||||
),
|
||||
const Divider(),
|
||||
...commonEntires,
|
||||
Gap(MediaQuery.of(context).padding.bottom + 16),
|
||||
],
|
||||
);
|
||||
break;
|
||||
|
||||
case FabMenuType.chat:
|
||||
icon = Symbols.chat_add_on;
|
||||
useRootNavigator = true;
|
||||
@@ -160,16 +192,6 @@ class FabMenu extends HookConsumerWidget {
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Gap(24),
|
||||
ListTile(
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
||||
leading: const Icon(Symbols.post_add_rounded),
|
||||
title: Text('postCompose').tr(),
|
||||
onTap: () async {
|
||||
Navigator.of(context).pop();
|
||||
await PostComposeSheet.show(context);
|
||||
},
|
||||
),
|
||||
const Divider(),
|
||||
...commonEntires,
|
||||
Gap(MediaQuery.of(context).padding.bottom + 16),
|
||||
],
|
||||
|
||||
@@ -10,6 +10,7 @@ import 'package:island/services/time.dart';
|
||||
import 'package:island/widgets/alert.dart';
|
||||
import 'package:island/widgets/post/post_item.dart';
|
||||
import 'package:island/widgets/post/post_shared.dart';
|
||||
import 'package:island/widgets/post/compose_sheet.dart';
|
||||
import 'package:material_symbols_icons/symbols.dart';
|
||||
import 'package:styled_widget/styled_widget.dart';
|
||||
import 'package:super_context_menu/super_context_menu.dart';
|
||||
@@ -45,13 +46,23 @@ class PostItemCreator extends HookConsumerWidget {
|
||||
title: 'edit'.tr(),
|
||||
image: MenuImage.icon(Symbols.edit),
|
||||
callback: () {
|
||||
context
|
||||
.pushNamed('postEdit', pathParameters: {'id': item.id})
|
||||
.then((value) {
|
||||
if (value != null) {
|
||||
onRefresh?.call();
|
||||
}
|
||||
});
|
||||
if (item.type == 1) {
|
||||
context
|
||||
.pushNamed('articleEdit', pathParameters: {'id': item.id})
|
||||
.then((value) {
|
||||
if (value != null) {
|
||||
onRefresh?.call();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
PostComposeSheet.show(context, originalPost: item).then((
|
||||
value,
|
||||
) {
|
||||
if (value == true) {
|
||||
onRefresh?.call();
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
),
|
||||
MenuAction(
|
||||
|
||||
Reference in New Issue
Block a user