From 8d76cfdcd3aca5794f0a0f021044df3d86afdbd0 Mon Sep 17 00:00:00 2001 From: Marcin Czachurski Date: Fri, 5 May 2023 14:59:06 +0200 Subject: [PATCH 1/5] Remove duplicated code --- Vernissage/Views/StatusView/StatusView.swift | 8 ++------ Vernissage/Widgets/ImageViewer.swift | 8 ++------ 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/Vernissage/Views/StatusView/StatusView.swift b/Vernissage/Views/StatusView/StatusView.swift index f420855..bcfcb36 100644 --- a/Vernissage/Views/StatusView/StatusView.swift +++ b/Vernissage/Views/StatusView/StatusView.swift @@ -240,18 +240,14 @@ struct StatusView: View { } if let imageHeight = self.imageHeight, let imageWidth = self.imageWidth, imageHeight > 0 && imageWidth > 0 { - return self.calculateHeight(width: Double(imageWidth), height: Double(imageHeight)) + let calculatedSize = ImageSizeService.shared.calculate(width: Double(imageWidth), height: Double(imageHeight)) + return calculatedSize.height } // If we don't have image height and width in metadata, we have to use some constant height. return UIScreen.main.bounds.width * 0.75 } - private func calculateHeight(width: Double, height: Double) -> CGFloat { - let divider = width / UIScreen.main.bounds.size.width - return height / divider - } - private func getMainStatus(status: StatusModel) async throws -> StatusModel { guard let inReplyToId = status.inReplyToId else { return status diff --git a/Vernissage/Widgets/ImageViewer.swift b/Vernissage/Widgets/ImageViewer.swift index 9833202..7c25f9a 100644 --- a/Vernissage/Widgets/ImageViewer.swift +++ b/Vernissage/Widgets/ImageViewer.swift @@ -224,7 +224,8 @@ struct ImageViewer: View { private func calculateStartingOffset() -> CGSize { // Image size on the screen. - let imageOnScreenHeight = self.calculateHeight(width: self.imageWidth, height: self.imageHeight) + let calculatedSize = ImageSizeService.shared.calculate(width: self.imageWidth, height: self.imageHeight) + let imageOnScreenHeight = calculatedSize.height // Calculate full space for image. let safeAreaInsetsTop = UIApplication.shared.keyWindow?.safeAreaInsets.top ?? 20.0 @@ -254,9 +255,4 @@ struct ImageViewer: View { return 88.0 } } - - private func calculateHeight(width: Double, height: Double) -> CGFloat { - let divider = width / UIScreen.main.bounds.size.width - return height / divider - } } From f2d8c5c47fb48a60247e2b8c8e6e3b0b44153d1c Mon Sep 17 00:00:00 2001 From: Marcin Czachurski Date: Fri, 5 May 2023 18:36:15 +0200 Subject: [PATCH 2/5] Initial implementation --- .../Subviews/UserProfileStatusesView.swift | 26 ++++++++++--------- Vernissage/Widgets/ImageRowAsync.swift | 24 +++++++++++++---- Vernissage/Widgets/ImageRowItemAsync.swift | 11 ++++++-- 3 files changed, 42 insertions(+), 19 deletions(-) diff --git a/Vernissage/Views/UserProfileView/Subviews/UserProfileStatusesView.swift b/Vernissage/Views/UserProfileView/Subviews/UserProfileStatusesView.swift index c547edc..5f6843e 100644 --- a/Vernissage/Views/UserProfileView/Subviews/UserProfileStatusesView.swift +++ b/Vernissage/Views/UserProfileView/Subviews/UserProfileStatusesView.swift @@ -26,10 +26,12 @@ struct UserProfileStatusesView: View { private let imagePrefetcher = ImagePrefetcher(destination: .diskCache) var body: some View { - LazyVStack(alignment: .center) { - if firstLoadFinished == true { + // LazyVStack(alignment: .center) { + if firstLoadFinished == true { + LazyVGrid(columns: [GridItem(.flexible(), spacing: 10), GridItem(.flexible(), spacing: 0)], spacing: 5) { ForEach(self.statusViewModels, id: \.id) { item in - ImageRowAsync(statusViewModel: item, withAvatar: false) + ImageRowAsync(statusViewModel: item, withAvatar: false, clipToSquare: true) + .frame(width: UIScreen.main.bounds.width / 2, height: UIScreen.main.bounds.width / 2) } if allItemsLoaded == false && firstLoadFinished == true { @@ -46,16 +48,16 @@ struct UserProfileStatusesView: View { Spacer() } } - } else { - LoadingIndicator() - } - } - .onFirstAppear { - do { - try await self.loadStatuses() - } catch { - ErrorService.shared.handle(error, message: "global.error.errorDuringDownloadStatuses", showToastr: !Task.isCancelled) } + } else { + LoadingIndicator() + .onFirstAppear { + do { + try await self.loadStatuses() + } catch { + ErrorService.shared.handle(error, message: "global.error.errorDuringDownloadStatuses", showToastr: !Task.isCancelled) + } + } } } diff --git a/Vernissage/Widgets/ImageRowAsync.swift b/Vernissage/Widgets/ImageRowAsync.swift index 9e5cf52..12af889 100644 --- a/Vernissage/Widgets/ImageRowAsync.swift +++ b/Vernissage/Widgets/ImageRowAsync.swift @@ -14,13 +14,15 @@ struct ImageRowAsync: View { private let statusViewModel: StatusModel private let firstAttachment: AttachmentModel? private let showAvatar: Bool + private let clipToSquare: Bool @State private var selected: String @State private var imageHeight: Double @State private var imageWidth: Double - init(statusViewModel: StatusModel, withAvatar showAvatar: Bool = true) { + init(statusViewModel: StatusModel, withAvatar showAvatar: Bool = true, clipToSquare: Bool = false) { self.showAvatar = showAvatar + self.clipToSquare = clipToSquare self.statusViewModel = statusViewModel self.firstAttachment = statusViewModel.mediaAttachments.first self.selected = String.empty() @@ -44,7 +46,11 @@ struct ImageRowAsync: View { var body: some View { if statusViewModel.mediaAttachments.count == 1, let firstAttachment = self.firstAttachment { - ImageRowItemAsync(statusViewModel: self.statusViewModel, attachment: firstAttachment, withAvatar: self.showAvatar) { (imageWidth, imageHeight) in + ImageRowItemAsync(statusViewModel: self.statusViewModel, + attachment: firstAttachment, + withAvatar: self.showAvatar, + clipToSquare: self.clipToSquare) { (imageWidth, imageHeight) in + // When we download image and calculate real size we have to change view size. if imageWidth != self.imageWidth || imageHeight != self.imageHeight { withAnimation(.linear(duration: 0.4)) { @@ -53,11 +59,17 @@ struct ImageRowAsync: View { } } } - .frame(width: self.imageWidth, height: self.imageHeight) + .if(self.clipToSquare == false) { + $0.frame(width: self.imageWidth, height: self.imageHeight) + } } else { TabView(selection: $selected) { ForEach(statusViewModel.mediaAttachments, id: \.id) { attachment in - ImageRowItemAsync(statusViewModel: self.statusViewModel, attachment: attachment, withAvatar: self.showAvatar) { (imageWidth, imageHeight) in + ImageRowItemAsync(statusViewModel: self.statusViewModel, + attachment: attachment, + withAvatar: self.showAvatar, + clipToSquare: self.clipToSquare) { (imageWidth, imageHeight) in + // When we download image and calculate real size we have to change view size (only when image is now visible). if attachment.id == self.selected { if imageWidth != self.imageWidth || imageHeight != self.imageHeight { @@ -86,7 +98,9 @@ struct ImageRowAsync: View { } } }) - .frame(width: self.imageWidth, height: self.imageHeight) + .if(self.clipToSquare == false) { + $0.frame(width: self.imageWidth, height: self.imageHeight) + } .tabViewStyle(.page(indexDisplayMode: .never)) .overlay(CustomPageTabViewStyleView(pages: self.statusViewModel.mediaAttachments, currentId: $selected)) } diff --git a/Vernissage/Widgets/ImageRowItemAsync.swift b/Vernissage/Widgets/ImageRowItemAsync.swift index 45a79d3..c99e4d8 100644 --- a/Vernissage/Widgets/ImageRowItemAsync.swift +++ b/Vernissage/Widgets/ImageRowItemAsync.swift @@ -21,6 +21,7 @@ struct ImageRowItemAsync: View { private var statusViewModel: StatusModel private var attachment: AttachmentModel private let showAvatar: Bool + private let clipToSquare: Bool private let imageFromCache: Bool @State private var showThumbImage = false @@ -31,8 +32,11 @@ struct ImageRowItemAsync: View { init(statusViewModel: StatusModel, attachment: AttachmentModel, - withAvatar showAvatar: Bool = true, onImageDownloaded: @escaping (_: Double, _: Double) -> Void) { + withAvatar showAvatar: Bool = true, + clipToSquare: Bool = false, + onImageDownloaded: @escaping (_: Double, _: Double) -> Void) { self.showAvatar = showAvatar + self.clipToSquare = clipToSquare self.statusViewModel = statusViewModel self.attachment = attachment self.onImageDownloaded = onImageDownloaded @@ -149,7 +153,10 @@ struct ImageRowItemAsync: View { private func imageView(image: Image) -> some View { image .resizable() - .aspectRatio(contentMode: .fit) + .scaledToFill() + .if(self.clipToSquare) { + $0.frame(width: UIScreen.main.bounds.width / 2, height: UIScreen.main.bounds.width / 2).clipped() + } .onTapGesture(count: 2) { Task { // Update favourite in Pixelfed server. From 2ca4b42b30c3e64488fa925077ffd41b2605e398 Mon Sep 17 00:00:00 2001 From: Marcin Czachurski Date: Sat, 6 May 2023 11:47:13 +0200 Subject: [PATCH 3/5] Add switch for changing user profile photos grid view --- ...plicationSettings+CoreDataProperties.swift | 1 + CoreData/ApplicationSettingsHandler.swift | 7 ++ .../Vernissage.xcdatamodeld/.xccurrentversion | 2 +- .../Vernissage-014.xcdatamodel/contents | 103 ++++++++++++++++++ .../EnvironmentKit/ApplicationState.swift | 3 + .../Sources/PixelfedKit/PixelfedClient.swift | 4 + Vernissage.xcodeproj/project.pbxproj | 28 ++--- Vernissage/Views/HomeFeedView.swift | 4 +- .../Subviews/UserProfileHeaderView.swift | 2 +- .../Subviews/UserProfileStatusesView.swift | 37 ++++++- Vernissage/Widgets/ImageRowItemAsync.swift | 14 ++- 11 files changed, 179 insertions(+), 26 deletions(-) create mode 100644 CoreData/Vernissage.xcdatamodeld/Vernissage-014.xcdatamodel/contents diff --git a/CoreData/ApplicationSettings+CoreDataProperties.swift b/CoreData/ApplicationSettings+CoreDataProperties.swift index b71289c..0ecf351 100644 --- a/CoreData/ApplicationSettings+CoreDataProperties.swift +++ b/CoreData/ApplicationSettings+CoreDataProperties.swift @@ -33,6 +33,7 @@ extension ApplicationSettings { @NSManaged public var showFavouritesOnTimeline: Bool @NSManaged public var showAltIconOnTimeline: Bool @NSManaged public var warnAboutMissingAlt: Bool + @NSManaged public var showGridOnUserProfile: Bool @NSManaged public var customNavigationMenuItem1: Int32 @NSManaged public var customNavigationMenuItem2: Int32 diff --git a/CoreData/ApplicationSettingsHandler.swift b/CoreData/ApplicationSettingsHandler.swift index 9e6a87c..edc87ea 100644 --- a/CoreData/ApplicationSettingsHandler.swift +++ b/CoreData/ApplicationSettingsHandler.swift @@ -58,6 +58,7 @@ class ApplicationSettingsHandler { applicationState.showFavouritesOnTimeline = defaultSettings.showFavouritesOnTimeline applicationState.showAltIconOnTimeline = defaultSettings.showAltIconOnTimeline applicationState.warnAboutMissingAlt = defaultSettings.warnAboutMissingAlt + applicationState.showGridOnUserProfile = defaultSettings.showGridOnUserProfile if let menuPosition = MenuPosition(rawValue: Int(defaultSettings.menuPosition)) { applicationState.menuPosition = menuPosition @@ -190,6 +191,12 @@ class ApplicationSettingsHandler { CoreDataHandler.shared.save() } + func set(showGridOnUserProfile: Bool) { + let defaultSettings = self.get() + defaultSettings.showGridOnUserProfile = showGridOnUserProfile + CoreDataHandler.shared.save() + } + private func createApplicationSettingsEntity(viewContext: NSManagedObjectContext? = nil) -> ApplicationSettings { let context = viewContext ?? CoreDataHandler.shared.container.viewContext return ApplicationSettings(context: context) diff --git a/CoreData/Vernissage.xcdatamodeld/.xccurrentversion b/CoreData/Vernissage.xcdatamodeld/.xccurrentversion index 866776e..a37b6e9 100644 --- a/CoreData/Vernissage.xcdatamodeld/.xccurrentversion +++ b/CoreData/Vernissage.xcdatamodeld/.xccurrentversion @@ -3,6 +3,6 @@ _XCCurrentVersionName - Vernissage-013.xcdatamodel + Vernissage-014.xcdatamodel diff --git a/CoreData/Vernissage.xcdatamodeld/Vernissage-014.xcdatamodel/contents b/CoreData/Vernissage.xcdatamodeld/Vernissage-014.xcdatamodel/contents new file mode 100644 index 0000000..1de970b --- /dev/null +++ b/CoreData/Vernissage.xcdatamodeld/Vernissage-014.xcdatamodel/contents @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/EnvironmentKit/Sources/EnvironmentKit/ApplicationState.swift b/EnvironmentKit/Sources/EnvironmentKit/ApplicationState.swift index 2f5bc16..dbdfbb2 100644 --- a/EnvironmentKit/Sources/EnvironmentKit/ApplicationState.swift +++ b/EnvironmentKit/Sources/EnvironmentKit/ApplicationState.swift @@ -104,6 +104,9 @@ public class ApplicationState: ObservableObject { /// Show warning about missing ALT texts on compose screen. @Published public var warnAboutMissingAlt = true + /// Show grid of photos on user profile. + @Published public var showGridOnUserProfile = false + public func changeApplicationState(accountModel: AccountModel, instance: Instance?, lastSeenStatusId: String?) { self.account = accountModel self.lastSeenStatusId = lastSeenStatusId diff --git a/PixelfedKit/Sources/PixelfedKit/PixelfedClient.swift b/PixelfedKit/Sources/PixelfedKit/PixelfedClient.swift index fd7a742..cc5f74b 100644 --- a/PixelfedKit/Sources/PixelfedKit/PixelfedClient.swift +++ b/PixelfedKit/Sources/PixelfedKit/PixelfedClient.swift @@ -131,7 +131,9 @@ public class PixelfedClientAuthenticated: PixelfedClientProtocol { return try JSONDecoder().decode(type, from: data) } catch { let json = String(data: data, encoding: .utf8)! + print(json) + print(String(describing: error)) throw error } @@ -158,7 +160,9 @@ public class PixelfedClientAuthenticated: PixelfedClientProtocol { return Linkable(data: decoded, link: link) } catch { let json = String(data: data, encoding: .utf8)! + print(json) + print(String(describing: error)) throw error } diff --git a/Vernissage.xcodeproj/project.pbxproj b/Vernissage.xcodeproj/project.pbxproj index 0d69375..626603f 100644 --- a/Vernissage.xcodeproj/project.pbxproj +++ b/Vernissage.xcodeproj/project.pbxproj @@ -246,6 +246,7 @@ F808641329756666009F035C /* NotificationRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationRowView.swift; sourceTree = ""; }; F8121CA7298A86D600B466C7 /* InstanceRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstanceRowView.swift; sourceTree = ""; }; F815F60B29E49CF20044566B /* Avatar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Avatar.swift; sourceTree = ""; }; + F8206A032A06547600E19412 /* Vernissage-014.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "Vernissage-014.xcdatamodel"; sourceTree = ""; }; F8210DCE2966B600001D9973 /* ImageRowAsync.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageRowAsync.swift; sourceTree = ""; }; F8210DDC2966CF17001D9973 /* StatusData+Status.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "StatusData+Status.swift"; sourceTree = ""; }; F8210DDE2966CFC7001D9973 /* AttachmentData+Attachment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AttachmentData+Attachment.swift"; sourceTree = ""; }; @@ -1316,7 +1317,7 @@ CODE_SIGN_ENTITLEMENTS = VernissageWidget/VernissageWidgetExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 139; + CURRENT_PROJECT_VERSION = 140; DEVELOPMENT_TEAM = B2U9FEKYP8; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = VernissageWidget/Info.plist; @@ -1327,7 +1328,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.8.0; + MARKETING_VERSION = 1.9.0; PRODUCT_BUNDLE_IDENTIFIER = dev.mczachurski.vernissage.widget; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; @@ -1344,7 +1345,7 @@ ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; CODE_SIGN_ENTITLEMENTS = VernissageWidget/VernissageWidgetExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 139; + CURRENT_PROJECT_VERSION = 140; DEVELOPMENT_TEAM = B2U9FEKYP8; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = VernissageWidget/Info.plist; @@ -1355,7 +1356,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.8.0; + MARKETING_VERSION = 1.9.0; PRODUCT_BUNDLE_IDENTIFIER = dev.mczachurski.vernissage.widget; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; @@ -1371,7 +1372,7 @@ CODE_SIGN_ENTITLEMENTS = VernissageShare/VernissageShareExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 139; + CURRENT_PROJECT_VERSION = 140; DEVELOPMENT_TEAM = B2U9FEKYP8; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = VernissageShare/Info.plist; @@ -1383,7 +1384,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.8.0; + MARKETING_VERSION = 1.9.0; PRODUCT_BUNDLE_IDENTIFIER = dev.mczachurski.vernissage.share; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; @@ -1398,7 +1399,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = VernissageShare/VernissageShareExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 139; + CURRENT_PROJECT_VERSION = 140; DEVELOPMENT_TEAM = B2U9FEKYP8; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = VernissageShare/Info.plist; @@ -1410,7 +1411,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.8.0; + MARKETING_VERSION = 1.9.0; PRODUCT_BUNDLE_IDENTIFIER = dev.mczachurski.vernissage.share; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; @@ -1547,7 +1548,7 @@ CODE_SIGN_ENTITLEMENTS = Vernissage/Vernissage.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 139; + CURRENT_PROJECT_VERSION = 140; DEVELOPMENT_ASSET_PATHS = "\"Vernissage/Preview Content\""; DEVELOPMENT_TEAM = B2U9FEKYP8; ENABLE_PREVIEWS = YES; @@ -1566,7 +1567,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.8.0; + MARKETING_VERSION = 1.9.0; PRODUCT_BUNDLE_IDENTIFIER = dev.mczachurski.vernissage; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -1589,7 +1590,7 @@ ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO; CODE_SIGN_ENTITLEMENTS = Vernissage/Vernissage.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 139; + CURRENT_PROJECT_VERSION = 140; DEVELOPMENT_ASSET_PATHS = "\"Vernissage/Preview Content\""; DEVELOPMENT_TEAM = B2U9FEKYP8; ENABLE_PREVIEWS = YES; @@ -1608,7 +1609,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.8.0; + MARKETING_VERSION = 1.9.0; PRODUCT_BUNDLE_IDENTIFIER = dev.mczachurski.vernissage; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; @@ -1773,6 +1774,7 @@ F88C2476295C37BB0006098B /* Vernissage.xcdatamodeld */ = { isa = XCVersionGroup; children = ( + F8206A032A06547600E19412 /* Vernissage-014.xcdatamodel */, F865B4D42A0252FB008ACDFC /* Vernissage-013.xcdatamodel */, F8EF3C8B29FC3A5F00CBFF7C /* Vernissage-012.xcdatamodel */, F871F21F29EF0FEC00A351EF /* Vernissage-011.xcdatamodel */, @@ -1788,7 +1790,7 @@ F8C937A929882CA90004D782 /* Vernissage-001.xcdatamodel */, F88C2477295C37BB0006098B /* Vernissage.xcdatamodel */, ); - currentVersion = F865B4D42A0252FB008ACDFC /* Vernissage-013.xcdatamodel */; + currentVersion = F8206A032A06547600E19412 /* Vernissage-014.xcdatamodel */; path = Vernissage.xcdatamodeld; sourceTree = ""; versionGroupType = wrapper.xcdatamodel; diff --git a/Vernissage/Views/HomeFeedView.swift b/Vernissage/Views/HomeFeedView.swift index 5495af6..1a3e11a 100644 --- a/Vernissage/Views/HomeFeedView.swift +++ b/Vernissage/Views/HomeFeedView.swift @@ -103,8 +103,10 @@ struct HomeFeedView: View { if let account = self.applicationState.account { if let lastSeenStatusId = try await HomeTimelineService.shared.refreshTimeline(for: account) { try await HomeTimelineService.shared.save(lastSeenStatusId: lastSeenStatusId, for: account) - self.applicationState.lastSeenStatusId = lastSeenStatusId + } + + asyncAfter(0.35) { self.applicationState.amountOfNewStatuses = 0 } } diff --git a/Vernissage/Views/UserProfileView/Subviews/UserProfileHeaderView.swift b/Vernissage/Views/UserProfileView/Subviews/UserProfileHeaderView.swift index bfc2c86..24c73ea 100644 --- a/Vernissage/Views/UserProfileView/Subviews/UserProfileHeaderView.swift +++ b/Vernissage/Views/UserProfileView/Subviews/UserProfileHeaderView.swift @@ -102,7 +102,7 @@ struct UserProfileHeaderView: View { .foregroundColor(.lightGrayColor.opacity(0.5)) .font(.footnote) } - .padding() + .padding([.top, .leading, .trailing]) } @ViewBuilder diff --git a/Vernissage/Views/UserProfileView/Subviews/UserProfileStatusesView.swift b/Vernissage/Views/UserProfileView/Subviews/UserProfileStatusesView.swift index 5f6843e..c7741a9 100644 --- a/Vernissage/Views/UserProfileView/Subviews/UserProfileStatusesView.swift +++ b/Vernissage/Views/UserProfileView/Subviews/UserProfileStatusesView.swift @@ -24,14 +24,43 @@ struct UserProfileStatusesView: View { private let defaultLimit = 20 private let imagePrefetcher = ImagePrefetcher(destination: .diskCache) + private let singleGrids = [GridItem(.flexible(), spacing: 10)] + private let dubleGrid = [GridItem(.flexible(), spacing: 10), GridItem(.flexible(), spacing: 0)] var body: some View { - // LazyVStack(alignment: .center) { if firstLoadFinished == true { - LazyVGrid(columns: [GridItem(.flexible(), spacing: 10), GridItem(.flexible(), spacing: 0)], spacing: 5) { + HStack { + Spacer() + Button { + withAnimation { + self.applicationState.showGridOnUserProfile = false + ApplicationSettingsHandler.shared.set(showGridOnUserProfile: false) + } + } label: { + Image(systemName: "rectangle.grid.1x2.fill") + .foregroundColor(self.applicationState.showGridOnUserProfile ? .lightGrayColor : .accentColor) + .padding(.trailing, 8) + .padding(.bottom, 8) + } + Button { + withAnimation { + self.applicationState.showGridOnUserProfile = true + ApplicationSettingsHandler.shared.set(showGridOnUserProfile: true) + } + } label: { + Image(systemName: "rectangle.grid.2x2.fill") + .foregroundColor(self.applicationState.showGridOnUserProfile ? .accentColor : .lightGrayColor) + .padding(.trailing, 16) + .padding(.bottom, 8) + } + } + + LazyVGrid(columns: self.applicationState.showGridOnUserProfile ? dubleGrid : singleGrids, spacing: 5) { ForEach(self.statusViewModels, id: \.id) { item in - ImageRowAsync(statusViewModel: item, withAvatar: false, clipToSquare: true) - .frame(width: UIScreen.main.bounds.width / 2, height: UIScreen.main.bounds.width / 2) + ImageRowAsync(statusViewModel: item, withAvatar: false, clipToSquare: self.applicationState.showGridOnUserProfile) + .if(self.applicationState.showGridOnUserProfile) { + $0.frame(width: UIScreen.main.bounds.width / 2, height: UIScreen.main.bounds.width / 2) + } } if allItemsLoaded == false && firstLoadFinished == true { diff --git a/Vernissage/Widgets/ImageRowItemAsync.swift b/Vernissage/Widgets/ImageRowItemAsync.swift index c99e4d8..ae01cef 100644 --- a/Vernissage/Widgets/ImageRowItemAsync.swift +++ b/Vernissage/Widgets/ImageRowItemAsync.swift @@ -49,7 +49,7 @@ struct ImageRowItemAsync: View { if let image = state.image { if self.statusViewModel.sensitive && !self.applicationState.showSensitive { ZStack { - ContentWarning(spoilerText: self.statusViewModel.spoilerText) { + ContentWarning(spoilerText: self.clipToSquare ? nil : self.statusViewModel.spoilerText) { self.imageContainerView(image: image) .imageContextMenu(statusModel: self.statusViewModel, attachmentModel: self.attachment, @@ -57,11 +57,13 @@ struct ImageRowItemAsync: View { } blurred: { ZStack { BlurredImage(blurhash: attachment.blurhash) - ImageAvatar(displayName: self.statusViewModel.account.displayNameWithoutEmojis, - avatarUrl: self.statusViewModel.account.avatar) { - self.routerPath.navigate(to: .userProfile(accountId: self.statusViewModel.account.id, - accountDisplayName: self.statusViewModel.account.displayNameWithoutEmojis, - accountUserName: self.statusViewModel.account.acct)) + if self.showAvatar { + ImageAvatar(displayName: self.statusViewModel.account.displayNameWithoutEmojis, + avatarUrl: self.statusViewModel.account.avatar) { + self.routerPath.navigate(to: .userProfile(accountId: self.statusViewModel.account.id, + accountDisplayName: self.statusViewModel.account.displayNameWithoutEmojis, + accountUserName: self.statusViewModel.account.acct)) + } } } .onTapGesture { From ccdecddfe22a2eb55264a89b4e94dba3ebb877e7 Mon Sep 17 00:00:00 2001 From: Marcin Czachurski Date: Sat, 6 May 2023 18:21:05 +0200 Subject: [PATCH 4/5] Refactoring square user profile grid --- Vernissage.xcodeproj/project.pbxproj | 4 ++++ Vernissage/Models/ImageScale.swift | 12 ++++++++++++ .../Subviews/UserProfileStatusesView.swift | 5 +++-- Vernissage/Widgets/ImageRowAsync.swift | 14 +++++++------- Vernissage/Widgets/ImageRowItemAsync.swift | 10 +++++----- 5 files changed, 31 insertions(+), 14 deletions(-) create mode 100644 Vernissage/Models/ImageScale.swift diff --git a/Vernissage.xcodeproj/project.pbxproj b/Vernissage.xcodeproj/project.pbxproj index 626603f..7d5fa4b 100644 --- a/Vernissage.xcodeproj/project.pbxproj +++ b/Vernissage.xcodeproj/project.pbxproj @@ -190,6 +190,7 @@ F8B0885E29942E31002AB40A /* ThirdPartyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8B0885D29942E31002AB40A /* ThirdPartyView.swift */; }; F8B0886029943498002AB40A /* OtherSectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8B0885F29943498002AB40A /* OtherSectionView.swift */; }; F8B08862299435C9002AB40A /* SupportView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8B08861299435C9002AB40A /* SupportView.swift */; }; + F8C287A32A06B4C90072213F /* ImageScale.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8C287A22A06B4C90072213F /* ImageScale.swift */; }; F8D5444329D4066C002225D6 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8D5444229D4066C002225D6 /* AppDelegate.swift */; }; F8DF38E429DD68820047F1AA /* ViewOffsetKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8DF38E329DD68820047F1AA /* ViewOffsetKey.swift */; }; F8DF38E629DDB98A0047F1AA /* SocialsSectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8DF38E529DDB98A0047F1AA /* SocialsSectionView.swift */; }; @@ -390,6 +391,7 @@ F8B08861299435C9002AB40A /* SupportView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SupportView.swift; sourceTree = ""; }; F8B3699A29D86EB600BE3808 /* .swiftlint.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = .swiftlint.yml; sourceTree = ""; }; F8B3699B29D86EBD00BE3808 /* .gitignore */ = {isa = PBXFileReference; lastKnownFileType = text; path = .gitignore; sourceTree = ""; }; + F8C287A22A06B4C90072213F /* ImageScale.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageScale.swift; sourceTree = ""; }; F8C937A929882CA90004D782 /* Vernissage-001.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "Vernissage-001.xcdatamodel"; sourceTree = ""; }; F8CAE64129B8F1AF001E0372 /* Vernissage-005.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "Vernissage-005.xcdatamodel"; sourceTree = ""; }; F8D5444229D4066C002225D6 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; @@ -531,6 +533,7 @@ F8DF38E329DD68820047F1AA /* ViewOffsetKey.swift */, F871F21C29EF0D7000A351EF /* NavigationMenuItemDetails.swift */, F8624D3C29F2D3AC00204986 /* SelectedMenuItemDetails.swift */, + F8C287A22A06B4C90072213F /* ImageScale.swift */, ); path = Models; sourceTree = ""; @@ -1255,6 +1258,7 @@ F89B5CC229D01BF700549F2F /* InstanceView.swift in Sources */, F825F0CB29F7CFC4008BD204 /* FollowRequestsView.swift in Sources */, F825F0C929F7A562008BD204 /* UserProfilePrivateAccountView.swift in Sources */, + F8C287A32A06B4C90072213F /* ImageScale.swift in Sources */, F89F57B029D1C11200001EE3 /* RelationshipModel.swift in Sources */, F88AB05829B36B8200345EDE /* AccountsPhotoView.swift in Sources */, F85D4971296402DC00751DF7 /* AuthorizationService.swift in Sources */, diff --git a/Vernissage/Models/ImageScale.swift b/Vernissage/Models/ImageScale.swift new file mode 100644 index 0000000..01bb86f --- /dev/null +++ b/Vernissage/Models/ImageScale.swift @@ -0,0 +1,12 @@ +// +// https://mczachurski.dev +// Copyright © 2023 Marcin Czachurski and the repository contributors. +// Licensed under the Apache License 2.0. +// + +import Foundation + +enum ImageScale { + case orginalFullWidth + case squareHalfWidth +} diff --git a/Vernissage/Views/UserProfileView/Subviews/UserProfileStatusesView.swift b/Vernissage/Views/UserProfileView/Subviews/UserProfileStatusesView.swift index c7741a9..47e654d 100644 --- a/Vernissage/Views/UserProfileView/Subviews/UserProfileStatusesView.swift +++ b/Vernissage/Views/UserProfileView/Subviews/UserProfileStatusesView.swift @@ -57,7 +57,9 @@ struct UserProfileStatusesView: View { LazyVGrid(columns: self.applicationState.showGridOnUserProfile ? dubleGrid : singleGrids, spacing: 5) { ForEach(self.statusViewModels, id: \.id) { item in - ImageRowAsync(statusViewModel: item, withAvatar: false, clipToSquare: self.applicationState.showGridOnUserProfile) + ImageRowAsync(statusViewModel: item, + withAvatar: false, + imageScale: self.applicationState.showGridOnUserProfile ? .squareHalfWidth : .orginalFullWidth) .if(self.applicationState.showGridOnUserProfile) { $0.frame(width: UIScreen.main.bounds.width / 2, height: UIScreen.main.bounds.width / 2) } @@ -132,5 +134,4 @@ struct UserProfileStatusesView: View { private func prefetch(statusModels: [StatusModel]) { imagePrefetcher.startPrefetching(with: statusModels.getAllImagesUrls()) } - } diff --git a/Vernissage/Widgets/ImageRowAsync.swift b/Vernissage/Widgets/ImageRowAsync.swift index 12af889..d84afc6 100644 --- a/Vernissage/Widgets/ImageRowAsync.swift +++ b/Vernissage/Widgets/ImageRowAsync.swift @@ -14,15 +14,15 @@ struct ImageRowAsync: View { private let statusViewModel: StatusModel private let firstAttachment: AttachmentModel? private let showAvatar: Bool - private let clipToSquare: Bool + private let imageScale: ImageScale @State private var selected: String @State private var imageHeight: Double @State private var imageWidth: Double - init(statusViewModel: StatusModel, withAvatar showAvatar: Bool = true, clipToSquare: Bool = false) { + init(statusViewModel: StatusModel, withAvatar showAvatar: Bool = true, imageScale: ImageScale = .orginalFullWidth) { self.showAvatar = showAvatar - self.clipToSquare = clipToSquare + self.imageScale = imageScale self.statusViewModel = statusViewModel self.firstAttachment = statusViewModel.mediaAttachments.first self.selected = String.empty() @@ -49,7 +49,7 @@ struct ImageRowAsync: View { ImageRowItemAsync(statusViewModel: self.statusViewModel, attachment: firstAttachment, withAvatar: self.showAvatar, - clipToSquare: self.clipToSquare) { (imageWidth, imageHeight) in + imageScale: self.imageScale) { (imageWidth, imageHeight) in // When we download image and calculate real size we have to change view size. if imageWidth != self.imageWidth || imageHeight != self.imageHeight { @@ -59,7 +59,7 @@ struct ImageRowAsync: View { } } } - .if(self.clipToSquare == false) { + .if(self.imageScale == .orginalFullWidth) { $0.frame(width: self.imageWidth, height: self.imageHeight) } } else { @@ -68,7 +68,7 @@ struct ImageRowAsync: View { ImageRowItemAsync(statusViewModel: self.statusViewModel, attachment: attachment, withAvatar: self.showAvatar, - clipToSquare: self.clipToSquare) { (imageWidth, imageHeight) in + imageScale: self.imageScale) { (imageWidth, imageHeight) in // When we download image and calculate real size we have to change view size (only when image is now visible). if attachment.id == self.selected { @@ -98,7 +98,7 @@ struct ImageRowAsync: View { } } }) - .if(self.clipToSquare == false) { + .if(self.imageScale == .orginalFullWidth) { $0.frame(width: self.imageWidth, height: self.imageHeight) } .tabViewStyle(.page(indexDisplayMode: .never)) diff --git a/Vernissage/Widgets/ImageRowItemAsync.swift b/Vernissage/Widgets/ImageRowItemAsync.swift index ae01cef..f660505 100644 --- a/Vernissage/Widgets/ImageRowItemAsync.swift +++ b/Vernissage/Widgets/ImageRowItemAsync.swift @@ -21,8 +21,8 @@ struct ImageRowItemAsync: View { private var statusViewModel: StatusModel private var attachment: AttachmentModel private let showAvatar: Bool - private let clipToSquare: Bool private let imageFromCache: Bool + private let imageScale: ImageScale @State private var showThumbImage = false @State private var opacity = 1.0 @@ -33,10 +33,10 @@ struct ImageRowItemAsync: View { init(statusViewModel: StatusModel, attachment: AttachmentModel, withAvatar showAvatar: Bool = true, - clipToSquare: Bool = false, + imageScale: ImageScale = .orginalFullWidth, onImageDownloaded: @escaping (_: Double, _: Double) -> Void) { self.showAvatar = showAvatar - self.clipToSquare = clipToSquare + self.imageScale = imageScale self.statusViewModel = statusViewModel self.attachment = attachment self.onImageDownloaded = onImageDownloaded @@ -49,7 +49,7 @@ struct ImageRowItemAsync: View { if let image = state.image { if self.statusViewModel.sensitive && !self.applicationState.showSensitive { ZStack { - ContentWarning(spoilerText: self.clipToSquare ? nil : self.statusViewModel.spoilerText) { + ContentWarning(spoilerText: self.imageScale == .orginalFullWidth ? self.statusViewModel.spoilerText : nil) { self.imageContainerView(image: image) .imageContextMenu(statusModel: self.statusViewModel, attachmentModel: self.attachment, @@ -156,7 +156,7 @@ struct ImageRowItemAsync: View { image .resizable() .scaledToFill() - .if(self.clipToSquare) { + .if(self.imageScale == .squareHalfWidth) { $0.frame(width: UIScreen.main.bounds.width / 2, height: UIScreen.main.bounds.width / 2).clipped() } .onTapGesture(count: 2) { From 6aa7ed6d7d0739f2bc43435523d619881853b3ca Mon Sep 17 00:00:00 2001 From: Marcin Czachurski Date: Sat, 6 May 2023 18:21:52 +0200 Subject: [PATCH 5/5] Change version to 1.9.0 (141) --- Vernissage.xcodeproj/project.pbxproj | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Vernissage.xcodeproj/project.pbxproj b/Vernissage.xcodeproj/project.pbxproj index 7d5fa4b..2eb6c3b 100644 --- a/Vernissage.xcodeproj/project.pbxproj +++ b/Vernissage.xcodeproj/project.pbxproj @@ -1321,7 +1321,7 @@ CODE_SIGN_ENTITLEMENTS = VernissageWidget/VernissageWidgetExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 140; + CURRENT_PROJECT_VERSION = 141; DEVELOPMENT_TEAM = B2U9FEKYP8; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = VernissageWidget/Info.plist; @@ -1349,7 +1349,7 @@ ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; CODE_SIGN_ENTITLEMENTS = VernissageWidget/VernissageWidgetExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 140; + CURRENT_PROJECT_VERSION = 141; DEVELOPMENT_TEAM = B2U9FEKYP8; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = VernissageWidget/Info.plist; @@ -1376,7 +1376,7 @@ CODE_SIGN_ENTITLEMENTS = VernissageShare/VernissageShareExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 140; + CURRENT_PROJECT_VERSION = 141; DEVELOPMENT_TEAM = B2U9FEKYP8; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = VernissageShare/Info.plist; @@ -1403,7 +1403,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = VernissageShare/VernissageShareExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 140; + CURRENT_PROJECT_VERSION = 141; DEVELOPMENT_TEAM = B2U9FEKYP8; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = VernissageShare/Info.plist; @@ -1552,7 +1552,7 @@ CODE_SIGN_ENTITLEMENTS = Vernissage/Vernissage.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 140; + CURRENT_PROJECT_VERSION = 141; DEVELOPMENT_ASSET_PATHS = "\"Vernissage/Preview Content\""; DEVELOPMENT_TEAM = B2U9FEKYP8; ENABLE_PREVIEWS = YES; @@ -1594,7 +1594,7 @@ ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO; CODE_SIGN_ENTITLEMENTS = Vernissage/Vernissage.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 140; + CURRENT_PROJECT_VERSION = 141; DEVELOPMENT_ASSET_PATHS = "\"Vernissage/Preview Content\""; DEVELOPMENT_TEAM = B2U9FEKYP8; ENABLE_PREVIEWS = YES;