Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Block Instances #1019

Merged
merged 3 commits into from
Apr 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Mlem.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
030E86482AC6FD1D000283A6 /* _assignIfNotEqual.swift in Sources */ = {isa = PBXBuildFile; fileRef = 030E86472AC6FD1D000283A6 /* _assignIfNotEqual.swift */; };
030E864C2AC7037F000283A6 /* SearchBarExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 030E864B2AC7037F000283A6 /* SearchBarExtensions.swift */; };
030FF6862BCB218000F6BFAC /* Int+Abbreviated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 030FF6852BCB218000F6BFAC /* Int+Abbreviated.swift */; };
030FF6882BCEE58900F6BFAC /* BlockInstance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 030FF6872BCEE58900F6BFAC /* BlockInstance.swift */; };
0317D46F2B558CB500EEE72C /* BadgeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0317D46E2B558CB500EEE72C /* BadgeView.swift */; };
0317D4712B55AE0700EEE72C /* Color+Hex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0317D4702B55AE0700EEE72C /* Color+Hex.swift */; };
031A617C2B1BDFD100ABF23B /* AdvancedAccountSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 031A617B2B1BDFD100ABF23B /* AdvancedAccountSettingsView.swift */; };
Expand Down Expand Up @@ -759,6 +760,7 @@
030E86472AC6FD1D000283A6 /* _assignIfNotEqual.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = _assignIfNotEqual.swift; sourceTree = "<group>"; };
030E864B2AC7037F000283A6 /* SearchBarExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchBarExtensions.swift; sourceTree = "<group>"; };
030FF6852BCB218000F6BFAC /* Int+Abbreviated.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Int+Abbreviated.swift"; sourceTree = "<group>"; };
030FF6872BCEE58900F6BFAC /* BlockInstance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockInstance.swift; sourceTree = "<group>"; };
0317D46E2B558CB500EEE72C /* BadgeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BadgeView.swift; sourceTree = "<group>"; };
0317D4702B55AE0700EEE72C /* Color+Hex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Color+Hex.swift"; sourceTree = "<group>"; };
031A617B2B1BDFD100ABF23B /* AdvancedAccountSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdvancedAccountSettingsView.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2446,6 +2448,7 @@
CD2697E42B9E14FB0002B459 /* GetModlogRequest.swift */,
6372183B2A3A2AAD008C4816 /* GetSite.swift */,
031A617F2B1CEA7300ABF23B /* ChangePassword.swift */,
030FF6872BCEE58900F6BFAC /* BlockInstance.swift */,
03A18CBC2B1005A400BA69D2 /* SaveUserSettings.swift */,
);
path = Site;
Expand Down Expand Up @@ -4363,6 +4366,7 @@
CD04D5E72A3636FB008EF95B /* Headline Post.swift in Sources */,
50A8812E2A72D76C003E3661 /* APIClient+Comment.swift in Sources */,
CDD0B8CF2BBA0D31003E7174 /* ResolveCommentReportRequest.swift in Sources */,
030FF6882BCEE58900F6BFAC /* BlockInstance.swift in Sources */,
6363D5C527EE196700E34822 /* MlemApp.swift in Sources */,
CDF1EF162A6C3BC2003594B6 /* End Of Feed View.swift in Sources */,
CDD0B8D32BBB4158003E7174 /* InboxView+Feeds.swift in Sources */,
Expand Down
10 changes: 10 additions & 0 deletions Mlem/API/APIClient/APIClient+Instance.swift
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,14 @@ extension APIClient {

return ret.sorted(by: { $0.date > $1.date })
}

func blockSite(id: Int, shouldBlock: Bool) async throws -> BlockInstanceResponse {
let request = try BlockInstanceRequest(
session: session,
instanceId: id,
block: shouldBlock
)

return try await perform(request: request)
}
}
41 changes: 41 additions & 0 deletions Mlem/API/Requests/Site/BlockInstance.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//
// BlockInstance.swift
// Mlem
//
// Created by Sjmarf on 16/04/2024.
//

import Foundation

struct BlockInstanceRequest: APIPostRequest {
typealias Response = BlockInstanceResponse

let instanceURL: URL
let path = "site/block"
let body: Body

// lemmy_api_common::community::BlockCommunity
struct Body: Encodable {
let instance_id: Int
let block: Bool

let auth: String
}

init(
session: APISession,
instanceId: Int,
block: Bool
) throws {
self.instanceURL = try session.instanceUrl
self.body = try .init(
instance_id: instanceId,
block: block,
auth: session.token
)
}
}

struct BlockInstanceResponse: Decodable {
let blocked: Bool
}
1 change: 1 addition & 0 deletions Mlem/App Constants.swift
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,5 @@ enum AppConstants {

static let blockUserPrompt: String = "Really block this user?"
static let blockCommunityPrompt: String = "Really block this community?"
static let blockInstancePrompt: String = "Really block this instance?"
}
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,9 @@ extension CommunityModel {
}
do {
if let instanceHost = communityUrl.host() {
let instance: InstanceModel
var instance: InstanceModel
if let site {
instance = .init(from: site)
instance = .init(from: site, isLocal: true)
} else {
instance = try .init(domainName: instanceHost)
}
Expand Down
33 changes: 31 additions & 2 deletions Mlem/Models/Content/Instance/InstanceModel+MenuFunctions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,38 @@
import Foundation

extension InstanceModel {
func menuFunctions() -> [MenuFunction] {

func blockMenuFunction(_ callback: @escaping (_ item: Self) -> Void = { _ in }) -> MenuFunction {
if blocked {
return .standardMenuFunction(
text: "Unblock",
imageName: Icons.show,
enabled: localSiteId != nil
) {
Task { await toggleBlock(callback) }
}
}
return .standardMenuFunction(
text: "Block",
imageName: Icons.hide,
confirmationPrompt: AppConstants.blockInstancePrompt,
enabled: localSiteId != nil
) {
Task { await toggleBlock(callback) }
}
}

func menuFunctions(_ callback: @escaping (_ item: Self) -> Void = { _ in }) -> [MenuFunction] {
if let url {
return [.shareMenuFunction(url: url)]
var functions: [MenuFunction] = [
.shareMenuFunction(url: url),
.openUrlMenuFunction(text: "View on Web", imageName: Icons.browser, destination: url),
.divider
]
if localSiteId != nil {
functions.append(blockMenuFunction(callback))
}
return functions
}
return []
}
Expand Down
47 changes: 46 additions & 1 deletion Mlem/Models/Content/Instance/InstanceModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,15 @@
// Created by Sjmarf on 13/01/2024.
//

import Dependencies
import SwiftUI

struct InstanceModel {
@Dependency(\.apiClient) var apiClient
@Dependency(\.hapticManager) var hapticManager
@Dependency(\.errorHandler) var errorHandler
@Dependency(\.notifier) var notifier

enum InstanceError: Error {
case invalidUrl
}
Expand Down Expand Up @@ -45,6 +51,12 @@ struct InstanceModel {
var applicationsEmailAdmins: Bool?
var reportsEmailAdmins: Bool?

// Not included in any API types; assumed to be false
var blocked: Bool = false

// This is included in APISite, but should ONLY be set when fetched from local instance
var localSiteId: Int?

init(domainName: String) throws {
var components = URLComponents()
components.scheme = "https"
Expand All @@ -65,8 +77,11 @@ struct InstanceModel {
update(with: siteView)
}

init(from site: APISite) {
init(from site: APISite, isLocal: Bool = false) {
update(with: site)
if isLocal {
self.localSiteId = site.instanceId
}
}

init(from stub: InstanceStub) {
Expand Down Expand Up @@ -163,6 +178,35 @@ struct InstanceModel {
return nil
}

func toggleBlock(_ callback: @escaping (_ item: Self) -> Void = { _ in }) async {
guard let localSiteId else { return }
var new = self
new.blocked = !blocked
RunLoop.main.perform { [new] in
callback(new)
}
do {
let response: BlockInstanceResponse
if !blocked {
response = try await apiClient.blockSite(id: localSiteId, shouldBlock: true)
} else {
response = try await apiClient.blockSite(id: localSiteId, shouldBlock: false)
}
new.blocked = response.blocked
RunLoop.main.perform { [new] in
callback(new)
}
await notifier.add(.success(response.blocked ? "Blocked instance" : "Unblocked instance"))
} catch {
hapticManager.play(haptic: .failure, priority: .high)
errorHandler.handle(error)
let phrase = !blocked ? "block" : "unblock"
errorHandler.handle(
.init(title: "Failed to \(phrase) instance", style: .toast, underlyingError: error)
)
}
}

static func mock() -> InstanceModel {
.init(from: SiteResponse.mock())
}
Expand All @@ -181,5 +225,6 @@ extension InstanceModel: Hashable {
func hash(into hasher: inout Hasher) {
hasher.combine(url)
hasher.combine(creationDate)
hasher.combine(blocked)
}
}
2 changes: 1 addition & 1 deletion Mlem/Models/Content/User/UserModel+MenuFunctions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ extension UserModel {
if let instanceHost = profileUrl.host() {
let instance: InstanceModel
if let site {
instance = .init(from: site)
instance = .init(from: site, isLocal: true)
} else {
instance = try .init(domainName: instanceHost)
}
Expand Down
11 changes: 9 additions & 2 deletions Mlem/Views/Shared/Instance/InstanceView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ struct InstanceView: View {
@Namespace var scrollToTop
@State private var scrollToTopAppeared = false

@State private var menuFunctionPopup: MenuFunctionPopup?

@State var selectedTab: InstanceViewTab = .about

var uptimeRefreshTimer = Timer.publish(every: 30, tolerance: 0.5, on: .main, in: .common)
Expand Down Expand Up @@ -176,11 +178,16 @@ struct InstanceView: View {
}
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
Link(destination: instance.url) {
Label("Open in Browser", systemImage: Icons.browser)
ToolbarEllipsisMenu {
ForEach(instance.menuFunctions { new in
self.instance = new
}) { item in
MenuButton(menuFunction: item, menuFunctionPopup: $menuFunctionPopup)
}
}
}
}
.destructiveConfirmation(menuFunctionPopup: $menuFunctionPopup)
.onAppear(perform: attemptToLoadInstanceData)
.fancyTabScrollCompatible()
.hoistNavigation {
Expand Down
Loading