Skip to content

Commit

Permalink
Merge pull request #1135 from anyproto/ios-2536-support-limits
Browse files Browse the repository at this point in the history
iOS-2536 Spaces | support limits for participants
  • Loading branch information
mgolovko authored Apr 8, 2024
2 parents d32a2b9 + 3c7a5dd commit bfb32b2
Show file tree
Hide file tree
Showing 8 changed files with 109 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,33 +7,32 @@ import Combine
@MainActor
final class SpaceShareViewModel: ObservableObject {

private enum Constants {
static let participantLimit = 11 // 10 participants and 1 owner
}

@Injected(\.activeSpaceParticipantStorage)
private var activeSpaceParticipantStorage: ActiveSpaceParticipantStorageProtocol
@Injected(\.workspaceService)
private var workspaceService: WorkspaceServiceProtocol
@Injected(\.activeWorkspaceStorage)
private var activeWorkspaceStorage: ActiveWorkpaceStorageProtocol
@Injected(\.workspaceStorage)
private var workspacesStorage: WorkspacesStorageProtocol
@Injected(\.deepLinkParser)
private var deppLinkParser: DeepLinkParserProtocol
@Injected(\.workspaceStorage)
private var workspaceStorage: WorkspacesStorageProtocol

private var onMoreInfo: () -> Void
private var participants: [Participant] = []
private lazy var workspaceInfo = activeWorkspaceStorage.workspaceInfo
private var spaceView: SpaceView?
private var canChangeWriterToReader = false
private var canChangeReaderToWriter = false
private lazy var workspaceInfo = activeWorkspaceStorage.workspaceInfo

var accountSpaceId: String { workspaceInfo.accountSpaceId }

@Published var rows: [SpaceShareParticipantViewModel] = []
@Published var inviteLink: URL?
@Published var shareInviteLink: URL?
@Published var qrCodeInviteLink: URL?
@Published var allowToAddMembers = false
@Published var toastBarData: ToastBarData = .empty
@Published var requestAlertModel: SpaceRequestAlertData?
@Published var changeAccessAlertModel: SpaceChangeAccessViewModel?
Expand All @@ -48,13 +47,15 @@ final class SpaceShareViewModel: ObservableObject {

func startParticipantsTask() async {
for await items in activeSpaceParticipantStorage.participantsPublisher.values {
updateParticipant(items: items)
participants = items.sorted { $0.sortingWeight > $1.sortingWeight }
updateView()
}
}

func startSpaceViewTask() async {
for await spaceView in workspaceStorage.spaceViewPublisher(spaceId: accountSpaceId).values {
canStopShare = spaceView.canStopShare
for await spaceView in workspacesStorage.spaceViewPublisher(spaceId: accountSpaceId).values {
self.spaceView = spaceView
updateView()
}
}

Expand Down Expand Up @@ -105,8 +106,13 @@ final class SpaceShareViewModel: ObservableObject {

// MARK: - Private

private func updateParticipant(items: [Participant]) {
participants = items.sorted { $0.sortingWeight > $1.sortingWeight }
private func updateView() {
guard let spaceView else { return }

canStopShare = spaceView.canStopShare
canChangeReaderToWriter = spaceView.canChangeReaderToWriter(participants: participants)
canChangeWriterToReader = spaceView.canChangeWriterToReader(participants: participants)

rows = participants.map { participant in
let isYou = workspaceInfo.profileObjectID == participant.identityProfileLink
return SpaceShareParticipantViewModel(
Expand All @@ -118,7 +124,6 @@ final class SpaceShareViewModel: ObservableObject {
contextActions: participantContextActions(participant)
)
}
allowToAddMembers = Constants.participantLimit > items.count
}

private func participantStatus(_ participant: Participant) -> SpaceShareParticipantViewModel.Status? {
Expand Down Expand Up @@ -157,6 +162,7 @@ final class SpaceShareViewModel: ObservableObject {
title: Loc.SpaceShare.Permissions.reader,
isSelected: participant.permission == .reader,
destructive: false,
disabled: !canChangeWriterToReader && participant.permission != .reader,
action: { [weak self] in
self?.showPermissionAlert(participant: participant, newPermission: .reader)
}
Expand All @@ -165,6 +171,7 @@ final class SpaceShareViewModel: ObservableObject {
title: Loc.SpaceShare.Permissions.writer,
isSelected: participant.permission == .writer,
destructive: false,
disabled: !canChangeReaderToWriter && participant.permission != .writer,
action: { [weak self] in
self?.showPermissionAlert(participant: participant, newPermission: .writer)
}
Expand All @@ -173,6 +180,7 @@ final class SpaceShareViewModel: ObservableObject {
title: Loc.SpaceShare.RemoveMember.title,
isSelected: false,
destructive: true,
disabled: false,
action: { [weak self] in
self?.showRemoveAlert(participant: participant)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ struct SpaceRequestAlert: View {

var body: some View {
BottomAlertView(title: model.title, message: "") {
BottomAlertButton(text: Loc.SpaceShare.ViewRequest.viewAccess, style: .secondary) {
BottomAlertButton(text: Loc.SpaceShare.ViewRequest.viewAccess, style: .secondary, disable: !model.canAddReaded) {
try await model.onViewAccess()
dismiss()
}
BottomAlertButton(text: Loc.SpaceShare.ViewRequest.editAccess, style: .secondary) {
BottomAlertButton(text: Loc.SpaceShare.ViewRequest.editAccess, style: .secondary, disable: !model.canAddWriter) {
try await model.onEditAccess()
dismiss()
}
Expand All @@ -33,5 +33,8 @@ struct SpaceRequestAlert: View {
dismiss()
}
}
.throwTask {
try await model.onAppear()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,16 @@ final class SpaceRequestAlertModel: ObservableObject {

@Injected(\.workspaceService)
private var workspaceService: WorkspaceServiceProtocol
@Injected(\.participantService)
private var participantService: ParticipantServiceProtocol
@Injected(\.workspaceStorage)
private var workspaceStorage: WorkspacesStorageProtocol

private let data: SpaceRequestAlertData

@Published var title = ""
@Published var canAddReaded = false
@Published var canAddWriter = false

init(data: SpaceRequestAlertData) {
self.data = data
Expand All @@ -19,6 +25,17 @@ final class SpaceRequestAlertModel: ObservableObject {
)
}

func onAppear() async throws {
guard let spaceView = workspaceStorage.spaceView(spaceId: data.spaceId) else {
throw CommonError.undefined
}
// Don't use participant from active subscription, because active space and space for request can be different
let participants = try await participantService.searchParticipants(spaceId: data.spaceId)

canAddReaded = spaceView.canAddReaders(participants: participants)
canAddWriter = spaceView.canAddWriters(participants: participants)
}

func onViewAccess() async throws {
try await workspaceService.requestApprove(
spaceId: data.spaceId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ struct SpaceShareParticipantViewModel: Identifiable {
let title: String
let isSelected: Bool
let destructive: Bool
let disabled: Bool
let action: () -> Void
}
}
Expand Down Expand Up @@ -86,6 +87,7 @@ struct SpaceShareParticipantView: View {
}
}
}
.disabled(action.disabled)
}

} label: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ final class WorkspacesStorageMock: WorkspacesStorageProtocol {
createdDate: nil,
accountStatus: .spaceActive,
localStatus: .spaceActive,
spaceAccessType: .shared
spaceAccessType: .shared,
readersLimit: nil,
writersLimit: nil
)
]
}
Expand Down
35 changes: 35 additions & 0 deletions Anytype/Sources/ServiceLayer/SpaceStorage/Models/SpaceView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ struct SpaceView: Identifiable, Equatable {
let accountStatus: SpaceStatus?
let localStatus: SpaceStatus?
let spaceAccessType: SpaceAccessType?
let readersLimit: Int?
let writersLimit: Int?
}

extension SpaceView: DetailsModel {
Expand All @@ -22,6 +24,8 @@ extension SpaceView: DetailsModel {
self.accountStatus = details.spaceAccountStatusValue
self.localStatus = details.spaceLocalStatusValue
self.spaceAccessType = details.spaceAccessTypeValue
self.readersLimit = details.readersLimit
self.writersLimit = details.writersLimit
}

static var subscriptionKeys: [BundledRelationKey] = .builder {
Expand All @@ -33,6 +37,8 @@ extension SpaceView: DetailsModel {
BundledRelationKey.spaceAccessType
BundledRelationKey.spaceAccountStatus
BundledRelationKey.spaceLocalStatus
BundledRelationKey.readersLimit
BundledRelationKey.writersLimit
}
}

Expand Down Expand Up @@ -73,4 +79,33 @@ extension SpaceView {
var isActive: Bool {
localStatus == .ok && accountStatus != .spaceRemoving && accountStatus != .spaceDeleted
}

func canAddWriters(participants: [Participant]) -> Bool {
guard canAddReaders(participants: participants) else { return false }
guard let writersLimit else { return true }
return writersLimit > activeWriters(participants: participants)
}

func canAddReaders(participants: [Participant]) -> Bool {
guard let readersLimit else { return true }
return readersLimit > activeReaders(participants: participants)
}

func canChangeWriterToReader(participants: [Participant]) -> Bool {
guard let readersLimit else { return true }
return readersLimit >= activeReaders(participants: participants)
}

func canChangeReaderToWriter(participants: [Participant]) -> Bool {
guard let writersLimit else { return true }
return writersLimit > activeWriters(participants: participants)
}

private func activeReaders(participants: [Participant]) -> Int {
participants.filter { $0.permission == .reader || $0.permission == .writer || $0.permission == .owner }.count
}

private func activeWriters(participants: [Participant]) -> Int {
participants.filter { $0.permission == .writer || $0.permission == .owner }.count
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import Foundation

public protocol ParticipantServiceProtocol: AnyObject {
func searchParticipants(spaceId: String) async throws -> [Participant]
}

final class ParticipantService: ParticipantServiceProtocol {

@Injected(\.searchMiddleService)
private var searchService: SearchMiddleServiceProtocol

func searchParticipants(spaceId: String) async throws -> [Participant] {

let filters: [DataviewFilter] = .builder {
SearchHelper.spaceId(spaceId)
SearchHelper.layoutFilter([.participant])
}

let data = SearchRequest(filters: filters, sorts: [], fullText: "", keys: Participant.subscriptionKeys.map(\.rawValue), limit: 0)
let details = try await searchService.search(data: data)
return details.compactMap { try? Participant(details: $0) }
}
}
4 changes: 4 additions & 0 deletions Modules/Services/Sources/ServicesDI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -115,4 +115,8 @@ public extension Container {
var nameService: Factory<NameServiceProtocol> {
self { NameService() }.shared
}

var participantService: Factory<ParticipantServiceProtocol> {
self { ParticipantService() }.shared
}
}

0 comments on commit bfb32b2

Please sign in to comment.