2023-02-06 11:39:40 +01:00
|
|
|
// Copyright © 2023 Mastodon gGmbH. All rights reserved.
|
|
|
|
|
|
|
|
import SwiftUI
|
|
|
|
import WidgetKit
|
|
|
|
import MastodonSDK
|
|
|
|
import MastodonAsset
|
|
|
|
import MastodonUI
|
2023-02-06 12:13:29 +01:00
|
|
|
import MastodonLocalization
|
2023-02-06 11:39:40 +01:00
|
|
|
|
|
|
|
struct LatestFollowersWidgetView: View {
|
|
|
|
private let dateFormatter: DateFormatter = {
|
|
|
|
let formatter = DateFormatter()
|
|
|
|
formatter.dateStyle = .none
|
|
|
|
formatter.timeStyle = .short
|
|
|
|
return formatter
|
|
|
|
}()
|
|
|
|
|
|
|
|
@Environment(\.widgetFamily) var family
|
|
|
|
|
|
|
|
var entry: LatestFollowersWidgetProvider.Entry
|
|
|
|
|
|
|
|
var body: some View {
|
|
|
|
if let accounts = entry.accounts {
|
|
|
|
switch family {
|
|
|
|
case .systemSmall:
|
|
|
|
viewForSmallWidget(accounts, lastUpdate: entry.date)
|
|
|
|
case .systemMedium:
|
|
|
|
viewForMediumWidget(accounts, lastUpdate: entry.date)
|
|
|
|
default:
|
2023-02-06 12:13:29 +01:00
|
|
|
Text(L10n.Widget.Common.unsupportedWidgetFamily)
|
2023-02-06 11:39:40 +01:00
|
|
|
}
|
|
|
|
} else {
|
2023-02-06 12:13:29 +01:00
|
|
|
Text(L10n.Widget.Common.userNotLoggedIn)
|
2023-02-06 11:39:40 +01:00
|
|
|
.multilineTextAlignment(.center)
|
|
|
|
.font(.caption)
|
|
|
|
.padding(.all, 20)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private func viewForSmallWidget(_ accounts: [LatestFollowersEntryAccountable], lastUpdate: Date) -> some View {
|
2023-02-06 11:46:46 +01:00
|
|
|
VStack(alignment: .leading) {
|
2023-02-06 12:13:29 +01:00
|
|
|
Text(L10n.Widget.LatestFollowers.title)
|
2023-02-06 11:46:46 +01:00
|
|
|
.font(.system(size: UIFontMetrics.default.scaledValue(for: 16)))
|
|
|
|
|
2023-02-06 11:39:40 +01:00
|
|
|
ForEach(accounts, id: \.acct) { account in
|
|
|
|
HStack {
|
2023-03-01 14:58:12 +01:00
|
|
|
Image(uiImage: account.avatarImage)
|
|
|
|
.resizable()
|
|
|
|
.frame(width: 32, height: 32)
|
|
|
|
.cornerRadius(5)
|
2023-02-06 11:39:40 +01:00
|
|
|
VStack(alignment: .leading) {
|
|
|
|
|
2023-02-06 11:46:46 +01:00
|
|
|
Text(account.displayNameWithFallback)
|
|
|
|
.font(.footnote.bold())
|
|
|
|
.lineLimit(1)
|
|
|
|
.truncationMode(.tail)
|
|
|
|
|
|
|
|
Text("@\(account.acct)")
|
|
|
|
.font(.caption2)
|
|
|
|
.foregroundColor(.secondary)
|
|
|
|
.lineLimit(1)
|
|
|
|
.truncationMode(.tail)
|
|
|
|
|
2023-02-06 11:39:40 +01:00
|
|
|
}
|
|
|
|
Spacer()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Spacer()
|
2023-02-06 12:13:29 +01:00
|
|
|
Text(L10n.Widget.LatestFollowers.lastUpdate(dateFormatter.string(from: lastUpdate)))
|
2023-02-06 11:46:46 +01:00
|
|
|
.font(.caption2)
|
|
|
|
.foregroundColor(.secondary)
|
2023-02-06 11:39:40 +01:00
|
|
|
}
|
2023-02-06 11:46:46 +01:00
|
|
|
.padding(.horizontal, 20)
|
2023-02-06 11:39:40 +01:00
|
|
|
.padding(.vertical, 16)
|
|
|
|
}
|
|
|
|
|
|
|
|
private func viewForMediumWidget(_ accounts: [LatestFollowersEntryAccountable], lastUpdate: Date) -> some View {
|
|
|
|
VStack(alignment: .leading) {
|
|
|
|
HStack {
|
2023-02-06 12:13:29 +01:00
|
|
|
Text(L10n.Widget.LatestFollowers.title)
|
2023-02-06 11:39:40 +01:00
|
|
|
.font(.system(size: UIFontMetrics.default.scaledValue(for: 16)))
|
|
|
|
Spacer()
|
|
|
|
Image("BrandIconColored")
|
|
|
|
}
|
|
|
|
|
|
|
|
ForEach(accounts, id: \.acct) { account in
|
|
|
|
HStack {
|
2023-03-01 14:58:12 +01:00
|
|
|
Image(uiImage: account.avatarImage)
|
|
|
|
.resizable()
|
|
|
|
.frame(width: 32, height: 32)
|
|
|
|
.cornerRadius(5)
|
2023-02-06 11:39:40 +01:00
|
|
|
VStack(alignment: .leading) {
|
|
|
|
|
|
|
|
HStack {
|
|
|
|
Text(account.displayNameWithFallback)
|
|
|
|
.font(.footnote.bold())
|
|
|
|
.lineLimit(1)
|
|
|
|
.truncationMode(.tail)
|
|
|
|
|
|
|
|
Text("@\(account.acct)")
|
|
|
|
.font(.caption2)
|
|
|
|
.foregroundColor(.secondary)
|
|
|
|
.lineLimit(1)
|
|
|
|
.truncationMode(.tail)
|
|
|
|
}
|
|
|
|
|
2023-02-06 11:58:23 +01:00
|
|
|
Text(account.noteWithoutHtmlTags ?? "")
|
2023-02-06 11:39:40 +01:00
|
|
|
.font(.caption)
|
|
|
|
.lineLimit(1)
|
|
|
|
.truncationMode(.tail)
|
|
|
|
}
|
|
|
|
Spacer()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Spacer()
|
2023-02-06 12:13:29 +01:00
|
|
|
Text(L10n.Widget.LatestFollowers.lastUpdate(dateFormatter.string(from: lastUpdate)))
|
2023-02-06 11:39:40 +01:00
|
|
|
.font(.caption2)
|
|
|
|
.foregroundColor(.secondary)
|
|
|
|
}
|
|
|
|
.padding(.horizontal, 20)
|
|
|
|
.padding(.vertical, 16)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-07 09:38:27 +01:00
|
|
|
/// This code is used to strip HTML tags from the bio description as the widgets currently dont support
|
|
|
|
/// rich text rendering due to the lack of SwiftUI-only components for this purpose.
|
|
|
|
/// todo: Implement rich text rendering for bio description and remove this code
|
|
|
|
/// https://github.com/mastodon/mastodon-ios/issues/921
|
2023-02-06 11:39:40 +01:00
|
|
|
private extension LatestFollowersEntryAccountable {
|
|
|
|
var noteWithoutHtmlTags: String? {
|
|
|
|
do {
|
|
|
|
let regex = "<[^>]+>"
|
|
|
|
let expr = try NSRegularExpression(pattern: regex, options: NSRegularExpression.Options.caseInsensitive)
|
|
|
|
let result = expr.stringByReplacingMatches(in: note, options: [], range: NSMakeRange(0, note.count), withTemplate: "")
|
|
|
|
return result
|
|
|
|
} catch {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|