Add followers count widget

This commit is contained in:
Thomas Ricouard 2024-05-17 14:22:00 +02:00
parent 1578896b3e
commit f40aeb9cac
5 changed files with 137 additions and 0 deletions

View file

@ -89,6 +89,9 @@
9F7788F02BE78E77004E6BEF /* Timeline in Frameworks */ = {isa = PBXBuildFile; productRef = 9F7788EF2BE78E77004E6BEF /* Timeline */; };
9F7D93942980063100EE6B7A /* AppAccount in Frameworks */ = {isa = PBXBuildFile; productRef = 9F7D93932980063100EE6B7A /* AppAccount */; };
9F7D939A29805DBD00EE6B7A /* AccountSettingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F7D939929805DBD00EE6B7A /* AccountSettingView.swift */; };
9F8B92122BF77DBE003D37A2 /* AccountWidgetConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F8B920E2BF77DB4003D37A2 /* AccountWidgetConfiguration.swift */; };
9F8B92132BF77DBE003D37A2 /* AccountWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F8B92102BF77DBB003D37A2 /* AccountWidget.swift */; };
9F8B92162BF77F0B003D37A2 /* AccountWidgetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F8B92142BF77F05003D37A2 /* AccountWidgetView.swift */; };
9FA6FD6229C04A8800E2312C /* TranslationSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FA6FD6129C04A8800E2312C /* TranslationSettingsView.swift */; };
9FAD858B29743F7400496AB1 /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FAD858A29743F7400496AB1 /* ShareViewController.swift */; };
9FAD858E29743F7400496AB1 /* (null) in Resources */ = {isa = PBXBuildFile; };
@ -275,6 +278,9 @@
9F7788EC2BE78D75004E6BEF /* TimelineFilterEntity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineFilterEntity.swift; sourceTree = "<group>"; };
9F7D939529800B0300EE6B7A /* IceCubesApp-release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "IceCubesApp-release.xcconfig"; sourceTree = "<group>"; };
9F7D939929805DBD00EE6B7A /* AccountSettingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountSettingView.swift; sourceTree = "<group>"; };
9F8B920E2BF77DB4003D37A2 /* AccountWidgetConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountWidgetConfiguration.swift; sourceTree = "<group>"; };
9F8B92102BF77DBB003D37A2 /* AccountWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountWidget.swift; sourceTree = "<group>"; };
9F8B92142BF77F05003D37A2 /* AccountWidgetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountWidgetView.swift; sourceTree = "<group>"; };
9FA6FD6129C04A8800E2312C /* TranslationSettingsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TranslationSettingsView.swift; sourceTree = "<group>"; };
9FAD858829743F7400496AB1 /* IceCubesShareExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = IceCubesShareExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
9FAD858A29743F7400496AB1 /* ShareViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareViewController.swift; sourceTree = "<group>"; };
@ -497,6 +503,7 @@
9F7788CA2BE652B1004E6BEF /* IceCubesAppWidgetsExtension */ = {
isa = PBXGroup;
children = (
9F8B920D2BF77DA8003D37A2 /* AccountWidget */,
9F5BE6202BF48FB20074387E /* ListsWidget */,
9FF2FB6B2BE8AE78001560CE /* MentionWidget */,
9FF2FB642BE7F7FA001560CE /* Shared */,
@ -510,6 +517,16 @@
path = IceCubesAppWidgetsExtension;
sourceTree = "<group>";
};
9F8B920D2BF77DA8003D37A2 /* AccountWidget */ = {
isa = PBXGroup;
children = (
9F8B920E2BF77DB4003D37A2 /* AccountWidgetConfiguration.swift */,
9F8B92102BF77DBB003D37A2 /* AccountWidget.swift */,
9F8B92142BF77F05003D37A2 /* AccountWidgetView.swift */,
);
path = AccountWidget;
sourceTree = "<group>";
};
9FA0D2AC29921C1F008A143B /* Embeds */ = {
isa = PBXGroup;
children = (
@ -1011,6 +1028,9 @@
9F5BE6272BF492CF0074387E /* ListEntity.swift in Sources */,
9FF2FB622BE7F5D5001560CE /* HashtagPostsWidget.swift in Sources */,
9FF2FB712BE8AEA0001560CE /* MentionWidget.swift in Sources */,
9F8B92162BF77F0B003D37A2 /* AccountWidgetView.swift in Sources */,
9F8B92122BF77DBE003D37A2 /* AccountWidgetConfiguration.swift in Sources */,
9F8B92132BF77DBE003D37A2 /* AccountWidget.swift in Sources */,
9F7788EA2BE65585004E6BEF /* AppAccountEntity.swift in Sources */,
9FF2FB6A2BE7F84E001560CE /* SharedUtils.swift in Sources */,
9F7788CE2BE652B1004E6BEF /* LatestPostsWidget.swift in Sources */,

View file

@ -0,0 +1,64 @@
import DesignSystem
import Models
import Network
import SwiftUI
import Timeline
import WidgetKit
struct AccountWidgetProvider: AppIntentTimelineProvider {
func placeholder(in _: Context) -> AccountWidgetEntry {
.init(date: Date(), account: .placeholder(), avatar: nil)
}
func snapshot(for configuration: AccountWidgetConfiguration, in context: Context) async -> AccountWidgetEntry {
let account = await fetchAccount(configuration: configuration)
return .init(date: Date(), account: account, avatar: nil)
}
func timeline(for configuration: AccountWidgetConfiguration, in context: Context) async -> Timeline<AccountWidgetEntry> {
let account = await fetchAccount(configuration: configuration)
let images = try? await loadImages(urls: [account.avatar])
return .init(entries: [.init(date: Date(), account: account, avatar: images?.first?.value)],
policy: .atEnd)
}
private func fetchAccount(configuration: AccountWidgetConfiguration) async -> Account {
let client = Client(server: configuration.account.account.server,
oauthToken: configuration.account.account.oauthToken)
do {
let account: Account = try await client.get(endpoint: Accounts.verifyCredentials)
return account
} catch {
return .placeholder()
}
}
}
struct AccountWidgetEntry: TimelineEntry {
let date: Date
let account: Account
let avatar: UIImage?
}
struct AccountWidget: Widget {
let kind: String = "AccountWidget"
var body: some WidgetConfiguration {
AppIntentConfiguration(kind: kind,
intent: AccountWidgetConfiguration.self,
provider: AccountWidgetProvider())
{ entry in
AccountWidgetView(entry: entry)
.containerBackground(Color("WidgetBackground").gradient, for: .widget)
}
.configurationDisplayName("Account")
.description("Show information about your Mastodon account")
.supportedFamilies([.systemSmall])
}
}
#Preview(as: .systemSmall) {
AccountWidget()
} timeline: {
AccountWidgetEntry(date: Date(), account: .placeholder(), avatar: nil)
}

View file

@ -0,0 +1,18 @@
import AppIntents
import WidgetKit
struct AccountWidgetConfiguration: WidgetConfigurationIntent {
static let title: LocalizedStringResource = "Configuration"
static let description = IntentDescription("Choose the account for this widget")
@Parameter(title: "Account")
var account: AppAccountEntity
}
extension AccountWidgetConfiguration {
static var previewAccount: AccountWidgetConfiguration {
let intent = AccountWidgetConfiguration()
intent.account = .init(account: .init(server: "Test", accountName: "Test account"))
return intent
}
}

View file

@ -0,0 +1,34 @@
import DesignSystem
import Models
import Network
import SwiftUI
import Timeline
import WidgetKit
struct AccountWidgetView: View {
var entry: AccountWidgetProvider.Entry
@Environment(\.widgetFamily) var family
@Environment(\.redactionReasons) var redacted
var body: some View {
VStack(alignment: .center, spacing: 4) {
if let avatar = entry.avatar {
Image(uiImage: avatar)
.resizable()
.frame(width: 64, height: 64)
.clipShape(Circle())
Text("\(entry.account.followersCount ?? 0)")
.font(.title)
.fontDesign(.rounded)
.fontWeight(.bold)
.monospacedDigit()
Text("Followers")
.font(.headline)
.foregroundStyle(.secondary)
}
}
.frame(maxWidth: .infinity)
}
}

View file

@ -8,5 +8,6 @@ struct IceCubesAppWidgetsExtensionBundle: WidgetBundle {
HashtagPostsWidget()
ListsPostWidget()
MentionsWidget()
AccountWidget()
}
}