iOS client for Grain grain.social
ios photography atproto
7
fork

Configure Feed

Select the types of activity you want to include in your feed.

style: trailing commas, operator spacing, and indentation fixes

+27 -22
+25 -20
Grain/Utilities/BlueskyPost.swift
··· 11 11 } 12 12 13 13 enum BlueskyPost { 14 - 15 14 /// Create a cross-post to Bluesky with images, location, and description. 16 15 /// Mirrors the web client's `createBskyPost()` in `bsky-post.ts`. 17 16 static func create( ··· 43 42 "$type": AnyCodable(img.blob.type ?? "blob"), 44 43 "ref": AnyCodable(["$link": AnyCodable(img.blob.ref?.link ?? "")] as [String: AnyCodable]), 45 44 "mimeType": AnyCodable(img.blob.mimeType ?? "image/jpeg"), 46 - "size": AnyCodable(img.blob.size ?? 0) 45 + "size": AnyCodable(img.blob.size ?? 0), 47 46 ] 48 47 imageEmbeds.append([ 49 48 "image": AnyCodable(blobDict), 50 49 "alt": AnyCodable(img.alt), 51 50 "aspectRatio": AnyCodable([ 52 51 "width": AnyCodable(img.width), 53 - "height": AnyCodable(img.height) 54 - ] as [String: AnyCodable]) 52 + "height": AnyCodable(img.height), 53 + ] as [String: AnyCodable]), 55 54 ]) 56 55 logger.info(" image blob: type=\(img.blob.type ?? "nil"), ref=\(img.blob.ref?.link ?? "nil"), size=\(img.blob.size ?? 0)") 57 56 } ··· 61 60 var record: [String: AnyCodable] = [ 62 61 "text": AnyCodable(postText), 63 62 "tags": AnyCodable(["grainsocial"] as [String]), 64 - "createdAt": AnyCodable(DateFormatting.nowISO()) 63 + "createdAt": AnyCodable(DateFormatting.nowISO()), 65 64 ] 66 65 67 66 if !facets.isEmpty { 68 67 let facetDicts: [[String: AnyCodable]] = facets.map { facet in 69 68 let featureDicts: [[String: AnyCodable]] = facet.features.map { feature in 70 69 switch feature { 71 - case .link(let uri): 72 - return ["$type": AnyCodable("app.bsky.richtext.facet#link"), "uri": AnyCodable(uri)] 73 - case .mention(let did): 74 - return ["$type": AnyCodable("app.bsky.richtext.facet#mention"), "did": AnyCodable(did)] 75 - case .tag(let tag): 76 - return ["$type": AnyCodable("app.bsky.richtext.facet#tag"), "tag": AnyCodable(tag)] 70 + case let .link(uri): 71 + ["$type": AnyCodable("app.bsky.richtext.facet#link"), "uri": AnyCodable(uri)] 72 + case let .mention(did): 73 + ["$type": AnyCodable("app.bsky.richtext.facet#mention"), "did": AnyCodable(did)] 74 + case let .tag(tag): 75 + ["$type": AnyCodable("app.bsky.richtext.facet#tag"), "tag": AnyCodable(tag)] 77 76 } 78 77 } 79 78 return [ 80 79 "index": AnyCodable([ 81 80 "byteStart": AnyCodable(facet.index.byteStart), 82 - "byteEnd": AnyCodable(facet.index.byteEnd) 81 + "byteEnd": AnyCodable(facet.index.byteEnd), 83 82 ] as [String: AnyCodable]), 84 - "features": AnyCodable(featureDicts as [[String: AnyCodable]]) 83 + "features": AnyCodable(featureDicts as [[String: AnyCodable]]), 85 84 ] 86 85 } 87 86 record["facets"] = AnyCodable(facetDicts as [[String: AnyCodable]]) ··· 90 89 if !imageEmbeds.isEmpty { 91 90 record["embed"] = AnyCodable([ 92 91 "$type": AnyCodable("app.bsky.embed.images"), 93 - "images": AnyCodable(imageEmbeds as [[String: AnyCodable]]) 92 + "images": AnyCodable(imageEmbeds as [[String: AnyCodable]]), 94 93 ] as [String: AnyCodable]) 95 94 } 96 95 97 96 // 5. Log the full JSON for debugging 98 97 if let jsonData = try? JSONEncoder().encode(AnyCodable(record)), 99 - let jsonStr = String(data: jsonData, encoding: .utf8) { 98 + let jsonStr = String(data: jsonData, encoding: .utf8) 99 + { 100 100 logger.info(" record JSON: \(jsonStr)") 101 101 } 102 102 ··· 159 159 truncated = String(truncated.prefix(max(0, maxDescGraphemes - 1))) + "…" 160 160 } 161 161 // Truncate further to fit byte limit 162 - while truncated.utf8.count > maxDescBytes && !truncated.isEmpty { 162 + while truncated.utf8.count > maxDescBytes, !truncated.isEmpty { 163 163 truncated = String(truncated.dropLast(2)) + "…" 164 164 } 165 165 if !truncated.isEmpty { ··· 192 192 } 193 193 194 194 func isRangeClaimed(_ start: Int, _ end: Int) -> Bool { 195 - for i in start..<end where claimed.contains(i) { return true } 195 + for i in start ..< end where claimed.contains(i) { 196 + return true 197 + } 196 198 return false 197 199 } 198 200 199 201 func claimRange(_ start: Int, _ end: Int) { 200 - for i in start..<end { claimed.insert(i) } 202 + for i in start ..< end { 203 + claimed.insert(i) 204 + } 201 205 } 202 206 203 207 let nsText = text as NSString ··· 262 266 /// Same as web's `resolveHandle()` in `bsky-post.ts`. 263 267 private static func resolveHandle(_ handle: String) async -> String? { 264 268 guard let encoded = handle.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed), 265 - let url = URL(string: "https://public.api.bsky.app/xrpc/com.atproto.identity.resolveHandle?handle=\(encoded)") else { 269 + let url = URL(string: "https://public.api.bsky.app/xrpc/com.atproto.identity.resolveHandle?handle=\(encoded)") 270 + else { 266 271 return nil 267 272 } 268 273 269 274 do { 270 275 let (data, response) = try await URLSession.shared.data(from: url) 271 276 guard let httpResponse = response as? HTTPURLResponse, 272 - (200...299).contains(httpResponse.statusCode) else { return nil } 277 + (200 ... 299).contains(httpResponse.statusCode) else { return nil } 273 278 let json = try JSONDecoder().decode([String: String].self, from: data) 274 279 return json["did"] 275 280 } catch {
+1 -1
Grain/Views/Profile/ProfileView.swift
··· 165 165 .frame(maxWidth: .infinity) 166 166 } 167 167 .buttonStyle(.bordered) 168 - .tint(.primary) 168 + .tint(.primary) 169 169 } 170 170 } 171 171 .padding(.horizontal)
+1 -1
Grain/Views/Settings/SettingsView.swift
··· 23 23 } 24 24 } 25 25 26 - Section { 26 + Section { 27 27 Toggle("Include location", isOn: $includeLocation) 28 28 .onChange(of: includeLocation) { 29 29 guard hasLoadedPrefs else { return }