Avatars on timelines improvements

This commit is contained in:
Marcin Czachursk 2023-04-10 12:35:49 +02:00
parent 776236b2f4
commit 14b9785af2
8 changed files with 36 additions and 34 deletions

View File

@ -1168,7 +1168,7 @@
CODE_SIGN_ENTITLEMENTS = VernissageWidget/VernissageWidgetExtension.entitlements; CODE_SIGN_ENTITLEMENTS = VernissageWidget/VernissageWidgetExtension.entitlements;
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 102; CURRENT_PROJECT_VERSION = 104;
DEVELOPMENT_TEAM = B2U9FEKYP8; DEVELOPMENT_TEAM = B2U9FEKYP8;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = VernissageWidget/Info.plist; INFOPLIST_FILE = VernissageWidget/Info.plist;
@ -1179,7 +1179,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.2.0; MARKETING_VERSION = 1.3.0;
PRODUCT_BUNDLE_IDENTIFIER = dev.mczachurski.vernissage.widget; PRODUCT_BUNDLE_IDENTIFIER = dev.mczachurski.vernissage.widget;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
@ -1196,7 +1196,7 @@
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
CODE_SIGN_ENTITLEMENTS = VernissageWidget/VernissageWidgetExtension.entitlements; CODE_SIGN_ENTITLEMENTS = VernissageWidget/VernissageWidgetExtension.entitlements;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 102; CURRENT_PROJECT_VERSION = 104;
DEVELOPMENT_TEAM = B2U9FEKYP8; DEVELOPMENT_TEAM = B2U9FEKYP8;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = VernissageWidget/Info.plist; INFOPLIST_FILE = VernissageWidget/Info.plist;
@ -1207,7 +1207,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.2.0; MARKETING_VERSION = 1.3.0;
PRODUCT_BUNDLE_IDENTIFIER = dev.mczachurski.vernissage.widget; PRODUCT_BUNDLE_IDENTIFIER = dev.mczachurski.vernissage.widget;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
@ -1223,7 +1223,7 @@
CODE_SIGN_ENTITLEMENTS = VernissageShare/VernissageShareExtension.entitlements; CODE_SIGN_ENTITLEMENTS = VernissageShare/VernissageShareExtension.entitlements;
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 102; CURRENT_PROJECT_VERSION = 104;
DEVELOPMENT_TEAM = B2U9FEKYP8; DEVELOPMENT_TEAM = B2U9FEKYP8;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = VernissageShare/Info.plist; INFOPLIST_FILE = VernissageShare/Info.plist;
@ -1235,7 +1235,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.2.0; MARKETING_VERSION = 1.3.0;
PRODUCT_BUNDLE_IDENTIFIER = dev.mczachurski.vernissage.share; PRODUCT_BUNDLE_IDENTIFIER = dev.mczachurski.vernissage.share;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
@ -1250,7 +1250,7 @@
buildSettings = { buildSettings = {
CODE_SIGN_ENTITLEMENTS = VernissageShare/VernissageShareExtension.entitlements; CODE_SIGN_ENTITLEMENTS = VernissageShare/VernissageShareExtension.entitlements;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 102; CURRENT_PROJECT_VERSION = 104;
DEVELOPMENT_TEAM = B2U9FEKYP8; DEVELOPMENT_TEAM = B2U9FEKYP8;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = VernissageShare/Info.plist; INFOPLIST_FILE = VernissageShare/Info.plist;
@ -1262,7 +1262,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.2.0; MARKETING_VERSION = 1.3.0;
PRODUCT_BUNDLE_IDENTIFIER = dev.mczachurski.vernissage.share; PRODUCT_BUNDLE_IDENTIFIER = dev.mczachurski.vernissage.share;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
@ -1399,7 +1399,7 @@
CODE_SIGN_ENTITLEMENTS = Vernissage/Vernissage.entitlements; CODE_SIGN_ENTITLEMENTS = Vernissage/Vernissage.entitlements;
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 102; CURRENT_PROJECT_VERSION = 104;
DEVELOPMENT_ASSET_PATHS = "\"Vernissage/Preview Content\""; DEVELOPMENT_ASSET_PATHS = "\"Vernissage/Preview Content\"";
DEVELOPMENT_TEAM = B2U9FEKYP8; DEVELOPMENT_TEAM = B2U9FEKYP8;
ENABLE_PREVIEWS = YES; ENABLE_PREVIEWS = YES;
@ -1416,7 +1416,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.2.0; MARKETING_VERSION = 1.3.0;
PRODUCT_BUNDLE_IDENTIFIER = dev.mczachurski.vernissage; PRODUCT_BUNDLE_IDENTIFIER = dev.mczachurski.vernissage;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
@ -1439,7 +1439,7 @@
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO; ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
CODE_SIGN_ENTITLEMENTS = Vernissage/Vernissage.entitlements; CODE_SIGN_ENTITLEMENTS = Vernissage/Vernissage.entitlements;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 102; CURRENT_PROJECT_VERSION = 104;
DEVELOPMENT_ASSET_PATHS = "\"Vernissage/Preview Content\""; DEVELOPMENT_ASSET_PATHS = "\"Vernissage/Preview Content\"";
DEVELOPMENT_TEAM = B2U9FEKYP8; DEVELOPMENT_TEAM = B2U9FEKYP8;
ENABLE_PREVIEWS = YES; ENABLE_PREVIEWS = YES;
@ -1456,7 +1456,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.2.0; MARKETING_VERSION = 1.3.0;
PRODUCT_BUNDLE_IDENTIFIER = dev.mczachurski.vernissage; PRODUCT_BUNDLE_IDENTIFIER = dev.mczachurski.vernissage;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";

View File

@ -9,6 +9,7 @@ import Nuke
import NukeUI import NukeUI
import ClientKit import ClientKit
import EnvironmentKit import EnvironmentKit
import WidgetKit
@main @main
struct VernissageApp: App { struct VernissageApp: App {
@ -66,6 +67,9 @@ struct VernissageApp: App {
await self.calculateNewPhotosInBackground() await self.calculateNewPhotosInBackground()
} }
} }
// Reload widget content when application become active.
WidgetCenter.shared.reloadAllTimelines()
} }
.onReceive(timer) { _ in .onReceive(timer) { _ in
Task { Task {

View File

@ -12,18 +12,18 @@ import ServicesKit
import EnvironmentKit import EnvironmentKit
public extension View { public extension View {
func imageAvatar(applicationState: ApplicationState, displayName: String?, avatarUrl: URL?) -> some View { func imageAvatar(displayName: String?, avatarUrl: URL?) -> some View {
modifier(ImageAvatar(applicationState: applicationState, displayName: displayName, avatarUrl: avatarUrl)) modifier(ImageAvatar(displayName: displayName, avatarUrl: avatarUrl))
} }
} }
private struct ImageAvatar: ViewModifier { private struct ImageAvatar: ViewModifier {
private let applicationState: ApplicationState @EnvironmentObject var applicationState: ApplicationState
private let displayName: String? private let displayName: String?
private let avatarUrl: URL? private let avatarUrl: URL?
init(applicationState: ApplicationState, displayName: String?, avatarUrl: URL?) { init(displayName: String?, avatarUrl: URL?) {
self.applicationState = applicationState
self.displayName = displayName self.displayName = displayName
self.avatarUrl = avatarUrl self.avatarUrl = avatarUrl
} }

View File

@ -10,22 +10,22 @@ import ClientKit
import ServicesKit import ServicesKit
public extension View { public extension View {
func imageContextMenu(client: Client, statusModel: StatusModel) -> some View { func imageContextMenu(statusModel: StatusModel) -> some View {
modifier(ImageContextMenu(client: client, id: statusModel.id, url: statusModel.url)) modifier(ImageContextMenu(id: statusModel.id, url: statusModel.url))
} }
func imageContextMenu(client: Client, statusData: StatusData) -> some View { func imageContextMenu(statusData: StatusData) -> some View {
modifier(ImageContextMenu(client: client, id: statusData.id, url: statusData.url)) modifier(ImageContextMenu(id: statusData.id, url: statusData.url))
} }
} }
private struct ImageContextMenu: ViewModifier { private struct ImageContextMenu: ViewModifier {
private let client: Client @EnvironmentObject var client: Client
private let id: String private let id: String
private let url: URL? private let url: URL?
init(client: Client, id: String, url: URL?) { init(id: String, url: URL?) {
self.client = client
self.id = id self.id = id
self.url = url self.url = url
} }

View File

@ -121,10 +121,9 @@ struct ImageRowItem: View {
.onTapGesture { .onTapGesture {
self.navigateToStatus() self.navigateToStatus()
} }
.imageAvatar(applicationState: self.applicationState, .imageAvatar(displayName: self.status.accountDisplayName,
displayName: self.status.accountDisplayName,
avatarUrl: self.status.accountAvatar) avatarUrl: self.status.accountAvatar)
.imageContextMenu(client: self.client, statusData: self.status) .imageContextMenu(statusData: self.status)
} }
private func downloadImage(attachmentData: AttachmentData) async { private func downloadImage(attachmentData: AttachmentData) async {

View File

@ -128,11 +128,10 @@ struct ImageRowItemAsync: View {
self.navigateToStatus() self.navigateToStatus()
} }
.if(self.showAvatar) { .if(self.showAvatar) {
$0.imageAvatar(applicationState: self.applicationState, $0.imageAvatar(displayName: self.statusViewModel.account.displayNameWithoutEmojis,
displayName: self.statusViewModel.account.displayNameWithoutEmojis,
avatarUrl: self.statusViewModel.account.avatar) avatarUrl: self.statusViewModel.account.avatar)
} }
.imageContextMenu(client: self.client, statusModel: self.statusViewModel) .imageContextMenu(statusModel: self.statusViewModel)
} }
private func navigateToStatus() { private func navigateToStatus() {

View File

@ -54,7 +54,7 @@ public class ImageFetcher {
continue continue
} }
let displayDate = Calendar.current.date(byAdding: .minute, value: widgetEntries.count * 15, to: Date()) let displayDate = Calendar.current.date(byAdding: .minute, value: widgetEntries.count * 20, to: Date())
widgetEntries.append(WidgetEntry(date: displayDate ?? Date(), widgetEntries.append(WidgetEntry(date: displayDate ?? Date(),
image: uiImage, image: uiImage,
@ -67,7 +67,7 @@ public class ImageFetcher {
widgetEntries.append(self.placeholder()) widgetEntries.append(self.placeholder())
} }
return widgetEntries return widgetEntries.shuffled()
} }
func placeholder() -> WidgetEntry { func placeholder() -> WidgetEntry {

View File

@ -31,13 +31,13 @@ struct Provider: TimelineProvider {
let currentDate = Date() let currentDate = Date()
let widgetEntries = await self.getWidgetEntries() let widgetEntries = await self.getWidgetEntries()
let nextUpdateDate = Calendar.current.date(byAdding: .hour, value: 2, to: currentDate)! let nextUpdateDate = Calendar.current.date(byAdding: .hour, value: 1, to: currentDate)!
let timeline = Timeline(entries: widgetEntries, policy: .after(nextUpdateDate)) let timeline = Timeline(entries: widgetEntries, policy: .after(nextUpdateDate))
completion(timeline) completion(timeline)
} }
} }
func getWidgetEntries(length: Int = 8) async -> [WidgetEntry] { func getWidgetEntries(length: Int = 3) async -> [WidgetEntry] {
do { do {
return try await ImageFetcher.shared.fetchWidgetEntries(length: length) return try await ImageFetcher.shared.fetchWidgetEntries(length: length)
} catch { } catch {