🐛 Fix bugs again
This commit is contained in:
@@ -309,25 +309,6 @@ class FeaturedPostCard extends HookConsumerWidget {
|
|||||||
children: [
|
children: [
|
||||||
const Icon(Symbols.highlight),
|
const Icon(Symbols.highlight),
|
||||||
Text('highlightPost').tr(),
|
Text('highlightPost').tr(),
|
||||||
const Spacer(),
|
|
||||||
IconButton(
|
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
visualDensity: VisualDensity.compact,
|
|
||||||
constraints: const BoxConstraints(),
|
|
||||||
onPressed: () {
|
|
||||||
// Navigation to previous post
|
|
||||||
},
|
|
||||||
icon: const Icon(Symbols.arrow_left),
|
|
||||||
),
|
|
||||||
IconButton(
|
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
visualDensity: VisualDensity.compact,
|
|
||||||
constraints: const BoxConstraints(),
|
|
||||||
onPressed: () {
|
|
||||||
// Navigation to next post
|
|
||||||
},
|
|
||||||
icon: const Icon(Symbols.arrow_right),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
).padding(horizontal: 16, vertical: 8),
|
).padding(horizontal: 16, vertical: 8),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -28,3 +28,13 @@ class OidcAuthCallbackEvent {
|
|||||||
class CommandPaletteTriggerEvent {
|
class CommandPaletteTriggerEvent {
|
||||||
const CommandPaletteTriggerEvent();
|
const CommandPaletteTriggerEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Event fired to show the compose post sheet
|
||||||
|
class ShowComposeSheetEvent {
|
||||||
|
const ShowComposeSheetEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Event fired to show the notification sheet
|
||||||
|
class ShowNotificationSheetEvent {
|
||||||
|
const ShowNotificationSheetEvent();
|
||||||
|
}
|
||||||
|
|||||||
@@ -17,6 +17,9 @@ import 'package:island/services/sharing_intent.dart';
|
|||||||
import 'package:island/services/update_service.dart';
|
import 'package:island/services/update_service.dart';
|
||||||
import 'package:island/widgets/content/network_status_sheet.dart';
|
import 'package:island/widgets/content/network_status_sheet.dart';
|
||||||
import 'package:island/widgets/tour/tour.dart';
|
import 'package:island/widgets/tour/tour.dart';
|
||||||
|
import 'package:island/widgets/post/compose_sheet.dart';
|
||||||
|
import 'package:island/screens/notification.dart';
|
||||||
|
import 'package:island/services/event_bus.dart';
|
||||||
import 'package:tray_manager/tray_manager.dart';
|
import 'package:tray_manager/tray_manager.dart';
|
||||||
import 'package:window_manager/window_manager.dart';
|
import 'package:window_manager/window_manager.dart';
|
||||||
|
|
||||||
@@ -33,6 +36,9 @@ class _AppWrapperState extends ConsumerState<AppWrapper>
|
|||||||
StreamSubscription? ntySubs;
|
StreamSubscription? ntySubs;
|
||||||
bool networkStateShowing = false;
|
bool networkStateShowing = false;
|
||||||
|
|
||||||
|
StreamSubscription? composeSheetSubs;
|
||||||
|
StreamSubscription? notificationSheetSubs;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
@@ -49,6 +55,21 @@ class _AppWrapperState extends ConsumerState<AppWrapper>
|
|||||||
ref.read(rpcServerStateProvider.notifier).start();
|
ref.read(rpcServerStateProvider.notifier).start();
|
||||||
ref.read(webAuthServerStateProvider.notifier).start();
|
ref.read(webAuthServerStateProvider.notifier).start();
|
||||||
|
|
||||||
|
// Listen to special action events
|
||||||
|
composeSheetSubs = eventBus.on<ShowComposeSheetEvent>().listen((event) {
|
||||||
|
if (mounted) {
|
||||||
|
_showComposeSheet();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
notificationSheetSubs = eventBus.on<ShowNotificationSheetEvent>().listen((
|
||||||
|
event,
|
||||||
|
) {
|
||||||
|
if (mounted) {
|
||||||
|
_showNotificationSheet();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
final initialUrl = await protocolHandler.getInitialUrl();
|
final initialUrl = await protocolHandler.getInitialUrl();
|
||||||
if (initialUrl != null && mounted) {
|
if (initialUrl != null && mounted) {
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
@@ -64,6 +85,8 @@ class _AppWrapperState extends ConsumerState<AppWrapper>
|
|||||||
ref.read(rpcServerProvider).stop();
|
ref.read(rpcServerProvider).stop();
|
||||||
TrayService.instance.dispose(this);
|
TrayService.instance.dispose(this);
|
||||||
ntySubs?.cancel();
|
ntySubs?.cancel();
|
||||||
|
composeSheetSubs?.cancel();
|
||||||
|
notificationSheetSubs?.cancel();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,9 +103,8 @@ class _AppWrapperState extends ConsumerState<AppWrapper>
|
|||||||
context: context,
|
context: context,
|
||||||
isScrollControlled: true,
|
isScrollControlled: true,
|
||||||
isDismissible: false,
|
isDismissible: false,
|
||||||
builder:
|
builder: (context) =>
|
||||||
(context) =>
|
NetworkStatusSheet(onReconnect: () => wsNotifier.connect()),
|
||||||
NetworkStatusSheet(onReconnect: () => wsNotifier.connect()),
|
|
||||||
).then((_) => setState(() => networkStateShowing = false));
|
).then((_) => setState(() => networkStateShowing = false));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -119,6 +141,19 @@ class _AppWrapperState extends ConsumerState<AppWrapper>
|
|||||||
TrayService.instance.handleAction(menuItem);
|
TrayService.instance.handleAction(menuItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _showComposeSheet() {
|
||||||
|
PostComposeSheet.show(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _showNotificationSheet() {
|
||||||
|
showModalBottomSheet(
|
||||||
|
context: context,
|
||||||
|
isScrollControlled: true,
|
||||||
|
useRootNavigator: true,
|
||||||
|
builder: (context) => const NotificationSheet(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
void _handleDeepLink(Uri uri, WidgetRef ref) async {
|
void _handleDeepLink(Uri uri, WidgetRef ref) async {
|
||||||
String path = '/${uri.host}${uri.path}';
|
String path = '/${uri.host}${uri.path}';
|
||||||
|
|
||||||
@@ -153,10 +188,9 @@ class _AppWrapperState extends ConsumerState<AppWrapper>
|
|||||||
|
|
||||||
final router = ref.read(routerProvider);
|
final router = ref.read(routerProvider);
|
||||||
if (uri.queryParameters.isNotEmpty) {
|
if (uri.queryParameters.isNotEmpty) {
|
||||||
path =
|
path = Uri.parse(
|
||||||
Uri.parse(
|
path,
|
||||||
path,
|
).replace(queryParameters: uri.queryParameters).toString();
|
||||||
).replace(queryParameters: uri.queryParameters).toString();
|
|
||||||
}
|
}
|
||||||
router.push(path);
|
router.push(path);
|
||||||
if (!kIsWeb &&
|
if (!kIsWeb &&
|
||||||
|
|||||||
@@ -18,6 +18,21 @@ import 'package:island/widgets/content/cloud_files.dart';
|
|||||||
import 'package:material_symbols_icons/symbols.dart';
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
import 'package:relative_time/relative_time.dart';
|
import 'package:relative_time/relative_time.dart';
|
||||||
import 'package:styled_widget/styled_widget.dart';
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
|
import 'package:island/services/event_bus.dart';
|
||||||
|
|
||||||
|
class SpecialAction {
|
||||||
|
final String name;
|
||||||
|
final String description;
|
||||||
|
final IconData icon;
|
||||||
|
final VoidCallback action;
|
||||||
|
|
||||||
|
const SpecialAction({
|
||||||
|
required this.name,
|
||||||
|
required this.description,
|
||||||
|
required this.icon,
|
||||||
|
required this.action,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
class CommandPattleWidget extends HookConsumerWidget {
|
class CommandPattleWidget extends HookConsumerWidget {
|
||||||
final VoidCallback onDismiss;
|
final VoidCallback onDismiss;
|
||||||
@@ -189,6 +204,27 @@ class CommandPattleWidget extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
static List<SpecialAction> _getSpecialActions(BuildContext context) {
|
||||||
|
return [
|
||||||
|
SpecialAction(
|
||||||
|
name: 'Compose Post',
|
||||||
|
description: 'Create a new post',
|
||||||
|
icon: Symbols.edit,
|
||||||
|
action: () {
|
||||||
|
eventBus.fire(const ShowComposeSheetEvent());
|
||||||
|
},
|
||||||
|
),
|
||||||
|
SpecialAction(
|
||||||
|
name: 'Notifications',
|
||||||
|
description: 'View your notifications',
|
||||||
|
icon: Symbols.notifications,
|
||||||
|
action: () {
|
||||||
|
eventBus.fire(const ShowNotificationSheetEvent());
|
||||||
|
},
|
||||||
|
),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final textController = useTextEditingController();
|
final textController = useTextEditingController();
|
||||||
@@ -269,8 +305,23 @@ class CommandPattleWidget extends HookConsumerWidget {
|
|||||||
.take(5) // Limit to 5 results
|
.take(5) // Limit to 5 results
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
// Combine results: chats first, then routes
|
final filteredSpecialActions = searchQuery.value.isEmpty
|
||||||
final allResults = [...filteredChats, ...filteredRoutes];
|
? <SpecialAction>[]
|
||||||
|
: _getSpecialActions(context)
|
||||||
|
.where((action) {
|
||||||
|
final query = searchQuery.value.toLowerCase();
|
||||||
|
return action.name.toLowerCase().contains(query) ||
|
||||||
|
action.description.toLowerCase().contains(query);
|
||||||
|
})
|
||||||
|
.take(5) // Limit to 5 results
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
// Combine results: chats first, then special actions, then routes
|
||||||
|
final allResults = [
|
||||||
|
...filteredChats,
|
||||||
|
...filteredSpecialActions,
|
||||||
|
...filteredRoutes,
|
||||||
|
];
|
||||||
|
|
||||||
// Update focused index when results change
|
// Update focused index when results change
|
||||||
useEffect(() {
|
useEffect(() {
|
||||||
@@ -326,6 +377,9 @@ class CommandPattleWidget extends HookConsumerWidget {
|
|||||||
final item = allResults[focusedIndex.value ?? 0];
|
final item = allResults[focusedIndex.value ?? 0];
|
||||||
if (item is SnChatRoom) {
|
if (item is SnChatRoom) {
|
||||||
_navigateToChat(context, ref, item);
|
_navigateToChat(context, ref, item);
|
||||||
|
} else if (item is SpecialAction) {
|
||||||
|
onDismiss();
|
||||||
|
item.action();
|
||||||
} else if (item is RouteItem) {
|
} else if (item is RouteItem) {
|
||||||
_navigateToRoute(context, ref, item);
|
_navigateToRoute(context, ref, item);
|
||||||
}
|
}
|
||||||
@@ -379,7 +433,7 @@ class CommandPattleWidget extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Theme.of(context).colorScheme.surface,
|
color: Theme.of(context).colorScheme.surface,
|
||||||
borderRadius: BorderRadius.circular(24),
|
borderRadius: BorderRadius.circular(28),
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: Colors.black.withOpacity(0.3),
|
color: Colors.black.withOpacity(0.3),
|
||||||
@@ -389,8 +443,9 @@ class CommandPattleWidget extends HookConsumerWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
child: Material(
|
child: Material(
|
||||||
|
elevation: 0,
|
||||||
color: Theme.of(context).colorScheme.surface,
|
color: Theme.of(context).colorScheme.surface,
|
||||||
borderRadius: BorderRadius.circular(24),
|
borderRadius: BorderRadius.circular(28),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
@@ -428,6 +483,16 @@ class CommandPattleWidget extends HookConsumerWidget {
|
|||||||
item,
|
item,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
} else if (item is SpecialAction) {
|
||||||
|
return _SpecialActionSearchResult(
|
||||||
|
action: item,
|
||||||
|
isFocused:
|
||||||
|
index == focusedIndex.value,
|
||||||
|
onTap: () {
|
||||||
|
onDismiss();
|
||||||
|
item.action();
|
||||||
|
},
|
||||||
|
);
|
||||||
} else if (item is RouteItem) {
|
} else if (item is RouteItem) {
|
||||||
return _RouteSearchResult(
|
return _RouteSearchResult(
|
||||||
route: item,
|
route: item,
|
||||||
@@ -497,7 +562,7 @@ class _RouteSearchResult extends StatelessWidget {
|
|||||||
color: isFocused
|
color: isFocused
|
||||||
? Theme.of(context).colorScheme.surfaceContainerHighest
|
? Theme.of(context).colorScheme.surfaceContainerHighest
|
||||||
: null,
|
: null,
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(24)),
|
borderRadius: const BorderRadius.all(Radius.circular(28)),
|
||||||
),
|
),
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
leading: CircleAvatar(
|
leading: CircleAvatar(
|
||||||
@@ -513,6 +578,40 @@ class _RouteSearchResult extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _SpecialActionSearchResult extends StatelessWidget {
|
||||||
|
final SpecialAction action;
|
||||||
|
final bool isFocused;
|
||||||
|
final VoidCallback onTap;
|
||||||
|
|
||||||
|
const _SpecialActionSearchResult({
|
||||||
|
required this.action,
|
||||||
|
required this.isFocused,
|
||||||
|
required this.onTap,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: isFocused
|
||||||
|
? Theme.of(context).colorScheme.surfaceContainerHighest
|
||||||
|
: null,
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(24)),
|
||||||
|
),
|
||||||
|
child: ListTile(
|
||||||
|
leading: CircleAvatar(
|
||||||
|
backgroundColor: Theme.of(context).colorScheme.tertiaryContainer,
|
||||||
|
foregroundColor: Theme.of(context).colorScheme.onTertiaryContainer,
|
||||||
|
child: Icon(action.icon),
|
||||||
|
),
|
||||||
|
title: Text(action.name),
|
||||||
|
subtitle: Text(action.description),
|
||||||
|
onTap: onTap,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class _ChatRoomSearchResult extends HookConsumerWidget {
|
class _ChatRoomSearchResult extends HookConsumerWidget {
|
||||||
final SnChatRoom room;
|
final SnChatRoom room;
|
||||||
final bool isFocused;
|
final bool isFocused;
|
||||||
|
|||||||
@@ -179,7 +179,7 @@ class PostComposeSheet extends HookConsumerWidget {
|
|||||||
final isTablet =
|
final isTablet =
|
||||||
isWideScreen(context) &&
|
isWideScreen(context) &&
|
||||||
!kIsWeb &&
|
!kIsWeb &&
|
||||||
(Platform.isAndroid || Platform.isAndroid);
|
(Platform.isAndroid || Platform.isIOS);
|
||||||
|
|
||||||
return SheetScaffold(
|
return SheetScaffold(
|
||||||
heightFactor: isTablet ? 0.95 : 0.8,
|
heightFactor: isTablet ? 0.95 : 0.8,
|
||||||
|
|||||||
24
pubspec.lock
24
pubspec.lock
@@ -2430,6 +2430,30 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.3.2"
|
version: "0.3.2"
|
||||||
|
sensors_plus:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: sensors_plus
|
||||||
|
sha256: "89e2bfc3d883743539ce5774a2b93df61effde40ff958ecad78cd66b1a8b8d52"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "6.1.2"
|
||||||
|
sensors_plus_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: sensors_plus_platform_interface
|
||||||
|
sha256: "58815d2f5e46c0c41c40fb39375d3f127306f7742efe3b891c0b1c87e2b5cd5d"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.1"
|
||||||
|
shake:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: shake
|
||||||
|
sha256: "7bb2bd14e9cd23a0d569f8a286b2b63ba1552ac348914d2d41ec757117ddda4e"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.0"
|
||||||
share_plus:
|
share_plus:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|||||||
@@ -173,6 +173,7 @@ dependencies:
|
|||||||
skeletonizer: ^2.1.2
|
skeletonizer: ^2.1.2
|
||||||
permission_handler: ^12.0.1
|
permission_handler: ^12.0.1
|
||||||
hotkey_manager: ^0.2.3
|
hotkey_manager: ^0.2.3
|
||||||
|
shake: ^3.0.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|||||||
Reference in New Issue
Block a user