9dfa06ffee
Docker image / Build (linux/amd64) (push) Has been cancelled
Docker image / Build (linux/arm64) (push) Has been cancelled
Docker image / Merge release multi-arch manifest (push) Has been cancelled
Docker image / Merge debug multi-arch manifest (push) Has been cancelled
Docker image / Build public push gateway (linux/amd64) (push) Has been cancelled
Docker image / Build public push gateway (linux/arm64) (push) Has been cancelled
Docker image / Publish public push gateway image (push) Has been cancelled
Sprig image / Build (linux/amd64) (push) Has been cancelled
Sprig image / Build (linux/arm64) (push) Has been cancelled
Sprig image / Merge multi-arch manifest (push) Has been cancelled
Harbor Buzz Orchestra / Python tests and lint (push) Has been cancelled
CI / Detect Changed Paths (push) Has been cancelled
CI / Rust Lint (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Desktop Core (push) Has been cancelled
CI / Desktop Smoke E2E (1) (push) Has been cancelled
CI / Desktop Smoke E2E (2) (push) Has been cancelled
CI / Desktop Smoke E2E (3) (push) Has been cancelled
CI / Desktop Smoke E2E (4) (push) Has been cancelled
CI / Desktop (push) Has been cancelled
CI / Desktop E2E Relay (push) Has been cancelled
CI / Desktop E2E Integration (1/2) (push) Has been cancelled
CI / Desktop E2E Integration (2/2) (push) Has been cancelled
CI / Desktop E2E Integration (push) Has been cancelled
CI / Backend Integration (relay e2e) (push) Has been cancelled
CI / Relay E2E (push) Has been cancelled
CI / Web (push) Has been cancelled
CI / Mobile (push) Has been cancelled
CI / Security (push) Has been cancelled
CI / Dead Token Reference Guard (push) Has been cancelled
CI / Server Cross-Compile (aarch64-unknown-linux-musl) (push) Has been cancelled
CI / Server Cross-Compile (x86_64-unknown-linux-musl) (push) Has been cancelled
CI / Windows Rust (x86_64-pc-windows-msvc) (push) Has been cancelled
CI / Desktop Build (macOS) (push) Has been cancelled
helm chart / lint + unittest + render matrix (push) Has been cancelled
helm chart / install on kind (gated) (push) Has been cancelled
helm chart / publish chart to GHCR (push) Has been cancelled
Mesh Lifecycle / Relay-Driven Mesh Lifecycle Smoke (push) Has been cancelled
Sprig / Build (aarch64-unknown-linux-musl) (push) Has been cancelled
Sprig / Build (x86_64-unknown-linux-musl) (push) Has been cancelled
Sprig / Publish rolling release (push) Has been cancelled
Sprig / Publish tagged release (push) Has been cancelled
Signed-off-by: cls_宁波本机 <908705107@qq.com>
138 lines
3.8 KiB
Dart
138 lines
3.8 KiB
Dart
import 'package:flutter/foundation.dart';
|
|
|
|
@immutable
|
|
class FeedItem {
|
|
final String id;
|
|
final int kind;
|
|
final String pubkey;
|
|
final String content;
|
|
final int createdAt;
|
|
final String? channelId;
|
|
final String channelName;
|
|
final List<List<String>> tags;
|
|
final String
|
|
category; // "mention", "needs_action", "activity", "agent_activity"
|
|
|
|
const FeedItem({
|
|
required this.id,
|
|
required this.kind,
|
|
required this.pubkey,
|
|
required this.content,
|
|
required this.createdAt,
|
|
required this.channelId,
|
|
required this.channelName,
|
|
required this.tags,
|
|
required this.category,
|
|
});
|
|
|
|
factory FeedItem.fromJson(Map<String, dynamic> json) => FeedItem(
|
|
id: json['id'] as String,
|
|
kind: json['kind'] as int,
|
|
pubkey: json['pubkey'] as String,
|
|
content: json['content'] as String,
|
|
createdAt: json['created_at'] as int,
|
|
channelId: json['channel_id'] as String?,
|
|
channelName: (json['channel_name'] as String?) ?? '',
|
|
tags:
|
|
(json['tags'] as List<dynamic>?)
|
|
?.map((t) => (t as List<dynamic>).map((e) => e as String).toList())
|
|
.toList() ??
|
|
const [],
|
|
category: json['category'] as String,
|
|
);
|
|
|
|
/// The message whose thread directly contains this item, when this event is
|
|
/// a reply. For nested replies this is the direct parent, not the outer root.
|
|
String? get threadHeadId {
|
|
for (final tag in tags) {
|
|
if (tag.length >= 4 && tag[0] == 'e' && tag[3] == 'reply') {
|
|
return tag[1];
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/// Human-readable headline based on event kind and category.
|
|
String get headline {
|
|
switch (kind) {
|
|
case 45001:
|
|
return 'Forum post';
|
|
case 45003:
|
|
return 'Forum reply';
|
|
case 46010:
|
|
return 'Approval requested';
|
|
case 43001:
|
|
return 'Job requested';
|
|
case 43002:
|
|
return 'Job accepted';
|
|
case 43003:
|
|
return 'Progress update';
|
|
case 43004:
|
|
return 'Job result';
|
|
case 43005:
|
|
return 'Job cancelled';
|
|
case 43006:
|
|
return 'Job failed';
|
|
default:
|
|
if (category == 'mention') return 'Mention';
|
|
if (category == 'agent_activity') return 'Agent update';
|
|
return 'Channel update';
|
|
}
|
|
}
|
|
|
|
/// Trimmed content, with a fallback for empty events.
|
|
String get displayContent {
|
|
final trimmed = content.trim();
|
|
if (trimmed.isNotEmpty) return trimmed;
|
|
if (kind == 46010) return 'A workflow is waiting for approval.';
|
|
return 'No additional details.';
|
|
}
|
|
}
|
|
|
|
/// Parsed response from GET /api/feed.
|
|
@immutable
|
|
class HomeFeedResponse {
|
|
final List<FeedItem> mentions;
|
|
final List<FeedItem> needsAction;
|
|
final List<FeedItem> activity;
|
|
final List<FeedItem> agentActivity;
|
|
|
|
HomeFeedResponse({
|
|
required this.mentions,
|
|
required this.needsAction,
|
|
required this.activity,
|
|
required this.agentActivity,
|
|
});
|
|
|
|
factory HomeFeedResponse.fromJson(Map<String, dynamic> json) {
|
|
final feed = json['feed'] as Map<String, dynamic>;
|
|
return HomeFeedResponse(
|
|
mentions: _parseItems(feed['mentions']),
|
|
needsAction: _parseItems(feed['needs_action']),
|
|
activity: _parseItems(feed['activity']),
|
|
agentActivity: _parseItems(feed['agent_activity']),
|
|
);
|
|
}
|
|
|
|
/// All items merged into a single list, sorted newest-first.
|
|
late final List<FeedItem> all = () {
|
|
final items = [...mentions, ...needsAction, ...activity, ...agentActivity];
|
|
items.sort((a, b) => b.createdAt.compareTo(a.createdAt));
|
|
return List<FeedItem>.unmodifiable(items);
|
|
}();
|
|
|
|
bool get isEmpty =>
|
|
mentions.isEmpty &&
|
|
needsAction.isEmpty &&
|
|
activity.isEmpty &&
|
|
agentActivity.isEmpty;
|
|
}
|
|
|
|
List<FeedItem> _parseItems(dynamic json) {
|
|
if (json == null) return const [];
|
|
return (json as List<dynamic>)
|
|
.cast<Map<String, dynamic>>()
|
|
.map(FeedItem.fromJson)
|
|
.toList();
|
|
}
|