✨ Stickers & packs
This commit is contained in:
parent
e4c6477bba
commit
f6d651a98f
@ -95,6 +95,7 @@
|
|||||||
"edited": "Edited",
|
"edited": "Edited",
|
||||||
"addVideo": "Add video",
|
"addVideo": "Add video",
|
||||||
"addPhoto": "Add photo",
|
"addPhoto": "Add photo",
|
||||||
|
"addFile": "Add file",
|
||||||
"createDirectMessage": "New direct message",
|
"createDirectMessage": "New direct message",
|
||||||
"react": "React",
|
"react": "React",
|
||||||
"reactions": {
|
"reactions": {
|
||||||
@ -151,5 +152,31 @@
|
|||||||
"settings": "Settings",
|
"settings": "Settings",
|
||||||
"language": "Language",
|
"language": "Language",
|
||||||
"settingsDisplayLanguage": "Display Language",
|
"settingsDisplayLanguage": "Display Language",
|
||||||
"languageFollowSystem": "Follow System"
|
"languageFollowSystem": "Follow System",
|
||||||
|
"publisherUnselected": "Unselected",
|
||||||
|
"postsCreatedCount": "Posts",
|
||||||
|
"stickerPacksCreatedCount": "Sticker Packs",
|
||||||
|
"stickersCreatedCount": "Stickers",
|
||||||
|
"upvoteReceived": "Upvotes Recieved",
|
||||||
|
"downvoteReceived": "Downvotes Recieved",
|
||||||
|
"stickerPacks": "Sticker Packs",
|
||||||
|
"createStickerPack": "Create a Sticker Pack",
|
||||||
|
"editStickerPack": "Edit Sticker Pack",
|
||||||
|
"deleteStickerPack": "Delete Sticker Pack",
|
||||||
|
"deleteStickerPackHint": "Are you sure to delete this sticker pack? This action cannot be undone.",
|
||||||
|
"stickerPackPrefix": "Prefix",
|
||||||
|
"stickerPackPrefixHint": "The prefix will be added before each stickers' slug in this pack.",
|
||||||
|
"stickers": "Stickers",
|
||||||
|
"createSticker": "Create a Sticker",
|
||||||
|
"editSticker": "Edit Sticker",
|
||||||
|
"deleteSticker": "Delete Sticker",
|
||||||
|
"deleteStickerHint": "Are you sure to delete this sticker? This action cannot be undone.",
|
||||||
|
"stickerImage": "Image",
|
||||||
|
"stickerSlug": "Slug",
|
||||||
|
"stickerSlugHint": "The slug will be combined with the prefix to form the sticker's unique identifier.",
|
||||||
|
"dataEmpty": "Nothing's here yet.",
|
||||||
|
"pickFile": "Pick a file",
|
||||||
|
"uploading": "Uploading",
|
||||||
|
"uploadingProgress": "Uploading {} of {}",
|
||||||
|
"uploadAll": "Upload All"
|
||||||
}
|
}
|
||||||
|
@ -64,6 +64,20 @@ abstract class SnPublisher with _$SnPublisher {
|
|||||||
_$SnPublisherFromJson(json);
|
_$SnPublisherFromJson(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
abstract class SnPublisherStats with _$SnPublisherStats {
|
||||||
|
const factory SnPublisherStats({
|
||||||
|
required int postsCreated,
|
||||||
|
required int stickerPacksCreated,
|
||||||
|
required int stickersCreated,
|
||||||
|
required int upvoteReceived,
|
||||||
|
required int downvoteReceived,
|
||||||
|
}) = _SnPublisherStats;
|
||||||
|
|
||||||
|
factory SnPublisherStats.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$SnPublisherStatsFromJson(json);
|
||||||
|
}
|
||||||
|
|
||||||
@freezed
|
@freezed
|
||||||
abstract class ReactInfo with _$ReactInfo {
|
abstract class ReactInfo with _$ReactInfo {
|
||||||
const factory ReactInfo({required String icon, required int attitude}) =
|
const factory ReactInfo({required String icon, required int attitude}) =
|
||||||
|
@ -511,6 +511,151 @@ $SnCloudFileCopyWith<$Res>? get background {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
mixin _$SnPublisherStats {
|
||||||
|
|
||||||
|
int get postsCreated; int get stickerPacksCreated; int get stickersCreated; int get upvoteReceived; int get downvoteReceived;
|
||||||
|
/// Create a copy of SnPublisherStats
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
$SnPublisherStatsCopyWith<SnPublisherStats> get copyWith => _$SnPublisherStatsCopyWithImpl<SnPublisherStats>(this as SnPublisherStats, _$identity);
|
||||||
|
|
||||||
|
/// Serializes this SnPublisherStats to a JSON map.
|
||||||
|
Map<String, dynamic> toJson();
|
||||||
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is SnPublisherStats&&(identical(other.postsCreated, postsCreated) || other.postsCreated == postsCreated)&&(identical(other.stickerPacksCreated, stickerPacksCreated) || other.stickerPacksCreated == stickerPacksCreated)&&(identical(other.stickersCreated, stickersCreated) || other.stickersCreated == stickersCreated)&&(identical(other.upvoteReceived, upvoteReceived) || other.upvoteReceived == upvoteReceived)&&(identical(other.downvoteReceived, downvoteReceived) || other.downvoteReceived == downvoteReceived));
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(runtimeType,postsCreated,stickerPacksCreated,stickersCreated,upvoteReceived,downvoteReceived);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'SnPublisherStats(postsCreated: $postsCreated, stickerPacksCreated: $stickerPacksCreated, stickersCreated: $stickersCreated, upvoteReceived: $upvoteReceived, downvoteReceived: $downvoteReceived)';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract mixin class $SnPublisherStatsCopyWith<$Res> {
|
||||||
|
factory $SnPublisherStatsCopyWith(SnPublisherStats value, $Res Function(SnPublisherStats) _then) = _$SnPublisherStatsCopyWithImpl;
|
||||||
|
@useResult
|
||||||
|
$Res call({
|
||||||
|
int postsCreated, int stickerPacksCreated, int stickersCreated, int upvoteReceived, int downvoteReceived
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
/// @nodoc
|
||||||
|
class _$SnPublisherStatsCopyWithImpl<$Res>
|
||||||
|
implements $SnPublisherStatsCopyWith<$Res> {
|
||||||
|
_$SnPublisherStatsCopyWithImpl(this._self, this._then);
|
||||||
|
|
||||||
|
final SnPublisherStats _self;
|
||||||
|
final $Res Function(SnPublisherStats) _then;
|
||||||
|
|
||||||
|
/// Create a copy of SnPublisherStats
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline') @override $Res call({Object? postsCreated = null,Object? stickerPacksCreated = null,Object? stickersCreated = null,Object? upvoteReceived = null,Object? downvoteReceived = null,}) {
|
||||||
|
return _then(_self.copyWith(
|
||||||
|
postsCreated: null == postsCreated ? _self.postsCreated : postsCreated // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,stickerPacksCreated: null == stickerPacksCreated ? _self.stickerPacksCreated : stickerPacksCreated // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,stickersCreated: null == stickersCreated ? _self.stickersCreated : stickersCreated // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,upvoteReceived: null == upvoteReceived ? _self.upvoteReceived : upvoteReceived // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,downvoteReceived: null == downvoteReceived ? _self.downvoteReceived : downvoteReceived // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
@JsonSerializable()
|
||||||
|
|
||||||
|
class _SnPublisherStats implements SnPublisherStats {
|
||||||
|
const _SnPublisherStats({required this.postsCreated, required this.stickerPacksCreated, required this.stickersCreated, required this.upvoteReceived, required this.downvoteReceived});
|
||||||
|
factory _SnPublisherStats.fromJson(Map<String, dynamic> json) => _$SnPublisherStatsFromJson(json);
|
||||||
|
|
||||||
|
@override final int postsCreated;
|
||||||
|
@override final int stickerPacksCreated;
|
||||||
|
@override final int stickersCreated;
|
||||||
|
@override final int upvoteReceived;
|
||||||
|
@override final int downvoteReceived;
|
||||||
|
|
||||||
|
/// Create a copy of SnPublisherStats
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$SnPublisherStatsCopyWith<_SnPublisherStats> get copyWith => __$SnPublisherStatsCopyWithImpl<_SnPublisherStats>(this, _$identity);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return _$SnPublisherStatsToJson(this, );
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnPublisherStats&&(identical(other.postsCreated, postsCreated) || other.postsCreated == postsCreated)&&(identical(other.stickerPacksCreated, stickerPacksCreated) || other.stickerPacksCreated == stickerPacksCreated)&&(identical(other.stickersCreated, stickersCreated) || other.stickersCreated == stickersCreated)&&(identical(other.upvoteReceived, upvoteReceived) || other.upvoteReceived == upvoteReceived)&&(identical(other.downvoteReceived, downvoteReceived) || other.downvoteReceived == downvoteReceived));
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(runtimeType,postsCreated,stickerPacksCreated,stickersCreated,upvoteReceived,downvoteReceived);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'SnPublisherStats(postsCreated: $postsCreated, stickerPacksCreated: $stickerPacksCreated, stickersCreated: $stickersCreated, upvoteReceived: $upvoteReceived, downvoteReceived: $downvoteReceived)';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract mixin class _$SnPublisherStatsCopyWith<$Res> implements $SnPublisherStatsCopyWith<$Res> {
|
||||||
|
factory _$SnPublisherStatsCopyWith(_SnPublisherStats value, $Res Function(_SnPublisherStats) _then) = __$SnPublisherStatsCopyWithImpl;
|
||||||
|
@override @useResult
|
||||||
|
$Res call({
|
||||||
|
int postsCreated, int stickerPacksCreated, int stickersCreated, int upvoteReceived, int downvoteReceived
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
/// @nodoc
|
||||||
|
class __$SnPublisherStatsCopyWithImpl<$Res>
|
||||||
|
implements _$SnPublisherStatsCopyWith<$Res> {
|
||||||
|
__$SnPublisherStatsCopyWithImpl(this._self, this._then);
|
||||||
|
|
||||||
|
final _SnPublisherStats _self;
|
||||||
|
final $Res Function(_SnPublisherStats) _then;
|
||||||
|
|
||||||
|
/// Create a copy of SnPublisherStats
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override @pragma('vm:prefer-inline') $Res call({Object? postsCreated = null,Object? stickerPacksCreated = null,Object? stickersCreated = null,Object? upvoteReceived = null,Object? downvoteReceived = null,}) {
|
||||||
|
return _then(_SnPublisherStats(
|
||||||
|
postsCreated: null == postsCreated ? _self.postsCreated : postsCreated // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,stickerPacksCreated: null == stickerPacksCreated ? _self.stickerPacksCreated : stickerPacksCreated // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,stickersCreated: null == stickersCreated ? _self.stickersCreated : stickersCreated // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,upvoteReceived: null == upvoteReceived ? _self.upvoteReceived : upvoteReceived // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,downvoteReceived: null == downvoteReceived ? _self.downvoteReceived : downvoteReceived // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
mixin _$ReactInfo {
|
mixin _$ReactInfo {
|
||||||
|
|
||||||
|
@ -126,3 +126,21 @@ Map<String, dynamic> _$SnPublisherToJson(_SnPublisher instance) =>
|
|||||||
'updated_at': instance.updatedAt.toIso8601String(),
|
'updated_at': instance.updatedAt.toIso8601String(),
|
||||||
'deleted_at': instance.deletedAt?.toIso8601String(),
|
'deleted_at': instance.deletedAt?.toIso8601String(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
_SnPublisherStats _$SnPublisherStatsFromJson(Map<String, dynamic> json) =>
|
||||||
|
_SnPublisherStats(
|
||||||
|
postsCreated: (json['posts_created'] as num).toInt(),
|
||||||
|
stickerPacksCreated: (json['sticker_packs_created'] as num).toInt(),
|
||||||
|
stickersCreated: (json['stickers_created'] as num).toInt(),
|
||||||
|
upvoteReceived: (json['upvote_received'] as num).toInt(),
|
||||||
|
downvoteReceived: (json['downvote_received'] as num).toInt(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$SnPublisherStatsToJson(_SnPublisherStats instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'posts_created': instance.postsCreated,
|
||||||
|
'sticker_packs_created': instance.stickerPacksCreated,
|
||||||
|
'stickers_created': instance.stickersCreated,
|
||||||
|
'upvote_received': instance.upvoteReceived,
|
||||||
|
'downvote_received': instance.downvoteReceived,
|
||||||
|
};
|
||||||
|
42
lib/models/sticker.dart
Normal file
42
lib/models/sticker.dart
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
import 'package:island/models/file.dart';
|
||||||
|
import 'package:island/models/post.dart';
|
||||||
|
|
||||||
|
part 'sticker.freezed.dart';
|
||||||
|
part 'sticker.g.dart';
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
abstract class SnSticker with _$SnSticker {
|
||||||
|
const factory SnSticker({
|
||||||
|
required String id,
|
||||||
|
required String slug,
|
||||||
|
required String imageId,
|
||||||
|
required SnCloudFile image,
|
||||||
|
required String packId,
|
||||||
|
required SnStickerPack? pack,
|
||||||
|
required DateTime createdAt,
|
||||||
|
required DateTime updatedAt,
|
||||||
|
required DateTime? deletedAt,
|
||||||
|
}) = _SnSticker;
|
||||||
|
|
||||||
|
factory SnSticker.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$SnStickerFromJson(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
abstract class SnStickerPack with _$SnStickerPack {
|
||||||
|
const factory SnStickerPack({
|
||||||
|
required String id,
|
||||||
|
required String name,
|
||||||
|
required String description,
|
||||||
|
required String prefix,
|
||||||
|
required int publisherId,
|
||||||
|
required SnPublisher? publisher,
|
||||||
|
required DateTime createdAt,
|
||||||
|
required DateTime updatedAt,
|
||||||
|
required DateTime? deletedAt,
|
||||||
|
}) = _SnStickerPack;
|
||||||
|
|
||||||
|
factory SnStickerPack.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$SnStickerPackFromJson(json);
|
||||||
|
}
|
395
lib/models/sticker.freezed.dart
Normal file
395
lib/models/sticker.freezed.dart
Normal file
@ -0,0 +1,395 @@
|
|||||||
|
// dart format width=80
|
||||||
|
// 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 'sticker.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// FreezedGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
// dart format off
|
||||||
|
T _$identity<T>(T value) => value;
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
mixin _$SnSticker {
|
||||||
|
|
||||||
|
String get id; String get slug; String get imageId; SnCloudFile get image; String get packId; SnStickerPack? get pack; DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt;
|
||||||
|
/// Create a copy of SnSticker
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
$SnStickerCopyWith<SnSticker> get copyWith => _$SnStickerCopyWithImpl<SnSticker>(this as SnSticker, _$identity);
|
||||||
|
|
||||||
|
/// Serializes this SnSticker to a JSON map.
|
||||||
|
Map<String, dynamic> toJson();
|
||||||
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is SnSticker&&(identical(other.id, id) || other.id == id)&&(identical(other.slug, slug) || other.slug == slug)&&(identical(other.imageId, imageId) || other.imageId == imageId)&&(identical(other.image, image) || other.image == image)&&(identical(other.packId, packId) || other.packId == packId)&&(identical(other.pack, pack) || other.pack == pack)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt));
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(runtimeType,id,slug,imageId,image,packId,pack,createdAt,updatedAt,deletedAt);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'SnSticker(id: $id, slug: $slug, imageId: $imageId, image: $image, packId: $packId, pack: $pack, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract mixin class $SnStickerCopyWith<$Res> {
|
||||||
|
factory $SnStickerCopyWith(SnSticker value, $Res Function(SnSticker) _then) = _$SnStickerCopyWithImpl;
|
||||||
|
@useResult
|
||||||
|
$Res call({
|
||||||
|
String id, String slug, String imageId, SnCloudFile image, String packId, SnStickerPack? pack, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
$SnCloudFileCopyWith<$Res> get image;$SnStickerPackCopyWith<$Res>? get pack;
|
||||||
|
|
||||||
|
}
|
||||||
|
/// @nodoc
|
||||||
|
class _$SnStickerCopyWithImpl<$Res>
|
||||||
|
implements $SnStickerCopyWith<$Res> {
|
||||||
|
_$SnStickerCopyWithImpl(this._self, this._then);
|
||||||
|
|
||||||
|
final SnSticker _self;
|
||||||
|
final $Res Function(SnSticker) _then;
|
||||||
|
|
||||||
|
/// Create a copy of SnSticker
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? slug = null,Object? imageId = null,Object? image = null,Object? packId = null,Object? pack = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) {
|
||||||
|
return _then(_self.copyWith(
|
||||||
|
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,slug: null == slug ? _self.slug : slug // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,imageId: null == imageId ? _self.imageId : imageId // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,image: null == image ? _self.image : image // ignore: cast_nullable_to_non_nullable
|
||||||
|
as SnCloudFile,packId: null == packId ? _self.packId : packId // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,pack: freezed == pack ? _self.pack : pack // ignore: cast_nullable_to_non_nullable
|
||||||
|
as SnStickerPack?,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime?,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
/// Create a copy of SnSticker
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
$SnCloudFileCopyWith<$Res> get image {
|
||||||
|
|
||||||
|
return $SnCloudFileCopyWith<$Res>(_self.image, (value) {
|
||||||
|
return _then(_self.copyWith(image: value));
|
||||||
|
});
|
||||||
|
}/// Create a copy of SnSticker
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
$SnStickerPackCopyWith<$Res>? get pack {
|
||||||
|
if (_self.pack == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $SnStickerPackCopyWith<$Res>(_self.pack!, (value) {
|
||||||
|
return _then(_self.copyWith(pack: value));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
@JsonSerializable()
|
||||||
|
|
||||||
|
class _SnSticker implements SnSticker {
|
||||||
|
const _SnSticker({required this.id, required this.slug, required this.imageId, required this.image, required this.packId, required this.pack, required this.createdAt, required this.updatedAt, required this.deletedAt});
|
||||||
|
factory _SnSticker.fromJson(Map<String, dynamic> json) => _$SnStickerFromJson(json);
|
||||||
|
|
||||||
|
@override final String id;
|
||||||
|
@override final String slug;
|
||||||
|
@override final String imageId;
|
||||||
|
@override final SnCloudFile image;
|
||||||
|
@override final String packId;
|
||||||
|
@override final SnStickerPack? pack;
|
||||||
|
@override final DateTime createdAt;
|
||||||
|
@override final DateTime updatedAt;
|
||||||
|
@override final DateTime? deletedAt;
|
||||||
|
|
||||||
|
/// Create a copy of SnSticker
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$SnStickerCopyWith<_SnSticker> get copyWith => __$SnStickerCopyWithImpl<_SnSticker>(this, _$identity);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return _$SnStickerToJson(this, );
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnSticker&&(identical(other.id, id) || other.id == id)&&(identical(other.slug, slug) || other.slug == slug)&&(identical(other.imageId, imageId) || other.imageId == imageId)&&(identical(other.image, image) || other.image == image)&&(identical(other.packId, packId) || other.packId == packId)&&(identical(other.pack, pack) || other.pack == pack)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt));
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(runtimeType,id,slug,imageId,image,packId,pack,createdAt,updatedAt,deletedAt);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'SnSticker(id: $id, slug: $slug, imageId: $imageId, image: $image, packId: $packId, pack: $pack, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract mixin class _$SnStickerCopyWith<$Res> implements $SnStickerCopyWith<$Res> {
|
||||||
|
factory _$SnStickerCopyWith(_SnSticker value, $Res Function(_SnSticker) _then) = __$SnStickerCopyWithImpl;
|
||||||
|
@override @useResult
|
||||||
|
$Res call({
|
||||||
|
String id, String slug, String imageId, SnCloudFile image, String packId, SnStickerPack? pack, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
@override $SnCloudFileCopyWith<$Res> get image;@override $SnStickerPackCopyWith<$Res>? get pack;
|
||||||
|
|
||||||
|
}
|
||||||
|
/// @nodoc
|
||||||
|
class __$SnStickerCopyWithImpl<$Res>
|
||||||
|
implements _$SnStickerCopyWith<$Res> {
|
||||||
|
__$SnStickerCopyWithImpl(this._self, this._then);
|
||||||
|
|
||||||
|
final _SnSticker _self;
|
||||||
|
final $Res Function(_SnSticker) _then;
|
||||||
|
|
||||||
|
/// Create a copy of SnSticker
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? slug = null,Object? imageId = null,Object? image = null,Object? packId = null,Object? pack = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) {
|
||||||
|
return _then(_SnSticker(
|
||||||
|
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,slug: null == slug ? _self.slug : slug // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,imageId: null == imageId ? _self.imageId : imageId // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,image: null == image ? _self.image : image // ignore: cast_nullable_to_non_nullable
|
||||||
|
as SnCloudFile,packId: null == packId ? _self.packId : packId // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,pack: freezed == pack ? _self.pack : pack // ignore: cast_nullable_to_non_nullable
|
||||||
|
as SnStickerPack?,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime?,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a copy of SnSticker
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
$SnCloudFileCopyWith<$Res> get image {
|
||||||
|
|
||||||
|
return $SnCloudFileCopyWith<$Res>(_self.image, (value) {
|
||||||
|
return _then(_self.copyWith(image: value));
|
||||||
|
});
|
||||||
|
}/// Create a copy of SnSticker
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
$SnStickerPackCopyWith<$Res>? get pack {
|
||||||
|
if (_self.pack == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $SnStickerPackCopyWith<$Res>(_self.pack!, (value) {
|
||||||
|
return _then(_self.copyWith(pack: value));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
mixin _$SnStickerPack {
|
||||||
|
|
||||||
|
String get id; String get name; String get description; String get prefix; int get publisherId; SnPublisher? get publisher; DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt;
|
||||||
|
/// Create a copy of SnStickerPack
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
$SnStickerPackCopyWith<SnStickerPack> get copyWith => _$SnStickerPackCopyWithImpl<SnStickerPack>(this as SnStickerPack, _$identity);
|
||||||
|
|
||||||
|
/// Serializes this SnStickerPack to a JSON map.
|
||||||
|
Map<String, dynamic> toJson();
|
||||||
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is SnStickerPack&&(identical(other.id, id) || other.id == id)&&(identical(other.name, name) || other.name == name)&&(identical(other.description, description) || other.description == description)&&(identical(other.prefix, prefix) || other.prefix == prefix)&&(identical(other.publisherId, publisherId) || other.publisherId == publisherId)&&(identical(other.publisher, publisher) || other.publisher == publisher)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt));
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(runtimeType,id,name,description,prefix,publisherId,publisher,createdAt,updatedAt,deletedAt);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'SnStickerPack(id: $id, name: $name, description: $description, prefix: $prefix, publisherId: $publisherId, publisher: $publisher, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract mixin class $SnStickerPackCopyWith<$Res> {
|
||||||
|
factory $SnStickerPackCopyWith(SnStickerPack value, $Res Function(SnStickerPack) _then) = _$SnStickerPackCopyWithImpl;
|
||||||
|
@useResult
|
||||||
|
$Res call({
|
||||||
|
String id, String name, String description, String prefix, int publisherId, SnPublisher? publisher, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
$SnPublisherCopyWith<$Res>? get publisher;
|
||||||
|
|
||||||
|
}
|
||||||
|
/// @nodoc
|
||||||
|
class _$SnStickerPackCopyWithImpl<$Res>
|
||||||
|
implements $SnStickerPackCopyWith<$Res> {
|
||||||
|
_$SnStickerPackCopyWithImpl(this._self, this._then);
|
||||||
|
|
||||||
|
final SnStickerPack _self;
|
||||||
|
final $Res Function(SnStickerPack) _then;
|
||||||
|
|
||||||
|
/// Create a copy of SnStickerPack
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? name = null,Object? description = null,Object? prefix = null,Object? publisherId = null,Object? publisher = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) {
|
||||||
|
return _then(_self.copyWith(
|
||||||
|
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,description: null == description ? _self.description : description // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,prefix: null == prefix ? _self.prefix : prefix // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,publisherId: null == publisherId ? _self.publisherId : publisherId // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,publisher: freezed == publisher ? _self.publisher : publisher // ignore: cast_nullable_to_non_nullable
|
||||||
|
as SnPublisher?,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime?,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
/// Create a copy of SnStickerPack
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
$SnPublisherCopyWith<$Res>? get publisher {
|
||||||
|
if (_self.publisher == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $SnPublisherCopyWith<$Res>(_self.publisher!, (value) {
|
||||||
|
return _then(_self.copyWith(publisher: value));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
@JsonSerializable()
|
||||||
|
|
||||||
|
class _SnStickerPack implements SnStickerPack {
|
||||||
|
const _SnStickerPack({required this.id, required this.name, required this.description, required this.prefix, required this.publisherId, required this.publisher, required this.createdAt, required this.updatedAt, required this.deletedAt});
|
||||||
|
factory _SnStickerPack.fromJson(Map<String, dynamic> json) => _$SnStickerPackFromJson(json);
|
||||||
|
|
||||||
|
@override final String id;
|
||||||
|
@override final String name;
|
||||||
|
@override final String description;
|
||||||
|
@override final String prefix;
|
||||||
|
@override final int publisherId;
|
||||||
|
@override final SnPublisher? publisher;
|
||||||
|
@override final DateTime createdAt;
|
||||||
|
@override final DateTime updatedAt;
|
||||||
|
@override final DateTime? deletedAt;
|
||||||
|
|
||||||
|
/// Create a copy of SnStickerPack
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$SnStickerPackCopyWith<_SnStickerPack> get copyWith => __$SnStickerPackCopyWithImpl<_SnStickerPack>(this, _$identity);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return _$SnStickerPackToJson(this, );
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnStickerPack&&(identical(other.id, id) || other.id == id)&&(identical(other.name, name) || other.name == name)&&(identical(other.description, description) || other.description == description)&&(identical(other.prefix, prefix) || other.prefix == prefix)&&(identical(other.publisherId, publisherId) || other.publisherId == publisherId)&&(identical(other.publisher, publisher) || other.publisher == publisher)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt));
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(runtimeType,id,name,description,prefix,publisherId,publisher,createdAt,updatedAt,deletedAt);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'SnStickerPack(id: $id, name: $name, description: $description, prefix: $prefix, publisherId: $publisherId, publisher: $publisher, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract mixin class _$SnStickerPackCopyWith<$Res> implements $SnStickerPackCopyWith<$Res> {
|
||||||
|
factory _$SnStickerPackCopyWith(_SnStickerPack value, $Res Function(_SnStickerPack) _then) = __$SnStickerPackCopyWithImpl;
|
||||||
|
@override @useResult
|
||||||
|
$Res call({
|
||||||
|
String id, String name, String description, String prefix, int publisherId, SnPublisher? publisher, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
@override $SnPublisherCopyWith<$Res>? get publisher;
|
||||||
|
|
||||||
|
}
|
||||||
|
/// @nodoc
|
||||||
|
class __$SnStickerPackCopyWithImpl<$Res>
|
||||||
|
implements _$SnStickerPackCopyWith<$Res> {
|
||||||
|
__$SnStickerPackCopyWithImpl(this._self, this._then);
|
||||||
|
|
||||||
|
final _SnStickerPack _self;
|
||||||
|
final $Res Function(_SnStickerPack) _then;
|
||||||
|
|
||||||
|
/// Create a copy of SnStickerPack
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? name = null,Object? description = null,Object? prefix = null,Object? publisherId = null,Object? publisher = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) {
|
||||||
|
return _then(_SnStickerPack(
|
||||||
|
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,description: null == description ? _self.description : description // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,prefix: null == prefix ? _self.prefix : prefix // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,publisherId: null == publisherId ? _self.publisherId : publisherId // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,publisher: freezed == publisher ? _self.publisher : publisher // ignore: cast_nullable_to_non_nullable
|
||||||
|
as SnPublisher?,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime?,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a copy of SnStickerPack
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
$SnPublisherCopyWith<$Res>? get publisher {
|
||||||
|
if (_self.publisher == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $SnPublisherCopyWith<$Res>(_self.publisher!, (value) {
|
||||||
|
return _then(_self.copyWith(publisher: value));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// dart format on
|
70
lib/models/sticker.g.dart
Normal file
70
lib/models/sticker.g.dart
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'sticker.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// JsonSerializableGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
_SnSticker _$SnStickerFromJson(Map<String, dynamic> json) => _SnSticker(
|
||||||
|
id: json['id'] as String,
|
||||||
|
slug: json['slug'] as String,
|
||||||
|
imageId: json['image_id'] as String,
|
||||||
|
image: SnCloudFile.fromJson(json['image'] as Map<String, dynamic>),
|
||||||
|
packId: json['pack_id'] as String,
|
||||||
|
pack:
|
||||||
|
json['pack'] == null
|
||||||
|
? null
|
||||||
|
: SnStickerPack.fromJson(json['pack'] as Map<String, dynamic>),
|
||||||
|
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),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$SnStickerToJson(_SnSticker instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'id': instance.id,
|
||||||
|
'slug': instance.slug,
|
||||||
|
'image_id': instance.imageId,
|
||||||
|
'image': instance.image.toJson(),
|
||||||
|
'pack_id': instance.packId,
|
||||||
|
'pack': instance.pack?.toJson(),
|
||||||
|
'created_at': instance.createdAt.toIso8601String(),
|
||||||
|
'updated_at': instance.updatedAt.toIso8601String(),
|
||||||
|
'deleted_at': instance.deletedAt?.toIso8601String(),
|
||||||
|
};
|
||||||
|
|
||||||
|
_SnStickerPack _$SnStickerPackFromJson(Map<String, dynamic> json) =>
|
||||||
|
_SnStickerPack(
|
||||||
|
id: json['id'] as String,
|
||||||
|
name: json['name'] as String,
|
||||||
|
description: json['description'] as String,
|
||||||
|
prefix: json['prefix'] as String,
|
||||||
|
publisherId: (json['publisher_id'] as num).toInt(),
|
||||||
|
publisher:
|
||||||
|
json['publisher'] == null
|
||||||
|
? null
|
||||||
|
: SnPublisher.fromJson(json['publisher'] as Map<String, dynamic>),
|
||||||
|
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),
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$SnStickerPackToJson(_SnStickerPack instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'id': instance.id,
|
||||||
|
'name': instance.name,
|
||||||
|
'description': instance.description,
|
||||||
|
'prefix': instance.prefix,
|
||||||
|
'publisher_id': instance.publisherId,
|
||||||
|
'publisher': instance.publisher?.toJson(),
|
||||||
|
'created_at': instance.createdAt.toIso8601String(),
|
||||||
|
'updated_at': instance.updatedAt.toIso8601String(),
|
||||||
|
'deleted_at': instance.deletedAt?.toIso8601String(),
|
||||||
|
};
|
@ -50,16 +50,31 @@ class WebSocketService {
|
|||||||
Stream<WebSocketPacket> get dataStream => _streamController.stream;
|
Stream<WebSocketPacket> get dataStream => _streamController.stream;
|
||||||
Stream<WebSocketState> get statusStream => _statusStreamController.stream;
|
Stream<WebSocketState> get statusStream => _statusStreamController.stream;
|
||||||
|
|
||||||
Future<void> connect(String url, String atk) async {
|
Future<void> connect(String url, String atk, {Ref? ref}) async {
|
||||||
_lastUrl = url;
|
_lastUrl = url;
|
||||||
_lastAtk = atk;
|
_lastAtk = atk;
|
||||||
|
|
||||||
|
if (ref != null) {
|
||||||
|
final freshAtk = await getFreshAtk(
|
||||||
|
ref.watch(tokenPairProvider),
|
||||||
|
url.replaceFirst('ws', 'http').replaceFirst('/ws', ''),
|
||||||
|
onRefreshed: (atk, rtk) {
|
||||||
|
setTokenPair(ref.watch(sharedPreferencesProvider), atk, rtk);
|
||||||
|
ref.invalidate(tokenPairProvider);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if (freshAtk != null) {
|
||||||
|
atk = freshAtk;
|
||||||
|
_lastAtk = freshAtk;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
log('[WebSocket] Trying connecting to $url');
|
log('[WebSocket] Trying connecting to $url');
|
||||||
try {
|
try {
|
||||||
_channel = IOWebSocketChannel.connect(
|
_channel = IOWebSocketChannel.connect(
|
||||||
Uri.parse(url),
|
Uri.parse(url),
|
||||||
headers: {'Authorization': 'Bearer $atk'},
|
headers: {'Authorization': 'Bearer $atk'},
|
||||||
);
|
);
|
||||||
// TODO Fix the atk is expired when reconnecting
|
|
||||||
await _channel!.ready;
|
await _channel!.ready;
|
||||||
_statusStreamController.sink.add(WebSocketState.connected());
|
_statusStreamController.sink.add(WebSocketState.connected());
|
||||||
_channel!.stream.listen(
|
_channel!.stream.listen(
|
||||||
@ -141,7 +156,11 @@ class WebSocketStateNotifier extends StateNotifier<WebSocketState> {
|
|||||||
state = const WebSocketState.error('Unauthorized');
|
state = const WebSocketState.error('Unauthorized');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await service.connect('$baseUrl/ws'.replaceFirst('http', 'ws'), atk);
|
await service.connect(
|
||||||
|
'$baseUrl/ws'.replaceFirst('http', 'ws'),
|
||||||
|
atk,
|
||||||
|
ref: ref,
|
||||||
|
);
|
||||||
state = const WebSocketState.connected();
|
state = const WebSocketState.connected();
|
||||||
service.statusStream.listen((event) {
|
service.statusStream.listen((event) {
|
||||||
state = event;
|
state = event;
|
||||||
|
@ -45,5 +45,27 @@ class AppRouter extends RootStackRouter {
|
|||||||
AutoRoute(page: EditChatRoute.page, path: '/chat/:id/edit'),
|
AutoRoute(page: EditChatRoute.page, path: '/chat/:id/edit'),
|
||||||
AutoRoute(page: ChatRoomRoute.page, path: '/chat/:id'),
|
AutoRoute(page: ChatRoomRoute.page, path: '/chat/:id'),
|
||||||
AutoRoute(page: ChatDetailRoute.page, path: '/chat/:id/detail'),
|
AutoRoute(page: ChatDetailRoute.page, path: '/chat/:id/detail'),
|
||||||
|
AutoRoute(page: CreatorHubRoute.page, path: '/creators'),
|
||||||
|
AutoRoute(page: StickersRoute.page, path: '/creators/:name/stickers'),
|
||||||
|
AutoRoute(
|
||||||
|
page: NewStickerPacksRoute.page,
|
||||||
|
path: '/creators/:name/stickers/new',
|
||||||
|
),
|
||||||
|
AutoRoute(
|
||||||
|
page: EditStickerPacksRoute.page,
|
||||||
|
path: '/creators/:name/stickers/:packId/edit',
|
||||||
|
),
|
||||||
|
AutoRoute(
|
||||||
|
page: StickerPackDetailRoute.page,
|
||||||
|
path: '/creators/:name/stickers/:packId',
|
||||||
|
),
|
||||||
|
AutoRoute(
|
||||||
|
page: NewStickersRoute.page,
|
||||||
|
path: '/creators/:name/stickers/new',
|
||||||
|
),
|
||||||
|
AutoRoute(
|
||||||
|
page: EditStickersRoute.page,
|
||||||
|
path: '/creators/:name/stickers/:id/edit',
|
||||||
|
),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -28,7 +28,7 @@ class AccountScreen extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return AppScaffold(
|
return AppScaffold(
|
||||||
appBar: AppBar(title: const Text('Account')),
|
appBar: AppBar(title: const Text('account').tr()),
|
||||||
body: SingleChildScrollView(
|
body: SingleChildScrollView(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
@ -102,9 +102,11 @@ class AccountScreen extends HookConsumerWidget {
|
|||||||
Text('creatorHubDescription').tr(),
|
Text('creatorHubDescription').tr(),
|
||||||
],
|
],
|
||||||
).padding(horizontal: 16, vertical: 12),
|
).padding(horizontal: 16, vertical: 12),
|
||||||
onTap: () {},
|
onTap: () {
|
||||||
),
|
context.router.push(CreatorHubRoute());
|
||||||
|
},
|
||||||
),
|
),
|
||||||
|
).height(140),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Card(
|
child: Card(
|
||||||
@ -120,7 +122,7 @@ class AccountScreen extends HookConsumerWidget {
|
|||||||
).padding(horizontal: 16, vertical: 12),
|
).padding(horizontal: 16, vertical: 12),
|
||||||
onTap: () {},
|
onTap: () {},
|
||||||
),
|
),
|
||||||
),
|
).height(140),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
).padding(horizontal: 8),
|
).padding(horizontal: 8),
|
||||||
|
@ -26,6 +26,7 @@ class UpdateProfileScreen extends HookConsumerWidget {
|
|||||||
final submitting = useState(false);
|
final submitting = useState(false);
|
||||||
|
|
||||||
void updateProfilePicture(String position) async {
|
void updateProfilePicture(String position) async {
|
||||||
|
showLoadingModal(context);
|
||||||
var result = await ref
|
var result = await ref
|
||||||
.read(imagePickerProvider)
|
.read(imagePickerProvider)
|
||||||
.pickImage(source: ImageSource.gallery);
|
.pickImage(source: ImageSource.gallery);
|
||||||
@ -41,7 +42,10 @@ class UpdateProfileScreen extends HookConsumerWidget {
|
|||||||
CropAspectRatio(height: 1, width: 1),
|
CropAspectRatio(height: 1, width: 1),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
if (result == null) return;
|
if (result == null) {
|
||||||
|
if (context.mounted) hideLoadingModal(context);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!context.mounted) return;
|
if (!context.mounted) return;
|
||||||
|
|
||||||
submitting.value = true;
|
submitting.value = true;
|
||||||
@ -78,6 +82,7 @@ class UpdateProfileScreen extends HookConsumerWidget {
|
|||||||
showErrorAlert(err);
|
showErrorAlert(err);
|
||||||
} finally {
|
} finally {
|
||||||
submitting.value = false;
|
submitting.value = false;
|
||||||
|
if (context.mounted) hideLoadingModal(context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -245,11 +245,16 @@ class EditChatScreen extends HookConsumerWidget {
|
|||||||
}, [chat]);
|
}, [chat]);
|
||||||
|
|
||||||
void setPicture(String position) async {
|
void setPicture(String position) async {
|
||||||
|
showLoadingModal(context);
|
||||||
var result = await ref
|
var result = await ref
|
||||||
.read(imagePickerProvider)
|
.read(imagePickerProvider)
|
||||||
.pickImage(source: ImageSource.gallery);
|
.pickImage(source: ImageSource.gallery);
|
||||||
if (result == null) return;
|
if (result == null) {
|
||||||
|
if (context.mounted) hideLoadingModal(context);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!context.mounted) return;
|
if (!context.mounted) return;
|
||||||
|
|
||||||
result = await cropImage(
|
result = await cropImage(
|
||||||
context,
|
context,
|
||||||
image: result,
|
image: result,
|
||||||
@ -295,6 +300,7 @@ class EditChatScreen extends HookConsumerWidget {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
showErrorAlert(err);
|
showErrorAlert(err);
|
||||||
} finally {
|
} finally {
|
||||||
|
if (context.mounted) hideLoadingModal(context);
|
||||||
submitting.value = false;
|
submitting.value = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
252
lib/screens/creators/hub.dart
Normal file
252
lib/screens/creators/hub.dart
Normal file
@ -0,0 +1,252 @@
|
|||||||
|
import 'package:auto_route/auto_route.dart';
|
||||||
|
import 'package:dropdown_button2/dropdown_button2.dart';
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:island/models/post.dart';
|
||||||
|
import 'package:island/pods/network.dart';
|
||||||
|
import 'package:island/route.gr.dart';
|
||||||
|
import 'package:island/screens/account/me/publishers.dart';
|
||||||
|
import 'package:island/widgets/app_scaffold.dart';
|
||||||
|
import 'package:island/widgets/content/cloud_files.dart';
|
||||||
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
|
|
||||||
|
part 'hub.g.dart';
|
||||||
|
|
||||||
|
@riverpod
|
||||||
|
Future<SnPublisherStats?> publisherStats(Ref ref, String? uname) async {
|
||||||
|
if (uname == null) return null;
|
||||||
|
final apiClient = ref.watch(apiClientProvider);
|
||||||
|
final resp = await apiClient.get('/publishers/$uname/stats');
|
||||||
|
return SnPublisherStats.fromJson(resp.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
@RoutePage()
|
||||||
|
class CreatorHubScreen extends HookConsumerWidget {
|
||||||
|
const CreatorHubScreen({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final publishers = ref.watch(publishersManagedProvider);
|
||||||
|
final currentPublisher = useState<SnPublisher?>(
|
||||||
|
publishers.value?.firstOrNull,
|
||||||
|
);
|
||||||
|
|
||||||
|
final publishersMenu = publishers.when(
|
||||||
|
data:
|
||||||
|
(data) =>
|
||||||
|
data
|
||||||
|
.map(
|
||||||
|
(item) => DropdownMenuItem<SnPublisher>(
|
||||||
|
value: item,
|
||||||
|
child: ListTile(
|
||||||
|
minTileHeight: 48,
|
||||||
|
leading: ProfilePictureWidget(
|
||||||
|
radius: 16,
|
||||||
|
fileId: item.pictureId,
|
||||||
|
),
|
||||||
|
title: Text(item.nick),
|
||||||
|
subtitle: Text('@${item.name}'),
|
||||||
|
trailing:
|
||||||
|
currentPublisher.value?.id == item.id
|
||||||
|
? const Icon(Icons.check)
|
||||||
|
: null,
|
||||||
|
contentPadding: EdgeInsets.symmetric(horizontal: 8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
|
loading: () => [],
|
||||||
|
error: (_, __) => [],
|
||||||
|
);
|
||||||
|
|
||||||
|
final publisherStats = ref.watch(
|
||||||
|
publisherStatsProvider(currentPublisher.value?.name),
|
||||||
|
);
|
||||||
|
|
||||||
|
return AppScaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text('creatorHub').tr(),
|
||||||
|
actions: [
|
||||||
|
DropdownButtonHideUnderline(
|
||||||
|
child: DropdownButton2<SnPublisher>(
|
||||||
|
alignment: Alignment.centerRight,
|
||||||
|
value: currentPublisher.value,
|
||||||
|
hint: CircleAvatar(
|
||||||
|
radius: 16,
|
||||||
|
child: Icon(
|
||||||
|
Symbols.unknown_med,
|
||||||
|
color: Theme.of(context).colorScheme.onSecondaryContainer,
|
||||||
|
),
|
||||||
|
).center().padding(right: 8),
|
||||||
|
items: [...publishersMenu],
|
||||||
|
onChanged: (value) {
|
||||||
|
currentPublisher.value = value;
|
||||||
|
},
|
||||||
|
selectedItemBuilder: (context) {
|
||||||
|
return [
|
||||||
|
ProfilePictureWidget(
|
||||||
|
radius: 16,
|
||||||
|
fileId: currentPublisher.value?.pictureId,
|
||||||
|
).center().padding(right: 8),
|
||||||
|
];
|
||||||
|
},
|
||||||
|
buttonStyleData: ButtonStyleData(
|
||||||
|
height: 40,
|
||||||
|
padding: const EdgeInsets.only(left: 14, right: 8),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
dropdownStyleData: DropdownStyleData(
|
||||||
|
width: 320,
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 6),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(4),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
menuItemStyleData: const MenuItemStyleData(
|
||||||
|
height: 64,
|
||||||
|
padding: EdgeInsets.only(left: 14, right: 14),
|
||||||
|
),
|
||||||
|
iconStyleData: IconStyleData(
|
||||||
|
icon: Icon(Icons.arrow_drop_down),
|
||||||
|
iconSize: 19,
|
||||||
|
iconEnabledColor:
|
||||||
|
Theme.of(context).appBarTheme.foregroundColor!,
|
||||||
|
iconDisabledColor:
|
||||||
|
Theme.of(context).appBarTheme.foregroundColor!,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Gap(8),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
body: publisherStats.when(
|
||||||
|
data:
|
||||||
|
(stats) => SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
if (stats != null)
|
||||||
|
_PublisherStatsWidget(
|
||||||
|
stats: stats,
|
||||||
|
).padding(vertical: 12, horizontal: 12),
|
||||||
|
if (currentPublisher.value != null)
|
||||||
|
ListTile(
|
||||||
|
minTileHeight: 48,
|
||||||
|
title: Text('stickers').tr(),
|
||||||
|
trailing: Icon(Symbols.chevron_right),
|
||||||
|
leading: const Icon(Symbols.sticky_note),
|
||||||
|
contentPadding: EdgeInsets.symmetric(horizontal: 24),
|
||||||
|
onTap: () {
|
||||||
|
context.router.push(
|
||||||
|
StickersRoute(pubName: currentPublisher.value!.name),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
loading: () => const Center(child: CircularProgressIndicator()),
|
||||||
|
error: (_, __) => const SizedBox.shrink(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _PublisherStatsWidget extends StatelessWidget {
|
||||||
|
final SnPublisherStats stats;
|
||||||
|
const _PublisherStatsWidget({required this.stats});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
spacing: 8,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
spacing: 8,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: _buildStatsCard(
|
||||||
|
context,
|
||||||
|
stats.postsCreated.toString(),
|
||||||
|
'postsCreatedCount',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: _buildStatsCard(
|
||||||
|
context,
|
||||||
|
stats.stickerPacksCreated.toString(),
|
||||||
|
'stickerPacksCreatedCount',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: _buildStatsCard(
|
||||||
|
context,
|
||||||
|
stats.stickersCreated.toString(),
|
||||||
|
'stickersCreatedCount',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
spacing: 8,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: _buildStatsCard(
|
||||||
|
context,
|
||||||
|
stats.upvoteReceived.toString(),
|
||||||
|
'upvoteReceived',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: _buildStatsCard(
|
||||||
|
context,
|
||||||
|
stats.downvoteReceived.toString(),
|
||||||
|
'downvoteReceived',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildStatsCard(
|
||||||
|
BuildContext context,
|
||||||
|
String statValue,
|
||||||
|
String statLabel,
|
||||||
|
) {
|
||||||
|
return Card(
|
||||||
|
margin: EdgeInsets.zero,
|
||||||
|
child: SizedBox(
|
||||||
|
height: 100,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 8),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
statValue,
|
||||||
|
style: Theme.of(context).textTheme.headlineMedium,
|
||||||
|
),
|
||||||
|
const Gap(4),
|
||||||
|
Text(
|
||||||
|
statLabel,
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
).tr(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
153
lib/screens/creators/hub.g.dart
Normal file
153
lib/screens/creators/hub.g.dart
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'hub.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// RiverpodGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
String _$publisherStatsHash() => r'315705881d116b2aeac93f94f5ee2bc816d9f0f6';
|
||||||
|
|
||||||
|
/// Copied from Dart SDK
|
||||||
|
class _SystemHash {
|
||||||
|
_SystemHash._();
|
||||||
|
|
||||||
|
static int combine(int hash, int value) {
|
||||||
|
// ignore: parameter_assignments
|
||||||
|
hash = 0x1fffffff & (hash + value);
|
||||||
|
// ignore: parameter_assignments
|
||||||
|
hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
|
||||||
|
return hash ^ (hash >> 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int finish(int hash) {
|
||||||
|
// ignore: parameter_assignments
|
||||||
|
hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
|
||||||
|
// ignore: parameter_assignments
|
||||||
|
hash = hash ^ (hash >> 11);
|
||||||
|
return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See also [publisherStats].
|
||||||
|
@ProviderFor(publisherStats)
|
||||||
|
const publisherStatsProvider = PublisherStatsFamily();
|
||||||
|
|
||||||
|
/// See also [publisherStats].
|
||||||
|
class PublisherStatsFamily extends Family<AsyncValue<SnPublisherStats?>> {
|
||||||
|
/// See also [publisherStats].
|
||||||
|
const PublisherStatsFamily();
|
||||||
|
|
||||||
|
/// See also [publisherStats].
|
||||||
|
PublisherStatsProvider call(String? uname) {
|
||||||
|
return PublisherStatsProvider(uname);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
PublisherStatsProvider getProviderOverride(
|
||||||
|
covariant PublisherStatsProvider provider,
|
||||||
|
) {
|
||||||
|
return call(provider.uname);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const Iterable<ProviderOrFamily>? _dependencies = null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
|
||||||
|
|
||||||
|
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
|
||||||
|
_allTransitiveDependencies;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String? get name => r'publisherStatsProvider';
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See also [publisherStats].
|
||||||
|
class PublisherStatsProvider
|
||||||
|
extends AutoDisposeFutureProvider<SnPublisherStats?> {
|
||||||
|
/// See also [publisherStats].
|
||||||
|
PublisherStatsProvider(String? uname)
|
||||||
|
: this._internal(
|
||||||
|
(ref) => publisherStats(ref as PublisherStatsRef, uname),
|
||||||
|
from: publisherStatsProvider,
|
||||||
|
name: r'publisherStatsProvider',
|
||||||
|
debugGetCreateSourceHash:
|
||||||
|
const bool.fromEnvironment('dart.vm.product')
|
||||||
|
? null
|
||||||
|
: _$publisherStatsHash,
|
||||||
|
dependencies: PublisherStatsFamily._dependencies,
|
||||||
|
allTransitiveDependencies:
|
||||||
|
PublisherStatsFamily._allTransitiveDependencies,
|
||||||
|
uname: uname,
|
||||||
|
);
|
||||||
|
|
||||||
|
PublisherStatsProvider._internal(
|
||||||
|
super._createNotifier, {
|
||||||
|
required super.name,
|
||||||
|
required super.dependencies,
|
||||||
|
required super.allTransitiveDependencies,
|
||||||
|
required super.debugGetCreateSourceHash,
|
||||||
|
required super.from,
|
||||||
|
required this.uname,
|
||||||
|
}) : super.internal();
|
||||||
|
|
||||||
|
final String? uname;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Override overrideWith(
|
||||||
|
FutureOr<SnPublisherStats?> Function(PublisherStatsRef provider) create,
|
||||||
|
) {
|
||||||
|
return ProviderOverride(
|
||||||
|
origin: this,
|
||||||
|
override: PublisherStatsProvider._internal(
|
||||||
|
(ref) => create(ref as PublisherStatsRef),
|
||||||
|
from: from,
|
||||||
|
name: null,
|
||||||
|
dependencies: null,
|
||||||
|
allTransitiveDependencies: null,
|
||||||
|
debugGetCreateSourceHash: null,
|
||||||
|
uname: uname,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
AutoDisposeFutureProviderElement<SnPublisherStats?> createElement() {
|
||||||
|
return _PublisherStatsProviderElement(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return other is PublisherStatsProvider && other.uname == uname;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode {
|
||||||
|
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||||
|
hash = _SystemHash.combine(hash, uname.hashCode);
|
||||||
|
|
||||||
|
return _SystemHash.finish(hash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||||
|
// ignore: unused_element
|
||||||
|
mixin PublisherStatsRef on AutoDisposeFutureProviderRef<SnPublisherStats?> {
|
||||||
|
/// The parameter `uname` of this provider.
|
||||||
|
String? get uname;
|
||||||
|
}
|
||||||
|
|
||||||
|
class _PublisherStatsProviderElement
|
||||||
|
extends AutoDisposeFutureProviderElement<SnPublisherStats?>
|
||||||
|
with PublisherStatsRef {
|
||||||
|
_PublisherStatsProviderElement(super.provider);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String? get uname => (origin as PublisherStatsProvider).uname;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package
|
406
lib/screens/creators/stickers/pack_detail.dart
Normal file
406
lib/screens/creators/stickers/pack_detail.dart
Normal file
@ -0,0 +1,406 @@
|
|||||||
|
import 'package:auto_route/auto_route.dart';
|
||||||
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:island/models/sticker.dart';
|
||||||
|
import 'package:island/pods/network.dart';
|
||||||
|
import 'package:island/route.gr.dart';
|
||||||
|
import 'package:island/screens/creators/stickers/stickers.dart';
|
||||||
|
import 'package:island/widgets/alert.dart';
|
||||||
|
import 'package:island/widgets/app_scaffold.dart';
|
||||||
|
import 'package:island/widgets/content/cloud_file_picker.dart';
|
||||||
|
import 'package:island/widgets/content/cloud_files.dart';
|
||||||
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
|
import 'package:super_context_menu/super_context_menu.dart';
|
||||||
|
|
||||||
|
part 'pack_detail.g.dart';
|
||||||
|
part 'pack_detail.freezed.dart';
|
||||||
|
|
||||||
|
@riverpod
|
||||||
|
Future<List<SnSticker>> stickerPackContent(Ref ref, String packId) async {
|
||||||
|
final apiClient = ref.watch(apiClientProvider);
|
||||||
|
final resp = await apiClient.get('/stickers/$packId/content');
|
||||||
|
return resp.data
|
||||||
|
.map<SnSticker>((e) => SnSticker.fromJson(e))
|
||||||
|
.cast<SnSticker>()
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@RoutePage()
|
||||||
|
class StickerPackDetailScreen extends HookConsumerWidget {
|
||||||
|
final String id;
|
||||||
|
final String pubName;
|
||||||
|
const StickerPackDetailScreen({
|
||||||
|
super.key,
|
||||||
|
@PathParam('name') required this.pubName,
|
||||||
|
@PathParam('packId') required this.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final pack = ref.watch(stickerPackProvider(id));
|
||||||
|
final packContent = ref.watch(stickerPackContentProvider(id));
|
||||||
|
|
||||||
|
Future<void> deleteSticker(SnSticker sticker) async {
|
||||||
|
final confirm = await showConfirmAlert(
|
||||||
|
'deleteStickerHint'.tr(),
|
||||||
|
'deleteSticker'.tr(),
|
||||||
|
);
|
||||||
|
if (!confirm) return;
|
||||||
|
if (!context.mounted) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
showLoadingModal(context);
|
||||||
|
final apiClient = ref.watch(apiClientProvider);
|
||||||
|
await apiClient.delete('/stickers/$id/content/${sticker.id}');
|
||||||
|
ref.invalidate(stickerPackContentProvider(id));
|
||||||
|
} catch (err) {
|
||||||
|
showErrorAlert(err);
|
||||||
|
} finally {
|
||||||
|
if (context.mounted) hideLoadingModal(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return AppScaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text(pack.value?.name ?? 'loading'.tr()),
|
||||||
|
actions: [
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Symbols.add_circle),
|
||||||
|
onPressed: () {
|
||||||
|
AutoRouter.of(context).push(NewStickersRoute(packId: id)).then((
|
||||||
|
value,
|
||||||
|
) {
|
||||||
|
if (value != null) {
|
||||||
|
ref.invalidate(stickerPackContentProvider(id));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const Gap(8),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
body: pack.when(
|
||||||
|
data:
|
||||||
|
(pack) => Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: [
|
||||||
|
Text(pack!.description),
|
||||||
|
Row(
|
||||||
|
spacing: 4,
|
||||||
|
children: [
|
||||||
|
const Icon(Symbols.folder, size: 16),
|
||||||
|
Text(
|
||||||
|
'${packContent.value?.length ?? 0}/24',
|
||||||
|
style: GoogleFonts.robotoMono(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).opacity(0.85),
|
||||||
|
Row(
|
||||||
|
spacing: 4,
|
||||||
|
children: [
|
||||||
|
const Icon(Symbols.sell, size: 16),
|
||||||
|
Text(pack.prefix, style: GoogleFonts.robotoMono()),
|
||||||
|
],
|
||||||
|
).opacity(0.85),
|
||||||
|
Row(
|
||||||
|
spacing: 4,
|
||||||
|
children: [
|
||||||
|
const Icon(Symbols.tag, size: 16),
|
||||||
|
SelectableText(
|
||||||
|
pack.id,
|
||||||
|
style: GoogleFonts.robotoMono(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).opacity(0.85),
|
||||||
|
],
|
||||||
|
).padding(horizontal: 24, vertical: 24),
|
||||||
|
const Divider(height: 1),
|
||||||
|
Expanded(
|
||||||
|
child: packContent.when(
|
||||||
|
data:
|
||||||
|
(stickers) => RefreshIndicator(
|
||||||
|
onRefresh:
|
||||||
|
() => ref.refresh(
|
||||||
|
stickerPackContentProvider(id).future,
|
||||||
|
),
|
||||||
|
child: GridView.builder(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 24,
|
||||||
|
vertical: 20,
|
||||||
|
),
|
||||||
|
gridDelegate:
|
||||||
|
const SliverGridDelegateWithMaxCrossAxisExtent(
|
||||||
|
maxCrossAxisExtent: 48,
|
||||||
|
mainAxisSpacing: 8,
|
||||||
|
crossAxisSpacing: 8,
|
||||||
|
),
|
||||||
|
itemCount: stickers.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final sticker = stickers[index];
|
||||||
|
return ContextMenuWidget(
|
||||||
|
menuProvider: (_) {
|
||||||
|
return Menu(
|
||||||
|
children: [
|
||||||
|
MenuAction(
|
||||||
|
title: 'edit'.tr(),
|
||||||
|
image: MenuImage.icon(Symbols.edit),
|
||||||
|
callback: () {
|
||||||
|
context.router
|
||||||
|
.push(
|
||||||
|
EditStickersRoute(
|
||||||
|
packId: id,
|
||||||
|
id: sticker.id,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.then((value) {
|
||||||
|
if (value != null) {
|
||||||
|
ref.invalidate(
|
||||||
|
stickerPackContentProvider(
|
||||||
|
id,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
MenuAction(
|
||||||
|
title: 'delete'.tr(),
|
||||||
|
image: MenuImage.icon(Symbols.delete),
|
||||||
|
callback: () {
|
||||||
|
deleteSticker(sticker);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: ClipRRect(
|
||||||
|
borderRadius: BorderRadius.all(
|
||||||
|
Radius.circular(8),
|
||||||
|
),
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color:
|
||||||
|
Theme.of(
|
||||||
|
context,
|
||||||
|
).colorScheme.surfaceContainer,
|
||||||
|
borderRadius: BorderRadius.all(
|
||||||
|
Radius.circular(8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: CloudImageWidget(
|
||||||
|
fileId: sticker.imageId,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
error:
|
||||||
|
(err, _) =>
|
||||||
|
Text(
|
||||||
|
'Error: $err',
|
||||||
|
).textAlignment(TextAlign.center).center(),
|
||||||
|
loading: () => const CircularProgressIndicator().center(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
error:
|
||||||
|
(err, _) =>
|
||||||
|
Text('Error: $err').textAlignment(TextAlign.center).center(),
|
||||||
|
loading: () => const CircularProgressIndicator().center(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
abstract class StickerWithPackQuery with _$StickerWithPackQuery {
|
||||||
|
const factory StickerWithPackQuery({
|
||||||
|
required String packId,
|
||||||
|
required String id,
|
||||||
|
}) = _StickerWithPackQuery;
|
||||||
|
}
|
||||||
|
|
||||||
|
@riverpod
|
||||||
|
Future<SnSticker?> stickerPackSticker(
|
||||||
|
Ref ref,
|
||||||
|
StickerWithPackQuery? query,
|
||||||
|
) async {
|
||||||
|
if (query == null) return null;
|
||||||
|
final apiClient = ref.watch(apiClientProvider);
|
||||||
|
final resp = await apiClient.get(
|
||||||
|
'/stickers/${query.packId}/content/${query.id}',
|
||||||
|
);
|
||||||
|
if (resp.data == null) return null;
|
||||||
|
return SnSticker.fromJson(resp.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
@RoutePage()
|
||||||
|
class NewStickersScreen extends StatelessWidget {
|
||||||
|
final String packId;
|
||||||
|
const NewStickersScreen({
|
||||||
|
super.key,
|
||||||
|
@PathParam('packId') required this.packId,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return EditStickersScreen(packId: packId, id: null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@RoutePage()
|
||||||
|
class EditStickersScreen extends HookConsumerWidget {
|
||||||
|
final String packId;
|
||||||
|
final String? id;
|
||||||
|
const EditStickersScreen({
|
||||||
|
super.key,
|
||||||
|
@PathParam("packId") required this.packId,
|
||||||
|
@PathParam("id") required this.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final sticker = ref.watch(
|
||||||
|
stickerPackStickerProvider(
|
||||||
|
id == null ? null : StickerWithPackQuery(packId: packId, id: id!),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final formKey = useMemoized(() => GlobalKey<FormState>(), []);
|
||||||
|
|
||||||
|
final image = useState<String?>(id == null ? '' : sticker.value?.imageId);
|
||||||
|
final imageController = useTextEditingController(text: image.value);
|
||||||
|
final slugController = useTextEditingController(
|
||||||
|
text: id == null ? '' : sticker.value?.slug,
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() {
|
||||||
|
if (sticker.value != null) {
|
||||||
|
image.value = sticker.value!.imageId;
|
||||||
|
imageController.text = sticker.value!.imageId;
|
||||||
|
slugController.text = sticker.value!.slug;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}, [sticker]);
|
||||||
|
|
||||||
|
final submitting = useState(false);
|
||||||
|
|
||||||
|
Future<void> submit() async {
|
||||||
|
final apiClient = ref.watch(apiClientProvider);
|
||||||
|
submitting.value = true;
|
||||||
|
try {
|
||||||
|
final resp = await apiClient.request(
|
||||||
|
id == null
|
||||||
|
? '/stickers/$packId/content'
|
||||||
|
: '/stickers/$packId/content/$id',
|
||||||
|
data: {'slug': slugController.text, 'image_id': imageController.text},
|
||||||
|
options: Options(method: id == null ? 'POST' : 'PATCH'),
|
||||||
|
);
|
||||||
|
if (context.mounted) {
|
||||||
|
Navigator.pop(context, SnSticker.fromJson(resp.data));
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
showErrorAlert(err);
|
||||||
|
} finally {
|
||||||
|
submitting.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return AppScaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text(id == null ? 'createSticker' : 'editSticker').tr(),
|
||||||
|
),
|
||||||
|
body: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
height: 96,
|
||||||
|
width: 96,
|
||||||
|
child: ClipRRect(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(8)),
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(8)),
|
||||||
|
),
|
||||||
|
child:
|
||||||
|
(image.value?.isEmpty ?? true)
|
||||||
|
? const SizedBox.shrink()
|
||||||
|
: CloudImageWidget(fileId: image.value!),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Gap(16),
|
||||||
|
Form(
|
||||||
|
key: formKey,
|
||||||
|
child: Column(
|
||||||
|
spacing: 8,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: [
|
||||||
|
TextFormField(
|
||||||
|
controller: imageController,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: 'stickerImage'.tr(),
|
||||||
|
border: const UnderlineInputBorder(),
|
||||||
|
suffix: InkWell(
|
||||||
|
onTap: () {
|
||||||
|
showModalBottomSheet(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => CloudFilePicker(),
|
||||||
|
).then((value) {
|
||||||
|
if (value == null) return;
|
||||||
|
image.value = value[0].id;
|
||||||
|
imageController.text = image.value!;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(8)),
|
||||||
|
child: const Icon(
|
||||||
|
Symbols.cloud_upload,
|
||||||
|
).padding(horizontal: 4),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
readOnly: true,
|
||||||
|
onTapOutside:
|
||||||
|
(_) => FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
|
),
|
||||||
|
TextFormField(
|
||||||
|
controller: slugController,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: 'stickerSlug'.tr(),
|
||||||
|
helperText: 'stickerSlugHint'.tr(),
|
||||||
|
border: const UnderlineInputBorder(),
|
||||||
|
),
|
||||||
|
onTapOutside:
|
||||||
|
(_) => FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Gap(12),
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.centerRight,
|
||||||
|
child: TextButton.icon(
|
||||||
|
onPressed: submitting.value ? null : submit,
|
||||||
|
icon: const Icon(Symbols.save),
|
||||||
|
label: Text(id == null ? 'create' : 'saveChanges').tr(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).padding(horizontal: 24, vertical: 24),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
145
lib/screens/creators/stickers/pack_detail.freezed.dart
Normal file
145
lib/screens/creators/stickers/pack_detail.freezed.dart
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
// dart format width=80
|
||||||
|
// 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 'pack_detail.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// FreezedGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
// dart format off
|
||||||
|
T _$identity<T>(T value) => value;
|
||||||
|
/// @nodoc
|
||||||
|
mixin _$StickerWithPackQuery {
|
||||||
|
|
||||||
|
String get packId; String get id;
|
||||||
|
/// Create a copy of StickerWithPackQuery
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
$StickerWithPackQueryCopyWith<StickerWithPackQuery> get copyWith => _$StickerWithPackQueryCopyWithImpl<StickerWithPackQuery>(this as StickerWithPackQuery, _$identity);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is StickerWithPackQuery&&(identical(other.packId, packId) || other.packId == packId)&&(identical(other.id, id) || other.id == id));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(runtimeType,packId,id);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'StickerWithPackQuery(packId: $packId, id: $id)';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract mixin class $StickerWithPackQueryCopyWith<$Res> {
|
||||||
|
factory $StickerWithPackQueryCopyWith(StickerWithPackQuery value, $Res Function(StickerWithPackQuery) _then) = _$StickerWithPackQueryCopyWithImpl;
|
||||||
|
@useResult
|
||||||
|
$Res call({
|
||||||
|
String packId, String id
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
/// @nodoc
|
||||||
|
class _$StickerWithPackQueryCopyWithImpl<$Res>
|
||||||
|
implements $StickerWithPackQueryCopyWith<$Res> {
|
||||||
|
_$StickerWithPackQueryCopyWithImpl(this._self, this._then);
|
||||||
|
|
||||||
|
final StickerWithPackQuery _self;
|
||||||
|
final $Res Function(StickerWithPackQuery) _then;
|
||||||
|
|
||||||
|
/// Create a copy of StickerWithPackQuery
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline') @override $Res call({Object? packId = null,Object? id = null,}) {
|
||||||
|
return _then(_self.copyWith(
|
||||||
|
packId: null == packId ? _self.packId : packId // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
|
||||||
|
|
||||||
|
class _StickerWithPackQuery implements StickerWithPackQuery {
|
||||||
|
const _StickerWithPackQuery({required this.packId, required this.id});
|
||||||
|
|
||||||
|
|
||||||
|
@override final String packId;
|
||||||
|
@override final String id;
|
||||||
|
|
||||||
|
/// Create a copy of StickerWithPackQuery
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$StickerWithPackQueryCopyWith<_StickerWithPackQuery> get copyWith => __$StickerWithPackQueryCopyWithImpl<_StickerWithPackQuery>(this, _$identity);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is _StickerWithPackQuery&&(identical(other.packId, packId) || other.packId == packId)&&(identical(other.id, id) || other.id == id));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(runtimeType,packId,id);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'StickerWithPackQuery(packId: $packId, id: $id)';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract mixin class _$StickerWithPackQueryCopyWith<$Res> implements $StickerWithPackQueryCopyWith<$Res> {
|
||||||
|
factory _$StickerWithPackQueryCopyWith(_StickerWithPackQuery value, $Res Function(_StickerWithPackQuery) _then) = __$StickerWithPackQueryCopyWithImpl;
|
||||||
|
@override @useResult
|
||||||
|
$Res call({
|
||||||
|
String packId, String id
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
/// @nodoc
|
||||||
|
class __$StickerWithPackQueryCopyWithImpl<$Res>
|
||||||
|
implements _$StickerWithPackQueryCopyWith<$Res> {
|
||||||
|
__$StickerWithPackQueryCopyWithImpl(this._self, this._then);
|
||||||
|
|
||||||
|
final _StickerWithPackQuery _self;
|
||||||
|
final $Res Function(_StickerWithPackQuery) _then;
|
||||||
|
|
||||||
|
/// Create a copy of StickerWithPackQuery
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override @pragma('vm:prefer-inline') $Res call({Object? packId = null,Object? id = null,}) {
|
||||||
|
return _then(_StickerWithPackQuery(
|
||||||
|
packId: null == packId ? _self.packId : packId // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// dart format on
|
277
lib/screens/creators/stickers/pack_detail.g.dart
Normal file
277
lib/screens/creators/stickers/pack_detail.g.dart
Normal file
@ -0,0 +1,277 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'pack_detail.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// RiverpodGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
String _$stickerPackContentHash() =>
|
||||||
|
r'78de848fba1f341f217f8ae4b9eef2d8afa67964';
|
||||||
|
|
||||||
|
/// Copied from Dart SDK
|
||||||
|
class _SystemHash {
|
||||||
|
_SystemHash._();
|
||||||
|
|
||||||
|
static int combine(int hash, int value) {
|
||||||
|
// ignore: parameter_assignments
|
||||||
|
hash = 0x1fffffff & (hash + value);
|
||||||
|
// ignore: parameter_assignments
|
||||||
|
hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
|
||||||
|
return hash ^ (hash >> 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int finish(int hash) {
|
||||||
|
// ignore: parameter_assignments
|
||||||
|
hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
|
||||||
|
// ignore: parameter_assignments
|
||||||
|
hash = hash ^ (hash >> 11);
|
||||||
|
return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See also [stickerPackContent].
|
||||||
|
@ProviderFor(stickerPackContent)
|
||||||
|
const stickerPackContentProvider = StickerPackContentFamily();
|
||||||
|
|
||||||
|
/// See also [stickerPackContent].
|
||||||
|
class StickerPackContentFamily extends Family<AsyncValue<List<SnSticker>>> {
|
||||||
|
/// See also [stickerPackContent].
|
||||||
|
const StickerPackContentFamily();
|
||||||
|
|
||||||
|
/// See also [stickerPackContent].
|
||||||
|
StickerPackContentProvider call(String packId) {
|
||||||
|
return StickerPackContentProvider(packId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
StickerPackContentProvider getProviderOverride(
|
||||||
|
covariant StickerPackContentProvider provider,
|
||||||
|
) {
|
||||||
|
return call(provider.packId);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const Iterable<ProviderOrFamily>? _dependencies = null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
|
||||||
|
|
||||||
|
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
|
||||||
|
_allTransitiveDependencies;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String? get name => r'stickerPackContentProvider';
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See also [stickerPackContent].
|
||||||
|
class StickerPackContentProvider
|
||||||
|
extends AutoDisposeFutureProvider<List<SnSticker>> {
|
||||||
|
/// See also [stickerPackContent].
|
||||||
|
StickerPackContentProvider(String packId)
|
||||||
|
: this._internal(
|
||||||
|
(ref) => stickerPackContent(ref as StickerPackContentRef, packId),
|
||||||
|
from: stickerPackContentProvider,
|
||||||
|
name: r'stickerPackContentProvider',
|
||||||
|
debugGetCreateSourceHash:
|
||||||
|
const bool.fromEnvironment('dart.vm.product')
|
||||||
|
? null
|
||||||
|
: _$stickerPackContentHash,
|
||||||
|
dependencies: StickerPackContentFamily._dependencies,
|
||||||
|
allTransitiveDependencies:
|
||||||
|
StickerPackContentFamily._allTransitiveDependencies,
|
||||||
|
packId: packId,
|
||||||
|
);
|
||||||
|
|
||||||
|
StickerPackContentProvider._internal(
|
||||||
|
super._createNotifier, {
|
||||||
|
required super.name,
|
||||||
|
required super.dependencies,
|
||||||
|
required super.allTransitiveDependencies,
|
||||||
|
required super.debugGetCreateSourceHash,
|
||||||
|
required super.from,
|
||||||
|
required this.packId,
|
||||||
|
}) : super.internal();
|
||||||
|
|
||||||
|
final String packId;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Override overrideWith(
|
||||||
|
FutureOr<List<SnSticker>> Function(StickerPackContentRef provider) create,
|
||||||
|
) {
|
||||||
|
return ProviderOverride(
|
||||||
|
origin: this,
|
||||||
|
override: StickerPackContentProvider._internal(
|
||||||
|
(ref) => create(ref as StickerPackContentRef),
|
||||||
|
from: from,
|
||||||
|
name: null,
|
||||||
|
dependencies: null,
|
||||||
|
allTransitiveDependencies: null,
|
||||||
|
debugGetCreateSourceHash: null,
|
||||||
|
packId: packId,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
AutoDisposeFutureProviderElement<List<SnSticker>> createElement() {
|
||||||
|
return _StickerPackContentProviderElement(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return other is StickerPackContentProvider && other.packId == packId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode {
|
||||||
|
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||||
|
hash = _SystemHash.combine(hash, packId.hashCode);
|
||||||
|
|
||||||
|
return _SystemHash.finish(hash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||||
|
// ignore: unused_element
|
||||||
|
mixin StickerPackContentRef on AutoDisposeFutureProviderRef<List<SnSticker>> {
|
||||||
|
/// The parameter `packId` of this provider.
|
||||||
|
String get packId;
|
||||||
|
}
|
||||||
|
|
||||||
|
class _StickerPackContentProviderElement
|
||||||
|
extends AutoDisposeFutureProviderElement<List<SnSticker>>
|
||||||
|
with StickerPackContentRef {
|
||||||
|
_StickerPackContentProviderElement(super.provider);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get packId => (origin as StickerPackContentProvider).packId;
|
||||||
|
}
|
||||||
|
|
||||||
|
String _$stickerPackStickerHash() =>
|
||||||
|
r'36f524c047e632236d5597aaaa8678ed86599602';
|
||||||
|
|
||||||
|
/// See also [stickerPackSticker].
|
||||||
|
@ProviderFor(stickerPackSticker)
|
||||||
|
const stickerPackStickerProvider = StickerPackStickerFamily();
|
||||||
|
|
||||||
|
/// See also [stickerPackSticker].
|
||||||
|
class StickerPackStickerFamily extends Family<AsyncValue<SnSticker?>> {
|
||||||
|
/// See also [stickerPackSticker].
|
||||||
|
const StickerPackStickerFamily();
|
||||||
|
|
||||||
|
/// See also [stickerPackSticker].
|
||||||
|
StickerPackStickerProvider call(StickerWithPackQuery? query) {
|
||||||
|
return StickerPackStickerProvider(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
StickerPackStickerProvider getProviderOverride(
|
||||||
|
covariant StickerPackStickerProvider provider,
|
||||||
|
) {
|
||||||
|
return call(provider.query);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const Iterable<ProviderOrFamily>? _dependencies = null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
|
||||||
|
|
||||||
|
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
|
||||||
|
_allTransitiveDependencies;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String? get name => r'stickerPackStickerProvider';
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See also [stickerPackSticker].
|
||||||
|
class StickerPackStickerProvider extends AutoDisposeFutureProvider<SnSticker?> {
|
||||||
|
/// See also [stickerPackSticker].
|
||||||
|
StickerPackStickerProvider(StickerWithPackQuery? query)
|
||||||
|
: this._internal(
|
||||||
|
(ref) => stickerPackSticker(ref as StickerPackStickerRef, query),
|
||||||
|
from: stickerPackStickerProvider,
|
||||||
|
name: r'stickerPackStickerProvider',
|
||||||
|
debugGetCreateSourceHash:
|
||||||
|
const bool.fromEnvironment('dart.vm.product')
|
||||||
|
? null
|
||||||
|
: _$stickerPackStickerHash,
|
||||||
|
dependencies: StickerPackStickerFamily._dependencies,
|
||||||
|
allTransitiveDependencies:
|
||||||
|
StickerPackStickerFamily._allTransitiveDependencies,
|
||||||
|
query: query,
|
||||||
|
);
|
||||||
|
|
||||||
|
StickerPackStickerProvider._internal(
|
||||||
|
super._createNotifier, {
|
||||||
|
required super.name,
|
||||||
|
required super.dependencies,
|
||||||
|
required super.allTransitiveDependencies,
|
||||||
|
required super.debugGetCreateSourceHash,
|
||||||
|
required super.from,
|
||||||
|
required this.query,
|
||||||
|
}) : super.internal();
|
||||||
|
|
||||||
|
final StickerWithPackQuery? query;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Override overrideWith(
|
||||||
|
FutureOr<SnSticker?> Function(StickerPackStickerRef provider) create,
|
||||||
|
) {
|
||||||
|
return ProviderOverride(
|
||||||
|
origin: this,
|
||||||
|
override: StickerPackStickerProvider._internal(
|
||||||
|
(ref) => create(ref as StickerPackStickerRef),
|
||||||
|
from: from,
|
||||||
|
name: null,
|
||||||
|
dependencies: null,
|
||||||
|
allTransitiveDependencies: null,
|
||||||
|
debugGetCreateSourceHash: null,
|
||||||
|
query: query,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
AutoDisposeFutureProviderElement<SnSticker?> createElement() {
|
||||||
|
return _StickerPackStickerProviderElement(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return other is StickerPackStickerProvider && other.query == query;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode {
|
||||||
|
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||||
|
hash = _SystemHash.combine(hash, query.hashCode);
|
||||||
|
|
||||||
|
return _SystemHash.finish(hash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||||
|
// ignore: unused_element
|
||||||
|
mixin StickerPackStickerRef on AutoDisposeFutureProviderRef<SnSticker?> {
|
||||||
|
/// The parameter `query` of this provider.
|
||||||
|
StickerWithPackQuery? get query;
|
||||||
|
}
|
||||||
|
|
||||||
|
class _StickerPackStickerProviderElement
|
||||||
|
extends AutoDisposeFutureProviderElement<SnSticker?>
|
||||||
|
with StickerPackStickerRef {
|
||||||
|
_StickerPackStickerProviderElement(super.provider);
|
||||||
|
|
||||||
|
@override
|
||||||
|
StickerWithPackQuery? get query =>
|
||||||
|
(origin as StickerPackStickerProvider).query;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package
|
299
lib/screens/creators/stickers/stickers.dart
Normal file
299
lib/screens/creators/stickers/stickers.dart
Normal file
@ -0,0 +1,299 @@
|
|||||||
|
import 'package:auto_route/auto_route.dart';
|
||||||
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:island/models/sticker.dart';
|
||||||
|
import 'package:island/pods/network.dart';
|
||||||
|
import 'package:island/route.gr.dart';
|
||||||
|
import 'package:island/widgets/alert.dart';
|
||||||
|
import 'package:island/widgets/app_scaffold.dart';
|
||||||
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
|
import 'package:very_good_infinite_list/very_good_infinite_list.dart';
|
||||||
|
|
||||||
|
part 'stickers.g.dart';
|
||||||
|
|
||||||
|
@RoutePage()
|
||||||
|
class StickersScreen extends HookConsumerWidget {
|
||||||
|
final String pubName;
|
||||||
|
const StickersScreen({super.key, @PathParam("name") required this.pubName});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final stickersState = ref.watch(stickerPacksProvider);
|
||||||
|
final stickersNotifier = ref.watch(stickerPacksProvider.notifier);
|
||||||
|
|
||||||
|
return AppScaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: const Text('stickers').tr(),
|
||||||
|
actions: [
|
||||||
|
IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
context.router.push(NewStickerPacksRoute(pubName: pubName)).then((
|
||||||
|
value,
|
||||||
|
) {
|
||||||
|
if (value != null) {
|
||||||
|
stickersNotifier.refresh();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
icon: const Icon(Symbols.add_circle),
|
||||||
|
),
|
||||||
|
const Gap(8),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
body: stickersState.when(
|
||||||
|
data:
|
||||||
|
(stickers) => RefreshIndicator(
|
||||||
|
onRefresh: stickersNotifier.refresh,
|
||||||
|
child: InfiniteList(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
itemCount: stickers.length,
|
||||||
|
hasReachedMax: stickersNotifier.isReachedMax,
|
||||||
|
isLoading: stickersNotifier.isLoading,
|
||||||
|
onFetchData: stickersNotifier.fetchMore,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
return ListTile(
|
||||||
|
title: Text(stickers[index].name),
|
||||||
|
subtitle: Text(stickers[index].description),
|
||||||
|
trailing: const Icon(Symbols.chevron_right),
|
||||||
|
onTap: () {
|
||||||
|
context.router.push(
|
||||||
|
StickerPackDetailRoute(
|
||||||
|
pubName: pubName,
|
||||||
|
id: stickers[index].id,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
loading: () => const CircularProgressIndicator(),
|
||||||
|
error: (error, stack) => Text('Error: $error'),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final stickerPacksProvider = StateNotifierProvider<
|
||||||
|
StickerPacksNotifier,
|
||||||
|
AsyncValue<List<SnStickerPack>>
|
||||||
|
>((ref) {
|
||||||
|
return StickerPacksNotifier(ref.watch(apiClientProvider));
|
||||||
|
});
|
||||||
|
|
||||||
|
class StickerPacksNotifier
|
||||||
|
extends StateNotifier<AsyncValue<List<SnStickerPack>>> {
|
||||||
|
final Dio _apiClient;
|
||||||
|
StickerPacksNotifier(this._apiClient) : super(const AsyncValue.loading()) {
|
||||||
|
fetchStickers();
|
||||||
|
}
|
||||||
|
|
||||||
|
int offset = 0;
|
||||||
|
int take = 20;
|
||||||
|
int total = 0;
|
||||||
|
|
||||||
|
bool isLoading = false;
|
||||||
|
bool get isReachedMax =>
|
||||||
|
state.valueOrNull != null && state.valueOrNull!.length >= total;
|
||||||
|
|
||||||
|
Future<void> fetchStickers() async {
|
||||||
|
if (isLoading) return;
|
||||||
|
isLoading = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
final response = await _apiClient.get(
|
||||||
|
'/stickers?offset=$offset&take=$take',
|
||||||
|
);
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
total = int.parse(response.headers.value('X-Total') ?? '0');
|
||||||
|
final newStickers =
|
||||||
|
response.data
|
||||||
|
.map((e) => SnStickerPack.fromJson(e))
|
||||||
|
.cast<SnStickerPack>()
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
state = AsyncValue.data(
|
||||||
|
state.valueOrNull != null
|
||||||
|
? [...state.value!, ...newStickers]
|
||||||
|
: newStickers,
|
||||||
|
);
|
||||||
|
offset += take;
|
||||||
|
} else {
|
||||||
|
state = AsyncValue.error('Failed to load stickers', StackTrace.current);
|
||||||
|
}
|
||||||
|
} catch (err, stackTrace) {
|
||||||
|
state = AsyncValue.error(err, stackTrace);
|
||||||
|
} finally {
|
||||||
|
isLoading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> fetchMore() async {
|
||||||
|
if (state.valueOrNull == null || state.valueOrNull!.length >= total) return;
|
||||||
|
await fetchStickers();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> refresh() async {
|
||||||
|
offset = 0;
|
||||||
|
state = const AsyncValue.loading();
|
||||||
|
await fetchStickers();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@riverpod
|
||||||
|
Future<SnStickerPack?> stickerPack(Ref ref, String? packId) async {
|
||||||
|
if (packId == null) return null;
|
||||||
|
final apiClient = ref.watch(apiClientProvider);
|
||||||
|
final resp = await apiClient.get('/stickers/$packId');
|
||||||
|
return SnStickerPack.fromJson(resp.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
@RoutePage()
|
||||||
|
class NewStickerPacksScreen extends HookConsumerWidget {
|
||||||
|
final String pubName;
|
||||||
|
const NewStickerPacksScreen({
|
||||||
|
super.key,
|
||||||
|
@PathParam("name") required this.pubName,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
return EditStickerPacksScreen(pubName: pubName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@RoutePage()
|
||||||
|
class EditStickerPacksScreen extends HookConsumerWidget {
|
||||||
|
final String pubName;
|
||||||
|
final String? packId;
|
||||||
|
const EditStickerPacksScreen({
|
||||||
|
super.key,
|
||||||
|
@PathParam("name") required this.pubName,
|
||||||
|
@PathParam("packId") this.packId,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final formKey = useMemoized(() => GlobalKey<FormState>(), []);
|
||||||
|
final initialPack = ref.watch(stickerPackProvider(packId));
|
||||||
|
|
||||||
|
final nameController = useTextEditingController();
|
||||||
|
final descriptionController = useTextEditingController();
|
||||||
|
final prefixController = useTextEditingController();
|
||||||
|
|
||||||
|
useEffect(() {
|
||||||
|
if (initialPack.value != null) {
|
||||||
|
nameController.text = initialPack.value!.name;
|
||||||
|
descriptionController.text = initialPack.value!.description;
|
||||||
|
prefixController.text = initialPack.value!.prefix;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}, [initialPack]);
|
||||||
|
|
||||||
|
final submitting = useState(false);
|
||||||
|
|
||||||
|
Future<void> submit() async {
|
||||||
|
if (!(formKey.currentState?.validate() ?? false)) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
submitting.value = true;
|
||||||
|
final apiClient = ref.watch(apiClientProvider);
|
||||||
|
final resp = await apiClient.request(
|
||||||
|
'/stickers',
|
||||||
|
data: {
|
||||||
|
'name': nameController.text,
|
||||||
|
'description': descriptionController.text,
|
||||||
|
'prefix': prefixController.text,
|
||||||
|
},
|
||||||
|
options: Options(
|
||||||
|
method: packId == null ? 'POST' : 'PATCH',
|
||||||
|
headers: {'X-Pub': pubName},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
if (!context.mounted) return;
|
||||||
|
context.router.maybePop(SnStickerPack.fromJson(resp.data));
|
||||||
|
} catch (err) {
|
||||||
|
showErrorAlert(err);
|
||||||
|
} finally {
|
||||||
|
submitting.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return AppScaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title:
|
||||||
|
Text(packId == null ? 'createStickerPack' : 'editStickerPack').tr(),
|
||||||
|
),
|
||||||
|
body: Column(
|
||||||
|
children: [
|
||||||
|
Form(
|
||||||
|
key: formKey,
|
||||||
|
child: Column(
|
||||||
|
spacing: 8,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: [
|
||||||
|
TextFormField(
|
||||||
|
controller: nameController,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: 'name'.tr(),
|
||||||
|
border: const UnderlineInputBorder(),
|
||||||
|
),
|
||||||
|
validator: (value) {
|
||||||
|
if (value == null || value.isEmpty) {
|
||||||
|
return 'fieldCannotBeEmpty'.tr();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
onTapOutside:
|
||||||
|
(_) => FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
|
),
|
||||||
|
TextFormField(
|
||||||
|
controller: descriptionController,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: 'description'.tr(),
|
||||||
|
border: const UnderlineInputBorder(),
|
||||||
|
),
|
||||||
|
minLines: 3,
|
||||||
|
maxLines: null,
|
||||||
|
onTapOutside:
|
||||||
|
(_) => FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
|
),
|
||||||
|
TextFormField(
|
||||||
|
controller: prefixController,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: 'stickerPackPrefix'.tr(),
|
||||||
|
border: const UnderlineInputBorder(),
|
||||||
|
helperText: 'deleteStickerHint'.tr(),
|
||||||
|
),
|
||||||
|
validator: (value) {
|
||||||
|
if (value == null || value.isEmpty) {
|
||||||
|
return 'fieldCannotBeEmpty'.tr();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
onTapOutside:
|
||||||
|
(_) => FocusManager.instance.primaryFocus?.unfocus(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Gap(12),
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.centerRight,
|
||||||
|
child: TextButton.icon(
|
||||||
|
onPressed: submitting.value ? null : submit,
|
||||||
|
icon: const Icon(Symbols.save),
|
||||||
|
label: Text(packId == null ? 'create'.tr() : 'saveChanges'.tr()),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).padding(horizontal: 24, vertical: 16),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
151
lib/screens/creators/stickers/stickers.g.dart
Normal file
151
lib/screens/creators/stickers/stickers.g.dart
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'stickers.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// RiverpodGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
String _$stickerPackHash() => r'4f70d26e695ba1d8c7273d12730f77da79361733';
|
||||||
|
|
||||||
|
/// Copied from Dart SDK
|
||||||
|
class _SystemHash {
|
||||||
|
_SystemHash._();
|
||||||
|
|
||||||
|
static int combine(int hash, int value) {
|
||||||
|
// ignore: parameter_assignments
|
||||||
|
hash = 0x1fffffff & (hash + value);
|
||||||
|
// ignore: parameter_assignments
|
||||||
|
hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
|
||||||
|
return hash ^ (hash >> 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int finish(int hash) {
|
||||||
|
// ignore: parameter_assignments
|
||||||
|
hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
|
||||||
|
// ignore: parameter_assignments
|
||||||
|
hash = hash ^ (hash >> 11);
|
||||||
|
return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See also [stickerPack].
|
||||||
|
@ProviderFor(stickerPack)
|
||||||
|
const stickerPackProvider = StickerPackFamily();
|
||||||
|
|
||||||
|
/// See also [stickerPack].
|
||||||
|
class StickerPackFamily extends Family<AsyncValue<SnStickerPack?>> {
|
||||||
|
/// See also [stickerPack].
|
||||||
|
const StickerPackFamily();
|
||||||
|
|
||||||
|
/// See also [stickerPack].
|
||||||
|
StickerPackProvider call(String? packId) {
|
||||||
|
return StickerPackProvider(packId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
StickerPackProvider getProviderOverride(
|
||||||
|
covariant StickerPackProvider provider,
|
||||||
|
) {
|
||||||
|
return call(provider.packId);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const Iterable<ProviderOrFamily>? _dependencies = null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
|
||||||
|
|
||||||
|
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
|
||||||
|
_allTransitiveDependencies;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String? get name => r'stickerPackProvider';
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See also [stickerPack].
|
||||||
|
class StickerPackProvider extends AutoDisposeFutureProvider<SnStickerPack?> {
|
||||||
|
/// See also [stickerPack].
|
||||||
|
StickerPackProvider(String? packId)
|
||||||
|
: this._internal(
|
||||||
|
(ref) => stickerPack(ref as StickerPackRef, packId),
|
||||||
|
from: stickerPackProvider,
|
||||||
|
name: r'stickerPackProvider',
|
||||||
|
debugGetCreateSourceHash:
|
||||||
|
const bool.fromEnvironment('dart.vm.product')
|
||||||
|
? null
|
||||||
|
: _$stickerPackHash,
|
||||||
|
dependencies: StickerPackFamily._dependencies,
|
||||||
|
allTransitiveDependencies: StickerPackFamily._allTransitiveDependencies,
|
||||||
|
packId: packId,
|
||||||
|
);
|
||||||
|
|
||||||
|
StickerPackProvider._internal(
|
||||||
|
super._createNotifier, {
|
||||||
|
required super.name,
|
||||||
|
required super.dependencies,
|
||||||
|
required super.allTransitiveDependencies,
|
||||||
|
required super.debugGetCreateSourceHash,
|
||||||
|
required super.from,
|
||||||
|
required this.packId,
|
||||||
|
}) : super.internal();
|
||||||
|
|
||||||
|
final String? packId;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Override overrideWith(
|
||||||
|
FutureOr<SnStickerPack?> Function(StickerPackRef provider) create,
|
||||||
|
) {
|
||||||
|
return ProviderOverride(
|
||||||
|
origin: this,
|
||||||
|
override: StickerPackProvider._internal(
|
||||||
|
(ref) => create(ref as StickerPackRef),
|
||||||
|
from: from,
|
||||||
|
name: null,
|
||||||
|
dependencies: null,
|
||||||
|
allTransitiveDependencies: null,
|
||||||
|
debugGetCreateSourceHash: null,
|
||||||
|
packId: packId,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
AutoDisposeFutureProviderElement<SnStickerPack?> createElement() {
|
||||||
|
return _StickerPackProviderElement(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return other is StickerPackProvider && other.packId == packId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode {
|
||||||
|
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||||
|
hash = _SystemHash.combine(hash, packId.hashCode);
|
||||||
|
|
||||||
|
return _SystemHash.finish(hash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||||
|
// ignore: unused_element
|
||||||
|
mixin StickerPackRef on AutoDisposeFutureProviderRef<SnStickerPack?> {
|
||||||
|
/// The parameter `packId` of this provider.
|
||||||
|
String? get packId;
|
||||||
|
}
|
||||||
|
|
||||||
|
class _StickerPackProviderElement
|
||||||
|
extends AutoDisposeFutureProviderElement<SnStickerPack?>
|
||||||
|
with StickerPackRef {
|
||||||
|
_StickerPackProviderElement(super.provider);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String? get packId => (origin as StickerPackProvider).packId;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package
|
@ -436,8 +436,17 @@ class AttachmentPreview extends StatelessWidget {
|
|||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Text('Uploading', style: TextStyle(color: Colors.white)),
|
if (progress != null)
|
||||||
Gap(4),
|
Text(
|
||||||
|
'${progress!.toStringAsFixed(2)}%',
|
||||||
|
style: TextStyle(color: Colors.white),
|
||||||
|
)
|
||||||
|
else
|
||||||
|
Text(
|
||||||
|
'uploading'.tr(),
|
||||||
|
style: TextStyle(color: Colors.white),
|
||||||
|
),
|
||||||
|
Gap(6),
|
||||||
Center(child: LinearProgressIndicator(value: progress)),
|
Center(child: LinearProgressIndicator(value: progress)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -455,6 +464,7 @@ class AttachmentPreview extends StatelessWidget {
|
|||||||
child: Row(
|
child: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
|
if (onDelete != null)
|
||||||
InkWell(
|
InkWell(
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
child: const Icon(
|
child: const Icon(
|
||||||
@ -466,6 +476,7 @@ class AttachmentPreview extends StatelessWidget {
|
|||||||
onDelete?.call();
|
onDelete?.call();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
if (onDelete != null && onMove != null)
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: 26,
|
height: 26,
|
||||||
child: const VerticalDivider(
|
child: const VerticalDivider(
|
||||||
@ -474,6 +485,7 @@ class AttachmentPreview extends StatelessWidget {
|
|||||||
thickness: 0.3,
|
thickness: 0.3,
|
||||||
),
|
),
|
||||||
).padding(horizontal: 2),
|
).padding(horizontal: 2),
|
||||||
|
if (onMove != null)
|
||||||
InkWell(
|
InkWell(
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
child: const Icon(
|
child: const Icon(
|
||||||
@ -485,6 +497,7 @@ class AttachmentPreview extends StatelessWidget {
|
|||||||
onMove?.call(-1);
|
onMove?.call(-1);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
if (onMove != null)
|
||||||
InkWell(
|
InkWell(
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
child: const Icon(
|
child: const Icon(
|
||||||
@ -502,6 +515,7 @@ class AttachmentPreview extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
if (onRequestUpload != null)
|
||||||
Positioned(
|
Positioned(
|
||||||
top: 8,
|
top: 8,
|
||||||
right: 8,
|
right: 8,
|
||||||
|
@ -159,6 +159,7 @@ class EditRealmScreen extends HookConsumerWidget {
|
|||||||
}, [realm]);
|
}, [realm]);
|
||||||
|
|
||||||
void setPicture(String position) async {
|
void setPicture(String position) async {
|
||||||
|
showLoadingModal(context);
|
||||||
var result = await ref
|
var result = await ref
|
||||||
.read(imagePickerProvider)
|
.read(imagePickerProvider)
|
||||||
.pickImage(source: ImageSource.gallery);
|
.pickImage(source: ImageSource.gallery);
|
||||||
@ -174,7 +175,10 @@ class EditRealmScreen extends HookConsumerWidget {
|
|||||||
CropAspectRatio(height: 1, width: 1),
|
CropAspectRatio(height: 1, width: 1),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
if (result == null) return;
|
if (result == null) {
|
||||||
|
if (context.mounted) hideLoadingModal(context);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!context.mounted) return;
|
if (!context.mounted) return;
|
||||||
|
|
||||||
submitting.value = true;
|
submitting.value = true;
|
||||||
@ -209,6 +213,7 @@ class EditRealmScreen extends HookConsumerWidget {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
showErrorAlert(err);
|
showErrorAlert(err);
|
||||||
} finally {
|
} finally {
|
||||||
|
if (context.mounted) hideLoadingModal(context);
|
||||||
submitting.value = false;
|
submitting.value = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,10 @@ import 'dart:developer';
|
|||||||
|
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_platform_alert/flutter_platform_alert.dart';
|
import 'package:flutter_platform_alert/flutter_platform_alert.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
|
|
||||||
String _parseRemoteError(DioException err) {
|
String _parseRemoteError(DioException err) {
|
||||||
log('${err.requestOptions.method} ${err.requestOptions.uri} ${err.message}');
|
log('${err.requestOptions.method} ${err.requestOptions.uri} ${err.message}');
|
||||||
@ -52,3 +55,83 @@ Future<bool> showConfirmAlert(String message, String title) async {
|
|||||||
);
|
);
|
||||||
return result == AlertButton.okButton;
|
return result == AlertButton.okButton;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OverlayEntry? _loadingOverlay;
|
||||||
|
GlobalKey<_FadeOverlayState> _loadingOverlayKey = GlobalKey();
|
||||||
|
|
||||||
|
class _FadeOverlay extends StatefulWidget {
|
||||||
|
const _FadeOverlay({super.key, required this.child});
|
||||||
|
final Widget child;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<_FadeOverlay> createState() => _FadeOverlayState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _FadeOverlayState extends State<_FadeOverlay> {
|
||||||
|
bool _visible = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
setState(() => _visible = true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return AnimatedOpacity(
|
||||||
|
opacity: _visible ? 1.0 : 0.0,
|
||||||
|
duration: const Duration(milliseconds: 200),
|
||||||
|
child: widget.child,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void showLoadingModal(BuildContext context) {
|
||||||
|
if (_loadingOverlay != null) return;
|
||||||
|
|
||||||
|
_loadingOverlay = OverlayEntry(
|
||||||
|
builder:
|
||||||
|
(context) => _FadeOverlay(
|
||||||
|
key: _loadingOverlayKey,
|
||||||
|
child: Material(
|
||||||
|
color: Colors.black54,
|
||||||
|
child: Center(
|
||||||
|
child: Material(
|
||||||
|
color: Colors.white,
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
elevation: 4,
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
CircularProgressIndicator(year2023: true),
|
||||||
|
const Gap(24),
|
||||||
|
Text('loading'.tr()),
|
||||||
|
],
|
||||||
|
).padding(all: 32),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
Overlay.of(context).insert(_loadingOverlay!);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hideLoadingModal(BuildContext context) async {
|
||||||
|
if (_loadingOverlay == null) return;
|
||||||
|
|
||||||
|
final entry = _loadingOverlay!;
|
||||||
|
_loadingOverlay = null;
|
||||||
|
|
||||||
|
final state = entry.mounted ? _loadingOverlayKey.currentState : null;
|
||||||
|
|
||||||
|
if (state != null) {
|
||||||
|
// ignore: invalid_use_of_protected_member
|
||||||
|
state.setState(() => state._visible = false);
|
||||||
|
await Future.delayed(const Duration(milliseconds: 200));
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.remove();
|
||||||
|
}
|
||||||
|
312
lib/widgets/content/cloud_file_picker.dart
Normal file
312
lib/widgets/content/cloud_file_picker.dart
Normal file
@ -0,0 +1,312 @@
|
|||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:file_picker/file_picker.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:image_picker/image_picker.dart';
|
||||||
|
import 'package:island/models/file.dart';
|
||||||
|
import 'package:island/pods/config.dart';
|
||||||
|
import 'package:island/pods/network.dart';
|
||||||
|
import 'package:island/screens/posts/compose.dart';
|
||||||
|
import 'package:island/services/file.dart';
|
||||||
|
import 'package:island/widgets/alert.dart';
|
||||||
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
|
|
||||||
|
class CloudFilePicker extends HookConsumerWidget {
|
||||||
|
final bool allowMultiple;
|
||||||
|
const CloudFilePicker({super.key, this.allowMultiple = false});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final files = useState<List<UniversalFile>>([]);
|
||||||
|
|
||||||
|
final uploadPosition = useState<int?>(null);
|
||||||
|
final uploadProgress = useState<double?>(null);
|
||||||
|
|
||||||
|
final uploadOverallProgress = useMemoized<double?>(() {
|
||||||
|
if (uploadPosition.value == null || uploadProgress.value == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate completed files (100% each) + current file progress
|
||||||
|
final completedProgress = uploadPosition.value! * 100.0;
|
||||||
|
final currentProgress = uploadProgress.value!;
|
||||||
|
|
||||||
|
// Calculate overall progress as percentage
|
||||||
|
return (completedProgress + currentProgress) /
|
||||||
|
(files.value.length * 100.0);
|
||||||
|
}, [uploadPosition.value, uploadProgress.value, files.value.length]);
|
||||||
|
|
||||||
|
Future<void> startUpload() async {
|
||||||
|
if (files.value.isEmpty) return;
|
||||||
|
|
||||||
|
final baseUrl = ref.read(serverUrlProvider);
|
||||||
|
final atk = await getFreshAtk(
|
||||||
|
ref.watch(tokenPairProvider),
|
||||||
|
baseUrl,
|
||||||
|
onRefreshed: (atk, rtk) {
|
||||||
|
setTokenPair(ref.watch(sharedPreferencesProvider), atk, rtk);
|
||||||
|
ref.invalidate(tokenPairProvider);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if (atk == null) throw Exception("Unauthorized");
|
||||||
|
|
||||||
|
List<SnCloudFile> result = List.empty(growable: true);
|
||||||
|
|
||||||
|
uploadProgress.value = 0;
|
||||||
|
uploadPosition.value = 0;
|
||||||
|
try {
|
||||||
|
for (var idx = 0; idx < files.value.length; idx++) {
|
||||||
|
uploadPosition.value = idx;
|
||||||
|
final file = files.value[idx];
|
||||||
|
final cloudFile =
|
||||||
|
await putMediaToCloud(
|
||||||
|
fileData: file.data,
|
||||||
|
atk: atk,
|
||||||
|
baseUrl: baseUrl,
|
||||||
|
filename: file.data.name ?? 'Post media',
|
||||||
|
mimetype:
|
||||||
|
file.data.mimeType ??
|
||||||
|
switch (file.type) {
|
||||||
|
UniversalFileType.image => 'image/unknown',
|
||||||
|
UniversalFileType.video => 'video/unknown',
|
||||||
|
UniversalFileType.audio => 'audio/unknown',
|
||||||
|
UniversalFileType.file => 'application/octet-stream',
|
||||||
|
},
|
||||||
|
onProgress: (progress, _) {
|
||||||
|
uploadProgress.value = progress;
|
||||||
|
},
|
||||||
|
).future;
|
||||||
|
if (cloudFile == null) {
|
||||||
|
throw ArgumentError('Failed to upload the file...');
|
||||||
|
}
|
||||||
|
result.add(cloudFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context.mounted) Navigator.pop(context, result);
|
||||||
|
} catch (err) {
|
||||||
|
showErrorAlert(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void pickFile() async {
|
||||||
|
showLoadingModal(context);
|
||||||
|
final result = await FilePickerIO().pickFiles(
|
||||||
|
allowMultiple: allowMultiple,
|
||||||
|
);
|
||||||
|
if (result == null) {
|
||||||
|
if (context.mounted) hideLoadingModal(context);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final newFiles =
|
||||||
|
result.files
|
||||||
|
.map((e) => UniversalFile(data: e, type: UniversalFileType.file))
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
if (!allowMultiple) {
|
||||||
|
files.value = newFiles;
|
||||||
|
if (context.mounted) {
|
||||||
|
hideLoadingModal(context);
|
||||||
|
startUpload();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
files.value = [...files.value, ...newFiles];
|
||||||
|
if (context.mounted) hideLoadingModal(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pickImage() async {
|
||||||
|
showLoadingModal(context);
|
||||||
|
final result =
|
||||||
|
allowMultiple
|
||||||
|
? await ref.read(imagePickerProvider).pickMultiImage()
|
||||||
|
: [
|
||||||
|
await ref
|
||||||
|
.read(imagePickerProvider)
|
||||||
|
.pickImage(source: ImageSource.gallery),
|
||||||
|
];
|
||||||
|
if (result.isEmpty) {
|
||||||
|
if (context.mounted) hideLoadingModal(context);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final newFiles =
|
||||||
|
result
|
||||||
|
.map((e) => UniversalFile(data: e, type: UniversalFileType.image))
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
if (!allowMultiple) {
|
||||||
|
files.value = newFiles;
|
||||||
|
if (context.mounted) {
|
||||||
|
hideLoadingModal(context);
|
||||||
|
startUpload();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
files.value = [...files.value, ...newFiles];
|
||||||
|
if (context.mounted) hideLoadingModal(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pickVideo() async {
|
||||||
|
showLoadingModal(context);
|
||||||
|
final result = await ref
|
||||||
|
.read(imagePickerProvider)
|
||||||
|
.pickVideo(source: ImageSource.gallery);
|
||||||
|
if (result == null) {
|
||||||
|
if (context.mounted) hideLoadingModal(context);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final newFile = UniversalFile(
|
||||||
|
data: result,
|
||||||
|
type: UniversalFileType.video,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!allowMultiple) {
|
||||||
|
files.value = [newFile];
|
||||||
|
if (context.mounted) {
|
||||||
|
hideLoadingModal(context);
|
||||||
|
startUpload();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
files.value = [...files.value, newFile];
|
||||||
|
if (context.mounted) hideLoadingModal(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Container(
|
||||||
|
constraints: BoxConstraints(
|
||||||
|
maxHeight: MediaQuery.of(context).size.height * 0.5,
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.only(top: 16, left: 20, right: 16, bottom: 12),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'pickFile'.tr(),
|
||||||
|
style: Theme.of(context).textTheme.headlineSmall?.copyWith(
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
letterSpacing: -0.5,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Symbols.close),
|
||||||
|
onPressed: () => Navigator.pop(context),
|
||||||
|
style: IconButton.styleFrom(minimumSize: const Size(36, 36)),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Divider(height: 1),
|
||||||
|
Expanded(
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
spacing: 16,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: [
|
||||||
|
if (uploadOverallProgress != null)
|
||||||
|
Column(
|
||||||
|
spacing: 6,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: [
|
||||||
|
Text('uploadingProgress')
|
||||||
|
.tr(
|
||||||
|
args: [
|
||||||
|
((uploadPosition.value ?? 0) + 1).toString(),
|
||||||
|
files.value.length.toString(),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
.opacity(0.85),
|
||||||
|
LinearProgressIndicator(
|
||||||
|
value: uploadOverallProgress,
|
||||||
|
color: Theme.of(context).colorScheme.primary,
|
||||||
|
backgroundColor:
|
||||||
|
Theme.of(context).colorScheme.surfaceVariant,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
if (files.value.isNotEmpty)
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: ElevatedButton.icon(
|
||||||
|
onPressed: startUpload,
|
||||||
|
icon: const Icon(Symbols.play_arrow),
|
||||||
|
label: Text('uploadAll'.tr()),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (files.value.isNotEmpty)
|
||||||
|
SizedBox(
|
||||||
|
height: 280,
|
||||||
|
child: ListView.separated(
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
itemCount: files.value.length,
|
||||||
|
itemBuilder: (context, idx) {
|
||||||
|
return AttachmentPreview(
|
||||||
|
onDelete:
|
||||||
|
uploadOverallProgress != null
|
||||||
|
? null
|
||||||
|
: () {
|
||||||
|
files.value = [
|
||||||
|
...files.value.where(
|
||||||
|
(e) => e != files.value[idx],
|
||||||
|
),
|
||||||
|
];
|
||||||
|
},
|
||||||
|
item: files.value[idx],
|
||||||
|
progress: null,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
separatorBuilder: (_, __) => const Gap(8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Card(
|
||||||
|
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||||
|
margin: EdgeInsets.zero,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
ListTile(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
leading: const Icon(Symbols.photo),
|
||||||
|
title: Text('addPhoto'.tr()),
|
||||||
|
onTap: () => pickImage(),
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
leading: const Icon(Symbols.video_call),
|
||||||
|
title: Text('addVideo'.tr()),
|
||||||
|
onTap: () => pickVideo(),
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
leading: const Icon(Symbols.draft),
|
||||||
|
title: Text('addFile'.tr()),
|
||||||
|
onTap: () => pickFile(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).padding(all: 24),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -73,7 +73,7 @@ dependencies:
|
|||||||
git: https://github.com/LittleSheep2Code/tus_client.git
|
git: https://github.com/LittleSheep2Code/tus_client.git
|
||||||
cross_file: ^0.3.4+2
|
cross_file: ^0.3.4+2
|
||||||
image_picker: ^1.1.2
|
image_picker: ^1.1.2
|
||||||
file_picker: ^10.1.2
|
file_picker: ^10.1.7
|
||||||
riverpod_annotation: ^2.6.1
|
riverpod_annotation: ^2.6.1
|
||||||
image_picker_platform_interface: ^2.10.1
|
image_picker_platform_interface: ^2.10.1
|
||||||
image_picker_android: ^0.8.12+23
|
image_picker_android: ^0.8.12+23
|
||||||
|
Loading…
x
Reference in New Issue
Block a user