Compare commits

...

10 Commits

17 changed files with 302 additions and 85 deletions

View File

@@ -282,7 +282,11 @@ class AccountScreen extends HookConsumerWidget {
], ],
), ),
onTap: () { onTap: () {
context.pushNamed('notifications'); showModalBottomSheet(
context: context,
isScrollControlled: true,
builder: (context) => const NotificationScreen(),
);
}, },
), ),
ListTile( ListTile(

View File

@@ -20,6 +20,7 @@ class ArticleDetailScreen extends ConsumerWidget {
final articleAsync = ref.watch(articleDetailProvider(articleId)); final articleAsync = ref.watch(articleDetailProvider(articleId));
return AppScaffold( return AppScaffold(
isNoBackground: false,
body: articleAsync.when( body: articleAsync.when(
data: data:
(article) => AppScaffold( (article) => AppScaffold(

View File

@@ -54,7 +54,11 @@ Widget notificationIndicatorWidget(
trailing: const Icon(Symbols.chevron_right), trailing: const Icon(Symbols.chevron_right),
contentPadding: EdgeInsets.only(left: 16, right: 15), contentPadding: EdgeInsets.only(left: 16, right: 15),
onTap: () { onTap: () {
GoRouter.of(context).pushNamed('notifications'); showModalBottomSheet(
context: context,
isScrollControlled: true,
builder: (context) => const NotificationScreen(),
);
}, },
), ),
); );

View File

@@ -3,7 +3,6 @@ import 'dart:math' as math;
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:gap/gap.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/account.dart'; import 'package:island/models/account.dart';
@@ -11,8 +10,8 @@ import 'package:island/pods/network.dart';
import 'package:island/pods/websocket.dart'; import 'package:island/pods/websocket.dart';
import 'package:island/route.dart'; import 'package:island/route.dart';
import 'package:island/widgets/alert.dart'; import 'package:island/widgets/alert.dart';
import 'package:island/widgets/app_scaffold.dart';
import 'package:island/widgets/content/markdown.dart'; import 'package:island/widgets/content/markdown.dart';
import 'package:island/widgets/content/sheet.dart';
import 'package:material_symbols_icons/material_symbols_icons.dart'; import 'package:material_symbols_icons/material_symbols_icons.dart';
import 'package:relative_time/relative_time.dart'; import 'package:relative_time/relative_time.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart';
@@ -128,19 +127,15 @@ class NotificationScreen extends HookConsumerWidget {
ref.watch(notificationUnreadCountNotifierProvider.notifier).clear(); ref.watch(notificationUnreadCountNotifierProvider.notifier).clear();
} }
return AppScaffold( return SheetScaffold(
appBar: AppBar( titleText: 'notifications'.tr(),
leading: const PageBackButton(),
title: const Text('notifications').tr(),
actions: [ actions: [
IconButton( IconButton(
onPressed: markAllRead, onPressed: markAllRead,
icon: const Icon(Symbols.mark_as_unread), icon: const Icon(Symbols.mark_as_unread),
), ),
const Gap(8),
], ],
), child: PagingHelperView(
body: PagingHelperView(
provider: notificationListNotifierProvider, provider: notificationListNotifierProvider,
futureRefreshable: notificationListNotifierProvider.future, futureRefreshable: notificationListNotifierProvider.future,
notifierRefreshable: notificationListNotifierProvider.notifier, notifierRefreshable: notificationListNotifierProvider.notifier,

View File

@@ -8,6 +8,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/post.dart'; import 'package:island/models/post.dart';
import 'package:island/models/publisher.dart'; import 'package:island/models/publisher.dart';
import 'package:island/models/account.dart'; import 'package:island/models/account.dart';
import 'package:island/models/heatmap.dart';
import 'package:island/pods/config.dart'; import 'package:island/pods/config.dart';
import 'package:island/pods/network.dart'; import 'package:island/pods/network.dart';
import 'package:island/services/color.dart'; import 'package:island/services/color.dart';
@@ -20,6 +21,7 @@ import 'package:island/widgets/app_scaffold.dart';
import 'package:island/widgets/content/cloud_files.dart'; import 'package:island/widgets/content/cloud_files.dart';
import 'package:island/widgets/content/markdown.dart'; import 'package:island/widgets/content/markdown.dart';
import 'package:island/widgets/post/post_list.dart'; import 'package:island/widgets/post/post_list.dart';
import 'package:island/widgets/activity_heatmap.dart';
import 'package:material_symbols_icons/symbols.dart'; import 'package:material_symbols_icons/symbols.dart';
import 'package:island/services/color_extraction.dart'; import 'package:island/services/color_extraction.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart';
@@ -222,6 +224,32 @@ class _PublisherBioWidget extends StatelessWidget {
} }
} }
class _PublisherHeatmapWidget extends StatelessWidget {
final AsyncValue<SnHeatmap?> heatmap;
final bool forceDense;
const _PublisherHeatmapWidget({
required this.heatmap,
this.forceDense = false,
});
@override
Widget build(BuildContext context) {
return heatmap.when(
data:
(data) =>
data != null
? ActivityHeatmapWidget(
heatmap: data,
forceDense: forceDense,
).padding(horizontal: 8)
: const SizedBox.shrink(),
loading: () => const SizedBox.shrink(),
error: (_, _) => const SizedBox.shrink(),
);
}
}
class _PublisherCategoryTabWidget extends StatelessWidget { class _PublisherCategoryTabWidget extends StatelessWidget {
final TabController categoryTabController; final TabController categoryTabController;
@@ -292,6 +320,13 @@ Future<Color?> publisherAppbarForcegroundColor(Ref ref, String pubName) async {
} }
} }
@riverpod
Future<SnHeatmap?> publisherHeatmap(Ref ref, String uname) async {
final apiClient = ref.watch(apiClientProvider);
final resp = await apiClient.get('/sphere/publishers/$uname/heatmap');
return SnHeatmap.fromJson(resp.data);
}
class PublisherProfileScreen extends HookConsumerWidget { class PublisherProfileScreen extends HookConsumerWidget {
final String name; final String name;
const PublisherProfileScreen({super.key, required this.name}); const PublisherProfileScreen({super.key, required this.name});
@@ -301,6 +336,7 @@ class PublisherProfileScreen extends HookConsumerWidget {
final publisher = ref.watch(publisherProvider(name)); final publisher = ref.watch(publisherProvider(name));
final badges = ref.watch(publisherBadgesProvider(name)); final badges = ref.watch(publisherBadgesProvider(name));
final subStatus = ref.watch(publisherSubscriptionStatusProvider(name)); final subStatus = ref.watch(publisherSubscriptionStatusProvider(name));
final heatmap = ref.watch(publisherHeatmapProvider(name));
final appbarColor = ref.watch( final appbarColor = ref.watch(
publisherAppbarForcegroundColorProvider(name), publisherAppbarForcegroundColorProvider(name),
); );
@@ -446,6 +482,10 @@ class PublisherProfileScreen extends HookConsumerWidget {
), ),
_PublisherVerificationWidget(data: data), _PublisherVerificationWidget(data: data),
_PublisherBioWidget(data: data), _PublisherBioWidget(data: data),
_PublisherHeatmapWidget(
heatmap: heatmap,
forceDense: true,
),
], ],
), ),
), ),
@@ -517,6 +557,9 @@ class PublisherProfileScreen extends HookConsumerWidget {
SliverToBoxAdapter( SliverToBoxAdapter(
child: _PublisherBioWidget(data: data), child: _PublisherBioWidget(data: data),
), ),
SliverToBoxAdapter(
child: _PublisherHeatmapWidget(heatmap: heatmap),
),
SliverPostList(pubName: name, pinned: true), SliverPostList(pubName: name, pinned: true),
SliverToBoxAdapter( SliverToBoxAdapter(
child: _PublisherCategoryTabWidget( child: _PublisherCategoryTabWidget(

View File

@@ -530,5 +530,126 @@ class _PublisherAppbarForcegroundColorProviderElement
(origin as PublisherAppbarForcegroundColorProvider).pubName; (origin as PublisherAppbarForcegroundColorProvider).pubName;
} }
String _$publisherHeatmapHash() => r'86db275ce3861a2855b5ec35fbfef85fc47b23a6';
/// See also [publisherHeatmap].
@ProviderFor(publisherHeatmap)
const publisherHeatmapProvider = PublisherHeatmapFamily();
/// See also [publisherHeatmap].
class PublisherHeatmapFamily extends Family<AsyncValue<SnHeatmap?>> {
/// See also [publisherHeatmap].
const PublisherHeatmapFamily();
/// See also [publisherHeatmap].
PublisherHeatmapProvider call(String uname) {
return PublisherHeatmapProvider(uname);
}
@override
PublisherHeatmapProvider getProviderOverride(
covariant PublisherHeatmapProvider provider,
) {
return call(provider.uname);
}
static const Iterable<ProviderOrFamily>? _dependencies = null;
@override
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
@override
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
_allTransitiveDependencies;
@override
String? get name => r'publisherHeatmapProvider';
}
/// See also [publisherHeatmap].
class PublisherHeatmapProvider extends AutoDisposeFutureProvider<SnHeatmap?> {
/// See also [publisherHeatmap].
PublisherHeatmapProvider(String uname)
: this._internal(
(ref) => publisherHeatmap(ref as PublisherHeatmapRef, uname),
from: publisherHeatmapProvider,
name: r'publisherHeatmapProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product')
? null
: _$publisherHeatmapHash,
dependencies: PublisherHeatmapFamily._dependencies,
allTransitiveDependencies:
PublisherHeatmapFamily._allTransitiveDependencies,
uname: uname,
);
PublisherHeatmapProvider._internal(
super._createNotifier, {
required super.name,
required super.dependencies,
required super.allTransitiveDependencies,
required super.debugGetCreateSourceHash,
required super.from,
required this.uname,
}) : super.internal();
final String uname;
@override
Override overrideWith(
FutureOr<SnHeatmap?> Function(PublisherHeatmapRef provider) create,
) {
return ProviderOverride(
origin: this,
override: PublisherHeatmapProvider._internal(
(ref) => create(ref as PublisherHeatmapRef),
from: from,
name: null,
dependencies: null,
allTransitiveDependencies: null,
debugGetCreateSourceHash: null,
uname: uname,
),
);
}
@override
AutoDisposeFutureProviderElement<SnHeatmap?> createElement() {
return _PublisherHeatmapProviderElement(this);
}
@override
bool operator ==(Object other) {
return other is PublisherHeatmapProvider && other.uname == uname;
}
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, uname.hashCode);
return _SystemHash.finish(hash);
}
}
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
mixin PublisherHeatmapRef on AutoDisposeFutureProviderRef<SnHeatmap?> {
/// The parameter `uname` of this provider.
String get uname;
}
class _PublisherHeatmapProviderElement
extends AutoDisposeFutureProviderElement<SnHeatmap?>
with PublisherHeatmapRef {
_PublisherHeatmapProviderElement(super.provider);
@override
String get uname => (origin as PublisherHeatmapProvider).uname;
}
// ignore_for_file: type=lint // ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package

View File

@@ -66,12 +66,12 @@ class TabsScreen extends HookConsumerWidget {
if (wideScreen) if (wideScreen)
NavigationDestination( NavigationDestination(
label: 'creatorHub'.tr(), label: 'creatorHub'.tr(),
icon: const Icon(Symbols.draw), icon: const Icon(Symbols.ink_pen),
), ),
if (wideScreen) if (wideScreen)
NavigationDestination( NavigationDestination(
label: 'developerHub'.tr(), label: 'developerHub'.tr(),
icon: const Icon(Symbols.code), icon: const Icon(Symbols.data_object),
), ),
]; ];
@@ -126,6 +126,7 @@ class TabsScreen extends HookConsumerWidget {
return Scaffold( return Scaffold(
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
extendBody: true, extendBody: true,
resizeToAvoidBottomInset: false,
body: ClipRRect( body: ClipRRect(
borderRadius: const BorderRadius.only( borderRadius: const BorderRadius.only(
topLeft: Radius.circular(16), topLeft: Radius.circular(16),

View File

@@ -11,8 +11,13 @@ import '../services/responsive.dart';
/// Shows exactly 365 days (wide screen) or 90 days (non-wide screen) of data ending at the current date. /// Shows exactly 365 days (wide screen) or 90 days (non-wide screen) of data ending at the current date.
class ActivityHeatmapWidget extends HookConsumerWidget { class ActivityHeatmapWidget extends HookConsumerWidget {
final SnHeatmap heatmap; final SnHeatmap heatmap;
final bool forceDense;
const ActivityHeatmapWidget({super.key, required this.heatmap}); const ActivityHeatmapWidget({
super.key,
required this.heatmap,
this.forceDense = false,
});
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
@@ -21,7 +26,7 @@ class ActivityHeatmapWidget extends HookConsumerWidget {
final now = DateTime.now(); final now = DateTime.now();
final isWide = isWideScreen(context); final isWide = isWideScreen(context);
final days = isWide ? 365 : 90; final days = (isWide && !forceDense) ? 365 : 90;
// Start from exactly the selected days ago // Start from exactly the selected days ago
final startDate = now.subtract(Duration(days: days)); final startDate = now.subtract(Duration(days: days));

View File

@@ -314,28 +314,22 @@ class AppScaffold extends HookConsumerWidget {
final noBackground = isNoBackground ?? isWideScreen(context); final noBackground = isNoBackground ?? isWideScreen(context);
final content = Column( final builtWidget = Focus(
children: [
IgnorePointer(
child: SizedBox(height: appBar != null ? appBarHeight + safeTop : 0),
),
if (body != null) Expanded(child: body!),
],
);
return Focus(
focusNode: focusNode, focusNode: focusNode,
child: Scaffold( child: Scaffold(
extendBody: extendBody ?? true, extendBody: extendBody ?? true,
extendBodyBehindAppBar: true, extendBodyBehindAppBar: true,
backgroundColor: backgroundColor: Colors.transparent,
noBackground body: Column(
? Colors.transparent children: [
: Theme.of(context).scaffoldBackgroundColor, IgnorePointer(
body: child: SizedBox(
noBackground height: appBar != null ? appBarHeight + safeTop : 0,
? content ),
: AppBackground(isRoot: true, child: content), ),
if (body != null) Expanded(child: body!),
],
),
appBar: appBar, appBar: appBar,
bottomNavigationBar: bottomNavigationBar, bottomNavigationBar: bottomNavigationBar,
bottomSheet: bottomSheet, bottomSheet: bottomSheet,
@@ -348,6 +342,10 @@ class AppScaffold extends HookConsumerWidget {
onEndDrawerChanged: onEndDrawerChanged, onEndDrawerChanged: onEndDrawerChanged,
), ),
); );
return noBackground
? builtWidget
: AppBackground(isRoot: true, child: builtWidget);
} }
} }

View File

@@ -73,10 +73,8 @@ class ChatInput extends HookConsumerWidget {
final chatSubscribe = ref.watch(chatSubscribeNotifierProvider(chatRoom.id)); final chatSubscribe = ref.watch(chatSubscribeNotifierProvider(chatRoom.id));
void send() { void send() {
onSend.call();
WidgetsBinding.instance.addPostFrameCallback((_) {
inputFocusNode.requestFocus(); inputFocusNode.requestFocus();
}); onSend.call();
} }
void insertNewLine() { void insertNewLine() {
@@ -539,6 +537,10 @@ class ChatInput extends HookConsumerWidget {
onTapOutside: onTapOutside:
(_) => (_) =>
FocusManager.instance.primaryFocus?.unfocus(), FocusManager.instance.primaryFocus?.unfocus(),
textInputAction:
settings.enterToSend
? TextInputAction.send
: null,
onSubmitted: onSubmitted:
settings.enterToSend ? (_) => send() : null, settings.enterToSend ? (_) => send() : null,
); );
@@ -550,11 +552,13 @@ class ChatInput extends HookConsumerWidget {
final triggerIndex = final triggerIndex =
atIndex > colonIndex ? atIndex : colonIndex; atIndex > colonIndex ? atIndex : colonIndex;
if (triggerIndex == -1) return []; if (triggerIndex == -1) return [];
final chopped = pattern.substring(triggerIndex);
if (chopped.contains(' ')) return [];
final service = ref.read(autocompleteServiceProvider); final service = ref.read(autocompleteServiceProvider);
try { try {
return await service.getSuggestions( return await service.getSuggestions(
chatRoom.id, chatRoom.id,
pattern, chopped,
); );
} catch (e) { } catch (e) {
return []; return [];
@@ -645,7 +649,7 @@ class ChatInput extends HookConsumerWidget {
direction: VerticalDirection.up, direction: VerticalDirection.up,
hideOnEmpty: true, hideOnEmpty: true,
hideOnLoading: true, hideOnLoading: true,
debounceDuration: const Duration(milliseconds: 500), debounceDuration: const Duration(milliseconds: 1000),
), ),
), ),
IconButton( IconButton(

View File

@@ -23,12 +23,12 @@ class PostComposeDialog extends HookConsumerWidget {
this.isBottomSheet = false, this.isBottomSheet = false,
}); });
static Future<SnPost?> show( static Future<bool?> show(
BuildContext context, { BuildContext context, {
SnPost? originalPost, SnPost? originalPost,
PostComposeInitialState? initialState, PostComposeInitialState? initialState,
}) { }) {
return showDialog<SnPost>( return showDialog<bool>(
context: context, context: context,
useRootNavigator: true, useRootNavigator: true,
builder: builder:

View File

@@ -149,9 +149,11 @@ class ComposeFormFields extends HookConsumerWidget {
final triggerIndex = final triggerIndex =
atIndex > colonIndex ? atIndex : colonIndex; atIndex > colonIndex ? atIndex : colonIndex;
if (triggerIndex == -1) return []; if (triggerIndex == -1) return [];
final chopped = pattern.substring(triggerIndex);
if (chopped.contains(' ')) return [];
final service = ref.read(autocompleteServiceProvider); final service = ref.read(autocompleteServiceProvider);
try { try {
return await service.getGeneralSuggestions(pattern); return await service.getGeneralSuggestions(chopped);
} catch (e) { } catch (e) {
return []; return [];
} }
@@ -235,7 +237,7 @@ class ComposeFormFields extends HookConsumerWidget {
direction: VerticalDirection.down, direction: VerticalDirection.down,
hideOnEmpty: true, hideOnEmpty: true,
hideOnLoading: true, hideOnLoading: true,
debounceDuration: const Duration(milliseconds: 500), debounceDuration: const Duration(milliseconds: 1000),
), ),
], ],
), ),

View File

@@ -23,6 +23,7 @@ class PostListNotifier extends _$PostListNotifier
List<String>? tags, List<String>? tags,
bool? pinned, bool? pinned,
bool shuffle = false, bool shuffle = false,
bool? includeReplies,
}) { }) {
return fetch(cursor: null); return fetch(cursor: null);
} }
@@ -42,6 +43,7 @@ class PostListNotifier extends _$PostListNotifier
if (categories != null) 'categories': categories, if (categories != null) 'categories': categories,
if (shuffle) 'shuffle': true, if (shuffle) 'shuffle': true,
if (pinned != null) 'pinned': pinned, if (pinned != null) 'pinned': pinned,
if (includeReplies != null) 'includeReplies': includeReplies,
}; };
final response = await client.get( final response = await client.get(

View File

@@ -6,7 +6,7 @@ part of 'post_list.dart';
// RiverpodGenerator // RiverpodGenerator
// ************************************************************************** // **************************************************************************
String _$postListNotifierHash() => r'3c0a8154ded4bcd8f5456f7a4ea2e542f57efa85'; String _$postListNotifierHash() => r'fc139ad4df0deb67bcbb949560319f2f7fbfb503';
/// Copied from Dart SDK /// Copied from Dart SDK
class _SystemHash { class _SystemHash {
@@ -38,6 +38,7 @@ abstract class _$PostListNotifier
late final List<String>? tags; late final List<String>? tags;
late final bool? pinned; late final bool? pinned;
late final bool shuffle; late final bool shuffle;
late final bool? includeReplies;
FutureOr<CursorPagingData<SnPost>> build({ FutureOr<CursorPagingData<SnPost>> build({
String? pubName, String? pubName,
@@ -47,6 +48,7 @@ abstract class _$PostListNotifier
List<String>? tags, List<String>? tags,
bool? pinned, bool? pinned,
bool shuffle = false, bool shuffle = false,
bool? includeReplies,
}); });
} }
@@ -69,6 +71,7 @@ class PostListNotifierFamily
List<String>? tags, List<String>? tags,
bool? pinned, bool? pinned,
bool shuffle = false, bool shuffle = false,
bool? includeReplies,
}) { }) {
return PostListNotifierProvider( return PostListNotifierProvider(
pubName: pubName, pubName: pubName,
@@ -78,6 +81,7 @@ class PostListNotifierFamily
tags: tags, tags: tags,
pinned: pinned, pinned: pinned,
shuffle: shuffle, shuffle: shuffle,
includeReplies: includeReplies,
); );
} }
@@ -93,6 +97,7 @@ class PostListNotifierFamily
tags: provider.tags, tags: provider.tags,
pinned: provider.pinned, pinned: provider.pinned,
shuffle: provider.shuffle, shuffle: provider.shuffle,
includeReplies: provider.includeReplies,
); );
} }
@@ -127,6 +132,7 @@ class PostListNotifierProvider
List<String>? tags, List<String>? tags,
bool? pinned, bool? pinned,
bool shuffle = false, bool shuffle = false,
bool? includeReplies,
}) : this._internal( }) : this._internal(
() => () =>
PostListNotifier() PostListNotifier()
@@ -136,7 +142,8 @@ class PostListNotifierProvider
..categories = categories ..categories = categories
..tags = tags ..tags = tags
..pinned = pinned ..pinned = pinned
..shuffle = shuffle, ..shuffle = shuffle
..includeReplies = includeReplies,
from: postListNotifierProvider, from: postListNotifierProvider,
name: r'postListNotifierProvider', name: r'postListNotifierProvider',
debugGetCreateSourceHash: debugGetCreateSourceHash:
@@ -153,6 +160,7 @@ class PostListNotifierProvider
tags: tags, tags: tags,
pinned: pinned, pinned: pinned,
shuffle: shuffle, shuffle: shuffle,
includeReplies: includeReplies,
); );
PostListNotifierProvider._internal( PostListNotifierProvider._internal(
@@ -169,6 +177,7 @@ class PostListNotifierProvider
required this.tags, required this.tags,
required this.pinned, required this.pinned,
required this.shuffle, required this.shuffle,
required this.includeReplies,
}) : super.internal(); }) : super.internal();
final String? pubName; final String? pubName;
@@ -178,6 +187,7 @@ class PostListNotifierProvider
final List<String>? tags; final List<String>? tags;
final bool? pinned; final bool? pinned;
final bool shuffle; final bool shuffle;
final bool? includeReplies;
@override @override
FutureOr<CursorPagingData<SnPost>> runNotifierBuild( FutureOr<CursorPagingData<SnPost>> runNotifierBuild(
@@ -191,6 +201,7 @@ class PostListNotifierProvider
tags: tags, tags: tags,
pinned: pinned, pinned: pinned,
shuffle: shuffle, shuffle: shuffle,
includeReplies: includeReplies,
); );
} }
@@ -207,7 +218,8 @@ class PostListNotifierProvider
..categories = categories ..categories = categories
..tags = tags ..tags = tags
..pinned = pinned ..pinned = pinned
..shuffle = shuffle, ..shuffle = shuffle
..includeReplies = includeReplies,
from: from, from: from,
name: null, name: null,
dependencies: null, dependencies: null,
@@ -220,6 +232,7 @@ class PostListNotifierProvider
tags: tags, tags: tags,
pinned: pinned, pinned: pinned,
shuffle: shuffle, shuffle: shuffle,
includeReplies: includeReplies,
), ),
); );
} }
@@ -242,7 +255,8 @@ class PostListNotifierProvider
other.categories == categories && other.categories == categories &&
other.tags == tags && other.tags == tags &&
other.pinned == pinned && other.pinned == pinned &&
other.shuffle == shuffle; other.shuffle == shuffle &&
other.includeReplies == includeReplies;
} }
@override @override
@@ -255,6 +269,7 @@ class PostListNotifierProvider
hash = _SystemHash.combine(hash, tags.hashCode); hash = _SystemHash.combine(hash, tags.hashCode);
hash = _SystemHash.combine(hash, pinned.hashCode); hash = _SystemHash.combine(hash, pinned.hashCode);
hash = _SystemHash.combine(hash, shuffle.hashCode); hash = _SystemHash.combine(hash, shuffle.hashCode);
hash = _SystemHash.combine(hash, includeReplies.hashCode);
return _SystemHash.finish(hash); return _SystemHash.finish(hash);
} }
@@ -284,6 +299,9 @@ mixin PostListNotifierRef
/// The parameter `shuffle` of this provider. /// The parameter `shuffle` of this provider.
bool get shuffle; bool get shuffle;
/// The parameter `includeReplies` of this provider.
bool? get includeReplies;
} }
class _PostListNotifierProviderElement class _PostListNotifierProviderElement
@@ -310,6 +328,9 @@ class _PostListNotifierProviderElement
bool? get pinned => (origin as PostListNotifierProvider).pinned; bool? get pinned => (origin as PostListNotifierProvider).pinned;
@override @override
bool get shuffle => (origin as PostListNotifierProvider).shuffle; bool get shuffle => (origin as PostListNotifierProvider).shuffle;
@override
bool? get includeReplies =>
(origin as PostListNotifierProvider).includeReplies;
} }
// ignore_for_file: type=lint // ignore_for_file: type=lint

View File

@@ -68,21 +68,24 @@ class PostQuickReply extends HookConsumerWidget {
} }
} }
const kInputChipHeight = 54.0;
return publishers.when( return publishers.when(
data: data:
(data) => Material( (data) => Material(
elevation: 2, elevation: 2,
color: Theme.of(context).colorScheme.surfaceContainerHighest, color: Theme.of(context).colorScheme.surfaceContainerHighest,
borderRadius: BorderRadius.circular(20), borderRadius: BorderRadius.circular(28),
child: Padding( child: Container(
padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 8), constraints: BoxConstraints(minHeight: kInputChipHeight),
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6),
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
GestureDetector( GestureDetector(
child: ProfilePictureWidget( child: ProfilePictureWidget(
fileId: currentPublisher.value?.picture?.id, fileId: currentPublisher.value?.picture?.id,
radius: 16, radius: (kInputChipHeight * 0.5) - 6,
), ),
onTap: () { onTap: () {
showModalBottomSheet( showModalBottomSheet(
@@ -106,11 +109,13 @@ class PostQuickReply extends HookConsumerWidget {
isCollapsed: true, isCollapsed: true,
contentPadding: EdgeInsets.symmetric( contentPadding: EdgeInsets.symmetric(
horizontal: 12, horizontal: 12,
vertical: 9, vertical: 14,
), ),
visualDensity: VisualDensity.compact,
), ),
style: TextStyle(fontSize: 14), style: TextStyle(fontSize: 14),
maxLines: null, minLines: 1,
maxLines: 5,
onTapOutside: onTapOutside:
(_) => FocusManager.instance.primaryFocus?.unfocus(), (_) => FocusManager.instance.primaryFocus?.unfocus(),
), ),
@@ -130,6 +135,10 @@ class PostQuickReply extends HookConsumerWidget {
}, },
icon: const Icon(Symbols.launch, size: 20), icon: const Icon(Symbols.launch, size: 20),
visualDensity: VisualDensity.compact, visualDensity: VisualDensity.compact,
constraints: BoxConstraints(
maxHeight: kInputChipHeight - 6,
minHeight: kInputChipHeight - 6,
),
), ),
IconButton( IconButton(
icon: icon:
@@ -143,6 +152,10 @@ class PostQuickReply extends HookConsumerWidget {
color: Theme.of(context).colorScheme.primary, color: Theme.of(context).colorScheme.primary,
onPressed: submitting.value ? null : performAction, onPressed: submitting.value ? null : performAction,
visualDensity: VisualDensity.compact, visualDensity: VisualDensity.compact,
constraints: BoxConstraints(
maxHeight: kInputChipHeight - 6,
minHeight: kInputChipHeight - 6,
),
), ),
], ],
), ),

View File

@@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:gap/gap.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/post.dart'; import 'package:island/models/post.dart';
import 'package:island/pods/userinfo.dart'; import 'package:island/pods/userinfo.dart';
@@ -19,11 +20,9 @@ class PostRepliesSheet extends HookConsumerWidget {
return SheetScaffold( return SheetScaffold(
titleText: 'repliesCount'.plural(post.repliesCount), titleText: 'repliesCount'.plural(post.repliesCount),
child: Column( child: Stack(
children: [ children: [
// Replies list CustomScrollView(
Expanded(
child: CustomScrollView(
slivers: [ slivers: [
PostRepliesList( PostRepliesList(
postId: post.id.toString(), postId: post.id.toString(),
@@ -31,12 +30,15 @@ class PostRepliesSheet extends HookConsumerWidget {
Navigator.pop(context); Navigator.pop(context);
}, },
), ),
SliverGap(80),
], ],
), ),
),
// Quick reply section
if (user.value != null) if (user.value != null)
PostQuickReply( Positioned(
bottom: 0,
left: 0,
right: 0,
child: PostQuickReply(
parent: post, parent: post,
onPosted: () { onPosted: () {
ref.invalidate(postRepliesNotifierProvider(post.id)); ref.invalidate(postRepliesNotifierProvider(post.id));
@@ -49,6 +51,7 @@ class PostRepliesSheet extends HookConsumerWidget {
top: 8, top: 8,
horizontal: 16, horizontal: 16,
), ),
),
], ],
), ),
); );

View File

@@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts # In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix. # of the product and file versions while build-number is used as the build suffix.
version: 3.3.0+135 version: 3.3.0+136
environment: environment:
sdk: ^3.7.2 sdk: ^3.7.2