Compare commits

..

12 Commits

Author SHA1 Message Date
69d5e95565 🚀 Launch 2.4.2+86 March Update 2025-03-31 23:05:36 +08:00
3e3442fc89 💄 Enable new sfx on special days 2025-03-31 23:00:13 +08:00
8181010b0b 💄 New desktop loading animation 2025-03-31 22:50:08 +08:00
269caf7555 💄 Some improvements
🐛 Bug fixes
 The heart reaction
2025-03-31 01:27:45 +08:00
ae0809ad35 💄 Optimize background color 2025-03-31 00:51:37 +08:00
4005f03cf8 🐛 Fix notification 2025-03-30 23:25:38 +08:00
4bd8ec54f1 Optimize initialization 2025-03-30 20:43:47 +08:00
51a387851f 🐛 Fix infinite starting up 2025-03-30 20:37:04 +08:00
8ed847d870 ♻️ Use API Version 2 to load post 2025-03-30 15:31:02 +08:00
dfe13de220 Program Badges 2025-03-29 17:00:17 +08:00
b02a54c1e9 🐛 Fix sound mode 2025-03-29 16:41:23 +08:00
55a7e7d900 🐛 Fix app drawer did not close after selected 2025-03-29 01:04:37 +08:00
51 changed files with 824 additions and 486 deletions

View File

@ -941,5 +941,12 @@
"settingsResetMemorizedWindowSize": "Reset Window Size", "settingsResetMemorizedWindowSize": "Reset Window Size",
"settingsResetMemorizedWindowSizeDescription": "Reset the memorized window size, and set it to the default size.", "settingsResetMemorizedWindowSizeDescription": "Reset the memorized window size, and set it to the default size.",
"chatDirect": "Direct Messages", "chatDirect": "Direct Messages",
"back": "返回" "back": "Back",
"badgeProgramDeveloper": "Developer Program Member",
"badgeProgramStellar": "A Stellar",
"badgeProgramModerator": "Community Moderator",
"postEditedHint": "edited",
"splashScreenServer": "Server",
"splashScreenServerName": "Potato",
"splashScreenCaption": "Trying to establishing connection with HyperNet™"
} }

View File

@ -938,5 +938,12 @@
"settingsResetMemorizedWindowSize": "重置窗口大小", "settingsResetMemorizedWindowSize": "重置窗口大小",
"settingsResetMemorizedWindowSizeDescription": "重置记忆的窗口大小,以重新设置为默认大小。", "settingsResetMemorizedWindowSizeDescription": "重置记忆的窗口大小,以重新设置为默认大小。",
"chatDirect": "私信", "chatDirect": "私信",
"back": "返回" "back": "返回",
"badgeProgramDeveloper": "开发者计划成员",
"badgeProgramStellar": "一颗恒星",
"badgeProgramModerator": "社区管理员",
"postEditedHint": "已编辑",
"splashScreenServer": "服务器",
"splashScreenServerName": "土豆",
"splashScreenCaption": "正在尝试与 HyperNet™ 取得太阳链连接"
} }

View File

@ -189,6 +189,11 @@ PODS:
- Flutter - Flutter
- flutter_webrtc - flutter_webrtc
- WebRTC-SDK (= 125.6422.06) - WebRTC-SDK (= 125.6422.06)
- livekit_noise_filter (0.0.1):
- Flutter
- flutter_webrtc
- LiveKitKrispNoiseFilter (= 0.0.7)
- LiveKitKrispNoiseFilter (0.0.7)
- media_kit_libs_ios_video (1.0.4): - media_kit_libs_ios_video (1.0.4):
- Flutter - Flutter
- media_kit_video (0.0.1): - media_kit_video (0.0.1):
@ -283,6 +288,7 @@ DEPENDENCIES:
- in_app_review (from `.symlinks/plugins/in_app_review/ios`) - in_app_review (from `.symlinks/plugins/in_app_review/ios`)
- Kingfisher (~> 8.0) - Kingfisher (~> 8.0)
- livekit_client (from `.symlinks/plugins/livekit_client/ios`) - livekit_client (from `.symlinks/plugins/livekit_client/ios`)
- livekit_noise_filter (from `.symlinks/plugins/livekit_noise_filter/ios`)
- media_kit_libs_ios_video (from `.symlinks/plugins/media_kit_libs_ios_video/ios`) - media_kit_libs_ios_video (from `.symlinks/plugins/media_kit_libs_ios_video/ios`)
- media_kit_video (from `.symlinks/plugins/media_kit_video/ios`) - media_kit_video (from `.symlinks/plugins/media_kit_video/ios`)
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
@ -315,6 +321,7 @@ SPEC REPOS:
- GoogleDataTransport - GoogleDataTransport
- GoogleUtilities - GoogleUtilities
- Kingfisher - Kingfisher
- LiveKitKrispNoiseFilter
- nanopb - nanopb
- OrderedSet - OrderedSet
- PromisesObjC - PromisesObjC
@ -369,6 +376,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/in_app_review/ios" :path: ".symlinks/plugins/in_app_review/ios"
livekit_client: livekit_client:
:path: ".symlinks/plugins/livekit_client/ios" :path: ".symlinks/plugins/livekit_client/ios"
livekit_noise_filter:
:path: ".symlinks/plugins/livekit_noise_filter/ios"
media_kit_libs_ios_video: media_kit_libs_ios_video:
:path: ".symlinks/plugins/media_kit_libs_ios_video/ios" :path: ".symlinks/plugins/media_kit_libs_ios_video/ios"
media_kit_video: media_kit_video:
@ -438,6 +447,8 @@ SPEC CHECKSUMS:
in_app_review: 5596fe56fab799e8edb3561c03d053363ab13457 in_app_review: 5596fe56fab799e8edb3561c03d053363ab13457
Kingfisher: 323e5c4ec7983aaace12af655a7b51a7f88a599d Kingfisher: 323e5c4ec7983aaace12af655a7b51a7f88a599d
livekit_client: 08755cabfa4da4ed455642f460cfbb39bc518070 livekit_client: 08755cabfa4da4ed455642f460cfbb39bc518070
livekit_noise_filter: a26aeb1c1eae6db0a023fd2f6ea3ff108c3ecbb0
LiveKitKrispNoiseFilter: efe418ceca28163ace0ff222bd2cc02384645d84
media_kit_libs_ios_video: 5a18affdb97d1f5d466dc79988b13eff6c5e2854 media_kit_libs_ios_video: 5a18affdb97d1f5d466dc79988b13eff6c5e2854
media_kit_video: 1746e198cb697d1ffb734b1d05ec429d1fcd1474 media_kit_video: 1746e198cb697d1ffb734b1d05ec429d1fcd1474
nanopb: fad817b59e0457d11a5dfbde799381cd727c1275 nanopb: fad817b59e0457d11a5dfbde799381cd727c1275

View File

@ -241,7 +241,7 @@ class PostWriteController extends ChangeNotifier {
contentController.text = post.body['content'] ?? ''; contentController.text = post.body['content'] ?? '';
aliasController.text = post.alias ?? ''; aliasController.text = post.alias ?? '';
rewardController.text = post.body['reward']?.toString() ?? ''; rewardController.text = post.body['reward']?.toString() ?? '';
videoAttachment = post.preload?.video; videoAttachment = SnAttachment.fromJson(post.body['video']);
publishedAt = post.publishedAt; publishedAt = post.publishedAt;
publishedUntil = post.publishedUntil; publishedUntil = post.publishedUntil;
visibleUsers = List.from(post.visibleUsersList ?? [], growable: true); visibleUsers = List.from(post.visibleUsersList ?? [], growable: true);
@ -252,17 +252,21 @@ class PostWriteController extends ChangeNotifier {
categories = categories =
List.from(post.categories.map((ele) => ele.alias), growable: true); List.from(post.categories.map((ele) => ele.alias), growable: true);
attachments.addAll( attachments.addAll(
post.preload?.attachments?.map((ele) => PostWriteMedia(ele)) ?? []); post.body['attachments']
poll = post.preload?.poll; .where(SnAttachment.fromJson)
?.map(PostWriteMedia) ??
[],
);
poll = post.poll;
editingDraft = post.isDraft; editingDraft = post.isDraft;
if (post.preload?.thumbnail != null && if (post.body['thumbnail'] != null) {
(post.preload?.thumbnail?.rid.isNotEmpty ?? false)) { thumbnail =
thumbnail = PostWriteMedia(post.preload!.thumbnail); PostWriteMedia(SnAttachment.fromJson(post.body['thumbnail']));
} }
if (post.preload?.realm != null) { if (post.realm != null) {
realm = post.preload!.realm!; realm = post.realm!;
} }
editingPost = post; editingPost = post;

View File

@ -15,6 +15,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:gap/gap.dart'; import 'package:gap/gap.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:hotkey_manager/hotkey_manager.dart'; import 'package:hotkey_manager/hotkey_manager.dart';
import 'package:package_info_plus/package_info_plus.dart'; import 'package:package_info_plus/package_info_plus.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
@ -49,6 +50,7 @@ import 'package:surface/router.dart';
import 'package:flutter_web_plugins/url_strategy.dart' show usePathUrlStrategy; import 'package:flutter_web_plugins/url_strategy.dart' show usePathUrlStrategy;
import 'package:surface/widgets/dialog.dart'; import 'package:surface/widgets/dialog.dart';
import 'package:surface/widgets/menu_bar.dart'; import 'package:surface/widgets/menu_bar.dart';
import 'package:surface/widgets/navigation/app_scaffold.dart';
import 'package:surface/widgets/version_label.dart'; import 'package:surface/widgets/version_label.dart';
import 'package:tray_manager/tray_manager.dart'; import 'package:tray_manager/tray_manager.dart';
import 'package:version/version.dart'; import 'package:version/version.dart';
@ -57,6 +59,7 @@ import 'package:in_app_review/in_app_review.dart';
import 'package:image_picker_android/image_picker_android.dart'; import 'package:image_picker_android/image_picker_android.dart';
import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; import 'package:image_picker_platform_interface/image_picker_platform_interface.dart';
import 'package:local_notifier/local_notifier.dart'; import 'package:local_notifier/local_notifier.dart';
import 'package:flutter_animate/flutter_animate.dart';
@pragma('vm:entry-point') @pragma('vm:entry-point')
void appBackgroundDispatcher() { void appBackgroundDispatcher() {
@ -257,6 +260,7 @@ class _AppSplashScreen extends StatefulWidget {
class _AppSplashScreenState extends State<_AppSplashScreen> with TrayListener { class _AppSplashScreenState extends State<_AppSplashScreen> with TrayListener {
bool _isBusy = false; bool _isBusy = false;
double _initPercentage = 0;
String _phaseText = 'appInitStarting'; String _phaseText = 'appInitStarting';
void _tryRequestRating() async { void _tryRequestRating() async {
@ -331,20 +335,24 @@ class _AppSplashScreenState extends State<_AppSplashScreen> with TrayListener {
// The Network initialization must be done after the HomeWidget initialization // The Network initialization must be done after the HomeWidget initialization
// The Network initialization will save the server url to the HomeWidget // The Network initialization will save the server url to the HomeWidget
// The Network initialization will also save initialize the Config, so it not need to be initialized again // The Network initialization will also save initialize the Config, so it not need to be initialized again
_initPercentage = 0.1;
_setPhaseText('network'); _setPhaseText('network');
final sn = context.read<SnNetworkProvider>(); final sn = context.read<SnNetworkProvider>();
await sn.initializeUserAgent(); await sn.initializeUserAgent();
await sn.setConfigWithNative(); await sn.setConfigWithNative();
if (!mounted) return; if (!mounted) return;
_initPercentage = 0.2;
_setPhaseText('userdata'); _setPhaseText('userdata');
final ua = context.read<UserProvider>(); final ua = context.read<UserProvider>();
await ua.initialize(); await ua.initialize();
if (!mounted) return; if (!mounted) return;
_initPercentage = 0.3;
_setPhaseText('websocket'); _setPhaseText('websocket');
final ws = context.read<WebSocketProvider>(); final ws = context.read<WebSocketProvider>();
await ws.tryConnect(); await ws.tryConnect();
try { try {
if (!mounted) return; if (!mounted) return;
_initPercentage = 0.9;
_setPhaseText('keyPair'); _setPhaseText('keyPair');
final kp = context.read<KeyPairProvider>(); final kp = context.read<KeyPairProvider>();
kp.reloadActive(); kp.reloadActive();
@ -357,7 +365,6 @@ class _AppSplashScreenState extends State<_AppSplashScreen> with TrayListener {
notify.listen(); notify.listen();
try { try {
notify.registerPushNotifications(); notify.registerPushNotifications();
} catch (_) {}
if (!mounted) return; if (!mounted) return;
_setPhaseText('stickers'); _setPhaseText('stickers');
final sticker = context.read<SnStickerProvider>(); final sticker = context.read<SnStickerProvider>();
@ -374,7 +381,9 @@ class _AppSplashScreenState extends State<_AppSplashScreen> with TrayListener {
_setPhaseText('chat'); _setPhaseText('chat');
final ct = context.read<ChatChannelProvider>(); final ct = context.read<ChatChannelProvider>();
await ct.refreshAvailableChannels(); await ct.refreshAvailableChannels();
_initPercentage = 1;
_setPhaseText('done'); _setPhaseText('done');
} catch (_) {}
_playIntro(); _playIntro();
} }
} catch (err) { } catch (err) {
@ -396,8 +405,22 @@ class _AppSplashScreenState extends State<_AppSplashScreen> with TrayListener {
final cfg = context.read<ConfigProvider>(); final cfg = context.read<ConfigProvider>();
if (!cfg.soundEffects) return; if (!cfg.soundEffects) return;
final date = DateTime.now();
final player = AudioPlayer(playerId: 'launch-done-player'); final player = AudioPlayer(playerId: 'launch-done-player');
await player.play(AssetSource('audio/sfx/launch-done.mp3'), volume: 0.8); await player.play(
(cfg.aprilFoolFeatures && date.month == 4 && date.day == 1)
? AssetSource('audio/sfx/launch-intro.mp3')
: AssetSource('audio/sfx/launch-done.mp3'),
volume: 0.8,
ctx: AudioContext(
android: AudioContextAndroid(
contentType: AndroidContentType.sonification,
usageType: AndroidUsageType.notificationEvent,
),
iOS: AudioContextIOS(category: AVAudioSessionCategory.ambient),
),
mode: PlayerMode.lowLatency,
);
player.onPlayerComplete.listen((_) { player.onPlayerComplete.listen((_) {
player.dispose(); player.dispose();
}); });
@ -456,6 +479,7 @@ class _AppSplashScreenState extends State<_AppSplashScreen> with TrayListener {
AppLifecycleListener(onExitRequested: _onExitRequested); AppLifecycleListener(onExitRequested: _onExitRequested);
} }
try {
_trayInitialization(); _trayInitialization();
_hotkeyInitialization(); _hotkeyInitialization();
_notifyInitialization(); _notifyInitialization();
@ -464,7 +488,13 @@ class _AppSplashScreenState extends State<_AppSplashScreen> with TrayListener {
_tryRequestRating(); _tryRequestRating();
_checkForUpdate(); _checkForUpdate();
setState(() => _isBusy = false); setState(() => _isBusy = false);
}).catchError((err) {
logging.error('[Bootstrap] Unable to initialize app', err);
setState(() => _isBusy = false);
}); });
} catch (err) {
logging.error('[Bootstrap] Unable to initialize (pre-stage) app', err);
}
} }
Future<AppExitResponse> _onExitRequested() async { Future<AppExitResponse> _onExitRequested() async {
@ -555,8 +585,205 @@ class _AppSplashScreenState extends State<_AppSplashScreen> with TrayListener {
}); });
return SizeChangedLayoutNotifier( return SizeChangedLayoutNotifier(
child: _isBusy child: _isBusy
? Material( ? _AppLoadingScreen(
key: Key('app-splash-screen-$_isBusy'), isBusy: _isBusy,
initPercentage: _initPercentage,
phaseText: _phaseText,
)
: widget.child,
);
},
),
),
);
}
}
class _AppLoadingScreen extends StatelessWidget {
const _AppLoadingScreen({
required this.isBusy,
required this.initPercentage,
required this.phaseText,
});
final bool isBusy;
final double initPercentage;
final String phaseText;
@override
Widget build(BuildContext context) {
if (ResponsiveScaffold.getIsExpand(context)) {
return Material(
key: Key('app-splash-screen-$isBusy'),
child: Stack(
children: [
Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/icon/kanban-1st.jpg'),
fit: BoxFit.cover,
opacity: 0.1,
),
color: Theme.of(context).colorScheme.surface,
backgroundBlendMode: BlendMode.darken,
),
),
Center(
child: Row(
children: [
Expanded(
child: TweenAnimationBuilder<double>(
tween: Tween(begin: 0, end: initPercentage),
duration: Duration(milliseconds: 300),
builder: (context, value, _) => Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('${(value * 100).toStringAsFixed(0)}%')
.padding(left: 32, bottom: 4),
LinearProgressIndicator(
value: value,
borderRadius: const BorderRadius.all(
Radius.circular(0),
),
stopIndicatorColor: Colors.transparent,
backgroundColor: Colors.transparent,
),
const Gap(24),
],
),
),
),
Expanded(
child: TweenAnimationBuilder<double>(
tween: Tween(begin: 0, end: initPercentage),
duration: Duration(milliseconds: 300),
builder: (context, value, _) => Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text('${(value * 100).toStringAsFixed(0)}%')
.padding(right: 32, bottom: 4),
Transform.flip(
flipX: true,
child: LinearProgressIndicator(
value: value,
borderRadius: const BorderRadius.all(
Radius.circular(0),
),
stopIndicatorColor: Colors.transparent,
backgroundColor: Colors.transparent,
),
),
const Gap(24),
],
),
),
),
],
),
),
Center(
child: Container(
constraints: const BoxConstraints(maxWidth: 240, minWidth: 160),
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 24),
decoration: BoxDecoration(
color:
Theme.of(context).colorScheme.surface.withOpacity(0.85),
border: Border.all(
color: Theme.of(context).dividerColor,
width: 3,
),
borderRadius: const BorderRadius.all(Radius.circular(12)),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
'splashScreenServer',
style: GoogleFonts.notoSerifHk(height: 1, fontSize: 11),
textAlign: TextAlign.center,
).tr().opacity(0.85),
Text(
'splashScreenServerName',
style: GoogleFonts.notoSerifHk(
fontSize: 24,
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center,
).tr().opacity(0.85),
Text.rich(
TextSpan(
text: '#',
style: GoogleFonts.notoSerifHk(),
children: [
TextSpan(
text: '0',
style: GoogleFonts.notoSerifHk(
fontSize: 80,
fontWeight: FontWeight.bold,
),
),
],
),
textAlign: TextAlign.center,
).padding(vertical: 16),
],
),
),
),
Positioned(
left: 0,
right: 0,
bottom: MediaQuery.of(context).size.height * 0.2,
child: Column(
children: [
Text(
phaseText,
textAlign: TextAlign.center,
),
AnimateWidgetExtensions(Text(
'splashScreenCaption',
textAlign: TextAlign.center,
).tr())
.animate(onPlay: (e) => e.repeat())
.fadeIn(duration: 500.ms, curve: Curves.easeOut)
.then()
.fadeOut(
duration: 500.ms,
delay: 1000.ms,
curve: Curves.easeIn,
),
],
),
),
Positioned(
bottom: 8,
left: 16,
right: 16,
child: Row(
children: [
Image.asset(
'assets/icon/icon.png',
width: 40,
height: 40,
color: Theme.of(context).colorScheme.onSurface,
).padding(all: 4),
const Gap(4),
Text('Solar Network').bold(),
Expanded(child: const SizedBox()),
AppVersionLabel(),
const Gap(12),
],
),
),
],
),
);
}
return Material(
key: Key('app-splash-screen-$isBusy'),
child: Stack( child: Stack(
children: [ children: [
Container( Container(
@ -580,27 +807,25 @@ class _AppSplashScreenState extends State<_AppSplashScreen> with TrayListener {
'assets/icon/icon.png', 'assets/icon/icon.png',
width: 64, width: 64,
height: 64, height: 64,
color: color: Theme.of(context).colorScheme.onSurface,
Theme.of(context).colorScheme.onSurface,
), ),
Text('Solar Network').bold(), Text('Solar Network').bold(),
AppVersionLabel(), AppVersionLabel(),
Gap(8), Gap(8),
Text(_phaseText, textAlign: TextAlign.center), Text(phaseText, textAlign: TextAlign.center),
Gap(16), Gap(16),
const LinearProgressIndicator(), TweenAnimationBuilder<double>(
tween: Tween(begin: 0, end: initPercentage),
duration: Duration(milliseconds: 300),
builder: (context, value, _) =>
LinearProgressIndicator(value: value),
),
], ],
), ),
), ),
), ),
], ],
), ),
)
: widget.child,
);
},
),
),
); );
} }
} }

View File

@ -106,6 +106,14 @@ class NotificationProvider extends ChangeNotifier {
_notifySoundPlayer.play( _notifySoundPlayer.play(
AssetSource('audio/notify/metal-pipe.mp3'), AssetSource('audio/notify/metal-pipe.mp3'),
volume: 0.6, volume: 0.6,
ctx: AudioContext(
android: AudioContextAndroid(
contentType: AndroidContentType.sonification,
usageType: AndroidUsageType.notificationEvent,
),
iOS: AudioContextIOS(category: AVAudioSessionCategory.ambient),
),
mode: PlayerMode.lowLatency,
); );
} }
} }

View File

@ -1,144 +1,31 @@
import 'package:dio/dio.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:surface/providers/sn_attachment.dart';
import 'package:surface/providers/sn_network.dart'; import 'package:surface/providers/sn_network.dart';
import 'package:surface/providers/sn_realm.dart';
import 'package:surface/providers/user_directory.dart';
import 'package:surface/types/poll.dart';
import 'package:surface/types/post.dart'; import 'package:surface/types/post.dart';
import 'package:surface/types/realm.dart';
class SnPostContentProvider { class SnPostContentProvider {
late final SnNetworkProvider _sn; late final SnNetworkProvider _sn;
late final UserDirectoryProvider _ud;
late final SnAttachmentProvider _attach;
late final SnRealmProvider _realm;
SnPostContentProvider(BuildContext context) { SnPostContentProvider(BuildContext context) {
_sn = context.read<SnNetworkProvider>(); _sn = context.read<SnNetworkProvider>();
_ud = context.read<UserDirectoryProvider>();
_attach = context.read<SnAttachmentProvider>();
_realm = context.read<SnRealmProvider>();
}
Future<SnPoll> _fetchPoll(int id) async {
final resp = await _sn.client.get('/cgi/co/polls/$id');
return SnPoll.fromJson(resp.data);
} }
Future<List<SnPost>> _preloadRelatedDataInBatch(List<SnPost> out) async { Future<List<SnPost>> _preloadRelatedDataInBatch(List<SnPost> out) async {
Set<String> rids = {};
Set<int> uids = {};
for (var i = 0; i < out.length; i++) {
rids.addAll(out[i].body['attachments']?.cast<String>() ?? []);
if (out[i].body['thumbnail'] != null) {
rids.add(out[i].body['thumbnail']);
}
if (out[i].body['video'] != null) {
rids.add(out[i].body['video']);
}
if (out[i].repostTo != null) {
out[i] = out[i].copyWith(
repostTo: await _preloadRelatedDataSingle(out[i].repostTo!),
);
}
if (out[i].publisher.type == 0) {
uids.add(out[i].publisher.accountId);
}
}
final attachments = await _attach.getMultiple(rids.toList());
for (var i = 0; i < out.length; i++) {
SnPoll? poll;
SnRealm? realm;
if (out[i].pollId != null) {
poll = await _fetchPoll(out[i].pollId!);
}
if (out[i].realmId != null) {
realm = await _realm.getRealm(out[i].realmId!);
}
out[i] = out[i].copyWith(
preload: SnPostPreload(
thumbnail: attachments
.where((ele) => ele?.rid == out[i].body['thumbnail'])
.firstOrNull,
attachments: attachments
.where((ele) =>
out[i].body['attachments']?.contains(ele?.rid) ?? false)
.toList(),
video: attachments
.where((ele) => ele?.rid == out[i].body['video'])
.firstOrNull,
poll: poll,
realm: realm,
),
);
}
uids.addAll(
attachments.where((ele) => ele != null).map((ele) => ele!.accountId));
await _ud.listAccount(uids);
return out; return out;
} }
Future<SnPost> _preloadRelatedDataSingle(SnPost out) async { Future<SnPost> _preloadRelatedDataSingle(SnPost out) async {
Set<String> rids = {};
Set<int> uids = {};
rids.addAll(out.body['attachments']?.cast<String>() ?? []);
if (out.body['thumbnail'] != null) {
rids.add(out.body['thumbnail']);
}
if (out.body['video'] != null) {
rids.add(out.body['video']);
}
if (out.repostTo != null) {
out = out.copyWith(
repostTo: await _preloadRelatedDataSingle(out.repostTo!),
);
}
if (out.publisher.type == 0) {
uids.add(out.publisher.accountId);
}
final attachments = await _attach.getMultiple(rids.toList());
SnPoll? poll;
SnRealm? realm;
if (out.pollId != null) {
poll = await _fetchPoll(out.pollId!);
}
if (out.realmId != null) {
realm = await _realm.getRealm(out.realmId!);
}
out = out.copyWith(
preload: SnPostPreload(
thumbnail: attachments
.where((ele) => ele?.rid == out.body['thumbnail'])
.firstOrNull,
attachments: attachments
.where(
(ele) => out.body['attachments']?.contains(ele?.rid) ?? false)
.toList(),
video: attachments
.where((ele) => ele?.rid == out.body['video'])
.firstOrNull,
poll: poll,
realm: realm,
),
);
uids.addAll(
attachments.where((ele) => ele != null).map((ele) => ele!.accountId));
await _ud.listAccount(uids);
return out; return out;
} }
Future<List<SnPost>> listRecommendations() async { Future<List<SnPost>> listRecommendations() async {
final resp = await _sn.client.get('/cgi/co/recommendations'); final resp = await _sn.client.get(
'/cgi/co/recommendations',
options: Options(headers: {
'X-API-Version': '2',
}),
);
final out = _preloadRelatedDataInBatch( final out = _preloadRelatedDataInBatch(
List.from(resp.data.map((ele) => SnPost.fromJson(ele))), List.from(resp.data.map((ele) => SnPost.fromJson(ele))),
); );
@ -202,6 +89,9 @@ class SnPostContentProvider {
if (realm != null) 'realm': realm, if (realm != null) 'realm': realm,
if (channel != null) 'channel': channel, if (channel != null) 'channel': channel,
}, },
options: Options(headers: {
'X-API-Version': '2',
}),
); );
final List<SnPost> out = await _preloadRelatedDataInBatch( final List<SnPost> out = await _preloadRelatedDataInBatch(
List.from(resp.data['data']?.map((e) => SnPost.fromJson(e)) ?? []), List.from(resp.data['data']?.map((e) => SnPost.fromJson(e)) ?? []),
@ -215,11 +105,16 @@ class SnPostContentProvider {
int take = 10, int take = 10,
int offset = 0, int offset = 0,
}) async { }) async {
final resp = await _sn.client final resp = await _sn.client.get(
.get('/cgi/co/posts/$parentId/replies', queryParameters: { '/cgi/co/posts/$parentId/replies',
queryParameters: {
'take': take, 'take': take,
'offset': offset, 'offset': offset,
}); },
options: Options(headers: {
'X-API-Version': '2',
}),
);
final List<SnPost> out = await _preloadRelatedDataInBatch( final List<SnPost> out = await _preloadRelatedDataInBatch(
List.from(resp.data['data']?.map((e) => SnPost.fromJson(e)) ?? []), List.from(resp.data['data']?.map((e) => SnPost.fromJson(e)) ?? []),
); );
@ -234,13 +129,20 @@ class SnPostContentProvider {
Iterable<String>? tags, Iterable<String>? tags,
Iterable<String>? categories, Iterable<String>? categories,
}) async { }) async {
final resp = await _sn.client.get('/cgi/co/posts/search', queryParameters: { final resp = await _sn.client.get(
'/cgi/co/posts/search',
queryParameters: {
'take': take, 'take': take,
'offset': offset, 'offset': offset,
'probe': searchTerm, 'probe': searchTerm,
if (tags?.isNotEmpty ?? false) 'tags': tags!.join(','), if (tags?.isNotEmpty ?? false) 'tags': tags!.join(','),
if (categories?.isNotEmpty ?? false) 'categories': categories!.join(','), if (categories?.isNotEmpty ?? false)
}); 'categories': categories!.join(','),
},
options: Options(headers: {
'X-API-Version': '2',
}),
);
final List<SnPost> out = await _preloadRelatedDataInBatch( final List<SnPost> out = await _preloadRelatedDataInBatch(
List.from(resp.data['data']?.map((e) => SnPost.fromJson(e)) ?? []), List.from(resp.data['data']?.map((e) => SnPost.fromJson(e)) ?? []),
); );
@ -249,7 +151,12 @@ class SnPostContentProvider {
} }
Future<SnPost> getPost(dynamic id) async { Future<SnPost> getPost(dynamic id) async {
final resp = await _sn.client.get('/cgi/co/posts/$id'); final resp = await _sn.client.get(
'/cgi/co/posts/$id',
options: Options(headers: {
'X-API-Version': '2',
}),
);
final out = _preloadRelatedDataSingle( final out = _preloadRelatedDataSingle(
SnPost.fromJson(resp.data), SnPost.fromJson(resp.data),
); );

View File

@ -1,4 +1,3 @@
import 'package:animations/animations.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:surface/screens/abuse_report.dart'; import 'package:surface/screens/abuse_report.dart';
@ -54,16 +53,6 @@ import 'package:surface/types/post.dart';
import 'package:surface/widgets/about.dart'; import 'package:surface/widgets/about.dart';
import 'package:surface/widgets/navigation/app_scaffold.dart'; import 'package:surface/widgets/navigation/app_scaffold.dart';
Widget _fadeThroughTransition(BuildContext context, Animation<double> animation,
Animation<double> secondaryAnimation, Widget child) {
return FadeThroughTransition(
animation: animation,
secondaryAnimation: secondaryAnimation,
fillColor: Colors.transparent,
child: child,
);
}
final _appRoutes = [ final _appRoutes = [
GoRoute( GoRoute(
path: '/', path: '/',
@ -305,10 +294,7 @@ final _appRoutes = [
GoRoute( GoRoute(
path: '/realm', path: '/realm',
name: 'realm', name: 'realm',
pageBuilder: (context, state) => CustomTransitionPage( builder: (context, state) => const RealmScreen(),
transitionsBuilder: _fadeThroughTransition,
child: const RealmScreen(),
),
routes: [ routes: [
GoRoute( GoRoute(
path: '/:alias/community', path: '/:alias/community',

View File

@ -110,7 +110,7 @@ class AccountScreen extends StatelessWidget {
final sn = context.read<SnNetworkProvider>(); final sn = context.read<SnNetworkProvider>();
return AppScaffold( return AppScaffold(
noBackground: true, noBackground: ResponsiveScaffold.getIsExpand(context),
appBar: AppBar( appBar: AppBar(
leading: AutoAppBarLeading(), leading: AutoAppBarLeading(),
title: Text("screenAccount").tr(), title: Text("screenAccount").tr(),

View File

@ -59,7 +59,7 @@ class _ActionEventScreenState extends State<ActionEventScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return AppScaffold( return AppScaffold(
noBackground: true, noBackground: ResponsiveScaffold.getIsExpand(context),
appBar: AppBar( appBar: AppBar(
leading: const PageBackButton(), leading: const PageBackButton(),
title: Text('accountActionEvent').tr(), title: Text('accountActionEvent').tr(),

View File

@ -91,7 +91,7 @@ class _AccountAuthTicketState extends State<AccountAuthTicket> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return AppScaffold( return AppScaffold(
noBackground: true, noBackground: ResponsiveScaffold.getIsExpand(context),
appBar: AppBar( appBar: AppBar(
leading: const PageBackButton(), leading: const PageBackButton(),
title: Text('accountAuthTickets').tr(), title: Text('accountAuthTickets').tr(),

View File

@ -70,7 +70,7 @@ class _AccountBadgesScreenState extends State<AccountBadgesScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return AppScaffold( return AppScaffold(
noBackground: true, noBackground: ResponsiveScaffold.getIsExpand(context),
appBar: AppBar( appBar: AppBar(
title: Text('screenAccountBadges').tr(), title: Text('screenAccountBadges').tr(),
), ),

View File

@ -69,7 +69,7 @@ class _AccountContactMethodState extends State<AccountContactMethod> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return AppScaffold( return AppScaffold(
noBackground: true, noBackground: ResponsiveScaffold.getIsExpand(context),
appBar: AppBar( appBar: AppBar(
leading: const PageBackButton(), leading: const PageBackButton(),
title: Text('accountContactMethods').tr(), title: Text('accountContactMethods').tr(),

View File

@ -62,7 +62,7 @@ class _FactorSettingsScreenState extends State<FactorSettingsScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return AppScaffold( return AppScaffold(
noBackground: true, noBackground: ResponsiveScaffold.getIsExpand(context),
appBar: AppBar( appBar: AppBar(
leading: PageBackButton(), leading: PageBackButton(),
title: Text('screenFactorSettings').tr(), title: Text('screenFactorSettings').tr(),

View File

@ -37,7 +37,7 @@ class _KeyPairScreenState extends State<KeyPairScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return AppScaffold( return AppScaffold(
noBackground: true, noBackground: ResponsiveScaffold.getIsExpand(context),
appBar: AppBar( appBar: AppBar(
title: Text('screenKeyPairs').tr(), title: Text('screenKeyPairs').tr(),
), ),

View File

@ -75,7 +75,7 @@ class _AccountNotifyPrefsScreenState extends State<AccountNotifyPrefsScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return AppScaffold( return AppScaffold(
noBackground: true, noBackground: ResponsiveScaffold.getIsExpand(context),
appBar: AppBar( appBar: AppBar(
leading: const PageBackButton(), leading: const PageBackButton(),
title: Text('accountSettingsNotify').tr(), title: Text('accountSettingsNotify').tr(),

View File

@ -70,7 +70,7 @@ class _AccountSecurityPrefsScreenState
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return AppScaffold( return AppScaffold(
noBackground: true, noBackground: ResponsiveScaffold.getIsExpand(context),
appBar: AppBar( appBar: AppBar(
leading: const PageBackButton(), leading: const PageBackButton(),
title: Text('accountSettingsSecurity').tr(), title: Text('accountSettingsSecurity').tr(),

View File

@ -244,7 +244,7 @@ class _ProfileEditScreenState extends State<ProfileEditScreen> {
final sn = context.read<SnNetworkProvider>(); final sn = context.read<SnNetworkProvider>();
return AppScaffold( return AppScaffold(
noBackground: true, noBackground: ResponsiveScaffold.getIsExpand(context),
appBar: AppBar( appBar: AppBar(
leading: const PageBackButton(), leading: const PageBackButton(),
title: Text('screenAccountProfileEdit').tr()), title: Text('screenAccountProfileEdit').tr()),
@ -263,7 +263,7 @@ class _ProfileEditScreenState extends State<ProfileEditScreen> {
child: ClipRRect( child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(8)), borderRadius: const BorderRadius.all(Radius.circular(8)),
child: AspectRatio( child: AspectRatio(
aspectRatio: 16 / 9, aspectRatio: 16 / 7,
child: Container( child: Container(
color: Theme.of(context) color: Theme.of(context)
.colorScheme .colorScheme

View File

@ -61,6 +61,21 @@ final Map<String, (String, IconData, Color)> kBadgesMeta = {
Symbols.thumb_up, Symbols.thumb_up,
Colors.lightGreen, Colors.lightGreen,
), ),
'programs.developers': (
'badgeProgramDeveloper',
Symbols.code,
Colors.blue,
),
'programs.stellar': (
'badgeProgramStellar',
Symbols.family_star,
Colors.orange,
),
'programs.moderator': (
'badgeProgramModerator',
Symbols.sword_rose,
Colors.blue,
),
}; };
class UserScreen extends StatefulWidget { class UserScreen extends StatefulWidget {

View File

@ -70,7 +70,7 @@ class _AccountProgramScreenState extends State<AccountProgramScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return AppScaffold( return AppScaffold(
noBackground: true, noBackground: ResponsiveScaffold.getIsExpand(context),
appBar: AppBar( appBar: AppBar(
title: Text('accountProgram').tr(), title: Text('accountProgram').tr(),
), ),

View File

@ -196,7 +196,7 @@ class _AccountPublisherEditScreenState
final sn = context.read<SnNetworkProvider>(); final sn = context.read<SnNetworkProvider>();
return AppScaffold( return AppScaffold(
noBackground: true, noBackground: ResponsiveScaffold.getIsExpand(context),
appBar: AppBar( appBar: AppBar(
leading: PageBackButton(), leading: PageBackButton(),
title: Text('screenAccountPublisherEdit').tr()), title: Text('screenAccountPublisherEdit').tr()),
@ -214,7 +214,7 @@ class _AccountPublisherEditScreenState
child: ClipRRect( child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(8)), borderRadius: const BorderRadius.all(Radius.circular(8)),
child: AspectRatio( child: AspectRatio(
aspectRatio: 16 / 9, aspectRatio: 16 / 7,
child: Container( child: Container(
color: Theme.of(context) color: Theme.of(context)
.colorScheme .colorScheme

View File

@ -26,7 +26,7 @@ class _AccountPublisherNewScreenState extends State<AccountPublisherNewScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return AppScaffold( return AppScaffold(
noBackground: true, noBackground: ResponsiveScaffold.getIsExpand(context),
appBar: AppBar( appBar: AppBar(
leading: const PageBackButton(), leading: const PageBackButton(),
title: Text('screenAccountPublisherNew').tr(), title: Text('screenAccountPublisherNew').tr(),

View File

@ -82,7 +82,7 @@ class _PublisherScreenState extends State<PublisherScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return AppScaffold( return AppScaffold(
noBackground: true, noBackground: ResponsiveScaffold.getIsExpand(context),
appBar: AppBar( appBar: AppBar(
leading: const PageBackButton(), leading: const PageBackButton(),
title: Text('screenAccountPublishers').tr(), title: Text('screenAccountPublishers').tr(),

View File

@ -55,7 +55,7 @@ class _PunishmentsScreenState extends State<PunishmentsScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return AppScaffold( return AppScaffold(
noBackground: true, noBackground: ResponsiveScaffold.getIsExpand(context),
appBar: AppBar( appBar: AppBar(
title: Text('accountPunishments').tr(), title: Text('accountPunishments').tr(),
leading: PageBackButton(), leading: PageBackButton(),

View File

@ -37,7 +37,7 @@ class AccountSettingsScreen extends StatelessWidget {
final ua = context.watch<UserProvider>(); final ua = context.watch<UserProvider>();
return AppScaffold( return AppScaffold(
noBackground: true, noBackground: ResponsiveScaffold.getIsExpand(context),
appBar: AppBar( appBar: AppBar(
leading: PageBackButton(), leading: PageBackButton(),
title: Text('screenAccountSettings').tr(), title: Text('screenAccountSettings').tr(),

View File

@ -8,7 +8,6 @@ import 'package:material_symbols_icons/symbols.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
import 'package:surface/providers/sn_network.dart'; import 'package:surface/providers/sn_network.dart';
import 'package:surface/providers/user_directory.dart';
import 'package:surface/types/attachment.dart'; import 'package:surface/types/attachment.dart';
import 'package:surface/widgets/attachment/attachment_zoom.dart'; import 'package:surface/widgets/attachment/attachment_zoom.dart';
import 'package:surface/widgets/attachment/attachment_item.dart'; import 'package:surface/widgets/attachment/attachment_item.dart';
@ -53,7 +52,6 @@ class _AlbumScreenState extends State<AlbumScreen> {
try { try {
final sn = context.read<SnNetworkProvider>(); final sn = context.read<SnNetworkProvider>();
final ud = context.read<UserDirectoryProvider>();
final resp = await sn.client.get('/cgi/uc/attachments', queryParameters: { final resp = await sn.client.get('/cgi/uc/attachments', queryParameters: {
'take': 10, 'take': 10,
'offset': _attachments.length, 'offset': _attachments.length,
@ -64,8 +62,6 @@ class _AlbumScreenState extends State<AlbumScreen> {
_attachments.addAll(attachments); _attachments.addAll(attachments);
_heroTags.addAll(_attachments.map((_) => uuid.v4())); _heroTags.addAll(_attachments.map((_) => uuid.v4()));
await ud.listAccount(attachments.map((e) => e.accountId).toSet());
_totalCount = resp.data['count'] as int?; _totalCount = resp.data['count'] as int?;
} catch (err) { } catch (err) {
if (!mounted) return; if (!mounted) return;

View File

@ -223,7 +223,7 @@ class _ChatScreenState extends State<ChatScreen> {
} }
return AppScaffold( return AppScaffold(
noBackground: true, noBackground: ResponsiveScaffold.getIsExpand(context),
appBar: AppBar( appBar: AppBar(
leading: AutoAppBarLeading(), leading: AutoAppBarLeading(),
title: Text('screenChat').tr(), title: Text('screenChat').tr(),
@ -389,7 +389,7 @@ class _ChatScreenState extends State<ChatScreen> {
children: [ children: [
if (_focusedRealm!.banner != null) if (_focusedRealm!.banner != null)
AspectRatio( AspectRatio(
aspectRatio: 16 / 9, aspectRatio: 16 / 7,
child: AutoResizeUniversalImage( child: AutoResizeUniversalImage(
sn.getAttachmentUrl( sn.getAttachmentUrl(
_focusedRealm!.banner!, _focusedRealm!.banner!,

View File

@ -124,7 +124,7 @@ class _CallRoomScreenState extends State<CallRoomScreen> {
listenable: call, listenable: call,
builder: (context, _) { builder: (context, _) {
return AppScaffold( return AppScaffold(
noBackground: true, noBackground: ResponsiveScaffold.getIsExpand(context),
appBar: AppBar( appBar: AppBar(
title: RichText( title: RichText(
textAlign: TextAlign.center, textAlign: TextAlign.center,

View File

@ -220,7 +220,7 @@ class _ChannelDetailScreenState extends State<ChannelDetailScreen> {
final isOwned = ua.isAuthorized && _channel?.accountId == ua.user?.id; final isOwned = ua.isAuthorized && _channel?.accountId == ua.user?.id;
return AppScaffold( return AppScaffold(
noBackground: true, noBackground: ResponsiveScaffold.getIsExpand(context),
appBar: AppBar( appBar: AppBar(
title: _channel != null ? Text(_channel!.name) : Text('loading').tr(), title: _channel != null ? Text(_channel!.name) : Text('loading').tr(),
), ),

View File

@ -141,7 +141,7 @@ class _ChatManageScreenState extends State<ChatManageScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return AppScaffold( return AppScaffold(
noBackground: true, noBackground: ResponsiveScaffold.getIsExpand(context),
appBar: AppBar( appBar: AppBar(
title: widget.editingChannelAlias != null title: widget.editingChannelAlias != null
? Text('screenChatManage').tr() ? Text('screenChatManage').tr()

View File

@ -304,7 +304,7 @@ class _ChatRoomScreenState extends State<ChatRoomScreen> {
final ud = context.read<UserDirectoryProvider>(); final ud = context.read<UserDirectoryProvider>();
return AppScaffold( return AppScaffold(
noBackground: true, noBackground: ResponsiveScaffold.getIsExpand(context),
appBar: AppBar( appBar: AppBar(
title: Text( title: Text(
_channel?.type == 1 _channel?.type == 1

View File

@ -157,7 +157,7 @@ class _ExploreScreenState extends State<ExploreScreen>
Widget build(BuildContext context) { Widget build(BuildContext context) {
final cfg = context.watch<ConfigProvider>(); final cfg = context.watch<ConfigProvider>();
return AppScaffold( return AppScaffold(
noBackground: true, noBackground: ResponsiveScaffold.getIsExpand(context),
floatingActionButtonLocation: ExpandableFab.location, floatingActionButtonLocation: ExpandableFab.location,
floatingActionButton: ExpandableFab( floatingActionButton: ExpandableFab(
key: _fabKey, key: _fabKey,

View File

@ -11,13 +11,11 @@ import 'package:styled_widget/styled_widget.dart';
import 'package:surface/providers/notification.dart'; import 'package:surface/providers/notification.dart';
import 'package:surface/providers/sn_network.dart'; import 'package:surface/providers/sn_network.dart';
import 'package:surface/types/notification.dart'; import 'package:surface/types/notification.dart';
import 'package:surface/types/post.dart';
import 'package:surface/widgets/app_bar_leading.dart'; import 'package:surface/widgets/app_bar_leading.dart';
import 'package:surface/widgets/dialog.dart'; import 'package:surface/widgets/dialog.dart';
import 'package:surface/widgets/loading_indicator.dart'; import 'package:surface/widgets/loading_indicator.dart';
import 'package:surface/widgets/markdown_content.dart'; import 'package:surface/widgets/markdown_content.dart';
import 'package:surface/widgets/navigation/app_scaffold.dart'; import 'package:surface/widgets/navigation/app_scaffold.dart';
import 'package:surface/widgets/post/post_item.dart';
import 'package:very_good_infinite_list/very_good_infinite_list.dart'; import 'package:very_good_infinite_list/very_good_infinite_list.dart';
import '../providers/userinfo.dart'; import '../providers/userinfo.dart';
@ -219,34 +217,24 @@ class _NotificationScreenState extends State<NotificationScreen> {
'interactive.subscription', 'interactive.subscription',
].contains(nty.topic) && ].contains(nty.topic) &&
nty.metadata['related_post'] != null) nty.metadata['related_post'] != null)
GestureDetector( TextButton(
child: Container( style: ButtonStyle(
decoration: BoxDecoration( padding: WidgetStatePropertyAll(
borderRadius: const BorderRadius.all( EdgeInsets.zero,
Radius.circular(8)),
border: Border.all(
color: Theme.of(context).dividerColor,
width: 1),
), ),
child: PostItem( visualDensity: VisualDensity.compact,
data: SnPost.fromJson(
nty.metadata['related_post']!),
showComments: false,
showReactions: false,
showMenu: false,
).padding(vertical: 4),
), ),
onTap: () { child: Text('postReadMore').tr(),
onPressed: () {
GoRouter.of(context).pushNamed( GoRouter.of(context).pushNamed(
'postDetail', 'postDetail',
pathParameters: { pathParameters: {
'slug': nty 'slug': nty.metadata['related_post']['id']
.metadata['related_post']!['id'] .toString(),
.toString()
}, },
); );
}, },
).padding(top: 8), ),
const Gap(8), const Gap(8),
Row( Row(
children: [ children: [

View File

@ -66,7 +66,7 @@ class _PostDetailScreenState extends State<PostDetailScreen> {
final double maxWidth = _data?.type == 'video' ? double.infinity : 640; final double maxWidth = _data?.type == 'video' ? double.infinity : 640;
return AppScaffold( return AppScaffold(
noBackground: true, noBackground: ResponsiveScaffold.getIsExpand(context),
appBar: AppBar( appBar: AppBar(
leading: BackButton( leading: BackButton(
onPressed: () { onPressed: () {

View File

@ -79,6 +79,7 @@ class _PostShuffleScreenState extends State<PostShuffleScreen> {
key: ValueKey(ele), key: ValueKey(ele),
data: ele, data: ele,
maxWidth: 640, maxWidth: 640,
useReplace: true,
onChanged: (ele) { onChanged: (ele) {
_posts[idx] = ele; _posts[idx] = ele;
setState(() {}); setState(() {});

View File

@ -286,7 +286,7 @@ class _PostPublisherScreenState extends State<PostPublisherScreen>
final sn = context.read<SnNetworkProvider>(); final sn = context.read<SnNetworkProvider>();
return AppScaffold( return AppScaffold(
noBackground: true, noBackground: ResponsiveScaffold.getIsExpand(context),
body: NestedScrollView( body: NestedScrollView(
controller: _scrollController, controller: _scrollController,
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) { headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {

View File

@ -45,7 +45,7 @@ class _WalletScreenState extends State<WalletScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return AppScaffold( return AppScaffold(
noBackground: true, noBackground: ResponsiveScaffold.getIsExpand(context),
appBar: AppBar( appBar: AppBar(
leading: PageBackButton(), title: Text('screenAccountWallet').tr()), leading: PageBackButton(), title: Text('screenAccountWallet').tr()),
body: Column( body: Column(

View File

@ -1,4 +1,5 @@
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:surface/types/account.dart';
part 'attachment.freezed.dart'; part 'attachment.freezed.dart';
@ -39,6 +40,7 @@ abstract class SnAttachment with _$SnAttachment {
required int? refId, required int? refId,
required SnAttachmentPool? pool, required SnAttachmentPool? pool,
required int? poolId, required int? poolId,
required SnAccount? account,
required int accountId, required int accountId,
int? thumbnailId, int? thumbnailId,
SnAttachment? thumbnail, SnAttachment? thumbnail,
@ -49,7 +51,8 @@ abstract class SnAttachment with _$SnAttachment {
@Default({}) Map<String, dynamic> metadata, @Default({}) Map<String, dynamic> metadata,
}) = _SnAttachment; }) = _SnAttachment;
factory SnAttachment.fromJson(Map<String, Object?> json) => _$SnAttachmentFromJson(json); factory SnAttachment.fromJson(Map<String, Object?> json) =>
_$SnAttachmentFromJson(json);
Map<String, dynamic> get data => { Map<String, dynamic> get data => {
...metadata, ...metadata,
@ -85,7 +88,8 @@ abstract class SnAttachmentFragment with _$SnAttachmentFragment {
@Default([]) List<String> fileChunksMissing, @Default([]) List<String> fileChunksMissing,
}) = _SnAttachmentFragment; }) = _SnAttachmentFragment;
factory SnAttachmentFragment.fromJson(Map<String, Object?> json) => _$SnAttachmentFragmentFromJson(json); factory SnAttachmentFragment.fromJson(Map<String, Object?> json) =>
_$SnAttachmentFragmentFromJson(json);
SnMediaType get mediaType => switch (mimetype.split('/').firstOrNull) { SnMediaType get mediaType => switch (mimetype.split('/').firstOrNull) {
'image' => SnMediaType.image, 'image' => SnMediaType.image,
@ -109,7 +113,8 @@ abstract class SnAttachmentPool with _$SnAttachmentPool {
required int? accountId, required int? accountId,
}) = _SnAttachmentPool; }) = _SnAttachmentPool;
factory SnAttachmentPool.fromJson(Map<String, Object?> json) => _$SnAttachmentPoolFromJson(json); factory SnAttachmentPool.fromJson(Map<String, Object?> json) =>
_$SnAttachmentPoolFromJson(json);
} }
@freezed @freezed
@ -122,7 +127,8 @@ abstract class SnAttachmentDestination with _$SnAttachmentDestination {
required bool isBoost, required bool isBoost,
}) = _SnAttachmentDestination; }) = _SnAttachmentDestination;
factory SnAttachmentDestination.fromJson(Map<String, Object?> json) => _$SnAttachmentDestinationFromJson(json); factory SnAttachmentDestination.fromJson(Map<String, Object?> json) =>
_$SnAttachmentDestinationFromJson(json);
} }
@freezed @freezed
@ -139,7 +145,8 @@ abstract class SnAttachmentBoost with _$SnAttachmentBoost {
required int account, required int account,
}) = _SnAttachmentBoost; }) = _SnAttachmentBoost;
factory SnAttachmentBoost.fromJson(Map<String, Object?> json) => _$SnAttachmentBoostFromJson(json); factory SnAttachmentBoost.fromJson(Map<String, Object?> json) =>
_$SnAttachmentBoostFromJson(json);
} }
@freezed @freezed
@ -158,7 +165,8 @@ abstract class SnSticker with _$SnSticker {
required int accountId, required int accountId,
}) = _SnSticker; }) = _SnSticker;
factory SnSticker.fromJson(Map<String, Object?> json) => _$SnStickerFromJson(json); factory SnSticker.fromJson(Map<String, Object?> json) =>
_$SnStickerFromJson(json);
} }
@freezed @freezed
@ -175,7 +183,8 @@ abstract class SnStickerPack with _$SnStickerPack {
required int accountId, required int accountId,
}) = _SnStickerPack; }) = _SnStickerPack;
factory SnStickerPack.fromJson(Map<String, Object?> json) => _$SnStickerPackFromJson(json); factory SnStickerPack.fromJson(Map<String, Object?> json) =>
_$SnStickerPackFromJson(json);
} }
@freezed @freezed
@ -186,5 +195,6 @@ abstract class SnAttachmentBilling with _$SnAttachmentBilling {
required double includedRatio, required double includedRatio,
}) = _SnAttachmentBilling; }) = _SnAttachmentBilling;
factory SnAttachmentBilling.fromJson(Map<String, Object?> json) => _$SnAttachmentBillingFromJson(json); factory SnAttachmentBilling.fromJson(Map<String, Object?> json) =>
_$SnAttachmentBillingFromJson(json);
} }

View File

@ -38,6 +38,7 @@ mixin _$SnAttachment {
int? get refId; int? get refId;
SnAttachmentPool? get pool; SnAttachmentPool? get pool;
int? get poolId; int? get poolId;
SnAccount? get account;
int get accountId; int get accountId;
int? get thumbnailId; int? get thumbnailId;
SnAttachment? get thumbnail; SnAttachment? get thumbnail;
@ -98,6 +99,7 @@ mixin _$SnAttachment {
(identical(other.refId, refId) || other.refId == refId) && (identical(other.refId, refId) || other.refId == refId) &&
(identical(other.pool, pool) || other.pool == pool) && (identical(other.pool, pool) || other.pool == pool) &&
(identical(other.poolId, poolId) || other.poolId == poolId) && (identical(other.poolId, poolId) || other.poolId == poolId) &&
(identical(other.account, account) || other.account == account) &&
(identical(other.accountId, accountId) || (identical(other.accountId, accountId) ||
other.accountId == accountId) && other.accountId == accountId) &&
(identical(other.thumbnailId, thumbnailId) || (identical(other.thumbnailId, thumbnailId) ||
@ -140,6 +142,7 @@ mixin _$SnAttachment {
refId, refId,
pool, pool,
poolId, poolId,
account,
accountId, accountId,
thumbnailId, thumbnailId,
thumbnail, thumbnail,
@ -152,7 +155,7 @@ mixin _$SnAttachment {
@override @override
String toString() { String toString() {
return 'SnAttachment(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, rid: $rid, uuid: $uuid, size: $size, name: $name, alt: $alt, mimetype: $mimetype, hash: $hash, destination: $destination, refCount: $refCount, contentRating: $contentRating, qualityRating: $qualityRating, cleanedAt: $cleanedAt, isAnalyzed: $isAnalyzed, isSelfRef: $isSelfRef, isIndexable: $isIndexable, ref: $ref, refId: $refId, pool: $pool, poolId: $poolId, accountId: $accountId, thumbnailId: $thumbnailId, thumbnail: $thumbnail, compressedId: $compressedId, compressed: $compressed, boosts: $boosts, usermeta: $usermeta, metadata: $metadata)'; return 'SnAttachment(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, rid: $rid, uuid: $uuid, size: $size, name: $name, alt: $alt, mimetype: $mimetype, hash: $hash, destination: $destination, refCount: $refCount, contentRating: $contentRating, qualityRating: $qualityRating, cleanedAt: $cleanedAt, isAnalyzed: $isAnalyzed, isSelfRef: $isSelfRef, isIndexable: $isIndexable, ref: $ref, refId: $refId, pool: $pool, poolId: $poolId, account: $account, accountId: $accountId, thumbnailId: $thumbnailId, thumbnail: $thumbnail, compressedId: $compressedId, compressed: $compressed, boosts: $boosts, usermeta: $usermeta, metadata: $metadata)';
} }
} }
@ -186,6 +189,7 @@ abstract mixin class $SnAttachmentCopyWith<$Res> {
int? refId, int? refId,
SnAttachmentPool? pool, SnAttachmentPool? pool,
int? poolId, int? poolId,
SnAccount? account,
int accountId, int accountId,
int? thumbnailId, int? thumbnailId,
SnAttachment? thumbnail, SnAttachment? thumbnail,
@ -197,6 +201,7 @@ abstract mixin class $SnAttachmentCopyWith<$Res> {
$SnAttachmentCopyWith<$Res>? get ref; $SnAttachmentCopyWith<$Res>? get ref;
$SnAttachmentPoolCopyWith<$Res>? get pool; $SnAttachmentPoolCopyWith<$Res>? get pool;
$SnAccountCopyWith<$Res>? get account;
$SnAttachmentCopyWith<$Res>? get thumbnail; $SnAttachmentCopyWith<$Res>? get thumbnail;
$SnAttachmentCopyWith<$Res>? get compressed; $SnAttachmentCopyWith<$Res>? get compressed;
} }
@ -236,6 +241,7 @@ class _$SnAttachmentCopyWithImpl<$Res> implements $SnAttachmentCopyWith<$Res> {
Object? refId = freezed, Object? refId = freezed,
Object? pool = freezed, Object? pool = freezed,
Object? poolId = freezed, Object? poolId = freezed,
Object? account = freezed,
Object? accountId = null, Object? accountId = null,
Object? thumbnailId = freezed, Object? thumbnailId = freezed,
Object? thumbnail = freezed, Object? thumbnail = freezed,
@ -338,6 +344,10 @@ class _$SnAttachmentCopyWithImpl<$Res> implements $SnAttachmentCopyWith<$Res> {
? _self.poolId ? _self.poolId
: poolId // ignore: cast_nullable_to_non_nullable : poolId // ignore: cast_nullable_to_non_nullable
as int?, as int?,
account: freezed == account
? _self.account
: account // ignore: cast_nullable_to_non_nullable
as SnAccount?,
accountId: null == accountId accountId: null == accountId
? _self.accountId ? _self.accountId
: accountId // ignore: cast_nullable_to_non_nullable : accountId // ignore: cast_nullable_to_non_nullable
@ -401,6 +411,20 @@ class _$SnAttachmentCopyWithImpl<$Res> implements $SnAttachmentCopyWith<$Res> {
}); });
} }
/// Create a copy of SnAttachment
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$SnAccountCopyWith<$Res>? get account {
if (_self.account == null) {
return null;
}
return $SnAccountCopyWith<$Res>(_self.account!, (value) {
return _then(_self.copyWith(account: value));
});
}
/// Create a copy of SnAttachment /// Create a copy of SnAttachment
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@override @override
@ -457,6 +481,7 @@ class _SnAttachment extends SnAttachment {
required this.refId, required this.refId,
required this.pool, required this.pool,
required this.poolId, required this.poolId,
required this.account,
required this.accountId, required this.accountId,
this.thumbnailId, this.thumbnailId,
this.thumbnail, this.thumbnail,
@ -521,6 +546,8 @@ class _SnAttachment extends SnAttachment {
@override @override
final int? poolId; final int? poolId;
@override @override
final SnAccount? account;
@override
final int accountId; final int accountId;
@override @override
final int? thumbnailId; final int? thumbnailId;
@ -612,6 +639,7 @@ class _SnAttachment extends SnAttachment {
(identical(other.refId, refId) || other.refId == refId) && (identical(other.refId, refId) || other.refId == refId) &&
(identical(other.pool, pool) || other.pool == pool) && (identical(other.pool, pool) || other.pool == pool) &&
(identical(other.poolId, poolId) || other.poolId == poolId) && (identical(other.poolId, poolId) || other.poolId == poolId) &&
(identical(other.account, account) || other.account == account) &&
(identical(other.accountId, accountId) || (identical(other.accountId, accountId) ||
other.accountId == accountId) && other.accountId == accountId) &&
(identical(other.thumbnailId, thumbnailId) || (identical(other.thumbnailId, thumbnailId) ||
@ -654,6 +682,7 @@ class _SnAttachment extends SnAttachment {
refId, refId,
pool, pool,
poolId, poolId,
account,
accountId, accountId,
thumbnailId, thumbnailId,
thumbnail, thumbnail,
@ -666,7 +695,7 @@ class _SnAttachment extends SnAttachment {
@override @override
String toString() { String toString() {
return 'SnAttachment(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, rid: $rid, uuid: $uuid, size: $size, name: $name, alt: $alt, mimetype: $mimetype, hash: $hash, destination: $destination, refCount: $refCount, contentRating: $contentRating, qualityRating: $qualityRating, cleanedAt: $cleanedAt, isAnalyzed: $isAnalyzed, isSelfRef: $isSelfRef, isIndexable: $isIndexable, ref: $ref, refId: $refId, pool: $pool, poolId: $poolId, accountId: $accountId, thumbnailId: $thumbnailId, thumbnail: $thumbnail, compressedId: $compressedId, compressed: $compressed, boosts: $boosts, usermeta: $usermeta, metadata: $metadata)'; return 'SnAttachment(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, rid: $rid, uuid: $uuid, size: $size, name: $name, alt: $alt, mimetype: $mimetype, hash: $hash, destination: $destination, refCount: $refCount, contentRating: $contentRating, qualityRating: $qualityRating, cleanedAt: $cleanedAt, isAnalyzed: $isAnalyzed, isSelfRef: $isSelfRef, isIndexable: $isIndexable, ref: $ref, refId: $refId, pool: $pool, poolId: $poolId, account: $account, accountId: $accountId, thumbnailId: $thumbnailId, thumbnail: $thumbnail, compressedId: $compressedId, compressed: $compressed, boosts: $boosts, usermeta: $usermeta, metadata: $metadata)';
} }
} }
@ -702,6 +731,7 @@ abstract mixin class _$SnAttachmentCopyWith<$Res>
int? refId, int? refId,
SnAttachmentPool? pool, SnAttachmentPool? pool,
int? poolId, int? poolId,
SnAccount? account,
int accountId, int accountId,
int? thumbnailId, int? thumbnailId,
SnAttachment? thumbnail, SnAttachment? thumbnail,
@ -716,6 +746,8 @@ abstract mixin class _$SnAttachmentCopyWith<$Res>
@override @override
$SnAttachmentPoolCopyWith<$Res>? get pool; $SnAttachmentPoolCopyWith<$Res>? get pool;
@override @override
$SnAccountCopyWith<$Res>? get account;
@override
$SnAttachmentCopyWith<$Res>? get thumbnail; $SnAttachmentCopyWith<$Res>? get thumbnail;
@override @override
$SnAttachmentCopyWith<$Res>? get compressed; $SnAttachmentCopyWith<$Res>? get compressed;
@ -757,6 +789,7 @@ class __$SnAttachmentCopyWithImpl<$Res>
Object? refId = freezed, Object? refId = freezed,
Object? pool = freezed, Object? pool = freezed,
Object? poolId = freezed, Object? poolId = freezed,
Object? account = freezed,
Object? accountId = null, Object? accountId = null,
Object? thumbnailId = freezed, Object? thumbnailId = freezed,
Object? thumbnail = freezed, Object? thumbnail = freezed,
@ -859,6 +892,10 @@ class __$SnAttachmentCopyWithImpl<$Res>
? _self.poolId ? _self.poolId
: poolId // ignore: cast_nullable_to_non_nullable : poolId // ignore: cast_nullable_to_non_nullable
as int?, as int?,
account: freezed == account
? _self.account
: account // ignore: cast_nullable_to_non_nullable
as SnAccount?,
accountId: null == accountId accountId: null == accountId
? _self.accountId ? _self.accountId
: accountId // ignore: cast_nullable_to_non_nullable : accountId // ignore: cast_nullable_to_non_nullable
@ -922,6 +959,20 @@ class __$SnAttachmentCopyWithImpl<$Res>
}); });
} }
/// Create a copy of SnAttachment
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$SnAccountCopyWith<$Res>? get account {
if (_self.account == null) {
return null;
}
return $SnAccountCopyWith<$Res>(_self.account!, (value) {
return _then(_self.copyWith(account: value));
});
}
/// Create a copy of SnAttachment /// Create a copy of SnAttachment
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@override @override

View File

@ -39,6 +39,9 @@ _SnAttachment _$SnAttachmentFromJson(Map<String, dynamic> json) =>
? null ? null
: SnAttachmentPool.fromJson(json['pool'] as Map<String, dynamic>), : SnAttachmentPool.fromJson(json['pool'] as Map<String, dynamic>),
poolId: (json['pool_id'] as num?)?.toInt(), poolId: (json['pool_id'] as num?)?.toInt(),
account: json['account'] == null
? null
: SnAccount.fromJson(json['account'] as Map<String, dynamic>),
accountId: (json['account_id'] as num).toInt(), accountId: (json['account_id'] as num).toInt(),
thumbnailId: (json['thumbnail_id'] as num?)?.toInt(), thumbnailId: (json['thumbnail_id'] as num?)?.toInt(),
thumbnail: json['thumbnail'] == null thumbnail: json['thumbnail'] == null
@ -82,6 +85,7 @@ Map<String, dynamic> _$SnAttachmentToJson(_SnAttachment instance) =>
'ref_id': instance.refId, 'ref_id': instance.refId,
'pool': instance.pool?.toJson(), 'pool': instance.pool?.toJson(),
'pool_id': instance.poolId, 'pool_id': instance.poolId,
'account': instance.account?.toJson(),
'account_id': instance.accountId, 'account_id': instance.accountId,
'thumbnail_id': instance.thumbnailId, 'thumbnail_id': instance.thumbnailId,
'thumbnail': instance.thumbnail?.toJson(), 'thumbnail': instance.thumbnail?.toJson(),

View File

@ -1,4 +1,5 @@
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:surface/types/account.dart';
import 'package:surface/types/attachment.dart'; import 'package:surface/types/attachment.dart';
import 'package:surface/types/poll.dart'; import 'package:surface/types/poll.dart';
import 'package:surface/types/realm.dart'; import 'package:surface/types/realm.dart';
@ -26,6 +27,7 @@ abstract class SnPost with _$SnPost {
required int? replyId, required int? replyId,
required int? repostId, required int? repostId,
required int? realmId, required int? realmId,
required SnRealm? realm,
required SnPost? replyTo, required SnPost? replyTo,
required SnPost? repostTo, required SnPost? repostTo,
required List<int>? visibleUsersList, required List<int>? visibleUsersList,
@ -43,9 +45,9 @@ abstract class SnPost with _$SnPost {
@Default(0) int totalAggregatedViews, @Default(0) int totalAggregatedViews,
required int publisherId, required int publisherId,
required int? pollId, required int? pollId,
required SnPoll? poll,
required SnPublisher publisher, required SnPublisher publisher,
required SnMetric metric, required SnMetric metric,
SnPostPreload? preload,
}) = _SnPost; }) = _SnPost;
factory SnPost.fromJson(Map<String, Object?> json) => _$SnPostFromJson(json); factory SnPost.fromJson(Map<String, Object?> json) => _$SnPostFromJson(json);
@ -146,6 +148,7 @@ abstract class SnPublisher with _$SnPublisher {
required int totalDownvote, required int totalDownvote,
required int? realmId, required int? realmId,
required int accountId, required int accountId,
required SnAccount? account,
}) = _SnPublisher; }) = _SnPublisher;
factory SnPublisher.fromJson(Map<String, Object?> json) => factory SnPublisher.fromJson(Map<String, Object?> json) =>

View File

@ -30,6 +30,7 @@ mixin _$SnPost {
int? get replyId; int? get replyId;
int? get repostId; int? get repostId;
int? get realmId; int? get realmId;
SnRealm? get realm;
SnPost? get replyTo; SnPost? get replyTo;
SnPost? get repostTo; SnPost? get repostTo;
List<int>? get visibleUsersList; List<int>? get visibleUsersList;
@ -47,9 +48,9 @@ mixin _$SnPost {
int get totalAggregatedViews; int get totalAggregatedViews;
int get publisherId; int get publisherId;
int? get pollId; int? get pollId;
SnPoll? get poll;
SnPublisher get publisher; SnPublisher get publisher;
SnMetric get metric; SnMetric get metric;
SnPostPreload? get preload;
/// Create a copy of SnPost /// Create a copy of SnPost
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@ -88,6 +89,7 @@ mixin _$SnPost {
(identical(other.repostId, repostId) || (identical(other.repostId, repostId) ||
other.repostId == repostId) && other.repostId == repostId) &&
(identical(other.realmId, realmId) || other.realmId == realmId) && (identical(other.realmId, realmId) || other.realmId == realmId) &&
(identical(other.realm, realm) || other.realm == realm) &&
(identical(other.replyTo, replyTo) || other.replyTo == replyTo) && (identical(other.replyTo, replyTo) || other.replyTo == replyTo) &&
(identical(other.repostTo, repostTo) || (identical(other.repostTo, repostTo) ||
other.repostTo == repostTo) && other.repostTo == repostTo) &&
@ -119,10 +121,10 @@ mixin _$SnPost {
(identical(other.publisherId, publisherId) || (identical(other.publisherId, publisherId) ||
other.publisherId == publisherId) && other.publisherId == publisherId) &&
(identical(other.pollId, pollId) || other.pollId == pollId) && (identical(other.pollId, pollId) || other.pollId == pollId) &&
(identical(other.poll, poll) || other.poll == poll) &&
(identical(other.publisher, publisher) || (identical(other.publisher, publisher) ||
other.publisher == publisher) && other.publisher == publisher) &&
(identical(other.metric, metric) || other.metric == metric) && (identical(other.metric, metric) || other.metric == metric));
(identical(other.preload, preload) || other.preload == preload));
} }
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@ -144,6 +146,7 @@ mixin _$SnPost {
replyId, replyId,
repostId, repostId,
realmId, realmId,
realm,
replyTo, replyTo,
repostTo, repostTo,
const DeepCollectionEquality().hash(visibleUsersList), const DeepCollectionEquality().hash(visibleUsersList),
@ -161,14 +164,14 @@ mixin _$SnPost {
totalAggregatedViews, totalAggregatedViews,
publisherId, publisherId,
pollId, pollId,
poll,
publisher, publisher,
metric, metric
preload
]); ]);
@override @override
String toString() { String toString() {
return 'SnPost(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, type: $type, body: $body, language: $language, alias: $alias, aliasPrefix: $aliasPrefix, tags: $tags, categories: $categories, replies: $replies, replyId: $replyId, repostId: $repostId, realmId: $realmId, replyTo: $replyTo, repostTo: $repostTo, visibleUsersList: $visibleUsersList, invisibleUsersList: $invisibleUsersList, visibility: $visibility, editedAt: $editedAt, pinnedAt: $pinnedAt, lockedAt: $lockedAt, isDraft: $isDraft, publishedAt: $publishedAt, publishedUntil: $publishedUntil, totalUpvote: $totalUpvote, totalDownvote: $totalDownvote, totalViews: $totalViews, totalAggregatedViews: $totalAggregatedViews, publisherId: $publisherId, pollId: $pollId, publisher: $publisher, metric: $metric, preload: $preload)'; return 'SnPost(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, type: $type, body: $body, language: $language, alias: $alias, aliasPrefix: $aliasPrefix, tags: $tags, categories: $categories, replies: $replies, replyId: $replyId, repostId: $repostId, realmId: $realmId, realm: $realm, replyTo: $replyTo, repostTo: $repostTo, visibleUsersList: $visibleUsersList, invisibleUsersList: $invisibleUsersList, visibility: $visibility, editedAt: $editedAt, pinnedAt: $pinnedAt, lockedAt: $lockedAt, isDraft: $isDraft, publishedAt: $publishedAt, publishedUntil: $publishedUntil, totalUpvote: $totalUpvote, totalDownvote: $totalDownvote, totalViews: $totalViews, totalAggregatedViews: $totalAggregatedViews, publisherId: $publisherId, pollId: $pollId, poll: $poll, publisher: $publisher, metric: $metric)';
} }
} }
@ -193,6 +196,7 @@ abstract mixin class $SnPostCopyWith<$Res> {
int? replyId, int? replyId,
int? repostId, int? repostId,
int? realmId, int? realmId,
SnRealm? realm,
SnPost? replyTo, SnPost? replyTo,
SnPost? repostTo, SnPost? repostTo,
List<int>? visibleUsersList, List<int>? visibleUsersList,
@ -210,15 +214,16 @@ abstract mixin class $SnPostCopyWith<$Res> {
int totalAggregatedViews, int totalAggregatedViews,
int publisherId, int publisherId,
int? pollId, int? pollId,
SnPoll? poll,
SnPublisher publisher, SnPublisher publisher,
SnMetric metric, SnMetric metric});
SnPostPreload? preload});
$SnRealmCopyWith<$Res>? get realm;
$SnPostCopyWith<$Res>? get replyTo; $SnPostCopyWith<$Res>? get replyTo;
$SnPostCopyWith<$Res>? get repostTo; $SnPostCopyWith<$Res>? get repostTo;
$SnPollCopyWith<$Res>? get poll;
$SnPublisherCopyWith<$Res> get publisher; $SnPublisherCopyWith<$Res> get publisher;
$SnMetricCopyWith<$Res> get metric; $SnMetricCopyWith<$Res> get metric;
$SnPostPreloadCopyWith<$Res>? get preload;
} }
/// @nodoc /// @nodoc
@ -248,6 +253,7 @@ class _$SnPostCopyWithImpl<$Res> implements $SnPostCopyWith<$Res> {
Object? replyId = freezed, Object? replyId = freezed,
Object? repostId = freezed, Object? repostId = freezed,
Object? realmId = freezed, Object? realmId = freezed,
Object? realm = freezed,
Object? replyTo = freezed, Object? replyTo = freezed,
Object? repostTo = freezed, Object? repostTo = freezed,
Object? visibleUsersList = freezed, Object? visibleUsersList = freezed,
@ -265,9 +271,9 @@ class _$SnPostCopyWithImpl<$Res> implements $SnPostCopyWith<$Res> {
Object? totalAggregatedViews = null, Object? totalAggregatedViews = null,
Object? publisherId = null, Object? publisherId = null,
Object? pollId = freezed, Object? pollId = freezed,
Object? poll = freezed,
Object? publisher = null, Object? publisher = null,
Object? metric = null, Object? metric = null,
Object? preload = freezed,
}) { }) {
return _then(_self.copyWith( return _then(_self.copyWith(
id: null == id id: null == id
@ -330,6 +336,10 @@ class _$SnPostCopyWithImpl<$Res> implements $SnPostCopyWith<$Res> {
? _self.realmId ? _self.realmId
: realmId // ignore: cast_nullable_to_non_nullable : realmId // ignore: cast_nullable_to_non_nullable
as int?, as int?,
realm: freezed == realm
? _self.realm
: realm // ignore: cast_nullable_to_non_nullable
as SnRealm?,
replyTo: freezed == replyTo replyTo: freezed == replyTo
? _self.replyTo ? _self.replyTo
: replyTo // ignore: cast_nullable_to_non_nullable : replyTo // ignore: cast_nullable_to_non_nullable
@ -398,6 +408,10 @@ class _$SnPostCopyWithImpl<$Res> implements $SnPostCopyWith<$Res> {
? _self.pollId ? _self.pollId
: pollId // ignore: cast_nullable_to_non_nullable : pollId // ignore: cast_nullable_to_non_nullable
as int?, as int?,
poll: freezed == poll
? _self.poll
: poll // ignore: cast_nullable_to_non_nullable
as SnPoll?,
publisher: null == publisher publisher: null == publisher
? _self.publisher ? _self.publisher
: publisher // ignore: cast_nullable_to_non_nullable : publisher // ignore: cast_nullable_to_non_nullable
@ -406,13 +420,23 @@ class _$SnPostCopyWithImpl<$Res> implements $SnPostCopyWith<$Res> {
? _self.metric ? _self.metric
: metric // ignore: cast_nullable_to_non_nullable : metric // ignore: cast_nullable_to_non_nullable
as SnMetric, as SnMetric,
preload: freezed == preload
? _self.preload
: preload // ignore: cast_nullable_to_non_nullable
as SnPostPreload?,
)); ));
} }
/// Create a copy of SnPost
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$SnRealmCopyWith<$Res>? get realm {
if (_self.realm == null) {
return null;
}
return $SnRealmCopyWith<$Res>(_self.realm!, (value) {
return _then(_self.copyWith(realm: value));
});
}
/// Create a copy of SnPost /// Create a copy of SnPost
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@override @override
@ -441,6 +465,20 @@ class _$SnPostCopyWithImpl<$Res> implements $SnPostCopyWith<$Res> {
}); });
} }
/// Create a copy of SnPost
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$SnPollCopyWith<$Res>? get poll {
if (_self.poll == null) {
return null;
}
return $SnPollCopyWith<$Res>(_self.poll!, (value) {
return _then(_self.copyWith(poll: value));
});
}
/// Create a copy of SnPost /// Create a copy of SnPost
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@override @override
@ -460,20 +498,6 @@ class _$SnPostCopyWithImpl<$Res> implements $SnPostCopyWith<$Res> {
return _then(_self.copyWith(metric: value)); return _then(_self.copyWith(metric: value));
}); });
} }
/// Create a copy of SnPost
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$SnPostPreloadCopyWith<$Res>? get preload {
if (_self.preload == null) {
return null;
}
return $SnPostPreloadCopyWith<$Res>(_self.preload!, (value) {
return _then(_self.copyWith(preload: value));
});
}
} }
/// @nodoc /// @nodoc
@ -495,6 +519,7 @@ class _SnPost extends SnPost {
required this.replyId, required this.replyId,
required this.repostId, required this.repostId,
required this.realmId, required this.realmId,
required this.realm,
required this.replyTo, required this.replyTo,
required this.repostTo, required this.repostTo,
required final List<int>? visibleUsersList, required final List<int>? visibleUsersList,
@ -512,9 +537,9 @@ class _SnPost extends SnPost {
this.totalAggregatedViews = 0, this.totalAggregatedViews = 0,
required this.publisherId, required this.publisherId,
required this.pollId, required this.pollId,
required this.poll,
required this.publisher, required this.publisher,
required this.metric, required this.metric})
this.preload})
: _body = body, : _body = body,
_tags = tags, _tags = tags,
_categories = categories, _categories = categories,
@ -583,6 +608,8 @@ class _SnPost extends SnPost {
@override @override
final int? realmId; final int? realmId;
@override @override
final SnRealm? realm;
@override
final SnPost? replyTo; final SnPost? replyTo;
@override @override
final SnPost? repostTo; final SnPost? repostTo;
@ -637,11 +664,11 @@ class _SnPost extends SnPost {
@override @override
final int? pollId; final int? pollId;
@override @override
final SnPoll? poll;
@override
final SnPublisher publisher; final SnPublisher publisher;
@override @override
final SnMetric metric; final SnMetric metric;
@override
final SnPostPreload? preload;
/// Create a copy of SnPost /// Create a copy of SnPost
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@ -685,6 +712,7 @@ class _SnPost extends SnPost {
(identical(other.repostId, repostId) || (identical(other.repostId, repostId) ||
other.repostId == repostId) && other.repostId == repostId) &&
(identical(other.realmId, realmId) || other.realmId == realmId) && (identical(other.realmId, realmId) || other.realmId == realmId) &&
(identical(other.realm, realm) || other.realm == realm) &&
(identical(other.replyTo, replyTo) || other.replyTo == replyTo) && (identical(other.replyTo, replyTo) || other.replyTo == replyTo) &&
(identical(other.repostTo, repostTo) || (identical(other.repostTo, repostTo) ||
other.repostTo == repostTo) && other.repostTo == repostTo) &&
@ -716,10 +744,10 @@ class _SnPost extends SnPost {
(identical(other.publisherId, publisherId) || (identical(other.publisherId, publisherId) ||
other.publisherId == publisherId) && other.publisherId == publisherId) &&
(identical(other.pollId, pollId) || other.pollId == pollId) && (identical(other.pollId, pollId) || other.pollId == pollId) &&
(identical(other.poll, poll) || other.poll == poll) &&
(identical(other.publisher, publisher) || (identical(other.publisher, publisher) ||
other.publisher == publisher) && other.publisher == publisher) &&
(identical(other.metric, metric) || other.metric == metric) && (identical(other.metric, metric) || other.metric == metric));
(identical(other.preload, preload) || other.preload == preload));
} }
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@ -741,6 +769,7 @@ class _SnPost extends SnPost {
replyId, replyId,
repostId, repostId,
realmId, realmId,
realm,
replyTo, replyTo,
repostTo, repostTo,
const DeepCollectionEquality().hash(_visibleUsersList), const DeepCollectionEquality().hash(_visibleUsersList),
@ -758,14 +787,14 @@ class _SnPost extends SnPost {
totalAggregatedViews, totalAggregatedViews,
publisherId, publisherId,
pollId, pollId,
poll,
publisher, publisher,
metric, metric
preload
]); ]);
@override @override
String toString() { String toString() {
return 'SnPost(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, type: $type, body: $body, language: $language, alias: $alias, aliasPrefix: $aliasPrefix, tags: $tags, categories: $categories, replies: $replies, replyId: $replyId, repostId: $repostId, realmId: $realmId, replyTo: $replyTo, repostTo: $repostTo, visibleUsersList: $visibleUsersList, invisibleUsersList: $invisibleUsersList, visibility: $visibility, editedAt: $editedAt, pinnedAt: $pinnedAt, lockedAt: $lockedAt, isDraft: $isDraft, publishedAt: $publishedAt, publishedUntil: $publishedUntil, totalUpvote: $totalUpvote, totalDownvote: $totalDownvote, totalViews: $totalViews, totalAggregatedViews: $totalAggregatedViews, publisherId: $publisherId, pollId: $pollId, publisher: $publisher, metric: $metric, preload: $preload)'; return 'SnPost(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, type: $type, body: $body, language: $language, alias: $alias, aliasPrefix: $aliasPrefix, tags: $tags, categories: $categories, replies: $replies, replyId: $replyId, repostId: $repostId, realmId: $realmId, realm: $realm, replyTo: $replyTo, repostTo: $repostTo, visibleUsersList: $visibleUsersList, invisibleUsersList: $invisibleUsersList, visibility: $visibility, editedAt: $editedAt, pinnedAt: $pinnedAt, lockedAt: $lockedAt, isDraft: $isDraft, publishedAt: $publishedAt, publishedUntil: $publishedUntil, totalUpvote: $totalUpvote, totalDownvote: $totalDownvote, totalViews: $totalViews, totalAggregatedViews: $totalAggregatedViews, publisherId: $publisherId, pollId: $pollId, poll: $poll, publisher: $publisher, metric: $metric)';
} }
} }
@ -791,6 +820,7 @@ abstract mixin class _$SnPostCopyWith<$Res> implements $SnPostCopyWith<$Res> {
int? replyId, int? replyId,
int? repostId, int? repostId,
int? realmId, int? realmId,
SnRealm? realm,
SnPost? replyTo, SnPost? replyTo,
SnPost? repostTo, SnPost? repostTo,
List<int>? visibleUsersList, List<int>? visibleUsersList,
@ -808,20 +838,22 @@ abstract mixin class _$SnPostCopyWith<$Res> implements $SnPostCopyWith<$Res> {
int totalAggregatedViews, int totalAggregatedViews,
int publisherId, int publisherId,
int? pollId, int? pollId,
SnPoll? poll,
SnPublisher publisher, SnPublisher publisher,
SnMetric metric, SnMetric metric});
SnPostPreload? preload});
@override
$SnRealmCopyWith<$Res>? get realm;
@override @override
$SnPostCopyWith<$Res>? get replyTo; $SnPostCopyWith<$Res>? get replyTo;
@override @override
$SnPostCopyWith<$Res>? get repostTo; $SnPostCopyWith<$Res>? get repostTo;
@override @override
$SnPollCopyWith<$Res>? get poll;
@override
$SnPublisherCopyWith<$Res> get publisher; $SnPublisherCopyWith<$Res> get publisher;
@override @override
$SnMetricCopyWith<$Res> get metric; $SnMetricCopyWith<$Res> get metric;
@override
$SnPostPreloadCopyWith<$Res>? get preload;
} }
/// @nodoc /// @nodoc
@ -851,6 +883,7 @@ class __$SnPostCopyWithImpl<$Res> implements _$SnPostCopyWith<$Res> {
Object? replyId = freezed, Object? replyId = freezed,
Object? repostId = freezed, Object? repostId = freezed,
Object? realmId = freezed, Object? realmId = freezed,
Object? realm = freezed,
Object? replyTo = freezed, Object? replyTo = freezed,
Object? repostTo = freezed, Object? repostTo = freezed,
Object? visibleUsersList = freezed, Object? visibleUsersList = freezed,
@ -868,9 +901,9 @@ class __$SnPostCopyWithImpl<$Res> implements _$SnPostCopyWith<$Res> {
Object? totalAggregatedViews = null, Object? totalAggregatedViews = null,
Object? publisherId = null, Object? publisherId = null,
Object? pollId = freezed, Object? pollId = freezed,
Object? poll = freezed,
Object? publisher = null, Object? publisher = null,
Object? metric = null, Object? metric = null,
Object? preload = freezed,
}) { }) {
return _then(_SnPost( return _then(_SnPost(
id: null == id id: null == id
@ -933,6 +966,10 @@ class __$SnPostCopyWithImpl<$Res> implements _$SnPostCopyWith<$Res> {
? _self.realmId ? _self.realmId
: realmId // ignore: cast_nullable_to_non_nullable : realmId // ignore: cast_nullable_to_non_nullable
as int?, as int?,
realm: freezed == realm
? _self.realm
: realm // ignore: cast_nullable_to_non_nullable
as SnRealm?,
replyTo: freezed == replyTo replyTo: freezed == replyTo
? _self.replyTo ? _self.replyTo
: replyTo // ignore: cast_nullable_to_non_nullable : replyTo // ignore: cast_nullable_to_non_nullable
@ -1001,6 +1038,10 @@ class __$SnPostCopyWithImpl<$Res> implements _$SnPostCopyWith<$Res> {
? _self.pollId ? _self.pollId
: pollId // ignore: cast_nullable_to_non_nullable : pollId // ignore: cast_nullable_to_non_nullable
as int?, as int?,
poll: freezed == poll
? _self.poll
: poll // ignore: cast_nullable_to_non_nullable
as SnPoll?,
publisher: null == publisher publisher: null == publisher
? _self.publisher ? _self.publisher
: publisher // ignore: cast_nullable_to_non_nullable : publisher // ignore: cast_nullable_to_non_nullable
@ -1009,13 +1050,23 @@ class __$SnPostCopyWithImpl<$Res> implements _$SnPostCopyWith<$Res> {
? _self.metric ? _self.metric
: metric // ignore: cast_nullable_to_non_nullable : metric // ignore: cast_nullable_to_non_nullable
as SnMetric, as SnMetric,
preload: freezed == preload
? _self.preload
: preload // ignore: cast_nullable_to_non_nullable
as SnPostPreload?,
)); ));
} }
/// Create a copy of SnPost
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$SnRealmCopyWith<$Res>? get realm {
if (_self.realm == null) {
return null;
}
return $SnRealmCopyWith<$Res>(_self.realm!, (value) {
return _then(_self.copyWith(realm: value));
});
}
/// Create a copy of SnPost /// Create a copy of SnPost
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@override @override
@ -1044,6 +1095,20 @@ class __$SnPostCopyWithImpl<$Res> implements _$SnPostCopyWith<$Res> {
}); });
} }
/// Create a copy of SnPost
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$SnPollCopyWith<$Res>? get poll {
if (_self.poll == null) {
return null;
}
return $SnPollCopyWith<$Res>(_self.poll!, (value) {
return _then(_self.copyWith(poll: value));
});
}
/// Create a copy of SnPost /// Create a copy of SnPost
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@override @override
@ -1063,20 +1128,6 @@ class __$SnPostCopyWithImpl<$Res> implements _$SnPostCopyWith<$Res> {
return _then(_self.copyWith(metric: value)); return _then(_self.copyWith(metric: value));
}); });
} }
/// Create a copy of SnPost
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$SnPostPreloadCopyWith<$Res>? get preload {
if (_self.preload == null) {
return null;
}
return $SnPostPreloadCopyWith<$Res>(_self.preload!, (value) {
return _then(_self.copyWith(preload: value));
});
}
} }
/// @nodoc /// @nodoc
@ -2465,6 +2516,7 @@ mixin _$SnPublisher {
int get totalDownvote; int get totalDownvote;
int? get realmId; int? get realmId;
int get accountId; int get accountId;
SnAccount? get account;
/// Create a copy of SnPublisher /// Create a copy of SnPublisher
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@ -2501,7 +2553,8 @@ mixin _$SnPublisher {
other.totalDownvote == totalDownvote) && other.totalDownvote == totalDownvote) &&
(identical(other.realmId, realmId) || other.realmId == realmId) && (identical(other.realmId, realmId) || other.realmId == realmId) &&
(identical(other.accountId, accountId) || (identical(other.accountId, accountId) ||
other.accountId == accountId)); other.accountId == accountId) &&
(identical(other.account, account) || other.account == account));
} }
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@ -2521,11 +2574,12 @@ mixin _$SnPublisher {
totalUpvote, totalUpvote,
totalDownvote, totalDownvote,
realmId, realmId,
accountId); accountId,
account);
@override @override
String toString() { String toString() {
return 'SnPublisher(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, type: $type, name: $name, nick: $nick, description: $description, avatar: $avatar, banner: $banner, totalUpvote: $totalUpvote, totalDownvote: $totalDownvote, realmId: $realmId, accountId: $accountId)'; return 'SnPublisher(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, type: $type, name: $name, nick: $nick, description: $description, avatar: $avatar, banner: $banner, totalUpvote: $totalUpvote, totalDownvote: $totalDownvote, realmId: $realmId, accountId: $accountId, account: $account)';
} }
} }
@ -2549,7 +2603,10 @@ abstract mixin class $SnPublisherCopyWith<$Res> {
int totalUpvote, int totalUpvote,
int totalDownvote, int totalDownvote,
int? realmId, int? realmId,
int accountId}); int accountId,
SnAccount? account});
$SnAccountCopyWith<$Res>? get account;
} }
/// @nodoc /// @nodoc
@ -2578,6 +2635,7 @@ class _$SnPublisherCopyWithImpl<$Res> implements $SnPublisherCopyWith<$Res> {
Object? totalDownvote = null, Object? totalDownvote = null,
Object? realmId = freezed, Object? realmId = freezed,
Object? accountId = null, Object? accountId = null,
Object? account = freezed,
}) { }) {
return _then(_self.copyWith( return _then(_self.copyWith(
id: null == id id: null == id
@ -2636,8 +2694,26 @@ class _$SnPublisherCopyWithImpl<$Res> implements $SnPublisherCopyWith<$Res> {
? _self.accountId ? _self.accountId
: accountId // ignore: cast_nullable_to_non_nullable : accountId // ignore: cast_nullable_to_non_nullable
as int, as int,
account: freezed == account
? _self.account
: account // ignore: cast_nullable_to_non_nullable
as SnAccount?,
)); ));
} }
/// Create a copy of SnPublisher
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$SnAccountCopyWith<$Res>? get account {
if (_self.account == null) {
return null;
}
return $SnAccountCopyWith<$Res>(_self.account!, (value) {
return _then(_self.copyWith(account: value));
});
}
} }
/// @nodoc /// @nodoc
@ -2657,7 +2733,8 @@ class _SnPublisher implements SnPublisher {
required this.totalUpvote, required this.totalUpvote,
required this.totalDownvote, required this.totalDownvote,
required this.realmId, required this.realmId,
required this.accountId}); required this.accountId,
required this.account});
factory _SnPublisher.fromJson(Map<String, dynamic> json) => factory _SnPublisher.fromJson(Map<String, dynamic> json) =>
_$SnPublisherFromJson(json); _$SnPublisherFromJson(json);
@ -2689,6 +2766,8 @@ class _SnPublisher implements SnPublisher {
final int? realmId; final int? realmId;
@override @override
final int accountId; final int accountId;
@override
final SnAccount? account;
/// Create a copy of SnPublisher /// Create a copy of SnPublisher
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@ -2730,7 +2809,8 @@ class _SnPublisher implements SnPublisher {
other.totalDownvote == totalDownvote) && other.totalDownvote == totalDownvote) &&
(identical(other.realmId, realmId) || other.realmId == realmId) && (identical(other.realmId, realmId) || other.realmId == realmId) &&
(identical(other.accountId, accountId) || (identical(other.accountId, accountId) ||
other.accountId == accountId)); other.accountId == accountId) &&
(identical(other.account, account) || other.account == account));
} }
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@ -2750,11 +2830,12 @@ class _SnPublisher implements SnPublisher {
totalUpvote, totalUpvote,
totalDownvote, totalDownvote,
realmId, realmId,
accountId); accountId,
account);
@override @override
String toString() { String toString() {
return 'SnPublisher(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, type: $type, name: $name, nick: $nick, description: $description, avatar: $avatar, banner: $banner, totalUpvote: $totalUpvote, totalDownvote: $totalDownvote, realmId: $realmId, accountId: $accountId)'; return 'SnPublisher(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, type: $type, name: $name, nick: $nick, description: $description, avatar: $avatar, banner: $banner, totalUpvote: $totalUpvote, totalDownvote: $totalDownvote, realmId: $realmId, accountId: $accountId, account: $account)';
} }
} }
@ -2780,7 +2861,11 @@ abstract mixin class _$SnPublisherCopyWith<$Res>
int totalUpvote, int totalUpvote,
int totalDownvote, int totalDownvote,
int? realmId, int? realmId,
int accountId}); int accountId,
SnAccount? account});
@override
$SnAccountCopyWith<$Res>? get account;
} }
/// @nodoc /// @nodoc
@ -2809,6 +2894,7 @@ class __$SnPublisherCopyWithImpl<$Res> implements _$SnPublisherCopyWith<$Res> {
Object? totalDownvote = null, Object? totalDownvote = null,
Object? realmId = freezed, Object? realmId = freezed,
Object? accountId = null, Object? accountId = null,
Object? account = freezed,
}) { }) {
return _then(_SnPublisher( return _then(_SnPublisher(
id: null == id id: null == id
@ -2867,8 +2953,26 @@ class __$SnPublisherCopyWithImpl<$Res> implements _$SnPublisherCopyWith<$Res> {
? _self.accountId ? _self.accountId
: accountId // ignore: cast_nullable_to_non_nullable : accountId // ignore: cast_nullable_to_non_nullable
as int, as int,
account: freezed == account
? _self.account
: account // ignore: cast_nullable_to_non_nullable
as SnAccount?,
)); ));
} }
/// Create a copy of SnPublisher
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$SnAccountCopyWith<$Res>? get account {
if (_self.account == null) {
return null;
}
return $SnAccountCopyWith<$Res>(_self.account!, (value) {
return _then(_self.copyWith(account: value));
});
}
} }
/// @nodoc /// @nodoc

View File

@ -32,6 +32,9 @@ _SnPost _$SnPostFromJson(Map<String, dynamic> json) => _SnPost(
replyId: (json['reply_id'] as num?)?.toInt(), replyId: (json['reply_id'] as num?)?.toInt(),
repostId: (json['repost_id'] as num?)?.toInt(), repostId: (json['repost_id'] as num?)?.toInt(),
realmId: (json['realm_id'] as num?)?.toInt(), realmId: (json['realm_id'] as num?)?.toInt(),
realm: json['realm'] == null
? null
: SnRealm.fromJson(json['realm'] as Map<String, dynamic>),
replyTo: json['reply_to'] == null replyTo: json['reply_to'] == null
? null ? null
: SnPost.fromJson(json['reply_to'] as Map<String, dynamic>), : SnPost.fromJson(json['reply_to'] as Map<String, dynamic>),
@ -68,12 +71,12 @@ _SnPost _$SnPostFromJson(Map<String, dynamic> json) => _SnPost(
(json['total_aggregated_views'] as num?)?.toInt() ?? 0, (json['total_aggregated_views'] as num?)?.toInt() ?? 0,
publisherId: (json['publisher_id'] as num).toInt(), publisherId: (json['publisher_id'] as num).toInt(),
pollId: (json['poll_id'] as num?)?.toInt(), pollId: (json['poll_id'] as num?)?.toInt(),
poll: json['poll'] == null
? null
: SnPoll.fromJson(json['poll'] as Map<String, dynamic>),
publisher: publisher:
SnPublisher.fromJson(json['publisher'] as Map<String, dynamic>), SnPublisher.fromJson(json['publisher'] as Map<String, dynamic>),
metric: SnMetric.fromJson(json['metric'] as Map<String, dynamic>), metric: SnMetric.fromJson(json['metric'] as Map<String, dynamic>),
preload: json['preload'] == null
? null
: SnPostPreload.fromJson(json['preload'] as Map<String, dynamic>),
); );
Map<String, dynamic> _$SnPostToJson(_SnPost instance) => <String, dynamic>{ Map<String, dynamic> _$SnPostToJson(_SnPost instance) => <String, dynamic>{
@ -92,6 +95,7 @@ Map<String, dynamic> _$SnPostToJson(_SnPost instance) => <String, dynamic>{
'reply_id': instance.replyId, 'reply_id': instance.replyId,
'repost_id': instance.repostId, 'repost_id': instance.repostId,
'realm_id': instance.realmId, 'realm_id': instance.realmId,
'realm': instance.realm?.toJson(),
'reply_to': instance.replyTo?.toJson(), 'reply_to': instance.replyTo?.toJson(),
'repost_to': instance.repostTo?.toJson(), 'repost_to': instance.repostTo?.toJson(),
'visible_users_list': instance.visibleUsersList, 'visible_users_list': instance.visibleUsersList,
@ -109,9 +113,9 @@ Map<String, dynamic> _$SnPostToJson(_SnPost instance) => <String, dynamic>{
'total_aggregated_views': instance.totalAggregatedViews, 'total_aggregated_views': instance.totalAggregatedViews,
'publisher_id': instance.publisherId, 'publisher_id': instance.publisherId,
'poll_id': instance.pollId, 'poll_id': instance.pollId,
'poll': instance.poll?.toJson(),
'publisher': instance.publisher.toJson(), 'publisher': instance.publisher.toJson(),
'metric': instance.metric.toJson(), 'metric': instance.metric.toJson(),
'preload': instance.preload?.toJson(),
}; };
_SnPostTag _$SnPostTagFromJson(Map<String, dynamic> json) => _SnPostTag( _SnPostTag _$SnPostTagFromJson(Map<String, dynamic> json) => _SnPostTag(
@ -241,6 +245,9 @@ _SnPublisher _$SnPublisherFromJson(Map<String, dynamic> json) => _SnPublisher(
totalDownvote: (json['total_downvote'] as num).toInt(), totalDownvote: (json['total_downvote'] as num).toInt(),
realmId: (json['realm_id'] as num?)?.toInt(), realmId: (json['realm_id'] as num?)?.toInt(),
accountId: (json['account_id'] as num).toInt(), accountId: (json['account_id'] as num).toInt(),
account: json['account'] == null
? null
: SnAccount.fromJson(json['account'] as Map<String, dynamic>),
); );
Map<String, dynamic> _$SnPublisherToJson(_SnPublisher instance) => Map<String, dynamic> _$SnPublisherToJson(_SnPublisher instance) =>
@ -259,6 +266,7 @@ Map<String, dynamic> _$SnPublisherToJson(_SnPublisher instance) =>
'total_downvote': instance.totalDownvote, 'total_downvote': instance.totalDownvote,
'realm_id': instance.realmId, 'realm_id': instance.realmId,
'account_id': instance.accountId, 'account_id': instance.accountId,
'account': instance.account?.toJson(),
}; };
_SnSubscription _$SnSubscriptionFromJson(Map<String, dynamic> json) => _SnSubscription _$SnSubscriptionFromJson(Map<String, dynamic> json) =>

View File

@ -17,4 +17,5 @@ const Map<String, ReactInfo> kTemplateReactions = {
'party': ReactInfo(icon: '🎉', attitude: 1), 'party': ReactInfo(icon: '🎉', attitude: 1),
'joy': ReactInfo(icon: '🤣', attitude: 1), 'joy': ReactInfo(icon: '🤣', attitude: 1),
'pray': ReactInfo(icon: '🙏', attitude: 1), 'pray': ReactInfo(icon: '🙏', attitude: 1),
'heart': ReactInfo(icon: '❤️', attitude: 1),
}; };

View File

@ -16,7 +16,6 @@ import 'package:photo_view/photo_view_gallery.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
import 'package:surface/providers/sn_network.dart'; import 'package:surface/providers/sn_network.dart';
import 'package:surface/providers/user_directory.dart';
import 'package:surface/types/attachment.dart'; import 'package:surface/types/attachment.dart';
import 'package:surface/widgets/account/account_image.dart'; import 'package:surface/widgets/account/account_image.dart';
import 'package:surface/widgets/dialog.dart'; import 'package:surface/widgets/dialog.dart';
@ -418,8 +417,7 @@ class _AttachmentZoomDetailPopup extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final ud = context.read<UserDirectoryProvider>(); final account = data.account!;
final account = ud.getFromCache(data.accountId);
const tableGap = TableRow( const tableGap = TableRow(
children: [ children: [
@ -461,12 +459,12 @@ class _AttachmentZoomDetailPopup extends StatelessWidget {
children: [ children: [
if (data.accountId > 0) if (data.accountId > 0)
AccountImage( AccountImage(
content: account?.avatar, content: account.avatar,
radius: 8, radius: 8,
), ),
const Gap(8), const Gap(8),
Text(data.accountId > 0 Text(data.accountId > 0
? account?.nick ?? 'unknown'.tr() ? account.nick
: 'unknown'.tr()), : 'unknown'.tr()),
const Gap(8), const Gap(8),
Text('#${data.accountId}', Text('#${data.accountId}',

View File

@ -26,11 +26,13 @@ class _LinkPreviewWidgetState extends State<LinkPreviewWidget> {
Future<void> _getLinkMeta() async { Future<void> _getLinkMeta() async {
final linkRegex = RegExp(r'https?:\/\/[^\s/$.?#].[^\s]*'); final linkRegex = RegExp(r'https?:\/\/[^\s/$.?#].[^\s]*');
final links = linkRegex.allMatches(widget.text).map((e) => e.group(0)).toSet(); final links =
linkRegex.allMatches(widget.text).map((e) => e.group(0)).toSet();
final lp = context.read<SnLinkPreviewProvider>(); final lp = context.read<SnLinkPreviewProvider>();
final List<Future<SnLinkMeta?>> futures = links.where((e) => e != null).map((e) => lp.getLinkMeta(e!)).toList(); final List<Future<SnLinkMeta?>> futures =
links.where((e) => e != null).map((e) => lp.getLinkMeta(e!)).toList();
final results = await Future.wait(futures); final results = await Future.wait(futures);
_links.addAll(results.where((e) => e != null).map((e) => e!).toList()); _links.addAll(results.where((e) => e != null).map((e) => e!).toList());
@ -66,7 +68,9 @@ class _LinkPreviewEntry extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return Container(
constraints: BoxConstraints( constraints: BoxConstraints(
maxWidth: ResponsiveBreakpoints.of(context).smallerOrEqualTo(MOBILE) ? double.infinity : 480, maxWidth: ResponsiveBreakpoints.of(context).smallerOrEqualTo(MOBILE)
? double.infinity
: 480,
), ),
child: GestureDetector( child: GestureDetector(
child: Card( child: Card(
@ -74,20 +78,29 @@ class _LinkPreviewEntry extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
if (meta.image != null) if (meta.image != null)
Container( ClipRRect(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(8),
topRight: Radius.circular(8),
),
child: Container(
margin: const EdgeInsets.only(bottom: 4), margin: const EdgeInsets.only(bottom: 4),
color: Theme.of(context).colorScheme.surfaceContainer, color: Theme.of(context).colorScheme.surfaceContainer,
child: AspectRatio( child: AspectRatio(
aspectRatio: 16 / 9, aspectRatio: 16 / 9,
child: ClipRRect( child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(8)), borderRadius:
const BorderRadius.all(Radius.circular(8)),
child: AutoResizeUniversalImage( child: AutoResizeUniversalImage(
meta.image!.startsWith('//') ? 'https:${meta.image}' : meta.image!, meta.image!.startsWith('//')
? 'https:${meta.image}'
: meta.image!,
fit: BoxFit.contain, fit: BoxFit.contain,
), ),
), ),
), ),
), ),
),
SizedBox( SizedBox(
height: 48, height: 48,
child: Row( child: Row(
@ -98,7 +111,8 @@ class _LinkPreviewEntry extends StatelessWidget {
width: 36, width: 36,
height: 36, height: 36,
child: meta.icon!.endsWith('.svg') child: meta.icon!.endsWith('.svg')
? SvgPicture.network(meta.icon!, width: 36, height: 36) ? SvgPicture.network(meta.icon!,
width: 36, height: 36)
: UniversalImage( : UniversalImage(
meta.icon!, meta.icon!,
noErrorWidget: true, noErrorWidget: true,

View File

@ -87,6 +87,7 @@ class _AppNavigationDrawerState extends State<AppNavigationDrawer> {
onTap: () { onTap: () {
GoRouter.of(context).pushNamed(ele.screen); GoRouter.of(context).pushNamed(ele.screen);
nav.setIndex(idx); nav.setIndex(idx);
Scaffold.of(context).closeDrawer();
}, },
); );
}) })

View File

@ -282,7 +282,7 @@ class ResponsiveScaffoldLanding extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (ResponsiveScaffold.getIsExpand(context) || child == null) { if (ResponsiveScaffold.getIsExpand(context) || child == null) {
return AppScaffold( return AppScaffold(
noBackground: true, noBackground: ResponsiveScaffold.getIsExpand(context),
appBar: AppBar(), appBar: AppBar(),
body: const SizedBox.shrink(), body: const SizedBox.shrink(),
); );

View File

@ -179,9 +179,7 @@ class _PostCommentListPopupState extends State<PostCommentListPopup> {
final ua = context.watch<UserProvider>(); final ua = context.watch<UserProvider>();
final devicePixelRatio = MediaQuery.of(context).devicePixelRatio; final devicePixelRatio = MediaQuery.of(context).devicePixelRatio;
return SizedBox( return Column(
height: MediaQuery.of(context).size.height * 0.85,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Row( Row(
@ -229,7 +227,6 @@ class _PostCommentListPopupState extends State<PostCommentListPopup> {
), ),
), ),
], ],
),
); );
} }
} }

View File

@ -274,8 +274,10 @@ class _PostItemState extends State<PostItem> {
final isParentAuthor = ua.isAuthorized && final isParentAuthor = ua.isAuthorized &&
widget.data.replyTo?.publisher.accountId == ua.user?.id; widget.data.replyTo?.publisher.accountId == ua.user?.id;
final displayableAttachments = widget.data.preload?.attachments final displayableAttachments = widget.data.body['attachments']
?.where((ele) => ?.map((e) => SnAttachment.fromJson(e))
.cast<SnAttachment>()
.where((ele) =>
ele?.mediaType != SnMediaType.image || ele?.mediaType != SnMediaType.image ||
widget.data.type != 'article') widget.data.type != 'article')
.toList(); .toList();
@ -284,7 +286,7 @@ class _PostItemState extends State<PostItem> {
var attachmentSize = math.min( var attachmentSize = math.min(
MediaQuery.of(context).size.width, widget.maxWidth ?? double.infinity); MediaQuery.of(context).size.width, widget.maxWidth ?? double.infinity);
if ((widget.data.preload?.attachments?.length ?? 0) > 1) { if ((widget.data.body['attachments']?.length ?? 0) > 1) {
attachmentSize -= 80; attachmentSize -= 80;
} }
@ -341,7 +343,7 @@ class _PostItemState extends State<PostItem> {
], ],
), ),
const Gap(8), const Gap(8),
if (widget.data.preload?.thumbnail != null) if (widget.data.body['thumbnail'] != null)
Container( Container(
margin: const EdgeInsets.only(bottom: 8), margin: const EdgeInsets.only(bottom: 8),
decoration: BoxDecoration( decoration: BoxDecoration(
@ -361,14 +363,14 @@ class _PostItemState extends State<PostItem> {
), ),
child: AutoResizeUniversalImage( child: AutoResizeUniversalImage(
sn.getAttachmentUrl( sn.getAttachmentUrl(
widget.data.preload!.thumbnail!.rid, widget.data.body['thumbnail']['rid'],
), ),
fit: BoxFit.cover, fit: BoxFit.cover,
), ),
), ),
), ),
), ),
if (widget.data.preload?.video != null) if (widget.data.body['video'] != null)
_PostVideoPlayer(data: widget.data).padding(bottom: 8), _PostVideoPlayer(data: widget.data).padding(bottom: 8),
if (widget.data.type == 'question') if (widget.data.type == 'question')
_PostQuestionHint(data: widget.data).padding(bottom: 8), _PostQuestionHint(data: widget.data).padding(bottom: 8),
@ -455,8 +457,8 @@ class _PostItemState extends State<PostItem> {
if (widget.data.repostTo != null) if (widget.data.repostTo != null)
_PostQuoteContent(child: widget.data.repostTo!).padding( _PostQuoteContent(child: widget.data.repostTo!).padding(
top: 4, top: 4,
bottom: widget.data.preload?.attachments?.isNotEmpty ?? bottom:
false widget.data.body['attachments'].isNotEmpty ?? false
? 12 ? 12
: 0, : 0,
), ),
@ -479,11 +481,11 @@ class _PostItemState extends State<PostItem> {
fit: widget.showFullPost ? BoxFit.cover : BoxFit.contain, fit: widget.showFullPost ? BoxFit.cover : BoxFit.contain,
padding: EdgeInsets.only(left: 12, right: 12), padding: EdgeInsets.only(left: 12, right: 12),
), ),
if (widget.data.preload?.poll != null) if (widget.data.poll != null)
StyledWidget(Container( StyledWidget(Container(
constraints: constraints:
BoxConstraints(maxWidth: widget.maxWidth ?? double.infinity), BoxConstraints(maxWidth: widget.maxWidth ?? double.infinity),
child: PostPoll(poll: widget.data.preload!.poll!), child: PostPoll(poll: widget.data.poll!),
)) ))
.padding( .padding(
left: 12, left: 12,
@ -585,7 +587,7 @@ class _PostItemState extends State<PostItem> {
), ),
], ],
).padding(bottom: widget.showCompactAvatar ? 4 : 0), ).padding(bottom: widget.showCompactAvatar ? 4 : 0),
if (widget.data.preload?.thumbnail != null) if (widget.data.body['thumbnail'] != null)
Container( Container(
margin: const EdgeInsets.only(bottom: 8), margin: const EdgeInsets.only(bottom: 8),
decoration: BoxDecoration( decoration: BoxDecoration(
@ -605,14 +607,14 @@ class _PostItemState extends State<PostItem> {
), ),
child: AutoResizeUniversalImage( child: AutoResizeUniversalImage(
sn.getAttachmentUrl( sn.getAttachmentUrl(
widget.data.preload!.thumbnail!.rid, widget.data.body['thumbnail']['rid'],
), ),
fit: BoxFit.cover, fit: BoxFit.cover,
), ),
), ),
), ),
), ),
if (widget.data.preload?.video != null) if (widget.data.body['video'] != null)
_PostVideoPlayer(data: widget.data) _PostVideoPlayer(data: widget.data)
.padding(bottom: 8), .padding(bottom: 8),
if (widget.data.type == 'question') if (widget.data.type == 'question')
@ -712,7 +714,7 @@ class _PostItemState extends State<PostItem> {
_isTranslated || _isTranslated ||
_isTranslating) && _isTranslating) &&
(widget.data.repostTo != null || (widget.data.repostTo != null ||
(widget.data.preload?.attachments (widget.data.body['attachments']
?.isNotEmpty ?? ?.isNotEmpty ??
false)) false))
? 8 ? 8
@ -722,7 +724,7 @@ class _PostItemState extends State<PostItem> {
_PostQuoteContent(child: widget.data.repostTo!) _PostQuoteContent(child: widget.data.repostTo!)
.padding( .padding(
bottom: bottom:
(widget.data.preload?.attachments?.isNotEmpty ?? (widget.data.body['attachments']?.isNotEmpty ??
false) false)
? 8 ? 8
: 0, : 0,
@ -746,8 +748,8 @@ class _PostItemState extends State<PostItem> {
padding: padding:
EdgeInsets.only(left: widget.showAvatar ? 60 : 12, right: 12), EdgeInsets.only(left: widget.showAvatar ? 60 : 12, right: 12),
), ),
if (widget.data.preload?.poll != null) if (widget.data.poll != null)
PostPoll(poll: widget.data.preload!.poll!).padding( PostPoll(poll: widget.data.poll!).padding(
left: widget.showAvatar ? 60 : 12, left: widget.showAvatar ? 60 : 12,
right: 12, right: 12,
top: 12, top: 12,
@ -808,7 +810,7 @@ class PostShareImageWidget extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
if (data.preload?.thumbnail != null) if (data.body['thumbnail'] != null)
AspectRatio( AspectRatio(
aspectRatio: 16 / 9, aspectRatio: 16 / 9,
child: ClipRRect( child: ClipRRect(
@ -817,7 +819,7 @@ class PostShareImageWidget extends StatelessWidget {
topRight: Radius.circular(8), topRight: Radius.circular(8),
), ),
child: AutoResizeUniversalImage( child: AutoResizeUniversalImage(
sn.getAttachmentUrl(data.preload!.thumbnail!.rid), sn.getAttachmentUrl(data.body['thumbnail']['rid']),
fit: BoxFit.cover, fit: BoxFit.cover,
filterQuality: FilterQuality.high, filterQuality: FilterQuality.high,
), ),
@ -855,9 +857,13 @@ class PostShareImageWidget extends StatelessWidget {
isRelativeDate: false, isRelativeDate: false,
).padding(horizontal: 16, bottom: 8), ).padding(horizontal: 16, bottom: 8),
if (data.type != 'article' && if (data.type != 'article' &&
(data.preload?.attachments?.isNotEmpty ?? false)) (data.body['attachments']?.isNotEmpty ?? false))
StyledWidget(AttachmentList( StyledWidget(AttachmentList(
data: data.preload!.attachments!, data: data.body['attachments']
?.map((e) => SnAttachment.fromJson(e))
.cast<SnAttachment>()
.toList() ??
[],
columned: true, columned: true,
fit: BoxFit.contain, fit: BoxFit.contain,
filterQuality: FilterQuality.high, filterQuality: FilterQuality.high,
@ -1146,31 +1152,9 @@ class _PostHeadline extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (isEnlarge) { if (isEnlarge) {
final sn = context.read<SnNetworkProvider>();
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
if (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(data.preload!.thumbnail!.rid),
fit: BoxFit.cover,
),
),
),
),
if (data.body['title'] != null || (title?.isNotEmpty ?? false)) if (data.body['title'] != null || (title?.isNotEmpty ?? false))
Text( Text(
title ?? data.body['title'], title ?? data.body['title'],
@ -1255,7 +1239,7 @@ class _PostAvatar extends StatelessWidget {
: null; : null;
return GestureDetector( return GestureDetector(
child: data.preload?.realm == null child: data.realm == null
? AccountImage( ? AccountImage(
filterQuality: filterQuality, filterQuality: filterQuality,
content: data.publisher.avatar, content: data.publisher.avatar,
@ -1271,7 +1255,7 @@ class _PostAvatar extends StatelessWidget {
) )
: AccountImage( : AccountImage(
filterQuality: filterQuality, filterQuality: filterQuality,
content: data.preload!.realm!.avatar, content: data.realm!.avatar,
radius: isCompact ? 12 : 20, radius: isCompact ? 12 : 20,
borderRadius: isCompact ? 4 : 8, borderRadius: isCompact ? 4 : 8,
badgeOffset: Offset(-6, -4), badgeOffset: Offset(-6, -4),
@ -1568,6 +1552,7 @@ class _PostContentHeader extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (isCompact) { if (isCompact) {
return Row( return Row(
spacing: 4,
children: [ children: [
Flexible( Flexible(
child: Text( child: Text(
@ -1575,7 +1560,6 @@ class _PostContentHeader extends StatelessWidget {
maxLines: 1, maxLines: 1,
).bold(), ).bold(),
), ),
const Gap(4),
Flexible( Flexible(
child: Text( child: Text(
isRelativeDate isRelativeDate
@ -1587,6 +1571,10 @@ class _PostContentHeader extends StatelessWidget {
overflow: TextOverflow.fade, overflow: TextOverflow.fade,
).fontSize(13).opacity(0.8), ).fontSize(13).opacity(0.8),
), ),
if (data.editedAt != null)
Flexible(
child: Text('postEditedHint').tr().fontSize(13).opacity(0.8),
)
], ],
); );
} else { } else {
@ -1596,20 +1584,20 @@ class _PostContentHeader extends StatelessWidget {
Row( Row(
children: [ children: [
Text(data.publisher.nick).bold(), Text(data.publisher.nick).bold(),
if (data.preload?.realm != null) if (data.realm != null)
const Icon(Symbols.arrow_right, size: 16) const Icon(Symbols.arrow_right, size: 16)
.padding(horizontal: 2) .padding(horizontal: 2)
.opacity(0.5), .opacity(0.5),
if (data.preload?.realm != null) Text(data.preload!.realm!.name), if (data.realm != null) Text(data.realm!.name),
], ],
), ),
Row( Row(
spacing: 4,
children: [ children: [
Text( Text(
'@${data.publisher.name}', '@${data.publisher.name}',
maxLines: 1, maxLines: 1,
).fontSize(13), ).fontSize(13),
const Gap(4),
Text( Text(
isRelativeDate isRelativeDate
? RelativeTime(context) ? RelativeTime(context)
@ -1619,6 +1607,8 @@ class _PostContentHeader extends StatelessWidget {
maxLines: 1, maxLines: 1,
overflow: TextOverflow.fade, overflow: TextOverflow.fade,
).fontSize(13), ).fontSize(13),
if (data.editedAt != null)
Text('postEditedHint').tr().fontSize(13),
], ],
).opacity(0.8), ).opacity(0.8),
], ],
@ -1648,7 +1638,11 @@ class _PostContentBody extends StatelessWidget {
RegExp(r"^:([-\w]+):$").hasMatch(data.body['content'] ?? ''), RegExp(r"^:([-\w]+):$").hasMatch(data.body['content'] ?? ''),
textScaler: isEnlarge ? TextScaler.linear(1.1) : null, textScaler: isEnlarge ? TextScaler.linear(1.1) : null,
content: text, content: text,
attachments: data.preload?.attachments, attachments: data.body['attachments']
?.map((e) => SnAttachment.fromJson(e))
.cast<SnAttachment>()
.toList() ??
[],
); );
if (isSelectable) { if (isSelectable) {
@ -1706,14 +1700,14 @@ class _PostQuoteContent extends StatelessWidget {
], ],
).padding(horizontal: 16), ).padding(horizontal: 16),
if (child.type != 'article' && if (child.type != 'article' &&
(child.preload?.attachments?.isNotEmpty ?? false)) (child.body['attachments']?.isNotEmpty ?? false))
ClipRRect( ClipRRect(
borderRadius: const BorderRadius.only( borderRadius: const BorderRadius.only(
bottomLeft: Radius.circular(8), bottomLeft: Radius.circular(8),
bottomRight: Radius.circular(8), bottomRight: Radius.circular(8),
), ),
child: AttachmentList( child: AttachmentList(
data: child.preload!.attachments!, data: child.body['attachments']!,
maxHeight: 360, maxHeight: 360,
minWidth: 640, minWidth: 640,
fit: BoxFit.contain, fit: BoxFit.contain,
@ -2062,8 +2056,6 @@ class _PostFeaturedCommentState extends State<_PostFeaturedComment> {
onTap: () { onTap: () {
showModalBottomSheet( showModalBottomSheet(
context: context, context: context,
useRootNavigator: true,
isScrollControlled: true,
builder: (context) => PostCommentListPopup( builder: (context) => PostCommentListPopup(
post: widget.data, post: widget.data,
commentCount: widget.data.metric.replyCount, commentCount: widget.data.metric.replyCount,
@ -2352,7 +2344,7 @@ class _PostVideoPlayer extends StatelessWidget {
child: ClipRRect( child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(8)), borderRadius: const BorderRadius.all(Radius.circular(8)),
child: AttachmentItem( child: AttachmentItem(
data: data.preload!.video!, data: data.body['video'],
heroTag: 'post-video-${data.id}', heroTag: 'post-video-${data.id}',
), ),
), ),

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: 2.4.2+85 version: 2.4.2+86
environment: environment:
sdk: ^3.5.4 sdk: ^3.5.4