Refined presense activity

This commit is contained in:
2025-11-01 21:47:34 +08:00
parent 5ee2e70442
commit ba8d5cee09
11 changed files with 421 additions and 239 deletions

View File

@@ -1302,5 +1302,9 @@
"aiThought": "AI Thought", "aiThought": "AI Thought",
"aiThoughtTitle": "Let sn-chan think", "aiThoughtTitle": "Let sn-chan think",
"postReferenceUnavailable": "Referenced post is unavailable", "postReferenceUnavailable": "Referenced post is unavailable",
"fabLocation": "FAB Location" "fabLocation": "FAB Location",
"activities": "Activities",
"presenceTypeGaming": "Playing",
"presenceTypeMusic": "Listening to Music",
"presenceTypeWorkout": "Working out"
} }

View File

@@ -19,8 +19,8 @@ sealed class SnNotableDay with _$SnNotableDay {
} }
@freezed @freezed
sealed class SnActivity with _$SnActivity { sealed class SnTimelineEvent with _$SnTimelineEvent {
const factory SnActivity({ const factory SnTimelineEvent({
required String id, required String id,
required String type, required String type,
required String resourceIdentifier, required String resourceIdentifier,
@@ -28,10 +28,10 @@ sealed class SnActivity with _$SnActivity {
required DateTime createdAt, required DateTime createdAt,
required DateTime updatedAt, required DateTime updatedAt,
required DateTime? deletedAt, required DateTime? deletedAt,
}) = _SnActivity; }) = _SnTimelineEvent;
factory SnActivity.fromJson(Map<String, dynamic> json) => factory SnTimelineEvent.fromJson(Map<String, dynamic> json) =>
_$SnActivityFromJson(json); _$SnTimelineEventFromJson(json);
} }
@freezed @freezed
@@ -79,7 +79,7 @@ sealed class SnEventCalendarEntry with _$SnEventCalendarEntry {
sealed class SnPresenceActivity with _$SnPresenceActivity { sealed class SnPresenceActivity with _$SnPresenceActivity {
const factory SnPresenceActivity({ const factory SnPresenceActivity({
required String id, required String id,
required String type, required int type,
required String? manualId, required String? manualId,
required String? title, required String? title,
required String? subtitle, required String? subtitle,

View File

@@ -288,22 +288,22 @@ as List<int>,
/// @nodoc /// @nodoc
mixin _$SnActivity { mixin _$SnTimelineEvent {
String get id; String get type; String get resourceIdentifier; dynamic get data; DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt; String get id; String get type; String get resourceIdentifier; dynamic get data; DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt;
/// Create a copy of SnActivity /// Create a copy of SnTimelineEvent
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline') @pragma('vm:prefer-inline')
$SnActivityCopyWith<SnActivity> get copyWith => _$SnActivityCopyWithImpl<SnActivity>(this as SnActivity, _$identity); $SnTimelineEventCopyWith<SnTimelineEvent> get copyWith => _$SnTimelineEventCopyWithImpl<SnTimelineEvent>(this as SnTimelineEvent, _$identity);
/// Serializes this SnActivity to a JSON map. /// Serializes this SnTimelineEvent to a JSON map.
Map<String, dynamic> toJson(); Map<String, dynamic> toJson();
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is SnActivity&&(identical(other.id, id) || other.id == id)&&(identical(other.type, type) || other.type == type)&&(identical(other.resourceIdentifier, resourceIdentifier) || other.resourceIdentifier == resourceIdentifier)&&const DeepCollectionEquality().equals(other.data, data)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)); return identical(this, other) || (other.runtimeType == runtimeType&&other is SnTimelineEvent&&(identical(other.id, id) || other.id == id)&&(identical(other.type, type) || other.type == type)&&(identical(other.resourceIdentifier, resourceIdentifier) || other.resourceIdentifier == resourceIdentifier)&&const DeepCollectionEquality().equals(other.data, data)&&(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) @JsonKey(includeFromJson: false, includeToJson: false)
@@ -312,15 +312,15 @@ int get hashCode => Object.hash(runtimeType,id,type,resourceIdentifier,const Dee
@override @override
String toString() { String toString() {
return 'SnActivity(id: $id, type: $type, resourceIdentifier: $resourceIdentifier, data: $data, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)'; return 'SnTimelineEvent(id: $id, type: $type, resourceIdentifier: $resourceIdentifier, data: $data, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)';
} }
} }
/// @nodoc /// @nodoc
abstract mixin class $SnActivityCopyWith<$Res> { abstract mixin class $SnTimelineEventCopyWith<$Res> {
factory $SnActivityCopyWith(SnActivity value, $Res Function(SnActivity) _then) = _$SnActivityCopyWithImpl; factory $SnTimelineEventCopyWith(SnTimelineEvent value, $Res Function(SnTimelineEvent) _then) = _$SnTimelineEventCopyWithImpl;
@useResult @useResult
$Res call({ $Res call({
String id, String type, String resourceIdentifier, dynamic data, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt String id, String type, String resourceIdentifier, dynamic data, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
@@ -331,14 +331,14 @@ $Res call({
} }
/// @nodoc /// @nodoc
class _$SnActivityCopyWithImpl<$Res> class _$SnTimelineEventCopyWithImpl<$Res>
implements $SnActivityCopyWith<$Res> { implements $SnTimelineEventCopyWith<$Res> {
_$SnActivityCopyWithImpl(this._self, this._then); _$SnTimelineEventCopyWithImpl(this._self, this._then);
final SnActivity _self; final SnTimelineEvent _self;
final $Res Function(SnActivity) _then; final $Res Function(SnTimelineEvent) _then;
/// Create a copy of SnActivity /// Create a copy of SnTimelineEvent
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? type = null,Object? resourceIdentifier = null,Object? data = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) { @pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? type = null,Object? resourceIdentifier = null,Object? data = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) {
return _then(_self.copyWith( return _then(_self.copyWith(
@@ -356,8 +356,8 @@ as DateTime?,
} }
/// Adds pattern-matching-related methods to [SnActivity]. /// Adds pattern-matching-related methods to [SnTimelineEvent].
extension SnActivityPatterns on SnActivity { extension SnTimelineEventPatterns on SnTimelineEvent {
/// A variant of `map` that fallback to returning `orElse`. /// A variant of `map` that fallback to returning `orElse`.
/// ///
/// It is equivalent to doing: /// It is equivalent to doing:
@@ -370,10 +370,10 @@ extension SnActivityPatterns on SnActivity {
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _SnActivity value)? $default,{required TResult orElse(),}){ @optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _SnTimelineEvent value)? $default,{required TResult orElse(),}){
final _that = this; final _that = this;
switch (_that) { switch (_that) {
case _SnActivity() when $default != null: case _SnTimelineEvent() when $default != null:
return $default(_that);case _: return $default(_that);case _:
return orElse(); return orElse();
@@ -392,10 +392,10 @@ return $default(_that);case _:
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _SnActivity value) $default,){ @optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _SnTimelineEvent value) $default,){
final _that = this; final _that = this;
switch (_that) { switch (_that) {
case _SnActivity(): case _SnTimelineEvent():
return $default(_that);} return $default(_that);}
} }
/// A variant of `map` that fallback to returning `null`. /// A variant of `map` that fallback to returning `null`.
@@ -410,10 +410,10 @@ return $default(_that);}
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _SnActivity value)? $default,){ @optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _SnTimelineEvent value)? $default,){
final _that = this; final _that = this;
switch (_that) { switch (_that) {
case _SnActivity() when $default != null: case _SnTimelineEvent() when $default != null:
return $default(_that);case _: return $default(_that);case _:
return null; return null;
@@ -433,7 +433,7 @@ return $default(_that);case _:
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String type, String resourceIdentifier, dynamic data, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt)? $default,{required TResult orElse(),}) {final _that = this; @optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String type, String resourceIdentifier, dynamic data, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) { switch (_that) {
case _SnActivity() when $default != null: case _SnTimelineEvent() when $default != null:
return $default(_that.id,_that.type,_that.resourceIdentifier,_that.data,_that.createdAt,_that.updatedAt,_that.deletedAt);case _: return $default(_that.id,_that.type,_that.resourceIdentifier,_that.data,_that.createdAt,_that.updatedAt,_that.deletedAt);case _:
return orElse(); return orElse();
@@ -454,7 +454,7 @@ return $default(_that.id,_that.type,_that.resourceIdentifier,_that.data,_that.cr
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String type, String resourceIdentifier, dynamic data, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt) $default,) {final _that = this; @optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String type, String resourceIdentifier, dynamic data, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt) $default,) {final _that = this;
switch (_that) { switch (_that) {
case _SnActivity(): case _SnTimelineEvent():
return $default(_that.id,_that.type,_that.resourceIdentifier,_that.data,_that.createdAt,_that.updatedAt,_that.deletedAt);} return $default(_that.id,_that.type,_that.resourceIdentifier,_that.data,_that.createdAt,_that.updatedAt,_that.deletedAt);}
} }
/// A variant of `when` that fallback to returning `null` /// A variant of `when` that fallback to returning `null`
@@ -471,7 +471,7 @@ return $default(_that.id,_that.type,_that.resourceIdentifier,_that.data,_that.cr
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String type, String resourceIdentifier, dynamic data, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt)? $default,) {final _that = this; @optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String type, String resourceIdentifier, dynamic data, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt)? $default,) {final _that = this;
switch (_that) { switch (_that) {
case _SnActivity() when $default != null: case _SnTimelineEvent() when $default != null:
return $default(_that.id,_that.type,_that.resourceIdentifier,_that.data,_that.createdAt,_that.updatedAt,_that.deletedAt);case _: return $default(_that.id,_that.type,_that.resourceIdentifier,_that.data,_that.createdAt,_that.updatedAt,_that.deletedAt);case _:
return null; return null;
@@ -483,9 +483,9 @@ return $default(_that.id,_that.type,_that.resourceIdentifier,_that.data,_that.cr
/// @nodoc /// @nodoc
@JsonSerializable() @JsonSerializable()
class _SnActivity implements SnActivity { class _SnTimelineEvent implements SnTimelineEvent {
const _SnActivity({required this.id, required this.type, required this.resourceIdentifier, required this.data, required this.createdAt, required this.updatedAt, required this.deletedAt}); const _SnTimelineEvent({required this.id, required this.type, required this.resourceIdentifier, required this.data, required this.createdAt, required this.updatedAt, required this.deletedAt});
factory _SnActivity.fromJson(Map<String, dynamic> json) => _$SnActivityFromJson(json); factory _SnTimelineEvent.fromJson(Map<String, dynamic> json) => _$SnTimelineEventFromJson(json);
@override final String id; @override final String id;
@override final String type; @override final String type;
@@ -495,20 +495,20 @@ class _SnActivity implements SnActivity {
@override final DateTime updatedAt; @override final DateTime updatedAt;
@override final DateTime? deletedAt; @override final DateTime? deletedAt;
/// Create a copy of SnActivity /// Create a copy of SnTimelineEvent
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false) @override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline') @pragma('vm:prefer-inline')
_$SnActivityCopyWith<_SnActivity> get copyWith => __$SnActivityCopyWithImpl<_SnActivity>(this, _$identity); _$SnTimelineEventCopyWith<_SnTimelineEvent> get copyWith => __$SnTimelineEventCopyWithImpl<_SnTimelineEvent>(this, _$identity);
@override @override
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
return _$SnActivityToJson(this, ); return _$SnTimelineEventToJson(this, );
} }
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnActivity&&(identical(other.id, id) || other.id == id)&&(identical(other.type, type) || other.type == type)&&(identical(other.resourceIdentifier, resourceIdentifier) || other.resourceIdentifier == resourceIdentifier)&&const DeepCollectionEquality().equals(other.data, data)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)); return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnTimelineEvent&&(identical(other.id, id) || other.id == id)&&(identical(other.type, type) || other.type == type)&&(identical(other.resourceIdentifier, resourceIdentifier) || other.resourceIdentifier == resourceIdentifier)&&const DeepCollectionEquality().equals(other.data, data)&&(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) @JsonKey(includeFromJson: false, includeToJson: false)
@@ -517,15 +517,15 @@ int get hashCode => Object.hash(runtimeType,id,type,resourceIdentifier,const Dee
@override @override
String toString() { String toString() {
return 'SnActivity(id: $id, type: $type, resourceIdentifier: $resourceIdentifier, data: $data, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)'; return 'SnTimelineEvent(id: $id, type: $type, resourceIdentifier: $resourceIdentifier, data: $data, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)';
} }
} }
/// @nodoc /// @nodoc
abstract mixin class _$SnActivityCopyWith<$Res> implements $SnActivityCopyWith<$Res> { abstract mixin class _$SnTimelineEventCopyWith<$Res> implements $SnTimelineEventCopyWith<$Res> {
factory _$SnActivityCopyWith(_SnActivity value, $Res Function(_SnActivity) _then) = __$SnActivityCopyWithImpl; factory _$SnTimelineEventCopyWith(_SnTimelineEvent value, $Res Function(_SnTimelineEvent) _then) = __$SnTimelineEventCopyWithImpl;
@override @useResult @override @useResult
$Res call({ $Res call({
String id, String type, String resourceIdentifier, dynamic data, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt String id, String type, String resourceIdentifier, dynamic data, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
@@ -536,17 +536,17 @@ $Res call({
} }
/// @nodoc /// @nodoc
class __$SnActivityCopyWithImpl<$Res> class __$SnTimelineEventCopyWithImpl<$Res>
implements _$SnActivityCopyWith<$Res> { implements _$SnTimelineEventCopyWith<$Res> {
__$SnActivityCopyWithImpl(this._self, this._then); __$SnTimelineEventCopyWithImpl(this._self, this._then);
final _SnActivity _self; final _SnTimelineEvent _self;
final $Res Function(_SnActivity) _then; final $Res Function(_SnTimelineEvent) _then;
/// Create a copy of SnActivity /// Create a copy of SnTimelineEvent
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? type = null,Object? resourceIdentifier = null,Object? data = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) { @override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? type = null,Object? resourceIdentifier = null,Object? data = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) {
return _then(_SnActivity( return _then(_SnTimelineEvent(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable as String,type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable
as String,resourceIdentifier: null == resourceIdentifier ? _self.resourceIdentifier : resourceIdentifier // ignore: cast_nullable_to_non_nullable as String,resourceIdentifier: null == resourceIdentifier ? _self.resourceIdentifier : resourceIdentifier // ignore: cast_nullable_to_non_nullable
@@ -1429,7 +1429,7 @@ $SnCheckInResultCopyWith<$Res>? get checkInResult {
/// @nodoc /// @nodoc
mixin _$SnPresenceActivity { mixin _$SnPresenceActivity {
String get id; String get type; String? get manualId; String? get title; String? get subtitle; String? get caption; Map<String, dynamic>? get meta; int get leaseMinutes; DateTime get leaseExpiresAt; String get accountId; DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt; String get id; int get type; String? get manualId; String? get title; String? get subtitle; String? get caption; Map<String, dynamic>? get meta; int get leaseMinutes; DateTime get leaseExpiresAt; String get accountId; DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt;
/// Create a copy of SnPresenceActivity /// Create a copy of SnPresenceActivity
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@@ -1462,7 +1462,7 @@ abstract mixin class $SnPresenceActivityCopyWith<$Res> {
factory $SnPresenceActivityCopyWith(SnPresenceActivity value, $Res Function(SnPresenceActivity) _then) = _$SnPresenceActivityCopyWithImpl; factory $SnPresenceActivityCopyWith(SnPresenceActivity value, $Res Function(SnPresenceActivity) _then) = _$SnPresenceActivityCopyWithImpl;
@useResult @useResult
$Res call({ $Res call({
String id, String type, String? manualId, String? title, String? subtitle, String? caption, Map<String, dynamic>? meta, int leaseMinutes, DateTime leaseExpiresAt, String accountId, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt String id, int type, String? manualId, String? title, String? subtitle, String? caption, Map<String, dynamic>? meta, int leaseMinutes, DateTime leaseExpiresAt, String accountId, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
}); });
@@ -1483,7 +1483,7 @@ class _$SnPresenceActivityCopyWithImpl<$Res>
return _then(_self.copyWith( return _then(_self.copyWith(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable as String,type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable
as String,manualId: freezed == manualId ? _self.manualId : manualId // ignore: cast_nullable_to_non_nullable as int,manualId: freezed == manualId ? _self.manualId : manualId // ignore: cast_nullable_to_non_nullable
as String?,title: freezed == title ? _self.title : title // ignore: cast_nullable_to_non_nullable as String?,title: freezed == title ? _self.title : title // ignore: cast_nullable_to_non_nullable
as String?,subtitle: freezed == subtitle ? _self.subtitle : subtitle // ignore: cast_nullable_to_non_nullable as String?,subtitle: freezed == subtitle ? _self.subtitle : subtitle // ignore: cast_nullable_to_non_nullable
as String?,caption: freezed == caption ? _self.caption : caption // ignore: cast_nullable_to_non_nullable as String?,caption: freezed == caption ? _self.caption : caption // ignore: cast_nullable_to_non_nullable
@@ -1576,7 +1576,7 @@ return $default(_that);case _:
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String type, String? manualId, String? title, String? subtitle, String? caption, Map<String, dynamic>? meta, int leaseMinutes, DateTime leaseExpiresAt, String accountId, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt)? $default,{required TResult orElse(),}) {final _that = this; @optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, int type, String? manualId, String? title, String? subtitle, String? caption, Map<String, dynamic>? meta, int leaseMinutes, DateTime leaseExpiresAt, String accountId, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) { switch (_that) {
case _SnPresenceActivity() when $default != null: case _SnPresenceActivity() when $default != null:
return $default(_that.id,_that.type,_that.manualId,_that.title,_that.subtitle,_that.caption,_that.meta,_that.leaseMinutes,_that.leaseExpiresAt,_that.accountId,_that.createdAt,_that.updatedAt,_that.deletedAt);case _: return $default(_that.id,_that.type,_that.manualId,_that.title,_that.subtitle,_that.caption,_that.meta,_that.leaseMinutes,_that.leaseExpiresAt,_that.accountId,_that.createdAt,_that.updatedAt,_that.deletedAt);case _:
@@ -1597,7 +1597,7 @@ return $default(_that.id,_that.type,_that.manualId,_that.title,_that.subtitle,_t
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String type, String? manualId, String? title, String? subtitle, String? caption, Map<String, dynamic>? meta, int leaseMinutes, DateTime leaseExpiresAt, String accountId, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt) $default,) {final _that = this; @optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, int type, String? manualId, String? title, String? subtitle, String? caption, Map<String, dynamic>? meta, int leaseMinutes, DateTime leaseExpiresAt, String accountId, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt) $default,) {final _that = this;
switch (_that) { switch (_that) {
case _SnPresenceActivity(): case _SnPresenceActivity():
return $default(_that.id,_that.type,_that.manualId,_that.title,_that.subtitle,_that.caption,_that.meta,_that.leaseMinutes,_that.leaseExpiresAt,_that.accountId,_that.createdAt,_that.updatedAt,_that.deletedAt);} return $default(_that.id,_that.type,_that.manualId,_that.title,_that.subtitle,_that.caption,_that.meta,_that.leaseMinutes,_that.leaseExpiresAt,_that.accountId,_that.createdAt,_that.updatedAt,_that.deletedAt);}
@@ -1614,7 +1614,7 @@ return $default(_that.id,_that.type,_that.manualId,_that.title,_that.subtitle,_t
/// } /// }
/// ``` /// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String type, String? manualId, String? title, String? subtitle, String? caption, Map<String, dynamic>? meta, int leaseMinutes, DateTime leaseExpiresAt, String accountId, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt)? $default,) {final _that = this; @optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, int type, String? manualId, String? title, String? subtitle, String? caption, Map<String, dynamic>? meta, int leaseMinutes, DateTime leaseExpiresAt, String accountId, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt)? $default,) {final _that = this;
switch (_that) { switch (_that) {
case _SnPresenceActivity() when $default != null: case _SnPresenceActivity() when $default != null:
return $default(_that.id,_that.type,_that.manualId,_that.title,_that.subtitle,_that.caption,_that.meta,_that.leaseMinutes,_that.leaseExpiresAt,_that.accountId,_that.createdAt,_that.updatedAt,_that.deletedAt);case _: return $default(_that.id,_that.type,_that.manualId,_that.title,_that.subtitle,_that.caption,_that.meta,_that.leaseMinutes,_that.leaseExpiresAt,_that.accountId,_that.createdAt,_that.updatedAt,_that.deletedAt);case _:
@@ -1633,7 +1633,7 @@ class _SnPresenceActivity implements SnPresenceActivity {
factory _SnPresenceActivity.fromJson(Map<String, dynamic> json) => _$SnPresenceActivityFromJson(json); factory _SnPresenceActivity.fromJson(Map<String, dynamic> json) => _$SnPresenceActivityFromJson(json);
@override final String id; @override final String id;
@override final String type; @override final int type;
@override final String? manualId; @override final String? manualId;
@override final String? title; @override final String? title;
@override final String? subtitle; @override final String? subtitle;
@@ -1687,7 +1687,7 @@ abstract mixin class _$SnPresenceActivityCopyWith<$Res> implements $SnPresenceAc
factory _$SnPresenceActivityCopyWith(_SnPresenceActivity value, $Res Function(_SnPresenceActivity) _then) = __$SnPresenceActivityCopyWithImpl; factory _$SnPresenceActivityCopyWith(_SnPresenceActivity value, $Res Function(_SnPresenceActivity) _then) = __$SnPresenceActivityCopyWithImpl;
@override @useResult @override @useResult
$Res call({ $Res call({
String id, String type, String? manualId, String? title, String? subtitle, String? caption, Map<String, dynamic>? meta, int leaseMinutes, DateTime leaseExpiresAt, String accountId, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt String id, int type, String? manualId, String? title, String? subtitle, String? caption, Map<String, dynamic>? meta, int leaseMinutes, DateTime leaseExpiresAt, String accountId, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
}); });
@@ -1708,7 +1708,7 @@ class __$SnPresenceActivityCopyWithImpl<$Res>
return _then(_SnPresenceActivity( return _then(_SnPresenceActivity(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable as String,type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable
as String,manualId: freezed == manualId ? _self.manualId : manualId // ignore: cast_nullable_to_non_nullable as int,manualId: freezed == manualId ? _self.manualId : manualId // ignore: cast_nullable_to_non_nullable
as String?,title: freezed == title ? _self.title : title // ignore: cast_nullable_to_non_nullable as String?,title: freezed == title ? _self.title : title // ignore: cast_nullable_to_non_nullable
as String?,subtitle: freezed == subtitle ? _self.subtitle : subtitle // ignore: cast_nullable_to_non_nullable as String?,subtitle: freezed == subtitle ? _self.subtitle : subtitle // ignore: cast_nullable_to_non_nullable
as String?,caption: freezed == caption ? _self.caption : caption // ignore: cast_nullable_to_non_nullable as String?,caption: freezed == caption ? _self.caption : caption // ignore: cast_nullable_to_non_nullable

View File

@@ -27,20 +27,21 @@ Map<String, dynamic> _$SnNotableDayToJson(_SnNotableDay instance) =>
'holidays': instance.holidays, 'holidays': instance.holidays,
}; };
_SnActivity _$SnActivityFromJson(Map<String, dynamic> json) => _SnActivity( _SnTimelineEvent _$SnTimelineEventFromJson(Map<String, dynamic> json) =>
id: json['id'] as String, _SnTimelineEvent(
type: json['type'] as String, id: json['id'] as String,
resourceIdentifier: json['resource_identifier'] as String, type: json['type'] as String,
data: json['data'], resourceIdentifier: json['resource_identifier'] as String,
createdAt: DateTime.parse(json['created_at'] as String), data: json['data'],
updatedAt: DateTime.parse(json['updated_at'] as String), createdAt: DateTime.parse(json['created_at'] as String),
deletedAt: updatedAt: DateTime.parse(json['updated_at'] as String),
json['deleted_at'] == null deletedAt:
? null json['deleted_at'] == null
: DateTime.parse(json['deleted_at'] as String), ? null
); : DateTime.parse(json['deleted_at'] as String),
);
Map<String, dynamic> _$SnActivityToJson(_SnActivity instance) => Map<String, dynamic> _$SnTimelineEventToJson(_SnTimelineEvent instance) =>
<String, dynamic>{ <String, dynamic>{
'id': instance.id, 'id': instance.id,
'type': instance.type, 'type': instance.type,
@@ -125,7 +126,7 @@ Map<String, dynamic> _$SnEventCalendarEntryToJson(
_SnPresenceActivity _$SnPresenceActivityFromJson(Map<String, dynamic> json) => _SnPresenceActivity _$SnPresenceActivityFromJson(Map<String, dynamic> json) =>
_SnPresenceActivity( _SnPresenceActivity(
id: json['id'] as String, id: json['id'] as String,
type: json['type'] as String, type: (json['type'] as num).toInt(),
manualId: json['manual_id'] as String?, manualId: json['manual_id'] as String?,
title: json['title'] as String?, title: json['title'] as String?,
subtitle: json['subtitle'] as String?, subtitle: json['subtitle'] as String?,

View File

@@ -3,7 +3,6 @@ import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/account.dart';
import 'package:island/models/activity.dart'; import 'package:island/models/activity.dart';
import 'package:island/pods/network.dart'; import 'package:island/pods/network.dart';
import 'package:island/talker.dart'; import 'package:island/talker.dart';
@@ -14,11 +13,11 @@ import 'package:shelf/shelf_io.dart' as shelf_io;
import 'package:shelf_web_socket/shelf_web_socket.dart'; import 'package:shelf_web_socket/shelf_web_socket.dart';
import 'package:web_socket_channel/web_socket_channel.dart'; import 'package:web_socket_channel/web_socket_channel.dart';
part 'activity_rpc.g.dart';
// Conditional imports for IPC server - use web stubs on web platform // Conditional imports for IPC server - use web stubs on web platform
import 'ipc_server.dart' if (dart.library.html) 'ipc_server.web.dart'; import 'ipc_server.dart' if (dart.library.html) 'ipc_server.web.dart';
part 'activity_rpc.g.dart';
const String kRpcLogPrefix = 'arRPC.websocket'; const String kRpcLogPrefix = 'arRPC.websocket';
const String kRpcIpcLogPrefix = 'arRPC.ipc'; const String kRpcIpcLogPrefix = 'arRPC.ipc';
@@ -125,7 +124,7 @@ class ActivityRpcServer {
talker.log('[$kRpcLogPrefix] IPC server error: $e'); talker.log('[$kRpcLogPrefix] IPC server error: $e');
} }
} else { } else {
talker.log('IPC server disabled on macOS or web in production mode'); talker.log('IPC server disabled on macOS or web');
} }
} }
@@ -326,6 +325,8 @@ class ServerStateNotifier extends StateNotifier<ServerState> {
ServerStateNotifier(this.server) ServerStateNotifier(this.server)
: super(ServerState(status: 'Server not started')); : super(ServerState(status: 'Server not started'));
String? get currentActivityManualId => state.currentActivityManualId;
Future<void> start() async { Future<void> start() async {
if (!kIsWeb && !Platform.isAndroid && !Platform.isIOS) { if (!kIsWeb && !Platform.isAndroid && !Platform.isIOS) {
try { try {
@@ -354,114 +355,107 @@ class ServerStateNotifier extends StateNotifier<ServerState> {
} }
} }
const kPresenseActivityLease = 5;
// Providers // Providers
final rpcServerStateProvider = final rpcServerStateProvider = StateNotifierProvider<
StateNotifierProvider<ServerStateNotifier, ServerState>((ref) { ServerStateNotifier,
final server = ActivityRpcServer({}); ServerState
final notifier = ServerStateNotifier(server); >((ref) {
server.updateHandlers({ final server = ActivityRpcServer({});
'connection': (socket) { final notifier = ServerStateNotifier(server);
final clientId = server.updateHandlers({
socket is _WsSocketWrapper 'connection': (socket) {
? socket.clientId final clientId =
: (socket as IpcSocketWrapper).clientId; socket is _WsSocketWrapper
notifier.updateStatus('Client connected (ID: $clientId)'); ? socket.clientId
socket.send({ : (socket as IpcSocketWrapper).clientId;
'cmd': 'DISPATCH', notifier.updateStatus('Client connected (ID: $clientId)');
'data': { socket.send({
'v': 1, 'cmd': 'DISPATCH',
'config': { 'data': {
'cdn_host': 'fake.cdn', 'v': 1,
'api_endpoint': '//fake.api', 'config': {
'environment': 'dev', 'cdn_host': 'fake.cdn',
}, 'api_endpoint': '//fake.api',
'user': { 'environment': 'dev',
'id': 'fake_user_id', },
'username': 'FakeUser', 'user': {
'discriminator': '0001', 'id': 'fake_user_id',
'avatar': null, 'username': 'FakeUser',
'bot': false, 'discriminator': '0001',
}, 'avatar': null,
}, 'bot': false,
'evt': 'READY', },
'nonce': '12345',
});
},
'message': (socket, dynamic data) async {
if (data['cmd'] == 'SET_ACTIVITY') {
notifier.addActivity(
'Activity: ${data['args']['activity']['details'] ?? ''}',
);
final label = data['args']['activity']['details'] ?? '';
final appId = socket.clientId;
final meta = data['args']['activity'];
try {
final apiClient = ref.watch(apiClientProvider);
final currentId = notifier.state.currentActivityManualId;
final isUpdate = currentId == appId;
final activityData = {
'type': 'Gaming',
'manualId': appId,
'title': label,
'meta': meta,
'leaseMinutes': 30,
};
if (isUpdate) {
await apiClient.put(
'/pass/activities',
queryParameters: {'manual_id': appId},
data: {'leaseMinutes': 30},
);
} else {
await apiClient.post('/pass/activities', data: activityData);
notifier.setCurrentActivityManualId(appId);
}
final now = DateTime.now();
final status = SnAccountStatus(
id: 'local_$appId',
attitude: 0,
isOnline: true,
isInvisible: false,
isNotDisturb: false,
isCustomized: true,
label: label,
meta: meta,
clearedAt: null,
accountId: 'me',
createdAt: now,
updatedAt: now,
deletedAt: null,
);
ref.read(currentAccountStatusProvider.notifier).setStatus(status);
} catch (e) {
talker.log('Failed to set remote activity status: $e');
}
socket.send({
'cmd': 'SET_ACTIVITY',
'data': data['args']['activity'],
'evt': null,
'nonce': data['nonce'],
});
}
},
'close': (socket) async {
notifier.updateStatus('Client disconnected');
final appId = socket.clientId;
try {
final apiClient = ref.watch(apiClientProvider);
await apiClient.delete(
'/pass/activities',
queryParameters: {'manual_id': appId},
);
notifier.setCurrentActivityManualId(null);
ref.read(currentAccountStatusProvider.notifier).clearStatus();
} catch (e) {
talker.log('Failed to unset remote activity status: $e');
}
}, },
'evt': 'READY',
'nonce': '12345',
}); });
return notifier; },
}); 'message': (socket, dynamic data) async {
if (data['cmd'] == 'SET_ACTIVITY') {
final activity = data['args']['activity'];
notifier.addActivity('Activity: ${activity['details'] ?? 'Untitled'}');
final appId = activity['application_id'] ?? socket.clientId;
// https://discord.com/developers/docs/topics/rpc#setactivity-set-activity-argument-structure
final type = switch (activity['type']) {
0 => 1, // Discord Playing -> Playing
2 => 2, // Discord Music -> Listening
3 => 2, // Discord Watching -> Listening
_ => 1, // Discord Competing (or null) -> Playing
};
try {
final apiClient = ref.watch(apiClientProvider);
final currentId = notifier.currentActivityManualId;
final isUpdate = currentId == appId;
final activityData = {
'type': type,
'manual_id': appId,
'title': activity['name'],
'subtitle': activity['details'],
'caption': activity['state'],
'meta': activity,
'lease_minutes': kPresenseActivityLease,
};
if (isUpdate) {
await apiClient.put(
'/pass/activities',
queryParameters: {'manualId': appId},
data: {'lease_minutes': kPresenseActivityLease},
);
} else {
await apiClient.post('/pass/activities', data: activityData);
notifier.setCurrentActivityManualId(appId);
}
} catch (e) {
talker.log('Failed to set remote activity status: $e');
}
socket.send({
'cmd': 'SET_ACTIVITY',
'data': data['args']['activity'],
'evt': null,
'nonce': data['nonce'],
});
}
},
'close': (socket) async {
notifier.updateStatus('Client disconnected');
final appId = socket.clientId;
try {
final apiClient = ref.watch(apiClientProvider);
await apiClient.delete(
'/pass/activities',
queryParameters: {'manualId': appId},
);
notifier.setCurrentActivityManualId(null);
ref.read(currentAccountStatusProvider.notifier).clearStatus();
} catch (e) {
talker.log('Failed to unset remote activity status: $e');
}
},
});
return notifier;
});
final rpcServerProvider = Provider<ActivityRpcServer>((ref) { final rpcServerProvider = Provider<ActivityRpcServer>((ref) {
final notifier = ref.watch(rpcServerStateProvider.notifier); final notifier = ref.watch(rpcServerStateProvider.notifier);
@@ -474,7 +468,7 @@ Future<List<SnPresenceActivity>> presenceActivities(
String uname, String uname,
) async { ) async {
final apiClient = ref.watch(apiClientProvider); final apiClient = ref.watch(apiClientProvider);
final response = await apiClient.get('/pass/accounts/$uname/activities'); final response = await apiClient.get('/pass/activities/$uname');
final data = response.data as List<dynamic>; final data = response.data as List<dynamic>;
return data.map((json) => SnPresenceActivity.fromJson(json)).toList(); return data.map((json) => SnPresenceActivity.fromJson(json)).toList();
} }

View File

@@ -0,0 +1,157 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'activity_rpc.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$presenceActivitiesHash() =>
r'dcea3cad01b4010c0087f5281413d83a754c2a17';
/// 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 [presenceActivities].
@ProviderFor(presenceActivities)
const presenceActivitiesProvider = PresenceActivitiesFamily();
/// See also [presenceActivities].
class PresenceActivitiesFamily
extends Family<AsyncValue<List<SnPresenceActivity>>> {
/// See also [presenceActivities].
const PresenceActivitiesFamily();
/// See also [presenceActivities].
PresenceActivitiesProvider call(String uname) {
return PresenceActivitiesProvider(uname);
}
@override
PresenceActivitiesProvider getProviderOverride(
covariant PresenceActivitiesProvider 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'presenceActivitiesProvider';
}
/// See also [presenceActivities].
class PresenceActivitiesProvider
extends AutoDisposeFutureProvider<List<SnPresenceActivity>> {
/// See also [presenceActivities].
PresenceActivitiesProvider(String uname)
: this._internal(
(ref) => presenceActivities(ref as PresenceActivitiesRef, uname),
from: presenceActivitiesProvider,
name: r'presenceActivitiesProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product')
? null
: _$presenceActivitiesHash,
dependencies: PresenceActivitiesFamily._dependencies,
allTransitiveDependencies:
PresenceActivitiesFamily._allTransitiveDependencies,
uname: uname,
);
PresenceActivitiesProvider._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<List<SnPresenceActivity>> Function(PresenceActivitiesRef provider)
create,
) {
return ProviderOverride(
origin: this,
override: PresenceActivitiesProvider._internal(
(ref) => create(ref as PresenceActivitiesRef),
from: from,
name: null,
dependencies: null,
allTransitiveDependencies: null,
debugGetCreateSourceHash: null,
uname: uname,
),
);
}
@override
AutoDisposeFutureProviderElement<List<SnPresenceActivity>> createElement() {
return _PresenceActivitiesProviderElement(this);
}
@override
bool operator ==(Object other) {
return other is PresenceActivitiesProvider && 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 PresenceActivitiesRef
on AutoDisposeFutureProviderRef<List<SnPresenceActivity>> {
/// The parameter `uname` of this provider.
String get uname;
}
class _PresenceActivitiesProviderElement
extends AutoDisposeFutureProviderElement<List<SnPresenceActivity>>
with PresenceActivitiesRef {
_PresenceActivitiesProviderElement(super.provider);
@override
String get uname => (origin as PresenceActivitiesProvider).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

View File

@@ -1013,7 +1013,7 @@ class AccountProfileScreen extends HookConsumerWidget {
SliverToBoxAdapter( SliverToBoxAdapter(
child: ActivityPresenceWidget( child: ActivityPresenceWidget(
uname: name, uname: name,
).padding(horizontal: 4), ).padding(horizontal: 8),
), ),
], ],
), ),

View File

@@ -634,7 +634,7 @@ class _DiscoveryActivityItem extends StatelessWidget {
} }
class _ActivityListView extends HookConsumerWidget { class _ActivityListView extends HookConsumerWidget {
final CursorPagingData<SnActivity> data; final CursorPagingData<SnTimelineEvent> data;
final int widgetCount; final int widgetCount;
final Widget endItemView; final Widget endItemView;
final ActivityListNotifier activitiesNotifier; final ActivityListNotifier activitiesNotifier;
@@ -697,13 +697,13 @@ class _ActivityListView extends HookConsumerWidget {
@riverpod @riverpod
class ActivityListNotifier extends _$ActivityListNotifier class ActivityListNotifier extends _$ActivityListNotifier
with CursorPagingNotifierMixin<SnActivity> { with CursorPagingNotifierMixin<SnTimelineEvent> {
@override @override
Future<CursorPagingData<SnActivity>> build(String? filter) => Future<CursorPagingData<SnTimelineEvent>> build(String? filter) =>
fetch(cursor: null); fetch(cursor: null);
@override @override
Future<CursorPagingData<SnActivity>> fetch({required String? cursor}) async { Future<CursorPagingData<SnTimelineEvent>> fetch({required String? cursor}) async {
final client = ref.read(apiClientProvider); final client = ref.read(apiClientProvider);
final take = 20; final take = 20;
@@ -720,9 +720,9 @@ class ActivityListNotifier extends _$ActivityListNotifier
queryParameters: queryParameters, queryParameters: queryParameters,
); );
final List<SnActivity> items = final List<SnTimelineEvent> items =
(response.data as List) (response.data as List)
.map((e) => SnActivity.fromJson(e as Map<String, dynamic>)) .map((e) => SnTimelineEvent.fromJson(e as Map<String, dynamic>))
.toList(); .toList();
final hasMore = (items.firstOrNull?.type ?? 'empty') != 'empty'; final hasMore = (items.firstOrNull?.type ?? 'empty') != 'empty';
@@ -742,7 +742,7 @@ class ActivityListNotifier extends _$ActivityListNotifier
); );
} }
void updateOne(int index, SnActivity activity) { void updateOne(int index, SnTimelineEvent activity) {
final currentState = state.valueOrNull; final currentState = state.valueOrNull;
if (currentState == null) return; if (currentState == null) return;

View File

@@ -7,7 +7,7 @@ part of 'explore.dart';
// ************************************************************************** // **************************************************************************
String _$activityListNotifierHash() => String _$activityListNotifierHash() =>
r'167021cada54da7c8d8437eef1ffb387a92ea2e3'; r'a3ad3242f08139bef14a2f0fab6591ce8b3cb9f0';
/// Copied from Dart SDK /// Copied from Dart SDK
class _SystemHash { class _SystemHash {
@@ -31,10 +31,11 @@ class _SystemHash {
} }
abstract class _$ActivityListNotifier abstract class _$ActivityListNotifier
extends BuildlessAutoDisposeAsyncNotifier<CursorPagingData<SnActivity>> { extends
BuildlessAutoDisposeAsyncNotifier<CursorPagingData<SnTimelineEvent>> {
late final String? filter; late final String? filter;
FutureOr<CursorPagingData<SnActivity>> build(String? filter); FutureOr<CursorPagingData<SnTimelineEvent>> build(String? filter);
} }
/// See also [ActivityListNotifier]. /// See also [ActivityListNotifier].
@@ -43,7 +44,7 @@ const activityListNotifierProvider = ActivityListNotifierFamily();
/// See also [ActivityListNotifier]. /// See also [ActivityListNotifier].
class ActivityListNotifierFamily class ActivityListNotifierFamily
extends Family<AsyncValue<CursorPagingData<SnActivity>>> { extends Family<AsyncValue<CursorPagingData<SnTimelineEvent>>> {
/// See also [ActivityListNotifier]. /// See also [ActivityListNotifier].
const ActivityListNotifierFamily(); const ActivityListNotifierFamily();
@@ -79,7 +80,7 @@ class ActivityListNotifierProvider
extends extends
AutoDisposeAsyncNotifierProviderImpl< AutoDisposeAsyncNotifierProviderImpl<
ActivityListNotifier, ActivityListNotifier,
CursorPagingData<SnActivity> CursorPagingData<SnTimelineEvent>
> { > {
/// See also [ActivityListNotifier]. /// See also [ActivityListNotifier].
ActivityListNotifierProvider(String? filter) ActivityListNotifierProvider(String? filter)
@@ -110,7 +111,7 @@ class ActivityListNotifierProvider
final String? filter; final String? filter;
@override @override
FutureOr<CursorPagingData<SnActivity>> runNotifierBuild( FutureOr<CursorPagingData<SnTimelineEvent>> runNotifierBuild(
covariant ActivityListNotifier notifier, covariant ActivityListNotifier notifier,
) { ) {
return notifier.build(filter); return notifier.build(filter);
@@ -135,7 +136,7 @@ class ActivityListNotifierProvider
@override @override
AutoDisposeAsyncNotifierProviderElement< AutoDisposeAsyncNotifierProviderElement<
ActivityListNotifier, ActivityListNotifier,
CursorPagingData<SnActivity> CursorPagingData<SnTimelineEvent>
> >
createElement() { createElement() {
return _ActivityListNotifierProviderElement(this); return _ActivityListNotifierProviderElement(this);
@@ -158,7 +159,7 @@ class ActivityListNotifierProvider
@Deprecated('Will be removed in 3.0. Use Ref instead') @Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element // ignore: unused_element
mixin ActivityListNotifierRef mixin ActivityListNotifierRef
on AutoDisposeAsyncNotifierProviderRef<CursorPagingData<SnActivity>> { on AutoDisposeAsyncNotifierProviderRef<CursorPagingData<SnTimelineEvent>> {
/// The parameter `filter` of this provider. /// The parameter `filter` of this provider.
String? get filter; String? get filter;
} }
@@ -167,7 +168,7 @@ class _ActivityListNotifierProviderElement
extends extends
AutoDisposeAsyncNotifierProviderElement< AutoDisposeAsyncNotifierProviderElement<
ActivityListNotifier, ActivityListNotifier,
CursorPagingData<SnActivity> CursorPagingData<SnTimelineEvent>
> >
with ActivityListNotifierRef { with ActivityListNotifierRef {
_ActivityListNotifierProviderElement(super.provider); _ActivityListNotifierProviderElement(super.provider);

View File

@@ -1,7 +1,16 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/activity.dart';
import 'package:island/pods/activity/activity_rpc.dart'; import 'package:island/pods/activity/activity_rpc.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:styled_widget/styled_widget.dart';
const kPresenseActivityTypes = [
'unknown',
'presenceTypeGaming',
'presenceTypeMusic',
'presenceTypeWorkout',
];
class ActivityPresenceWidget extends ConsumerWidget { class ActivityPresenceWidget extends ConsumerWidget {
final String uname; final String uname;
@@ -13,48 +22,64 @@ class ActivityPresenceWidget extends ConsumerWidget {
final activitiesAsync = ref.watch(presenceActivitiesProvider(uname)); final activitiesAsync = ref.watch(presenceActivitiesProvider(uname));
return activitiesAsync.when( return activitiesAsync.when(
data: (activities) => _buildActivitiesList(activities), data:
(activities) => Card(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 8,
children: [
Text(
'activities',
).tr().bold().padding(horizontal: 8, vertical: 4),
if (activities.isEmpty)
Row(children: [
const Icon(Symbols.inbox),
Text('dataEmpty').tr()
],).opacity(0.75),
...activities.map(
(activity) => Card(
elevation: 0,
shape: RoundedRectangleBorder(
side: BorderSide(color: Colors.grey.shade300, width: 1),
borderRadius: BorderRadius.circular(8),
),
margin: EdgeInsets.zero,
child: ListTile(
title: Text(
(activity.title?.isEmpty ?? true)
? 'Untitled Activity'
: activity.title!,
),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(kPresenseActivityTypes[activity.type]).tr(),
StreamBuilder(
stream: Stream.periodic(const Duration(seconds: 1)),
builder: (context, snapshot) {
final duration = DateTime.now().difference(activity.createdAt);
final hours = duration.inHours.toString().padLeft(2, '0');
final minutes = (duration.inMinutes % 60).toString().padLeft(2, '0');
final seconds = (duration.inSeconds % 60).toString().padLeft(2, '0');
return Text('$hours:$minutes:$seconds').textColor(Colors.green);
},
),
if (activity.subtitle?.isNotEmpty ?? false)
Text(activity.subtitle!),
if (activity.caption?.isNotEmpty ?? false)
Text(activity.caption!),
],
),
),
),
),
],
).padding(all: 8),
),
loading: () => const Center(child: CircularProgressIndicator()), loading: () => const Center(child: CircularProgressIndicator()),
error: error:
(error, stack) => (error, stack) =>
Center(child: Text('Error loading activities: $error')), Center(child: Text('Error loading activities: $error')),
); );
} }
Widget _buildActivitiesList(List<SnPresenceActivity> activities) {
if (activities.isEmpty) {
return const Center(child: Text('No active activities'));
}
return ListView.builder(
itemCount: activities.length,
itemBuilder: (context, index) {
final activity = activities[index];
return Card(
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: ListTile(
title: Text(activity.title ?? 'Untitled Activity'),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Type: ${activity.type}'),
if (activity.subtitle != null) Text(activity.subtitle!),
if (activity.caption != null) Text(activity.caption!),
Text(
'Expires: ${activity.leaseExpiresAt.toLocal().toString()}',
style: const TextStyle(fontSize: 12),
),
],
),
trailing: IconButton(
icon: const Icon(Icons.delete),
onPressed: () {
// TODO: Implement delete functionality
},
),
),
);
},
);
}
} }

View File

@@ -296,7 +296,7 @@ class CheckInWidget extends HookConsumerWidget {
} }
class CheckInActivityWidget extends StatelessWidget { class CheckInActivityWidget extends StatelessWidget {
final SnActivity item; final SnTimelineEvent item;
const CheckInActivityWidget({super.key, required this.item}); const CheckInActivityWidget({super.key, required this.item});
@override @override