mirror of
https://github.com/Dimillian/IceCubesApp.git
synced 2024-11-22 00:11:00 +00:00
Widget: Add mentions widget
This commit is contained in:
parent
24d5ecd119
commit
189e10f2b4
9 changed files with 250 additions and 19 deletions
|
@ -124,6 +124,8 @@
|
|||
9FF2FB632BE7F5D9001560CE /* HashtagPostsWidgetConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FF2FB5E2BE7F56F001560CE /* HashtagPostsWidgetConfiguration.swift */; };
|
||||
9FF2FB672BE7F816001560CE /* PostsWidgetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FF2FB652BE7F805001560CE /* PostsWidgetView.swift */; };
|
||||
9FF2FB6A2BE7F84E001560CE /* SharedUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FF2FB682BE7F842001560CE /* SharedUtils.swift */; };
|
||||
9FF2FB702BE8AE9D001560CE /* MentionWidgetConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FF2FB6E2BE8AE9B001560CE /* MentionWidgetConfiguration.swift */; };
|
||||
9FF2FB712BE8AEA0001560CE /* MentionWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FF2FB6C2BE8AE90001560CE /* MentionWidget.swift */; };
|
||||
9FFF677C299B7B2C00FE700A /* Notifications in Frameworks */ = {isa = PBXBuildFile; productRef = 9FFF677B299B7B2C00FE700A /* Notifications */; };
|
||||
9FFF6780299B7D2B00FE700A /* DesignSystem in Frameworks */ = {isa = PBXBuildFile; productRef = 9FFF677F299B7D2B00FE700A /* DesignSystem */; };
|
||||
9FFF6782299B7D3A00FE700A /* Account in Frameworks */ = {isa = PBXBuildFile; productRef = 9FFF6781299B7D3A00FE700A /* Account */; };
|
||||
|
@ -298,6 +300,8 @@
|
|||
9FF2FB602BE7F5A7001560CE /* HashtagPostsWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HashtagPostsWidget.swift; sourceTree = "<group>"; };
|
||||
9FF2FB652BE7F805001560CE /* PostsWidgetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostsWidgetView.swift; sourceTree = "<group>"; };
|
||||
9FF2FB682BE7F842001560CE /* SharedUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharedUtils.swift; sourceTree = "<group>"; };
|
||||
9FF2FB6C2BE8AE90001560CE /* MentionWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MentionWidget.swift; sourceTree = "<group>"; };
|
||||
9FF2FB6E2BE8AE9B001560CE /* MentionWidgetConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MentionWidgetConfiguration.swift; sourceTree = "<group>"; };
|
||||
B0BAB49E29B3D7A9008F54D7 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
|
||||
C4CBB90B298A0DA3007E1707 /* en-GB */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-GB"; path = "en-GB.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
|
||||
C4FBCF6F298FD88A0015DF22 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
|
||||
|
@ -479,9 +483,10 @@
|
|||
9F7788CA2BE652B1004E6BEF /* IceCubesAppWidgetsExtension */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
9FF2FB6B2BE8AE78001560CE /* MentionWidget */,
|
||||
9FF2FB642BE7F7FA001560CE /* Shared */,
|
||||
9FF2FB5D2BE7F559001560CE /* HashtagPostsWidget */,
|
||||
9FF2FB5C2BE7F549001560CE /* LatestPosts */,
|
||||
9FF2FB5C2BE7F549001560CE /* LatestPostsWidget */,
|
||||
9F7788D72BE652B2004E6BEF /* IceCubesAppWidgetsExtensionExtension.entitlements */,
|
||||
9F7788CB2BE652B1004E6BEF /* IceCubesAppWidgetsExtensionBundle.swift */,
|
||||
9F7788D12BE652B2004E6BEF /* Assets.xcassets */,
|
||||
|
@ -641,13 +646,13 @@
|
|||
path = Settings;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
9FF2FB5C2BE7F549001560CE /* LatestPosts */ = {
|
||||
9FF2FB5C2BE7F549001560CE /* LatestPostsWidget */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
9F7788CD2BE652B1004E6BEF /* LatestPostsWidget.swift */,
|
||||
9F7788CF2BE652B1004E6BEF /* LatestPostsWidgetConfiguration.swift */,
|
||||
);
|
||||
path = LatestPosts;
|
||||
path = LatestPostsWidget;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
9FF2FB5D2BE7F559001560CE /* HashtagPostsWidget */ = {
|
||||
|
@ -668,6 +673,15 @@
|
|||
path = Shared;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
9FF2FB6B2BE8AE78001560CE /* MentionWidget */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
9FF2FB6C2BE8AE90001560CE /* MentionWidget.swift */,
|
||||
9FF2FB6E2BE8AE9B001560CE /* MentionWidgetConfiguration.swift */,
|
||||
);
|
||||
path = MentionWidget;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
E9B576C029743F2A00BCE646 /* Localization */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -982,11 +996,13 @@
|
|||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
9FF2FB622BE7F5D5001560CE /* HashtagPostsWidget.swift in Sources */,
|
||||
9FF2FB712BE8AEA0001560CE /* MentionWidget.swift in Sources */,
|
||||
9F7788EA2BE65585004E6BEF /* AppAccountEntity.swift in Sources */,
|
||||
9FF2FB6A2BE7F84E001560CE /* SharedUtils.swift in Sources */,
|
||||
9F7788CE2BE652B1004E6BEF /* LatestPostsWidget.swift in Sources */,
|
||||
9F7788EE2BE78D7B004E6BEF /* TimelineFilterEntity.swift in Sources */,
|
||||
9F7788CC2BE652B1004E6BEF /* IceCubesAppWidgetsExtensionBundle.swift in Sources */,
|
||||
9FF2FB702BE8AE9D001560CE /* MentionWidgetConfiguration.swift in Sources */,
|
||||
9F7788D02BE652B1004E6BEF /* LatestPostsWidgetConfiguration.swift in Sources */,
|
||||
9FF2FB672BE7F816001560CE /* PostsWidgetView.swift in Sources */,
|
||||
9FF2FB632BE7F5D9001560CE /* HashtagPostsWidgetConfiguration.swift in Sources */,
|
||||
|
|
|
@ -0,0 +1,114 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1530"
|
||||
wasCreatedForAppExtension = "YES"
|
||||
version = "2.0">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES"
|
||||
buildArchitectures = "Automatic">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "9F7788C42BE652B1004E6BEF"
|
||||
BuildableName = "IceCubesAppWidgetsExtensionExtension.appex"
|
||||
BlueprintName = "IceCubesAppWidgetsExtensionExtension"
|
||||
ReferencedContainer = "container:IceCubesApp.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "9FBFE638292A715500C250E9"
|
||||
BuildableName = "Ice Cubes.app"
|
||||
BlueprintName = "IceCubesApp"
|
||||
ReferencedContainer = "container:IceCubesApp.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
shouldAutocreateTestPlan = "YES">
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = ""
|
||||
selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
|
||||
launchStyle = "0"
|
||||
askForAppToLaunch = "Yes"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES"
|
||||
launchAutomaticallySubstyle = "2">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "9FBFE638292A715500C250E9"
|
||||
BuildableName = "Ice Cubes.app"
|
||||
BlueprintName = "IceCubesApp"
|
||||
ReferencedContainer = "container:IceCubesApp.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<EnvironmentVariables>
|
||||
<EnvironmentVariable
|
||||
key = "_XCWidgetKind"
|
||||
value = ""
|
||||
isEnabled = "YES">
|
||||
</EnvironmentVariable>
|
||||
<EnvironmentVariable
|
||||
key = "_XCWidgetDefaultView"
|
||||
value = "timeline"
|
||||
isEnabled = "YES">
|
||||
</EnvironmentVariable>
|
||||
<EnvironmentVariable
|
||||
key = "_XCWidgetFamily"
|
||||
value = "systemMedium"
|
||||
isEnabled = "YES">
|
||||
</EnvironmentVariable>
|
||||
</EnvironmentVariables>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
askForAppToLaunch = "Yes"
|
||||
launchAutomaticallySubstyle = "2">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "9FBFE638292A715500C250E9"
|
||||
BuildableName = "Ice Cubes.app"
|
||||
BlueprintName = "IceCubesApp"
|
||||
ReferencedContainer = "container:IceCubesApp.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
|
@ -8,7 +8,7 @@ import Timeline
|
|||
struct HashtagPostsWidgetProvider: AppIntentTimelineProvider {
|
||||
func placeholder(in context: Context) -> PostsWidgetEntry {
|
||||
.init(date: Date(),
|
||||
timeline: .hashtag(tag: "Mastodon", accountId: nil),
|
||||
title: "#Mastodon",
|
||||
statuses: [.placeholder()],
|
||||
images: [:])
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ struct HashtagPostsWidgetProvider: AppIntentTimelineProvider {
|
|||
return entry
|
||||
}
|
||||
return .init(date: Date(),
|
||||
timeline: .hashtag(tag: "Mastodon", accountId: nil),
|
||||
title: "#Mastodon",
|
||||
statuses: [],
|
||||
images: [:])
|
||||
}
|
||||
|
@ -29,18 +29,18 @@ struct HashtagPostsWidgetProvider: AppIntentTimelineProvider {
|
|||
|
||||
private func timeline(for configuration: HashtagPostsWidgetConfiguration, context: Context) async -> Timeline<PostsWidgetEntry> {
|
||||
do {
|
||||
let statuses = await loadStatuses(for: .hashtag(tag: configuration.hashgtag, accountId: nil),
|
||||
let timeline: TimelineFilter = .hashtag(tag: configuration.hashgtag, accountId: nil)
|
||||
let statuses = await loadStatuses(for: timeline,
|
||||
account: configuration.account,
|
||||
widgetFamily: context.family)
|
||||
let images = try await loadImages(urls: statuses.map{ $0.account.avatar } )
|
||||
return Timeline(entries: [.init(date: Date(),
|
||||
timeline: .hashtag(tag: configuration.hashgtag,
|
||||
accountId: nil),
|
||||
title: timeline.title,
|
||||
statuses: statuses,
|
||||
images: images)], policy: .atEnd)
|
||||
} catch {
|
||||
return Timeline(entries: [.init(date: Date(),
|
||||
timeline: .hashtag(tag: "Mastodon", accountId: nil),
|
||||
title: "#Mastodon",
|
||||
statuses: [],
|
||||
images: [:])],
|
||||
policy: .atEnd)
|
||||
|
@ -69,7 +69,7 @@ struct HashtagPostsWidget: Widget {
|
|||
HashtagPostsWidget()
|
||||
} timeline: {
|
||||
PostsWidgetEntry(date: .now,
|
||||
timeline: .hashtag(tag: "Matodon", accountId: nil),
|
||||
title: "#Mastodon",
|
||||
statuses: [.placeholder(), .placeholder(), .placeholder(), .placeholder()],
|
||||
images: [:])
|
||||
}
|
||||
|
|
|
@ -6,5 +6,6 @@ struct IceCubesAppWidgetsExtensionBundle: WidgetBundle {
|
|||
var body: some Widget {
|
||||
LatestPostsWidget()
|
||||
HashtagPostsWidget()
|
||||
MentionsWidget()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ import Timeline
|
|||
struct LatestPostsWidgetProvider: AppIntentTimelineProvider {
|
||||
func placeholder(in context: Context) -> PostsWidgetEntry {
|
||||
.init(date: Date(),
|
||||
timeline: .home,
|
||||
title: "Home",
|
||||
statuses: [.placeholder()],
|
||||
images: [:])
|
||||
}
|
||||
|
@ -18,7 +18,8 @@ struct LatestPostsWidgetProvider: AppIntentTimelineProvider {
|
|||
return entry
|
||||
}
|
||||
return .init(date: Date(),
|
||||
timeline: .home, statuses: [],
|
||||
title: configuration.timeline.timeline.title,
|
||||
statuses: [],
|
||||
images: [:])
|
||||
}
|
||||
|
||||
|
@ -33,12 +34,12 @@ struct LatestPostsWidgetProvider: AppIntentTimelineProvider {
|
|||
widgetFamily: context.family)
|
||||
let images = try await loadImages(urls: statuses.map{ $0.account.avatar } )
|
||||
return Timeline(entries: [.init(date: Date(),
|
||||
timeline: configuration.timeline.timeline,
|
||||
title: configuration.timeline.timeline.title,
|
||||
statuses: statuses,
|
||||
images: images)], policy: .atEnd)
|
||||
} catch {
|
||||
return Timeline(entries: [.init(date: Date(),
|
||||
timeline: .home,
|
||||
title: configuration.timeline.timeline.title,
|
||||
statuses: [],
|
||||
images: [:])],
|
||||
policy: .atEnd)
|
||||
|
@ -86,7 +87,7 @@ struct LatestPostsWidget: Widget {
|
|||
LatestPostsWidget()
|
||||
} timeline: {
|
||||
PostsWidgetEntry(date: .now,
|
||||
timeline: .home,
|
||||
statuses: [.placeholder(), .placeholder(), .placeholder(), .placeholder()],
|
||||
images: [:])
|
||||
title: "Mastodon",
|
||||
statuses: [.placeholder(), .placeholder(), .placeholder(), .placeholder()],
|
||||
images: [:])
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
import WidgetKit
|
||||
import SwiftUI
|
||||
import Network
|
||||
import DesignSystem
|
||||
import Models
|
||||
import Timeline
|
||||
|
||||
struct MentionsWidgetProvider: AppIntentTimelineProvider {
|
||||
func placeholder(in context: Context) -> PostsWidgetEntry {
|
||||
.init(date: Date(),
|
||||
title: "Mentions",
|
||||
statuses: [.placeholder()],
|
||||
images: [:])
|
||||
}
|
||||
|
||||
func snapshot(for configuration: MentionsWidgetConfiguration, in context: Context) async -> PostsWidgetEntry {
|
||||
if let entry = await timeline(for: configuration, context: context).entries.first {
|
||||
return entry
|
||||
}
|
||||
return .init(date: Date(),
|
||||
title: "Mentions",
|
||||
statuses: [],
|
||||
images: [:])
|
||||
}
|
||||
|
||||
func timeline(for configuration: MentionsWidgetConfiguration, in context: Context) async -> Timeline<PostsWidgetEntry> {
|
||||
await timeline(for: configuration, context: context)
|
||||
}
|
||||
|
||||
private func timeline(for configuration: MentionsWidgetConfiguration, context: Context) async -> Timeline<PostsWidgetEntry> {
|
||||
do {
|
||||
let client = Client(server: configuration.account.account.server,
|
||||
oauthToken: configuration.account.account.oauthToken)
|
||||
var excludedTypes = Models.Notification.NotificationType.allCases
|
||||
excludedTypes.removeAll(where: { $0 == .mention })
|
||||
var notifications: [Models.Notification] =
|
||||
try await client.get(endpoint: Notifications.notifications(minId: nil,
|
||||
maxId: nil,
|
||||
types: excludedTypes.map(\.rawValue),
|
||||
limit: 5))
|
||||
let statuses = notifications.compactMap{ $0.status }
|
||||
let images = try await loadImages(urls: statuses.map{ $0.account.avatar } )
|
||||
return Timeline(entries: [.init(date: Date(),
|
||||
title: "Mentions",
|
||||
statuses: statuses,
|
||||
images: images)], policy: .atEnd)
|
||||
} catch {
|
||||
return Timeline(entries: [.init(date: Date(),
|
||||
title: "Mentions",
|
||||
statuses: [],
|
||||
images: [:])],
|
||||
policy: .atEnd)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct MentionsWidget: Widget {
|
||||
let kind: String = "MentionsWidget"
|
||||
|
||||
var body: some WidgetConfiguration {
|
||||
AppIntentConfiguration(kind: kind,
|
||||
intent: MentionsWidgetConfiguration.self,
|
||||
provider: MentionsWidgetProvider()) { entry in
|
||||
PostsWidgetView(entry: entry)
|
||||
.containerBackground(Color("WidgetBackground").gradient, for: .widget)
|
||||
}
|
||||
.configurationDisplayName("Mentions")
|
||||
.description("Show the latest mentions for the selected account.")
|
||||
.supportedFamilies([.systemSmall, .systemMedium, .systemLarge, .systemExtraLarge])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#Preview(as: .systemMedium) {
|
||||
MentionsWidget()
|
||||
} timeline: {
|
||||
PostsWidgetEntry(date: .now,
|
||||
title: "Mentions",
|
||||
statuses: [.placeholder(), .placeholder(), .placeholder(), .placeholder()],
|
||||
images: [:])
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
import WidgetKit
|
||||
import AppIntents
|
||||
|
||||
struct MentionsWidgetConfiguration: WidgetConfigurationIntent {
|
||||
static let title: LocalizedStringResource = "Configuration"
|
||||
static let description = IntentDescription("Choose the account for this widget")
|
||||
|
||||
@Parameter(title: "Account")
|
||||
var account: AppAccountEntity
|
||||
}
|
||||
|
||||
extension MentionsWidgetConfiguration {
|
||||
static var previewAccount: MentionsWidgetConfiguration {
|
||||
let intent = MentionsWidgetConfiguration()
|
||||
intent.account = .init(account: .init(server: "Test", accountName: "Test account"))
|
||||
return intent
|
||||
}
|
||||
}
|
|
@ -7,7 +7,7 @@ import Timeline
|
|||
|
||||
struct PostsWidgetEntry: TimelineEntry {
|
||||
let date: Date
|
||||
let timeline: TimelineFilter
|
||||
let title: String
|
||||
let statuses: [Status]
|
||||
let images: [URL: UIImage]
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ struct PostsWidgetView : View {
|
|||
|
||||
private var headerView: some View {
|
||||
HStack {
|
||||
Text(entry.timeline.title)
|
||||
Text(entry.title)
|
||||
Spacer()
|
||||
Image(systemName: "cube")
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue