From 5e12a8860ce4f951da151a1c7d78189ac02672f2 Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Sat, 9 Nov 2024 12:04:03 +0800 Subject: [PATCH] :sparkles: Image attachment --- lib/main.dart | 2 + lib/providers/sn_attachment.dart | 47 + lib/screens/explore.dart | 26 +- lib/types/attachment.dart | 56 + lib/types/attachment.freezed.dart | 1048 +++++++++++++++++++ lib/types/attachment.g.dart | 97 ++ lib/types/post.dart | 14 +- lib/types/post.freezed.dart | 253 ++++- lib/types/post.g.dart | 18 +- lib/widgets/attachment/attachment_item.dart | 25 + lib/widgets/attachment/attachment_list.dart | 66 ++ lib/widgets/post/post_item.dart | 3 + 12 files changed, 1627 insertions(+), 28 deletions(-) create mode 100644 lib/providers/sn_attachment.dart create mode 100644 lib/types/attachment.dart create mode 100644 lib/types/attachment.freezed.dart create mode 100644 lib/types/attachment.g.dart create mode 100644 lib/widgets/attachment/attachment_item.dart create mode 100644 lib/widgets/attachment/attachment_list.dart diff --git a/lib/main.dart b/lib/main.dart index d1aec19..02cf71d 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:relative_time/relative_time.dart'; import 'package:responsive_framework/responsive_framework.dart'; +import 'package:surface/providers/sn_attachment.dart'; import 'package:surface/providers/sn_network.dart'; import 'package:surface/providers/theme.dart'; import 'package:surface/providers/userinfo.dart'; @@ -31,6 +32,7 @@ class SolianApp extends StatelessWidget { child: MultiProvider( providers: [ Provider(create: (_) => SnNetworkProvider()), + Provider(create: (ctx) => SnAttachmentProvider(ctx)), ChangeNotifierProvider(create: (_) => UserProvider()), ChangeNotifierProvider(create: (_) => ThemeProvider()), ], diff --git a/lib/providers/sn_attachment.dart b/lib/providers/sn_attachment.dart new file mode 100644 index 0000000..04161a9 --- /dev/null +++ b/lib/providers/sn_attachment.dart @@ -0,0 +1,47 @@ +import 'package:flutter/widgets.dart'; +import 'package:provider/provider.dart'; +import 'package:surface/providers/sn_network.dart'; +import 'package:surface/types/attachment.dart'; + +class SnAttachmentProvider { + late final SnNetworkProvider sn; + + final Map _cache = {}; + + SnAttachmentProvider(BuildContext context) { + sn = context.read(); + } + + Future getOne(String rid, {noCache = false}) async { + if (!noCache && _cache.containsKey(rid)) { + return _cache[rid]!; + } + + final resp = await sn.client.get('/cgi/uc/attachments/$rid/meta'); + final out = SnAttachment.fromJson(resp.data); + _cache[rid] = out; + + return out; + } + + Future> getMultiple(List rids, + {noCache = false}) async { + final pendingFetch = + noCache ? rids : rids.where((rid) => !_cache.containsKey(rid)).toList(); + + if (pendingFetch.isEmpty) { + return rids.map((rid) => _cache[rid]!).toList(); + } + + final resp = await sn.client.get('/cgi/uc/attachments', queryParameters: { + 'take': pendingFetch.length, + 'id': pendingFetch.join(','), + }); + final out = resp.data['data'].map((e) => SnAttachment.fromJson(e)).toList(); + + for (var i = 0; i < out.length; i++) { + _cache[pendingFetch[i]] = out[i]; + } + return rids.map((rid) => _cache[rid]!).toList(); + } +} diff --git a/lib/screens/explore.dart b/lib/screens/explore.dart index bd042a6..6ac5ed5 100644 --- a/lib/screens/explore.dart +++ b/lib/screens/explore.dart @@ -1,6 +1,7 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; +import 'package:surface/providers/sn_attachment.dart'; import 'package:surface/providers/sn_network.dart'; import 'package:surface/types/post.dart'; import 'package:surface/widgets/navigation/app_scaffold.dart'; @@ -30,10 +31,31 @@ class _ExploreScreenState extends State { 'take': 10, 'offset': _posts.length, }); + final List out = + List.from(resp.data['data']?.map((e) => SnPost.fromJson(e)) ?? []); + + Set rids = {}; + for (var i = 0; i < out.length; i++) { + rids.addAll(out[i].body['attachments']?.cast() ?? []); + } + + if (!mounted) return; + final attach = context.read(); + final attachments = await attach.getMultiple(rids.toList()); + for (var i = 0; i < out.length; i++) { + out[i] = out[i].copyWith( + preload: SnPostPreload( + attachments: attachments + .where( + (ele) => out[i].body['attachments']?.contains(ele.rid) ?? false, + ) + .toList(), + ), + ); + } _postCount = resp.data['count']; - _posts.addAll( - resp.data['data']?.map((e) => SnPost.fromJson(e)).cast() ?? []); + _posts.addAll(out); if (mounted) setState(() => _isBusy = false); } diff --git a/lib/types/attachment.dart b/lib/types/attachment.dart new file mode 100644 index 0000000..66d934b --- /dev/null +++ b/lib/types/attachment.dart @@ -0,0 +1,56 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'attachment.freezed.dart'; +part 'attachment.g.dart'; + +@freezed +class SnAttachment with _$SnAttachment { + const factory SnAttachment({ + required int id, + required DateTime createdAt, + required DateTime updatedAt, + required dynamic deletedAt, + required String rid, + required String uuid, + required int size, + required String name, + required String alt, + required String mimetype, + required String hash, + required int destination, + required int refCount, + required dynamic fileChunks, + required dynamic cleanedAt, + required Map metadata, + required bool isMature, + required bool isAnalyzed, + required bool isUploaded, + required bool isSelfRef, + required dynamic ref, + required dynamic refId, + required SnAttachmentPool? pool, + required int poolId, + required int accountId, + }) = _SnAttachment; + + factory SnAttachment.fromJson(Map json) => + _$SnAttachmentFromJson(json); +} + +@freezed +class SnAttachmentPool with _$SnAttachmentPool { + const factory SnAttachmentPool({ + required int id, + required DateTime createdAt, + required DateTime updatedAt, + required DateTime? deletedAt, + required String alias, + required String name, + required String description, + required Map config, + required int? accountId, + }) = _SnAttachmentPool; + + factory SnAttachmentPool.fromJson(Map json) => + _$SnAttachmentPoolFromJson(json); +} diff --git a/lib/types/attachment.freezed.dart b/lib/types/attachment.freezed.dart new file mode 100644 index 0000000..708e04a --- /dev/null +++ b/lib/types/attachment.freezed.dart @@ -0,0 +1,1048 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'attachment.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +SnAttachment _$SnAttachmentFromJson(Map json) { + return _SnAttachment.fromJson(json); +} + +/// @nodoc +mixin _$SnAttachment { + int get id => throw _privateConstructorUsedError; + DateTime get createdAt => throw _privateConstructorUsedError; + DateTime get updatedAt => throw _privateConstructorUsedError; + dynamic get deletedAt => throw _privateConstructorUsedError; + String get rid => throw _privateConstructorUsedError; + String get uuid => throw _privateConstructorUsedError; + int get size => throw _privateConstructorUsedError; + String get name => throw _privateConstructorUsedError; + String get alt => throw _privateConstructorUsedError; + String get mimetype => throw _privateConstructorUsedError; + String get hash => throw _privateConstructorUsedError; + int get destination => throw _privateConstructorUsedError; + int get refCount => throw _privateConstructorUsedError; + dynamic get fileChunks => throw _privateConstructorUsedError; + dynamic get cleanedAt => throw _privateConstructorUsedError; + Map get metadata => throw _privateConstructorUsedError; + bool get isMature => throw _privateConstructorUsedError; + bool get isAnalyzed => throw _privateConstructorUsedError; + bool get isUploaded => throw _privateConstructorUsedError; + bool get isSelfRef => throw _privateConstructorUsedError; + dynamic get ref => throw _privateConstructorUsedError; + dynamic get refId => throw _privateConstructorUsedError; + SnAttachmentPool? get pool => throw _privateConstructorUsedError; + int get poolId => throw _privateConstructorUsedError; + int get accountId => throw _privateConstructorUsedError; + + /// Serializes this SnAttachment to a JSON map. + Map toJson() => throw _privateConstructorUsedError; + + /// Create a copy of SnAttachment + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + $SnAttachmentCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $SnAttachmentCopyWith<$Res> { + factory $SnAttachmentCopyWith( + SnAttachment value, $Res Function(SnAttachment) then) = + _$SnAttachmentCopyWithImpl<$Res, SnAttachment>; + @useResult + $Res call( + {int id, + DateTime createdAt, + DateTime updatedAt, + dynamic deletedAt, + String rid, + String uuid, + int size, + String name, + String alt, + String mimetype, + String hash, + int destination, + int refCount, + dynamic fileChunks, + dynamic cleanedAt, + Map metadata, + bool isMature, + bool isAnalyzed, + bool isUploaded, + bool isSelfRef, + dynamic ref, + dynamic refId, + SnAttachmentPool? pool, + int poolId, + int accountId}); + + $SnAttachmentPoolCopyWith<$Res>? get pool; +} + +/// @nodoc +class _$SnAttachmentCopyWithImpl<$Res, $Val extends SnAttachment> + implements $SnAttachmentCopyWith<$Res> { + _$SnAttachmentCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of SnAttachment + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? id = null, + Object? createdAt = null, + Object? updatedAt = null, + Object? deletedAt = freezed, + Object? rid = null, + Object? uuid = null, + Object? size = null, + Object? name = null, + Object? alt = null, + Object? mimetype = null, + Object? hash = null, + Object? destination = null, + Object? refCount = null, + Object? fileChunks = freezed, + Object? cleanedAt = freezed, + Object? metadata = null, + Object? isMature = null, + Object? isAnalyzed = null, + Object? isUploaded = null, + Object? isSelfRef = null, + Object? ref = freezed, + Object? refId = freezed, + Object? pool = freezed, + Object? poolId = null, + Object? accountId = null, + }) { + return _then(_value.copyWith( + id: null == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as int, + createdAt: null == createdAt + ? _value.createdAt + : createdAt // ignore: cast_nullable_to_non_nullable + as DateTime, + updatedAt: null == updatedAt + ? _value.updatedAt + : updatedAt // ignore: cast_nullable_to_non_nullable + as DateTime, + deletedAt: freezed == deletedAt + ? _value.deletedAt + : deletedAt // ignore: cast_nullable_to_non_nullable + as dynamic, + rid: null == rid + ? _value.rid + : rid // ignore: cast_nullable_to_non_nullable + as String, + uuid: null == uuid + ? _value.uuid + : uuid // ignore: cast_nullable_to_non_nullable + as String, + size: null == size + ? _value.size + : size // ignore: cast_nullable_to_non_nullable + as int, + name: null == name + ? _value.name + : name // ignore: cast_nullable_to_non_nullable + as String, + alt: null == alt + ? _value.alt + : alt // ignore: cast_nullable_to_non_nullable + as String, + mimetype: null == mimetype + ? _value.mimetype + : mimetype // ignore: cast_nullable_to_non_nullable + as String, + hash: null == hash + ? _value.hash + : hash // ignore: cast_nullable_to_non_nullable + as String, + destination: null == destination + ? _value.destination + : destination // ignore: cast_nullable_to_non_nullable + as int, + refCount: null == refCount + ? _value.refCount + : refCount // ignore: cast_nullable_to_non_nullable + as int, + fileChunks: freezed == fileChunks + ? _value.fileChunks + : fileChunks // ignore: cast_nullable_to_non_nullable + as dynamic, + cleanedAt: freezed == cleanedAt + ? _value.cleanedAt + : cleanedAt // ignore: cast_nullable_to_non_nullable + as dynamic, + metadata: null == metadata + ? _value.metadata + : metadata // ignore: cast_nullable_to_non_nullable + as Map, + isMature: null == isMature + ? _value.isMature + : isMature // ignore: cast_nullable_to_non_nullable + as bool, + isAnalyzed: null == isAnalyzed + ? _value.isAnalyzed + : isAnalyzed // ignore: cast_nullable_to_non_nullable + as bool, + isUploaded: null == isUploaded + ? _value.isUploaded + : isUploaded // ignore: cast_nullable_to_non_nullable + as bool, + isSelfRef: null == isSelfRef + ? _value.isSelfRef + : isSelfRef // ignore: cast_nullable_to_non_nullable + as bool, + ref: freezed == ref + ? _value.ref + : ref // ignore: cast_nullable_to_non_nullable + as dynamic, + refId: freezed == refId + ? _value.refId + : refId // ignore: cast_nullable_to_non_nullable + as dynamic, + pool: freezed == pool + ? _value.pool + : pool // ignore: cast_nullable_to_non_nullable + as SnAttachmentPool?, + poolId: null == poolId + ? _value.poolId + : poolId // ignore: cast_nullable_to_non_nullable + as int, + accountId: null == accountId + ? _value.accountId + : accountId // ignore: cast_nullable_to_non_nullable + as int, + ) as $Val); + } + + /// Create a copy of SnAttachment + /// with the given fields replaced by the non-null parameter values. + @override + @pragma('vm:prefer-inline') + $SnAttachmentPoolCopyWith<$Res>? get pool { + if (_value.pool == null) { + return null; + } + + return $SnAttachmentPoolCopyWith<$Res>(_value.pool!, (value) { + return _then(_value.copyWith(pool: value) as $Val); + }); + } +} + +/// @nodoc +abstract class _$$SnAttachmentImplCopyWith<$Res> + implements $SnAttachmentCopyWith<$Res> { + factory _$$SnAttachmentImplCopyWith( + _$SnAttachmentImpl value, $Res Function(_$SnAttachmentImpl) then) = + __$$SnAttachmentImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {int id, + DateTime createdAt, + DateTime updatedAt, + dynamic deletedAt, + String rid, + String uuid, + int size, + String name, + String alt, + String mimetype, + String hash, + int destination, + int refCount, + dynamic fileChunks, + dynamic cleanedAt, + Map metadata, + bool isMature, + bool isAnalyzed, + bool isUploaded, + bool isSelfRef, + dynamic ref, + dynamic refId, + SnAttachmentPool? pool, + int poolId, + int accountId}); + + @override + $SnAttachmentPoolCopyWith<$Res>? get pool; +} + +/// @nodoc +class __$$SnAttachmentImplCopyWithImpl<$Res> + extends _$SnAttachmentCopyWithImpl<$Res, _$SnAttachmentImpl> + implements _$$SnAttachmentImplCopyWith<$Res> { + __$$SnAttachmentImplCopyWithImpl( + _$SnAttachmentImpl _value, $Res Function(_$SnAttachmentImpl) _then) + : super(_value, _then); + + /// Create a copy of SnAttachment + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? id = null, + Object? createdAt = null, + Object? updatedAt = null, + Object? deletedAt = freezed, + Object? rid = null, + Object? uuid = null, + Object? size = null, + Object? name = null, + Object? alt = null, + Object? mimetype = null, + Object? hash = null, + Object? destination = null, + Object? refCount = null, + Object? fileChunks = freezed, + Object? cleanedAt = freezed, + Object? metadata = null, + Object? isMature = null, + Object? isAnalyzed = null, + Object? isUploaded = null, + Object? isSelfRef = null, + Object? ref = freezed, + Object? refId = freezed, + Object? pool = freezed, + Object? poolId = null, + Object? accountId = null, + }) { + return _then(_$SnAttachmentImpl( + id: null == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as int, + createdAt: null == createdAt + ? _value.createdAt + : createdAt // ignore: cast_nullable_to_non_nullable + as DateTime, + updatedAt: null == updatedAt + ? _value.updatedAt + : updatedAt // ignore: cast_nullable_to_non_nullable + as DateTime, + deletedAt: freezed == deletedAt + ? _value.deletedAt + : deletedAt // ignore: cast_nullable_to_non_nullable + as dynamic, + rid: null == rid + ? _value.rid + : rid // ignore: cast_nullable_to_non_nullable + as String, + uuid: null == uuid + ? _value.uuid + : uuid // ignore: cast_nullable_to_non_nullable + as String, + size: null == size + ? _value.size + : size // ignore: cast_nullable_to_non_nullable + as int, + name: null == name + ? _value.name + : name // ignore: cast_nullable_to_non_nullable + as String, + alt: null == alt + ? _value.alt + : alt // ignore: cast_nullable_to_non_nullable + as String, + mimetype: null == mimetype + ? _value.mimetype + : mimetype // ignore: cast_nullable_to_non_nullable + as String, + hash: null == hash + ? _value.hash + : hash // ignore: cast_nullable_to_non_nullable + as String, + destination: null == destination + ? _value.destination + : destination // ignore: cast_nullable_to_non_nullable + as int, + refCount: null == refCount + ? _value.refCount + : refCount // ignore: cast_nullable_to_non_nullable + as int, + fileChunks: freezed == fileChunks + ? _value.fileChunks + : fileChunks // ignore: cast_nullable_to_non_nullable + as dynamic, + cleanedAt: freezed == cleanedAt + ? _value.cleanedAt + : cleanedAt // ignore: cast_nullable_to_non_nullable + as dynamic, + metadata: null == metadata + ? _value._metadata + : metadata // ignore: cast_nullable_to_non_nullable + as Map, + isMature: null == isMature + ? _value.isMature + : isMature // ignore: cast_nullable_to_non_nullable + as bool, + isAnalyzed: null == isAnalyzed + ? _value.isAnalyzed + : isAnalyzed // ignore: cast_nullable_to_non_nullable + as bool, + isUploaded: null == isUploaded + ? _value.isUploaded + : isUploaded // ignore: cast_nullable_to_non_nullable + as bool, + isSelfRef: null == isSelfRef + ? _value.isSelfRef + : isSelfRef // ignore: cast_nullable_to_non_nullable + as bool, + ref: freezed == ref + ? _value.ref + : ref // ignore: cast_nullable_to_non_nullable + as dynamic, + refId: freezed == refId + ? _value.refId + : refId // ignore: cast_nullable_to_non_nullable + as dynamic, + pool: freezed == pool + ? _value.pool + : pool // ignore: cast_nullable_to_non_nullable + as SnAttachmentPool?, + poolId: null == poolId + ? _value.poolId + : poolId // ignore: cast_nullable_to_non_nullable + as int, + accountId: null == accountId + ? _value.accountId + : accountId // ignore: cast_nullable_to_non_nullable + as int, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$SnAttachmentImpl implements _SnAttachment { + const _$SnAttachmentImpl( + {required this.id, + required this.createdAt, + required this.updatedAt, + required this.deletedAt, + required this.rid, + required this.uuid, + required this.size, + required this.name, + required this.alt, + required this.mimetype, + required this.hash, + required this.destination, + required this.refCount, + required this.fileChunks, + required this.cleanedAt, + required final Map metadata, + required this.isMature, + required this.isAnalyzed, + required this.isUploaded, + required this.isSelfRef, + required this.ref, + required this.refId, + required this.pool, + required this.poolId, + required this.accountId}) + : _metadata = metadata; + + factory _$SnAttachmentImpl.fromJson(Map json) => + _$$SnAttachmentImplFromJson(json); + + @override + final int id; + @override + final DateTime createdAt; + @override + final DateTime updatedAt; + @override + final dynamic deletedAt; + @override + final String rid; + @override + final String uuid; + @override + final int size; + @override + final String name; + @override + final String alt; + @override + final String mimetype; + @override + final String hash; + @override + final int destination; + @override + final int refCount; + @override + final dynamic fileChunks; + @override + final dynamic cleanedAt; + final Map _metadata; + @override + Map get metadata { + if (_metadata is EqualUnmodifiableMapView) return _metadata; + // ignore: implicit_dynamic_type + return EqualUnmodifiableMapView(_metadata); + } + + @override + final bool isMature; + @override + final bool isAnalyzed; + @override + final bool isUploaded; + @override + final bool isSelfRef; + @override + final dynamic ref; + @override + final dynamic refId; + @override + final SnAttachmentPool? pool; + @override + final int poolId; + @override + final int accountId; + + @override + 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, fileChunks: $fileChunks, cleanedAt: $cleanedAt, metadata: $metadata, isMature: $isMature, isAnalyzed: $isAnalyzed, isUploaded: $isUploaded, isSelfRef: $isSelfRef, ref: $ref, refId: $refId, pool: $pool, poolId: $poolId, accountId: $accountId)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$SnAttachmentImpl && + (identical(other.id, id) || other.id == id) && + (identical(other.createdAt, createdAt) || + other.createdAt == createdAt) && + (identical(other.updatedAt, updatedAt) || + other.updatedAt == updatedAt) && + const DeepCollectionEquality().equals(other.deletedAt, deletedAt) && + (identical(other.rid, rid) || other.rid == rid) && + (identical(other.uuid, uuid) || other.uuid == uuid) && + (identical(other.size, size) || other.size == size) && + (identical(other.name, name) || other.name == name) && + (identical(other.alt, alt) || other.alt == alt) && + (identical(other.mimetype, mimetype) || + other.mimetype == mimetype) && + (identical(other.hash, hash) || other.hash == hash) && + (identical(other.destination, destination) || + other.destination == destination) && + (identical(other.refCount, refCount) || + other.refCount == refCount) && + const DeepCollectionEquality() + .equals(other.fileChunks, fileChunks) && + const DeepCollectionEquality().equals(other.cleanedAt, cleanedAt) && + const DeepCollectionEquality().equals(other._metadata, _metadata) && + (identical(other.isMature, isMature) || + other.isMature == isMature) && + (identical(other.isAnalyzed, isAnalyzed) || + other.isAnalyzed == isAnalyzed) && + (identical(other.isUploaded, isUploaded) || + other.isUploaded == isUploaded) && + (identical(other.isSelfRef, isSelfRef) || + other.isSelfRef == isSelfRef) && + const DeepCollectionEquality().equals(other.ref, ref) && + const DeepCollectionEquality().equals(other.refId, refId) && + (identical(other.pool, pool) || other.pool == pool) && + (identical(other.poolId, poolId) || other.poolId == poolId) && + (identical(other.accountId, accountId) || + other.accountId == accountId)); + } + + @JsonKey(includeFromJson: false, includeToJson: false) + @override + int get hashCode => Object.hashAll([ + runtimeType, + id, + createdAt, + updatedAt, + const DeepCollectionEquality().hash(deletedAt), + rid, + uuid, + size, + name, + alt, + mimetype, + hash, + destination, + refCount, + const DeepCollectionEquality().hash(fileChunks), + const DeepCollectionEquality().hash(cleanedAt), + const DeepCollectionEquality().hash(_metadata), + isMature, + isAnalyzed, + isUploaded, + isSelfRef, + const DeepCollectionEquality().hash(ref), + const DeepCollectionEquality().hash(refId), + pool, + poolId, + accountId + ]); + + /// Create a copy of SnAttachment + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$SnAttachmentImplCopyWith<_$SnAttachmentImpl> get copyWith => + __$$SnAttachmentImplCopyWithImpl<_$SnAttachmentImpl>(this, _$identity); + + @override + Map toJson() { + return _$$SnAttachmentImplToJson( + this, + ); + } +} + +abstract class _SnAttachment implements SnAttachment { + const factory _SnAttachment( + {required final int id, + required final DateTime createdAt, + required final DateTime updatedAt, + required final dynamic deletedAt, + required final String rid, + required final String uuid, + required final int size, + required final String name, + required final String alt, + required final String mimetype, + required final String hash, + required final int destination, + required final int refCount, + required final dynamic fileChunks, + required final dynamic cleanedAt, + required final Map metadata, + required final bool isMature, + required final bool isAnalyzed, + required final bool isUploaded, + required final bool isSelfRef, + required final dynamic ref, + required final dynamic refId, + required final SnAttachmentPool? pool, + required final int poolId, + required final int accountId}) = _$SnAttachmentImpl; + + factory _SnAttachment.fromJson(Map json) = + _$SnAttachmentImpl.fromJson; + + @override + int get id; + @override + DateTime get createdAt; + @override + DateTime get updatedAt; + @override + dynamic get deletedAt; + @override + String get rid; + @override + String get uuid; + @override + int get size; + @override + String get name; + @override + String get alt; + @override + String get mimetype; + @override + String get hash; + @override + int get destination; + @override + int get refCount; + @override + dynamic get fileChunks; + @override + dynamic get cleanedAt; + @override + Map get metadata; + @override + bool get isMature; + @override + bool get isAnalyzed; + @override + bool get isUploaded; + @override + bool get isSelfRef; + @override + dynamic get ref; + @override + dynamic get refId; + @override + SnAttachmentPool? get pool; + @override + int get poolId; + @override + int get accountId; + + /// Create a copy of SnAttachment + /// with the given fields replaced by the non-null parameter values. + @override + @JsonKey(includeFromJson: false, includeToJson: false) + _$$SnAttachmentImplCopyWith<_$SnAttachmentImpl> get copyWith => + throw _privateConstructorUsedError; +} + +SnAttachmentPool _$SnAttachmentPoolFromJson(Map json) { + return _SnAttachmentPool.fromJson(json); +} + +/// @nodoc +mixin _$SnAttachmentPool { + int get id => throw _privateConstructorUsedError; + DateTime get createdAt => throw _privateConstructorUsedError; + DateTime get updatedAt => throw _privateConstructorUsedError; + DateTime? get deletedAt => throw _privateConstructorUsedError; + String get alias => throw _privateConstructorUsedError; + String get name => throw _privateConstructorUsedError; + String get description => throw _privateConstructorUsedError; + Map get config => throw _privateConstructorUsedError; + int? get accountId => throw _privateConstructorUsedError; + + /// Serializes this SnAttachmentPool to a JSON map. + Map toJson() => throw _privateConstructorUsedError; + + /// Create a copy of SnAttachmentPool + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + $SnAttachmentPoolCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $SnAttachmentPoolCopyWith<$Res> { + factory $SnAttachmentPoolCopyWith( + SnAttachmentPool value, $Res Function(SnAttachmentPool) then) = + _$SnAttachmentPoolCopyWithImpl<$Res, SnAttachmentPool>; + @useResult + $Res call( + {int id, + DateTime createdAt, + DateTime updatedAt, + DateTime? deletedAt, + String alias, + String name, + String description, + Map config, + int? accountId}); +} + +/// @nodoc +class _$SnAttachmentPoolCopyWithImpl<$Res, $Val extends SnAttachmentPool> + implements $SnAttachmentPoolCopyWith<$Res> { + _$SnAttachmentPoolCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of SnAttachmentPool + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? id = null, + Object? createdAt = null, + Object? updatedAt = null, + Object? deletedAt = freezed, + Object? alias = null, + Object? name = null, + Object? description = null, + Object? config = null, + Object? accountId = freezed, + }) { + return _then(_value.copyWith( + id: null == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as int, + createdAt: null == createdAt + ? _value.createdAt + : createdAt // ignore: cast_nullable_to_non_nullable + as DateTime, + updatedAt: null == updatedAt + ? _value.updatedAt + : updatedAt // ignore: cast_nullable_to_non_nullable + as DateTime, + deletedAt: freezed == deletedAt + ? _value.deletedAt + : deletedAt // ignore: cast_nullable_to_non_nullable + as DateTime?, + alias: null == alias + ? _value.alias + : alias // ignore: cast_nullable_to_non_nullable + as String, + name: null == name + ? _value.name + : name // ignore: cast_nullable_to_non_nullable + as String, + description: null == description + ? _value.description + : description // ignore: cast_nullable_to_non_nullable + as String, + config: null == config + ? _value.config + : config // ignore: cast_nullable_to_non_nullable + as Map, + accountId: freezed == accountId + ? _value.accountId + : accountId // ignore: cast_nullable_to_non_nullable + as int?, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$SnAttachmentPoolImplCopyWith<$Res> + implements $SnAttachmentPoolCopyWith<$Res> { + factory _$$SnAttachmentPoolImplCopyWith(_$SnAttachmentPoolImpl value, + $Res Function(_$SnAttachmentPoolImpl) then) = + __$$SnAttachmentPoolImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {int id, + DateTime createdAt, + DateTime updatedAt, + DateTime? deletedAt, + String alias, + String name, + String description, + Map config, + int? accountId}); +} + +/// @nodoc +class __$$SnAttachmentPoolImplCopyWithImpl<$Res> + extends _$SnAttachmentPoolCopyWithImpl<$Res, _$SnAttachmentPoolImpl> + implements _$$SnAttachmentPoolImplCopyWith<$Res> { + __$$SnAttachmentPoolImplCopyWithImpl(_$SnAttachmentPoolImpl _value, + $Res Function(_$SnAttachmentPoolImpl) _then) + : super(_value, _then); + + /// Create a copy of SnAttachmentPool + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? id = null, + Object? createdAt = null, + Object? updatedAt = null, + Object? deletedAt = freezed, + Object? alias = null, + Object? name = null, + Object? description = null, + Object? config = null, + Object? accountId = freezed, + }) { + return _then(_$SnAttachmentPoolImpl( + id: null == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as int, + createdAt: null == createdAt + ? _value.createdAt + : createdAt // ignore: cast_nullable_to_non_nullable + as DateTime, + updatedAt: null == updatedAt + ? _value.updatedAt + : updatedAt // ignore: cast_nullable_to_non_nullable + as DateTime, + deletedAt: freezed == deletedAt + ? _value.deletedAt + : deletedAt // ignore: cast_nullable_to_non_nullable + as DateTime?, + alias: null == alias + ? _value.alias + : alias // ignore: cast_nullable_to_non_nullable + as String, + name: null == name + ? _value.name + : name // ignore: cast_nullable_to_non_nullable + as String, + description: null == description + ? _value.description + : description // ignore: cast_nullable_to_non_nullable + as String, + config: null == config + ? _value._config + : config // ignore: cast_nullable_to_non_nullable + as Map, + accountId: freezed == accountId + ? _value.accountId + : accountId // ignore: cast_nullable_to_non_nullable + as int?, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$SnAttachmentPoolImpl implements _SnAttachmentPool { + const _$SnAttachmentPoolImpl( + {required this.id, + required this.createdAt, + required this.updatedAt, + required this.deletedAt, + required this.alias, + required this.name, + required this.description, + required final Map config, + required this.accountId}) + : _config = config; + + factory _$SnAttachmentPoolImpl.fromJson(Map json) => + _$$SnAttachmentPoolImplFromJson(json); + + @override + final int id; + @override + final DateTime createdAt; + @override + final DateTime updatedAt; + @override + final DateTime? deletedAt; + @override + final String alias; + @override + final String name; + @override + final String description; + final Map _config; + @override + Map get config { + if (_config is EqualUnmodifiableMapView) return _config; + // ignore: implicit_dynamic_type + return EqualUnmodifiableMapView(_config); + } + + @override + final int? accountId; + + @override + String toString() { + return 'SnAttachmentPool(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, alias: $alias, name: $name, description: $description, config: $config, accountId: $accountId)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$SnAttachmentPoolImpl && + (identical(other.id, id) || other.id == id) && + (identical(other.createdAt, createdAt) || + other.createdAt == createdAt) && + (identical(other.updatedAt, updatedAt) || + other.updatedAt == updatedAt) && + (identical(other.deletedAt, deletedAt) || + other.deletedAt == deletedAt) && + (identical(other.alias, alias) || other.alias == alias) && + (identical(other.name, name) || other.name == name) && + (identical(other.description, description) || + other.description == description) && + const DeepCollectionEquality().equals(other._config, _config) && + (identical(other.accountId, accountId) || + other.accountId == accountId)); + } + + @JsonKey(includeFromJson: false, includeToJson: false) + @override + int get hashCode => Object.hash( + runtimeType, + id, + createdAt, + updatedAt, + deletedAt, + alias, + name, + description, + const DeepCollectionEquality().hash(_config), + accountId); + + /// Create a copy of SnAttachmentPool + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$SnAttachmentPoolImplCopyWith<_$SnAttachmentPoolImpl> get copyWith => + __$$SnAttachmentPoolImplCopyWithImpl<_$SnAttachmentPoolImpl>( + this, _$identity); + + @override + Map toJson() { + return _$$SnAttachmentPoolImplToJson( + this, + ); + } +} + +abstract class _SnAttachmentPool implements SnAttachmentPool { + const factory _SnAttachmentPool( + {required final int id, + required final DateTime createdAt, + required final DateTime updatedAt, + required final DateTime? deletedAt, + required final String alias, + required final String name, + required final String description, + required final Map config, + required final int? accountId}) = _$SnAttachmentPoolImpl; + + factory _SnAttachmentPool.fromJson(Map json) = + _$SnAttachmentPoolImpl.fromJson; + + @override + int get id; + @override + DateTime get createdAt; + @override + DateTime get updatedAt; + @override + DateTime? get deletedAt; + @override + String get alias; + @override + String get name; + @override + String get description; + @override + Map get config; + @override + int? get accountId; + + /// Create a copy of SnAttachmentPool + /// with the given fields replaced by the non-null parameter values. + @override + @JsonKey(includeFromJson: false, includeToJson: false) + _$$SnAttachmentPoolImplCopyWith<_$SnAttachmentPoolImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/types/attachment.g.dart b/lib/types/attachment.g.dart new file mode 100644 index 0000000..b38362c --- /dev/null +++ b/lib/types/attachment.g.dart @@ -0,0 +1,97 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'attachment.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_$SnAttachmentImpl _$$SnAttachmentImplFromJson(Map json) => + _$SnAttachmentImpl( + id: (json['id'] as num).toInt(), + createdAt: DateTime.parse(json['created_at'] as String), + updatedAt: DateTime.parse(json['updated_at'] as String), + deletedAt: json['deleted_at'], + rid: json['rid'] as String, + uuid: json['uuid'] as String, + size: (json['size'] as num).toInt(), + name: json['name'] as String, + alt: json['alt'] as String, + mimetype: json['mimetype'] as String, + hash: json['hash'] as String, + destination: (json['destination'] as num).toInt(), + refCount: (json['ref_count'] as num).toInt(), + fileChunks: json['file_chunks'], + cleanedAt: json['cleaned_at'], + metadata: json['metadata'] as Map, + isMature: json['is_mature'] as bool, + isAnalyzed: json['is_analyzed'] as bool, + isUploaded: json['is_uploaded'] as bool, + isSelfRef: json['is_self_ref'] as bool, + ref: json['ref'], + refId: json['ref_id'], + pool: json['pool'] == null + ? null + : SnAttachmentPool.fromJson(json['pool'] as Map), + poolId: (json['pool_id'] as num).toInt(), + accountId: (json['account_id'] as num).toInt(), + ); + +Map _$$SnAttachmentImplToJson(_$SnAttachmentImpl instance) => + { + 'id': instance.id, + 'created_at': instance.createdAt.toIso8601String(), + 'updated_at': instance.updatedAt.toIso8601String(), + 'deleted_at': instance.deletedAt, + 'rid': instance.rid, + 'uuid': instance.uuid, + 'size': instance.size, + 'name': instance.name, + 'alt': instance.alt, + 'mimetype': instance.mimetype, + 'hash': instance.hash, + 'destination': instance.destination, + 'ref_count': instance.refCount, + 'file_chunks': instance.fileChunks, + 'cleaned_at': instance.cleanedAt, + 'metadata': instance.metadata, + 'is_mature': instance.isMature, + 'is_analyzed': instance.isAnalyzed, + 'is_uploaded': instance.isUploaded, + 'is_self_ref': instance.isSelfRef, + 'ref': instance.ref, + 'ref_id': instance.refId, + 'pool': instance.pool?.toJson(), + 'pool_id': instance.poolId, + 'account_id': instance.accountId, + }; + +_$SnAttachmentPoolImpl _$$SnAttachmentPoolImplFromJson( + Map json) => + _$SnAttachmentPoolImpl( + id: (json['id'] as num).toInt(), + createdAt: DateTime.parse(json['created_at'] as String), + updatedAt: DateTime.parse(json['updated_at'] as String), + deletedAt: json['deleted_at'] == null + ? null + : DateTime.parse(json['deleted_at'] as String), + alias: json['alias'] as String, + name: json['name'] as String, + description: json['description'] as String, + config: json['config'] as Map, + accountId: (json['account_id'] as num?)?.toInt(), + ); + +Map _$$SnAttachmentPoolImplToJson( + _$SnAttachmentPoolImpl instance) => + { + 'id': instance.id, + 'created_at': instance.createdAt.toIso8601String(), + 'updated_at': instance.updatedAt.toIso8601String(), + 'deleted_at': instance.deletedAt?.toIso8601String(), + 'alias': instance.alias, + 'name': instance.name, + 'description': instance.description, + 'config': instance.config, + 'account_id': instance.accountId, + }; diff --git a/lib/types/post.dart b/lib/types/post.dart index 18213c4..8e864e6 100644 --- a/lib/types/post.dart +++ b/lib/types/post.dart @@ -1,4 +1,5 @@ import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:surface/types/attachment.dart'; part 'post.freezed.dart'; part 'post.g.dart'; @@ -11,7 +12,7 @@ class SnPost with _$SnPost { required DateTime updatedAt, required DateTime? deletedAt, required String type, - required dynamic body, + required Map body, required String language, required String? alias, required String? aliasPrefix, @@ -39,11 +40,22 @@ class SnPost with _$SnPost { required int publisherId, required SnPublisher publisher, required SnMetric metric, + SnPostPreload? preload, }) = _SnPost; factory SnPost.fromJson(Map json) => _$SnPostFromJson(json); } +@freezed +class SnPostPreload with _$SnPostPreload { + const factory SnPostPreload({ + required List? attachments, + }) = _SnPostPreload; + + factory SnPostPreload.fromJson(Map json) => + _$SnPostPreloadFromJson(json); +} + @freezed class SnBody with _$SnBody { const factory SnBody({ diff --git a/lib/types/post.freezed.dart b/lib/types/post.freezed.dart index cf06628..eb03e9a 100644 --- a/lib/types/post.freezed.dart +++ b/lib/types/post.freezed.dart @@ -25,7 +25,7 @@ mixin _$SnPost { DateTime get updatedAt => throw _privateConstructorUsedError; DateTime? get deletedAt => throw _privateConstructorUsedError; String get type => throw _privateConstructorUsedError; - dynamic get body => throw _privateConstructorUsedError; + Map get body => throw _privateConstructorUsedError; String get language => throw _privateConstructorUsedError; String? get alias => throw _privateConstructorUsedError; String? get aliasPrefix => throw _privateConstructorUsedError; @@ -53,6 +53,7 @@ mixin _$SnPost { int get publisherId => throw _privateConstructorUsedError; SnPublisher get publisher => throw _privateConstructorUsedError; SnMetric get metric => throw _privateConstructorUsedError; + SnPostPreload? get preload => throw _privateConstructorUsedError; /// Serializes this SnPost to a JSON map. Map toJson() => throw _privateConstructorUsedError; @@ -74,7 +75,7 @@ abstract class $SnPostCopyWith<$Res> { DateTime updatedAt, DateTime? deletedAt, String type, - dynamic body, + Map body, String language, String? alias, String? aliasPrefix, @@ -101,10 +102,12 @@ abstract class $SnPostCopyWith<$Res> { dynamic realm, int publisherId, SnPublisher publisher, - SnMetric metric}); + SnMetric metric, + SnPostPreload? preload}); $SnPublisherCopyWith<$Res> get publisher; $SnMetricCopyWith<$Res> get metric; + $SnPostPreloadCopyWith<$Res>? get preload; } /// @nodoc @@ -127,7 +130,7 @@ class _$SnPostCopyWithImpl<$Res, $Val extends SnPost> Object? updatedAt = null, Object? deletedAt = freezed, Object? type = null, - Object? body = freezed, + Object? body = null, Object? language = null, Object? alias = freezed, Object? aliasPrefix = freezed, @@ -155,6 +158,7 @@ class _$SnPostCopyWithImpl<$Res, $Val extends SnPost> Object? publisherId = null, Object? publisher = null, Object? metric = null, + Object? preload = freezed, }) { return _then(_value.copyWith( id: null == id @@ -177,10 +181,10 @@ class _$SnPostCopyWithImpl<$Res, $Val extends SnPost> ? _value.type : type // ignore: cast_nullable_to_non_nullable as String, - body: freezed == body + body: null == body ? _value.body : body // ignore: cast_nullable_to_non_nullable - as dynamic, + as Map, language: null == language ? _value.language : language // ignore: cast_nullable_to_non_nullable @@ -289,6 +293,10 @@ class _$SnPostCopyWithImpl<$Res, $Val extends SnPost> ? _value.metric : metric // ignore: cast_nullable_to_non_nullable as SnMetric, + preload: freezed == preload + ? _value.preload + : preload // ignore: cast_nullable_to_non_nullable + as SnPostPreload?, ) as $Val); } @@ -311,6 +319,20 @@ class _$SnPostCopyWithImpl<$Res, $Val extends SnPost> return _then(_value.copyWith(metric: value) as $Val); }); } + + /// 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 (_value.preload == null) { + return null; + } + + return $SnPostPreloadCopyWith<$Res>(_value.preload!, (value) { + return _then(_value.copyWith(preload: value) as $Val); + }); + } } /// @nodoc @@ -326,7 +348,7 @@ abstract class _$$SnPostImplCopyWith<$Res> implements $SnPostCopyWith<$Res> { DateTime updatedAt, DateTime? deletedAt, String type, - dynamic body, + Map body, String language, String? alias, String? aliasPrefix, @@ -353,12 +375,15 @@ abstract class _$$SnPostImplCopyWith<$Res> implements $SnPostCopyWith<$Res> { dynamic realm, int publisherId, SnPublisher publisher, - SnMetric metric}); + SnMetric metric, + SnPostPreload? preload}); @override $SnPublisherCopyWith<$Res> get publisher; @override $SnMetricCopyWith<$Res> get metric; + @override + $SnPostPreloadCopyWith<$Res>? get preload; } /// @nodoc @@ -379,7 +404,7 @@ class __$$SnPostImplCopyWithImpl<$Res> Object? updatedAt = null, Object? deletedAt = freezed, Object? type = null, - Object? body = freezed, + Object? body = null, Object? language = null, Object? alias = freezed, Object? aliasPrefix = freezed, @@ -407,6 +432,7 @@ class __$$SnPostImplCopyWithImpl<$Res> Object? publisherId = null, Object? publisher = null, Object? metric = null, + Object? preload = freezed, }) { return _then(_$SnPostImpl( id: null == id @@ -429,10 +455,10 @@ class __$$SnPostImplCopyWithImpl<$Res> ? _value.type : type // ignore: cast_nullable_to_non_nullable as String, - body: freezed == body - ? _value.body + body: null == body + ? _value._body : body // ignore: cast_nullable_to_non_nullable - as dynamic, + as Map, language: null == language ? _value.language : language // ignore: cast_nullable_to_non_nullable @@ -541,6 +567,10 @@ class __$$SnPostImplCopyWithImpl<$Res> ? _value.metric : metric // ignore: cast_nullable_to_non_nullable as SnMetric, + preload: freezed == preload + ? _value.preload + : preload // ignore: cast_nullable_to_non_nullable + as SnPostPreload?, )); } } @@ -554,7 +584,7 @@ class _$SnPostImpl implements _SnPost { required this.updatedAt, required this.deletedAt, required this.type, - required this.body, + required final Map body, required this.language, required this.alias, required this.aliasPrefix, @@ -581,8 +611,10 @@ class _$SnPostImpl implements _SnPost { required this.realm, required this.publisherId, required this.publisher, - required this.metric}) - : _tags = tags, + required this.metric, + this.preload}) + : _body = body, + _tags = tags, _categories = categories; factory _$SnPostImpl.fromJson(Map json) => @@ -598,8 +630,14 @@ class _$SnPostImpl implements _SnPost { final DateTime? deletedAt; @override final String type; + final Map _body; @override - final dynamic body; + Map get body { + if (_body is EqualUnmodifiableMapView) return _body; + // ignore: implicit_dynamic_type + return EqualUnmodifiableMapView(_body); + } + @override final String language; @override @@ -666,10 +704,12 @@ class _$SnPostImpl implements _SnPost { final SnPublisher publisher; @override final SnMetric metric; + @override + final SnPostPreload? preload; @override 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, reactions: $reactions, replies: $replies, replyId: $replyId, repostId: $repostId, 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, realmId: $realmId, realm: $realm, publisherId: $publisherId, publisher: $publisher, metric: $metric)'; + return 'SnPost(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, type: $type, body: $body, language: $language, alias: $alias, aliasPrefix: $aliasPrefix, tags: $tags, categories: $categories, reactions: $reactions, replies: $replies, replyId: $replyId, repostId: $repostId, 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, realmId: $realmId, realm: $realm, publisherId: $publisherId, publisher: $publisher, metric: $metric, preload: $preload)'; } @override @@ -685,7 +725,7 @@ class _$SnPostImpl implements _SnPost { (identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt) && (identical(other.type, type) || other.type == type) && - const DeepCollectionEquality().equals(other.body, body) && + const DeepCollectionEquality().equals(other._body, _body) && (identical(other.language, language) || other.language == language) && (identical(other.alias, alias) || other.alias == alias) && @@ -727,7 +767,8 @@ class _$SnPostImpl implements _SnPost { other.publisherId == publisherId) && (identical(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) @@ -739,7 +780,7 @@ class _$SnPostImpl implements _SnPost { updatedAt, deletedAt, type, - const DeepCollectionEquality().hash(body), + const DeepCollectionEquality().hash(_body), language, alias, aliasPrefix, @@ -766,7 +807,8 @@ class _$SnPostImpl implements _SnPost { const DeepCollectionEquality().hash(realm), publisherId, publisher, - metric + metric, + preload ]); /// Create a copy of SnPost @@ -792,7 +834,7 @@ abstract class _SnPost implements SnPost { required final DateTime updatedAt, required final DateTime? deletedAt, required final String type, - required final dynamic body, + required final Map body, required final String language, required final String? alias, required final String? aliasPrefix, @@ -819,7 +861,8 @@ abstract class _SnPost implements SnPost { required final dynamic realm, required final int publisherId, required final SnPublisher publisher, - required final SnMetric metric}) = _$SnPostImpl; + required final SnMetric metric, + final SnPostPreload? preload}) = _$SnPostImpl; factory _SnPost.fromJson(Map json) = _$SnPostImpl.fromJson; @@ -834,7 +877,7 @@ abstract class _SnPost implements SnPost { @override String get type; @override - dynamic get body; + Map get body; @override String get language; @override @@ -889,6 +932,8 @@ abstract class _SnPost implements SnPost { SnPublisher get publisher; @override SnMetric get metric; + @override + SnPostPreload? get preload; /// Create a copy of SnPost /// with the given fields replaced by the non-null parameter values. @@ -898,6 +943,166 @@ abstract class _SnPost implements SnPost { throw _privateConstructorUsedError; } +SnPostPreload _$SnPostPreloadFromJson(Map json) { + return _SnPostPreload.fromJson(json); +} + +/// @nodoc +mixin _$SnPostPreload { + List? get attachments => throw _privateConstructorUsedError; + + /// Serializes this SnPostPreload to a JSON map. + Map toJson() => throw _privateConstructorUsedError; + + /// Create a copy of SnPostPreload + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + $SnPostPreloadCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $SnPostPreloadCopyWith<$Res> { + factory $SnPostPreloadCopyWith( + SnPostPreload value, $Res Function(SnPostPreload) then) = + _$SnPostPreloadCopyWithImpl<$Res, SnPostPreload>; + @useResult + $Res call({List? attachments}); +} + +/// @nodoc +class _$SnPostPreloadCopyWithImpl<$Res, $Val extends SnPostPreload> + implements $SnPostPreloadCopyWith<$Res> { + _$SnPostPreloadCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of SnPostPreload + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? attachments = freezed, + }) { + return _then(_value.copyWith( + attachments: freezed == attachments + ? _value.attachments + : attachments // ignore: cast_nullable_to_non_nullable + as List?, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$SnPostPreloadImplCopyWith<$Res> + implements $SnPostPreloadCopyWith<$Res> { + factory _$$SnPostPreloadImplCopyWith( + _$SnPostPreloadImpl value, $Res Function(_$SnPostPreloadImpl) then) = + __$$SnPostPreloadImplCopyWithImpl<$Res>; + @override + @useResult + $Res call({List? attachments}); +} + +/// @nodoc +class __$$SnPostPreloadImplCopyWithImpl<$Res> + extends _$SnPostPreloadCopyWithImpl<$Res, _$SnPostPreloadImpl> + implements _$$SnPostPreloadImplCopyWith<$Res> { + __$$SnPostPreloadImplCopyWithImpl( + _$SnPostPreloadImpl _value, $Res Function(_$SnPostPreloadImpl) _then) + : super(_value, _then); + + /// Create a copy of SnPostPreload + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? attachments = freezed, + }) { + return _then(_$SnPostPreloadImpl( + attachments: freezed == attachments + ? _value._attachments + : attachments // ignore: cast_nullable_to_non_nullable + as List?, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$SnPostPreloadImpl implements _SnPostPreload { + const _$SnPostPreloadImpl({required final List? attachments}) + : _attachments = attachments; + + factory _$SnPostPreloadImpl.fromJson(Map json) => + _$$SnPostPreloadImplFromJson(json); + + final List? _attachments; + @override + List? get attachments { + final value = _attachments; + if (value == null) return null; + if (_attachments is EqualUnmodifiableListView) return _attachments; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(value); + } + + @override + String toString() { + return 'SnPostPreload(attachments: $attachments)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$SnPostPreloadImpl && + const DeepCollectionEquality() + .equals(other._attachments, _attachments)); + } + + @JsonKey(includeFromJson: false, includeToJson: false) + @override + int get hashCode => Object.hash( + runtimeType, const DeepCollectionEquality().hash(_attachments)); + + /// Create a copy of SnPostPreload + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$SnPostPreloadImplCopyWith<_$SnPostPreloadImpl> get copyWith => + __$$SnPostPreloadImplCopyWithImpl<_$SnPostPreloadImpl>(this, _$identity); + + @override + Map toJson() { + return _$$SnPostPreloadImplToJson( + this, + ); + } +} + +abstract class _SnPostPreload implements SnPostPreload { + const factory _SnPostPreload( + {required final List? attachments}) = _$SnPostPreloadImpl; + + factory _SnPostPreload.fromJson(Map json) = + _$SnPostPreloadImpl.fromJson; + + @override + List? get attachments; + + /// Create a copy of SnPostPreload + /// with the given fields replaced by the non-null parameter values. + @override + @JsonKey(includeFromJson: false, includeToJson: false) + _$$SnPostPreloadImplCopyWith<_$SnPostPreloadImpl> get copyWith => + throw _privateConstructorUsedError; +} + SnBody _$SnBodyFromJson(Map json) { return _SnBody.fromJson(json); } diff --git a/lib/types/post.g.dart b/lib/types/post.g.dart index 61c8766..6646cfd 100644 --- a/lib/types/post.g.dart +++ b/lib/types/post.g.dart @@ -14,7 +14,7 @@ _$SnPostImpl _$$SnPostImplFromJson(Map json) => _$SnPostImpl( ? null : DateTime.parse(json['deleted_at'] as String), type: json['type'] as String, - body: json['body'], + body: json['body'] as Map, language: json['language'] as String, alias: json['alias'] as String?, aliasPrefix: json['alias_prefix'] as String?, @@ -49,6 +49,9 @@ _$SnPostImpl _$$SnPostImplFromJson(Map json) => _$SnPostImpl( publisher: SnPublisher.fromJson(json['publisher'] as Map), metric: SnMetric.fromJson(json['metric'] as Map), + preload: json['preload'] == null + ? null + : SnPostPreload.fromJson(json['preload'] as Map), ); Map _$$SnPostImplToJson(_$SnPostImpl instance) => @@ -86,6 +89,19 @@ Map _$$SnPostImplToJson(_$SnPostImpl instance) => 'publisher_id': instance.publisherId, 'publisher': instance.publisher.toJson(), 'metric': instance.metric.toJson(), + 'preload': instance.preload?.toJson(), + }; + +_$SnPostPreloadImpl _$$SnPostPreloadImplFromJson(Map json) => + _$SnPostPreloadImpl( + attachments: (json['attachments'] as List?) + ?.map((e) => SnAttachment.fromJson(e as Map)) + .toList(), + ); + +Map _$$SnPostPreloadImplToJson(_$SnPostPreloadImpl instance) => + { + 'attachments': instance.attachments?.map((e) => e.toJson()).toList(), }; _$SnBodyImpl _$$SnBodyImplFromJson(Map json) => _$SnBodyImpl( diff --git a/lib/widgets/attachment/attachment_item.dart b/lib/widgets/attachment/attachment_item.dart new file mode 100644 index 0000000..b895e0f --- /dev/null +++ b/lib/widgets/attachment/attachment_item.dart @@ -0,0 +1,25 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:surface/providers/sn_network.dart'; +import 'package:surface/types/attachment.dart'; +import 'package:surface/widgets/universal_image.dart'; + +class AttachmentItem extends StatelessWidget { + final SnAttachment data; + const AttachmentItem({super.key, required this.data}); + + @override + Widget build(BuildContext context) { + final tp = data.mimetype.split('/').firstOrNull; + final sn = context.read(); + switch (tp) { + case 'image': + return AspectRatio( + aspectRatio: data.metadata['ratio']?.toDouble(), + child: UniversalImage(sn.getAttachmentUrl(data.rid)), + ); + default: + return const Placeholder(); + } + } +} diff --git a/lib/widgets/attachment/attachment_list.dart b/lib/widgets/attachment/attachment_list.dart new file mode 100644 index 0000000..ebcc026 --- /dev/null +++ b/lib/widgets/attachment/attachment_list.dart @@ -0,0 +1,66 @@ +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:gap/gap.dart'; +import 'package:surface/types/attachment.dart'; +import 'package:surface/widgets/attachment/attachment_item.dart'; + +class AttachmentList extends StatelessWidget { + final List data; + final bool? bordered; + final double? maxListHeight; + const AttachmentList( + {super.key, required this.data, this.bordered, this.maxListHeight}); + + @override + Widget build(BuildContext context) { + final borderSide = (bordered ?? false) + ? BorderSide(width: 1, color: Theme.of(context).dividerColor) + : BorderSide.none; + + if (data.isEmpty) return const SizedBox.shrink(); + if (data.length == 1) { + return Container( + decoration: BoxDecoration( + border: Border(top: borderSide, bottom: borderSide), + ), + child: AttachmentItem(data: data[0]), + ); + } + + return Container( + constraints: BoxConstraints(maxHeight: maxListHeight ?? 320), + child: ScrollConfiguration( + behavior: _AttachmentListScrollBehavior(), + child: ListView.separated( + shrinkWrap: true, + itemCount: data.length, + itemBuilder: (context, idx) { + const radius = BorderRadius.all(Radius.circular(8)); + return Container( + decoration: BoxDecoration( + border: Border(top: borderSide, bottom: borderSide), + borderRadius: radius, + ), + child: ClipRRect( + borderRadius: radius, + child: AttachmentItem(data: data[idx]), + ), + ); + }, + separatorBuilder: (context, index) => const Gap(8), + padding: const EdgeInsets.symmetric(horizontal: 12), + physics: const BouncingScrollPhysics(), + scrollDirection: Axis.horizontal, + ), + ), + ); + } +} + +class _AttachmentListScrollBehavior extends MaterialScrollBehavior { + @override + Set get dragDevices => { + PointerDeviceKind.touch, + PointerDeviceKind.mouse, + }; +} diff --git a/lib/widgets/post/post_item.dart b/lib/widgets/post/post_item.dart index 1a1a91d..d0c8d92 100644 --- a/lib/widgets/post/post_item.dart +++ b/lib/widgets/post/post_item.dart @@ -4,6 +4,7 @@ import 'package:relative_time/relative_time.dart'; import 'package:styled_widget/styled_widget.dart'; import 'package:surface/types/post.dart'; import 'package:surface/widgets/account/account_image.dart'; +import 'package:surface/widgets/attachment/attachment_list.dart'; import 'package:surface/widgets/markdown_content.dart'; import 'package:gap/gap.dart'; @@ -18,6 +19,8 @@ class PostItem extends StatelessWidget { children: [ _PostContentHeader(data: data), _PostContentBody(data: data.body).padding(horizontal: 16, bottom: 6), + if (data.preload?.attachments?.isNotEmpty ?? true) + AttachmentList(data: data.preload!.attachments!, bordered: true), ], ); }