Initial PoC of iPad grid

This commit is contained in:
Marcin Czachurski 2023-05-11 20:00:39 +02:00
parent 0ac6ad376f
commit 446fbd9b9e
6 changed files with 78 additions and 36 deletions

View File

@ -27,6 +27,7 @@
F8210DEA2966E4F9001D9973 /* AnimatePlaceholderModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8210DE92966E4F9001D9973 /* AnimatePlaceholderModifier.swift */; }; F8210DEA2966E4F9001D9973 /* AnimatePlaceholderModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8210DE92966E4F9001D9973 /* AnimatePlaceholderModifier.swift */; };
F825F0C929F7A562008BD204 /* UserProfilePrivateAccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F825F0C829F7A562008BD204 /* UserProfilePrivateAccountView.swift */; }; F825F0C929F7A562008BD204 /* UserProfilePrivateAccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F825F0C829F7A562008BD204 /* UserProfilePrivateAccountView.swift */; };
F825F0CB29F7CFC4008BD204 /* FollowRequestsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F825F0CA29F7CFC4008BD204 /* FollowRequestsView.swift */; }; F825F0CB29F7CFC4008BD204 /* FollowRequestsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F825F0CA29F7CFC4008BD204 /* FollowRequestsView.swift */; };
F830C3CD2A07A4020005FEF8 /* WaterfallGrid in Frameworks */ = {isa = PBXBuildFile; productRef = F830C3CC2A07A4020005FEF8 /* WaterfallGrid */; };
F835082329BEF9C400DE3247 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = F835082629BEF9C400DE3247 /* Localizable.strings */; }; F835082329BEF9C400DE3247 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = F835082629BEF9C400DE3247 /* Localizable.strings */; };
F835082429BEF9C400DE3247 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = F835082629BEF9C400DE3247 /* Localizable.strings */; }; F835082429BEF9C400DE3247 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = F835082629BEF9C400DE3247 /* Localizable.strings */; };
F83CBEFB298298A1002972C8 /* ImageCarouselPicture.swift in Sources */ = {isa = PBXBuildFile; fileRef = F83CBEFA298298A1002972C8 /* ImageCarouselPicture.swift */; }; F83CBEFB298298A1002972C8 /* ImageCarouselPicture.swift in Sources */ = {isa = PBXBuildFile; fileRef = F83CBEFA298298A1002972C8 /* ImageCarouselPicture.swift */; };
@ -449,6 +450,7 @@
F8210DD92966BB7E001D9973 /* NukeUI in Frameworks */, F8210DD92966BB7E001D9973 /* NukeUI in Frameworks */,
F89B5CC029D019B600549F2F /* HTMLString in Frameworks */, F89B5CC029D019B600549F2F /* HTMLString in Frameworks */,
F88BC52A29E046D700CE6141 /* WidgetsKit in Frameworks */, F88BC52A29E046D700CE6141 /* WidgetsKit in Frameworks */,
F830C3CD2A07A4020005FEF8 /* WaterfallGrid in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -988,6 +990,7 @@
F88BC52629E0431D00CE6141 /* ServicesKit */, F88BC52629E0431D00CE6141 /* ServicesKit */,
F88BC52929E046D700CE6141 /* WidgetsKit */, F88BC52929E046D700CE6141 /* WidgetsKit */,
F88BC52C29E04BB600CE6141 /* EnvironmentKit */, F88BC52C29E04BB600CE6141 /* EnvironmentKit */,
F830C3CC2A07A4020005FEF8 /* WaterfallGrid */,
); );
productName = Vernissage; productName = Vernissage;
productReference = F88C2468295C37B80006098B /* Vernissage.app */; productReference = F88C2468295C37B80006098B /* Vernissage.app */;
@ -1031,6 +1034,7 @@
F88E4D4B297EA4290057491A /* XCRemoteSwiftPackageReference "EmojiText" */, F88E4D4B297EA4290057491A /* XCRemoteSwiftPackageReference "EmojiText" */,
F89B5CBE29D019B600549F2F /* XCRemoteSwiftPackageReference "HTMLString" */, F89B5CBE29D019B600549F2F /* XCRemoteSwiftPackageReference "HTMLString" */,
F84625F929FE393B002D3AF4 /* XCRemoteSwiftPackageReference "QRCode" */, F84625F929FE393B002D3AF4 /* XCRemoteSwiftPackageReference "QRCode" */,
F830C3CB2A07A4020005FEF8 /* XCRemoteSwiftPackageReference "WaterfallGrid" */,
); );
productRefGroup = F88C2469295C37B80006098B /* Products */; productRefGroup = F88C2469295C37B80006098B /* Products */;
projectDirPath = ""; projectDirPath = "";
@ -1338,7 +1342,7 @@
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 1; TARGETED_DEVICE_FAMILY = "1,2";
}; };
name = Debug; name = Debug;
}; };
@ -1366,7 +1370,7 @@
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 1; TARGETED_DEVICE_FAMILY = "1,2";
}; };
name = Release; name = Release;
}; };
@ -1394,7 +1398,7 @@
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 1; TARGETED_DEVICE_FAMILY = "1,2";
}; };
name = Debug; name = Debug;
}; };
@ -1421,7 +1425,7 @@
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 1; TARGETED_DEVICE_FAMILY = "1,2";
}; };
name = Release; name = Release;
}; };
@ -1580,7 +1584,7 @@
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 1; TARGETED_DEVICE_FAMILY = "1,2";
}; };
name = Debug; name = Debug;
}; };
@ -1621,7 +1625,7 @@
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 1; TARGETED_DEVICE_FAMILY = "1,2";
}; };
name = Release; name = Release;
}; };
@ -1675,6 +1679,14 @@
minimumVersion = 12.0.0; minimumVersion = 12.0.0;
}; };
}; };
F830C3CB2A07A4020005FEF8 /* XCRemoteSwiftPackageReference "WaterfallGrid" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/paololeonardi/WaterfallGrid.git";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 1.0.0;
};
};
F84625F929FE393B002D3AF4 /* XCRemoteSwiftPackageReference "QRCode" */ = { F84625F929FE393B002D3AF4 /* XCRemoteSwiftPackageReference "QRCode" */ = {
isa = XCRemoteSwiftPackageReference; isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/dmrschmidt/QRCode"; repositoryURL = "https://github.com/dmrschmidt/QRCode";
@ -1717,6 +1729,11 @@
package = F8210DD32966BB7E001D9973 /* XCRemoteSwiftPackageReference "Nuke" */; package = F8210DD32966BB7E001D9973 /* XCRemoteSwiftPackageReference "Nuke" */;
productName = NukeUI; productName = NukeUI;
}; };
F830C3CC2A07A4020005FEF8 /* WaterfallGrid */ = {
isa = XCSwiftPackageProductDependency;
package = F830C3CB2A07A4020005FEF8 /* XCRemoteSwiftPackageReference "WaterfallGrid" */;
productName = WaterfallGrid;
};
F84625FA29FE393B002D3AF4 /* QRCode */ = { F84625FA29FE393B002D3AF4 /* QRCode */ = {
isa = XCSwiftPackageProductDependency; isa = XCSwiftPackageProductDependency;
package = F84625F929FE393B002D3AF4 /* XCRemoteSwiftPackageReference "QRCode" */; package = F84625F929FE393B002D3AF4 /* XCRemoteSwiftPackageReference "QRCode" */;

View File

@ -11,6 +11,7 @@ import ClientKit
import ServicesKit import ServicesKit
import EnvironmentKit import EnvironmentKit
import WidgetsKit import WidgetsKit
import WaterfallGrid
struct StatusesView: View { struct StatusesView: View {
public enum ListType: Hashable { public enum ListType: Hashable {
@ -50,7 +51,7 @@ struct StatusesView: View {
@State private var state: ViewState = .loading @State private var state: ViewState = .loading
@State private var lastStatusId: String? @State private var lastStatusId: String?
private let defaultLimit = 20 private let defaultLimit = 40
private let imagePrefetcher = ImagePrefetcher(destination: .diskCache) private let imagePrefetcher = ImagePrefetcher(destination: .diskCache)
var body: some View { var body: some View {
@ -88,26 +89,35 @@ struct StatusesView: View {
@ViewBuilder @ViewBuilder
private func list() -> some View { private func list() -> some View {
ScrollView { ScrollView {
LazyVStack(alignment: .center) { WaterfallGrid(self.statusViewModels, id: \.id) { item in
ForEach(self.statusViewModels, id: \.id) { item in ImageRowAsync(statusViewModel: item,
ImageRowAsync(statusViewModel: item) withAvatar: true,
} imageScale: self.applicationState.showGridOnUserProfile ? .squareHalfWidth : .orginalFullWidth)
.padding(.top, -2)
if allItemsLoaded == false {
HStack {
Spacer()
LoadingIndicator()
.task {
do {
try await self.loadMoreStatuses()
} catch {
ErrorService.shared.handle(error, message: "statuses.error.loadingStatusesFailed", showToastr: !Task.isCancelled)
}
}
Spacer()
}
}
} }
.gridStyle(columns: 3, spacing: 4)
// .padding(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
// LazyVStack(alignment: .center) {
// ForEach(self.statusViewModels, id: \.id) { item in
// ImageRowAsync(statusViewModel: item)
// }
//
// if allItemsLoaded == false {
// HStack {
// Spacer()
// LoadingIndicator()
// .task {
// do {
// try await self.loadMoreStatuses()
// } catch {
// ErrorService.shared.handle(error, message: "statuses.error.loadingStatusesFailed", showToastr: !Task.isCancelled)
// }
// }
// Spacer()
// }
// }
// }
} }
.refreshable { .refreshable {
do { do {

View File

@ -11,6 +11,7 @@ import ClientKit
import ServicesKit import ServicesKit
import EnvironmentKit import EnvironmentKit
import WidgetsKit import WidgetsKit
import WaterfallGrid
struct UserProfileStatusesView: View { struct UserProfileStatusesView: View {
@EnvironmentObject private var applicationState: ApplicationState @EnvironmentObject private var applicationState: ApplicationState
@ -22,7 +23,7 @@ struct UserProfileStatusesView: View {
@State private var firstLoadFinished = false @State private var firstLoadFinished = false
@State private var statusViewModels: [StatusModel] = [] @State private var statusViewModels: [StatusModel] = []
private let defaultLimit = 20 private let defaultLimit = 40
private let imagePrefetcher = ImagePrefetcher(destination: .diskCache) private let imagePrefetcher = ImagePrefetcher(destination: .diskCache)
private let singleGrids = [GridItem(.flexible(), spacing: 10)] private let singleGrids = [GridItem(.flexible(), spacing: 10)]
private let dubleGrid = [GridItem(.flexible(), spacing: 10), GridItem(.flexible(), spacing: 0)] private let dubleGrid = [GridItem(.flexible(), spacing: 10), GridItem(.flexible(), spacing: 0)]
@ -54,7 +55,19 @@ struct UserProfileStatusesView: View {
.padding(.bottom, 8) .padding(.bottom, 8)
} }
} }
WaterfallGrid(self.statusViewModels, id: \.id) { item in
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)
// }
}
.gridStyle(columns: 3, spacing: 2)
.padding(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
/*
LazyVGrid(columns: self.applicationState.showGridOnUserProfile ? dubleGrid : singleGrids, spacing: 5) { LazyVGrid(columns: self.applicationState.showGridOnUserProfile ? dubleGrid : singleGrids, spacing: 5) {
ForEach(self.statusViewModels, id: \.id) { item in ForEach(self.statusViewModels, id: \.id) { item in
ImageRowAsync(statusViewModel: item, ImageRowAsync(statusViewModel: item,
@ -80,6 +93,7 @@ struct UserProfileStatusesView: View {
} }
} }
} }
*/
} else { } else {
LoadingIndicator() LoadingIndicator()
.onFirstAppear { .onFirstAppear {

View File

@ -59,8 +59,8 @@ struct ImageRowAsync: View {
} }
} }
} }
.if(self.imageScale == .orginalFullWidth) { .if(self.imageScale == .squareHalfWidth) {
$0.frame(width: self.imageWidth, height: self.imageHeight) $0.frame(width: self.imageWidth / 3, height: self.imageHeight / 3)
} }
} else { } else {
TabView(selection: $selected) { TabView(selection: $selected) {
@ -98,8 +98,8 @@ struct ImageRowAsync: View {
} }
} }
}) })
.if(self.imageScale == .orginalFullWidth) { .if(self.imageScale == .squareHalfWidth) {
$0.frame(width: self.imageWidth, height: self.imageHeight) $0.frame(width: self.imageWidth / 3, height: self.imageHeight / 3)
} }
.tabViewStyle(.page(indexDisplayMode: .never)) .tabViewStyle(.page(indexDisplayMode: .never))
.overlay(CustomPageTabViewStyleView(pages: self.statusViewModel.mediaAttachments, currentId: $selected)) .overlay(CustomPageTabViewStyleView(pages: self.statusViewModel.mediaAttachments, currentId: $selected))

View File

@ -155,10 +155,11 @@ struct ImageRowItemAsync: View {
private func imageView(image: Image) -> some View { private func imageView(image: Image) -> some View {
image image
.resizable() .resizable()
.scaledToFill() //.aspectRatio(contentMode: .fill)
.if(self.imageScale == .squareHalfWidth) { .aspectRatio(contentMode: .fit)
$0.frame(width: UIScreen.main.bounds.width / 2, height: UIScreen.main.bounds.width / 2).clipped() // .if(self.imageScale == .squareHalfWidth) {
} // $0.frame(width: UIScreen.main.bounds.width / 4, height: UIScreen.main.bounds.width / 4).clipped()
// }
.onTapGesture(count: 2) { .onTapGesture(count: 2) {
Task { Task {
// Update favourite in Pixelfed server. // Update favourite in Pixelfed server.

View File

@ -159,7 +159,7 @@ public struct BaseComposeView: View {
} }
.photosPicker(isPresented: $photosPickerVisible, .photosPicker(isPresented: $photosPickerVisible,
selection: $selectedItems, selection: $selectedItems,
maxSelectionCount: 4, maxSelectionCount: self.applicationState.statusMaxMediaAttachments,
matching: .images) matching: .images)
.fileImporter(isPresented: $isFileImporterPresented, .fileImporter(isPresented: $isFileImporterPresented,
allowedContentTypes: [.image], allowedContentTypes: [.image],