💄 Bunch of optimization
This commit is contained in:
parent
b492db90ca
commit
5976d61997
@ -551,9 +551,11 @@ class _PostListWidgetState extends State<_PostListWidget> {
|
|||||||
maxWidth: 640,
|
maxWidth: 640,
|
||||||
);
|
);
|
||||||
case 'reader.news':
|
case 'reader.news':
|
||||||
return Container(
|
return Center(
|
||||||
constraints: BoxConstraints(maxWidth: 640),
|
child: Container(
|
||||||
child: NewsFeedEntry(data: ele),
|
constraints: BoxConstraints(maxWidth: 640),
|
||||||
|
child: NewsFeedEntry(data: ele),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
default:
|
default:
|
||||||
return Container(
|
return Container(
|
||||||
|
@ -389,7 +389,7 @@ class _HomeDashServiceStatusState extends State<_HomeDashServiceStatus> {
|
|||||||
size: 20,
|
size: 20,
|
||||||
),
|
),
|
||||||
const Gap(10),
|
const Gap(10),
|
||||||
Text('serviceStatusOperational').tr(),
|
Text('loading').tr(),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
: switch (_serviceStatus) {
|
: switch (_serviceStatus) {
|
||||||
@ -434,6 +434,7 @@ class _HomeDashServiceStatusState extends State<_HomeDashServiceStatus> {
|
|||||||
padding: EdgeInsets.only(top: 6),
|
padding: EdgeInsets.only(top: 6),
|
||||||
child: Wrap(
|
child: Wrap(
|
||||||
spacing: 8,
|
spacing: 8,
|
||||||
|
runSpacing: 8,
|
||||||
children: [
|
children: [
|
||||||
for (final entry in _statuses!.entries)
|
for (final entry in _statuses!.entries)
|
||||||
Tooltip(
|
Tooltip(
|
||||||
@ -441,6 +442,8 @@ class _HomeDashServiceStatusState extends State<_HomeDashServiceStatus> {
|
|||||||
? 'serviceName${kServicesName[entry.key]}'.tr()
|
? 'serviceName${kServicesName[entry.key]}'.tr()
|
||||||
: 'unknown'.tr(),
|
: 'unknown'.tr(),
|
||||||
child: Chip(
|
child: Chip(
|
||||||
|
visualDensity:
|
||||||
|
VisualDensity(horizontal: -4, vertical: -4),
|
||||||
avatar: entry.value
|
avatar: entry.value
|
||||||
? const Icon(
|
? const Icon(
|
||||||
Symbols.circle,
|
Symbols.circle,
|
||||||
@ -877,8 +880,10 @@ class _HomeDashRecommendationPostWidgetState
|
|||||||
).tr(),
|
).tr(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
Text('${_currentPage + 1}/${_posts?.length ?? 0}',
|
Text(
|
||||||
style: GoogleFonts.robotoMono())
|
'${_currentPage + 1}/${_posts?.length ?? 0}',
|
||||||
|
style: GoogleFonts.robotoMono(),
|
||||||
|
)
|
||||||
],
|
],
|
||||||
).padding(horizontal: 18, top: 12, bottom: 8),
|
).padding(horizontal: 18, top: 12, bottom: 8),
|
||||||
Expanded(
|
Expanded(
|
||||||
@ -896,6 +901,7 @@ class _HomeDashRecommendationPostWidgetState
|
|||||||
child: PostItem(
|
child: PostItem(
|
||||||
data: _posts![index],
|
data: _posts![index],
|
||||||
showMenu: false,
|
showMenu: false,
|
||||||
|
showFullPost: true,
|
||||||
).padding(bottom: 8),
|
).padding(bottom: 8),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
GoRouter.of(context)
|
GoRouter.of(context)
|
||||||
|
@ -63,7 +63,10 @@ class _NotificationScreenState extends State<NotificationScreen> {
|
|||||||
queryParameters: {'take': 10, 'offset': _notifications.length},
|
queryParameters: {'take': 10, 'offset': _notifications.length},
|
||||||
);
|
);
|
||||||
_totalCount = resp.data['count'];
|
_totalCount = resp.data['count'];
|
||||||
_notifications.addAll(resp.data['data']?.map((e) => SnNotification.fromJson(e)).cast<SnNotification>() ?? []);
|
_notifications.addAll(resp.data['data']
|
||||||
|
?.map((e) => SnNotification.fromJson(e))
|
||||||
|
.cast<SnNotification>() ??
|
||||||
|
[]);
|
||||||
nty.updateTray();
|
nty.updateTray();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
@ -98,7 +101,8 @@ class _NotificationScreenState extends State<NotificationScreen> {
|
|||||||
nty.clear();
|
nty.clear();
|
||||||
|
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
context.showSnackbar('notificationMarkAllReadPrompt'.plural(resp.data['count']));
|
context.showSnackbar(
|
||||||
|
'notificationMarkAllReadPrompt'.plural(resp.data['count']));
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
context.showErrorDialog(err);
|
context.showErrorDialog(err);
|
||||||
@ -122,7 +126,8 @@ class _NotificationScreenState extends State<NotificationScreen> {
|
|||||||
_fetchNotifications();
|
_fetchNotifications();
|
||||||
|
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
context.showSnackbar('notificationMarkOneReadPrompt'.tr(args: ['#${notification.id}']));
|
context.showSnackbar(
|
||||||
|
'notificationMarkOneReadPrompt'.tr(args: ['#${notification.id}']));
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
context.showErrorDialog(err);
|
context.showErrorDialog(err);
|
||||||
@ -143,7 +148,9 @@ class _NotificationScreenState extends State<NotificationScreen> {
|
|||||||
|
|
||||||
if (!ua.isAuthorized) {
|
if (!ua.isAuthorized) {
|
||||||
return AppScaffold(
|
return AppScaffold(
|
||||||
appBar: AppBar(leading: AutoAppBarLeading(), title: Text('screenNotification').tr()),
|
appBar: AppBar(
|
||||||
|
leading: AutoAppBarLeading(),
|
||||||
|
title: Text('screenNotification').tr()),
|
||||||
body: Center(child: UnauthorizedHint()),
|
body: Center(child: UnauthorizedHint()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -153,7 +160,9 @@ class _NotificationScreenState extends State<NotificationScreen> {
|
|||||||
leading: AutoAppBarLeading(),
|
leading: AutoAppBarLeading(),
|
||||||
title: Text('screenNotification').tr(),
|
title: Text('screenNotification').tr(),
|
||||||
actions: [
|
actions: [
|
||||||
IconButton(icon: const Icon(Symbols.checklist), onPressed: _isSubmitting ? null : _markAllAsRead),
|
IconButton(
|
||||||
|
icon: const Icon(Symbols.checklist),
|
||||||
|
onPressed: _isSubmitting ? null : _markAllAsRead),
|
||||||
const Gap(8),
|
const Gap(8),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -167,13 +176,17 @@ class _NotificationScreenState extends State<NotificationScreen> {
|
|||||||
return _fetchNotifications();
|
return _fetchNotifications();
|
||||||
},
|
},
|
||||||
child: InfiniteList(
|
child: InfiniteList(
|
||||||
padding: EdgeInsets.only(top: 16, bottom: math.max(MediaQuery.of(context).padding.bottom, 16)),
|
padding: EdgeInsets.only(
|
||||||
|
top: 16,
|
||||||
|
bottom:
|
||||||
|
math.max(MediaQuery.of(context).padding.bottom, 16)),
|
||||||
itemCount: _notifications.length,
|
itemCount: _notifications.length,
|
||||||
onFetchData: () {
|
onFetchData: () {
|
||||||
_fetchNotifications();
|
_fetchNotifications();
|
||||||
},
|
},
|
||||||
isLoading: _isBusy,
|
isLoading: _isBusy,
|
||||||
hasReachedMax: _totalCount != null && _notifications.length >= _totalCount!,
|
hasReachedMax: _totalCount != null &&
|
||||||
|
_notifications.length >= _totalCount!,
|
||||||
itemBuilder: (context, idx) {
|
itemBuilder: (context, idx) {
|
||||||
final nty = _notifications[idx];
|
final nty = _notifications[idx];
|
||||||
return Row(
|
return Row(
|
||||||
@ -186,12 +199,19 @@ class _NotificationScreenState extends State<NotificationScreen> {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
if (nty.readAt == null)
|
if (nty.readAt == null)
|
||||||
StyledWidget(Badge(label: Text('notificationUnread').tr())).padding(bottom: 4),
|
StyledWidget(Badge(
|
||||||
Text(nty.title, style: Theme.of(context).textTheme.titleMedium),
|
label: Text('notificationUnread').tr()))
|
||||||
|
.padding(bottom: 4),
|
||||||
|
Text(nty.title,
|
||||||
|
style: Theme.of(context).textTheme.titleMedium),
|
||||||
if (nty.subtitle != null)
|
if (nty.subtitle != null)
|
||||||
Text(nty.subtitle!, style: Theme.of(context).textTheme.titleSmall),
|
Text(nty.subtitle!,
|
||||||
|
style:
|
||||||
|
Theme.of(context).textTheme.titleSmall),
|
||||||
if (nty.subtitle != null) const Gap(4),
|
if (nty.subtitle != null) const Gap(4),
|
||||||
SelectionArea(child: MarkdownTextContent(content: nty.body, isAutoWarp: true)),
|
SelectionArea(
|
||||||
|
child: MarkdownTextContent(
|
||||||
|
content: nty.body, isAutoWarp: true)),
|
||||||
if ([
|
if ([
|
||||||
'interactive.reply',
|
'interactive.reply',
|
||||||
'interactive.feedback',
|
'interactive.feedback',
|
||||||
@ -201,31 +221,43 @@ class _NotificationScreenState extends State<NotificationScreen> {
|
|||||||
GestureDetector(
|
GestureDetector(
|
||||||
child: Container(
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
borderRadius: const BorderRadius.all(
|
||||||
border: Border.all(color: Theme.of(context).dividerColor, width: 1),
|
Radius.circular(8)),
|
||||||
|
border: Border.all(
|
||||||
|
color: Theme.of(context).dividerColor,
|
||||||
|
width: 1),
|
||||||
),
|
),
|
||||||
child: PostItem(
|
child: PostItem(
|
||||||
data: SnPost.fromJson(nty.metadata['related_post']!),
|
data: SnPost.fromJson(
|
||||||
|
nty.metadata['related_post']!),
|
||||||
showComments: false,
|
showComments: false,
|
||||||
showReactions: false,
|
showReactions: false,
|
||||||
showMenu: false,
|
showMenu: false,
|
||||||
),
|
).padding(vertical: 4),
|
||||||
),
|
),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
GoRouter.of(context).pushNamed(
|
GoRouter.of(context).pushNamed(
|
||||||
'postDetail',
|
'postDetail',
|
||||||
pathParameters: {'slug': nty.metadata['related_post']!['id'].toString()},
|
pathParameters: {
|
||||||
|
'slug': nty
|
||||||
|
.metadata['related_post']!['id']
|
||||||
|
.toString()
|
||||||
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
).padding(top: 8),
|
).padding(top: 8),
|
||||||
const Gap(8),
|
const Gap(8),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Text(DateFormat('yy/MM/dd').format(nty.createdAt)).fontSize(12),
|
Text(DateFormat('yy/MM/dd')
|
||||||
|
.format(nty.createdAt))
|
||||||
|
.fontSize(12),
|
||||||
const Gap(4),
|
const Gap(4),
|
||||||
Text('·', style: TextStyle(fontSize: 12)),
|
Text('·', style: TextStyle(fontSize: 12)),
|
||||||
const Gap(4),
|
const Gap(4),
|
||||||
Text(RelativeTime(context).format(nty.createdAt)).fontSize(12),
|
Text(RelativeTime(context)
|
||||||
|
.format(nty.createdAt))
|
||||||
|
.fontSize(12),
|
||||||
],
|
],
|
||||||
).opacity(0.75),
|
).opacity(0.75),
|
||||||
],
|
],
|
||||||
@ -235,8 +267,10 @@ class _NotificationScreenState extends State<NotificationScreen> {
|
|||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Symbols.check),
|
icon: const Icon(Symbols.check),
|
||||||
padding: EdgeInsets.all(0),
|
padding: EdgeInsets.all(0),
|
||||||
visualDensity: const VisualDensity(horizontal: -4, vertical: -4),
|
visualDensity:
|
||||||
onPressed: _isSubmitting ? null : () => _markOneAsRead(nty),
|
const VisualDensity(horizontal: -4, vertical: -4),
|
||||||
|
onPressed:
|
||||||
|
_isSubmitting ? null : () => _markOneAsRead(nty),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
).padding(horizontal: 16);
|
).padding(horizontal: 16);
|
||||||
|
@ -22,7 +22,8 @@ class PostDetailScreen extends StatefulWidget {
|
|||||||
final SnPost? preload;
|
final SnPost? preload;
|
||||||
final Function? onBack;
|
final Function? onBack;
|
||||||
|
|
||||||
const PostDetailScreen({super.key, required this.slug, this.preload, this.onBack});
|
const PostDetailScreen(
|
||||||
|
{super.key, required this.slug, this.preload, this.onBack});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<PostDetailScreen> createState() => _PostDetailScreenState();
|
State<PostDetailScreen> createState() => _PostDetailScreenState();
|
||||||
@ -88,14 +89,16 @@ class _PostDetailScreenState extends State<PostDetailScreen> {
|
|||||||
TextSpan(
|
TextSpan(
|
||||||
text: _data?.body['title'] ?? 'postNoun'.tr(),
|
text: _data?.body['title'] ?? 'postNoun'.tr(),
|
||||||
style: Theme.of(context).textTheme.titleLarge!.copyWith(
|
style: Theme.of(context).textTheme.titleLarge!.copyWith(
|
||||||
color: Theme.of(context).appBarTheme.foregroundColor!,
|
color:
|
||||||
|
Theme.of(context).appBarTheme.foregroundColor!,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const TextSpan(text: '\n'),
|
const TextSpan(text: '\n'),
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: 'postDetail'.tr(),
|
text: 'postDetail'.tr(),
|
||||||
style: Theme.of(context).textTheme.bodySmall!.copyWith(
|
style: Theme.of(context).textTheme.bodySmall!.copyWith(
|
||||||
color: Theme.of(context).appBarTheme.foregroundColor!,
|
color:
|
||||||
|
Theme.of(context).appBarTheme.foregroundColor!,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
]),
|
]),
|
||||||
@ -124,8 +127,11 @@ class _PostDetailScreenState extends State<PostDetailScreen> {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (_data != null && _data!.type != 'video') const SliverToBoxAdapter(child: Divider(height: 1)),
|
if (_data != null)
|
||||||
if (_data != null && _data!.type != 'video')
|
SliverToBoxAdapter(
|
||||||
|
child: Divider(height: 1).padding(top: 8),
|
||||||
|
),
|
||||||
|
if (_data != null)
|
||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
child: Container(
|
child: Container(
|
||||||
constraints: BoxConstraints(maxWidth: maxWidth),
|
constraints: BoxConstraints(maxWidth: maxWidth),
|
||||||
@ -141,7 +147,7 @@ class _PostDetailScreenState extends State<PostDetailScreen> {
|
|||||||
).padding(horizontal: 20, vertical: 12).center(),
|
).padding(horizontal: 20, vertical: 12).center(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (_data != null && ua.isAuthorized && _data!.type != 'video')
|
if (_data != null && ua.isAuthorized)
|
||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
child: PostCommentQuickAction(
|
child: PostCommentQuickAction(
|
||||||
parentPost: _data!,
|
parentPost: _data!,
|
||||||
@ -158,13 +164,15 @@ class _PostDetailScreenState extends State<PostDetailScreen> {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (_data != null && _data!.type != 'video')
|
if (_data != null) SliverGap(8),
|
||||||
|
if (_data != null)
|
||||||
PostCommentSliverList(
|
PostCommentSliverList(
|
||||||
key: _childListKey,
|
key: _childListKey,
|
||||||
parentPost: _data!,
|
parentPost: _data!,
|
||||||
maxWidth: maxWidth,
|
maxWidth: maxWidth,
|
||||||
),
|
),
|
||||||
if (_data != null && _data!.type == 'video') SliverGap(math.max(MediaQuery.of(context).padding.bottom, 16)),
|
if (_data != null)
|
||||||
|
SliverGap(math.max(MediaQuery.of(context).padding.bottom, 16)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
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:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
import 'package:surface/providers/post.dart';
|
import 'package:surface/providers/post.dart';
|
||||||
import 'package:surface/types/post.dart';
|
import 'package:surface/types/post.dart';
|
||||||
import 'package:surface/widgets/dialog.dart';
|
import 'package:surface/widgets/dialog.dart';
|
||||||
@ -77,7 +77,8 @@ class _PostDraftBoxState extends State<PostDraftBox> {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
separatorBuilder: (_, __) => const Gap(8),
|
separatorBuilder: (_, __) =>
|
||||||
|
const Divider().padding(vertical: 2),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -45,7 +45,8 @@ class _PostSearchScreenState extends State<PostSearchScreen> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _fetchPosts() async {
|
Future<void> _fetchPosts() async {
|
||||||
if (_searchTerm.isEmpty && _searchCategories.isEmpty && _searchTags.isEmpty) return;
|
if (_searchTerm.isEmpty && _searchCategories.isEmpty && _searchTags.isEmpty)
|
||||||
|
return;
|
||||||
if (_postCount != null && _posts.length >= _postCount!) return;
|
if (_postCount != null && _posts.length >= _postCount!) return;
|
||||||
|
|
||||||
setState(() => _isBusy = true);
|
setState(() => _isBusy = true);
|
||||||
@ -152,7 +153,7 @@ class _PostSearchScreenState extends State<PostSearchScreen> {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
separatorBuilder: (_, __) => const Gap(8),
|
separatorBuilder: (_, __) => const Divider().padding(vertical: 2),
|
||||||
),
|
),
|
||||||
Positioned(
|
Positioned(
|
||||||
top: 16,
|
top: 16,
|
||||||
@ -166,7 +167,8 @@ class _PostSearchScreenState extends State<PostSearchScreen> {
|
|||||||
padding: const WidgetStatePropertyAll(
|
padding: const WidgetStatePropertyAll(
|
||||||
EdgeInsets.symmetric(horizontal: 24),
|
EdgeInsets.symmetric(horizontal: 24),
|
||||||
),
|
),
|
||||||
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
onTapOutside: (_) =>
|
||||||
|
FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
_searchTerm = value;
|
_searchTerm = value;
|
||||||
},
|
},
|
||||||
|
@ -34,9 +34,11 @@ class PostPublisherScreen extends StatefulWidget {
|
|||||||
State<PostPublisherScreen> createState() => _PostPublisherScreenState();
|
State<PostPublisherScreen> createState() => _PostPublisherScreenState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _PostPublisherScreenState extends State<PostPublisherScreen> with SingleTickerProviderStateMixin {
|
class _PostPublisherScreenState extends State<PostPublisherScreen>
|
||||||
|
with SingleTickerProviderStateMixin {
|
||||||
late final ScrollController _scrollController = ScrollController();
|
late final ScrollController _scrollController = ScrollController();
|
||||||
late final TabController _tabController = TabController(length: 3, vsync: this);
|
late final TabController _tabController =
|
||||||
|
TabController(length: 3, vsync: this);
|
||||||
|
|
||||||
SnPublisher? _publisher;
|
SnPublisher? _publisher;
|
||||||
SnAccount? _account;
|
SnAccount? _account;
|
||||||
@ -66,7 +68,8 @@ class _PostPublisherScreenState extends State<PostPublisherScreen> with SingleTi
|
|||||||
_account = await ud.getAccount(_publisher?.accountId);
|
_account = await ud.getAccount(_publisher?.accountId);
|
||||||
_accountRelationship = await rel.getRelationship(_account!.id);
|
_accountRelationship = await rel.getRelationship(_account!.id);
|
||||||
if (_publisher?.realmId != null && _publisher!.realmId != 0) {
|
if (_publisher?.realmId != null && _publisher!.realmId != 0) {
|
||||||
final resp = await sn.client.get('/cgi/id/realms/${_publisher!.realmId}');
|
final resp =
|
||||||
|
await sn.client.get('/cgi/id/realms/${_publisher!.realmId}');
|
||||||
_realm = SnRealm.fromJson(resp.data);
|
_realm = SnRealm.fromJson(resp.data);
|
||||||
}
|
}
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
@ -133,12 +136,14 @@ class _PostPublisherScreenState extends State<PostPublisherScreen> with SingleTi
|
|||||||
double _appBarBlur = 0.0;
|
double _appBarBlur = 0.0;
|
||||||
|
|
||||||
late final _appBarWidth = MediaQuery.of(context).size.width;
|
late final _appBarWidth = MediaQuery.of(context).size.width;
|
||||||
late final _appBarHeight = (_appBarWidth * kBannerAspectRatio).roundToDouble();
|
late final _appBarHeight =
|
||||||
|
(_appBarWidth * kBannerAspectRatio).roundToDouble();
|
||||||
|
|
||||||
void _updateAppBarBlur() {
|
void _updateAppBarBlur() {
|
||||||
if (_scrollController.offset > _appBarHeight) return;
|
if (_scrollController.offset > _appBarHeight) return;
|
||||||
setState(() {
|
setState(() {
|
||||||
_appBarBlur = (_scrollController.offset / _appBarHeight * 10).clamp(0.0, 10.0);
|
_appBarBlur =
|
||||||
|
(_scrollController.offset / _appBarHeight * 10).clamp(0.0, 10.0);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,7 +198,8 @@ class _PostPublisherScreenState extends State<PostPublisherScreen> with SingleTi
|
|||||||
'related': _account!.name,
|
'related': _account!.name,
|
||||||
});
|
});
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
context.showSnackbar('userBlocked'.tr(args: ['@${_account?.name ?? 'unknown'.tr()}']));
|
context.showSnackbar(
|
||||||
|
'userBlocked'.tr(args: ['@${_account?.name ?? 'unknown'.tr()}']));
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
context.showErrorDialog(err);
|
context.showErrorDialog(err);
|
||||||
@ -209,9 +215,11 @@ class _PostPublisherScreenState extends State<PostPublisherScreen> with SingleTi
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
final rel = context.read<SnRelationshipProvider>();
|
final rel = context.read<SnRelationshipProvider>();
|
||||||
await rel.updateRelationship(_account!.id, 1, _accountRelationship?.permNodes ?? {});
|
await rel.updateRelationship(
|
||||||
|
_account!.id, 1, _accountRelationship?.permNodes ?? {});
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
context.showSnackbar('userUnblocked'.tr(args: ['@${_account?.name ?? 'unknown'.tr()}']));
|
context.showSnackbar(
|
||||||
|
'userUnblocked'.tr(args: ['@${_account?.name ?? 'unknown'.tr()}']));
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
context.showErrorDialog(err);
|
context.showErrorDialog(err);
|
||||||
@ -299,7 +307,10 @@ class _PostPublisherScreenState extends State<PostPublisherScreen> with SingleTi
|
|||||||
text: TextSpan(children: [
|
text: TextSpan(children: [
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: _publisher!.nick,
|
text: _publisher!.nick,
|
||||||
style: Theme.of(context).textTheme.titleLarge!.copyWith(
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.titleLarge!
|
||||||
|
.copyWith(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
shadows: labelShadows,
|
shadows: labelShadows,
|
||||||
),
|
),
|
||||||
@ -307,7 +318,10 @@ class _PostPublisherScreenState extends State<PostPublisherScreen> with SingleTi
|
|||||||
const TextSpan(text: '\n'),
|
const TextSpan(text: '\n'),
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: '@${_publisher!.name}',
|
text: '@${_publisher!.name}',
|
||||||
style: Theme.of(context).textTheme.bodySmall!.copyWith(
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.bodySmall!
|
||||||
|
.copyWith(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
shadows: labelShadows,
|
shadows: labelShadows,
|
||||||
),
|
),
|
||||||
@ -330,13 +344,16 @@ class _PostPublisherScreenState extends State<PostPublisherScreen> with SingleTi
|
|||||||
)
|
)
|
||||||
else
|
else
|
||||||
Container(
|
Container(
|
||||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
color: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.surfaceContainer,
|
||||||
),
|
),
|
||||||
Positioned(
|
Positioned(
|
||||||
top: 0,
|
top: 0,
|
||||||
left: 0,
|
left: 0,
|
||||||
right: 0,
|
right: 0,
|
||||||
height: 56 + MediaQuery.of(context).padding.top,
|
height:
|
||||||
|
56 + MediaQuery.of(context).padding.top,
|
||||||
child: ClipRect(
|
child: ClipRect(
|
||||||
child: BackdropFilter(
|
child: BackdropFilter(
|
||||||
filter: ImageFilter.blur(
|
filter: ImageFilter.blur(
|
||||||
@ -345,7 +362,8 @@ class _PostPublisherScreenState extends State<PostPublisherScreen> with SingleTi
|
|||||||
),
|
),
|
||||||
child: Container(
|
child: Container(
|
||||||
color: Colors.black.withOpacity(
|
color: Colors.black.withOpacity(
|
||||||
clampDouble(_appBarBlur * 0.1, 0, 0.5),
|
clampDouble(
|
||||||
|
_appBarBlur * 0.1, 0, 0.5),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -372,11 +390,14 @@ class _PostPublisherScreenState extends State<PostPublisherScreen> with SingleTi
|
|||||||
const Gap(16),
|
const Gap(16),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment:
|
||||||
|
CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
_publisher!.nick,
|
_publisher!.nick,
|
||||||
style: Theme.of(context).textTheme.titleMedium,
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.titleMedium,
|
||||||
).bold(),
|
).bold(),
|
||||||
Text('@${_publisher!.name}').fontSize(13),
|
Text('@${_publisher!.name}').fontSize(13),
|
||||||
],
|
],
|
||||||
@ -387,7 +408,9 @@ class _PostPublisherScreenState extends State<PostPublisherScreen> with SingleTi
|
|||||||
style: ButtonStyle(
|
style: ButtonStyle(
|
||||||
elevation: WidgetStatePropertyAll(0),
|
elevation: WidgetStatePropertyAll(0),
|
||||||
),
|
),
|
||||||
onPressed: _isSubscribing ? null : _toggleSubscription,
|
onPressed: _isSubscribing
|
||||||
|
? null
|
||||||
|
: _toggleSubscription,
|
||||||
label: Text('subscribe').tr(),
|
label: Text('subscribe').tr(),
|
||||||
icon: const Icon(Symbols.add),
|
icon: const Icon(Symbols.add),
|
||||||
)
|
)
|
||||||
@ -396,14 +419,17 @@ class _PostPublisherScreenState extends State<PostPublisherScreen> with SingleTi
|
|||||||
style: ButtonStyle(
|
style: ButtonStyle(
|
||||||
elevation: WidgetStatePropertyAll(0),
|
elevation: WidgetStatePropertyAll(0),
|
||||||
),
|
),
|
||||||
onPressed: _isSubscribing ? null : _toggleSubscription,
|
onPressed: _isSubscribing
|
||||||
|
? null
|
||||||
|
: _toggleSubscription,
|
||||||
label: Text('unsubscribe').tr(),
|
label: Text('unsubscribe').tr(),
|
||||||
icon: const Icon(Symbols.remove),
|
icon: const Icon(Symbols.remove),
|
||||||
),
|
),
|
||||||
PopupMenuButton(
|
PopupMenuButton(
|
||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
style: ButtonStyle(
|
style: ButtonStyle(
|
||||||
visualDensity: VisualDensity(horizontal: -4, vertical: -4),
|
visualDensity: VisualDensity(
|
||||||
|
horizontal: -4, vertical: -4),
|
||||||
),
|
),
|
||||||
itemBuilder: (BuildContext context) => [
|
itemBuilder: (BuildContext context) => [
|
||||||
PopupMenuItem(
|
PopupMenuItem(
|
||||||
@ -443,7 +469,8 @@ class _PostPublisherScreenState extends State<PostPublisherScreen> with SingleTi
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
const Gap(12),
|
const Gap(12),
|
||||||
Text(_publisher!.description).padding(horizontal: 8),
|
Text(_publisher!.description)
|
||||||
|
.padding(horizontal: 8),
|
||||||
const Gap(12),
|
const Gap(12),
|
||||||
Column(
|
Column(
|
||||||
children: [
|
children: [
|
||||||
@ -451,8 +478,10 @@ class _PostPublisherScreenState extends State<PostPublisherScreen> with SingleTi
|
|||||||
children: [
|
children: [
|
||||||
const Icon(Symbols.calendar_add_on),
|
const Icon(Symbols.calendar_add_on),
|
||||||
const Gap(8),
|
const Gap(8),
|
||||||
Text('publisherJoinedAt')
|
Text('publisherJoinedAt').tr(args: [
|
||||||
.tr(args: [DateFormat('y/M/d').format(_publisher!.createdAt)]),
|
DateFormat('y/M/d')
|
||||||
|
.format(_publisher!.createdAt)
|
||||||
|
]),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
Row(
|
Row(
|
||||||
@ -460,7 +489,8 @@ class _PostPublisherScreenState extends State<PostPublisherScreen> with SingleTi
|
|||||||
const Icon(Symbols.trending_up),
|
const Icon(Symbols.trending_up),
|
||||||
const Gap(8),
|
const Gap(8),
|
||||||
Text('publisherSocialPointTotal').plural(
|
Text('publisherSocialPointTotal').plural(
|
||||||
_publisher!.totalUpvote - _publisher!.totalDownvote,
|
_publisher!.totalUpvote -
|
||||||
|
_publisher!.totalDownvote,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -470,18 +500,22 @@ class _PostPublisherScreenState extends State<PostPublisherScreen> with SingleTi
|
|||||||
const Icon(Symbols.group_work),
|
const Icon(Symbols.group_work),
|
||||||
const Gap(8),
|
const Gap(8),
|
||||||
InkWell(
|
InkWell(
|
||||||
child: Text('publisherAffiliatedBy').tr(args: [
|
child: Text('publisherAffiliatedBy')
|
||||||
|
.tr(args: [
|
||||||
'@${_realm?.alias ?? 'unknown'}',
|
'@${_realm?.alias ?? 'unknown'}',
|
||||||
]),
|
]),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
GoRouter.of(context).pushNamed(
|
GoRouter.of(context).pushNamed(
|
||||||
'realmDetail',
|
'realmDetail',
|
||||||
pathParameters: {'alias': _realm!.alias},
|
pathParameters: {
|
||||||
|
'alias': _realm!.alias
|
||||||
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
const Gap(8),
|
const Gap(8),
|
||||||
AccountImage(content: _realm?.avatar, radius: 8),
|
AccountImage(
|
||||||
|
content: _realm?.avatar, radius: 8),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
Row(
|
Row(
|
||||||
@ -502,7 +536,8 @@ class _PostPublisherScreenState extends State<PostPublisherScreen> with SingleTi
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
const Gap(8),
|
const Gap(8),
|
||||||
AccountImage(content: _account?.avatar, radius: 8),
|
AccountImage(
|
||||||
|
content: _account?.avatar, radius: 8),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -606,7 +641,7 @@ class _PublisherPostList extends StatelessWidget {
|
|||||||
onDeleted: onDeleted,
|
onDeleted: onDeleted,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
separatorBuilder: (_, __) => const Gap(8),
|
separatorBuilder: (_, __) => const Divider().padding(vertical: 2),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,89 +19,87 @@ class NewsFeedEntry extends StatelessWidget {
|
|||||||
.cast<SnNewsArticle>()
|
.cast<SnNewsArticle>()
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
return Card(
|
return Column(
|
||||||
child: Column(
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
children: [
|
||||||
children: [
|
Row(
|
||||||
Row(
|
children: [
|
||||||
children: [
|
const Icon(Symbols.newspaper),
|
||||||
const Icon(Symbols.newspaper),
|
const Gap(8),
|
||||||
const Gap(8),
|
Text(
|
||||||
Text(
|
'newsToday',
|
||||||
'newsToday',
|
style: Theme.of(context).textTheme.titleLarge,
|
||||||
style: Theme.of(context).textTheme.titleLarge,
|
).tr()
|
||||||
).tr()
|
],
|
||||||
],
|
).padding(horizontal: 18, top: 12, bottom: 8),
|
||||||
).padding(horizontal: 18, top: 12, bottom: 8),
|
Container(
|
||||||
Container(
|
margin: const EdgeInsets.only(bottom: 12),
|
||||||
margin: const EdgeInsets.only(bottom: 12),
|
height: 150,
|
||||||
height: 150,
|
child: ListView.separated(
|
||||||
child: ListView.separated(
|
scrollDirection: Axis.horizontal,
|
||||||
scrollDirection: Axis.horizontal,
|
itemCount: news.length,
|
||||||
itemCount: news.length,
|
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
itemBuilder: (context, idx) {
|
||||||
itemBuilder: (context, idx) {
|
return Container(
|
||||||
return Container(
|
width: 360,
|
||||||
width: 360,
|
decoration: BoxDecoration(
|
||||||
decoration: BoxDecoration(
|
border: Border.all(
|
||||||
border: Border.all(
|
color: Theme.of(context).dividerColor,
|
||||||
color: Theme.of(context).dividerColor,
|
width: 1,
|
||||||
width: 1,
|
|
||||||
),
|
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
|
||||||
),
|
),
|
||||||
child: Material(
|
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||||
elevation: 0,
|
),
|
||||||
color: Theme.of(context).colorScheme.surface,
|
child: Material(
|
||||||
|
elevation: 0,
|
||||||
|
color: Theme.of(context).colorScheme.surface,
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||||
|
child: InkWell(
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||||
child: InkWell(
|
child: Column(
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
child: Column(
|
children: [
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
Text(
|
||||||
children: [
|
news[idx].title,
|
||||||
Text(
|
maxLines: 2,
|
||||||
news[idx].title,
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
maxLines: 2,
|
).padding(horizontal: 16, top: 12, bottom: 4),
|
||||||
style: Theme.of(context).textTheme.titleMedium,
|
Text(
|
||||||
).padding(horizontal: 16, top: 12, bottom: 4),
|
news[idx].description,
|
||||||
Text(
|
maxLines: 2,
|
||||||
news[idx].description,
|
style: Theme.of(context).textTheme.bodyMedium,
|
||||||
maxLines: 2,
|
).padding(horizontal: 16, vertical: 4),
|
||||||
style: Theme.of(context).textTheme.bodyMedium,
|
const Gap(4),
|
||||||
).padding(horizontal: 16, vertical: 4),
|
Row(
|
||||||
const Gap(4),
|
children: [
|
||||||
Row(
|
Text(
|
||||||
children: [
|
DateFormat('y/M/d HH:mm')
|
||||||
Text(
|
.format(news[idx].createdAt.toLocal()),
|
||||||
DateFormat('y/M/d HH:mm')
|
style: Theme.of(context).textTheme.bodySmall,
|
||||||
.format(news[idx].createdAt.toLocal()),
|
),
|
||||||
style: Theme.of(context).textTheme.bodySmall,
|
const Gap(4),
|
||||||
),
|
Text(
|
||||||
const Gap(4),
|
RelativeTime(context)
|
||||||
Text(
|
.format(news[idx].createdAt.toLocal()),
|
||||||
RelativeTime(context)
|
style: Theme.of(context).textTheme.bodySmall,
|
||||||
.format(news[idx].createdAt.toLocal()),
|
),
|
||||||
style: Theme.of(context).textTheme.bodySmall,
|
],
|
||||||
),
|
).opacity(0.8).padding(horizontal: 16),
|
||||||
],
|
],
|
||||||
).opacity(0.8).padding(horizontal: 16),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
onTap: () {
|
|
||||||
GoRouter.of(context).pushNamed(
|
|
||||||
'newsDetail',
|
|
||||||
pathParameters: {'hash': news[idx].hash},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
|
onTap: () {
|
||||||
|
GoRouter.of(context).pushNamed(
|
||||||
|
'newsDetail',
|
||||||
|
pathParameters: {'hash': news[idx].hash},
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
},
|
);
|
||||||
separatorBuilder: (_, __) => const Gap(12),
|
},
|
||||||
),
|
separatorBuilder: (_, __) => const Gap(12),
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,16 +12,14 @@ class FeedUnknownEntry extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Card(
|
return Column(
|
||||||
child: Column(
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
children: [
|
||||||
children: [
|
const Icon(Symbols.help, size: 36),
|
||||||
const Icon(Symbols.help, size: 36),
|
const Gap(4),
|
||||||
const Gap(4),
|
Text('feedUnknownItem').tr(),
|
||||||
Text('feedUnknownItem').tr(),
|
Text(data.type, style: GoogleFonts.robotoMono()),
|
||||||
Text(data.type, style: GoogleFonts.robotoMono()),
|
],
|
||||||
],
|
).padding(horizontal: 12, vertical: 8);
|
||||||
).padding(horizontal: 12, vertical: 8),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,57 +23,54 @@ class FediversePostWidget extends StatelessWidget {
|
|||||||
return Center(
|
return Center(
|
||||||
child: Container(
|
child: Container(
|
||||||
constraints: BoxConstraints(maxWidth: maxWidth),
|
constraints: BoxConstraints(maxWidth: maxWidth),
|
||||||
child: Card(
|
child: Column(
|
||||||
margin: EdgeInsets.zero,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
child: Column(
|
children: [
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Row(
|
AccountImage(
|
||||||
children: [
|
content: data.user.avatar,
|
||||||
AccountImage(
|
radius: 20,
|
||||||
content: data.user.avatar,
|
|
||||||
radius: 20,
|
|
||||||
),
|
|
||||||
const Gap(12),
|
|
||||||
Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
data.user.nick.isNotEmpty
|
|
||||||
? data.user.nick
|
|
||||||
: '@${data.user.name}',
|
|
||||||
maxLines: 1,
|
|
||||||
).bold(),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
data.user.identifier.contains('@')
|
|
||||||
? data.user.identifier
|
|
||||||
: '${data.user.identifier}@${data.user.origin}',
|
|
||||||
maxLines: 1,
|
|
||||||
).fontSize(13),
|
|
||||||
const Gap(4),
|
|
||||||
Text(
|
|
||||||
RelativeTime(context)
|
|
||||||
.format(data.createdAt.toLocal()),
|
|
||||||
).fontSize(13),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
).padding(horizontal: 12, vertical: 8),
|
|
||||||
MarkdownTextContent(
|
|
||||||
isAutoWarp: true,
|
|
||||||
content: html2md.convert(data.content),
|
|
||||||
).padding(horizontal: 16, bottom: 6),
|
|
||||||
if (data.images.isNotEmpty)
|
|
||||||
_FediversePostImageList(
|
|
||||||
data: data,
|
|
||||||
maxWidth: maxWidth,
|
|
||||||
),
|
),
|
||||||
],
|
const Gap(12),
|
||||||
),
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
data.user.nick.isNotEmpty
|
||||||
|
? data.user.nick
|
||||||
|
: '@${data.user.name}',
|
||||||
|
maxLines: 1,
|
||||||
|
).bold(),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
data.user.identifier.contains('@')
|
||||||
|
? data.user.identifier
|
||||||
|
: '${data.user.identifier}@${data.user.origin}',
|
||||||
|
maxLines: 1,
|
||||||
|
).fontSize(13),
|
||||||
|
const Gap(4),
|
||||||
|
Text(
|
||||||
|
RelativeTime(context)
|
||||||
|
.format(data.createdAt.toLocal()),
|
||||||
|
).fontSize(13),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).padding(horizontal: 12, vertical: 8),
|
||||||
|
MarkdownTextContent(
|
||||||
|
isAutoWarp: true,
|
||||||
|
content: html2md.convert(data.content),
|
||||||
|
).padding(horizontal: 16, bottom: 6),
|
||||||
|
if (data.images.isNotEmpty)
|
||||||
|
_FediversePostImageList(
|
||||||
|
data: data,
|
||||||
|
maxWidth: maxWidth,
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -166,6 +166,14 @@ class _PostItemState extends State<PostItem> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didUpdateWidget(covariant PostItem oldWidget) {
|
||||||
|
_displayText = widget.data.body['content'] ?? '';
|
||||||
|
_displayTitle = widget.data.body['title'] ?? '';
|
||||||
|
_displayDescription = widget.data.body['description'] ?? '';
|
||||||
|
super.didUpdateWidget(oldWidget);
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _translateText() async {
|
Future<void> _translateText() async {
|
||||||
final ta = context.read<SnTranslator>();
|
final ta = context.read<SnTranslator>();
|
||||||
setState(() => _isTranslating = true);
|
setState(() => _isTranslating = true);
|
||||||
@ -284,6 +292,247 @@ class _PostItemState extends State<PostItem> {
|
|||||||
attachmentSize -= 80;
|
attachmentSize -= 80;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (widget.showFullPost) {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
constraints:
|
||||||
|
BoxConstraints(maxWidth: widget.maxWidth ?? double.infinity),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
if (widget.showAvatar)
|
||||||
|
_PostAvatar(
|
||||||
|
data: widget.data,
|
||||||
|
isCompact: false,
|
||||||
|
),
|
||||||
|
if (widget.showAvatar) const Gap(12),
|
||||||
|
Expanded(
|
||||||
|
child: _PostContentHeader(
|
||||||
|
isRelativeDate: !widget.showFullPost,
|
||||||
|
isCompact: false,
|
||||||
|
data: widget.data,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
_PostActionPopup(
|
||||||
|
data: widget.data,
|
||||||
|
isAuthor: isAuthor,
|
||||||
|
onShare: () => _doShare(context),
|
||||||
|
onShareImage: () => _doShareViaPicture(context),
|
||||||
|
onSelectAnswer: widget.onSelectAnswer,
|
||||||
|
onDeleted: () {
|
||||||
|
widget.onDeleted?.call();
|
||||||
|
},
|
||||||
|
onTranslate: () {
|
||||||
|
_translateText();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const Gap(8),
|
||||||
|
if (widget.data.preload?.thumbnail != null)
|
||||||
|
Container(
|
||||||
|
margin: const EdgeInsets.only(bottom: 8),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: const BorderRadius.all(
|
||||||
|
Radius.circular(8),
|
||||||
|
),
|
||||||
|
border: Border.all(
|
||||||
|
color: Theme.of(context).dividerColor,
|
||||||
|
width: 1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: AspectRatio(
|
||||||
|
aspectRatio: 16 / 9,
|
||||||
|
child: ClipRRect(
|
||||||
|
borderRadius: const BorderRadius.all(
|
||||||
|
Radius.circular(8),
|
||||||
|
),
|
||||||
|
child: AutoResizeUniversalImage(
|
||||||
|
sn.getAttachmentUrl(
|
||||||
|
widget.data.preload!.thumbnail!.rid,
|
||||||
|
),
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (widget.data.preload?.video != null)
|
||||||
|
_PostVideoPlayer(data: widget.data).padding(bottom: 8),
|
||||||
|
if (widget.data.type == 'question')
|
||||||
|
_PostQuestionHint(data: widget.data).padding(bottom: 8),
|
||||||
|
if (_displayDescription.isNotEmpty ||
|
||||||
|
_displayTitle.isNotEmpty)
|
||||||
|
_PostHeadline(
|
||||||
|
title: _displayTitle,
|
||||||
|
description: _displayDescription,
|
||||||
|
data: widget.data,
|
||||||
|
isEnlarge: widget.data.type == 'article' &&
|
||||||
|
widget.showFullPost,
|
||||||
|
).padding(bottom: 8),
|
||||||
|
if (widget.data.type == 'article' && !widget.showFullPost)
|
||||||
|
Text('postArticle')
|
||||||
|
.tr()
|
||||||
|
.fontSize(13)
|
||||||
|
.opacity(0.75)
|
||||||
|
.padding(bottom: 8),
|
||||||
|
if ((_displayText.isNotEmpty) &&
|
||||||
|
(widget.showFullPost ||
|
||||||
|
widget.data.type != 'article'))
|
||||||
|
_PostContentBody(
|
||||||
|
text: _displayText,
|
||||||
|
data: widget.data,
|
||||||
|
isSelectable: widget.showFullPost,
|
||||||
|
isEnlarge: widget.data.type == 'article' &&
|
||||||
|
widget.showFullPost,
|
||||||
|
).padding(bottom: 6),
|
||||||
|
if (widget.data.visibility > 0)
|
||||||
|
_PostVisibilityHint(data: widget.data).padding(
|
||||||
|
vertical: 4,
|
||||||
|
),
|
||||||
|
if (widget.data.body['content_truncated'] == true)
|
||||||
|
_PostTruncatedHint(data: widget.data).padding(
|
||||||
|
vertical: 4,
|
||||||
|
),
|
||||||
|
if (widget.data.tags.isNotEmpty)
|
||||||
|
_PostTagsList(data: widget.data)
|
||||||
|
.padding(top: 4, bottom: 6),
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
spacing: 4,
|
||||||
|
children: [
|
||||||
|
if (widget.showViews)
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Icon(Symbols.play_circle, size: 20),
|
||||||
|
const Gap(4),
|
||||||
|
Text('postViews')
|
||||||
|
.plural(widget.data.totalViews),
|
||||||
|
],
|
||||||
|
).opacity(0.75),
|
||||||
|
if (_isTranslating)
|
||||||
|
AnimateWidgetExtensions(Row(
|
||||||
|
children: [
|
||||||
|
Icon(Symbols.translate, size: 20),
|
||||||
|
const Gap(4),
|
||||||
|
Text('translating').tr(),
|
||||||
|
],
|
||||||
|
))
|
||||||
|
.animate(onPlay: (e) => e.repeat())
|
||||||
|
.fadeIn(duration: 500.ms, curve: Curves.easeOut)
|
||||||
|
.then()
|
||||||
|
.fadeOut(
|
||||||
|
duration: 500.ms,
|
||||||
|
delay: 1000.ms,
|
||||||
|
curve: Curves.easeIn,
|
||||||
|
),
|
||||||
|
if (_isTranslated)
|
||||||
|
InkWell(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Icon(Symbols.translate, size: 20),
|
||||||
|
const Gap(4),
|
||||||
|
Text('translated').tr(),
|
||||||
|
],
|
||||||
|
).opacity(0.75),
|
||||||
|
onTap: () {
|
||||||
|
setState(() {
|
||||||
|
_displayText =
|
||||||
|
widget.data.body['content'] ?? '';
|
||||||
|
_displayTitle =
|
||||||
|
widget.data.body['title'] ?? '';
|
||||||
|
_displayDescription =
|
||||||
|
widget.data.body['description'] ?? '';
|
||||||
|
_isTranslated = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
if (widget.data.repostTo != null)
|
||||||
|
_PostQuoteContent(child: widget.data.repostTo!)
|
||||||
|
.padding(
|
||||||
|
top: 4,
|
||||||
|
bottom: widget.data.preload?.attachments
|
||||||
|
?.isNotEmpty ??
|
||||||
|
false
|
||||||
|
? 12
|
||||||
|
: 0,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).padding(
|
||||||
|
bottom:
|
||||||
|
widget.showViews || _isTranslated || _isTranslating
|
||||||
|
? 8
|
||||||
|
: 0,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
).padding(horizontal: 12, top: 8),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (displayableAttachments?.isNotEmpty ?? false)
|
||||||
|
AttachmentList(
|
||||||
|
data: displayableAttachments!,
|
||||||
|
bordered: true,
|
||||||
|
maxHeight: widget.showFullPost ? null : 480,
|
||||||
|
minWidth: attachmentSize,
|
||||||
|
maxWidth: attachmentSize,
|
||||||
|
fit: widget.showFullPost ? BoxFit.cover : BoxFit.contain,
|
||||||
|
padding: EdgeInsets.only(left: 12, right: 12),
|
||||||
|
),
|
||||||
|
if (widget.data.preload?.poll != null)
|
||||||
|
StyledWidget(Container(
|
||||||
|
constraints:
|
||||||
|
BoxConstraints(maxWidth: widget.maxWidth ?? double.infinity),
|
||||||
|
child: PostPoll(poll: widget.data.preload!.poll!),
|
||||||
|
))
|
||||||
|
.padding(
|
||||||
|
left: 12,
|
||||||
|
right: 12,
|
||||||
|
top: 12,
|
||||||
|
bottom: 4,
|
||||||
|
)
|
||||||
|
.center(),
|
||||||
|
if (widget.data.body['content'] != null &&
|
||||||
|
(cfg.prefs.getBool(kAppExpandPostLink) ?? true))
|
||||||
|
LinkPreviewWidget(
|
||||||
|
text: widget.data.body['content'],
|
||||||
|
).padding(left: 12, right: 4),
|
||||||
|
if (widget.showExpandableComments)
|
||||||
|
_PostCommentIntent(
|
||||||
|
data: widget.data,
|
||||||
|
showAvatar: widget.showAvatar,
|
||||||
|
).padding(left: 12, right: 12)
|
||||||
|
else
|
||||||
|
_PostFeaturedComment(data: widget.data, maxWidth: widget.maxWidth)
|
||||||
|
.padding(left: 12, right: 12),
|
||||||
|
if (widget.showReactions)
|
||||||
|
Center(
|
||||||
|
child: Container(
|
||||||
|
constraints: BoxConstraints(
|
||||||
|
maxWidth: (widget.maxWidth ?? double.infinity) + 24,
|
||||||
|
),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 4),
|
||||||
|
child: _PostReactionList(
|
||||||
|
data: widget.data,
|
||||||
|
padding: EdgeInsets.only(left: 12, right: 12),
|
||||||
|
onChanged: _onChanged,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
@ -389,15 +638,6 @@ class _PostItemState extends State<PostItem> {
|
|||||||
isEnlarge: widget.data.type == 'article' &&
|
isEnlarge: widget.data.type == 'article' &&
|
||||||
widget.showFullPost,
|
widget.showFullPost,
|
||||||
).padding(bottom: 6),
|
).padding(bottom: 6),
|
||||||
if (widget.data.repostTo != null)
|
|
||||||
_PostQuoteContent(child: widget.data.repostTo!)
|
|
||||||
.padding(
|
|
||||||
bottom:
|
|
||||||
widget.data.preload?.attachments?.isNotEmpty ??
|
|
||||||
false
|
|
||||||
? 12
|
|
||||||
: 0,
|
|
||||||
),
|
|
||||||
if (widget.data.visibility > 0)
|
if (widget.data.visibility > 0)
|
||||||
_PostVisibilityHint(data: widget.data).padding(
|
_PostVisibilityHint(data: widget.data).padding(
|
||||||
vertical: 4,
|
vertical: 4,
|
||||||
@ -462,12 +702,25 @@ class _PostItemState extends State<PostItem> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
).padding(
|
).padding(
|
||||||
bottom: widget.showViews ||
|
bottom: (widget.showViews ||
|
||||||
_isTranslated ||
|
_isTranslated ||
|
||||||
_isTranslating
|
_isTranslating) &&
|
||||||
|
(widget.data.repostTo != null ||
|
||||||
|
(widget.data.preload?.attachments
|
||||||
|
?.isNotEmpty ??
|
||||||
|
false))
|
||||||
? 8
|
? 8
|
||||||
: 0,
|
: 0,
|
||||||
),
|
),
|
||||||
|
if (widget.data.repostTo != null)
|
||||||
|
_PostQuoteContent(child: widget.data.repostTo!)
|
||||||
|
.padding(
|
||||||
|
bottom:
|
||||||
|
(widget.data.preload?.attachments?.isNotEmpty ??
|
||||||
|
false)
|
||||||
|
? 8
|
||||||
|
: 0,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@ -504,7 +757,7 @@ class _PostItemState extends State<PostItem> {
|
|||||||
data: widget.data,
|
data: widget.data,
|
||||||
showAvatar: widget.showAvatar,
|
showAvatar: widget.showAvatar,
|
||||||
).padding(left: widget.showAvatar ? 60 : 12, right: 12)
|
).padding(left: widget.showAvatar ? 60 : 12, right: 12)
|
||||||
else
|
else if (widget.showComments)
|
||||||
_PostFeaturedComment(data: widget.data, maxWidth: widget.maxWidth)
|
_PostFeaturedComment(data: widget.data, maxWidth: widget.maxWidth)
|
||||||
.padding(left: widget.showAvatar ? 60 : 12, right: 12),
|
.padding(left: widget.showAvatar ? 60 : 12, right: 12),
|
||||||
if (widget.showReactions)
|
if (widget.showReactions)
|
||||||
@ -1403,10 +1656,19 @@ class _PostQuoteContent extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
Column(
|
Column(
|
||||||
children: [
|
children: [
|
||||||
_PostContentHeader(
|
Row(
|
||||||
data: child,
|
children: [
|
||||||
isCompact: true,
|
_PostAvatar(
|
||||||
isRelativeDate: isRelativeDate,
|
data: child,
|
||||||
|
isCompact: true,
|
||||||
|
),
|
||||||
|
const Gap(8),
|
||||||
|
_PostContentHeader(
|
||||||
|
data: child,
|
||||||
|
isCompact: true,
|
||||||
|
isRelativeDate: isRelativeDate,
|
||||||
|
),
|
||||||
|
],
|
||||||
).padding(bottom: 4),
|
).padding(bottom: 4),
|
||||||
_PostContentBody(
|
_PostContentBody(
|
||||||
data: child,
|
data: child,
|
||||||
@ -1637,6 +1899,7 @@ class _PostCommentIntentState extends State<_PostCommentIntent> {
|
|||||||
children: [
|
children: [
|
||||||
if (_comments.isNotEmpty)
|
if (_comments.isNotEmpty)
|
||||||
Card(
|
Card(
|
||||||
|
elevation: 4,
|
||||||
margin: EdgeInsets.zero,
|
margin: EdgeInsets.zero,
|
||||||
child: Column(
|
child: Column(
|
||||||
spacing: 8,
|
spacing: 8,
|
||||||
@ -1652,7 +1915,7 @@ class _PostCommentIntentState extends State<_PostCommentIntent> {
|
|||||||
).padding(vertical: 8, left: 6),
|
).padding(vertical: 8, left: 6),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
).padding(bottom: 8),
|
).padding(vertical: 8),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Transform.flip(
|
Transform.flip(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user