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
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:
@@ -0,0 +1,239 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
const Object _sentinel = Object();
|
||||
|
||||
/// Shown when a private-channel add is refused, so a missing Invite action
|
||||
/// reads as a rule rather than a bug.
|
||||
const privateChannelAddDeniedMessage =
|
||||
'Only channel owners and admins can add people to a private channel.';
|
||||
|
||||
@immutable
|
||||
class Channel {
|
||||
final String id;
|
||||
final String name;
|
||||
final String channelType; // "stream", "forum", "dm"
|
||||
final String visibility; // "open", "private"
|
||||
final String description;
|
||||
final String? topic;
|
||||
final String? purpose;
|
||||
final String createdBy;
|
||||
final DateTime createdAt;
|
||||
final int memberCount;
|
||||
final DateTime? lastMessageAt;
|
||||
final DateTime? archivedAt;
|
||||
final List<String> participants;
|
||||
final List<String> participantPubkeys;
|
||||
final bool isMember;
|
||||
final int? ttlSeconds;
|
||||
final DateTime? ttlDeadline;
|
||||
|
||||
const Channel({
|
||||
required this.id,
|
||||
required this.name,
|
||||
required this.channelType,
|
||||
required this.visibility,
|
||||
required this.description,
|
||||
required this.createdBy,
|
||||
required this.createdAt,
|
||||
required this.memberCount,
|
||||
this.topic,
|
||||
this.purpose,
|
||||
this.lastMessageAt,
|
||||
this.archivedAt,
|
||||
this.participants = const [],
|
||||
this.participantPubkeys = const [],
|
||||
this.isMember = false,
|
||||
this.ttlSeconds,
|
||||
this.ttlDeadline,
|
||||
});
|
||||
|
||||
factory Channel.fromJson(Map<String, dynamic> json) => Channel(
|
||||
id: json['id'] as String,
|
||||
name: json['name'] as String,
|
||||
channelType: json['channel_type'] as String,
|
||||
visibility: json['visibility'] as String,
|
||||
description: (json['description'] as String?) ?? '',
|
||||
topic: json['topic'] as String?,
|
||||
purpose: json['purpose'] as String?,
|
||||
createdBy: json['created_by'] as String,
|
||||
createdAt: DateTime.parse(json['created_at'] as String),
|
||||
memberCount: json['member_count'] as int,
|
||||
lastMessageAt: json['last_message_at'] != null
|
||||
? DateTime.parse(json['last_message_at'] as String)
|
||||
: null,
|
||||
archivedAt: json['archived_at'] != null
|
||||
? DateTime.parse(json['archived_at'] as String)
|
||||
: null,
|
||||
participants: (json['participants'] as List<dynamic>? ?? const [])
|
||||
.cast<String>(),
|
||||
participantPubkeys:
|
||||
(json['participant_pubkeys'] as List<dynamic>? ?? const [])
|
||||
.cast<String>(),
|
||||
isMember: json['is_member'] as bool? ?? false,
|
||||
ttlSeconds: json['ttl_seconds'] as int?,
|
||||
ttlDeadline: json['ttl_deadline'] != null
|
||||
? DateTime.parse(json['ttl_deadline'] as String)
|
||||
: null,
|
||||
);
|
||||
|
||||
bool get isEphemeral => ttlSeconds != null || ttlDeadline != null;
|
||||
|
||||
bool get isStream => channelType == 'stream';
|
||||
bool get isForum => channelType == 'forum';
|
||||
bool get isDm => channelType == 'dm';
|
||||
bool get isPrivate => visibility == 'private';
|
||||
|
||||
/// Whether [selfRole] may add *another* identity here, mirroring the relay's
|
||||
/// kind:9000 authority (`validate_admin_event` + `add_member`): DMs never,
|
||||
/// open channels always, private channels owners/admins only. An unknown
|
||||
/// visibility fails closed — the relay is the authority.
|
||||
bool canAddMembers(String? selfRole) {
|
||||
if (isDm) return false;
|
||||
if (visibility == 'open') return true;
|
||||
return selfRole == 'owner' || selfRole == 'admin';
|
||||
}
|
||||
|
||||
bool get isArchived => archivedAt != null;
|
||||
|
||||
String displayLabel({String? currentPubkey}) {
|
||||
if (!isDm || participants.isEmpty) {
|
||||
return name;
|
||||
}
|
||||
|
||||
final normalizedCurrent = currentPubkey?.toLowerCase();
|
||||
final labels = <String>[];
|
||||
for (var index = 0; index < participants.length; index++) {
|
||||
final participantPubkey = index < participantPubkeys.length
|
||||
? participantPubkeys[index].toLowerCase()
|
||||
: null;
|
||||
if (participantPubkey != null && participantPubkey == normalizedCurrent) {
|
||||
continue;
|
||||
}
|
||||
labels.add(participants[index]);
|
||||
}
|
||||
|
||||
if (labels.isEmpty) {
|
||||
labels.addAll(participants);
|
||||
}
|
||||
|
||||
return labels.join(', ');
|
||||
}
|
||||
|
||||
Channel mergeDetails(ChannelDetails details) => Channel(
|
||||
id: id,
|
||||
name: details.name,
|
||||
channelType: details.channelType,
|
||||
visibility: details.visibility,
|
||||
description: details.description,
|
||||
topic: details.topic,
|
||||
purpose: details.purpose,
|
||||
createdBy: details.createdBy,
|
||||
createdAt: details.createdAt,
|
||||
memberCount: memberCount,
|
||||
lastMessageAt: lastMessageAt,
|
||||
archivedAt: details.archivedAt,
|
||||
participants: participants,
|
||||
participantPubkeys: participantPubkeys,
|
||||
isMember: isMember,
|
||||
ttlSeconds: details.ttlSeconds,
|
||||
ttlDeadline: details.ttlDeadline,
|
||||
);
|
||||
|
||||
Channel copyWith({
|
||||
Object? lastMessageAt = _sentinel,
|
||||
Object? archivedAt = _sentinel,
|
||||
int? memberCount,
|
||||
bool? isMember,
|
||||
}) => Channel(
|
||||
id: id,
|
||||
name: name,
|
||||
channelType: channelType,
|
||||
visibility: visibility,
|
||||
description: description,
|
||||
topic: topic,
|
||||
purpose: purpose,
|
||||
createdBy: createdBy,
|
||||
createdAt: createdAt,
|
||||
memberCount: memberCount ?? this.memberCount,
|
||||
lastMessageAt: identical(lastMessageAt, _sentinel)
|
||||
? this.lastMessageAt
|
||||
: lastMessageAt as DateTime?,
|
||||
archivedAt: identical(archivedAt, _sentinel)
|
||||
? this.archivedAt
|
||||
: archivedAt as DateTime?,
|
||||
participants: participants,
|
||||
participantPubkeys: participantPubkeys,
|
||||
isMember: isMember ?? this.isMember,
|
||||
ttlSeconds: ttlSeconds,
|
||||
ttlDeadline: ttlDeadline,
|
||||
);
|
||||
}
|
||||
|
||||
@immutable
|
||||
class ChannelDetails {
|
||||
final String id;
|
||||
final String name;
|
||||
final String channelType;
|
||||
final String visibility;
|
||||
final String description;
|
||||
final String? topic;
|
||||
final String? purpose;
|
||||
final String createdBy;
|
||||
final DateTime createdAt;
|
||||
final int memberCount;
|
||||
final DateTime? archivedAt;
|
||||
final int? ttlSeconds;
|
||||
final DateTime? ttlDeadline;
|
||||
|
||||
const ChannelDetails({
|
||||
required this.id,
|
||||
required this.name,
|
||||
required this.channelType,
|
||||
required this.visibility,
|
||||
required this.description,
|
||||
required this.createdBy,
|
||||
required this.createdAt,
|
||||
required this.memberCount,
|
||||
this.topic,
|
||||
this.purpose,
|
||||
this.archivedAt,
|
||||
this.ttlSeconds,
|
||||
this.ttlDeadline,
|
||||
});
|
||||
|
||||
factory ChannelDetails.fromJson(Map<String, dynamic> json) => ChannelDetails(
|
||||
id: json['id'] as String,
|
||||
name: json['name'] as String,
|
||||
channelType: json['channel_type'] as String,
|
||||
visibility: json['visibility'] as String,
|
||||
description: (json['description'] as String?) ?? '',
|
||||
topic: json['topic'] as String?,
|
||||
purpose: json['purpose'] as String?,
|
||||
createdBy: json['created_by'] as String,
|
||||
createdAt: DateTime.parse(json['created_at'] as String),
|
||||
memberCount: json['member_count'] as int,
|
||||
archivedAt: json['archived_at'] != null
|
||||
? DateTime.parse(json['archived_at'] as String)
|
||||
: null,
|
||||
ttlSeconds: json['ttl_seconds'] as int?,
|
||||
ttlDeadline: json['ttl_deadline'] != null
|
||||
? DateTime.parse(json['ttl_deadline'] as String)
|
||||
: null,
|
||||
);
|
||||
|
||||
factory ChannelDetails.fromChannel(Channel channel) => ChannelDetails(
|
||||
id: channel.id,
|
||||
name: channel.name,
|
||||
channelType: channel.channelType,
|
||||
visibility: channel.visibility,
|
||||
description: channel.description,
|
||||
topic: channel.topic,
|
||||
purpose: channel.purpose,
|
||||
createdBy: channel.createdBy,
|
||||
createdAt: channel.createdAt,
|
||||
memberCount: channel.memberCount,
|
||||
archivedAt: channel.archivedAt,
|
||||
ttlSeconds: channel.ttlSeconds,
|
||||
ttlDeadline: channel.ttlDeadline,
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user