Compare commits
No commits in common. "335318ae3ff192ada7cc32410817da55148d21c6" and "c1aef89b84d6ff14c2be9020e45b97e97a27f344" have entirely different histories.
335318ae3f
...
c1aef89b84
@ -777,19 +777,5 @@
|
|||||||
"zero": "No streak",
|
"zero": "No streak",
|
||||||
"one": "{} day streak",
|
"one": "{} day streak",
|
||||||
"other": "{} days streak"
|
"other": "{} days streak"
|
||||||
},
|
}
|
||||||
"accountChangeStatus": "Change Status",
|
|
||||||
"accountStatusSilent": "Do not Disturb",
|
|
||||||
"accountStatusSilentDesc": "The notification will stop popping up",
|
|
||||||
"accountStatusInvisible": "Invisible",
|
|
||||||
"accountStatusInvisibleDesc": "Will show as offline, but all features still remain normal",
|
|
||||||
"accountCustomStatus": "Custom Status",
|
|
||||||
"accountCustomStatusDescription": "Customize your status.",
|
|
||||||
"accountClearStatus": "Clear Status",
|
|
||||||
"accountClearStatusDescription": "Clear your status, and let server decide which status you are for you.",
|
|
||||||
"fieldAccountStatusLabel": "Status Text",
|
|
||||||
"fieldAccountStatusClearAt": "Clear At",
|
|
||||||
"accountStatusNegative": "Negative",
|
|
||||||
"accountStatusNeutral": "Neutral",
|
|
||||||
"accountStatusPositive": "Positive"
|
|
||||||
}
|
}
|
||||||
|
@ -775,19 +775,5 @@
|
|||||||
"zero": "无连击",
|
"zero": "无连击",
|
||||||
"one": "连续签到 {} 天",
|
"one": "连续签到 {} 天",
|
||||||
"other": "连续签到 {} 天"
|
"other": "连续签到 {} 天"
|
||||||
},
|
}
|
||||||
"accountChangeStatus": "修改状态",
|
|
||||||
"accountStatusSilent": "请勿打扰",
|
|
||||||
"accountStatusSilentDesc": "将会暂停所有通知推送",
|
|
||||||
"accountStatusInvisible": "隐身",
|
|
||||||
"accountStatusInvisibleDesc": "将会在他人界面显示离线,但不影响功能使用",
|
|
||||||
"accountCustomStatus": "自定义状态",
|
|
||||||
"accountCustomStatusDescription": "客制化你的状态。",
|
|
||||||
"accountClearStatus": "清除状态",
|
|
||||||
"accountClearStatusDescription": "清除你的状态,并让服务器决定你的状态。",
|
|
||||||
"fieldAccountStatusLabel": "状态文字",
|
|
||||||
"fieldAccountStatusClearAt": "清除时间",
|
|
||||||
"accountStatusNegative": "负面",
|
|
||||||
"accountStatusNeutral": "中性",
|
|
||||||
"accountStatusPositive": "正面"
|
|
||||||
}
|
}
|
||||||
|
@ -775,19 +775,5 @@
|
|||||||
"zero": "無連擊",
|
"zero": "無連擊",
|
||||||
"one": "連續簽到 {} 天",
|
"one": "連續簽到 {} 天",
|
||||||
"other": "連續簽到 {} 天"
|
"other": "連續簽到 {} 天"
|
||||||
},
|
}
|
||||||
"accountChangeStatus": "修改狀態",
|
|
||||||
"accountStatusSilent": "請勿打擾",
|
|
||||||
"accountStatusSilentDesc": "將會暫停所有通知推送",
|
|
||||||
"accountStatusInvisible": "隱身",
|
|
||||||
"accountStatusInvisibleDesc": "將會在他人界面顯示離線,但不影響功能使用",
|
|
||||||
"accountCustomStatus": "自定義狀態",
|
|
||||||
"accountCustomStatusDescription": "客製化你的狀態。",
|
|
||||||
"accountClearStatus": "清除狀態",
|
|
||||||
"accountClearStatusDescription": "清除你的狀態,並讓服務器決定你的狀態。",
|
|
||||||
"fieldAccountStatusLabel": "狀態文字",
|
|
||||||
"fieldAccountStatusClearAt": "清除時間",
|
|
||||||
"accountStatusNegative": "負面",
|
|
||||||
"accountStatusNeutral": "中性",
|
|
||||||
"accountStatusPositive": "正面"
|
|
||||||
}
|
}
|
||||||
|
@ -775,19 +775,5 @@
|
|||||||
"zero": "無連擊",
|
"zero": "無連擊",
|
||||||
"one": "連續簽到 {} 天",
|
"one": "連續簽到 {} 天",
|
||||||
"other": "連續簽到 {} 天"
|
"other": "連續簽到 {} 天"
|
||||||
},
|
}
|
||||||
"accountChangeStatus": "修改狀態",
|
|
||||||
"accountStatusSilent": "請勿打擾",
|
|
||||||
"accountStatusSilentDesc": "將會暫停所有通知推送",
|
|
||||||
"accountStatusInvisible": "隱身",
|
|
||||||
"accountStatusInvisibleDesc": "將會在他人界面顯示離線,但不影響功能使用",
|
|
||||||
"accountCustomStatus": "自定義狀態",
|
|
||||||
"accountCustomStatusDescription": "客製化你的狀態。",
|
|
||||||
"accountClearStatus": "清除狀態",
|
|
||||||
"accountClearStatusDescription": "清除你的狀態,並讓服務器決定你的狀態。",
|
|
||||||
"fieldAccountStatusLabel": "狀態文字",
|
|
||||||
"fieldAccountStatusClearAt": "清除時間",
|
|
||||||
"accountStatusNegative": "負面",
|
|
||||||
"accountStatusNeutral": "中性",
|
|
||||||
"accountStatusPositive": "正面"
|
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,6 @@ class UserDirectoryProvider {
|
|||||||
|
|
||||||
final Map<String, int> _idCache = {};
|
final Map<String, int> _idCache = {};
|
||||||
final Map<int, SnAccount> _cache = {};
|
final Map<int, SnAccount> _cache = {};
|
||||||
DateTime? _cacheExpiredAt;
|
|
||||||
|
|
||||||
Future<int> loadAccountCache({int max = 100}) async {
|
Future<int> loadAccountCache({int max = 100}) async {
|
||||||
final out = await (_dt.db.snLocalAccount.select()..limit(max)).get();
|
final out = await (_dt.db.snLocalAccount.select()..limit(max)).get();
|
||||||
@ -27,18 +26,11 @@ class UserDirectoryProvider {
|
|||||||
_cache[ele.id] = ele.content;
|
_cache[ele.id] = ele.content;
|
||||||
_idCache[ele.name] = ele.id;
|
_idCache[ele.name] = ele.id;
|
||||||
}
|
}
|
||||||
_cacheExpiredAt = DateTime.now().add(const Duration(hours: 1));
|
|
||||||
return out.length;
|
return out.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<SnAccount?>> listAccount(Iterable<dynamic> id) async {
|
Future<List<SnAccount?>> listAccount(Iterable<dynamic> id) async {
|
||||||
// In-memory cache
|
// In-memory cache
|
||||||
if (_cacheExpiredAt != null && _cacheExpiredAt!.isAfter(DateTime.now())) {
|
|
||||||
_cache.clear();
|
|
||||||
_cacheExpiredAt = DateTime.now().add(const Duration(hours: 1));
|
|
||||||
} else {
|
|
||||||
_cacheExpiredAt ??= DateTime.now().add(const Duration(hours: 1));
|
|
||||||
}
|
|
||||||
final out = List<SnAccount?>.generate(id.length, (e) => null);
|
final out = List<SnAccount?>.generate(id.length, (e) => null);
|
||||||
final plannedQuery = <int>{};
|
final plannedQuery = <int>{};
|
||||||
for (var idx = 0; idx < out.length; idx++) {
|
for (var idx = 0; idx < out.length; idx++) {
|
||||||
@ -70,7 +62,6 @@ class UserDirectoryProvider {
|
|||||||
plannedQuery.remove(dbResp[idx].id);
|
plannedQuery.remove(dbResp[idx].id);
|
||||||
}
|
}
|
||||||
// Remote server
|
// Remote server
|
||||||
_saveToLocal(out.where((ele) => ele != null).cast());
|
|
||||||
if (plannedQuery.isEmpty) return out;
|
if (plannedQuery.isEmpty) return out;
|
||||||
final resp = await _sn.client
|
final resp = await _sn.client
|
||||||
.get('/cgi/id/users', queryParameters: {'id': plannedQuery.join(',')});
|
.get('/cgi/id/users', queryParameters: {'id': plannedQuery.join(',')});
|
||||||
|
@ -11,9 +11,7 @@ import 'package:surface/providers/database.dart';
|
|||||||
import 'package:surface/providers/sn_network.dart';
|
import 'package:surface/providers/sn_network.dart';
|
||||||
import 'package:surface/providers/userinfo.dart';
|
import 'package:surface/providers/userinfo.dart';
|
||||||
import 'package:surface/providers/websocket.dart';
|
import 'package:surface/providers/websocket.dart';
|
||||||
import 'package:surface/types/account.dart';
|
|
||||||
import 'package:surface/widgets/account/account_image.dart';
|
import 'package:surface/widgets/account/account_image.dart';
|
||||||
import 'package:surface/widgets/account/account_status.dart';
|
|
||||||
import 'package:surface/widgets/app_bar_leading.dart';
|
import 'package:surface/widgets/app_bar_leading.dart';
|
||||||
import 'package:surface/widgets/dialog.dart';
|
import 'package:surface/widgets/dialog.dart';
|
||||||
import 'package:surface/widgets/navigation/app_scaffold.dart';
|
import 'package:surface/widgets/navigation/app_scaffold.dart';
|
||||||
@ -114,14 +112,7 @@ class _AuthorizedAccountScreen extends StatelessWidget {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Row(
|
AccountImage(content: ua.user!.avatar, radius: 28),
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
AccountImage(content: ua.user!.avatar, radius: 28),
|
|
||||||
_AccountStatusWidget(account: ua.user!),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const Gap(8),
|
const Gap(8),
|
||||||
Row(
|
Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.baseline,
|
crossAxisAlignment: CrossAxisAlignment.baseline,
|
||||||
@ -299,81 +290,3 @@ class _UnauthorizedAccountScreen extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _AccountStatusWidget extends StatefulWidget {
|
|
||||||
final SnAccount account;
|
|
||||||
const _AccountStatusWidget({required this.account});
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<_AccountStatusWidget> createState() => _AccountStatusWidgetState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _AccountStatusWidgetState extends State<_AccountStatusWidget> {
|
|
||||||
SnAccountStatusInfo? _status;
|
|
||||||
|
|
||||||
Future<void> _fetchStatus() async {
|
|
||||||
try {
|
|
||||||
final sn = context.read<SnNetworkProvider>();
|
|
||||||
final resp =
|
|
||||||
await sn.client.get('/cgi/id/users/${widget.account.name}/status');
|
|
||||||
setState(() {
|
|
||||||
_status = SnAccountStatusInfo.fromJson(resp.data);
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
if (!mounted) return;
|
|
||||||
context.showErrorDialog(err);
|
|
||||||
} finally {
|
|
||||||
setState(() {});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
_fetchStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return InkWell(
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
_status != null
|
|
||||||
? (_status!.status?.label.isNotEmpty ?? false)
|
|
||||||
? _status!.status!.label
|
|
||||||
: _status!.isOnline
|
|
||||||
? 'accountStatusOnline'.tr()
|
|
||||||
: 'accountStatusOffline'.tr()
|
|
||||||
: 'loading'.tr(),
|
|
||||||
),
|
|
||||||
const Gap(4),
|
|
||||||
Icon(
|
|
||||||
(_status?.isDisturbable ?? true)
|
|
||||||
? Symbols.circle
|
|
||||||
: Symbols.do_not_disturb_on,
|
|
||||||
fill: (_status?.isOnline ?? false) ? 1 : 0,
|
|
||||||
size: 16,
|
|
||||||
color: (_status?.isOnline ?? false)
|
|
||||||
? (_status?.isDisturbable ?? true)
|
|
||||||
? Colors.green
|
|
||||||
: Colors.red
|
|
||||||
: Colors.grey,
|
|
||||||
).padding(all: 4),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
onTap: () {
|
|
||||||
showModalBottomSheet(
|
|
||||||
context: context,
|
|
||||||
builder: (context) => AccountStatusActionPopup(
|
|
||||||
currentStatus: _status,
|
|
||||||
),
|
|
||||||
).then((value) {
|
|
||||||
if (value == true && mounted) {
|
|
||||||
_fetchStatus();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -451,25 +451,19 @@ class _UserScreenState extends State<UserScreen>
|
|||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Icon(
|
Icon(
|
||||||
(_status?.isDisturbable ?? true)
|
Symbols.circle,
|
||||||
? Symbols.circle
|
fill: 1,
|
||||||
: Symbols.do_not_disturb_on,
|
|
||||||
fill: (_status?.isOnline ?? false) ? 1 : 0,
|
|
||||||
size: 16,
|
size: 16,
|
||||||
color: (_status?.isOnline ?? false)
|
color: (_status?.isOnline ?? false)
|
||||||
? (_status?.isDisturbable ?? true)
|
? Colors.green
|
||||||
? Colors.green
|
|
||||||
: Colors.red
|
|
||||||
: Colors.grey,
|
: Colors.grey,
|
||||||
).padding(all: 4),
|
).padding(all: 4),
|
||||||
const Gap(8),
|
const Gap(8),
|
||||||
Text(
|
Text(
|
||||||
_status != null
|
_status != null
|
||||||
? (_status!.status?.label.isNotEmpty ?? false)
|
? _status!.isOnline
|
||||||
? _status!.status!.label
|
? 'accountStatusOnline'.tr()
|
||||||
: _status!.isOnline
|
: 'accountStatusOffline'.tr()
|
||||||
? 'accountStatusOnline'.tr()
|
|
||||||
: 'accountStatusOffline'.tr()
|
|
||||||
: 'loading'.tr(),
|
: 'loading'.tr(),
|
||||||
),
|
),
|
||||||
if (_status != null &&
|
if (_status != null &&
|
||||||
|
@ -119,33 +119,13 @@ abstract class SnAccountStatusInfo with _$SnAccountStatusInfo {
|
|||||||
required bool isDisturbable,
|
required bool isDisturbable,
|
||||||
required bool isOnline,
|
required bool isOnline,
|
||||||
required DateTime? lastSeenAt,
|
required DateTime? lastSeenAt,
|
||||||
required SnAccountStatus? status,
|
required dynamic status,
|
||||||
}) = _SnAccountStatusInfo;
|
}) = _SnAccountStatusInfo;
|
||||||
|
|
||||||
factory SnAccountStatusInfo.fromJson(Map<String, Object?> json) =>
|
factory SnAccountStatusInfo.fromJson(Map<String, Object?> json) =>
|
||||||
_$SnAccountStatusInfoFromJson(json);
|
_$SnAccountStatusInfoFromJson(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
@freezed
|
|
||||||
abstract class SnAccountStatus with _$SnAccountStatus {
|
|
||||||
const factory SnAccountStatus({
|
|
||||||
required int id,
|
|
||||||
required DateTime createdAt,
|
|
||||||
required DateTime updatedAt,
|
|
||||||
required DateTime? deletedAt,
|
|
||||||
required String type,
|
|
||||||
required String label,
|
|
||||||
required int attitude,
|
|
||||||
required bool isNoDisturb,
|
|
||||||
required bool isInvisible,
|
|
||||||
required DateTime? clearAt,
|
|
||||||
required int accountId,
|
|
||||||
}) = _SnAccountStatus;
|
|
||||||
|
|
||||||
factory SnAccountStatus.fromJson(Map<String, Object?> json) =>
|
|
||||||
_$SnAccountStatusFromJson(json);
|
|
||||||
}
|
|
||||||
|
|
||||||
@freezed
|
@freezed
|
||||||
abstract class SnAbuseReport with _$SnAbuseReport {
|
abstract class SnAbuseReport with _$SnAbuseReport {
|
||||||
const factory SnAbuseReport({
|
const factory SnAbuseReport({
|
||||||
|
@ -2139,7 +2139,7 @@ mixin _$SnAccountStatusInfo {
|
|||||||
bool get isDisturbable;
|
bool get isDisturbable;
|
||||||
bool get isOnline;
|
bool get isOnline;
|
||||||
DateTime? get lastSeenAt;
|
DateTime? get lastSeenAt;
|
||||||
SnAccountStatus? get status;
|
dynamic get status;
|
||||||
|
|
||||||
/// Create a copy of SnAccountStatusInfo
|
/// Create a copy of SnAccountStatusInfo
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@ -2163,13 +2163,13 @@ mixin _$SnAccountStatusInfo {
|
|||||||
other.isOnline == isOnline) &&
|
other.isOnline == isOnline) &&
|
||||||
(identical(other.lastSeenAt, lastSeenAt) ||
|
(identical(other.lastSeenAt, lastSeenAt) ||
|
||||||
other.lastSeenAt == lastSeenAt) &&
|
other.lastSeenAt == lastSeenAt) &&
|
||||||
(identical(other.status, status) || other.status == status));
|
const DeepCollectionEquality().equals(other.status, status));
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
@override
|
@override
|
||||||
int get hashCode =>
|
int get hashCode => Object.hash(runtimeType, isDisturbable, isOnline,
|
||||||
Object.hash(runtimeType, isDisturbable, isOnline, lastSeenAt, status);
|
lastSeenAt, const DeepCollectionEquality().hash(status));
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
@ -2187,9 +2187,7 @@ abstract mixin class $SnAccountStatusInfoCopyWith<$Res> {
|
|||||||
{bool isDisturbable,
|
{bool isDisturbable,
|
||||||
bool isOnline,
|
bool isOnline,
|
||||||
DateTime? lastSeenAt,
|
DateTime? lastSeenAt,
|
||||||
SnAccountStatus? status});
|
dynamic status});
|
||||||
|
|
||||||
$SnAccountStatusCopyWith<$Res>? get status;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
@ -2226,23 +2224,9 @@ class _$SnAccountStatusInfoCopyWithImpl<$Res>
|
|||||||
status: freezed == status
|
status: freezed == status
|
||||||
? _self.status
|
? _self.status
|
||||||
: status // ignore: cast_nullable_to_non_nullable
|
: status // ignore: cast_nullable_to_non_nullable
|
||||||
as SnAccountStatus?,
|
as dynamic,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a copy of SnAccountStatusInfo
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@override
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
$SnAccountStatusCopyWith<$Res>? get status {
|
|
||||||
if (_self.status == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $SnAccountStatusCopyWith<$Res>(_self.status!, (value) {
|
|
||||||
return _then(_self.copyWith(status: value));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
@ -2263,7 +2247,7 @@ class _SnAccountStatusInfo implements SnAccountStatusInfo {
|
|||||||
@override
|
@override
|
||||||
final DateTime? lastSeenAt;
|
final DateTime? lastSeenAt;
|
||||||
@override
|
@override
|
||||||
final SnAccountStatus? status;
|
final dynamic status;
|
||||||
|
|
||||||
/// Create a copy of SnAccountStatusInfo
|
/// Create a copy of SnAccountStatusInfo
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@ -2292,13 +2276,13 @@ class _SnAccountStatusInfo implements SnAccountStatusInfo {
|
|||||||
other.isOnline == isOnline) &&
|
other.isOnline == isOnline) &&
|
||||||
(identical(other.lastSeenAt, lastSeenAt) ||
|
(identical(other.lastSeenAt, lastSeenAt) ||
|
||||||
other.lastSeenAt == lastSeenAt) &&
|
other.lastSeenAt == lastSeenAt) &&
|
||||||
(identical(other.status, status) || other.status == status));
|
const DeepCollectionEquality().equals(other.status, status));
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
@override
|
@override
|
||||||
int get hashCode =>
|
int get hashCode => Object.hash(runtimeType, isDisturbable, isOnline,
|
||||||
Object.hash(runtimeType, isDisturbable, isOnline, lastSeenAt, status);
|
lastSeenAt, const DeepCollectionEquality().hash(status));
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
@ -2318,10 +2302,7 @@ abstract mixin class _$SnAccountStatusInfoCopyWith<$Res>
|
|||||||
{bool isDisturbable,
|
{bool isDisturbable,
|
||||||
bool isOnline,
|
bool isOnline,
|
||||||
DateTime? lastSeenAt,
|
DateTime? lastSeenAt,
|
||||||
SnAccountStatus? status});
|
dynamic status});
|
||||||
|
|
||||||
@override
|
|
||||||
$SnAccountStatusCopyWith<$Res>? get status;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
@ -2358,386 +2339,7 @@ class __$SnAccountStatusInfoCopyWithImpl<$Res>
|
|||||||
status: freezed == status
|
status: freezed == status
|
||||||
? _self.status
|
? _self.status
|
||||||
: status // ignore: cast_nullable_to_non_nullable
|
: status // ignore: cast_nullable_to_non_nullable
|
||||||
as SnAccountStatus?,
|
as dynamic,
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a copy of SnAccountStatusInfo
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@override
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
$SnAccountStatusCopyWith<$Res>? get status {
|
|
||||||
if (_self.status == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $SnAccountStatusCopyWith<$Res>(_self.status!, (value) {
|
|
||||||
return _then(_self.copyWith(status: value));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
mixin _$SnAccountStatus {
|
|
||||||
int get id;
|
|
||||||
DateTime get createdAt;
|
|
||||||
DateTime get updatedAt;
|
|
||||||
DateTime? get deletedAt;
|
|
||||||
String get type;
|
|
||||||
String get label;
|
|
||||||
int get attitude;
|
|
||||||
bool get isNoDisturb;
|
|
||||||
bool get isInvisible;
|
|
||||||
DateTime? get clearAt;
|
|
||||||
int get accountId;
|
|
||||||
|
|
||||||
/// Create a copy of SnAccountStatus
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
$SnAccountStatusCopyWith<SnAccountStatus> get copyWith =>
|
|
||||||
_$SnAccountStatusCopyWithImpl<SnAccountStatus>(
|
|
||||||
this as SnAccountStatus, _$identity);
|
|
||||||
|
|
||||||
/// Serializes this SnAccountStatus to a JSON map.
|
|
||||||
Map<String, dynamic> toJson();
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
return identical(this, other) ||
|
|
||||||
(other.runtimeType == runtimeType &&
|
|
||||||
other is SnAccountStatus &&
|
|
||||||
(identical(other.id, id) || other.id == id) &&
|
|
||||||
(identical(other.createdAt, createdAt) ||
|
|
||||||
other.createdAt == createdAt) &&
|
|
||||||
(identical(other.updatedAt, updatedAt) ||
|
|
||||||
other.updatedAt == updatedAt) &&
|
|
||||||
(identical(other.deletedAt, deletedAt) ||
|
|
||||||
other.deletedAt == deletedAt) &&
|
|
||||||
(identical(other.type, type) || other.type == type) &&
|
|
||||||
(identical(other.label, label) || other.label == label) &&
|
|
||||||
(identical(other.attitude, attitude) ||
|
|
||||||
other.attitude == attitude) &&
|
|
||||||
(identical(other.isNoDisturb, isNoDisturb) ||
|
|
||||||
other.isNoDisturb == isNoDisturb) &&
|
|
||||||
(identical(other.isInvisible, isInvisible) ||
|
|
||||||
other.isInvisible == isInvisible) &&
|
|
||||||
(identical(other.clearAt, clearAt) || other.clearAt == clearAt) &&
|
|
||||||
(identical(other.accountId, accountId) ||
|
|
||||||
other.accountId == accountId));
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hash(
|
|
||||||
runtimeType,
|
|
||||||
id,
|
|
||||||
createdAt,
|
|
||||||
updatedAt,
|
|
||||||
deletedAt,
|
|
||||||
type,
|
|
||||||
label,
|
|
||||||
attitude,
|
|
||||||
isNoDisturb,
|
|
||||||
isInvisible,
|
|
||||||
clearAt,
|
|
||||||
accountId);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return 'SnAccountStatus(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, type: $type, label: $label, attitude: $attitude, isNoDisturb: $isNoDisturb, isInvisible: $isInvisible, clearAt: $clearAt, accountId: $accountId)';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract mixin class $SnAccountStatusCopyWith<$Res> {
|
|
||||||
factory $SnAccountStatusCopyWith(
|
|
||||||
SnAccountStatus value, $Res Function(SnAccountStatus) _then) =
|
|
||||||
_$SnAccountStatusCopyWithImpl;
|
|
||||||
@useResult
|
|
||||||
$Res call(
|
|
||||||
{int id,
|
|
||||||
DateTime createdAt,
|
|
||||||
DateTime updatedAt,
|
|
||||||
DateTime? deletedAt,
|
|
||||||
String type,
|
|
||||||
String label,
|
|
||||||
int attitude,
|
|
||||||
bool isNoDisturb,
|
|
||||||
bool isInvisible,
|
|
||||||
DateTime? clearAt,
|
|
||||||
int accountId});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
class _$SnAccountStatusCopyWithImpl<$Res>
|
|
||||||
implements $SnAccountStatusCopyWith<$Res> {
|
|
||||||
_$SnAccountStatusCopyWithImpl(this._self, this._then);
|
|
||||||
|
|
||||||
final SnAccountStatus _self;
|
|
||||||
final $Res Function(SnAccountStatus) _then;
|
|
||||||
|
|
||||||
/// Create a copy of SnAccountStatus
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
@override
|
|
||||||
$Res call({
|
|
||||||
Object? id = null,
|
|
||||||
Object? createdAt = null,
|
|
||||||
Object? updatedAt = null,
|
|
||||||
Object? deletedAt = freezed,
|
|
||||||
Object? type = null,
|
|
||||||
Object? label = null,
|
|
||||||
Object? attitude = null,
|
|
||||||
Object? isNoDisturb = null,
|
|
||||||
Object? isInvisible = null,
|
|
||||||
Object? clearAt = freezed,
|
|
||||||
Object? accountId = null,
|
|
||||||
}) {
|
|
||||||
return _then(_self.copyWith(
|
|
||||||
id: null == id
|
|
||||||
? _self.id
|
|
||||||
: id // ignore: cast_nullable_to_non_nullable
|
|
||||||
as int,
|
|
||||||
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?,
|
|
||||||
type: null == type
|
|
||||||
? _self.type
|
|
||||||
: type // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String,
|
|
||||||
label: null == label
|
|
||||||
? _self.label
|
|
||||||
: label // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String,
|
|
||||||
attitude: null == attitude
|
|
||||||
? _self.attitude
|
|
||||||
: attitude // ignore: cast_nullable_to_non_nullable
|
|
||||||
as int,
|
|
||||||
isNoDisturb: null == isNoDisturb
|
|
||||||
? _self.isNoDisturb
|
|
||||||
: isNoDisturb // ignore: cast_nullable_to_non_nullable
|
|
||||||
as bool,
|
|
||||||
isInvisible: null == isInvisible
|
|
||||||
? _self.isInvisible
|
|
||||||
: isInvisible // ignore: cast_nullable_to_non_nullable
|
|
||||||
as bool,
|
|
||||||
clearAt: freezed == clearAt
|
|
||||||
? _self.clearAt
|
|
||||||
: clearAt // ignore: cast_nullable_to_non_nullable
|
|
||||||
as DateTime?,
|
|
||||||
accountId: null == accountId
|
|
||||||
? _self.accountId
|
|
||||||
: accountId // ignore: cast_nullable_to_non_nullable
|
|
||||||
as int,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
@JsonSerializable()
|
|
||||||
class _SnAccountStatus implements SnAccountStatus {
|
|
||||||
const _SnAccountStatus(
|
|
||||||
{required this.id,
|
|
||||||
required this.createdAt,
|
|
||||||
required this.updatedAt,
|
|
||||||
required this.deletedAt,
|
|
||||||
required this.type,
|
|
||||||
required this.label,
|
|
||||||
required this.attitude,
|
|
||||||
required this.isNoDisturb,
|
|
||||||
required this.isInvisible,
|
|
||||||
required this.clearAt,
|
|
||||||
required this.accountId});
|
|
||||||
factory _SnAccountStatus.fromJson(Map<String, dynamic> json) =>
|
|
||||||
_$SnAccountStatusFromJson(json);
|
|
||||||
|
|
||||||
@override
|
|
||||||
final int id;
|
|
||||||
@override
|
|
||||||
final DateTime createdAt;
|
|
||||||
@override
|
|
||||||
final DateTime updatedAt;
|
|
||||||
@override
|
|
||||||
final DateTime? deletedAt;
|
|
||||||
@override
|
|
||||||
final String type;
|
|
||||||
@override
|
|
||||||
final String label;
|
|
||||||
@override
|
|
||||||
final int attitude;
|
|
||||||
@override
|
|
||||||
final bool isNoDisturb;
|
|
||||||
@override
|
|
||||||
final bool isInvisible;
|
|
||||||
@override
|
|
||||||
final DateTime? clearAt;
|
|
||||||
@override
|
|
||||||
final int accountId;
|
|
||||||
|
|
||||||
/// Create a copy of SnAccountStatus
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@override
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
_$SnAccountStatusCopyWith<_SnAccountStatus> get copyWith =>
|
|
||||||
__$SnAccountStatusCopyWithImpl<_SnAccountStatus>(this, _$identity);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
return _$SnAccountStatusToJson(
|
|
||||||
this,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
return identical(this, other) ||
|
|
||||||
(other.runtimeType == runtimeType &&
|
|
||||||
other is _SnAccountStatus &&
|
|
||||||
(identical(other.id, id) || other.id == id) &&
|
|
||||||
(identical(other.createdAt, createdAt) ||
|
|
||||||
other.createdAt == createdAt) &&
|
|
||||||
(identical(other.updatedAt, updatedAt) ||
|
|
||||||
other.updatedAt == updatedAt) &&
|
|
||||||
(identical(other.deletedAt, deletedAt) ||
|
|
||||||
other.deletedAt == deletedAt) &&
|
|
||||||
(identical(other.type, type) || other.type == type) &&
|
|
||||||
(identical(other.label, label) || other.label == label) &&
|
|
||||||
(identical(other.attitude, attitude) ||
|
|
||||||
other.attitude == attitude) &&
|
|
||||||
(identical(other.isNoDisturb, isNoDisturb) ||
|
|
||||||
other.isNoDisturb == isNoDisturb) &&
|
|
||||||
(identical(other.isInvisible, isInvisible) ||
|
|
||||||
other.isInvisible == isInvisible) &&
|
|
||||||
(identical(other.clearAt, clearAt) || other.clearAt == clearAt) &&
|
|
||||||
(identical(other.accountId, accountId) ||
|
|
||||||
other.accountId == accountId));
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hash(
|
|
||||||
runtimeType,
|
|
||||||
id,
|
|
||||||
createdAt,
|
|
||||||
updatedAt,
|
|
||||||
deletedAt,
|
|
||||||
type,
|
|
||||||
label,
|
|
||||||
attitude,
|
|
||||||
isNoDisturb,
|
|
||||||
isInvisible,
|
|
||||||
clearAt,
|
|
||||||
accountId);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return 'SnAccountStatus(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, type: $type, label: $label, attitude: $attitude, isNoDisturb: $isNoDisturb, isInvisible: $isInvisible, clearAt: $clearAt, accountId: $accountId)';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract mixin class _$SnAccountStatusCopyWith<$Res>
|
|
||||||
implements $SnAccountStatusCopyWith<$Res> {
|
|
||||||
factory _$SnAccountStatusCopyWith(
|
|
||||||
_SnAccountStatus value, $Res Function(_SnAccountStatus) _then) =
|
|
||||||
__$SnAccountStatusCopyWithImpl;
|
|
||||||
@override
|
|
||||||
@useResult
|
|
||||||
$Res call(
|
|
||||||
{int id,
|
|
||||||
DateTime createdAt,
|
|
||||||
DateTime updatedAt,
|
|
||||||
DateTime? deletedAt,
|
|
||||||
String type,
|
|
||||||
String label,
|
|
||||||
int attitude,
|
|
||||||
bool isNoDisturb,
|
|
||||||
bool isInvisible,
|
|
||||||
DateTime? clearAt,
|
|
||||||
int accountId});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
class __$SnAccountStatusCopyWithImpl<$Res>
|
|
||||||
implements _$SnAccountStatusCopyWith<$Res> {
|
|
||||||
__$SnAccountStatusCopyWithImpl(this._self, this._then);
|
|
||||||
|
|
||||||
final _SnAccountStatus _self;
|
|
||||||
final $Res Function(_SnAccountStatus) _then;
|
|
||||||
|
|
||||||
/// Create a copy of SnAccountStatus
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@override
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
$Res call({
|
|
||||||
Object? id = null,
|
|
||||||
Object? createdAt = null,
|
|
||||||
Object? updatedAt = null,
|
|
||||||
Object? deletedAt = freezed,
|
|
||||||
Object? type = null,
|
|
||||||
Object? label = null,
|
|
||||||
Object? attitude = null,
|
|
||||||
Object? isNoDisturb = null,
|
|
||||||
Object? isInvisible = null,
|
|
||||||
Object? clearAt = freezed,
|
|
||||||
Object? accountId = null,
|
|
||||||
}) {
|
|
||||||
return _then(_SnAccountStatus(
|
|
||||||
id: null == id
|
|
||||||
? _self.id
|
|
||||||
: id // ignore: cast_nullable_to_non_nullable
|
|
||||||
as int,
|
|
||||||
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?,
|
|
||||||
type: null == type
|
|
||||||
? _self.type
|
|
||||||
: type // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String,
|
|
||||||
label: null == label
|
|
||||||
? _self.label
|
|
||||||
: label // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String,
|
|
||||||
attitude: null == attitude
|
|
||||||
? _self.attitude
|
|
||||||
: attitude // ignore: cast_nullable_to_non_nullable
|
|
||||||
as int,
|
|
||||||
isNoDisturb: null == isNoDisturb
|
|
||||||
? _self.isNoDisturb
|
|
||||||
: isNoDisturb // ignore: cast_nullable_to_non_nullable
|
|
||||||
as bool,
|
|
||||||
isInvisible: null == isInvisible
|
|
||||||
? _self.isInvisible
|
|
||||||
: isInvisible // ignore: cast_nullable_to_non_nullable
|
|
||||||
as bool,
|
|
||||||
clearAt: freezed == clearAt
|
|
||||||
? _self.clearAt
|
|
||||||
: clearAt // ignore: cast_nullable_to_non_nullable
|
|
||||||
as DateTime?,
|
|
||||||
accountId: null == accountId
|
|
||||||
? _self.accountId
|
|
||||||
: accountId // ignore: cast_nullable_to_non_nullable
|
|
||||||
as int,
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -210,9 +210,7 @@ _SnAccountStatusInfo _$SnAccountStatusInfoFromJson(Map<String, dynamic> json) =>
|
|||||||
lastSeenAt: json['last_seen_at'] == null
|
lastSeenAt: json['last_seen_at'] == null
|
||||||
? null
|
? null
|
||||||
: DateTime.parse(json['last_seen_at'] as String),
|
: DateTime.parse(json['last_seen_at'] as String),
|
||||||
status: json['status'] == null
|
status: json['status'],
|
||||||
? null
|
|
||||||
: SnAccountStatus.fromJson(json['status'] as Map<String, dynamic>),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> _$SnAccountStatusInfoToJson(
|
Map<String, dynamic> _$SnAccountStatusInfoToJson(
|
||||||
@ -221,41 +219,7 @@ Map<String, dynamic> _$SnAccountStatusInfoToJson(
|
|||||||
'is_disturbable': instance.isDisturbable,
|
'is_disturbable': instance.isDisturbable,
|
||||||
'is_online': instance.isOnline,
|
'is_online': instance.isOnline,
|
||||||
'last_seen_at': instance.lastSeenAt?.toIso8601String(),
|
'last_seen_at': instance.lastSeenAt?.toIso8601String(),
|
||||||
'status': instance.status?.toJson(),
|
'status': instance.status,
|
||||||
};
|
|
||||||
|
|
||||||
_SnAccountStatus _$SnAccountStatusFromJson(Map<String, dynamic> json) =>
|
|
||||||
_SnAccountStatus(
|
|
||||||
id: (json['id'] as num).toInt(),
|
|
||||||
createdAt: DateTime.parse(json['created_at'] as String),
|
|
||||||
updatedAt: DateTime.parse(json['updated_at'] as String),
|
|
||||||
deletedAt: json['deleted_at'] == null
|
|
||||||
? null
|
|
||||||
: DateTime.parse(json['deleted_at'] as String),
|
|
||||||
type: json['type'] as String,
|
|
||||||
label: json['label'] as String,
|
|
||||||
attitude: (json['attitude'] as num).toInt(),
|
|
||||||
isNoDisturb: json['is_no_disturb'] as bool,
|
|
||||||
isInvisible: json['is_invisible'] as bool,
|
|
||||||
clearAt: json['clear_at'] == null
|
|
||||||
? null
|
|
||||||
: DateTime.parse(json['clear_at'] as String),
|
|
||||||
accountId: (json['account_id'] as num).toInt(),
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, dynamic> _$SnAccountStatusToJson(_SnAccountStatus instance) =>
|
|
||||||
<String, dynamic>{
|
|
||||||
'id': instance.id,
|
|
||||||
'created_at': instance.createdAt.toIso8601String(),
|
|
||||||
'updated_at': instance.updatedAt.toIso8601String(),
|
|
||||||
'deleted_at': instance.deletedAt?.toIso8601String(),
|
|
||||||
'type': instance.type,
|
|
||||||
'label': instance.label,
|
|
||||||
'attitude': instance.attitude,
|
|
||||||
'is_no_disturb': instance.isNoDisturb,
|
|
||||||
'is_invisible': instance.isInvisible,
|
|
||||||
'clear_at': instance.clearAt?.toIso8601String(),
|
|
||||||
'account_id': instance.accountId,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
_SnAbuseReport _$SnAbuseReportFromJson(Map<String, dynamic> json) =>
|
_SnAbuseReport _$SnAbuseReportFromJson(Map<String, dynamic> json) =>
|
||||||
|
@ -118,25 +118,18 @@ class AccountPopoverCard extends StatelessWidget {
|
|||||||
return Row(
|
return Row(
|
||||||
children: [
|
children: [
|
||||||
Icon(
|
Icon(
|
||||||
(status?.isDisturbable ?? true)
|
Symbols.circle,
|
||||||
? Symbols.circle
|
fill: 1,
|
||||||
: Symbols.do_not_disturb_on,
|
|
||||||
fill: (status?.isOnline ?? false) ? 1 : 0,
|
|
||||||
size: 16,
|
size: 16,
|
||||||
color: (status?.isOnline ?? false)
|
color:
|
||||||
? (status?.isDisturbable ?? true)
|
(status?.isOnline ?? false) ? Colors.green : Colors.grey,
|
||||||
? Colors.green
|
|
||||||
: Colors.red
|
|
||||||
: Colors.grey,
|
|
||||||
).padding(all: 4),
|
).padding(all: 4),
|
||||||
const Gap(8),
|
const Gap(8),
|
||||||
Text(
|
Text(
|
||||||
status != null
|
status != null
|
||||||
? (status.status?.label.isNotEmpty ?? false)
|
? status.isOnline
|
||||||
? status.status!.label
|
? 'accountStatusOnline'.tr()
|
||||||
: status.isOnline
|
: 'accountStatusOffline'.tr()
|
||||||
? 'accountStatusOnline'.tr()
|
|
||||||
: 'accountStatusOffline'.tr()
|
|
||||||
: 'loading'.tr(),
|
: 'loading'.tr(),
|
||||||
),
|
),
|
||||||
if (status != null &&
|
if (status != null &&
|
||||||
|
@ -1,391 +0,0 @@
|
|||||||
import 'package:dio/dio.dart';
|
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:gap/gap.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';
|
|
||||||
import 'package:surface/types/account.dart';
|
|
||||||
import 'package:surface/widgets/dialog.dart';
|
|
||||||
import 'package:surface/widgets/loading_indicator.dart';
|
|
||||||
|
|
||||||
final Map<String, (Widget, String, String?)> kPresetStatus = {
|
|
||||||
'online': (
|
|
||||||
const Icon(Symbols.circle, color: Colors.green, fill: 1),
|
|
||||||
'accountStatusOnline'.tr(),
|
|
||||||
null,
|
|
||||||
),
|
|
||||||
'silent': (
|
|
||||||
const Icon(Symbols.do_not_disturb_on, color: Colors.red),
|
|
||||||
'accountStatusSilent'.tr(),
|
|
||||||
'accountStatusSilentDesc'.tr(),
|
|
||||||
),
|
|
||||||
'invisible': (
|
|
||||||
const Icon(Symbols.circle, color: Colors.grey),
|
|
||||||
'accountStatusInvisible'.tr(),
|
|
||||||
'accountStatusInvisibleDesc'.tr(),
|
|
||||||
),
|
|
||||||
};
|
|
||||||
|
|
||||||
class AccountStatusActionPopup extends StatefulWidget {
|
|
||||||
final SnAccountStatusInfo? currentStatus;
|
|
||||||
const AccountStatusActionPopup({super.key, this.currentStatus});
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<AccountStatusActionPopup> createState() =>
|
|
||||||
_AccountStatusActionPopupState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _AccountStatusActionPopupState extends State<AccountStatusActionPopup> {
|
|
||||||
bool _isBusy = false;
|
|
||||||
|
|
||||||
Future<void> setStatus(
|
|
||||||
String type,
|
|
||||||
String? label,
|
|
||||||
int attitude, {
|
|
||||||
bool isUpdate = false,
|
|
||||||
bool isSilent = false,
|
|
||||||
bool isInvisible = false,
|
|
||||||
DateTime? clearAt,
|
|
||||||
}) async {
|
|
||||||
setState(() => _isBusy = true);
|
|
||||||
final sn = context.read<SnNetworkProvider>();
|
|
||||||
|
|
||||||
final payload = {
|
|
||||||
'type': type,
|
|
||||||
'label': label,
|
|
||||||
'attitude': attitude,
|
|
||||||
'is_no_disturb': isSilent,
|
|
||||||
'is_invisible': isInvisible,
|
|
||||||
'clear_at': clearAt?.toUtc().toIso8601String()
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
|
||||||
await sn.client.request(
|
|
||||||
'/cgi/id/users/me/status',
|
|
||||||
data: payload,
|
|
||||||
options: Options(method: isUpdate ? 'PUT' : 'POST'),
|
|
||||||
);
|
|
||||||
if (!mounted) return;
|
|
||||||
Navigator.pop(context, true);
|
|
||||||
} catch (err) {
|
|
||||||
if (!mounted) return;
|
|
||||||
context.showErrorDialog(err);
|
|
||||||
} finally {
|
|
||||||
setState(() => _isBusy = false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _clearStatus() async {
|
|
||||||
if (_isBusy) return;
|
|
||||||
|
|
||||||
setState(() => _isBusy = true);
|
|
||||||
try {
|
|
||||||
final sn = context.read<SnNetworkProvider>();
|
|
||||||
await sn.client.delete('/cgi/id/users/me/status');
|
|
||||||
if (!mounted) return;
|
|
||||||
Navigator.of(context).pop(true);
|
|
||||||
} catch (err) {
|
|
||||||
if (!mounted) return;
|
|
||||||
context.showErrorDialog(err);
|
|
||||||
} finally {
|
|
||||||
setState(() => _isBusy = false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Row(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
const Icon(Symbols.mood, size: 24),
|
|
||||||
const Gap(16),
|
|
||||||
Text('accountChangeStatus',
|
|
||||||
style: Theme.of(context).textTheme.titleLarge)
|
|
||||||
.tr(),
|
|
||||||
],
|
|
||||||
).padding(horizontal: 20, top: 16, bottom: 12),
|
|
||||||
LoadingIndicator(isActive: _isBusy),
|
|
||||||
SizedBox(
|
|
||||||
height: 48,
|
|
||||||
child: ListView(
|
|
||||||
padding: EdgeInsets.symmetric(horizontal: 18),
|
|
||||||
scrollDirection: Axis.horizontal,
|
|
||||||
children: kPresetStatus.entries
|
|
||||||
.map(
|
|
||||||
(x) => StyledWidget(ActionChip(
|
|
||||||
avatar: x.value.$1,
|
|
||||||
label: Text(x.value.$2),
|
|
||||||
tooltip: x.value.$3,
|
|
||||||
onPressed: _isBusy
|
|
||||||
? null
|
|
||||||
: () {
|
|
||||||
setStatus(
|
|
||||||
x.key,
|
|
||||||
x.value.$2,
|
|
||||||
0,
|
|
||||||
isInvisible: x.key == 'invisible',
|
|
||||||
isSilent: x.key == 'silent',
|
|
||||||
);
|
|
||||||
},
|
|
||||||
)).padding(right: 6),
|
|
||||||
)
|
|
||||||
.toList(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const Gap(16),
|
|
||||||
const Divider(thickness: 0.3, height: 0.3),
|
|
||||||
ListTile(
|
|
||||||
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
|
||||||
leading: widget.currentStatus != null
|
|
||||||
? const Icon(Icons.edit)
|
|
||||||
: const Icon(Icons.add),
|
|
||||||
title: Text('accountCustomStatus').tr(),
|
|
||||||
subtitle: Text('accountCustomStatusDescription').tr(),
|
|
||||||
onTap: _isBusy
|
|
||||||
? null
|
|
||||||
: () async {
|
|
||||||
final val = await showDialog(
|
|
||||||
context: context,
|
|
||||||
builder: (context) => _AccountStatusEditorDialog(
|
|
||||||
currentStatus: widget.currentStatus,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
if (val == true && context.mounted) {
|
|
||||||
Navigator.of(context).pop(true);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
if (widget.currentStatus != null)
|
|
||||||
ListTile(
|
|
||||||
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
|
||||||
leading: const Icon(Icons.clear),
|
|
||||||
title: Text('accountClearStatus').tr(),
|
|
||||||
subtitle: Text('accountClearStatusDescription').tr(),
|
|
||||||
onTap: _isBusy
|
|
||||||
? null
|
|
||||||
: () {
|
|
||||||
_clearStatus();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _AccountStatusEditorDialog extends StatefulWidget {
|
|
||||||
final SnAccountStatusInfo? currentStatus;
|
|
||||||
const _AccountStatusEditorDialog({this.currentStatus});
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<_AccountStatusEditorDialog> createState() =>
|
|
||||||
_AccountStatusEditorDialogState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _AccountStatusEditorDialogState
|
|
||||||
extends State<_AccountStatusEditorDialog> {
|
|
||||||
bool _isBusy = false;
|
|
||||||
|
|
||||||
final TextEditingController _labelController = TextEditingController();
|
|
||||||
final TextEditingController _clearAtController = TextEditingController();
|
|
||||||
|
|
||||||
int _attitude = 0;
|
|
||||||
bool _isSilent = false;
|
|
||||||
bool _isInvisible = false;
|
|
||||||
DateTime? _clearAt;
|
|
||||||
|
|
||||||
Future<void> _selectClearAt() async {
|
|
||||||
final DateTime? pickedDate = await showDatePicker(
|
|
||||||
context: context,
|
|
||||||
initialDate: _clearAt?.toLocal() ?? DateTime.now(),
|
|
||||||
firstDate: DateTime.now(),
|
|
||||||
lastDate: DateTime.now().add(const Duration(days: 365)),
|
|
||||||
);
|
|
||||||
if (pickedDate == null) return;
|
|
||||||
if (!mounted) return;
|
|
||||||
final TimeOfDay? pickedTime = await showTimePicker(
|
|
||||||
context: context,
|
|
||||||
initialTime: TimeOfDay.now(),
|
|
||||||
);
|
|
||||||
if (pickedTime == null) return;
|
|
||||||
if (!mounted) return;
|
|
||||||
final picked = pickedDate.copyWith(
|
|
||||||
hour: pickedTime.hour,
|
|
||||||
minute: pickedTime.minute,
|
|
||||||
);
|
|
||||||
setState(() {
|
|
||||||
_clearAt = picked;
|
|
||||||
_clearAtController.text = DateFormat('y/M/d HH:mm').format(_clearAt!);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _applyStatus() async {
|
|
||||||
if (_isBusy) return;
|
|
||||||
|
|
||||||
setState(() => _isBusy = true);
|
|
||||||
try {
|
|
||||||
final sn = context.read<SnNetworkProvider>();
|
|
||||||
await sn.client.request(
|
|
||||||
'/cgi/id/users/me/status',
|
|
||||||
data: {
|
|
||||||
'type': 'custom',
|
|
||||||
'label': _labelController.text,
|
|
||||||
'attitude': _attitude,
|
|
||||||
'is_no_disturb': _isSilent,
|
|
||||||
'is_invisible': _isInvisible,
|
|
||||||
'clear_at': _clearAt?.toUtc().toIso8601String(),
|
|
||||||
},
|
|
||||||
options: Options(
|
|
||||||
method: widget.currentStatus?.status != null ? 'PUT' : 'POST',
|
|
||||||
),
|
|
||||||
);
|
|
||||||
if (!mounted) return;
|
|
||||||
Navigator.of(context).pop(true);
|
|
||||||
} catch (err) {
|
|
||||||
if (!mounted) return;
|
|
||||||
context.showErrorDialog(err);
|
|
||||||
} finally {
|
|
||||||
setState(() => _isBusy = false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _syncWidget() {
|
|
||||||
if (widget.currentStatus?.status != null) {
|
|
||||||
_clearAt = widget.currentStatus!.status!.clearAt;
|
|
||||||
if (_clearAt != null) {
|
|
||||||
_clearAtController.text = DateFormat('y/M/d HH:mm').format(_clearAt!);
|
|
||||||
}
|
|
||||||
|
|
||||||
_labelController.text = widget.currentStatus!.status!.label;
|
|
||||||
_attitude = widget.currentStatus!.status!.attitude;
|
|
||||||
_isInvisible = widget.currentStatus!.status!.isInvisible;
|
|
||||||
_isSilent = widget.currentStatus!.status!.isNoDisturb;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
_syncWidget();
|
|
||||||
super.initState();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return AlertDialog(
|
|
||||||
title: Text('accountCustomStatus').tr(),
|
|
||||||
content: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
LoadingIndicator(isActive: _isBusy),
|
|
||||||
TextField(
|
|
||||||
controller: _labelController,
|
|
||||||
decoration: InputDecoration(
|
|
||||||
isDense: true,
|
|
||||||
prefixIcon: const Icon(Icons.label),
|
|
||||||
border: const OutlineInputBorder(),
|
|
||||||
labelText: 'fieldAccountStatusLabel'.tr(),
|
|
||||||
),
|
|
||||||
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
|
||||||
),
|
|
||||||
const Gap(8),
|
|
||||||
TextField(
|
|
||||||
controller: _clearAtController,
|
|
||||||
readOnly: true,
|
|
||||||
decoration: InputDecoration(
|
|
||||||
isDense: true,
|
|
||||||
prefixIcon: const Icon(Icons.event_busy),
|
|
||||||
border: const OutlineInputBorder(),
|
|
||||||
labelText: 'fieldAccountStatusClearAt'.tr(),
|
|
||||||
),
|
|
||||||
onTap: () => _selectClearAt(),
|
|
||||||
),
|
|
||||||
const Gap(8),
|
|
||||||
SingleChildScrollView(
|
|
||||||
scrollDirection: Axis.horizontal,
|
|
||||||
child: Wrap(
|
|
||||||
spacing: 6,
|
|
||||||
runSpacing: 0,
|
|
||||||
children: [
|
|
||||||
ChoiceChip(
|
|
||||||
avatar: Icon(
|
|
||||||
Symbols.radio_button_unchecked,
|
|
||||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
|
||||||
),
|
|
||||||
selected: _attitude == 2,
|
|
||||||
label: Text('accountStatusNegative'.tr()),
|
|
||||||
onSelected: (val) {
|
|
||||||
if (val) setState(() => _attitude = 2);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ChoiceChip(
|
|
||||||
avatar: Icon(
|
|
||||||
Symbols.contrast,
|
|
||||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
|
||||||
),
|
|
||||||
selected: _attitude == 0,
|
|
||||||
label: Text('accountStatusNeutral'.tr()),
|
|
||||||
onSelected: (val) {
|
|
||||||
if (val) setState(() => _attitude = 0);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ChoiceChip(
|
|
||||||
avatar: Icon(
|
|
||||||
Symbols.circle,
|
|
||||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
|
||||||
),
|
|
||||||
selected: _attitude == 1,
|
|
||||||
label: Text('accountStatusPositive'.tr()),
|
|
||||||
onSelected: (val) {
|
|
||||||
if (val) setState(() => _attitude = 1);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const Gap(4),
|
|
||||||
SingleChildScrollView(
|
|
||||||
scrollDirection: Axis.horizontal,
|
|
||||||
child: Wrap(
|
|
||||||
spacing: 6,
|
|
||||||
runSpacing: 0,
|
|
||||||
children: [
|
|
||||||
ChoiceChip(
|
|
||||||
selected: _isSilent,
|
|
||||||
label: Text('accountStatusSilent').tr(),
|
|
||||||
onSelected: (val) {
|
|
||||||
setState(() => _isSilent = val);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ChoiceChip(
|
|
||||||
selected: _isInvisible,
|
|
||||||
label: Text('accountStatusInvisible').tr(),
|
|
||||||
onSelected: (val) {
|
|
||||||
setState(() => _isInvisible = val);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
actions: <Widget>[
|
|
||||||
TextButton(
|
|
||||||
style: TextButton.styleFrom(
|
|
||||||
foregroundColor:
|
|
||||||
Theme.of(context).colorScheme.onSurface.withOpacity(0.8),
|
|
||||||
),
|
|
||||||
onPressed: _isBusy ? null : () => Navigator.pop(context),
|
|
||||||
child: Text('dialogCancel').tr(),
|
|
||||||
),
|
|
||||||
TextButton(
|
|
||||||
onPressed: _isBusy ? null : () => _applyStatus(),
|
|
||||||
child: Text('dialogConfirm').tr(),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user