✨ Post details
This commit is contained in:
@ -2,6 +2,7 @@ import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
@ -12,6 +13,7 @@ import 'package:island/models/post.dart';
|
||||
import 'package:island/pods/config.dart';
|
||||
import 'package:island/pods/network.dart';
|
||||
import 'package:island/screens/account/me/publishers.dart';
|
||||
import 'package:island/screens/posts/detail.dart';
|
||||
import 'package:island/services/file.dart';
|
||||
import 'package:island/widgets/alert.dart';
|
||||
import 'package:island/widgets/app_scaffold.dart';
|
||||
@ -19,9 +21,34 @@ import 'package:island/widgets/content/cloud_files.dart';
|
||||
import 'package:lucide_icons/lucide_icons.dart';
|
||||
import 'package:styled_widget/styled_widget.dart';
|
||||
|
||||
@RoutePage()
|
||||
class PostEditScreen extends HookConsumerWidget {
|
||||
final int id;
|
||||
const PostEditScreen({super.key, @PathParam('id') required this.id});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final post = ref.watch(postProvider(id));
|
||||
return post.when(
|
||||
data: (post) => PostComposeScreen(originalPost: post),
|
||||
loading:
|
||||
() => AppScaffold(
|
||||
appBar: AppBar(leading: const PageBackButton()),
|
||||
body: const Center(child: CircularProgressIndicator()),
|
||||
),
|
||||
error:
|
||||
(e, _) => AppScaffold(
|
||||
appBar: AppBar(leading: const PageBackButton()),
|
||||
body: Text('Error: $e', textAlign: TextAlign.center),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@RoutePage()
|
||||
class PostComposeScreen extends HookConsumerWidget {
|
||||
const PostComposeScreen({super.key});
|
||||
final SnPost? originalPost;
|
||||
const PostComposeScreen({super.key, this.originalPost});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
@ -37,10 +64,29 @@ class PostComposeScreen extends HookConsumerWidget {
|
||||
}, [publishers]);
|
||||
|
||||
// Contains the XFile, ByteData, or SnCloudFile
|
||||
final attachments = useState<List<UniversalFile>>([]);
|
||||
final contentController = useTextEditingController();
|
||||
final titleController = useTextEditingController();
|
||||
final descriptionController = useTextEditingController();
|
||||
final attachments = useState<List<UniversalFile>>(
|
||||
originalPost?.attachments
|
||||
.map(
|
||||
(e) => UniversalFile(
|
||||
data: e,
|
||||
type: switch (e.mimeType?.split('/').firstOrNull) {
|
||||
'image' => UniversalFileType.image,
|
||||
'video' => UniversalFileType.video,
|
||||
'audio' => UniversalFileType.audio,
|
||||
_ => UniversalFileType.file,
|
||||
},
|
||||
),
|
||||
)
|
||||
.toList() ??
|
||||
[],
|
||||
);
|
||||
final contentController = useTextEditingController(
|
||||
text: originalPost?.content,
|
||||
);
|
||||
final titleController = useTextEditingController(text: originalPost?.title);
|
||||
final descriptionController = useTextEditingController(
|
||||
text: originalPost?.description,
|
||||
);
|
||||
|
||||
final submitting = useState(false);
|
||||
|
||||
@ -149,6 +195,7 @@ class PostComposeScreen extends HookConsumerWidget {
|
||||
.map((e) => e.data.id)
|
||||
.toList(),
|
||||
},
|
||||
options: Options(headers: {'X-Pub': currentPublisher.value?.name}),
|
||||
);
|
||||
if (context.mounted) {
|
||||
context.maybePop(true);
|
||||
|
67
lib/screens/posts/detail.dart
Normal file
67
lib/screens/posts/detail.dart
Normal file
@ -0,0 +1,67 @@
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:island/models/post.dart';
|
||||
import 'package:island/pods/network.dart';
|
||||
import 'package:island/widgets/app_scaffold.dart';
|
||||
import 'package:island/widgets/post/post_item.dart';
|
||||
import 'package:island/widgets/post/post_quick_reply.dart';
|
||||
import 'package:island/widgets/post/post_replies.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:styled_widget/styled_widget.dart';
|
||||
|
||||
part 'detail.g.dart';
|
||||
|
||||
@riverpod
|
||||
Future<SnPost?> post(Ref ref, int id) async {
|
||||
final client = ref.watch(apiClientProvider);
|
||||
final resp = await client.get('/posts/$id');
|
||||
return SnPost.fromJson(resp.data);
|
||||
}
|
||||
|
||||
@RoutePage()
|
||||
class PostDetailScreen extends HookConsumerWidget {
|
||||
final int id;
|
||||
const PostDetailScreen({super.key, @PathParam('id') required this.id});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final post = ref.watch(postProvider(id));
|
||||
|
||||
return AppScaffold(
|
||||
appBar: AppBar(title: const Text('Post')),
|
||||
body: post.when(
|
||||
data:
|
||||
(post) => Stack(
|
||||
fit: StackFit.expand,
|
||||
children: [
|
||||
Column(
|
||||
children: [
|
||||
PostItem(item: post!),
|
||||
const Divider(height: 1),
|
||||
Expanded(child: PostRepliesList(postId: id)),
|
||||
Gap(MediaQuery.of(context).padding.bottom),
|
||||
],
|
||||
),
|
||||
Positioned(
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: Material(
|
||||
elevation: 2,
|
||||
child: PostQuickReply(parent: post).padding(
|
||||
bottom: MediaQuery.of(context).padding.bottom,
|
||||
top: 16,
|
||||
horizontal: 16,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
loading: () => const Center(child: CircularProgressIndicator()),
|
||||
error: (e, _) => Text('Error: $e'),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
144
lib/screens/posts/detail.g.dart
Normal file
144
lib/screens/posts/detail.g.dart
Normal file
@ -0,0 +1,144 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'detail.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$postHash() => r'58de03954e284b5c04544b61ccb9cadfc45e9422';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
_SystemHash._();
|
||||
|
||||
static int combine(int hash, int value) {
|
||||
// ignore: parameter_assignments
|
||||
hash = 0x1fffffff & (hash + value);
|
||||
// ignore: parameter_assignments
|
||||
hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
|
||||
return hash ^ (hash >> 6);
|
||||
}
|
||||
|
||||
static int finish(int hash) {
|
||||
// ignore: parameter_assignments
|
||||
hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
|
||||
// ignore: parameter_assignments
|
||||
hash = hash ^ (hash >> 11);
|
||||
return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
|
||||
}
|
||||
}
|
||||
|
||||
/// See also [post].
|
||||
@ProviderFor(post)
|
||||
const postProvider = PostFamily();
|
||||
|
||||
/// See also [post].
|
||||
class PostFamily extends Family<AsyncValue<SnPost?>> {
|
||||
/// See also [post].
|
||||
const PostFamily();
|
||||
|
||||
/// See also [post].
|
||||
PostProvider call(int id) {
|
||||
return PostProvider(id);
|
||||
}
|
||||
|
||||
@override
|
||||
PostProvider getProviderOverride(covariant PostProvider provider) {
|
||||
return call(provider.id);
|
||||
}
|
||||
|
||||
static const Iterable<ProviderOrFamily>? _dependencies = null;
|
||||
|
||||
@override
|
||||
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
|
||||
|
||||
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
|
||||
|
||||
@override
|
||||
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
|
||||
_allTransitiveDependencies;
|
||||
|
||||
@override
|
||||
String? get name => r'postProvider';
|
||||
}
|
||||
|
||||
/// See also [post].
|
||||
class PostProvider extends AutoDisposeFutureProvider<SnPost?> {
|
||||
/// See also [post].
|
||||
PostProvider(int id)
|
||||
: this._internal(
|
||||
(ref) => post(ref as PostRef, id),
|
||||
from: postProvider,
|
||||
name: r'postProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product') ? null : _$postHash,
|
||||
dependencies: PostFamily._dependencies,
|
||||
allTransitiveDependencies: PostFamily._allTransitiveDependencies,
|
||||
id: id,
|
||||
);
|
||||
|
||||
PostProvider._internal(
|
||||
super._createNotifier, {
|
||||
required super.name,
|
||||
required super.dependencies,
|
||||
required super.allTransitiveDependencies,
|
||||
required super.debugGetCreateSourceHash,
|
||||
required super.from,
|
||||
required this.id,
|
||||
}) : super.internal();
|
||||
|
||||
final int id;
|
||||
|
||||
@override
|
||||
Override overrideWith(FutureOr<SnPost?> Function(PostRef provider) create) {
|
||||
return ProviderOverride(
|
||||
origin: this,
|
||||
override: PostProvider._internal(
|
||||
(ref) => create(ref as PostRef),
|
||||
from: from,
|
||||
name: null,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
debugGetCreateSourceHash: null,
|
||||
id: id,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
AutoDisposeFutureProviderElement<SnPost?> createElement() {
|
||||
return _PostProviderElement(this);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is PostProvider && other.id == id;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||
hash = _SystemHash.combine(hash, id.hashCode);
|
||||
|
||||
return _SystemHash.finish(hash);
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||
// ignore: unused_element
|
||||
mixin PostRef on AutoDisposeFutureProviderRef<SnPost?> {
|
||||
/// The parameter `id` of this provider.
|
||||
int get id;
|
||||
}
|
||||
|
||||
class _PostProviderElement extends AutoDisposeFutureProviderElement<SnPost?>
|
||||
with PostRef {
|
||||
_PostProviderElement(super.provider);
|
||||
|
||||
@override
|
||||
int get id => (origin as PostProvider).id;
|
||||
}
|
||||
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package
|
Reference in New Issue
Block a user