feat: import Chinese-localized Buzz source snapshot
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>
This commit is contained in:
2026-08-13 18:34:25 +08:00
parent 61c3fa1df9
commit 9dfa06ffee
3785 changed files with 1085458 additions and 2 deletions
+137
View File
@@ -0,0 +1,137 @@
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();
}