Initial commit

This commit is contained in:
Marcin Czachursk 2022-12-29 08:06:21 +01:00
parent 392ca9a68d
commit 2ee44fac78
17 changed files with 589 additions and 94 deletions

View File

@ -7,22 +7,33 @@
objects = {
/* Begin PBXBuildFile section */
F8341F90295C636C009C8EE6 /* UIImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8341F8F295C636C009C8EE6 /* UIImage.swift */; };
F8341F92295C63BB009C8EE6 /* ImageStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8341F91295C63BB009C8EE6 /* ImageStatus.swift */; };
F88C246C295C37B80006098B /* VernissageApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = F88C246B295C37B80006098B /* VernissageApp.swift */; };
F88C246E295C37B80006098B /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F88C246D295C37B80006098B /* ContentView.swift */; };
F88C246E295C37B80006098B /* MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F88C246D295C37B80006098B /* MainView.swift */; };
F88C2470295C37BB0006098B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F88C246F295C37BB0006098B /* Assets.xcassets */; };
F88C2473295C37BB0006098B /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F88C2472295C37BB0006098B /* Preview Assets.xcassets */; };
F88C2475295C37BB0006098B /* Persistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = F88C2474295C37BB0006098B /* Persistence.swift */; };
F88C2478295C37BB0006098B /* Vernissage.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = F88C2476295C37BB0006098B /* Vernissage.xcdatamodeld */; };
F88C2480295C38400006098B /* MastodonSwift in Frameworks */ = {isa = PBXBuildFile; productRef = F88C247F295C38400006098B /* MastodonSwift */; };
F88C2482295C3A4F0006098B /* ImageDetailsModalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F88C2481295C3A4F0006098B /* ImageDetailsModalView.swift */; };
F88C2484295C3A870006098B /* DismissButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F88C2483295C3A870006098B /* DismissButtonView.swift */; };
F88C2486295C48030006098B /* HTMLFotmattedText.swift in Sources */ = {isa = PBXBuildFile; fileRef = F88C2485295C48030006098B /* HTMLFotmattedText.swift */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
F8341F8F295C636C009C8EE6 /* UIImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIImage.swift; sourceTree = "<group>"; };
F8341F91295C63BB009C8EE6 /* ImageStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageStatus.swift; sourceTree = "<group>"; };
F88C2468295C37B80006098B /* Vernissage.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Vernissage.app; sourceTree = BUILT_PRODUCTS_DIR; };
F88C246B295C37B80006098B /* VernissageApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VernissageApp.swift; sourceTree = "<group>"; };
F88C246D295C37B80006098B /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
F88C246D295C37B80006098B /* MainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainView.swift; sourceTree = "<group>"; };
F88C246F295C37BB0006098B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
F88C2472295C37BB0006098B /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
F88C2474295C37BB0006098B /* Persistence.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Persistence.swift; sourceTree = "<group>"; };
F88C2477295C37BB0006098B /* Vernissage.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Vernissage.xcdatamodel; sourceTree = "<group>"; };
F88C2481295C3A4F0006098B /* ImageDetailsModalView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageDetailsModalView.swift; sourceTree = "<group>"; };
F88C2483295C3A870006098B /* DismissButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DismissButtonView.swift; sourceTree = "<group>"; };
F88C2485295C48030006098B /* HTMLFotmattedText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTMLFotmattedText.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -30,12 +41,55 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
F88C2480295C38400006098B /* MastodonSwift in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
F8341F93295C63E2009C8EE6 /* Views */ = {
isa = PBXGroup;
children = (
F88C2483295C3A870006098B /* DismissButtonView.swift */,
F88C2481295C3A4F0006098B /* ImageDetailsModalView.swift */,
F88C246D295C37B80006098B /* MainView.swift */,
);
path = Views;
sourceTree = "<group>";
};
F8341F94295C63FE009C8EE6 /* Extensions */ = {
isa = PBXGroup;
children = (
F8341F8F295C636C009C8EE6 /* UIImage.swift */,
);
path = Extensions;
sourceTree = "<group>";
};
F8341F95295C640C009C8EE6 /* Models */ = {
isa = PBXGroup;
children = (
F8341F91295C63BB009C8EE6 /* ImageStatus.swift */,
);
path = Models;
sourceTree = "<group>";
};
F8341F96295C6427009C8EE6 /* CoreData */ = {
isa = PBXGroup;
children = (
F88C2474295C37BB0006098B /* Persistence.swift */,
);
path = CoreData;
sourceTree = "<group>";
};
F8341F97295C6434009C8EE6 /* Formatters */ = {
isa = PBXGroup;
children = (
F88C2485295C48030006098B /* HTMLFotmattedText.swift */,
);
path = Formatters;
sourceTree = "<group>";
};
F88C245F295C37B80006098B = {
isa = PBXGroup;
children = (
@ -55,10 +109,13 @@
F88C246A295C37B80006098B /* Vernissage */ = {
isa = PBXGroup;
children = (
F8341F97295C6434009C8EE6 /* Formatters */,
F8341F96295C6427009C8EE6 /* CoreData */,
F8341F95295C640C009C8EE6 /* Models */,
F8341F94295C63FE009C8EE6 /* Extensions */,
F8341F93295C63E2009C8EE6 /* Views */,
F88C246B295C37B80006098B /* VernissageApp.swift */,
F88C246D295C37B80006098B /* ContentView.swift */,
F88C246F295C37BB0006098B /* Assets.xcassets */,
F88C2474295C37BB0006098B /* Persistence.swift */,
F88C2476295C37BB0006098B /* Vernissage.xcdatamodeld */,
F88C2471295C37BB0006098B /* Preview Content */,
);
@ -89,6 +146,9 @@
dependencies = (
);
name = Vernissage;
packageProductDependencies = (
F88C247F295C38400006098B /* MastodonSwift */,
);
productName = Vernissage;
productReference = F88C2468295C37B80006098B /* Vernissage.app */;
productType = "com.apple.product-type.application";
@ -117,6 +177,9 @@
Base,
);
mainGroup = F88C245F295C37B80006098B;
packageReferences = (
F88C247E295C38400006098B /* XCRemoteSwiftPackageReference "Mastodon" */,
);
productRefGroup = F88C2469295C37B80006098B /* Products */;
projectDirPath = "";
projectRoot = "";
@ -144,8 +207,13 @@
buildActionMask = 2147483647;
files = (
F88C2475295C37BB0006098B /* Persistence.swift in Sources */,
F88C246E295C37B80006098B /* ContentView.swift in Sources */,
F8341F92295C63BB009C8EE6 /* ImageStatus.swift in Sources */,
F8341F90295C636C009C8EE6 /* UIImage.swift in Sources */,
F88C2484295C3A870006098B /* DismissButtonView.swift in Sources */,
F88C246E295C37B80006098B /* MainView.swift in Sources */,
F88C2478295C37BB0006098B /* Vernissage.xcdatamodeld in Sources */,
F88C2482295C3A4F0006098B /* ImageDetailsModalView.swift in Sources */,
F88C2486295C48030006098B /* HTMLFotmattedText.swift in Sources */,
F88C246C295C37B80006098B /* VernissageApp.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -283,6 +351,7 @@
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@ -312,6 +381,7 @@
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@ -348,6 +418,25 @@
};
/* End XCConfigurationList section */
/* Begin XCRemoteSwiftPackageReference section */
F88C247E295C38400006098B /* XCRemoteSwiftPackageReference "Mastodon" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/Swiftodon/Mastodon.swift";
requirement = {
branch = main;
kind = branch;
};
};
/* End XCRemoteSwiftPackageReference section */
/* Begin XCSwiftPackageProductDependency section */
F88C247F295C38400006098B /* MastodonSwift */ = {
isa = XCSwiftPackageProductDependency;
package = F88C247E295C38400006098B /* XCRemoteSwiftPackageReference "Mastodon" */;
productName = MastodonSwift;
};
/* End XCSwiftPackageProductDependency section */
/* Begin XCVersionGroup section */
F88C2476295C37BB0006098B /* Vernissage.xcdatamodeld */ = {
isa = XCVersionGroup;

View File

@ -1,6 +1,7 @@
{
"images" : [
{
"filename" : "icon.png",
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0.000",
"green" : "0.000",
"red" : "0.000"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "1.000",
"green" : "1.000",
"red" : "1.000"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "255",
"green" : "143",
"red" : "0"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "255",
"green" : "164",
"red" : "0"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0",
"green" : "0",
"red" : "0"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "255",
"green" : "255",
"red" : "255"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "160",
"green" : "160",
"red" : "160"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "160",
"green" : "160",
"red" : "160"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -1,88 +0,0 @@
//
// https://mczachurski.dev
// Copyright © 2022 Marcin Czachurski and the repository contributors.
// Licensed under the MIT License.
//
import SwiftUI
import CoreData
struct ContentView: View {
@Environment(\.managedObjectContext) private var viewContext
@FetchRequest(
sortDescriptors: [NSSortDescriptor(keyPath: \Item.timestamp, ascending: true)],
animation: .default)
private var items: FetchedResults<Item>
var body: some View {
NavigationView {
List {
ForEach(items) { item in
NavigationLink {
Text("Item at \(item.timestamp!, formatter: itemFormatter)")
} label: {
Text(item.timestamp!, formatter: itemFormatter)
}
}
.onDelete(perform: deleteItems)
}
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
EditButton()
}
ToolbarItem {
Button(action: addItem) {
Label("Add Item", systemImage: "plus")
}
}
}
Text("Select an item")
}
}
private func addItem() {
withAnimation {
let newItem = Item(context: viewContext)
newItem.timestamp = Date()
do {
try viewContext.save()
} catch {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
}
}
private func deleteItems(offsets: IndexSet) {
withAnimation {
offsets.map { items[$0] }.forEach(viewContext.delete)
do {
try viewContext.save()
} catch {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
}
}
}
private let itemFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateStyle = .short
formatter.timeStyle = .medium
return formatter
}()
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
}
}

View File

@ -0,0 +1,27 @@
//
// https://mczachurski.dev
// Copyright © 2022 Marcin Czachurski and the repository contributors.
// Licensed under the MIT License.
//
import Foundation
import UIKit
public extension UIImage {
func getExifData() -> CFDictionary? {
var exifData: CFDictionary? = nil
if let data = self.jpegData(compressionQuality: 1.0) {
data.withUnsafeBytes {
let bytes = $0.baseAddress?.assumingMemoryBound(to: UInt8.self)
if let cfData = CFDataCreate(kCFAllocatorDefault, bytes, data.count),
let source = CGImageSourceCreateWithData(cfData, nil) {
exifData = CGImageSourceCopyPropertiesAtIndex(source, 0, nil)
}
}
}
return exifData
}
}

View File

@ -0,0 +1,76 @@
//
// https://mczachurski.dev
// Copyright © 2022 Marcin Czachurski and the repository contributors.
// Licensed under the MIT License.
//
import UIKit
import SwiftUI
struct HTMLFormattedText: UIViewRepresentable {
let text: String
private let textView = UITextView()
init(_ content: String) {
self.text = content
}
func makeUIView(context: UIViewRepresentableContext<Self>) -> UITextView {
textView.widthAnchor.constraint(equalToConstant:UIScreen.main.bounds.width - 16).isActive = true
textView.isSelectable = false
textView.isUserInteractionEnabled = false
textView.translatesAutoresizingMaskIntoConstraints = false
textView.isScrollEnabled = false
return textView
}
func updateUIView(_ uiView: UITextView, context: UIViewRepresentableContext<Self>) {
DispatchQueue.main.async {
if let attributeText = self.converHTML(text: text) {
textView.attributedText = attributeText
} else {
textView.text = ""
}
}
}
private func converHTML(text: String) -> NSAttributedString?{
guard let data = text.data(using: .utf8) else {
return nil
}
let largeAttributes = [
NSAttributedString.Key.font: UIFont.systemFont(ofSize: 16),
NSAttributedString.Key.foregroundColor: UIColor(Color("mainTextColor"))
]
let linkAttributes = [
NSAttributedString.Key.font: UIFont.systemFont(ofSize: 16),
NSAttributedString.Key.foregroundColor: UIColor(Color("linkColor"))
]
if let attributedString = try? NSMutableAttributedString(data: data, options: [.documentType: NSAttributedString.DocumentType.html], documentAttributes: nil) {
attributedString.enumerateAttributes(in: NSRange(0..<attributedString.length)) { value, range, stop in
attributedString.setAttributes(largeAttributes, range: range)
if value.keys.contains(NSAttributedString.Key.link) {
attributedString.setAttributes(linkAttributes, range: range)
}
}
return attributedString
} else{
return nil
}
}
}
struct HTMLFotmattedText_Previews: PreviewProvider {
static var previews: some View {
HTMLFormattedText("<p>Danish-made 1st class kebab</p><p>Say yes thanks to 2kg. delicious kebab, which is confused and cooked.</p><p>Yes thanks for 149.95</p><p>Now you can make the most delicious sandwiches, kebab mix and much more at home</p>")
}
}

View File

@ -0,0 +1,15 @@
//
// https://mczachurski.dev
// Copyright © 2022 Marcin Czachurski and the repository contributors.
// Licensed under the MIT License.
//
import Foundation
import UIKit
import MastodonSwift
public struct ImageStatus: Identifiable {
public let id: String
public let image: UIImage
public let status: Status
}

View File

@ -13,7 +13,7 @@ struct VernissageApp: App {
var body: some Scene {
WindowGroup {
ContentView()
MainView()
.environment(\.managedObjectContext, persistenceController.container.viewContext)
}
}

View File

@ -0,0 +1,30 @@
//
// https://mczachurski.dev
// Copyright © 2022 Marcin Czachurski and the repository contributors.
// Licensed under the MIT License.
//
import SwiftUI
struct DismissButtonView: View {
var action: () -> ()
public init(_ action: @escaping () -> ()) {
self.action = action
}
public var body: some View {
Button(action: action) {
RoundedRectangle(cornerRadius: 16)
.fill(Color.gray)
.frame(width: 50, height: 5)
}
}
}
struct DismissButtonView_Previews: PreviewProvider {
static var previews: some View {
DismissButtonView { }
}
}

View File

@ -0,0 +1,84 @@
//
// https://mczachurski.dev
// Copyright © 2022 Marcin Czachurski and the repository contributors.
// Licensed under the MIT License.
//
import SwiftUI
import MastodonSwift
struct ImageDetailsModalView: View {
@Environment(\.dismiss) private var dismiss
@State public var current: ImageStatus
var body: some View {
VStack {
DismissButtonView { dismiss() }
ScrollView {
VStack (alignment: .leading) {
Image(uiImage: current.image)
.resizable().aspectRatio(contentMode: .fit)
.frame(maxWidth: .infinity)
.onTapGesture {
dismiss()
}
VStack(alignment: .leading) {
HStack (alignment: .center) {
AsyncImage(url: current.status.account?.avatar) { image in
image
.resizable()
.clipShape(Circle())
.shadow(radius: 10)
.aspectRatio(contentMode: .fit)
} placeholder: {
Color.gray
}
.frame(height: 60)
.frame(width: 60)
VStack (alignment: .leading) {
Text(current.status.account?.displayName ?? current.status.account?.username ?? "")
.foregroundColor(Color("displayNameColor"))
Text("@\(current.status.account?.username ?? "unknown")")
.foregroundColor(Color("userNameColor"))
.font(.footnote)
}
.padding(.leading, 8)
}
HTMLFormattedText(current.status.content)
HStack (alignment: .top) {
Image(systemName: current.status.favourited ? "heart.fill" : "heart")
Text("\(current.status.favouritesCount) likes")
Image(systemName: "arrow.2.squarepath")
.padding(.leading, 16)
Text("\(current.status.reblogsCount) boosts")
}
.font(.footnote)
.foregroundColor(Color("mainTextColor"))
}
.padding(8)
}
}
}
.gesture(
DragGesture().onEnded { value in
if value.location.y - value.startLocation.y > 150 {
dismiss()
}
}
)
}
}
struct FullScreenModalView_Previews: PreviewProvider {
static var previews: some View {
Text("")
// FullScreenModalView(current: ImageStatus(id: "123", image: UIImage(), status: Status(from: <#T##Decoder#>)))
}
}

View File

@ -0,0 +1,109 @@
//
// https://mczachurski.dev
// Copyright © 2022 Marcin Czachurski and the repository contributors.
// Licensed under the MIT License.
//
import SwiftUI
import UIKit
import CoreData
import MastodonSwift
struct MainView: View {
@Environment(\.managedObjectContext) private var viewContext
@FetchRequest(
sortDescriptors: [NSSortDescriptor(keyPath: \Item.timestamp, ascending: true)],
animation: .default)
private var items: FetchedResults<Item>
@State private var statuses: [Status] = []
@State private var images: [ImageStatus] = []
@State private var showDetails: Bool = false
@State private var current: ImageStatus? = nil
var body: some View {
ScrollView {
ForEach(images) { item in
Image(uiImage: item.image)
.resizable().aspectRatio(contentMode: .fit)
.onTapGesture {
current = item
showDetails.toggle()
}
.fullScreenCover(item: $current) { item in
ImageDetailsModalView(current: item)
}
}
}
.foregroundColor(Color.black)
.background(Color.black)
.edgesIgnoringSafeArea(.all)
.task {
do {
try await loadData()
} catch {
print("Error", error)
}
}
}
private func loadData() async throws {
let accessToken = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJhdWQiOiI2MTQwOCIsImp0aSI6IjZjMDg4N2ZlZThjZjBmZjQ1N2RjZDQ5MTU2YjE2NzYyYmQ2MDQ1NDQ1MmEwYzEyMzVmNDA3YjY2YjFhYjU3ZjBjMTY2YjFmZmIyNjJlZDg2IiwiaWF0IjoxNjcyMDU5MDYwLjI3NDgyMSwibmJmIjoxNjcyMDU5MDYwLjI3NDgyNCwiZXhwIjoxNzAzNTk1MDYwLjI1MzM1Nywic3ViIjoiNjc4MjMiLCJzY29wZXMiOlsicmVhZCIsIndyaXRlIiwiZm9sbG93Il19.kGvg3lW8lF1X1mOTdgGgoXNyzwUIJz5hz5RJKK_WiSoBWDQNadhZDty7XMNF0IAPjxOSi6UaIx2av7_eH_65aNlKFw89bkm8bT_zFQW2V0KbADJ-NmE6X0B_NgU2CNoF5IPn6bhCFHCKMtV6MWAQ_db6DT-LXaGemMY3QimcJzCqQuXI_1ouiZ235T297uEPNTrLwtLq-x_UoO-wx254LStBalDIGDVHAa4by9IT-mvu-QXz7k8pH2NHKoX-9Ql_Y3G9RJJNqoOmWMU45Dyo2HaJKKEb1tkeJ9tA3LIYgbwnEbG2PJ7CE8CXxtakiCIflJZpzzOmq1jXLAsCJ1mHnc77o7NfMaB_hY-f8PEI6d2ttOdH8bNlreF2avznNAIVHg_bf-yv_4wKUCUe0QZMG_yWqOwOk6lyruvboSGKuI5RnYsJbXBoJTGMLON6jVmtiKPbHy-9jNcfFgShAc3D5kTO-8Avj9_RquqEh1TQF_S4ljmganxKzMihyMDLK1OVcXzCFO6FKlCw7YKvbfJk1Qrn9kPBrVDM5jzIyXAmqRd1ivcE9nAdYb2l7KnxW_pi31uT0IdJMpTkZrUQSDMyEnj0HgV6Yd5BDlLG6Cnk8GXATTcU-a1pgE13OtWsCpD2cZQm-tOsFHWBDvY-BA0RtTvQAyEUxRIP9NjHe8rSR90"
let client = MastodonClient(baseURL: URL(string: "https://pixelfed.social")!)
.getAuthenticated(token: accessToken)
self.statuses = try await client.getHomeTimeline()
var imagesCache: [ImageStatus] = []
for item in self.statuses {
let imageStatus = try await self.fetchImage(status: item, accessToken: accessToken)
if let imageStatus {
imagesCache.append(imageStatus)
}
}
self.images = imagesCache
}
public func fetchImage(status: Status, accessToken: String) async throws -> ImageStatus? {
guard let url = status.mediaAttachments.first?.url, let id = status.mediaAttachments.first?.id else {
return nil
}
let urlRequest = URLRequest(url: url)
// Fetching data.
let (data, response) = try await URLSession.shared.data(for: urlRequest)
guard (response as? HTTPURLResponse)?.statusCode == 200 else {
return nil
}
// Decoding JSON.
let image = UIImage(data: data)
guard let image = image else {
return nil
}
/*
var exif = image.getExifData()
if let dict = exif as? [String: AnyObject] {
dict.keys.map { key in
print(key)
print(dict[key])
}
}
*/
return ImageStatus(id: id,image: image, status: status)
}
}
struct MainView_Previews: PreviewProvider {
static var previews: some View {
MainView().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
}
}

BIN
icon.afdesign Normal file

Binary file not shown.