feat(Widget): Implement MultiFollowersCountWidget for single Column
This commit is contained in:
parent
e05a8602d5
commit
9eb26d4ed8
|
@ -53,6 +53,9 @@
|
|||
2A72813F297EC762004138C5 /* WidgetExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A72813E297EC762004138C5 /* WidgetExtension.swift */; };
|
||||
2A76F75C2930D94700B3388D /* HashtagTimelineHeaderViewActionButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A76F75B2930D94700B3388D /* HashtagTimelineHeaderViewActionButton.swift */; };
|
||||
2A82294F29262EE000D2A1F7 /* AppContext+NextAccount.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A82294E29262EE000D2A1F7 /* AppContext+NextAccount.swift */; };
|
||||
2A86A14629892944007F1062 /* MultiFollowersCountIntentHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A86A14529892944007F1062 /* MultiFollowersCountIntentHandler.swift */; };
|
||||
2A86A14929892B3A007F1062 /* MultiFollowersCountWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A86A14829892B3A007F1062 /* MultiFollowersCountWidget.swift */; };
|
||||
2A86A14B2989326E007F1062 /* MultiFollowersCountWidgetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A86A14A2989326E007F1062 /* MultiFollowersCountWidgetView.swift */; };
|
||||
2A90A157296EEE500026C155 /* MastodonSDKDynamic in Frameworks */ = {isa = PBXBuildFile; productRef = 2A90A156296EEE500026C155 /* MastodonSDKDynamic */; };
|
||||
2AB12E4629362F27006BC925 /* DataSourceFacade+Translate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AB12E4529362F27006BC925 /* DataSourceFacade+Translate.swift */; };
|
||||
2AE202AA297FE10B00F66E55 /* WidgetExtension.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = 2A72812C297EA9D7004138C5 /* WidgetExtension.intentdefinition */; };
|
||||
|
@ -642,6 +645,9 @@
|
|||
2A72813E297EC762004138C5 /* WidgetExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetExtension.swift; sourceTree = "<group>"; };
|
||||
2A76F75B2930D94700B3388D /* HashtagTimelineHeaderViewActionButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HashtagTimelineHeaderViewActionButton.swift; sourceTree = "<group>"; };
|
||||
2A82294E29262EE000D2A1F7 /* AppContext+NextAccount.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppContext+NextAccount.swift"; sourceTree = "<group>"; };
|
||||
2A86A14529892944007F1062 /* MultiFollowersCountIntentHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiFollowersCountIntentHandler.swift; sourceTree = "<group>"; };
|
||||
2A86A14829892B3A007F1062 /* MultiFollowersCountWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiFollowersCountWidget.swift; sourceTree = "<group>"; };
|
||||
2A86A14A2989326E007F1062 /* MultiFollowersCountWidgetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiFollowersCountWidgetView.swift; sourceTree = "<group>"; };
|
||||
2AB12E4529362F27006BC925 /* DataSourceFacade+Translate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DataSourceFacade+Translate.swift"; sourceTree = "<group>"; };
|
||||
2AE202A9297FDDF500F66E55 /* WidgetExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = WidgetExtension.entitlements; sourceTree = "<group>"; };
|
||||
2AE202AB297FE19100F66E55 /* FollowersCountIntentHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowersCountIntentHandler.swift; sourceTree = "<group>"; };
|
||||
|
@ -1440,7 +1446,6 @@
|
|||
2AE202A9297FDDF500F66E55 /* WidgetExtension.entitlements */,
|
||||
2A72813E297EC762004138C5 /* WidgetExtension.swift */,
|
||||
2A728126297EA9D7004138C5 /* WidgetExtensionBundle.swift */,
|
||||
2A33062C2987DBFA001D4C51 /* FollowersCountHistory.swift */,
|
||||
2A72812C297EA9D7004138C5 /* WidgetExtension.intentdefinition */,
|
||||
2A72812D297EA9D8004138C5 /* Assets.xcassets */,
|
||||
2A72812F297EA9D8004138C5 /* Info.plist */,
|
||||
|
@ -1452,6 +1457,7 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
2A86A14429892709007F1062 /* FollowersCount */,
|
||||
2A86A14729892B1B007F1062 /* MultiFollowersCount */,
|
||||
);
|
||||
path = Variants;
|
||||
sourceTree = "<group>";
|
||||
|
@ -1459,12 +1465,22 @@
|
|||
2A86A14429892709007F1062 /* FollowersCount */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
2A33062C2987DBFA001D4C51 /* FollowersCountHistory.swift */,
|
||||
2A72812A297EA9D7004138C5 /* FollowersCountWidget.swift */,
|
||||
2A33AB652982C4AF008A7FB1 /* FollowersCountWidgetView.swift */,
|
||||
);
|
||||
path = FollowersCount;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
2A86A14729892B1B007F1062 /* MultiFollowersCount */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
2A86A14829892B3A007F1062 /* MultiFollowersCountWidget.swift */,
|
||||
2A86A14A2989326E007F1062 /* MultiFollowersCountWidgetView.swift */,
|
||||
);
|
||||
path = MultiFollowersCount;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
2D152A8A25C295B8009AA50C /* Content */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -2291,6 +2307,7 @@
|
|||
children = (
|
||||
DBB8AB4526AECDE200F6D281 /* SendPostIntentHandler.swift */,
|
||||
2AE202AB297FE19100F66E55 /* FollowersCountIntentHandler.swift */,
|
||||
2A86A14529892944007F1062 /* MultiFollowersCountIntentHandler.swift */,
|
||||
);
|
||||
path = Handler;
|
||||
sourceTree = "<group>";
|
||||
|
@ -3485,10 +3502,12 @@
|
|||
2A33062D2987DBFA001D4C51 /* FollowersCountHistory.swift in Sources */,
|
||||
2A33063829880835001D4C51 /* LineChart.swift in Sources */,
|
||||
2A728130297EA9D8004138C5 /* WidgetExtension.intentdefinition in Sources */,
|
||||
2A86A14B2989326E007F1062 /* MultiFollowersCountWidgetView.swift in Sources */,
|
||||
2A72813F297EC762004138C5 /* WidgetExtension.swift in Sources */,
|
||||
2A33063A29880835001D4C51 /* LightChart.swift in Sources */,
|
||||
2A33063B29880835001D4C51 /* ChartType.swift in Sources */,
|
||||
2A33063629880835001D4C51 /* Math.swift in Sources */,
|
||||
2A86A14929892B3A007F1062 /* MultiFollowersCountWidget.swift in Sources */,
|
||||
2A33AB662982C4AF008A7FB1 /* FollowersCountWidgetView.swift in Sources */,
|
||||
2A728127297EA9D7004138C5 /* WidgetExtensionBundle.swift in Sources */,
|
||||
2A72812B297EA9D7004138C5 /* FollowersCountWidget.swift in Sources */,
|
||||
|
@ -3932,6 +3951,7 @@
|
|||
2AE202AC297FE19100F66E55 /* FollowersCountIntentHandler.swift in Sources */,
|
||||
DB64BA452851F23000ADF1B7 /* MastodonAuthentication+Fetch.swift in Sources */,
|
||||
DB64BA482851F29300ADF1B7 /* Account+Fetch.swift in Sources */,
|
||||
2A86A14629892944007F1062 /* MultiFollowersCountIntentHandler.swift in Sources */,
|
||||
DB8FABCA26AEC7B2008E5AF4 /* IntentHandler.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
|
|
@ -62,6 +62,7 @@
|
|||
<key>NSUserActivityTypes</key>
|
||||
<array>
|
||||
<string>FollowersCountIntent</string>
|
||||
<string>MultiFollowersCountSmallIntent</string>
|
||||
<string>SendPostIntent</string>
|
||||
</array>
|
||||
<key>UIApplicationSceneManifest</key>
|
||||
|
|
|
@ -30,13 +30,11 @@ class FollowersCountIntentHandler: INExtension, FollowersCountIntentHandling {
|
|||
.apiService
|
||||
.search(query: .init(q: searchTerm), authenticationBox: authenticationBox)
|
||||
|
||||
debugPrint(results.value.statuses)
|
||||
|
||||
return INObjectCollection(items: results.value.accounts.map { $0.acctWithDomain(localDomain: authenticationBox.domain) as NSString })
|
||||
}
|
||||
}
|
||||
|
||||
private extension Mastodon.Entity.Account {
|
||||
extension Mastodon.Entity.Account {
|
||||
func acctWithDomain(localDomain: String) -> String {
|
||||
guard acct.contains("@") else {
|
||||
return "\(acct)@\(localDomain)"
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
// Copyright © 2023 Mastodon gGmbH. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import Intents
|
||||
import MastodonCore
|
||||
import MastodonSDK
|
||||
import MastodonLocalization
|
||||
|
||||
class MultiFollowersCountIntentHandler: INExtension, MultiFollowersCountSmallIntentHandling {
|
||||
func provideAccountsOptionsCollection(for intent: MultiFollowersCountSmallIntent, searchTerm: String?) async throws -> INObjectCollection<NSString> {
|
||||
guard
|
||||
let searchTerm = searchTerm,
|
||||
let authenticationBox = WidgetExtension.appContext
|
||||
.authenticationService
|
||||
.mastodonAuthenticationBoxes
|
||||
.first
|
||||
else {
|
||||
return INObjectCollection(items: [])
|
||||
}
|
||||
|
||||
let results = try await WidgetExtension.appContext
|
||||
.apiService
|
||||
.search(query: .init(q: searchTerm), authenticationBox: authenticationBox)
|
||||
|
||||
return INObjectCollection(items: results.value.accounts.map { $0.acctWithDomain(localDomain: authenticationBox.domain) as NSString })
|
||||
}
|
||||
}
|
|
@ -31,6 +31,7 @@
|
|||
<key>IntentsSupported</key>
|
||||
<array>
|
||||
<string>FollowersCountIntent</string>
|
||||
<string>MultiFollowersCountSmallIntent</string>
|
||||
<string>SendPostIntent</string>
|
||||
</array>
|
||||
</dict>
|
||||
|
|
|
@ -17,6 +17,8 @@ class IntentHandler: INExtension {
|
|||
return SendPostIntentHandler()
|
||||
case is FollowersCountIntent:
|
||||
return FollowersCountIntentHandler()
|
||||
case is MultiFollowersCountSmallIntent:
|
||||
return MultiFollowersCountIntentHandler()
|
||||
default:
|
||||
return self
|
||||
}
|
||||
|
|
|
@ -0,0 +1,150 @@
|
|||
// Copyright © 2023 Mastodon gGmbH. All rights reserved.
|
||||
|
||||
import WidgetKit
|
||||
import SwiftUI
|
||||
import Intents
|
||||
import MastodonSDK
|
||||
|
||||
struct MultiFollowersCountWidgetProvider: IntentTimelineProvider {
|
||||
func placeholder(in context: Context) -> MultiFollowersCountEntry {
|
||||
.placeholder
|
||||
}
|
||||
|
||||
func getSnapshot(for configuration: MultiFollowersCountSmallIntent, in context: Context, completion: @escaping (MultiFollowersCountEntry) -> ()) {
|
||||
guard !context.isPreview else {
|
||||
return completion(.placeholder)
|
||||
}
|
||||
loadCurrentEntry(for: configuration, in: context, completion: completion)
|
||||
}
|
||||
|
||||
func getTimeline(for configuration: MultiFollowersCountSmallIntent, in context: Context, completion: @escaping (Timeline<MultiFollowersCountEntry>) -> ()) {
|
||||
loadCurrentEntry(for: configuration, in: context) { entry in
|
||||
completion(Timeline(entries: [entry], policy: .after(.now)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct MultiFollowersCountEntry: TimelineEntry {
|
||||
let date: Date
|
||||
let accounts: [FollowersEntryAccountable]?
|
||||
let configuration: MultiFollowersCountSmallIntent
|
||||
|
||||
static var placeholder: Self {
|
||||
MultiFollowersCountEntry(
|
||||
date: .now,
|
||||
accounts: [
|
||||
FollowersEntryAccount(
|
||||
followersCount: 99_900,
|
||||
displayNameWithFallback: "Mastodon",
|
||||
acct: "mastodon",
|
||||
avatarImage: UIImage(named: "missingAvatar")!,
|
||||
domain: "mastodon"
|
||||
)
|
||||
],
|
||||
configuration: MultiFollowersCountSmallIntent()
|
||||
)
|
||||
}
|
||||
|
||||
static var unconfigured: Self {
|
||||
MultiFollowersCountEntry(
|
||||
date: .now,
|
||||
accounts: [],
|
||||
configuration: MultiFollowersCountSmallIntent()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
struct MultiFollowersCountWidget: Widget {
|
||||
private var availableFamilies: [WidgetFamily] {
|
||||
return [.systemSmall]
|
||||
}
|
||||
|
||||
var body: some WidgetConfiguration {
|
||||
IntentConfiguration(kind: "Multiple followers", intent: MultiFollowersCountSmallIntent.self, provider: MultiFollowersCountWidgetProvider()) { entry in
|
||||
MultiFollowersCountWidgetView(entry: entry)
|
||||
}
|
||||
.configurationDisplayName("Multiple followers")
|
||||
.description("Show number of followers for multiple accounts.")
|
||||
.supportedFamilies(availableFamilies)
|
||||
}
|
||||
}
|
||||
|
||||
private extension MultiFollowersCountWidgetProvider {
|
||||
func loadCurrentEntry(for configuration: MultiFollowersCountSmallIntent, in context: Context, completion: @escaping (MultiFollowersCountEntry) -> Void) {
|
||||
Task {
|
||||
guard
|
||||
let authBox = WidgetExtension.appContext
|
||||
.authenticationService
|
||||
.mastodonAuthenticationBoxes
|
||||
.first
|
||||
else {
|
||||
return completion(.unconfigured)
|
||||
}
|
||||
|
||||
guard let desiredAccounts: [String] = {
|
||||
guard let account = configuration.accounts?.compactMap({ $0 }) else {
|
||||
if let acct = authBox.authenticationRecord.object(in: WidgetExtension.appContext.managedObjectContext)?.user.acct {
|
||||
return [acct]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return account
|
||||
}() else {
|
||||
return completion(.unconfigured)
|
||||
}
|
||||
|
||||
var accounts = [FollowersEntryAccountable]()
|
||||
|
||||
for desiredAccount in desiredAccounts {
|
||||
let resultingAccount = try await WidgetExtension.appContext
|
||||
.apiService
|
||||
.search(query: .init(q: desiredAccount, type: .accounts), authenticationBox: authBox)
|
||||
.value
|
||||
.accounts
|
||||
.first!
|
||||
|
||||
let imageData = try await URLSession.shared.data(from: resultingAccount.avatarImageURLWithFallback(domain: authBox.domain)).0
|
||||
|
||||
accounts.append(FollowersEntryAccount.from(
|
||||
mastodonAccount: resultingAccount,
|
||||
domain: authBox.domain,
|
||||
avatarImage: UIImage(data: imageData) ?? UIImage(named: "missingAvatar")!
|
||||
))
|
||||
}
|
||||
|
||||
let entry = MultiFollowersCountEntry(
|
||||
date: Date(),
|
||||
accounts: accounts,
|
||||
configuration: configuration
|
||||
)
|
||||
|
||||
completion(entry)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protocol MultiFollowersEntryAccountable {
|
||||
var followersCount: Int { get }
|
||||
var displayNameWithFallback: String { get }
|
||||
var acct: String { get }
|
||||
var avatarImage: UIImage { get }
|
||||
var domain: String { get }
|
||||
}
|
||||
|
||||
struct MultiFollowersEntryAccount: MultiFollowersEntryAccountable {
|
||||
let followersCount: Int
|
||||
let displayNameWithFallback: String
|
||||
let acct: String
|
||||
let avatarImage: UIImage
|
||||
let domain: String
|
||||
|
||||
static func from(mastodonAccount: Mastodon.Entity.Account, domain: String, avatarImage: UIImage) -> Self {
|
||||
MultiFollowersEntryAccount(
|
||||
followersCount: mastodonAccount.followersCount,
|
||||
displayNameWithFallback: mastodonAccount.displayNameWithFallback,
|
||||
acct: mastodonAccount.acct,
|
||||
avatarImage: avatarImage,
|
||||
domain: domain
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
// Copyright © 2023 Mastodon gGmbH. All rights reserved.
|
||||
|
||||
import SwiftUI
|
||||
import WidgetKit
|
||||
import MastodonAsset
|
||||
|
||||
struct MultiFollowersCountWidgetView: View {
|
||||
@Environment(\.widgetFamily) var family
|
||||
|
||||
var entry: MultiFollowersCountWidgetProvider.Entry
|
||||
|
||||
var body: some View {
|
||||
if let accounts = entry.accounts {
|
||||
switch family {
|
||||
case .systemSmall:
|
||||
viewForSmallWidgetNoChart(accounts)
|
||||
default:
|
||||
Text("Sorry but this Widget family is unsupported.")
|
||||
}
|
||||
} else {
|
||||
Text("Please open Mastodon to log in to an Account.")
|
||||
.multilineTextAlignment(.center)
|
||||
.font(.caption)
|
||||
.padding(.all, 20)
|
||||
}
|
||||
}
|
||||
|
||||
private func viewForSmallWidgetNoChart(_ accounts: [FollowersEntryAccountable]) -> some View {
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
ForEach(accounts, id: \.acct) { account in
|
||||
HStack {
|
||||
if let avatarImage = account.avatarImage {
|
||||
Image(uiImage: avatarImage)
|
||||
.resizable()
|
||||
.frame(width: 32, height: 32)
|
||||
.cornerRadius(5)
|
||||
}
|
||||
VStack(alignment: .leading) {
|
||||
Text(account.followersCount.asAbbreviatedCountString())
|
||||
.font(.title2)
|
||||
.lineLimit(1)
|
||||
.truncationMode(.tail)
|
||||
|
||||
Text("@\(account.acct)")
|
||||
.font(.caption2)
|
||||
.foregroundColor(.secondary)
|
||||
.lineLimit(1)
|
||||
.truncationMode(.tail)
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
.padding(.leading, 20)
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
.padding(.vertical, 16)
|
||||
}
|
||||
}
|
|
@ -184,9 +184,9 @@
|
|||
<key>INIntentIneligibleForSuggestions</key>
|
||||
<true/>
|
||||
<key>INIntentLastParameterTag</key>
|
||||
<integer>5</integer>
|
||||
<integer>6</integer>
|
||||
<key>INIntentName</key>
|
||||
<string>MultiFollowCountSmall</string>
|
||||
<string>MultiFollowersCountSmall</string>
|
||||
<key>INIntentParameters</key>
|
||||
<array>
|
||||
<dict>
|
||||
|
@ -251,17 +251,24 @@
|
|||
<integer>1</integer>
|
||||
<key>INIntentParameterFixedSizeArray</key>
|
||||
<integer>1</integer>
|
||||
<key>INIntentParameterMetadata</key>
|
||||
<dict>
|
||||
<key>INIntentParameterMetadataCapitalization</key>
|
||||
<string>Sentences</string>
|
||||
<key>INIntentParameterMetadataDefaultValueID</key>
|
||||
<string>SNXOJo</string>
|
||||
</dict>
|
||||
<key>INIntentParameterName</key>
|
||||
<string>accounts</string>
|
||||
<key>INIntentParameterObjectType</key>
|
||||
<string>MultiFollowAccountsSmall</string>
|
||||
<key>INIntentParameterObjectTypeNamespace</key>
|
||||
<string>88xZPY</string>
|
||||
<key>INIntentParameterPromptDialogs</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>INIntentParameterPromptDialogCustom</key>
|
||||
<true/>
|
||||
<key>INIntentParameterPromptDialogFormatString</key>
|
||||
<string>Enter username</string>
|
||||
<key>INIntentParameterPromptDialogFormatStringID</key>
|
||||
<string>3d6HSO</string>
|
||||
<key>INIntentParameterPromptDialogType</key>
|
||||
<string>Configuration</string>
|
||||
</dict>
|
||||
|
@ -271,13 +278,37 @@
|
|||
<key>INIntentParameterPromptDialogType</key>
|
||||
<string>Primary</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>INIntentParameterPromptDialogCustom</key>
|
||||
<true/>
|
||||
<key>INIntentParameterPromptDialogFormatString</key>
|
||||
<string>There are ${count} options matching ‘${accounts}’.</string>
|
||||
<key>INIntentParameterPromptDialogFormatStringID</key>
|
||||
<string>3nWfxd</string>
|
||||
<key>INIntentParameterPromptDialogType</key>
|
||||
<string>DisambiguationIntroduction</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>INIntentParameterPromptDialogCustom</key>
|
||||
<true/>
|
||||
<key>INIntentParameterPromptDialogFormatString</key>
|
||||
<string>Just to confirm, you wanted ‘${accounts}’?</string>
|
||||
<key>INIntentParameterPromptDialogFormatStringID</key>
|
||||
<string>IP6ujX</string>
|
||||
<key>INIntentParameterPromptDialogType</key>
|
||||
<string>Confirmation</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>INIntentParameterSupportsDynamicEnumeration</key>
|
||||
<true/>
|
||||
<key>INIntentParameterSupportsMultipleValues</key>
|
||||
<true/>
|
||||
<key>INIntentParameterSupportsSearch</key>
|
||||
<true/>
|
||||
<key>INIntentParameterTag</key>
|
||||
<integer>5</integer>
|
||||
<integer>6</integer>
|
||||
<key>INIntentParameterType</key>
|
||||
<string>Object</string>
|
||||
<string>String</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>INIntentResponse</key>
|
||||
|
@ -295,9 +326,32 @@
|
|||
<string>failure</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>INIntentResponseLastParameterTag</key>
|
||||
<integer>4</integer>
|
||||
<key>INIntentResponseOutput</key>
|
||||
<string>username</string>
|
||||
<key>INIntentResponseParameters</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>INIntentResponseParameterDisplayName</key>
|
||||
<string>Username</string>
|
||||
<key>INIntentResponseParameterDisplayNameID</key>
|
||||
<string>7DZrRA</string>
|
||||
<key>INIntentResponseParameterDisplayPriority</key>
|
||||
<integer>1</integer>
|
||||
<key>INIntentResponseParameterName</key>
|
||||
<string>username</string>
|
||||
<key>INIntentResponseParameterSupportsMultipleValues</key>
|
||||
<true/>
|
||||
<key>INIntentResponseParameterTag</key>
|
||||
<integer>4</integer>
|
||||
<key>INIntentResponseParameterType</key>
|
||||
<string>Object</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
<key>INIntentTitle</key>
|
||||
<string>Multi Follow Count Small</string>
|
||||
<string>Multi Followers Count Small</string>
|
||||
<key>INIntentTitleID</key>
|
||||
<string>e0W2wo</string>
|
||||
<key>INIntentType</key>
|
||||
|
@ -307,86 +361,6 @@
|
|||
</dict>
|
||||
</array>
|
||||
<key>INTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>INTypeDisplayName</key>
|
||||
<string>Account</string>
|
||||
<key>INTypeDisplayNameID</key>
|
||||
<string>LUrJ3D</string>
|
||||
<key>INTypeLastPropertyTag</key>
|
||||
<integer>101</integer>
|
||||
<key>INTypeName</key>
|
||||
<string>MultiFollowAccountsSmall</string>
|
||||
<key>INTypeProperties</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>INTypePropertyDefault</key>
|
||||
<true/>
|
||||
<key>INTypePropertyDisplayPriority</key>
|
||||
<integer>1</integer>
|
||||
<key>INTypePropertyName</key>
|
||||
<string>identifier</string>
|
||||
<key>INTypePropertyTag</key>
|
||||
<integer>1</integer>
|
||||
<key>INTypePropertyType</key>
|
||||
<string>String</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>INTypePropertyDefault</key>
|
||||
<true/>
|
||||
<key>INTypePropertyDisplayPriority</key>
|
||||
<integer>2</integer>
|
||||
<key>INTypePropertyName</key>
|
||||
<string>displayString</string>
|
||||
<key>INTypePropertyTag</key>
|
||||
<integer>2</integer>
|
||||
<key>INTypePropertyType</key>
|
||||
<string>String</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>INTypePropertyDefault</key>
|
||||
<true/>
|
||||
<key>INTypePropertyDisplayPriority</key>
|
||||
<integer>3</integer>
|
||||
<key>INTypePropertyName</key>
|
||||
<string>pronunciationHint</string>
|
||||
<key>INTypePropertyTag</key>
|
||||
<integer>3</integer>
|
||||
<key>INTypePropertyType</key>
|
||||
<string>String</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>INTypePropertyDefault</key>
|
||||
<true/>
|
||||
<key>INTypePropertyDisplayPriority</key>
|
||||
<integer>4</integer>
|
||||
<key>INTypePropertyName</key>
|
||||
<string>alternativeSpeakableMatches</string>
|
||||
<key>INTypePropertySupportsMultipleValues</key>
|
||||
<true/>
|
||||
<key>INTypePropertyTag</key>
|
||||
<integer>4</integer>
|
||||
<key>INTypePropertyType</key>
|
||||
<string>SpeakableString</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>INTypePropertyDisplayName</key>
|
||||
<string>username</string>
|
||||
<key>INTypePropertyDisplayNameID</key>
|
||||
<string>TQNJZz</string>
|
||||
<key>INTypePropertyDisplayPriority</key>
|
||||
<integer>5</integer>
|
||||
<key>INTypePropertyName</key>
|
||||
<string>property</string>
|
||||
<key>INTypePropertySupportsMultipleValues</key>
|
||||
<true/>
|
||||
<key>INTypePropertyTag</key>
|
||||
<integer>101</integer>
|
||||
<key>INTypePropertyType</key>
|
||||
<string>String</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<array/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
@ -7,5 +7,6 @@ import SwiftUI
|
|||
struct WidgetExtensionBundle: WidgetBundle {
|
||||
var body: some Widget {
|
||||
FollowersCountWidget()
|
||||
MultiFollowersCountWidget()
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue