Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion app/Shared/Localization/zh-Hans.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -66,15 +66,21 @@
"Share" = "分享";
"Notifications" = "通知";
"Notifications (%lld)" = "通知 (%lld)";
"Followed Activity" = "关注动态";
"No Activity" = "没有动态";
"View activity from the users you follow." = "查看你关注的用户的动态。";
"%lld Unread" = "%lld个未读";
"All Read" = "全部已读";
"Read" = "已读";
"Unread" = "未读";
"Mark All as Read" = "全部标记为已读";
"replied to your post" = "回复了你的回复";
"replied to your topic" = "回复了你的话题";
"posted a topic" = "发表了话题";
"posted a reply" = "发表了回复";
"favorited" = "收藏了内容";
"Your post" = "你的回复";
"received 10 more votes" = "收获了 10 个评价";
"received 10 more votes" = "获得了 10 个评价";
"Discard" = "舍弃";
"Discard the draft?" = "舍弃草稿?";
"Save the draft by swiping down to dismiss the editor." = "你可以通过下滑关闭编辑器来保存草稿。";
Expand Down
32 changes: 32 additions & 0 deletions app/Shared/Protos/Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,34 @@ extension Notification.TypeEnum {
}
}

extension Activity.TypeEnum {
var icon: String {
switch self {
case .postTopic:
"square.and.pencil"
case .postReply:
"arrowshape.turn.up.left"
case .favor:
"bookmark"
case .unknown, .UNRECOGNIZED:
""
}
}

var description: LocalizedStringKey {
switch self {
case .postTopic:
"posted a topic"
case .postReply:
"posted a reply"
case .favor:
"favorited"
case .unknown, .UNRECOGNIZED:
""
}
}
}

extension Post {
var idWithAlterInfo: String {
id.debugDescription + alterInfo
Expand Down Expand Up @@ -256,6 +284,10 @@ extension UserName {
}

extension Topic {
var read: Bool {
hasRepliesNumLastVisit
}

var authorNameDisplay: String {
let new = authorName.display

Expand Down
7 changes: 7 additions & 0 deletions app/Shared/Utilities/PlusFeature.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ enum PlusFeature: CaseIterable {
case customAppearance
case multiAccount
case topicHistory
case followedActivity
case multiFavorite
case authorOnly
case jump
Expand All @@ -33,6 +34,8 @@ enum PlusFeature: CaseIterable {
"Short Messages"
case .topicHistory:
"History"
case .followedActivity:
"Followed Activity"
case .authorOnly:
"Author Only"
case .jump:
Expand Down Expand Up @@ -64,6 +67,8 @@ enum PlusFeature: CaseIterable {
"Send and receive short messages with other users."
case .topicHistory:
"View your footprint of topics you have explored."
case .followedActivity:
"View activity from the users you follow."
case .authorOnly:
"Check posts and replies from a specific author in a topic."
case .jump:
Expand Down Expand Up @@ -95,6 +100,8 @@ enum PlusFeature: CaseIterable {
"message"
case .topicHistory:
"clock"
case .followedActivity:
"sparkles"
Copy link

Copilot AI Jan 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The icon for the followedActivity feature is set to "sparkles" here, but the UI in UserMenuView.swift and FollowedActivityListView.swift uses "dot.radiowaves.left.and.right". Consider using the same icon in both places for consistency, or verify that the difference is intentional.

Suggested change
"sparkles"
"dot.radiowaves.left.and.right"

Copilot uses AI. Check for mistakes.
case .authorOnly:
"person.fill"
case .jump:
Expand Down
63 changes: 63 additions & 0 deletions app/Shared/Views/FollowedActivityListView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
//
// FollowedActivityListView.swift
// MNGA
//
// Created by Bugen Zhao on 2025/12/18.
//

import Foundation
import SwiftUI
import SwiftUIX

struct FollowedActivityListView: View {
typealias DataSource = PagingDataSource<ActivityListResponse, Activity>

@StateObject var dataSource: DataSource

static func build() -> Self {
let dataSource = DataSource(
buildRequest: { page in
.activityList(.with { $0.page = UInt32(page) })
},
onResponse: { response in
(response.activities, Int(response.pages))
},
id: \.id
)

return Self(dataSource: dataSource)
}

@ViewBuilder
func destination(for activity: Activity) -> some View {
if activity.postID.pid == "0" || activity.postID.pid.isEmpty {
TopicDetailsView.build(topic: activity.topic)
} else {
TopicDetailsView.build(topic: activity.topic, onlyPost: (id: activity.postID, atPage: nil))
}
}

var body: some View {
Group {
if dataSource.notLoaded {
ProgressView()
.onAppear { dataSource.initialLoad() }
} else if dataSource.items.isEmpty {
ContentUnavailableView("No Activity", systemImage: "dot.radiowaves.left.and.right")
} else {
List {
ForEach(dataSource.items, id: \.id) { activity in
CrossStackNavigationLinkHack(id: activity.id, destination: {
destination(for: activity)
}) {
FollowedActivityRowView(activity: activity)
}.onAppear { dataSource.loadMoreIfNeeded(currentItem: activity) }
}
}
}
}
.navigationTitle("Followed Activity")
.mayGroupedListStyle()
.refreshable(dataSource: dataSource)
}
}
58 changes: 58 additions & 0 deletions app/Shared/Views/FollowedActivityRowView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
//
// FollowedActivityRowView.swift
// MNGA
//
// Created by Bugen Zhao on 2025/12/18.
//

import Foundation
import SwiftUI

struct FollowedActivityRowView: View {
let activity: Activity

var body: some View {
VStack(alignment: .leading, spacing: 8) {
HStack {
Image(systemName: activity.type.icon)
TopicSubjectView(topic: activity.topic)
}
.topicSubjectDimmed(activity.topic.read)
.foregroundColor(activity.topic.read ? .secondary : .primary)

HStack {
HStack(alignment: .center) {
Image(systemName: "person")
Text(activity.actor.nameDisplayCompat)
}
Text(activity.type.description)
Spacer()
DateTimeTextView.build(timestamp: activity.timestamp, switchable: false)
}
.foregroundColor(.secondary)
.font(.footnote)
}
.padding(.vertical, 2)
}
}

struct FollowedActivityRowView_Previews: PreviewProvider {
static var previews: some View {
FollowedActivityRowView(activity: .with {
$0.id = "1"
$0.type = .postTopic
$0.actor = .with { $0.name.normal = "Bugen" }
$0.topic = .with {
$0.id = "123"
$0.subject = .with { $0.content = "Test Topic" }
}
$0.postID = .with {
$0.tid = "123"
$0.pid = "0"
}
$0.timestamp = UInt64(Date().timeIntervalSince1970 - 60)
})
.background(.primary.opacity(0.1))
.padding()
}
}
4 changes: 3 additions & 1 deletion app/Shared/Views/NotificationRowView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ struct NotificationRowView: View {
default:
TopicSubjectView(topic: noti.asTopic, showIndicators: false)
}
}.foregroundColor(noti.read ? .secondary : .primary)
}
.topicSubjectDimmed(noti.read)
.foregroundColor(noti.read ? .secondary : .primary)

DateTimeFooterView(timestamp: noti.timestamp, switchable: false) {
switch noti.type {
Expand Down
2 changes: 1 addition & 1 deletion app/Shared/Views/TopicPostRowView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ struct TopicPostRowView: View {

var body: some View {
VStack(alignment: .leading, spacing: 8) {
TopicSubjectView(topic: topic, lineLimit: 2)
TopicSubjectView(topic: topic)

QuoteView(fullWidth: false) {
PostContentView(lightPost: cleanPost, initialInQuote: true)
Expand Down
4 changes: 2 additions & 2 deletions app/Shared/Views/TopicRowView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,13 @@ struct TopicRowView: View {
}

var shouldDim: Bool {
dimmedSubject && !topic.id.isMNGAMockID && topic.hasRepliesNumLastVisit
dimmedSubject && !topic.id.isMNGAMockID && topic.read
}

@ViewBuilder
var subject: some View {
BlockedView(content: BlockWordsStorage.content(for: topic), revealOnTap: false) {
TopicSubjectView(topic: topic, lineLimit: 2, showIndicators: showIndicators)
TopicSubjectView(topic: topic, showIndicators: showIndicators)
.topicSubjectDimmed(shouldDim)
}
}
Expand Down
2 changes: 1 addition & 1 deletion app/Shared/Views/TopicSubjectView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ struct TopicSubjectView: View {
let lineLimit: Int?
let showIndicators: Bool

init(topic: Topic, lineLimit: Int? = nil, showIndicators: Bool = false) {
init(topic: Topic, lineLimit: Int? = 2, showIndicators: Bool = false) {
self.topic = topic
self.lineLimit = lineLimit
self.showIndicators = showIndicators
Expand Down
3 changes: 3 additions & 0 deletions app/Shared/Views/UserMenuView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,9 @@ struct UserMenuView: View {
NavigationLink(destination: FavoriteTopicListView()) {
Label("Favorite Topics", systemImage: "bookmark")
}
PlusCheckNavigationLink(destination: FollowedActivityListView.build(), feature: .followedActivity) {
Label("Followed Activity", systemImage: "dot.radiowaves.left.and.right")
}
}
PlusCheckNavigationLink(destination: TopicHistoryListView.build(), feature: .topicHistory) {
Label("History", systemImage: "clock")
Expand Down
Loading
Loading