diff --git a/CHANGELOG.md b/CHANGELOG.md index ed554cc..8c72dda 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,10 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +### Added + +- Brand new community app icon created by @MrSebSin + ## [2023.12.0] ### Added diff --git a/Lunar.xcodeproj/project.pbxproj b/Lunar.xcodeproj/project.pbxproj index 692b545..dc063ab 100644 --- a/Lunar.xcodeproj/project.pbxproj +++ b/Lunar.xcodeproj/project.pbxproj @@ -274,6 +274,11 @@ 3CF717FB2AF42ED2005030D8 /* RealmCommunity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CF717FA2AF42ED2005030D8 /* RealmCommunity.swift */; }; 3CF717FD2AF437D4005030D8 /* LegacyCommunitiesFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CF717FC2AF437D3005030D8 /* LegacyCommunitiesFetcher.swift */; }; 3CF717FF2AF44704005030D8 /* LegacyCommunityRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CF717FE2AF44704005030D8 /* LegacyCommunityRowView.swift */; }; + 3CF831BF2B1F9D960036BCE0 /* APIService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CF831BE2B1F9D960036BCE0 /* APIService.swift */; }; + 3CF831C52B1FA3E40036BCE0 /* ParameterStructures.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CF831C42B1FA3E40036BCE0 /* ParameterStructures.swift */; }; + 3CF831C72B1FAE390036BCE0 /* PostSenderM.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CF831C62B1FAE390036BCE0 /* PostSenderM.swift */; }; + 3CF831C92B1FAE5A0036BCE0 /* AppDelegateM.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CF831C82B1FAE5A0036BCE0 /* AppDelegateM.swift */; }; + 3CF831CB2B1FAE730036BCE0 /* CreatePostPopoverViewM.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CF831CA2B1FAE730036BCE0 /* CreatePostPopoverViewM.swift */; }; 3CFA769B2B127E76002B88B2 /* MockData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CFA769A2B127E76002B88B2 /* MockData.swift */; }; 3CFB89E82AF19DC3001BB9EB /* CreateCommentPopover.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CFB89E72AF19DC3001BB9EB /* CreateCommentPopover.swift */; }; 3CFE04BF2A7FEA71007BAD81 /* SwiftSoup in Frameworks */ = {isa = PBXBuildFile; productRef = 3CFE04BE2A7FEA71007BAD81 /* SwiftSoup */; }; @@ -552,6 +557,11 @@ 3CF717FA2AF42ED2005030D8 /* RealmCommunity.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RealmCommunity.swift; sourceTree = ""; }; 3CF717FC2AF437D3005030D8 /* LegacyCommunitiesFetcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LegacyCommunitiesFetcher.swift; sourceTree = ""; }; 3CF717FE2AF44704005030D8 /* LegacyCommunityRowView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LegacyCommunityRowView.swift; sourceTree = ""; }; + 3CF831BE2B1F9D960036BCE0 /* APIService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APIService.swift; sourceTree = ""; }; + 3CF831C42B1FA3E40036BCE0 /* ParameterStructures.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParameterStructures.swift; sourceTree = ""; }; + 3CF831C62B1FAE390036BCE0 /* PostSenderM.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PostSenderM.swift; sourceTree = ""; }; + 3CF831C82B1FAE5A0036BCE0 /* AppDelegateM.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegateM.swift; sourceTree = ""; }; + 3CF831CA2B1FAE730036BCE0 /* CreatePostPopoverViewM.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CreatePostPopoverViewM.swift; sourceTree = ""; }; 3CFA769A2B127E76002B88B2 /* MockData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockData.swift; sourceTree = ""; }; 3CFB89E72AF19DC3001BB9EB /* CreateCommentPopover.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CreateCommentPopover.swift; sourceTree = ""; }; 3CFE04C22A7FF0EB007BAD81 /* KbinThreadsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KbinThreadsModel.swift; sourceTree = ""; }; @@ -704,6 +714,7 @@ 3C62C23F2A72AABD003BAC5B /* Info.plist */, 3C62C23E2A72AA4D003BAC5B /* Lunar.entitlements */, 3C808E222A9A8C95008FA62E /* LunarDebug.entitlements */, + 3CF831C32B1FA3D10036BCE0 /* APIService */, 3CFA769A2B127E76002B88B2 /* MockData.swift */, 3C2EA6C22AFFFA42007AC12D /* WidgetLink.swift */, 3C4F09FF2A54399F009DF8AB /* LunarAppEntryView.swift */, @@ -1132,6 +1143,18 @@ path = "Account Tab"; sourceTree = ""; }; + 3CF831C32B1FA3D10036BCE0 /* APIService */ = { + isa = PBXGroup; + children = ( + 3CF831CA2B1FAE730036BCE0 /* CreatePostPopoverViewM.swift */, + 3CF831C82B1FAE5A0036BCE0 /* AppDelegateM.swift */, + 3CF831C62B1FAE390036BCE0 /* PostSenderM.swift */, + 3CF831BE2B1F9D960036BCE0 /* APIService.swift */, + 3CF831C42B1FA3E40036BCE0 /* ParameterStructures.swift */, + ); + path = APIService; + sourceTree = ""; + }; 3CFE04C42A7FF1E7007BAD81 /* Kbin */ = { isa = PBXGroup; children = ( @@ -1272,15 +1295,15 @@ ); mainGroup = 3C4F09F32A54399F009DF8AB; packageReferences = ( - 3C4B7DD92A58AD23003C8192 /* XCRemoteSwiftPackageReference "Alamofire.git" */, - 3CA95F062A7ACF3B00626353 /* XCRemoteSwiftPackageReference "Nuke.git" */, + 3C4B7DD92A58AD23003C8192 /* XCRemoteSwiftPackageReference "Alamofire" */, + 3CA95F062A7ACF3B00626353 /* XCRemoteSwiftPackageReference "Nuke" */, 3CFE04BD2A7FEA71007BAD81 /* XCRemoteSwiftPackageReference "SwiftSoup" */, 3C23F03C2AAA66E000AACA46 /* XCRemoteSwiftPackageReference "Pulse" */, 3CEB2C082AB2F3A50013609E /* XCRemoteSwiftPackageReference "Defaults" */, - 3CEB2C112AB2F45F0013609E /* XCRemoteSwiftPackageReference "SFSafeSymbols.git" */, + 3CEB2C112AB2F45F0013609E /* XCRemoteSwiftPackageReference "SFSafeSymbols" */, 3CC420082AB37BCB00BE1612 /* XCRemoteSwiftPackageReference "WhatsNewKit" */, 3CC4200B2AB37DDA00BE1612 /* XCRemoteSwiftPackageReference "BetterSafariView" */, - 3CE69E942ABDCA8A00E2821E /* XCRemoteSwiftPackageReference "realm-swift.git" */, + 3CE69E942ABDCA8A00E2821E /* XCRemoteSwiftPackageReference "realm-swift" */, 3CC3E4192ADC054000F7F9A9 /* XCRemoteSwiftPackageReference "shiny" */, 3C20B37D2AEB27F8003F578F /* XCRemoteSwiftPackageReference "LocalConsole" */, 3CEBF46A2B09426E00469008 /* XCRemoteSwiftPackageReference "swift-markdown-ui" */, @@ -1477,6 +1500,7 @@ 3CEF4CC22A69B38B002B83C0 /* SubscribedCommunitiesView.swift in Sources */, 3CB046F22ADAF3C00011BFEC /* CommentsViewWorkaroundWarning.swift in Sources */, 3CA8A0C92AE5DBA900EEABD3 /* RealmBrowser.swift in Sources */, + 3CF831C52B1FA3E40036BCE0 /* ParameterStructures.swift in Sources */, 3CBF88122AEEC8C20058B14D /* LegacyInPostActionsView.swift in Sources */, 3C4DB8BE2AB701B2000137CF /* AppDelegate.swift in Sources */, 3CC3E4412AE047FE00F7F9A9 /* RealmPost.swift in Sources */, @@ -1504,6 +1528,8 @@ 3C6ACC202AE52FD20073288E /* RichLinks.swift in Sources */, 3C54C0A12ABB995E00F1BADE /* DefaultValues.swift in Sources */, 3C5ECB1F2A75ABFE002BA561 /* SiteInfoFetcher.swift in Sources */, + 3CF831C72B1FAE390036BCE0 /* PostSenderM.swift in Sources */, + 3CF831CB2B1FAE730036BCE0 /* CreatePostPopoverViewM.swift in Sources */, 3C9E548C2A94A3DC0093DDC7 /* CommentSender.swift in Sources */, 3CF43AA92AE1A42A006C8E84 /* PostsView.swift in Sources */, 3CC09CE12B0E95490036422F /* PrivateMessage.swift in Sources */, @@ -1547,6 +1573,7 @@ 3C2A80592AFD88C70077B7D7 /* HiddenPostsGuardView.swift in Sources */, 3C9E54982A953FB60093DDC7 /* SettingsSplashScreenView.swift in Sources */, 3CEF4CD52A69B798002B83C0 /* URLParser.swift in Sources */, + 3CF831C92B1FAE5A0036BCE0 /* AppDelegateM.swift in Sources */, 3CC3E4292ADC7B7A00F7F9A9 /* LocalImagePopoverView.swift in Sources */, 3CB9C7222AA23F7400FB5A10 /* CommunityObject.swift in Sources */, 3CA92E3B2A543BF000688DEB /* CommunityModel.swift in Sources */, @@ -1586,6 +1613,7 @@ 3CBCCDF62A589F2C00307AC1 /* CommentsView.swift in Sources */, 3C65885F2A621C6100A46DA3 /* ExploreCommunitiesView.swift in Sources */, 3CC09CDD2B0E92D90036422F /* PrivateMessageModel.swift in Sources */, + 3CF831BF2B1F9D960036BCE0 /* APIService.swift in Sources */, 3CC3E4352ADDD74D00F7F9A9 /* PostSiteMetadataFetcher.swift in Sources */, 3C6ACC1E2AE426D40073288E /* AboutLemmyInfo.swift in Sources */, 3C64E7C92AF700E9003BB42E /* WhatsNew_0_0_0.swift in Sources */, @@ -2122,7 +2150,7 @@ minimumVersion = 4.0.3; }; }; - 3C4B7DD92A58AD23003C8192 /* XCRemoteSwiftPackageReference "Alamofire.git" */ = { + 3C4B7DD92A58AD23003C8192 /* XCRemoteSwiftPackageReference "Alamofire" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/Alamofire/Alamofire.git"; requirement = { @@ -2194,7 +2222,7 @@ kind = branch; }; }; - 3CEB2C112AB2F45F0013609E /* XCRemoteSwiftPackageReference "SFSafeSymbols.git" */ = { + 3CEB2C112AB2F45F0013609E /* XCRemoteSwiftPackageReference "SFSafeSymbols" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/SFSafeSymbols/SFSafeSymbols.git"; requirement = { @@ -2246,12 +2274,12 @@ }; 3C2EA6C82B002322007AC12D /* SFSafeSymbols */ = { isa = XCSwiftPackageProductDependency; - package = 3CEB2C112AB2F45F0013609E /* XCRemoteSwiftPackageReference "SFSafeSymbols.git" */; + package = 3CEB2C112AB2F45F0013609E /* XCRemoteSwiftPackageReference "SFSafeSymbols" */; productName = SFSafeSymbols; }; 3C4B7DDA2A58AD23003C8192 /* Alamofire */ = { isa = XCSwiftPackageProductDependency; - package = 3C4B7DD92A58AD23003C8192 /* XCRemoteSwiftPackageReference "Alamofire.git" */; + package = 3C4B7DD92A58AD23003C8192 /* XCRemoteSwiftPackageReference "Alamofire" */; productName = Alamofire; }; 3C4DB8C12AB70856000137CF /* NukeExtensions */ = { @@ -2265,12 +2293,12 @@ }; 3CA95F072A7ACF3B00626353 /* Nuke */ = { isa = XCSwiftPackageProductDependency; - package = 3CA95F062A7ACF3B00626353 /* XCRemoteSwiftPackageReference "Nuke.git" */; + package = 3CA95F062A7ACF3B00626353 /* XCRemoteSwiftPackageReference "Nuke" */; productName = Nuke; }; 3CA95F092A7ACF3B00626353 /* NukeUI */ = { isa = XCSwiftPackageProductDependency; - package = 3CA95F062A7ACF3B00626353 /* XCRemoteSwiftPackageReference "Nuke.git" */; + package = 3CA95F062A7ACF3B00626353 /* XCRemoteSwiftPackageReference "Nuke" */; productName = NukeUI; }; 3CC3E41A2ADC054000F7F9A9 /* Shiny */ = { @@ -2300,12 +2328,12 @@ }; 3CE69E952ABDCA8A00E2821E /* Realm */ = { isa = XCSwiftPackageProductDependency; - package = 3CE69E942ABDCA8A00E2821E /* XCRemoteSwiftPackageReference "realm-swift.git" */; + package = 3CE69E942ABDCA8A00E2821E /* XCRemoteSwiftPackageReference "realm-swift" */; productName = Realm; }; 3CE69E972ABDCA8A00E2821E /* RealmSwift */ = { isa = XCSwiftPackageProductDependency; - package = 3CE69E942ABDCA8A00E2821E /* XCRemoteSwiftPackageReference "realm-swift.git" */; + package = 3CE69E942ABDCA8A00E2821E /* XCRemoteSwiftPackageReference "realm-swift" */; productName = RealmSwift; }; 3CEB2C092AB2F3A50013609E /* Defaults */ = { @@ -2315,7 +2343,7 @@ }; 3CEB2C122AB2F45F0013609E /* SFSafeSymbols */ = { isa = XCSwiftPackageProductDependency; - package = 3CEB2C112AB2F45F0013609E /* XCRemoteSwiftPackageReference "SFSafeSymbols.git" */; + package = 3CEB2C112AB2F45F0013609E /* XCRemoteSwiftPackageReference "SFSafeSymbols" */; productName = SFSafeSymbols; }; 3CEBF46B2B09426E00469008 /* MarkdownUI */ = { diff --git a/Lunar/APIService/APIService.swift b/Lunar/APIService/APIService.swift new file mode 100644 index 0000000..fe94b58 --- /dev/null +++ b/Lunar/APIService/APIService.swift @@ -0,0 +1,79 @@ +// +// APIService.swift +// Lunar +// +// Created by Mani on 05/12/2023. +// + +import Foundation +import Moya + +let lemmyProvider = MoyaProvider() + +enum LemmyAPI { + case listPosts(parameters: ListPostsParameters) + case createPost(parameters: CreatePostParameters) +} + +extension LemmyAPI: TargetType { + var baseURL: URL { + return URL(string: "https://lemmy.world/api/v3")! + } + + var path: String { + switch self { + case .listPosts: return "/post/list" + case .createPost: return "/post" + } + } + + var method: Moya.Method { + switch self { + case .listPosts: .get + case .createPost: .post + } + } + +// var sampleData: Data { +// return Data() // You can provide sample data for testing +// } + + var task: Task { + switch self { + case .listPosts(let parameters): + var params: [String: Any] = [:] + if let type = parameters.type { params["type_"] = type } + if let sort = parameters.sort { params["sort"] = sort } + if let page = parameters.page { params["page"] = page } + if let limit = parameters.limit { params["limit"] = limit } + if let communityId = parameters.communityId { params["community_id"] = communityId } + if let communityName = parameters.communityName { params["community_name"] = communityName } + if let savedOnly = parameters.savedOnly { params["saved_only"] = savedOnly } + if let likedOnly = parameters.likedOnly { params["liked_only"] = likedOnly } + if let dislikedOnly = parameters.dislikedOnly { params["disliked_only"] = dislikedOnly } + if let pageCursor = parameters.pageCursor { params["page_cursor"] = pageCursor } + return .requestParameters(parameters: params, encoding: URLEncoding.default) + + case .createPost(parameters: let parameters): + var params: [String: Any] = [ + "name": parameters.name, + "community_id": parameters.communityId, + "language_id": parameters.languageId, + "auth": parameters.auth + ] + if let url = parameters.url { params["url"] = url } + if let body = parameters.body { params["body"] = body } + if let nsfw = parameters.nsfw { params["nsfw"] = nsfw } + return .requestParameters(parameters: params, encoding: JSONEncoding.default) + } + } + + var headers: [String: String]? { + switch self { + case .createPost(let parameters): + return ["Authorization": "Bearer \(parameters.auth)"] + default: + return ["Content-Type": "application/json"] + } + } +} diff --git a/Lunar/APIService/AppDelegateM.swift b/Lunar/APIService/AppDelegateM.swift new file mode 100644 index 0000000..9aa8482 --- /dev/null +++ b/Lunar/APIService/AppDelegateM.swift @@ -0,0 +1,85 @@ +// +// AppDelegate.swift +// Lunar +// +// Created by Mani on 17/09/2023. +// + +import Defaults +import Foundation +import LocalConsole +import Nuke +import RealmSwift +import SwiftUI + +class AppDelegateM: UIResponder, UIApplicationDelegate { + @Default(.appBundleID) var appBundleID + + var dataCacheHolder: DataCacheHolder? + + func application( + _: UIApplication, + didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]? = nil + ) -> Bool { + print("App Started") + initialiseLocalConsole() + initialiseNukeUI() + initialiseRealm() + PhaseChangeActions().homeScreenQuickActions() + listPostsMoya(parameters: ListPostsParameters(type: "All", sort: "Active")) + return true + } + + func initialiseLocalConsole() { + _ = LCManager.shared + } + + func initialiseNukeUI() { + DataLoader.sharedUrlCache.diskCapacity = 0 + + let pipeline = ImagePipeline { + self.dataCacheHolder = DataCacheHolder(appBundleID: self.appBundleID) + $0.dataCache = self.dataCacheHolder?.dataCache + $0.dataCachePolicy = .storeAll + $0.isProgressiveDecodingEnabled = true + } + + ImagePipeline.shared = pipeline + } + + func initialiseRealm() { + print("Realm DB Path:") + print("\(FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!.path)") + + let config = Realm.Configuration( + schemaVersion: 3, + migrationBlock: { _, oldSchemaVersion in + if oldSchemaVersion < 1 {} + }, + deleteRealmIfMigrationNeeded: true + ) + Realm.Configuration.defaultConfiguration = config + } + + func listPostsMoya(parameters: ListPostsParameters = ListPostsParameters()) { + lemmyProvider.request(.listPosts(parameters: parameters)) { result in + switch result { + case let .success(response): + do { + let decoder = JSONDecoder() + let postsModel = try decoder.decode(PostModel.self, from: response.data) + + print(postsModel.posts[0].post.name) + print(postsModel.posts[1].post.name) + print(postsModel.posts[2].post.name) + + } catch { + print("Error decoding posts: \(error)") + } + + case let .failure(error): + print("Network request failed: \(error)") + } + } + } +} diff --git a/Lunar/APIService/CreatePostPopoverViewM.swift b/Lunar/APIService/CreatePostPopoverViewM.swift new file mode 100644 index 0000000..9be38f9 --- /dev/null +++ b/Lunar/APIService/CreatePostPopoverViewM.swift @@ -0,0 +1,271 @@ +// +// CreatePostPopoverView.swift +// Lunar +// +// Created by Mani on 21/08/2023. +// + +import Defaults +import SFSafeSymbols +import SwiftUI + +struct CreatePostPopoverViewM: View { + @Default(.activeAccount) var activeAccount + + @Environment(\.dismiss) var dismiss + + @State private var postBody: String = "" + @State private var userInputBody: String = "" + @State var postName: String = "" + @State var userInputTitle: String = "" + @State var postURL: String = "" + @State var userInputURL: String = "" + @State var photoPickerIsPresented = false + @State var pickerResult: [UIImage] = [] + @State var showingImagePopover: Bool = false + @State var showingImageUploadResult: Bool = false + @State var responseMessage: String = "" + @State var fileToken: String = "" + @State var deleteToken: String = "" + + @State var siteMetadata = SiteMetadataObject( + title: nil, + description: nil, + image: nil + ) + + @State var expandSiteMetadata: Bool = false + @State var imageIsUploading: Bool = false + + var communityID: Int + var communityName: String + var communityActorID: String + + var submittable: Bool { + !userInputTitle.isEmpty && communityID != 0 + } + + let notificationHaptics = UINotificationFeedbackGenerator() + + var body: some View { + List { + createPostHeading + if activeAccount.userID.isEmpty { + notLoggedInWarning + } else { + postingToSection + postingAsSection + titleField + URLField + bodyField + imageUploaderSection + submitButtonSection + } + } + .listStyle(.insetGrouped) + .popover(isPresented: $photoPickerIsPresented) { + PhotoPicker( + pickerResult: $pickerResult, + isPresented: $photoPickerIsPresented + ) + } + .popover(isPresented: $showingImagePopover) { + LocalImagePopoverView( + showingImagePopover: $showingImagePopover, + image: pickerResult[0] + ) + } + .alert("Uploaded Image Response", isPresented: $showingImageUploadResult) { + VStack { + Text("responseMessage: \(responseMessage)") + Text("fileToken: \(fileToken)") + Text("deleteToken: \(deleteToken)") + } + + Button("OK", role: .cancel) {} + } + .onChange(of: pickerResult) { _ in + print(pickerResult) + } + } + + var notLoggedInWarning: some View { + HStack { + Spacer() + VStack { + Image(systemSymbol: .personCropCircleBadgeExclamationmarkFill) + .resizable() + .scaledToFit() + .frame(width: 100) + .padding(30) + .symbolRenderingMode(.palette) + .foregroundStyle(.yellow, .secondary) + Text("Login to Create Post") + .bold() + .font(.title2) + .foregroundStyle(.secondary) + } + Spacer() + } + .listRowSeparator(.hidden) + .listRowBackground(Color.clear) + } + + var postingToSection: some View { + Section { + VStack(alignment: .leading) { + HStack(spacing: 1) { + Text(communityName) + .bold() + Text("@\(URLParser.extractDomain(from: communityActorID))") + .foregroundStyle(.secondary) + } + } + } header: { + Text("Posting to:") + .textCase(.none) + } + .listRowSeparator(.hidden) + .listRowBackground(Color.clear) + } + + var postingAsSection: some View { + Section { + VStack(alignment: .leading) { + HStack(spacing: 1) { + Text(activeAccount.name) + .bold() + Text("@\(URLParser.extractDomain(from: activeAccount.actorID))") + .foregroundStyle(.secondary) + } + } + } header: { + Text("Posting as:") + .textCase(.none) + } + .listRowSeparator(.hidden) + .listRowBackground(Color.clear) + } + + var createPostHeading: some View { + Section { + HStack { + Text("Create Post") + .font(.title) + .bold() + Spacer() + Button { + dismiss() + } label: { + Image(systemSymbol: .xmarkCircleFill) + .font(.largeTitle) + .foregroundStyle(.secondary) + .saturation(0) + } + } + } + .listRowBackground(Color.clear) + } + + var titleField: some View { + Section { + TextField(text: $userInputTitle) { + Text("required").italic() + .foregroundStyle(.red.opacity(0.3)) + } + } header: { + Text("Title:") + .textCase(.none) + } + } + + var URLField: some View { + Section { + URLInputView(siteMetadata: $siteMetadata, userInputURL: $userInputURL) + } header: { + Text("URL:") + .textCase(.none) + } footer: { + URLInputFooterView( + siteMetadata: $siteMetadata, + expandSiteMetadata: $expandSiteMetadata + ) + } + .onDebouncedChange(of: $userInputURL, debounceFor: 1) { newValue in + URLInputDebounceLogic(siteMetadata: $siteMetadata, userInputURL: $userInputURL, newValue: newValue).run() + } + } + + var bodyField: some View { + Section { + TextEditor(text: $userInputBody) + .background(Color.clear) + .font(.body) + .frame(height: 150) + } header: { + Text("Body:") + .textCase(.none) + } + } + + var imageUploaderSection: some View { + Section { + ImageUploaderView( + photoPickerIsPresented: $photoPickerIsPresented, + pickerResult: $pickerResult, + showingImagePopover: $showingImagePopover, + showingImageUploadResult: $showingImageUploadResult, + imageIsUploading: $imageIsUploading, + responseMessage: $responseMessage, + fileToken: $fileToken, + deleteToken: $deleteToken + ) + } header: { + Text("Image Uploader Coming Soon:") + .textCase(.none) + } + } + + var submitButtonSection: some View { + Section { + Button { + postBody = userInputBody + postName = userInputTitle + postURL = userInputURL + if !postURL.isEmpty { + postURL = "https://\(postURL)" + } + if submittable { + + let createPostParameters = CreatePostParameters(name: postName, communityId: communityID, url: postURL, body: postBody, languageId: 0, auth: JWT().getJWTFromKeychain(actorID: activeAccount.actorID) ?? "") + + PostSender().createPost(parameters: createPostParameters) { response, postID in + if response == "success" { + notificationHaptics.notificationOccurred(.success) + dismiss() + print("CREATED NEW POST: id=\(postID)") + } else { + notificationHaptics.notificationOccurred(.error) + print("ERROR SUBMITTING POST") + } + } + } + } label: { + HStack { + Spacer() + Text(submittable ? "Post" : "Complete required fields to post") + .foregroundStyle(submittable ? .blue : .secondary) + Spacer() + } + } + } + } +} + +#Preview { + CreatePostPopoverView( + communityID: 234_309, + communityName: "API Testing Pls Ignore", + communityActorID: "https://lemmy.world/c/api_testing_pls_ignore" + ) +} diff --git a/Lunar/APIService/ParameterStructures.swift b/Lunar/APIService/ParameterStructures.swift new file mode 100644 index 0000000..02a8732 --- /dev/null +++ b/Lunar/APIService/ParameterStructures.swift @@ -0,0 +1,32 @@ +// +// DataStructs.swift +// Lunar +// +// Created by Mani on 05/12/2023. +// + +import Foundation + +struct ListPostsParameters { + var type: String? + var sort: String? + var page: Int? + var limit: Int? + var communityId: Int? + var communityName: String? + var savedOnly: Bool? + var likedOnly: Bool? + var dislikedOnly: Bool? + var pageCursor: String? +} + +struct CreatePostParameters { + var name: String + var communityId: Int + var url: String? + var body: String? + var honeypot: String? + var nsfw: Bool? + var languageId: Int + var auth: String +} diff --git a/Lunar/APIService/PostSenderM.swift b/Lunar/APIService/PostSenderM.swift new file mode 100644 index 0000000..ce7f742 --- /dev/null +++ b/Lunar/APIService/PostSenderM.swift @@ -0,0 +1,39 @@ +// +// PostSender.swift +// Lunar +// +// Created by Mani on 16/08/2023. +// + +import Alamofire +import Defaults +import Foundation +import Pulse +import SwiftUI + +class PostSenderM: ObservableObject { + @Default(.activeAccount) var activeAccount + @Default(.appBundleID) var appBundleID + @Default(.networkInspectorEnabled) var networkInspectorEnabled + + let pulse = Pulse.LoggerStore.shared + + func createPost(parameters: CreatePostParameters, completion: @escaping (String?, Int) -> Void) { + lemmyProvider.request(.createPost(parameters: parameters)) { result in + switch result { + case let .success(response): + do { + print(String(data: response.data, encoding: .utf8) ?? "Unable to convert data to string") + let responseData = try JSONDecoder().decode(CreatePostResponseModel.self, from: response.data) + let postID = Int(responseData.post?.post.id ?? 0) + print(String(postID)) + completion("success", postID) + } catch { + print("Error decoding create post response: \(error)") + } + case let .failure(error): + print("Network request failed: \(error)") + } + } + } +}