mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-03 06:55:59 +00:00
Make sure we pass the via parameters with all the routes.
Put the event ID first. Fix tests. Bump the SDK.
This commit is contained in:
@@ -130,7 +130,7 @@ class AppRouteURLParserTests: XCTestCase {
|
||||
|
||||
let route = appRouteURLParser.route(from: url)
|
||||
|
||||
XCTAssertEqual(route, .room(roomID: id))
|
||||
XCTAssertEqual(route, .room(roomID: id, via: []))
|
||||
}
|
||||
|
||||
func testWebRoomIDURL() {
|
||||
@@ -142,7 +142,7 @@ class AppRouteURLParserTests: XCTestCase {
|
||||
|
||||
let route = appRouteURLParser.route(from: url)
|
||||
|
||||
XCTAssertEqual(route, .room(roomID: id))
|
||||
XCTAssertEqual(route, .room(roomID: id, via: []))
|
||||
}
|
||||
|
||||
func testWebUserIDURL() {
|
||||
|
||||
@@ -55,7 +55,7 @@ class JoinRoomScreenViewModelTests: XCTestCase {
|
||||
private func setupViewModel(throwing: Bool = false) {
|
||||
let clientProxy = ClientProxyMock(.init())
|
||||
|
||||
clientProxy.joinRoomReturnValue = throwing ? .failure(.sdkError(ClientProxyMockError.generic)) : .success(())
|
||||
clientProxy.joinRoomViaReturnValue = throwing ? .failure(.sdkError(ClientProxyMockError.generic)) : .success(())
|
||||
|
||||
clientProxy.roomPreviewForIdentifierReturnValue = .success(.init(roomID: "",
|
||||
name: nil,
|
||||
@@ -70,6 +70,7 @@ class JoinRoomScreenViewModelTests: XCTestCase {
|
||||
canKnock: false))
|
||||
|
||||
viewModel = JoinRoomScreenViewModel(roomID: "1",
|
||||
via: [],
|
||||
clientProxy: clientProxy,
|
||||
mediaProvider: MockMediaProvider(),
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController)
|
||||
|
||||
@@ -30,7 +30,7 @@ class RoomFlowCoordinatorTests: XCTestCase {
|
||||
func testRoomPresentation() async throws {
|
||||
await setupRoomFlowCoordinator()
|
||||
|
||||
try await process(route: .room(roomID: "1"))
|
||||
try await process(route: .room(roomID: "1", via: []))
|
||||
XCTAssert(navigationStackCoordinator.rootCoordinator is RoomScreenCoordinator)
|
||||
|
||||
try await clearRoute(expectedActions: [.finished])
|
||||
@@ -64,7 +64,7 @@ class RoomFlowCoordinatorTests: XCTestCase {
|
||||
func testPushDetails() async throws {
|
||||
await setupRoomFlowCoordinator()
|
||||
|
||||
try await process(route: .room(roomID: "1"))
|
||||
try await process(route: .room(roomID: "1", via: []))
|
||||
XCTAssert(navigationStackCoordinator.rootCoordinator is RoomScreenCoordinator)
|
||||
XCTAssertEqual(navigationStackCoordinator.stackCoordinators.count, 0)
|
||||
|
||||
@@ -77,15 +77,15 @@ class RoomFlowCoordinatorTests: XCTestCase {
|
||||
func testChildRoomFlow() async throws {
|
||||
await setupRoomFlowCoordinator()
|
||||
|
||||
try await process(route: .room(roomID: "1"))
|
||||
try await process(route: .room(roomID: "1", via: []))
|
||||
XCTAssert(navigationStackCoordinator.rootCoordinator is RoomScreenCoordinator)
|
||||
XCTAssertEqual(navigationStackCoordinator.stackCoordinators.count, 0)
|
||||
|
||||
try await process(route: .childRoom(roomID: "2"))
|
||||
try await process(route: .childRoom(roomID: "2", via: []))
|
||||
XCTAssertEqual(navigationStackCoordinator.stackCoordinators.count, 1)
|
||||
XCTAssert(navigationStackCoordinator.stackCoordinators.first is RoomScreenCoordinator)
|
||||
|
||||
try await process(route: .childRoom(roomID: "3"))
|
||||
try await process(route: .childRoom(roomID: "3", via: []))
|
||||
XCTAssertEqual(navigationStackCoordinator.stackCoordinators.count, 2)
|
||||
XCTAssert(navigationStackCoordinator.stackCoordinators.first is RoomScreenCoordinator)
|
||||
XCTAssert(navigationStackCoordinator.stackCoordinators.last is RoomScreenCoordinator)
|
||||
@@ -100,7 +100,7 @@ class RoomFlowCoordinatorTests: XCTestCase {
|
||||
await setupRoomFlowCoordinator(asChildFlow: true)
|
||||
navigationStackCoordinator.setRootCoordinator(BlankFormCoordinator())
|
||||
|
||||
try await process(route: .room(roomID: "1"))
|
||||
try await process(route: .room(roomID: "1", via: []))
|
||||
try await process(route: .roomDetails(roomID: "1"))
|
||||
XCTAssertTrue(navigationStackCoordinator.rootCoordinator is BlankFormCoordinator, "A child room flow should push onto the stack, leaving the root alone.")
|
||||
XCTAssertEqual(navigationStackCoordinator.stackCoordinators.count, 2)
|
||||
@@ -117,11 +117,11 @@ class RoomFlowCoordinatorTests: XCTestCase {
|
||||
func testChildRoomMemberDetails() async throws {
|
||||
await setupRoomFlowCoordinator()
|
||||
|
||||
try await process(route: .room(roomID: "1"))
|
||||
try await process(route: .room(roomID: "1", via: []))
|
||||
XCTAssert(navigationStackCoordinator.rootCoordinator is RoomScreenCoordinator)
|
||||
XCTAssertEqual(navigationStackCoordinator.stackCoordinators.count, 0)
|
||||
|
||||
try await process(route: .childRoom(roomID: "2"))
|
||||
try await process(route: .childRoom(roomID: "2", via: []))
|
||||
XCTAssertEqual(navigationStackCoordinator.stackCoordinators.count, 1)
|
||||
XCTAssert(navigationStackCoordinator.stackCoordinators.first is RoomScreenCoordinator)
|
||||
|
||||
@@ -134,19 +134,19 @@ class RoomFlowCoordinatorTests: XCTestCase {
|
||||
func testChildRoomIgnoresDirectDuplicate() async throws {
|
||||
await setupRoomFlowCoordinator()
|
||||
|
||||
try await process(route: .room(roomID: "1"))
|
||||
try await process(route: .room(roomID: "1", via: []))
|
||||
XCTAssert(navigationStackCoordinator.rootCoordinator is RoomScreenCoordinator)
|
||||
XCTAssertEqual(navigationStackCoordinator.stackCoordinators.count, 0)
|
||||
|
||||
try await process(route: .childRoom(roomID: "1"))
|
||||
try await process(route: .childRoom(roomID: "1", via: []))
|
||||
XCTAssertEqual(navigationStackCoordinator.stackCoordinators.count, 0,
|
||||
"A room flow shouldn't present a direct child for the same room.")
|
||||
|
||||
try await process(route: .childRoom(roomID: "2"))
|
||||
try await process(route: .childRoom(roomID: "2", via: []))
|
||||
XCTAssertEqual(navigationStackCoordinator.stackCoordinators.count, 1)
|
||||
XCTAssert(navigationStackCoordinator.stackCoordinators.first is RoomScreenCoordinator)
|
||||
|
||||
try await process(route: .childRoom(roomID: "1"))
|
||||
try await process(route: .childRoom(roomID: "1", via: []))
|
||||
XCTAssertEqual(navigationStackCoordinator.stackCoordinators.count, 2,
|
||||
"Presenting the same room multiple times should be allowed when it's not a direct child of itself.")
|
||||
XCTAssert(navigationStackCoordinator.stackCoordinators.first is RoomScreenCoordinator)
|
||||
@@ -156,7 +156,7 @@ class RoomFlowCoordinatorTests: XCTestCase {
|
||||
func testRoomMembershipInvite() async throws {
|
||||
await setupRoomFlowCoordinator(roomType: .invited(roomID: "InvitedRoomID"))
|
||||
|
||||
try await process(route: .room(roomID: "InvitedRoomID"))
|
||||
try await process(route: .room(roomID: "InvitedRoomID", via: []))
|
||||
XCTAssert(navigationStackCoordinator.rootCoordinator is JoinRoomScreenCoordinator)
|
||||
XCTAssertEqual(navigationStackCoordinator.stackCoordinators.count, 0)
|
||||
|
||||
@@ -165,7 +165,7 @@ class RoomFlowCoordinatorTests: XCTestCase {
|
||||
|
||||
await setupRoomFlowCoordinator(roomType: .invited(roomID: "InvitedRoomID"))
|
||||
|
||||
try await process(route: .room(roomID: "InvitedRoomID"))
|
||||
try await process(route: .room(roomID: "InvitedRoomID", via: []))
|
||||
XCTAssert(navigationStackCoordinator.rootCoordinator is JoinRoomScreenCoordinator)
|
||||
XCTAssertEqual(navigationStackCoordinator.stackCoordinators.count, 0)
|
||||
|
||||
@@ -174,7 +174,7 @@ class RoomFlowCoordinatorTests: XCTestCase {
|
||||
RoomProxyMock(with: .init())
|
||||
}
|
||||
|
||||
try await process(route: .room(roomID: "InvitedRoomID"))
|
||||
try await process(route: .room(roomID: "InvitedRoomID", via: []))
|
||||
XCTAssert(navigationStackCoordinator.rootCoordinator is RoomScreenCoordinator)
|
||||
XCTAssertEqual(navigationStackCoordinator.stackCoordinators.count, 0)
|
||||
}
|
||||
@@ -183,7 +183,7 @@ class RoomFlowCoordinatorTests: XCTestCase {
|
||||
await setupRoomFlowCoordinator(asChildFlow: true, roomType: .invited(roomID: "InvitedRoomID"))
|
||||
navigationStackCoordinator.setRootCoordinator(BlankFormCoordinator())
|
||||
|
||||
try await process(route: .room(roomID: "InvitedRoomID"))
|
||||
try await process(route: .room(roomID: "InvitedRoomID", via: []))
|
||||
XCTAssertTrue(navigationStackCoordinator.rootCoordinator is BlankFormCoordinator, "A child room flow should push onto the stack, leaving the root alone.")
|
||||
XCTAssertEqual(navigationStackCoordinator.stackCoordinators.count, 1)
|
||||
XCTAssertTrue(navigationStackCoordinator.stackCoordinators.last is JoinRoomScreenCoordinator)
|
||||
@@ -194,7 +194,7 @@ class RoomFlowCoordinatorTests: XCTestCase {
|
||||
await setupRoomFlowCoordinator(asChildFlow: true, roomType: .invited(roomID: "InvitedRoomID"))
|
||||
navigationStackCoordinator.setRootCoordinator(BlankFormCoordinator())
|
||||
|
||||
try await process(route: .room(roomID: "InvitedRoomID"))
|
||||
try await process(route: .room(roomID: "InvitedRoomID", via: []))
|
||||
XCTAssertTrue(navigationStackCoordinator.rootCoordinator is BlankFormCoordinator, "A child room flow should push onto the stack, leaving the root alone.")
|
||||
XCTAssertEqual(navigationStackCoordinator.stackCoordinators.count, 1)
|
||||
XCTAssertTrue(navigationStackCoordinator.stackCoordinators.last is JoinRoomScreenCoordinator)
|
||||
@@ -204,7 +204,7 @@ class RoomFlowCoordinatorTests: XCTestCase {
|
||||
RoomProxyMock(with: .init())
|
||||
}
|
||||
|
||||
try await process(route: .room(roomID: "InvitedRoomID"))
|
||||
try await process(route: .room(roomID: "InvitedRoomID", via: []))
|
||||
XCTAssertTrue(navigationStackCoordinator.rootCoordinator is BlankFormCoordinator, "A child room flow should push onto the stack, leaving the root alone.")
|
||||
XCTAssertEqual(navigationStackCoordinator.stackCoordinators.count, 1)
|
||||
XCTAssertTrue(navigationStackCoordinator.stackCoordinators.last is RoomScreenCoordinator)
|
||||
@@ -213,15 +213,15 @@ class RoomFlowCoordinatorTests: XCTestCase {
|
||||
func testEventRoute() async throws {
|
||||
await setupRoomFlowCoordinator()
|
||||
|
||||
try await process(route: .event(roomID: "1", eventID: "1"))
|
||||
try await process(route: .event(eventID: "1", roomID: "1", via: []))
|
||||
XCTAssert(navigationStackCoordinator.rootCoordinator is RoomScreenCoordinator)
|
||||
XCTAssertEqual(navigationStackCoordinator.stackCoordinators.count, 0)
|
||||
|
||||
try await process(route: .childEvent(roomID: "1", eventID: "2"))
|
||||
try await process(route: .childEvent(eventID: "2", roomID: "1", via: []))
|
||||
XCTAssert(navigationStackCoordinator.rootCoordinator is RoomScreenCoordinator)
|
||||
XCTAssertEqual(navigationStackCoordinator.stackCoordinators.count, 0)
|
||||
|
||||
try await process(route: .childEvent(roomID: "2", eventID: "3"))
|
||||
try await process(route: .childEvent(eventID: "3", roomID: "2", via: []))
|
||||
XCTAssert(navigationStackCoordinator.rootCoordinator is RoomScreenCoordinator)
|
||||
XCTAssertEqual(navigationStackCoordinator.stackCoordinators.count, 1)
|
||||
XCTAssert(navigationStackCoordinator.stackCoordinators.first is RoomScreenCoordinator)
|
||||
|
||||
@@ -64,7 +64,7 @@ class UserSessionFlowCoordinatorTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testRoomPresentation() async throws {
|
||||
try await process(route: .room(roomID: "1"), expectedState: .roomList(selectedRoomID: "1"))
|
||||
try await process(route: .room(roomID: "1", via: []), expectedState: .roomList(selectedRoomID: "1"))
|
||||
XCTAssertTrue(detailNavigationStack?.rootCoordinator is RoomScreenCoordinator)
|
||||
XCTAssertNotNil(detailCoordinator)
|
||||
|
||||
@@ -72,11 +72,11 @@ class UserSessionFlowCoordinatorTests: XCTestCase {
|
||||
XCTAssertNil(detailNavigationStack?.rootCoordinator)
|
||||
XCTAssertNil(detailCoordinator)
|
||||
|
||||
try await process(route: .room(roomID: "1"), expectedState: .roomList(selectedRoomID: "1"))
|
||||
try await process(route: .room(roomID: "1", via: []), expectedState: .roomList(selectedRoomID: "1"))
|
||||
XCTAssertTrue(detailNavigationStack?.rootCoordinator is RoomScreenCoordinator)
|
||||
XCTAssertNotNil(detailCoordinator)
|
||||
|
||||
try await process(route: .room(roomID: "2"), expectedState: .roomList(selectedRoomID: "2"))
|
||||
try await process(route: .room(roomID: "2", via: []), expectedState: .roomList(selectedRoomID: "2"))
|
||||
XCTAssertTrue(detailNavigationStack?.rootCoordinator is RoomScreenCoordinator)
|
||||
XCTAssertNotNil(detailCoordinator)
|
||||
|
||||
@@ -88,7 +88,7 @@ class UserSessionFlowCoordinatorTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testRoomAliasPresentation() async throws {
|
||||
clientProxy.resolveRoomAliasReturnValue = "1"
|
||||
clientProxy.resolveRoomAliasReturnValue = .init(roomId: "1", servers: [])
|
||||
|
||||
try await process(route: .roomAlias("#alias:matrix.org"), expectedState: .roomList(selectedRoomID: "1"))
|
||||
XCTAssertTrue(detailNavigationStack?.rootCoordinator is RoomScreenCoordinator)
|
||||
@@ -98,13 +98,13 @@ class UserSessionFlowCoordinatorTests: XCTestCase {
|
||||
XCTAssertNil(detailNavigationStack?.rootCoordinator)
|
||||
XCTAssertNil(detailCoordinator)
|
||||
|
||||
try await process(route: .room(roomID: "1"), expectedState: .roomList(selectedRoomID: "1"))
|
||||
try await process(route: .room(roomID: "1", via: []), expectedState: .roomList(selectedRoomID: "1"))
|
||||
XCTAssertTrue(detailNavigationStack?.rootCoordinator is RoomScreenCoordinator)
|
||||
XCTAssertNotNil(detailCoordinator)
|
||||
|
||||
clientProxy.resolveRoomAliasReturnValue = "2"
|
||||
clientProxy.resolveRoomAliasReturnValue = .init(roomId: "2", servers: [])
|
||||
|
||||
try await process(route: .room(roomID: "2"), expectedState: .roomList(selectedRoomID: "2"))
|
||||
try await process(route: .room(roomID: "2", via: []), expectedState: .roomList(selectedRoomID: "2"))
|
||||
XCTAssertTrue(detailNavigationStack?.rootCoordinator is RoomScreenCoordinator)
|
||||
XCTAssertNotNil(detailCoordinator)
|
||||
|
||||
@@ -130,7 +130,7 @@ class UserSessionFlowCoordinatorTests: XCTestCase {
|
||||
XCTAssertTrue(detailNavigationStack?.rootCoordinator is RoomDetailsScreenCoordinator)
|
||||
XCTAssertNotNil(detailCoordinator)
|
||||
|
||||
try await process(route: .room(roomID: "2"), expectedState: .roomList(selectedRoomID: "2"))
|
||||
try await process(route: .room(roomID: "2", via: []), expectedState: .roomList(selectedRoomID: "2"))
|
||||
XCTAssertTrue(detailNavigationStack?.rootCoordinator is RoomScreenCoordinator)
|
||||
XCTAssertNotNil(detailCoordinator)
|
||||
}
|
||||
@@ -159,7 +159,7 @@ class UserSessionFlowCoordinatorTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testPushDetails() async throws {
|
||||
try await process(route: .room(roomID: "1"), expectedState: .roomList(selectedRoomID: "1"))
|
||||
try await process(route: .room(roomID: "1", via: []), expectedState: .roomList(selectedRoomID: "1"))
|
||||
XCTAssertTrue(detailNavigationStack?.rootCoordinator is RoomScreenCoordinator)
|
||||
XCTAssertNotNil(detailCoordinator)
|
||||
|
||||
@@ -178,7 +178,7 @@ class UserSessionFlowCoordinatorTests: XCTestCase {
|
||||
XCTAssertTrue(detailNavigationStack?.rootCoordinator is RoomDetailsScreenCoordinator)
|
||||
XCTAssertNotNil(detailCoordinator)
|
||||
|
||||
try await process(route: .room(roomID: "1"), expectedState: .roomList(selectedRoomID: "1"))
|
||||
try await process(route: .room(roomID: "1", via: []), expectedState: .roomList(selectedRoomID: "1"))
|
||||
XCTAssertTrue(detailNavigationStack?.rootCoordinator is RoomScreenCoordinator)
|
||||
XCTAssertNotNil(detailCoordinator)
|
||||
}
|
||||
@@ -199,19 +199,19 @@ class UserSessionFlowCoordinatorTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testRoomClearsStack() async throws {
|
||||
try await process(route: .room(roomID: "1"), expectedState: .roomList(selectedRoomID: "1"))
|
||||
try await process(route: .room(roomID: "1", via: []), expectedState: .roomList(selectedRoomID: "1"))
|
||||
XCTAssertTrue(detailNavigationStack?.rootCoordinator is RoomScreenCoordinator)
|
||||
XCTAssertEqual(detailNavigationStack?.stackCoordinators.count, 0)
|
||||
XCTAssertNotNil(detailCoordinator)
|
||||
|
||||
userSessionFlowCoordinator.handleAppRoute(.childRoom(roomID: "2"), animated: true)
|
||||
userSessionFlowCoordinator.handleAppRoute(.childRoom(roomID: "2", via: []), animated: true)
|
||||
try await Task.sleep(for: .milliseconds(100))
|
||||
XCTAssertTrue(detailNavigationStack?.rootCoordinator is RoomScreenCoordinator)
|
||||
XCTAssertEqual(detailNavigationStack?.stackCoordinators.count, 1)
|
||||
XCTAssertTrue(detailNavigationStack?.stackCoordinators.first is RoomScreenCoordinator)
|
||||
XCTAssertNotNil(detailCoordinator)
|
||||
|
||||
try await process(route: .room(roomID: "3"), expectedState: .roomList(selectedRoomID: "3"))
|
||||
try await process(route: .room(roomID: "3", via: []), expectedState: .roomList(selectedRoomID: "3"))
|
||||
XCTAssertTrue(detailNavigationStack?.rootCoordinator is RoomScreenCoordinator)
|
||||
XCTAssertEqual(detailNavigationStack?.stackCoordinators.count, 0)
|
||||
XCTAssertNotNil(detailCoordinator)
|
||||
@@ -219,7 +219,7 @@ class UserSessionFlowCoordinatorTests: XCTestCase {
|
||||
|
||||
func testEventRoutes() async throws {
|
||||
// A regular event route should set its room as the root of the stack and focus on the event.
|
||||
try await process(route: .event(roomID: "1", eventID: "1"), expectedState: .roomList(selectedRoomID: "1"))
|
||||
try await process(route: .event(eventID: "1", roomID: "1", via: []), expectedState: .roomList(selectedRoomID: "1"))
|
||||
XCTAssertTrue(detailNavigationStack?.rootCoordinator is RoomScreenCoordinator)
|
||||
XCTAssertEqual(detailNavigationStack?.stackCoordinators.count, 0)
|
||||
XCTAssertNotNil(detailCoordinator)
|
||||
@@ -227,7 +227,7 @@ class UserSessionFlowCoordinatorTests: XCTestCase {
|
||||
XCTAssertEqual(timelineControllerFactory.buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryReceivedArguments?.initialFocussedEventID, "1")
|
||||
|
||||
// A child event route should push a new room screen onto the stack and focus on the event.
|
||||
userSessionFlowCoordinator.handleAppRoute(.childEvent(roomID: "2", eventID: "2"), animated: true)
|
||||
userSessionFlowCoordinator.handleAppRoute(.childEvent(eventID: "2", roomID: "2", via: []), animated: true)
|
||||
try await Task.sleep(for: .milliseconds(100))
|
||||
XCTAssertTrue(detailNavigationStack?.rootCoordinator is RoomScreenCoordinator)
|
||||
XCTAssertEqual(detailNavigationStack?.stackCoordinators.count, 1)
|
||||
@@ -237,7 +237,7 @@ class UserSessionFlowCoordinatorTests: XCTestCase {
|
||||
XCTAssertEqual(timelineControllerFactory.buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryReceivedArguments?.initialFocussedEventID, "2")
|
||||
|
||||
// A subsequent regular event route should clear the stack and set the new room as the root of the stack.
|
||||
try await process(route: .event(roomID: "3", eventID: "3"), expectedState: .roomList(selectedRoomID: "3"))
|
||||
try await process(route: .event(eventID: "3", roomID: "3", via: []), expectedState: .roomList(selectedRoomID: "3"))
|
||||
XCTAssertTrue(detailNavigationStack?.rootCoordinator is RoomScreenCoordinator)
|
||||
XCTAssertEqual(detailNavigationStack?.stackCoordinators.count, 0)
|
||||
XCTAssertNotNil(detailCoordinator)
|
||||
@@ -245,7 +245,7 @@ class UserSessionFlowCoordinatorTests: XCTestCase {
|
||||
XCTAssertEqual(timelineControllerFactory.buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryReceivedArguments?.initialFocussedEventID, "3")
|
||||
|
||||
// A regular event route for the same room should set a new instance of the room as the root of the stack.
|
||||
try await process(route: .event(roomID: "3", eventID: "4"), expectedState: .roomList(selectedRoomID: "3"))
|
||||
try await process(route: .event(eventID: "4", roomID: "3", via: []), expectedState: .roomList(selectedRoomID: "3"))
|
||||
XCTAssertTrue(detailNavigationStack?.rootCoordinator is RoomScreenCoordinator)
|
||||
XCTAssertEqual(detailNavigationStack?.stackCoordinators.count, 0)
|
||||
XCTAssertNotNil(detailCoordinator)
|
||||
|
||||
Reference in New Issue
Block a user