mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-03 16:10:20 +00:00
Create space flow (#4957)
* create space flow implementation # Conflicts: # ElementX/Sources/FlowCoordinators/SpacesTabFlowCoordinator.swift * create space flow fully implemented and working * updated tests and updated the create room camera button UI * updated the avatar button in the create room screen, and added power level overrides for spaces * update power level content overrides to behave just as EW, and removed ask to join when creating a space regardless of the FF * updated UI tests snapshots * invite for a public space should always be forced to 50 * pr suggestions + PL override fix * fix a missed code change
This commit is contained in:
@@ -54,8 +54,18 @@ struct CreateRoomScreen: View {
|
||||
.shouldScrollOnKeyboardDidShow(focus == .alias, to: Focus.alias)
|
||||
}
|
||||
|
||||
private var nameTextFieldShape: AnyShape {
|
||||
if #available(iOS 26, *) {
|
||||
AnyShape(ConcentricRectangle(corners: .concentric(minimum: 26)))
|
||||
} else {
|
||||
AnyShape(RoundedRectangle(cornerRadius: 12))
|
||||
}
|
||||
}
|
||||
|
||||
private var roomSection: some View {
|
||||
Section {
|
||||
EmptyView()
|
||||
} header: {
|
||||
HStack(alignment: .center, spacing: 16) {
|
||||
roomAvatarButton
|
||||
let nameLabel = if #available(iOS 26, *) {
|
||||
@@ -80,11 +90,11 @@ struct CreateRoomScreen: View {
|
||||
.accessibilityIdentifier(A11yIdentifiers.createRoomScreen.roomName)
|
||||
.padding(.horizontal, ListRowPadding.horizontal)
|
||||
.padding(.vertical, ListRowPadding.vertical)
|
||||
.background(.compound.bgCanvasDefaultLevel1, in: RoundedRectangle(cornerRadius: 12))
|
||||
.background(.compound.bgCanvasDefaultLevel1, in: nameTextFieldShape)
|
||||
}
|
||||
}
|
||||
.listRowInsets(.init())
|
||||
.listRowBackground(Color.clear)
|
||||
.padding(.top, 16)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,15 +111,27 @@ struct CreateRoomScreen: View {
|
||||
} placeholder: {
|
||||
ProgressView()
|
||||
}
|
||||
.scaledFrame(size: 70)
|
||||
.clipShape(Circle())
|
||||
.scaledFrame(size: 70, relativeTo: .title)
|
||||
.clipShape(context.viewState.isSpace ? AnyShape(RoundedRectangle(cornerRadius: 16)) : AnyShape(Circle()))
|
||||
.overlay(alignment: .bottomTrailing) {
|
||||
editAvatarBadge
|
||||
.scaledOffset(x: 12, y: 4, relativeTo: .title)
|
||||
.accessibilityHidden(true)
|
||||
}
|
||||
} else {
|
||||
CompoundIcon(\.takePhoto, size: .custom(36), relativeTo: .title)
|
||||
.foregroundColor(.compound.iconSecondary)
|
||||
.scaledFrame(size: 70, relativeTo: .title)
|
||||
.background(.compound.bgSubtlePrimary, in: Circle())
|
||||
CompoundIcon(\.takePhoto, size: .medium, relativeTo: .title)
|
||||
.foregroundColor(.compound.iconPrimary)
|
||||
.scaledFrame(size: 50, relativeTo: .title)
|
||||
.background(.compound.bgCanvasDefault, in: Circle())
|
||||
.overlay {
|
||||
Circle()
|
||||
.stroke(.compound.borderInteractiveSecondary, lineWidth: 1)
|
||||
}
|
||||
.padding(10)
|
||||
.accessibilityHidden(true)
|
||||
}
|
||||
}
|
||||
.accessibilityLabel(L10n.a11yEditAvatar)
|
||||
.buttonStyle(.plain)
|
||||
.accessibilityIdentifier(A11yIdentifiers.createRoomScreen.roomAvatar)
|
||||
.confirmationDialog("", isPresented: $context.showAttachmentConfirmationDialog) {
|
||||
@@ -129,6 +151,23 @@ struct CreateRoomScreen: View {
|
||||
}
|
||||
}
|
||||
|
||||
private var editAvatarBadge: some View {
|
||||
CompoundIcon(\.edit, size: .small, relativeTo: .body)
|
||||
.foregroundStyle(.compound.iconPrimary)
|
||||
.scaledPadding(5, relativeTo: .title)
|
||||
.background {
|
||||
Circle()
|
||||
.fill(Color.compound.bgCanvasDefault)
|
||||
.overlay {
|
||||
Circle()
|
||||
.inset(by: 0.5)
|
||||
.stroke(.compound.borderInteractiveSecondary, lineWidth: 1)
|
||||
}
|
||||
}
|
||||
.scaledPadding(3.5, relativeTo: .title)
|
||||
.background(.compound.bgSubtleSecondaryLevel0, in: Circle())
|
||||
}
|
||||
|
||||
private var topicSection: some View {
|
||||
Section {
|
||||
ListRow(label: .plain(title: L10n.screenCreateRoomTopicPlaceholder),
|
||||
@@ -180,9 +219,18 @@ struct CreateRoomScreen: View {
|
||||
}
|
||||
}
|
||||
|
||||
@ToolbarContentBuilder
|
||||
private var toolbar: some ToolbarContent {
|
||||
if context.viewState.shouldShowCancelButton {
|
||||
ToolbarItem(placement: .topBarLeading) {
|
||||
ToolbarButton(role: .cancel) {
|
||||
context.send(viewAction: .dismiss)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ToolbarItem(placement: .confirmationAction) {
|
||||
Button(L10n.actionCreate) {
|
||||
ToolbarButton(role: .confirm(title: L10n.actionCreate)) {
|
||||
focus = nil
|
||||
context.send(viewAction: .createRoom)
|
||||
}
|
||||
@@ -247,26 +295,55 @@ struct CreateRoom_Previews: PreviewProvider, TestablePreview {
|
||||
AppSettings.resetAllSettings()
|
||||
let userSession = UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userID: "@userid:example.com"))))
|
||||
return CreateRoomScreenViewModel(isSpace: false,
|
||||
shouldShowCancelButton: false,
|
||||
userSession: userSession,
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: UserIndicatorControllerMock(),
|
||||
appSettings: ServiceLocator.shared.settings)
|
||||
}()
|
||||
|
||||
static let avatarViewModel = {
|
||||
AppSettings.resetAllSettings()
|
||||
let userSession = UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userID: "@userid:example.com"))))
|
||||
let viewModel = CreateRoomScreenViewModel(isSpace: false,
|
||||
shouldShowCancelButton: false,
|
||||
userSession: userSession,
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: UserIndicatorControllerMock(),
|
||||
appSettings: ServiceLocator.shared.settings)
|
||||
viewModel.updateAvatar(fileURL: Bundle.main.url(forResource: "preview_avatar_room", withExtension: "jpg")!)
|
||||
return viewModel
|
||||
}()
|
||||
|
||||
static let spaceViewModel = {
|
||||
AppSettings.resetAllSettings()
|
||||
let userSession = UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userID: "@userid:example.com"))))
|
||||
return CreateRoomScreenViewModel(isSpace: true,
|
||||
shouldShowCancelButton: true,
|
||||
userSession: userSession,
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: UserIndicatorControllerMock(),
|
||||
appSettings: ServiceLocator.shared.settings)
|
||||
}()
|
||||
|
||||
static let spaceWithAvatarViewModel = {
|
||||
AppSettings.resetAllSettings()
|
||||
let userSession = UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userID: "@userid:example.com"))))
|
||||
let viewModel = CreateRoomScreenViewModel(isSpace: true,
|
||||
shouldShowCancelButton: true,
|
||||
userSession: userSession,
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: UserIndicatorControllerMock(),
|
||||
appSettings: ServiceLocator.shared.settings)
|
||||
viewModel.updateAvatar(fileURL: Bundle.main.url(forResource: "preview_avatar_room", withExtension: "jpg")!)
|
||||
return viewModel
|
||||
}()
|
||||
|
||||
static let publicRoomViewModel = {
|
||||
AppSettings.resetAllSettings()
|
||||
let userSession = UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userIDServerName: "example.org", userID: "@userid:example.com"))))
|
||||
let viewModel = CreateRoomScreenViewModel(isSpace: false,
|
||||
shouldShowCancelButton: false,
|
||||
userSession: userSession,
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: UserIndicatorControllerMock(),
|
||||
@@ -281,6 +358,7 @@ struct CreateRoom_Previews: PreviewProvider, TestablePreview {
|
||||
appSettings.knockingEnabled = true
|
||||
let userSession = UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userIDServerName: "example.org", userID: "@userid:example.com"))))
|
||||
let viewModel = CreateRoomScreenViewModel(isSpace: false,
|
||||
shouldShowCancelButton: false,
|
||||
userSession: userSession,
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: UserIndicatorControllerMock(),
|
||||
@@ -293,6 +371,7 @@ struct CreateRoom_Previews: PreviewProvider, TestablePreview {
|
||||
AppSettings.resetAllSettings()
|
||||
let userSession = UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userIDServerName: "example.org", userID: "@userid:example.com"))))
|
||||
let viewModel = CreateRoomScreenViewModel(isSpace: false,
|
||||
shouldShowCancelButton: false,
|
||||
userSession: userSession,
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: UserIndicatorControllerMock(),
|
||||
@@ -308,6 +387,7 @@ struct CreateRoom_Previews: PreviewProvider, TestablePreview {
|
||||
clientProxy.isAliasAvailableReturnValue = .success(false)
|
||||
let userSession = UserSessionMock(.init(clientProxy: clientProxy))
|
||||
let viewModel = CreateRoomScreenViewModel(isSpace: false,
|
||||
shouldShowCancelButton: false,
|
||||
userSession: userSession,
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: UserIndicatorControllerMock(),
|
||||
@@ -323,11 +403,23 @@ struct CreateRoom_Previews: PreviewProvider, TestablePreview {
|
||||
}
|
||||
.previewDisplayName("Create Room")
|
||||
|
||||
NavigationStack {
|
||||
CreateRoomScreen(context: avatarViewModel.context)
|
||||
}
|
||||
.previewDisplayName("Create Room with avatar")
|
||||
.snapshotPreferences(expect: avatarViewModel.context.$viewState.map { $0.avatarMediaInfo != nil })
|
||||
|
||||
NavigationStack {
|
||||
CreateRoomScreen(context: spaceViewModel.context)
|
||||
}
|
||||
.previewDisplayName("Create Space")
|
||||
|
||||
NavigationStack {
|
||||
CreateRoomScreen(context: spaceWithAvatarViewModel.context)
|
||||
}
|
||||
.previewDisplayName("Create Space with avatar")
|
||||
.snapshotPreferences(expect: spaceWithAvatarViewModel.context.$viewState.map { $0.avatarMediaInfo != nil })
|
||||
|
||||
NavigationStack {
|
||||
CreateRoomScreen(context: publicRoomViewModel.context)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user