♻️ Refactored attachment loading system
This commit is contained in:
@ -15,16 +15,9 @@ import 'package:surface/types/post.dart';
|
||||
import 'package:surface/widgets/dialog.dart';
|
||||
import 'package:surface/widgets/universal_image.dart';
|
||||
|
||||
enum PostWriteMediaType {
|
||||
image,
|
||||
video,
|
||||
audio,
|
||||
file,
|
||||
}
|
||||
|
||||
class PostWriteMedia {
|
||||
late String name;
|
||||
late PostWriteMediaType type;
|
||||
late SnMediaType type;
|
||||
final SnAttachment? attachment;
|
||||
final XFile? file;
|
||||
final Uint8List? raw;
|
||||
@ -36,16 +29,16 @@ class PostWriteMedia {
|
||||
|
||||
switch (attachment?.mimetype.split('/').firstOrNull) {
|
||||
case 'image':
|
||||
type = PostWriteMediaType.image;
|
||||
type = SnMediaType.image;
|
||||
break;
|
||||
case 'video':
|
||||
type = PostWriteMediaType.video;
|
||||
type = SnMediaType.video;
|
||||
break;
|
||||
case 'audio':
|
||||
type = PostWriteMediaType.audio;
|
||||
type = SnMediaType.audio;
|
||||
break;
|
||||
default:
|
||||
type = PostWriteMediaType.file;
|
||||
type = SnMediaType.file;
|
||||
}
|
||||
}
|
||||
|
||||
@ -57,16 +50,16 @@ class PostWriteMedia {
|
||||
|
||||
switch (mimetype?.split('/').firstOrNull) {
|
||||
case 'image':
|
||||
type = PostWriteMediaType.image;
|
||||
type = SnMediaType.image;
|
||||
break;
|
||||
case 'video':
|
||||
type = PostWriteMediaType.video;
|
||||
type = SnMediaType.video;
|
||||
break;
|
||||
case 'audio':
|
||||
type = PostWriteMediaType.audio;
|
||||
type = SnMediaType.audio;
|
||||
break;
|
||||
default:
|
||||
type = PostWriteMediaType.file;
|
||||
type = SnMediaType.file;
|
||||
}
|
||||
}
|
||||
|
||||
@ -244,7 +237,7 @@ class PostWriteController extends ChangeNotifier {
|
||||
media.name,
|
||||
'interactive',
|
||||
null,
|
||||
mimetype: media.raw != null && media.type == PostWriteMediaType.image ? 'image/png' : null,
|
||||
mimetype: media.raw != null && media.type == SnMediaType.image ? 'image/png' : null,
|
||||
);
|
||||
|
||||
final item = await attach.chunkedUploadParts(
|
||||
@ -301,7 +294,7 @@ class PostWriteController extends ChangeNotifier {
|
||||
media.name,
|
||||
'interactive',
|
||||
null,
|
||||
mimetype: media.raw != null && media.type == PostWriteMediaType.image ? 'image/png' : null,
|
||||
mimetype: media.raw != null && media.type == SnMediaType.image ? 'image/png' : null,
|
||||
);
|
||||
|
||||
final item = await attach.chunkedUploadParts(
|
||||
|
@ -5,7 +5,6 @@ import 'package:provider/provider.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:surface/providers/config.dart';
|
||||
import 'package:surface/providers/sn_network.dart';
|
||||
import 'package:surface/providers/widget.dart';
|
||||
import 'package:surface/types/account.dart';
|
||||
|
||||
class UserProvider extends ChangeNotifier {
|
||||
@ -13,12 +12,10 @@ class UserProvider extends ChangeNotifier {
|
||||
SnAccount? user;
|
||||
|
||||
late final SnNetworkProvider _sn;
|
||||
late final HomeWidgetProvider _home;
|
||||
late final ConfigProvider _config;
|
||||
|
||||
UserProvider(BuildContext context) {
|
||||
_sn = context.read<SnNetworkProvider>();
|
||||
_home = context.read<HomeWidgetProvider>();
|
||||
_config = context.read<ConfigProvider>();
|
||||
}
|
||||
|
||||
|
@ -1,16 +1,11 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:dropdown_button2/dropdown_button2.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
import 'package:material_symbols_icons/symbols.dart';
|
||||
import 'package:pasteboard/pasteboard.dart';
|
||||
import 'package:styled_widget/styled_widget.dart';
|
||||
import 'package:surface/controllers/post_write_controller.dart';
|
||||
import 'package:surface/providers/config.dart';
|
||||
|
@ -33,7 +33,6 @@ Future<ThemeData> createAppTheme(
|
||||
brightness: brightness,
|
||||
);
|
||||
|
||||
final hasBackground = prefs.getBool(kAppBackgroundStoreKey) ?? false;
|
||||
final hasAppBarBlurry = prefs.getBool(kAppbarTransparentStoreKey) ?? false;
|
||||
|
||||
return ThemeData(
|
||||
|
@ -1,10 +1,20 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'attachment.freezed.dart';
|
||||
|
||||
part 'attachment.g.dart';
|
||||
|
||||
enum SnMediaType {
|
||||
image,
|
||||
video,
|
||||
audio,
|
||||
file,
|
||||
}
|
||||
|
||||
@freezed
|
||||
class SnAttachment with _$SnAttachment {
|
||||
const SnAttachment._();
|
||||
|
||||
const factory SnAttachment({
|
||||
required int id,
|
||||
required DateTime createdAt,
|
||||
@ -19,9 +29,10 @@ class SnAttachment with _$SnAttachment {
|
||||
required String hash,
|
||||
required int destination,
|
||||
required int refCount,
|
||||
@Default(0) int contentRating,
|
||||
@Default(0) int qualityRating,
|
||||
required dynamic fileChunks,
|
||||
required dynamic cleanedAt,
|
||||
required bool isMature,
|
||||
required bool isAnalyzed,
|
||||
required bool isUploaded,
|
||||
required bool isSelfRef,
|
||||
@ -30,11 +41,23 @@ class SnAttachment with _$SnAttachment {
|
||||
required SnAttachmentPool? pool,
|
||||
required int poolId,
|
||||
required int accountId,
|
||||
@Default({}) Map<String, dynamic> usermeta,
|
||||
@Default({}) Map<String, dynamic> metadata,
|
||||
}) = _SnAttachment;
|
||||
|
||||
factory SnAttachment.fromJson(Map<String, Object?> json) =>
|
||||
_$SnAttachmentFromJson(json);
|
||||
factory SnAttachment.fromJson(Map<String, Object?> json) => _$SnAttachmentFromJson(json);
|
||||
|
||||
Map<String, dynamic> get data => {
|
||||
...metadata,
|
||||
...usermeta,
|
||||
};
|
||||
|
||||
SnMediaType get mediaType => switch (mimetype.split('/').firstOrNull) {
|
||||
'image' => SnMediaType.image,
|
||||
'video' => SnMediaType.video,
|
||||
'audio' => SnMediaType.audio,
|
||||
_ => SnMediaType.file,
|
||||
};
|
||||
}
|
||||
|
||||
@freezed
|
||||
@ -51,6 +74,5 @@ class SnAttachmentPool with _$SnAttachmentPool {
|
||||
required int? accountId,
|
||||
}) = _SnAttachmentPool;
|
||||
|
||||
factory SnAttachmentPool.fromJson(Map<String, Object?> json) =>
|
||||
_$SnAttachmentPoolFromJson(json);
|
||||
factory SnAttachmentPool.fromJson(Map<String, Object?> json) => _$SnAttachmentPoolFromJson(json);
|
||||
}
|
||||
|
@ -33,9 +33,10 @@ mixin _$SnAttachment {
|
||||
String get hash => throw _privateConstructorUsedError;
|
||||
int get destination => throw _privateConstructorUsedError;
|
||||
int get refCount => throw _privateConstructorUsedError;
|
||||
int get contentRating => throw _privateConstructorUsedError;
|
||||
int get qualityRating => throw _privateConstructorUsedError;
|
||||
dynamic get fileChunks => throw _privateConstructorUsedError;
|
||||
dynamic get cleanedAt => throw _privateConstructorUsedError;
|
||||
bool get isMature => throw _privateConstructorUsedError;
|
||||
bool get isAnalyzed => throw _privateConstructorUsedError;
|
||||
bool get isUploaded => throw _privateConstructorUsedError;
|
||||
bool get isSelfRef => throw _privateConstructorUsedError;
|
||||
@ -44,6 +45,7 @@ mixin _$SnAttachment {
|
||||
SnAttachmentPool? get pool => throw _privateConstructorUsedError;
|
||||
int get poolId => throw _privateConstructorUsedError;
|
||||
int get accountId => throw _privateConstructorUsedError;
|
||||
Map<String, dynamic> get usermeta => throw _privateConstructorUsedError;
|
||||
Map<String, dynamic> get metadata => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this SnAttachment to a JSON map.
|
||||
@ -76,9 +78,10 @@ abstract class $SnAttachmentCopyWith<$Res> {
|
||||
String hash,
|
||||
int destination,
|
||||
int refCount,
|
||||
int contentRating,
|
||||
int qualityRating,
|
||||
dynamic fileChunks,
|
||||
dynamic cleanedAt,
|
||||
bool isMature,
|
||||
bool isAnalyzed,
|
||||
bool isUploaded,
|
||||
bool isSelfRef,
|
||||
@ -87,6 +90,7 @@ abstract class $SnAttachmentCopyWith<$Res> {
|
||||
SnAttachmentPool? pool,
|
||||
int poolId,
|
||||
int accountId,
|
||||
Map<String, dynamic> usermeta,
|
||||
Map<String, dynamic> metadata});
|
||||
|
||||
$SnAttachmentPoolCopyWith<$Res>? get pool;
|
||||
@ -120,9 +124,10 @@ class _$SnAttachmentCopyWithImpl<$Res, $Val extends SnAttachment>
|
||||
Object? hash = null,
|
||||
Object? destination = null,
|
||||
Object? refCount = null,
|
||||
Object? contentRating = null,
|
||||
Object? qualityRating = null,
|
||||
Object? fileChunks = freezed,
|
||||
Object? cleanedAt = freezed,
|
||||
Object? isMature = null,
|
||||
Object? isAnalyzed = null,
|
||||
Object? isUploaded = null,
|
||||
Object? isSelfRef = null,
|
||||
@ -131,6 +136,7 @@ class _$SnAttachmentCopyWithImpl<$Res, $Val extends SnAttachment>
|
||||
Object? pool = freezed,
|
||||
Object? poolId = null,
|
||||
Object? accountId = null,
|
||||
Object? usermeta = null,
|
||||
Object? metadata = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
@ -186,6 +192,14 @@ class _$SnAttachmentCopyWithImpl<$Res, $Val extends SnAttachment>
|
||||
? _value.refCount
|
||||
: refCount // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
contentRating: null == contentRating
|
||||
? _value.contentRating
|
||||
: contentRating // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
qualityRating: null == qualityRating
|
||||
? _value.qualityRating
|
||||
: qualityRating // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
fileChunks: freezed == fileChunks
|
||||
? _value.fileChunks
|
||||
: fileChunks // ignore: cast_nullable_to_non_nullable
|
||||
@ -194,10 +208,6 @@ class _$SnAttachmentCopyWithImpl<$Res, $Val extends SnAttachment>
|
||||
? _value.cleanedAt
|
||||
: cleanedAt // ignore: cast_nullable_to_non_nullable
|
||||
as dynamic,
|
||||
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
|
||||
@ -230,6 +240,10 @@ class _$SnAttachmentCopyWithImpl<$Res, $Val extends SnAttachment>
|
||||
? _value.accountId
|
||||
: accountId // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
usermeta: null == usermeta
|
||||
? _value.usermeta
|
||||
: usermeta // ignore: cast_nullable_to_non_nullable
|
||||
as Map<String, dynamic>,
|
||||
metadata: null == metadata
|
||||
? _value.metadata
|
||||
: metadata // ignore: cast_nullable_to_non_nullable
|
||||
@ -274,9 +288,10 @@ abstract class _$$SnAttachmentImplCopyWith<$Res>
|
||||
String hash,
|
||||
int destination,
|
||||
int refCount,
|
||||
int contentRating,
|
||||
int qualityRating,
|
||||
dynamic fileChunks,
|
||||
dynamic cleanedAt,
|
||||
bool isMature,
|
||||
bool isAnalyzed,
|
||||
bool isUploaded,
|
||||
bool isSelfRef,
|
||||
@ -285,6 +300,7 @@ abstract class _$$SnAttachmentImplCopyWith<$Res>
|
||||
SnAttachmentPool? pool,
|
||||
int poolId,
|
||||
int accountId,
|
||||
Map<String, dynamic> usermeta,
|
||||
Map<String, dynamic> metadata});
|
||||
|
||||
@override
|
||||
@ -317,9 +333,10 @@ class __$$SnAttachmentImplCopyWithImpl<$Res>
|
||||
Object? hash = null,
|
||||
Object? destination = null,
|
||||
Object? refCount = null,
|
||||
Object? contentRating = null,
|
||||
Object? qualityRating = null,
|
||||
Object? fileChunks = freezed,
|
||||
Object? cleanedAt = freezed,
|
||||
Object? isMature = null,
|
||||
Object? isAnalyzed = null,
|
||||
Object? isUploaded = null,
|
||||
Object? isSelfRef = null,
|
||||
@ -328,6 +345,7 @@ class __$$SnAttachmentImplCopyWithImpl<$Res>
|
||||
Object? pool = freezed,
|
||||
Object? poolId = null,
|
||||
Object? accountId = null,
|
||||
Object? usermeta = null,
|
||||
Object? metadata = null,
|
||||
}) {
|
||||
return _then(_$SnAttachmentImpl(
|
||||
@ -383,6 +401,14 @@ class __$$SnAttachmentImplCopyWithImpl<$Res>
|
||||
? _value.refCount
|
||||
: refCount // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
contentRating: null == contentRating
|
||||
? _value.contentRating
|
||||
: contentRating // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
qualityRating: null == qualityRating
|
||||
? _value.qualityRating
|
||||
: qualityRating // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
fileChunks: freezed == fileChunks
|
||||
? _value.fileChunks
|
||||
: fileChunks // ignore: cast_nullable_to_non_nullable
|
||||
@ -391,10 +417,6 @@ class __$$SnAttachmentImplCopyWithImpl<$Res>
|
||||
? _value.cleanedAt
|
||||
: cleanedAt // ignore: cast_nullable_to_non_nullable
|
||||
as dynamic,
|
||||
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
|
||||
@ -427,6 +449,10 @@ class __$$SnAttachmentImplCopyWithImpl<$Res>
|
||||
? _value.accountId
|
||||
: accountId // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
usermeta: null == usermeta
|
||||
? _value._usermeta
|
||||
: usermeta // ignore: cast_nullable_to_non_nullable
|
||||
as Map<String, dynamic>,
|
||||
metadata: null == metadata
|
||||
? _value._metadata
|
||||
: metadata // ignore: cast_nullable_to_non_nullable
|
||||
@ -437,7 +463,7 @@ class __$$SnAttachmentImplCopyWithImpl<$Res>
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _$SnAttachmentImpl implements _SnAttachment {
|
||||
class _$SnAttachmentImpl extends _SnAttachment {
|
||||
const _$SnAttachmentImpl(
|
||||
{required this.id,
|
||||
required this.createdAt,
|
||||
@ -452,9 +478,10 @@ class _$SnAttachmentImpl implements _SnAttachment {
|
||||
required this.hash,
|
||||
required this.destination,
|
||||
required this.refCount,
|
||||
this.contentRating = 0,
|
||||
this.qualityRating = 0,
|
||||
required this.fileChunks,
|
||||
required this.cleanedAt,
|
||||
required this.isMature,
|
||||
required this.isAnalyzed,
|
||||
required this.isUploaded,
|
||||
required this.isSelfRef,
|
||||
@ -463,8 +490,11 @@ class _$SnAttachmentImpl implements _SnAttachment {
|
||||
required this.pool,
|
||||
required this.poolId,
|
||||
required this.accountId,
|
||||
final Map<String, dynamic> usermeta = const {},
|
||||
final Map<String, dynamic> metadata = const {}})
|
||||
: _metadata = metadata;
|
||||
: _usermeta = usermeta,
|
||||
_metadata = metadata,
|
||||
super._();
|
||||
|
||||
factory _$SnAttachmentImpl.fromJson(Map<String, dynamic> json) =>
|
||||
_$$SnAttachmentImplFromJson(json);
|
||||
@ -496,12 +526,16 @@ class _$SnAttachmentImpl implements _SnAttachment {
|
||||
@override
|
||||
final int refCount;
|
||||
@override
|
||||
@JsonKey()
|
||||
final int contentRating;
|
||||
@override
|
||||
@JsonKey()
|
||||
final int qualityRating;
|
||||
@override
|
||||
final dynamic fileChunks;
|
||||
@override
|
||||
final dynamic cleanedAt;
|
||||
@override
|
||||
final bool isMature;
|
||||
@override
|
||||
final bool isAnalyzed;
|
||||
@override
|
||||
final bool isUploaded;
|
||||
@ -517,6 +551,15 @@ class _$SnAttachmentImpl implements _SnAttachment {
|
||||
final int poolId;
|
||||
@override
|
||||
final int accountId;
|
||||
final Map<String, dynamic> _usermeta;
|
||||
@override
|
||||
@JsonKey()
|
||||
Map<String, dynamic> get usermeta {
|
||||
if (_usermeta is EqualUnmodifiableMapView) return _usermeta;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableMapView(_usermeta);
|
||||
}
|
||||
|
||||
final Map<String, dynamic> _metadata;
|
||||
@override
|
||||
@JsonKey()
|
||||
@ -528,7 +571,7 @@ class _$SnAttachmentImpl implements _SnAttachment {
|
||||
|
||||
@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, isMature: $isMature, isAnalyzed: $isAnalyzed, isUploaded: $isUploaded, isSelfRef: $isSelfRef, ref: $ref, refId: $refId, pool: $pool, poolId: $poolId, accountId: $accountId, 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, fileChunks: $fileChunks, cleanedAt: $cleanedAt, isAnalyzed: $isAnalyzed, isUploaded: $isUploaded, isSelfRef: $isSelfRef, ref: $ref, refId: $refId, pool: $pool, poolId: $poolId, accountId: $accountId, usermeta: $usermeta, metadata: $metadata)';
|
||||
}
|
||||
|
||||
@override
|
||||
@ -554,11 +597,13 @@ class _$SnAttachmentImpl implements _SnAttachment {
|
||||
other.destination == destination) &&
|
||||
(identical(other.refCount, refCount) ||
|
||||
other.refCount == refCount) &&
|
||||
(identical(other.contentRating, contentRating) ||
|
||||
other.contentRating == contentRating) &&
|
||||
(identical(other.qualityRating, qualityRating) ||
|
||||
other.qualityRating == qualityRating) &&
|
||||
const DeepCollectionEquality()
|
||||
.equals(other.fileChunks, fileChunks) &&
|
||||
const DeepCollectionEquality().equals(other.cleanedAt, cleanedAt) &&
|
||||
(identical(other.isMature, isMature) ||
|
||||
other.isMature == isMature) &&
|
||||
(identical(other.isAnalyzed, isAnalyzed) ||
|
||||
other.isAnalyzed == isAnalyzed) &&
|
||||
(identical(other.isUploaded, isUploaded) ||
|
||||
@ -571,6 +616,7 @@ class _$SnAttachmentImpl implements _SnAttachment {
|
||||
(identical(other.poolId, poolId) || other.poolId == poolId) &&
|
||||
(identical(other.accountId, accountId) ||
|
||||
other.accountId == accountId) &&
|
||||
const DeepCollectionEquality().equals(other._usermeta, _usermeta) &&
|
||||
const DeepCollectionEquality().equals(other._metadata, _metadata));
|
||||
}
|
||||
|
||||
@ -591,9 +637,10 @@ class _$SnAttachmentImpl implements _SnAttachment {
|
||||
hash,
|
||||
destination,
|
||||
refCount,
|
||||
contentRating,
|
||||
qualityRating,
|
||||
const DeepCollectionEquality().hash(fileChunks),
|
||||
const DeepCollectionEquality().hash(cleanedAt),
|
||||
isMature,
|
||||
isAnalyzed,
|
||||
isUploaded,
|
||||
isSelfRef,
|
||||
@ -602,6 +649,7 @@ class _$SnAttachmentImpl implements _SnAttachment {
|
||||
pool,
|
||||
poolId,
|
||||
accountId,
|
||||
const DeepCollectionEquality().hash(_usermeta),
|
||||
const DeepCollectionEquality().hash(_metadata)
|
||||
]);
|
||||
|
||||
@ -621,7 +669,7 @@ class _$SnAttachmentImpl implements _SnAttachment {
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _SnAttachment implements SnAttachment {
|
||||
abstract class _SnAttachment extends SnAttachment {
|
||||
const factory _SnAttachment(
|
||||
{required final int id,
|
||||
required final DateTime createdAt,
|
||||
@ -636,9 +684,10 @@ abstract class _SnAttachment implements SnAttachment {
|
||||
required final String hash,
|
||||
required final int destination,
|
||||
required final int refCount,
|
||||
final int contentRating,
|
||||
final int qualityRating,
|
||||
required final dynamic fileChunks,
|
||||
required final dynamic cleanedAt,
|
||||
required final bool isMature,
|
||||
required final bool isAnalyzed,
|
||||
required final bool isUploaded,
|
||||
required final bool isSelfRef,
|
||||
@ -647,7 +696,9 @@ abstract class _SnAttachment implements SnAttachment {
|
||||
required final SnAttachmentPool? pool,
|
||||
required final int poolId,
|
||||
required final int accountId,
|
||||
final Map<String, dynamic> usermeta,
|
||||
final Map<String, dynamic> metadata}) = _$SnAttachmentImpl;
|
||||
const _SnAttachment._() : super._();
|
||||
|
||||
factory _SnAttachment.fromJson(Map<String, dynamic> json) =
|
||||
_$SnAttachmentImpl.fromJson;
|
||||
@ -679,12 +730,14 @@ abstract class _SnAttachment implements SnAttachment {
|
||||
@override
|
||||
int get refCount;
|
||||
@override
|
||||
int get contentRating;
|
||||
@override
|
||||
int get qualityRating;
|
||||
@override
|
||||
dynamic get fileChunks;
|
||||
@override
|
||||
dynamic get cleanedAt;
|
||||
@override
|
||||
bool get isMature;
|
||||
@override
|
||||
bool get isAnalyzed;
|
||||
@override
|
||||
bool get isUploaded;
|
||||
@ -701,6 +754,8 @@ abstract class _SnAttachment implements SnAttachment {
|
||||
@override
|
||||
int get accountId;
|
||||
@override
|
||||
Map<String, dynamic> get usermeta;
|
||||
@override
|
||||
Map<String, dynamic> get metadata;
|
||||
|
||||
/// Create a copy of SnAttachment
|
||||
|
@ -21,9 +21,10 @@ _$SnAttachmentImpl _$$SnAttachmentImplFromJson(Map<String, dynamic> json) =>
|
||||
hash: json['hash'] as String,
|
||||
destination: (json['destination'] as num).toInt(),
|
||||
refCount: (json['ref_count'] as num).toInt(),
|
||||
contentRating: (json['content_rating'] as num?)?.toInt() ?? 0,
|
||||
qualityRating: (json['quality_rating'] as num?)?.toInt() ?? 0,
|
||||
fileChunks: json['file_chunks'],
|
||||
cleanedAt: json['cleaned_at'],
|
||||
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,
|
||||
@ -34,6 +35,7 @@ _$SnAttachmentImpl _$$SnAttachmentImplFromJson(Map<String, dynamic> json) =>
|
||||
: SnAttachmentPool.fromJson(json['pool'] as Map<String, dynamic>),
|
||||
poolId: (json['pool_id'] as num).toInt(),
|
||||
accountId: (json['account_id'] as num).toInt(),
|
||||
usermeta: json['usermeta'] as Map<String, dynamic>? ?? const {},
|
||||
metadata: json['metadata'] as Map<String, dynamic>? ?? const {},
|
||||
);
|
||||
|
||||
@ -52,9 +54,10 @@ Map<String, dynamic> _$$SnAttachmentImplToJson(_$SnAttachmentImpl instance) =>
|
||||
'hash': instance.hash,
|
||||
'destination': instance.destination,
|
||||
'ref_count': instance.refCount,
|
||||
'content_rating': instance.contentRating,
|
||||
'quality_rating': instance.qualityRating,
|
||||
'file_chunks': instance.fileChunks,
|
||||
'cleaned_at': instance.cleanedAt,
|
||||
'is_mature': instance.isMature,
|
||||
'is_analyzed': instance.isAnalyzed,
|
||||
'is_uploaded': instance.isUploaded,
|
||||
'is_self_ref': instance.isSelfRef,
|
||||
@ -63,6 +66,7 @@ Map<String, dynamic> _$$SnAttachmentImplToJson(_$SnAttachmentImpl instance) =>
|
||||
'pool': instance.pool?.toJson(),
|
||||
'pool_id': instance.poolId,
|
||||
'account_id': instance.accountId,
|
||||
'usermeta': instance.usermeta,
|
||||
'metadata': instance.metadata,
|
||||
};
|
||||
|
||||
|
@ -99,10 +99,10 @@ class _AttachmentInputDialogState extends State<AttachmentInputDialog> {
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
child: Text('dialogDismiss').tr(),
|
||||
onPressed: _isBusy ? null : () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Text('dialogDismiss').tr(),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: _isBusy ? null : () => _finishUp(),
|
||||
|
@ -18,6 +18,7 @@ import 'package:uuid/uuid.dart';
|
||||
class AttachmentItem extends StatelessWidget {
|
||||
final SnAttachment? data;
|
||||
final String? heroTag;
|
||||
|
||||
const AttachmentItem({
|
||||
super.key,
|
||||
required this.data,
|
||||
@ -60,9 +61,14 @@ class AttachmentItem extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (data!.isMature) {
|
||||
return _AttachmentItemSensitiveBlur(
|
||||
child: _buildContent(context),
|
||||
if (data!.contentRating > 0) {
|
||||
return LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
return _AttachmentItemSensitiveBlur(
|
||||
isCompact: constraints.maxHeight < 360,
|
||||
child: _buildContent(context),
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@ -72,15 +78,15 @@ class AttachmentItem extends StatelessWidget {
|
||||
|
||||
class _AttachmentItemSensitiveBlur extends StatefulWidget {
|
||||
final Widget child;
|
||||
const _AttachmentItemSensitiveBlur({super.key, required this.child});
|
||||
final bool isCompact;
|
||||
|
||||
const _AttachmentItemSensitiveBlur({super.key, required this.child, this.isCompact = false});
|
||||
|
||||
@override
|
||||
State<_AttachmentItemSensitiveBlur> createState() =>
|
||||
_AttachmentItemSensitiveBlurState();
|
||||
State<_AttachmentItemSensitiveBlur> createState() => _AttachmentItemSensitiveBlurState();
|
||||
}
|
||||
|
||||
class _AttachmentItemSensitiveBlurState
|
||||
extends State<_AttachmentItemSensitiveBlur> {
|
||||
class _AttachmentItemSensitiveBlurState extends State<_AttachmentItemSensitiveBlur> {
|
||||
bool _doesShow = false;
|
||||
|
||||
@override
|
||||
@ -104,24 +110,21 @@ class _AttachmentItemSensitiveBlurState
|
||||
color: Colors.white,
|
||||
size: 32,
|
||||
),
|
||||
const Gap(8),
|
||||
Text('sensitiveContent', textAlign: TextAlign.center)
|
||||
.tr()
|
||||
.fontSize(20)
|
||||
.textColor(Colors.white)
|
||||
.bold(),
|
||||
Text(
|
||||
'sensitiveContentDescription',
|
||||
textAlign: TextAlign.center,
|
||||
)
|
||||
.tr()
|
||||
.fontSize(14)
|
||||
.textColor(Colors.white.withOpacity(0.8)),
|
||||
const Gap(16),
|
||||
InkWell(
|
||||
child: Text('sensitiveContentReveal')
|
||||
if (!widget.isCompact) const Gap(8),
|
||||
if (!widget.isCompact)
|
||||
Text('sensitiveContent', textAlign: TextAlign.center)
|
||||
.tr()
|
||||
.textColor(Colors.white),
|
||||
.fontSize(20)
|
||||
.textColor(Colors.white)
|
||||
.bold(),
|
||||
if (!widget.isCompact)
|
||||
Text(
|
||||
'sensitiveContentDescription',
|
||||
textAlign: TextAlign.center,
|
||||
).tr().fontSize(14).textColor(Colors.white.withOpacity(0.8)),
|
||||
if (!widget.isCompact) const Gap(16),
|
||||
InkWell(
|
||||
child: Text('sensitiveContentReveal').tr().textColor(Colors.white),
|
||||
onTap: () {
|
||||
setState(() => _doesShow = !_doesShow);
|
||||
},
|
||||
@ -131,9 +134,7 @@ class _AttachmentItemSensitiveBlurState
|
||||
).center(),
|
||||
),
|
||||
),
|
||||
)
|
||||
.opacity(_doesShow ? 0 : 1, animate: true)
|
||||
.animate(const Duration(milliseconds: 300), Curves.easeInOut),
|
||||
).opacity(_doesShow ? 0 : 1, animate: true).animate(const Duration(milliseconds: 300), Curves.easeInOut),
|
||||
if (_doesShow)
|
||||
Positioned(
|
||||
top: 0,
|
||||
@ -163,6 +164,7 @@ class _AttachmentItemSensitiveBlurState
|
||||
class _AttachmentItemContentVideo extends StatefulWidget {
|
||||
final SnAttachment data;
|
||||
final bool isAutoload;
|
||||
|
||||
const _AttachmentItemContentVideo({
|
||||
super.key,
|
||||
required this.data,
|
||||
@ -170,12 +172,10 @@ class _AttachmentItemContentVideo extends StatefulWidget {
|
||||
});
|
||||
|
||||
@override
|
||||
State<_AttachmentItemContentVideo> createState() =>
|
||||
_AttachmentItemContentVideoState();
|
||||
State<_AttachmentItemContentVideo> createState() => _AttachmentItemContentVideoState();
|
||||
}
|
||||
|
||||
class _AttachmentItemContentVideoState
|
||||
extends State<_AttachmentItemContentVideo> {
|
||||
class _AttachmentItemContentVideoState extends State<_AttachmentItemContentVideo> {
|
||||
bool _showContent = false;
|
||||
|
||||
Player? _videoPlayer;
|
||||
@ -266,10 +266,7 @@ class _AttachmentItemContentVideoState
|
||||
),
|
||||
Text(
|
||||
Duration(
|
||||
milliseconds:
|
||||
(widget.data.metadata['duration'] ?? 0)
|
||||
.toInt() *
|
||||
1000,
|
||||
milliseconds: (widget.data.metadata['duration'] ?? 0).toInt() * 1000,
|
||||
).toString(),
|
||||
style: GoogleFonts.robotoMono(
|
||||
fontSize: 12,
|
||||
@ -317,6 +314,7 @@ class _AttachmentItemContentVideoState
|
||||
class _AttachmentItemContentAudio extends StatefulWidget {
|
||||
final SnAttachment data;
|
||||
final bool isAutoload;
|
||||
|
||||
const _AttachmentItemContentAudio({
|
||||
super.key,
|
||||
required this.data,
|
||||
@ -324,12 +322,10 @@ class _AttachmentItemContentAudio extends StatefulWidget {
|
||||
});
|
||||
|
||||
@override
|
||||
State<_AttachmentItemContentAudio> createState() =>
|
||||
_AttachmentItemContentAudioState();
|
||||
State<_AttachmentItemContentAudio> createState() => _AttachmentItemContentAudioState();
|
||||
}
|
||||
|
||||
class _AttachmentItemContentAudioState
|
||||
extends State<_AttachmentItemContentAudio> {
|
||||
class _AttachmentItemContentAudioState extends State<_AttachmentItemContentAudio> {
|
||||
bool _showContent = false;
|
||||
|
||||
double? _draggingValue;
|
||||
@ -499,12 +495,8 @@ class _AttachmentItemContentAudioState
|
||||
overlayShape: SliderComponentShape.noOverlay,
|
||||
),
|
||||
child: Slider(
|
||||
secondaryTrackValue: _bufferedPosition
|
||||
.inMilliseconds
|
||||
.abs()
|
||||
.toDouble(),
|
||||
value: _draggingValue?.abs() ??
|
||||
_position.inMilliseconds.toDouble().abs(),
|
||||
secondaryTrackValue: _bufferedPosition.inMilliseconds.abs().toDouble(),
|
||||
value: _draggingValue?.abs() ?? _position.inMilliseconds.toDouble().abs(),
|
||||
min: 0,
|
||||
max: math
|
||||
.max(
|
||||
@ -544,9 +536,7 @@ class _AttachmentItemContentAudioState
|
||||
),
|
||||
const Gap(16),
|
||||
IconButton.filled(
|
||||
icon: _isPlaying
|
||||
? const Icon(Symbols.pause)
|
||||
: const Icon(Symbols.play_arrow),
|
||||
icon: _isPlaying ? const Icon(Symbols.pause) : const Icon(Symbols.play_arrow),
|
||||
onPressed: () {
|
||||
_audioPlayer!.playOrPause();
|
||||
},
|
||||
|
@ -58,7 +58,7 @@ class _AttachmentListState extends State<AttachmentList> {
|
||||
|
||||
if (widget.data.isEmpty) return const SizedBox.shrink();
|
||||
if (widget.data.length == 1) {
|
||||
final singleAspectRatio = widget.data[0]?.metadata['ratio']?.toDouble() ??
|
||||
final singleAspectRatio = widget.data[0]?.data['ratio']?.toDouble() ??
|
||||
switch (widget.data[0]?.mimetype.split('/').firstOrNull) {
|
||||
'audio' => 16 / 9,
|
||||
'video' => 16 / 9,
|
||||
@ -114,6 +114,7 @@ class _AttachmentListState extends State<AttachmentList> {
|
||||
},
|
||||
),
|
||||
onTap: () {
|
||||
if (widget.data.firstOrNull?.mediaType != SnMediaType.image) return;
|
||||
context.pushTransparentRoute(
|
||||
AttachmentZoomView(
|
||||
data: widget.data.where((ele) => ele != null).cast(),
|
||||
@ -136,7 +137,7 @@ class _AttachmentListState extends State<AttachmentList> {
|
||||
children: widget.data
|
||||
.mapIndexed(
|
||||
(idx, ele) => AspectRatio(
|
||||
aspectRatio: (ele?.metadata['ratio'] ?? 1).toDouble(),
|
||||
aspectRatio: (ele?.data['ratio'] ?? 1).toDouble(),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: backgroundColor,
|
||||
@ -161,7 +162,7 @@ class _AttachmentListState extends State<AttachmentList> {
|
||||
}
|
||||
|
||||
return AspectRatio(
|
||||
aspectRatio: (widget.data.firstOrNull?.metadata['ratio'] ?? 1).toDouble(),
|
||||
aspectRatio: (widget.data.firstOrNull?.data['ratio'] ?? 1).toDouble(),
|
||||
child: Container(
|
||||
constraints: BoxConstraints(maxHeight: constraints.maxHeight),
|
||||
child: ScrollConfiguration(
|
||||
@ -173,12 +174,14 @@ class _AttachmentListState extends State<AttachmentList> {
|
||||
return Container(
|
||||
constraints: constraints,
|
||||
child: AspectRatio(
|
||||
aspectRatio: (widget.data[idx]?.metadata['ratio'] ?? 1).toDouble(),
|
||||
aspectRatio: (widget.data[idx]?.data['ratio'] ?? 1).toDouble(),
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
if (widget.data[idx]?.mediaType != SnMediaType.image) return;
|
||||
context.pushTransparentRoute(
|
||||
AttachmentZoomView(
|
||||
data: widget.data.where((ele) => ele != null).cast(),
|
||||
data:
|
||||
widget.data.where((ele) => ele != null && ele.mediaType == SnMediaType.image).cast(),
|
||||
initialIndex: idx,
|
||||
heroTags: heroTags,
|
||||
),
|
||||
|
@ -1,18 +1,14 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
import 'package:material_symbols_icons/symbols.dart';
|
||||
import 'package:pasteboard/pasteboard.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:styled_widget/styled_widget.dart';
|
||||
import 'package:surface/controllers/chat_message_controller.dart';
|
||||
import 'package:surface/controllers/post_write_controller.dart';
|
||||
import 'package:surface/providers/sn_attachment.dart';
|
||||
import 'package:surface/providers/user_directory.dart';
|
||||
import 'package:surface/types/attachment.dart';
|
||||
import 'package:surface/types/chat.dart';
|
||||
import 'package:surface/widgets/dialog.dart';
|
||||
import 'package:surface/widgets/markdown_content.dart';
|
||||
@ -80,7 +76,7 @@ class ChatMessageInputState extends State<ChatMessageInput> {
|
||||
media.name,
|
||||
'messaging',
|
||||
null,
|
||||
mimetype: media.raw != null && media.type == PostWriteMediaType.image ? 'image/png' : null,
|
||||
mimetype: media.raw != null && media.type == SnMediaType.image ? 'image/png' : null,
|
||||
);
|
||||
|
||||
final item = await attach.chunkedUploadParts(
|
||||
|
@ -124,7 +124,7 @@ class PostMediaPendingList extends StatelessWidget {
|
||||
ContextMenu _createContextMenu(BuildContext context, int idx, PostWriteMedia media) {
|
||||
return ContextMenu(
|
||||
entries: [
|
||||
if (media.attachment != null && media.type == PostWriteMediaType.video)
|
||||
if (media.attachment != null && media.type == SnMediaType.video)
|
||||
MenuItem(
|
||||
label: 'attachmentSetThumbnail'.tr(),
|
||||
icon: Symbols.image,
|
||||
@ -140,7 +140,7 @@ class PostMediaPendingList extends StatelessWidget {
|
||||
onUpload!(idx);
|
||||
}),
|
||||
if (media.attachment != null &&
|
||||
media.type == PostWriteMediaType.image &&
|
||||
media.type == SnMediaType.image &&
|
||||
onPostSetThumbnail != null &&
|
||||
idx != -1)
|
||||
MenuItem(
|
||||
@ -150,7 +150,7 @@ class PostMediaPendingList extends StatelessWidget {
|
||||
onPostSetThumbnail!(idx);
|
||||
},
|
||||
)
|
||||
else if (media.attachment != null && media.type == PostWriteMediaType.image && onPostSetThumbnail != null)
|
||||
else if (media.attachment != null && media.type == SnMediaType.image && onPostSetThumbnail != null)
|
||||
MenuItem(
|
||||
label: 'attachmentUnsetAsPostThumbnail'.tr(),
|
||||
icon: Symbols.cancel,
|
||||
@ -166,7 +166,7 @@ class PostMediaPendingList extends StatelessWidget {
|
||||
onInsertLink!(idx);
|
||||
},
|
||||
),
|
||||
if (media.type == PostWriteMediaType.image && media.attachment != null)
|
||||
if (media.type == SnMediaType.image && media.attachment != null)
|
||||
MenuItem(
|
||||
label: 'preview'.tr(),
|
||||
icon: Symbols.preview,
|
||||
@ -177,7 +177,7 @@ class PostMediaPendingList extends StatelessWidget {
|
||||
);
|
||||
},
|
||||
),
|
||||
if (media.type == PostWriteMediaType.image && media.attachment == null)
|
||||
if (media.type == SnMediaType.image && media.attachment == null)
|
||||
MenuItem(
|
||||
label: 'crop'.tr(),
|
||||
icon: Symbols.crop,
|
||||
@ -219,10 +219,6 @@ class PostMediaPendingList extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final devicePixelRatio = MediaQuery.of(context).devicePixelRatio;
|
||||
|
||||
final sn = context.read<SnNetworkProvider>();
|
||||
|
||||
return Container(
|
||||
constraints: const BoxConstraints(maxHeight: 120),
|
||||
child: Row(
|
||||
@ -285,7 +281,7 @@ class _PostMediaPendingItem extends StatelessWidget {
|
||||
child: AspectRatio(
|
||||
aspectRatio: 1,
|
||||
child: switch (media.type) {
|
||||
PostWriteMediaType.image => Container(
|
||||
SnMediaType.image => Container(
|
||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||
child: LayoutBuilder(builder: (context, constraints) {
|
||||
return Image(
|
||||
@ -298,7 +294,7 @@ class _PostMediaPendingItem extends StatelessWidget {
|
||||
);
|
||||
}),
|
||||
),
|
||||
PostWriteMediaType.video => Container(
|
||||
SnMediaType.video => Container(
|
||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||
child: media.attachment?.metadata['thumbnail'] != null
|
||||
? AutoResizeUniversalImage(sn.getAttachmentUrl(media.attachment?.metadata['thumbnail']))
|
||||
@ -345,7 +341,7 @@ class AddPostMediaButton extends StatelessWidget {
|
||||
PostWriteMedia.fromBytes(
|
||||
imageBytes,
|
||||
'attachmentPastedImage'.tr(),
|
||||
PostWriteMediaType.image,
|
||||
SnMediaType.image,
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
Reference in New Issue
Block a user