✨ Sliding window pricing of attachment billing info displaying
This commit is contained in:
@ -1,7 +1,13 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:dismissible_page/dismissible_page.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:material_symbols_icons/symbols.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:styled_widget/styled_widget.dart';
|
||||
import 'package:surface/providers/sn_network.dart';
|
||||
@ -27,9 +33,23 @@ class _AlbumScreenState extends State<AlbumScreen> {
|
||||
bool _isBusy = false;
|
||||
int? _totalCount;
|
||||
|
||||
SnAttachmentBilling? _billing;
|
||||
|
||||
final List<SnAttachment> _attachments = List.empty(growable: true);
|
||||
final List<String> _heroTags = List.empty(growable: true);
|
||||
|
||||
Future<void> _fetchBillingStatus() async {
|
||||
try {
|
||||
final sn = context.read<SnNetworkProvider>();
|
||||
final resp = await sn.client.get('/cgi/uc/billing');
|
||||
final out = SnAttachmentBilling.fromJson(resp.data);
|
||||
setState(() => _billing = out);
|
||||
} catch (err) {
|
||||
if (!mounted) return;
|
||||
context.showErrorDialog(err);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _fetchAttachments() async {
|
||||
setState(() => _isBusy = true);
|
||||
|
||||
@ -62,6 +82,7 @@ class _AlbumScreenState extends State<AlbumScreen> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_fetchBillingStatus();
|
||||
_fetchAttachments();
|
||||
_scrollController.addListener(() {
|
||||
if (_scrollController.position.atEdge) {
|
||||
@ -91,6 +112,48 @@ class _AlbumScreenState extends State<AlbumScreen> {
|
||||
leading: AutoAppBarLeading(),
|
||||
title: Text('screenAlbum').tr(),
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: Card(
|
||||
child: Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 80,
|
||||
height: 80,
|
||||
child: CircularProgressIndicator(
|
||||
value: _billing?.includedRatio ?? 0,
|
||||
strokeWidth: 8,
|
||||
backgroundColor: Theme.of(context).colorScheme.surfaceContainerHigh,
|
||||
),
|
||||
).padding(all: 12),
|
||||
const Gap(24),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('attachmentBillingUploaded').tr().bold(),
|
||||
Text(
|
||||
(_billing?.currentBytes ?? 0).formatBytes(decimals: 4),
|
||||
style: GoogleFonts.robotoMono(),
|
||||
),
|
||||
Text('attachmentBillingDiscount').tr().bold(),
|
||||
Text(
|
||||
'${(_billing?.discountFileSize ?? 0).formatBytes(decimals: 2)} · ${((_billing?.includedRatio ?? 0) * 100).toStringAsFixed(2)}%',
|
||||
style: GoogleFonts.robotoMono(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Tooltip(
|
||||
message: 'attachmentBillingHint'.tr(),
|
||||
child: IconButton(
|
||||
icon: const Icon(Symbols.info),
|
||||
onPressed: () {},
|
||||
),
|
||||
),
|
||||
],
|
||||
).padding(horizontal: 24, vertical: 8),
|
||||
),
|
||||
),
|
||||
SliverMasonryGrid.extent(
|
||||
childCount: _attachments.length,
|
||||
maxCrossAxisExtent: 320,
|
||||
|
@ -177,3 +177,14 @@ class SnStickerPack with _$SnStickerPack {
|
||||
|
||||
factory SnStickerPack.fromJson(Map<String, Object?> json) => _$SnStickerPackFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
class SnAttachmentBilling with _$SnAttachmentBilling {
|
||||
const factory SnAttachmentBilling({
|
||||
required int currentBytes,
|
||||
required int discountFileSize,
|
||||
required double includedRatio,
|
||||
}) = _SnAttachmentBilling;
|
||||
|
||||
factory SnAttachmentBilling.fromJson(Map<String, Object?> json) => _$SnAttachmentBillingFromJson(json);
|
||||
}
|
||||
|
@ -3007,3 +3007,195 @@ abstract class _SnStickerPack implements SnStickerPack {
|
||||
_$$SnStickerPackImplCopyWith<_$SnStickerPackImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
SnAttachmentBilling _$SnAttachmentBillingFromJson(Map<String, dynamic> json) {
|
||||
return _SnAttachmentBilling.fromJson(json);
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$SnAttachmentBilling {
|
||||
int get currentBytes => throw _privateConstructorUsedError;
|
||||
int get discountFileSize => throw _privateConstructorUsedError;
|
||||
double get includedRatio => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this SnAttachmentBilling to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
|
||||
/// Create a copy of SnAttachmentBilling
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
$SnAttachmentBillingCopyWith<SnAttachmentBilling> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $SnAttachmentBillingCopyWith<$Res> {
|
||||
factory $SnAttachmentBillingCopyWith(
|
||||
SnAttachmentBilling value, $Res Function(SnAttachmentBilling) then) =
|
||||
_$SnAttachmentBillingCopyWithImpl<$Res, SnAttachmentBilling>;
|
||||
@useResult
|
||||
$Res call({int currentBytes, int discountFileSize, double includedRatio});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$SnAttachmentBillingCopyWithImpl<$Res, $Val extends SnAttachmentBilling>
|
||||
implements $SnAttachmentBillingCopyWith<$Res> {
|
||||
_$SnAttachmentBillingCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
/// Create a copy of SnAttachmentBilling
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? currentBytes = null,
|
||||
Object? discountFileSize = null,
|
||||
Object? includedRatio = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
currentBytes: null == currentBytes
|
||||
? _value.currentBytes
|
||||
: currentBytes // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
discountFileSize: null == discountFileSize
|
||||
? _value.discountFileSize
|
||||
: discountFileSize // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
includedRatio: null == includedRatio
|
||||
? _value.includedRatio
|
||||
: includedRatio // ignore: cast_nullable_to_non_nullable
|
||||
as double,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$SnAttachmentBillingImplCopyWith<$Res>
|
||||
implements $SnAttachmentBillingCopyWith<$Res> {
|
||||
factory _$$SnAttachmentBillingImplCopyWith(_$SnAttachmentBillingImpl value,
|
||||
$Res Function(_$SnAttachmentBillingImpl) then) =
|
||||
__$$SnAttachmentBillingImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call({int currentBytes, int discountFileSize, double includedRatio});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$SnAttachmentBillingImplCopyWithImpl<$Res>
|
||||
extends _$SnAttachmentBillingCopyWithImpl<$Res, _$SnAttachmentBillingImpl>
|
||||
implements _$$SnAttachmentBillingImplCopyWith<$Res> {
|
||||
__$$SnAttachmentBillingImplCopyWithImpl(_$SnAttachmentBillingImpl _value,
|
||||
$Res Function(_$SnAttachmentBillingImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
/// Create a copy of SnAttachmentBilling
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? currentBytes = null,
|
||||
Object? discountFileSize = null,
|
||||
Object? includedRatio = null,
|
||||
}) {
|
||||
return _then(_$SnAttachmentBillingImpl(
|
||||
currentBytes: null == currentBytes
|
||||
? _value.currentBytes
|
||||
: currentBytes // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
discountFileSize: null == discountFileSize
|
||||
? _value.discountFileSize
|
||||
: discountFileSize // ignore: cast_nullable_to_non_nullable
|
||||
as int,
|
||||
includedRatio: null == includedRatio
|
||||
? _value.includedRatio
|
||||
: includedRatio // ignore: cast_nullable_to_non_nullable
|
||||
as double,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _$SnAttachmentBillingImpl implements _SnAttachmentBilling {
|
||||
const _$SnAttachmentBillingImpl(
|
||||
{required this.currentBytes,
|
||||
required this.discountFileSize,
|
||||
required this.includedRatio});
|
||||
|
||||
factory _$SnAttachmentBillingImpl.fromJson(Map<String, dynamic> json) =>
|
||||
_$$SnAttachmentBillingImplFromJson(json);
|
||||
|
||||
@override
|
||||
final int currentBytes;
|
||||
@override
|
||||
final int discountFileSize;
|
||||
@override
|
||||
final double includedRatio;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'SnAttachmentBilling(currentBytes: $currentBytes, discountFileSize: $discountFileSize, includedRatio: $includedRatio)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$SnAttachmentBillingImpl &&
|
||||
(identical(other.currentBytes, currentBytes) ||
|
||||
other.currentBytes == currentBytes) &&
|
||||
(identical(other.discountFileSize, discountFileSize) ||
|
||||
other.discountFileSize == discountFileSize) &&
|
||||
(identical(other.includedRatio, includedRatio) ||
|
||||
other.includedRatio == includedRatio));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode =>
|
||||
Object.hash(runtimeType, currentBytes, discountFileSize, includedRatio);
|
||||
|
||||
/// Create a copy of SnAttachmentBilling
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$SnAttachmentBillingImplCopyWith<_$SnAttachmentBillingImpl> get copyWith =>
|
||||
__$$SnAttachmentBillingImplCopyWithImpl<_$SnAttachmentBillingImpl>(
|
||||
this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$$SnAttachmentBillingImplToJson(
|
||||
this,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _SnAttachmentBilling implements SnAttachmentBilling {
|
||||
const factory _SnAttachmentBilling(
|
||||
{required final int currentBytes,
|
||||
required final int discountFileSize,
|
||||
required final double includedRatio}) = _$SnAttachmentBillingImpl;
|
||||
|
||||
factory _SnAttachmentBilling.fromJson(Map<String, dynamic> json) =
|
||||
_$SnAttachmentBillingImpl.fromJson;
|
||||
|
||||
@override
|
||||
int get currentBytes;
|
||||
@override
|
||||
int get discountFileSize;
|
||||
@override
|
||||
double get includedRatio;
|
||||
|
||||
/// Create a copy of SnAttachmentBilling
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
_$$SnAttachmentBillingImplCopyWith<_$SnAttachmentBillingImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
@ -281,3 +281,19 @@ Map<String, dynamic> _$$SnStickerPackImplToJson(_$SnStickerPackImpl instance) =>
|
||||
'stickers': instance.stickers?.map((e) => e.toJson()).toList(),
|
||||
'account_id': instance.accountId,
|
||||
};
|
||||
|
||||
_$SnAttachmentBillingImpl _$$SnAttachmentBillingImplFromJson(
|
||||
Map<String, dynamic> json) =>
|
||||
_$SnAttachmentBillingImpl(
|
||||
currentBytes: (json['current_bytes'] as num).toInt(),
|
||||
discountFileSize: (json['discount_file_size'] as num).toInt(),
|
||||
includedRatio: (json['included_ratio'] as num).toDouble(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$SnAttachmentBillingImplToJson(
|
||||
_$SnAttachmentBillingImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'current_bytes': instance.currentBytes,
|
||||
'discount_file_size': instance.discountFileSize,
|
||||
'included_ratio': instance.includedRatio,
|
||||
};
|
||||
|
Reference in New Issue
Block a user