From 1558579a861bbfcad191532c655b0db9e522677c Mon Sep 17 00:00:00 2001 From: Marcus Kida Date: Mon, 30 Jan 2023 11:21:04 +0100 Subject: [PATCH] feat(Widget): Implement LightChartView for Small FollowerCount --- Mastodon.xcodeproj/project.pbxproj | 20 ++++++ .../xcshareddata/swiftpm/Package.resolved | 9 +++ .../Handler/FollowersCountIntentHandler.swift | 4 ++ .../WidgetExtension.intentdefinition | 72 +++++++++++++------ .../WidgetViews/FollowCountWidgetView.swift | 69 +++++++++++++++++- 5 files changed, 149 insertions(+), 25 deletions(-) diff --git a/Mastodon.xcodeproj/project.pbxproj b/Mastodon.xcodeproj/project.pbxproj index d1fb8b234..a6699f7ed 100644 --- a/Mastodon.xcodeproj/project.pbxproj +++ b/Mastodon.xcodeproj/project.pbxproj @@ -25,6 +25,7 @@ 2A1FE47C2938BB2600784BF1 /* FollowedTagsViewModel+DiffableDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A1FE47B2938BB2600784BF1 /* FollowedTagsViewModel+DiffableDataSource.swift */; }; 2A1FE47E2938C11200784BF1 /* Collection+IsNotEmpty.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A1FE47D2938C11200784BF1 /* Collection+IsNotEmpty.swift */; }; 2A33AB662982C4AF008A7FB1 /* FollowCountWidgetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A33AB652982C4AF008A7FB1 /* FollowCountWidgetView.swift */; }; + 2A33AB6D2987C2B3008A7FB1 /* LightChart in Frameworks */ = {isa = PBXBuildFile; productRef = 2A33AB6C2987C2B3008A7FB1 /* LightChart */; }; 2A3F6FE3292ECB5E002E6DA7 /* FollowedTagsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A3F6FE2292ECB5E002E6DA7 /* FollowedTagsViewModel.swift */; }; 2A3F6FE5292F6E44002E6DA7 /* FollowedTagsTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A3F6FE4292F6E44002E6DA7 /* FollowedTagsTableViewCell.swift */; }; 2A506CF4292CD85800059C37 /* FollowedTagsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A506CF3292CD85800059C37 /* FollowedTagsViewController.swift */; }; @@ -1196,6 +1197,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 2A33AB6D2987C2B3008A7FB1 /* LightChart in Frameworks */, 2A72813B297EC6F7004138C5 /* MastodonSDKDynamic in Frameworks */, 2A728124297EA9D7004138C5 /* SwiftUI.framework in Frameworks */, 2A728122297EA9D7004138C5 /* WidgetKit.framework in Frameworks */, @@ -2958,6 +2960,7 @@ name = WidgetExtension; packageProductDependencies = ( 2A72813A297EC6F7004138C5 /* MastodonSDKDynamic */, + 2A33AB6C2987C2B3008A7FB1 /* LightChart */, ); productName = WidgetExtensionExtension; productReference = 2A728120297EA9D7004138C5 /* WidgetExtension.appex */; @@ -3171,6 +3174,7 @@ ); mainGroup = DB427DC925BAA00100D1B89D; packageReferences = ( + 2A33AB6B2987C2B3008A7FB1 /* XCRemoteSwiftPackageReference "LightChart" */, ); productRefGroup = DB427DD325BAA00100D1B89D /* Products */; projectDirPath = ""; @@ -5242,7 +5246,23 @@ }; /* End XCConfigurationList section */ +/* Begin XCRemoteSwiftPackageReference section */ + 2A33AB6B2987C2B3008A7FB1 /* XCRemoteSwiftPackageReference "LightChart" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/pichukov/LightChart.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 1.0.0; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + /* Begin XCSwiftPackageProductDependency section */ + 2A33AB6C2987C2B3008A7FB1 /* LightChart */ = { + isa = XCSwiftPackageProductDependency; + package = 2A33AB6B2987C2B3008A7FB1 /* XCRemoteSwiftPackageReference "LightChart" */; + productName = LightChart; + }; 2A72813A297EC6F7004138C5 /* MastodonSDKDynamic */ = { isa = XCSwiftPackageProductDependency; productName = MastodonSDKDynamic; diff --git a/Mastodon.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Mastodon.xcworkspace/xcshareddata/swiftpm/Package.resolved index cd7395c7e..37232c2ef 100644 --- a/Mastodon.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Mastodon.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -73,6 +73,15 @@ "version": "4.2.2" } }, + { + "package": "LightChart", + "repositoryURL": "https://github.com/pichukov/LightChart.git", + "state": { + "branch": null, + "revision": "206fe7ab50620891c89531e2598e36e965678a1a", + "version": "1.0.3" + } + }, { "package": "MetaTextKit", "repositoryURL": "https://github.com/TwidereProject/MetaTextKit.git", diff --git a/MastodonIntent/Handler/FollowersCountIntentHandler.swift b/MastodonIntent/Handler/FollowersCountIntentHandler.swift index 7cf852de1..31c306127 100644 --- a/MastodonIntent/Handler/FollowersCountIntentHandler.swift +++ b/MastodonIntent/Handler/FollowersCountIntentHandler.swift @@ -7,6 +7,10 @@ import MastodonSDK import MastodonLocalization class FollowersCountIntentHandler: INExtension, FollowersCountIntentHandling { + func resolveShowChart(for intent: FollowersCountIntent) async -> INBooleanResolutionResult { + return .success(with: intent.showChart?.boolValue ?? false) + } + func resolveAccount(for intent: FollowersCountIntent) async -> INStringResolutionResult { .confirmationRequired(with: intent.account) } diff --git a/WidgetExtension/WidgetExtension.intentdefinition b/WidgetExtension/WidgetExtension.intentdefinition index 4c32b0533..bf1619332 100644 --- a/WidgetExtension/WidgetExtension.intentdefinition +++ b/WidgetExtension/WidgetExtension.intentdefinition @@ -9,7 +9,7 @@ INIntentDefinitionNamespace 88xZPY INIntentDefinitionSystemVersion - 22C65 + 22D49 INIntentDefinitionToolsBuildVersion 14C18 INIntentDefinitionToolsVersion @@ -28,7 +28,7 @@ INIntentIneligibleForSuggestions INIntentLastParameterTag - 5 + 7 INIntentName FollowersCount INIntentParameters @@ -69,26 +69,6 @@ INIntentParameterPromptDialogType Primary - - INIntentParameterPromptDialogCustom - - INIntentParameterPromptDialogFormatString - There are ${count} options matching ‘${account}’. - INIntentParameterPromptDialogFormatStringID - ceymMR - INIntentParameterPromptDialogType - DisambiguationIntroduction - - - INIntentParameterPromptDialogCustom - - INIntentParameterPromptDialogFormatString - Just to confirm, you wanted ‘${account}’? - INIntentParameterPromptDialogFormatStringID - z8tfVG - INIntentParameterPromptDialogType - Confirmation - INIntentParameterSupportsDynamicEnumeration @@ -99,6 +79,54 @@ INIntentParameterType String + + INIntentParameterConfigurable + + INIntentParameterDisplayName + Show chart + INIntentParameterDisplayNameID + xVtyec + INIntentParameterDisplayPriority + 2 + INIntentParameterMetadata + + INIntentParameterMetadataFalseDisplayName + No + INIntentParameterMetadataFalseDisplayNameID + jg9D5P + INIntentParameterMetadataTrueDisplayName + Yes + INIntentParameterMetadataTrueDisplayNameID + 82L4Nj + + INIntentParameterName + showChart + INIntentParameterPromptDialogs + + + INIntentParameterPromptDialogCustom + + INIntentParameterPromptDialogType + Configuration + + + INIntentParameterPromptDialogCustom + + INIntentParameterPromptDialogFormatString + Should the Widget show a chart? + INIntentParameterPromptDialogFormatStringID + zeJo4f + INIntentParameterPromptDialogType + Primary + + + INIntentParameterSupportsResolution + + INIntentParameterTag + 7 + INIntentParameterType + Boolean + INIntentResponse diff --git a/WidgetExtension/WidgetViews/FollowCountWidgetView.swift b/WidgetExtension/WidgetViews/FollowCountWidgetView.swift index c040f8d75..9b075f67a 100644 --- a/WidgetExtension/WidgetViews/FollowCountWidgetView.swift +++ b/WidgetExtension/WidgetViews/FollowCountWidgetView.swift @@ -2,6 +2,8 @@ import SwiftUI import WidgetKit +import LightChart +import MastodonAsset struct FollowCountWidgetView: View { @Environment(\.widgetFamily) var family @@ -12,7 +14,11 @@ struct FollowCountWidgetView: View { if let account = entry.account { switch family { case .systemSmall: - viewForSmallWidget(account) + if let showChart = entry.configuration.showChart?.boolValue, showChart { + viewForSmallWidgetYesChart(account) + } else { + viewForSmallWidgetNoChart(account) + } case .accessoryRectangular: viewForAccessoryRectangular(account) case .accessoryCircular: @@ -28,7 +34,7 @@ struct FollowCountWidgetView: View { } } - private func viewForSmallWidget(_ account: FollowersEntryAccountable) -> some View { + private func viewForSmallWidgetNoChart(_ account: FollowersEntryAccountable) -> some View { HStack { VStack(alignment: .leading, spacing: 0) { if let avatarImage = account.avatarImage { @@ -56,11 +62,68 @@ struct FollowCountWidgetView: View { .truncationMode(.tail) } .padding(.leading, 20) - .padding([.top, .bottom], 16) + .padding(.vertical, 16) Spacer() } } + private func viewForSmallWidgetYesChart(_ account: FollowersEntryAccountable) -> some View { + VStack(alignment: .leading, spacing: 0) { + HStack { + if let avatarImage = account.avatarImage { + Image(uiImage: avatarImage) + .resizable() + .frame(width: 23, height: 23) + .cornerRadius(5) + } + VStack(alignment: .leading) { + Text(account.displayNameWithFallback) + .font(.caption) + .lineLimit(1) + .truncationMode(.tail) + + Text("@\(account.acct)") + .font(.caption2) + .foregroundColor(.secondary) + .lineLimit(1) + .truncationMode(.tail) + } + Spacer() + } + .padding(.leading, 20) + + ZStack { + LightChartView( + data: [200, 205, 208, 213, 210, 211, 212], + type: .line, + visualType: .filled(color: Asset.Colors.Brand.blurple.swiftUIColor, lineWidth: 2), + offset: 0.8 /// this is the positive offset from the bottom edge of the graph (~80% above bottom level) + ) + + HStack { + VStack(alignment: .leading, spacing: 0) { + Spacer() + Text("+4 followers today") + .font(.system(size: 12)) + .foregroundColor(.secondary) + .lineLimit(1) + .truncationMode(.tail) + + Text(account.followersCount.asAbbreviatedCountString()) + .font(.largeTitle) + .lineLimit(1) + .truncationMode(.tail) + + } + Spacer() + } + .padding(.bottom, 16) + .padding(.leading, 20) + } + } + .padding(.top, 16) + } + private func viewForAccessoryRectangular(_ account :FollowersEntryAccountable) -> some View { HStack(spacing: 0) { VStack(alignment: .leading, spacing: 0) {