mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-03 06:55:59 +00:00
Empty spaces list screen (#4936)
* Added an FF to create spaces + empty spaces list state * removed learn more button and improved spacing * updated toolbar for chats and spaces to match ios 26 designs
This commit is contained in:
@@ -21,6 +21,8 @@ struct SpaceListScreenViewState: BindableState {
|
||||
var topLevelSpaces: [SpaceRoomProxyProtocol]
|
||||
var selectedSpaceID: String?
|
||||
|
||||
var isCreateSpaceEnabled: Bool
|
||||
|
||||
var bindings: SpaceListScreenViewStateBindings
|
||||
}
|
||||
|
||||
@@ -33,4 +35,5 @@ enum SpaceListScreenViewAction {
|
||||
case showSettings
|
||||
case screenAppeared
|
||||
case featureAnnouncementAppeared
|
||||
case createSpace
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ class SpaceListScreenViewModel: SpaceListScreenViewModelType, SpaceListScreenVie
|
||||
|
||||
super.init(initialViewState: SpaceListScreenViewState(userID: userSession.clientProxy.userID,
|
||||
topLevelSpaces: spaceServiceProxy.topLevelSpacesPublisher.value,
|
||||
isCreateSpaceEnabled: appSettings.createSpaceEnabled,
|
||||
bindings: .init()),
|
||||
mediaProvider: userSession.mediaProvider)
|
||||
|
||||
@@ -52,6 +53,10 @@ class SpaceListScreenViewModel: SpaceListScreenViewModelType, SpaceListScreenVie
|
||||
.receive(on: DispatchQueue.main)
|
||||
.weakAssign(to: \.state.userDisplayName, on: self)
|
||||
.store(in: &cancellables)
|
||||
|
||||
appSettings.$createSpaceEnabled
|
||||
.weakAssign(to: \.state.isCreateSpaceEnabled, on: self)
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
@@ -73,6 +78,9 @@ class SpaceListScreenViewModel: SpaceListScreenViewModelType, SpaceListScreenVie
|
||||
}
|
||||
case .featureAnnouncementAppeared:
|
||||
appSettings.hasSeenSpacesAnnouncement = true
|
||||
case .createSpace:
|
||||
// TODO: Implement
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,24 +13,46 @@ struct SpaceListScreen: View {
|
||||
@Bindable var context: SpaceListScreenViewModel.Context
|
||||
|
||||
var body: some View {
|
||||
ScrollView {
|
||||
LazyVStack(spacing: 0) {
|
||||
header
|
||||
spaces
|
||||
mainContent
|
||||
.navigationTitle(L10n.screenSpaceListTitle)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar { toolbar }
|
||||
.background(Color.compound.bgCanvasDefault.ignoresSafeArea())
|
||||
.toolbarBloom(hasSearchBar: false)
|
||||
.onAppear { context.send(viewAction: .screenAppeared) }
|
||||
.sheet(isPresented: $context.isPresentingFeatureAnnouncement) {
|
||||
SpacesAnnouncementSheetView(context: context)
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private var mainContent: some View {
|
||||
if context.viewState.isCreateSpaceEnabled, context.viewState.topLevelSpaces.isEmpty {
|
||||
emptyState
|
||||
} else {
|
||||
ScrollView {
|
||||
LazyVStack(spacing: 0) {
|
||||
header
|
||||
spaces
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationTitle(L10n.screenSpaceListTitle)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar { toolbar }
|
||||
.background(Color.compound.bgCanvasDefault.ignoresSafeArea())
|
||||
.toolbarBloom(hasSearchBar: false)
|
||||
.onAppear { context.send(viewAction: .screenAppeared) }
|
||||
.sheet(isPresented: $context.isPresentingFeatureAnnouncement) {
|
||||
SpacesAnnouncementSheetView(context: context)
|
||||
}
|
||||
}
|
||||
|
||||
var header: some View {
|
||||
private var emptyState: some View {
|
||||
FullscreenDialog(horizontalPadding: 24) {
|
||||
TitleAndIcon(title: L10n.screenSpaceListEmptyStateTitle,
|
||||
icon: \.spaceSolid,
|
||||
iconStyle: .defaultSolid)
|
||||
} bottomContent: {
|
||||
Button(L10n.actionCreateSpace) {
|
||||
context.send(viewAction: .createSpace)
|
||||
}
|
||||
.buttonStyle(.compound(.primary))
|
||||
}
|
||||
}
|
||||
|
||||
private var header: some View {
|
||||
VStack(spacing: 16) {
|
||||
BigIcon(icon: \.spaceSolid)
|
||||
|
||||
@@ -62,7 +84,7 @@ struct SpaceListScreen: View {
|
||||
}
|
||||
}
|
||||
|
||||
var spaces: some View {
|
||||
private var spaces: some View {
|
||||
ForEach(context.viewState.topLevelSpaces, id: \.id) { spaceRoomProxy in
|
||||
SpaceRoomCell(spaceRoomProxy: spaceRoomProxy,
|
||||
isSelected: spaceRoomProxy.id == context.viewState.selectedSpaceID,
|
||||
@@ -73,7 +95,7 @@ struct SpaceListScreen: View {
|
||||
}
|
||||
|
||||
@ToolbarContentBuilder
|
||||
var toolbar: some ToolbarContent {
|
||||
private var toolbar: some ToolbarContent {
|
||||
ToolbarItem(placement: .navigationBarLeading) {
|
||||
Button {
|
||||
context.send(viewAction: .showSettings)
|
||||
@@ -95,6 +117,18 @@ struct SpaceListScreen: View {
|
||||
Text("").accessibilityHidden(true)
|
||||
}
|
||||
.backportSharedBackgroundVisibility(.hidden)
|
||||
|
||||
if context.viewState.isCreateSpaceEnabled {
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
Button {
|
||||
context.send(viewAction: .createSpace)
|
||||
} label: {
|
||||
CompoundIcon(\.plus)
|
||||
.accessibilityHidden(true)
|
||||
}
|
||||
.accessibilityLabel(L10n.actionCreateSpace)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,20 +136,31 @@ struct SpaceListScreen: View {
|
||||
|
||||
struct SpaceListScreen_Previews: PreviewProvider, TestablePreview {
|
||||
static let viewModel = makeViewModel()
|
||||
static let emptyViewModel = makeViewModel(isEmpty: true)
|
||||
|
||||
static var previews: some View {
|
||||
NavigationStack {
|
||||
SpaceListScreen(context: viewModel.context)
|
||||
}
|
||||
|
||||
NavigationStack {
|
||||
SpaceListScreen(context: emptyViewModel.context)
|
||||
}
|
||||
.previewDisplayName("Empty")
|
||||
}
|
||||
|
||||
static func makeViewModel() -> SpaceListScreenViewModel {
|
||||
static func makeViewModel(isEmpty: Bool = false) -> SpaceListScreenViewModel {
|
||||
AppSettings.resetAllSettings()
|
||||
let appSettings = AppSettings()
|
||||
appSettings.createSpaceEnabled = true
|
||||
appSettings.hasSeenSpacesAnnouncement = true
|
||||
|
||||
let clientProxy = ClientProxyMock(.init())
|
||||
clientProxy.spaceService = SpaceServiceProxyMock(.init(topLevelSpaces: .mockJoinedSpaces))
|
||||
clientProxy.spaceService = SpaceServiceProxyMock(.init(topLevelSpaces: isEmpty ? [] : .mockJoinedSpaces))
|
||||
|
||||
let viewModel = SpaceListScreenViewModel(userSession: UserSessionMock(.init(clientProxy: clientProxy)),
|
||||
selectedSpacePublisher: .init(nil),
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
appSettings: appSettings,
|
||||
userIndicatorController: UserIndicatorControllerMock())
|
||||
|
||||
return viewModel
|
||||
|
||||
Reference in New Issue
Block a user