feat(Widget): Improve FollowerCount Widget placeholder/preview
This commit is contained in:
parent
6a0bd94bf4
commit
0d69e5ea4d
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "missing.png",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
Binary file not shown.
After Width: | Height: | Size: 2.8 KiB |
|
@ -7,10 +7,13 @@ import MastodonSDK
|
||||||
|
|
||||||
struct FollowersProvider: IntentTimelineProvider {
|
struct FollowersProvider: IntentTimelineProvider {
|
||||||
func placeholder(in context: Context) -> FollowersEntry {
|
func placeholder(in context: Context) -> FollowersEntry {
|
||||||
.empty(with: FollowersCountIntent())
|
.placeholder
|
||||||
}
|
}
|
||||||
|
|
||||||
func getSnapshot(for configuration: FollowersCountIntent, in context: Context, completion: @escaping (FollowersEntry) -> ()) {
|
func getSnapshot(for configuration: FollowersCountIntent, in context: Context, completion: @escaping (FollowersEntry) -> ()) {
|
||||||
|
guard !context.isPreview else {
|
||||||
|
return completion(.placeholder)
|
||||||
|
}
|
||||||
loadCurrentEntry(for: configuration, in: context, completion: completion)
|
loadCurrentEntry(for: configuration, in: context, completion: completion)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,12 +26,28 @@ struct FollowersProvider: IntentTimelineProvider {
|
||||||
|
|
||||||
struct FollowersEntry: TimelineEntry {
|
struct FollowersEntry: TimelineEntry {
|
||||||
let date: Date
|
let date: Date
|
||||||
let account: Mastodon.Entity.Account?
|
let account: FollowersEntryAccountable?
|
||||||
let avatarImage: UIImage?
|
|
||||||
let configuration: FollowersCountIntent
|
let configuration: FollowersCountIntent
|
||||||
|
|
||||||
static func empty(with configuration: FollowersCountIntent) -> Self {
|
static var placeholder: Self {
|
||||||
FollowersEntry(date: .now, account: nil, avatarImage: nil, configuration: configuration)
|
FollowersEntry(
|
||||||
|
date: .now,
|
||||||
|
account: FollowersEntryAccount(
|
||||||
|
followersCount: 99_900,
|
||||||
|
displayNameWithFallback: "Mastodon",
|
||||||
|
acct: "mastodon",
|
||||||
|
avatarImage: UIImage(named: "missingAvatar")!
|
||||||
|
),
|
||||||
|
configuration: FollowersCountIntent()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
static var unconfigured: Self {
|
||||||
|
FollowersEntry(
|
||||||
|
date: .now,
|
||||||
|
account: nil,
|
||||||
|
configuration: FollowersCountIntent()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,7 +58,7 @@ struct FollowersWidgetExtensionEntryView : View {
|
||||||
if let account = entry.account {
|
if let account = entry.account {
|
||||||
HStack {
|
HStack {
|
||||||
VStack(alignment: .leading, spacing: 0) {
|
VStack(alignment: .leading, spacing: 0) {
|
||||||
if let avatarImage = entry.avatarImage {
|
if let avatarImage = account.avatarImage {
|
||||||
Image(uiImage: avatarImage)
|
Image(uiImage: avatarImage)
|
||||||
.resizable()
|
.resizable()
|
||||||
.frame(width: 50, height: 50)
|
.frame(width: 50, height: 50)
|
||||||
|
@ -52,7 +71,7 @@ struct FollowersWidgetExtensionEntryView : View {
|
||||||
.lineLimit(1)
|
.lineLimit(1)
|
||||||
.truncationMode(.tail)
|
.truncationMode(.tail)
|
||||||
|
|
||||||
Text("\(account.displayNameWithFallback)")
|
Text(account.displayNameWithFallback)
|
||||||
.font(.system(size: 13))
|
.font(.system(size: 13))
|
||||||
.lineLimit(1)
|
.lineLimit(1)
|
||||||
.truncationMode(.tail)
|
.truncationMode(.tail)
|
||||||
|
@ -69,6 +88,9 @@ struct FollowersWidgetExtensionEntryView : View {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Text("Please use the Widget settings to select an Account.")
|
Text("Please use the Widget settings to select an Account.")
|
||||||
|
.multilineTextAlignment(.center)
|
||||||
|
.font(.caption)
|
||||||
|
.padding(.all, 20)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -80,6 +102,7 @@ struct FollowersWidgetExtension: Widget {
|
||||||
}
|
}
|
||||||
.configurationDisplayName("Followers")
|
.configurationDisplayName("Followers")
|
||||||
.description("Show number of followers.")
|
.description("Show number of followers.")
|
||||||
|
.supportedFamilies([.systemSmall])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,7 +111,6 @@ struct WidgetExtension_Previews: PreviewProvider {
|
||||||
FollowersWidgetExtensionEntryView(entry: FollowersEntry(
|
FollowersWidgetExtensionEntryView(entry: FollowersEntry(
|
||||||
date: Date(),
|
date: Date(),
|
||||||
account: nil,
|
account: nil,
|
||||||
avatarImage: nil,
|
|
||||||
configuration: FollowersCountIntent())
|
configuration: FollowersCountIntent())
|
||||||
)
|
)
|
||||||
.previewContext(WidgetPreviewContext(family: .systemSmall))
|
.previewContext(WidgetPreviewContext(family: .systemSmall))
|
||||||
|
@ -105,34 +127,49 @@ private extension FollowersProvider {
|
||||||
.first,
|
.first,
|
||||||
let account = configuration.account
|
let account = configuration.account
|
||||||
else {
|
else {
|
||||||
return completion(.empty(with: configuration))
|
return completion(.unconfigured)
|
||||||
}
|
}
|
||||||
let resultingAccount = try await WidgetExtension.appContext
|
let resultingAccount = try await WidgetExtension.appContext
|
||||||
.apiService
|
.apiService
|
||||||
.search(query: .init(q: account, type: .accounts), authenticationBox: authBox)
|
.search(query: .init(q: account, type: .accounts), authenticationBox: authBox)
|
||||||
.value
|
.value
|
||||||
.accounts
|
.accounts
|
||||||
.first
|
.first!
|
||||||
|
|
||||||
let image: UIImage? = try await {
|
let imageData = try await URLSession.shared.data(from: resultingAccount.avatarImageURLWithFallback(domain: authBox.domain)).0
|
||||||
guard
|
|
||||||
let account = resultingAccount
|
|
||||||
else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
let imageData = try await URLSession.shared.data(from: account.avatarImageURLWithFallback(domain: authBox.domain)).0
|
|
||||||
|
|
||||||
return UIImage(data: imageData)
|
|
||||||
}()
|
|
||||||
|
|
||||||
let entry = FollowersEntry(
|
let entry = FollowersEntry(
|
||||||
date: Date(),
|
date: Date(),
|
||||||
account: resultingAccount,
|
account: FollowersEntryAccount.from(
|
||||||
avatarImage: image,
|
mastodonAccount: resultingAccount,
|
||||||
|
avatarImage: UIImage(data: imageData) ?? UIImage(named: "missingAvatar")!
|
||||||
|
),
|
||||||
configuration: configuration
|
configuration: configuration
|
||||||
)
|
)
|
||||||
completion(entry)
|
completion(entry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protocol FollowersEntryAccountable {
|
||||||
|
var followersCount: Int { get }
|
||||||
|
var displayNameWithFallback: String { get }
|
||||||
|
var acct: String { get }
|
||||||
|
var avatarImage: UIImage { get }
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FollowersEntryAccount: FollowersEntryAccountable {
|
||||||
|
let followersCount: Int
|
||||||
|
let displayNameWithFallback: String
|
||||||
|
let acct: String
|
||||||
|
let avatarImage: UIImage
|
||||||
|
|
||||||
|
static func from(mastodonAccount: Mastodon.Entity.Account, avatarImage: UIImage) -> Self {
|
||||||
|
FollowersEntryAccount(
|
||||||
|
followersCount: mastodonAccount.followersCount,
|
||||||
|
displayNameWithFallback: mastodonAccount.displayNameWithFallback,
|
||||||
|
acct: mastodonAccount.acct,
|
||||||
|
avatarImage: avatarImage
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue