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:
Mauro
2026-01-09 15:17:43 +01:00
committed by GitHub
parent 082a2f850e
commit 2d1d62552a
79 changed files with 306 additions and 175 deletions

View File

@@ -67,6 +67,7 @@
"action_copy_text" = "Copy text";
"action_create" = "Create";
"action_create_a_room" = "Create a room";
"action_create_space" = "Create space";
"action_deactivate" = "Deactivate";
"action_deactivate_account" = "Deactivate account";
"action_decline" = "Decline";
@@ -188,6 +189,7 @@
"common_copied_to_clipboard" = "Copied to clipboard";
"common_copyright" = "Copyright";
"common_creating_room" = "Creating room…";
"common_creating_space" = "Creating space…";
"common_current_user_canceled_knock" = "Request canceled";
"common_current_user_left_room" = "Left room";
"common_current_user_left_space" = "Left space";
@@ -307,6 +309,7 @@
"common_starting_chat" = "Starting chat…";
"common_sticker" = "Sticker";
"common_success" = "Success";
"common_suggested" = "Suggested";
"common_suggestions" = "Suggestions";
"common_syncing" = "Syncing";
"common_system" = "System";
@@ -526,14 +529,19 @@
"screen_create_poll_options_section_title" = "Options";
"screen_create_poll_remove_accessibility_label" = "Remove %1$@";
"screen_create_poll_settings_section_title" = "Settings";
"screen_create_room_room_access_section_anyone_option_description" = "Anyone can join this room";
"screen_create_room_error_creating_space" = "The space could not be created because of an unknown error. Try again later.";
"screen_create_room_new_space_title" = "New space";
"screen_create_room_public_option_short_description" = "Anyone can join.";
"screen_create_room_room_access_section_anyone_option_description" = "Anyone can join.";
"screen_create_room_room_access_section_anyone_option_title" = "Anyone";
"screen_create_room_room_access_section_header" = "Room Access";
"screen_create_room_room_access_section_knocking_option_description" = "Anyone can ask to join the room but an administrator or a moderator will have to accept the request";
"screen_create_room_room_access_section_knocking_option_description" = "Anyone can ask to join but an administrator or a moderator must accept the request.";
"screen_create_room_room_access_section_knocking_option_title" = "Ask to join";
"screen_create_room_room_access_section_title" = "Who has access";
"screen_create_room_room_address_section_footer" = "In order for this room to be visible in the public room directory, you will need a room address.";
"screen_create_room_room_address_section_title" = "Room address";
"screen_create_room_room_visibility_section_title" = "Room visibility";
"screen_create_room_title_new_space" = "New space";
"screen_decline_and_block_block_user_option_description" = "You will not see any messages or room invites from this user";
"screen_decline_and_block_report_user_option_description" = "Report this room to your account provider.";
"screen_edit_room_address_room_address_section_footer" = "Youll need an address in order to make it visible in the public directory.";
@@ -712,6 +720,7 @@
"screen_space_announcement_title" = "Introducing Spaces";
"screen_space_list_description" = "Spaces you have created or joined.";
"screen_space_list_details" = "%1$@ • %2$@";
"screen_space_list_empty_state_title" = "Create spaces to organize rooms";
"screen_space_list_parent_space" = "%1$@ space";
"screen_space_list_title" = "Spaces";
"screen_start_chat_join_room_by_address_action" = "Join room by address";
@@ -835,8 +844,10 @@
"screen_create_poll_title" = "Create Poll";
"screen_create_room_action_create_room" = "New room";
"screen_create_room_error_creating_room" = "An error occurred when creating the room";
"screen_create_room_private_option_description" = "Only people invited can access this room. All messages are end-to-end encrypted.";
"screen_create_room_private_option_description" = "Only people invited can join.";
"screen_create_room_private_option_title" = "Private";
"screen_create_room_public_option_description" = "Anyone can find this room.\nYou can change this anytime in room settings.";
"screen_create_room_public_option_title" = "Public";
"screen_create_room_topic_label" = "Topic (optional)";
"screen_deactivate_account_confirmation_dialog_content" = "Please confirm that you want to deactivate your account. This action cannot be undone.";
"screen_deactivate_account_delete_all_messages" = "Delete all my messages";
@@ -1434,8 +1445,6 @@
"screen_create_account_title" = "Create account";
"screen_create_poll_cancel_confirmation_content_ios" = "Your changes wont be saved";
"screen_create_room_add_people_title" = "Invite people";
"screen_create_room_private_option_title" = "Private room";
"screen_create_room_public_option_title" = "Public room";
"screen_create_room_room_name_label" = "Room name";
"screen_create_room_title" = "Create a room";
"screen_deactivate_account_title" = "Deactivate account";

View File

@@ -67,6 +67,7 @@
"action_copy_text" = "Copy text";
"action_create" = "Create";
"action_create_a_room" = "Create a room";
"action_create_space" = "Create space";
"action_deactivate" = "Deactivate";
"action_deactivate_account" = "Deactivate account";
"action_decline" = "Decline";
@@ -188,6 +189,7 @@
"common_copied_to_clipboard" = "Copied to clipboard";
"common_copyright" = "Copyright";
"common_creating_room" = "Creating room…";
"common_creating_space" = "Creating space…";
"common_current_user_canceled_knock" = "Request canceled";
"common_current_user_left_room" = "Left room";
"common_current_user_left_space" = "Left space";
@@ -307,6 +309,7 @@
"common_starting_chat" = "Starting chat…";
"common_sticker" = "Sticker";
"common_success" = "Success";
"common_suggested" = "Suggested";
"common_suggestions" = "Suggestions";
"common_syncing" = "Syncing";
"common_system" = "System";
@@ -526,14 +529,19 @@
"screen_create_poll_options_section_title" = "Options";
"screen_create_poll_remove_accessibility_label" = "Remove %1$@";
"screen_create_poll_settings_section_title" = "Settings";
"screen_create_room_room_access_section_anyone_option_description" = "Anyone can join this room";
"screen_create_room_error_creating_space" = "The space could not be created because of an unknown error. Try again later.";
"screen_create_room_new_space_title" = "New space";
"screen_create_room_public_option_short_description" = "Anyone can join.";
"screen_create_room_room_access_section_anyone_option_description" = "Anyone can join.";
"screen_create_room_room_access_section_anyone_option_title" = "Anyone";
"screen_create_room_room_access_section_header" = "Room Access";
"screen_create_room_room_access_section_knocking_option_description" = "Anyone can ask to join the room but an administrator or a moderator will have to accept the request";
"screen_create_room_room_access_section_knocking_option_description" = "Anyone can ask to join but an administrator or a moderator must accept the request.";
"screen_create_room_room_access_section_knocking_option_title" = "Ask to join";
"screen_create_room_room_access_section_title" = "Who has access";
"screen_create_room_room_address_section_footer" = "In order for this room to be visible in the public room directory, you will need a room address.";
"screen_create_room_room_address_section_title" = "Room address";
"screen_create_room_room_visibility_section_title" = "Room visibility";
"screen_create_room_title_new_space" = "New space";
"screen_decline_and_block_block_user_option_description" = "You will not see any messages or room invites from this user";
"screen_decline_and_block_report_user_option_description" = "Report this room to your account provider.";
"screen_edit_room_address_room_address_section_footer" = "Youll need an address in order to make it visible in the public directory.";
@@ -712,6 +720,7 @@
"screen_space_announcement_title" = "Introducing Spaces";
"screen_space_list_description" = "Spaces you have created or joined.";
"screen_space_list_details" = "%1$@ • %2$@";
"screen_space_list_empty_state_title" = "Create spaces to organize rooms";
"screen_space_list_parent_space" = "%1$@ space";
"screen_space_list_title" = "Spaces";
"screen_start_chat_join_room_by_address_action" = "Join room by address";
@@ -835,8 +844,10 @@
"screen_create_poll_title" = "Create Poll";
"screen_create_room_action_create_room" = "New room";
"screen_create_room_error_creating_room" = "An error occurred when creating the room";
"screen_create_room_private_option_description" = "Only people invited can access this room. All messages are end-to-end encrypted.";
"screen_create_room_private_option_description" = "Only people invited can join.";
"screen_create_room_private_option_title" = "Private";
"screen_create_room_public_option_description" = "Anyone can find this room.\nYou can change this anytime in room settings.";
"screen_create_room_public_option_title" = "Public";
"screen_create_room_topic_label" = "Topic (optional)";
"screen_deactivate_account_confirmation_dialog_content" = "Please confirm that you want to deactivate your account. This action cannot be undone.";
"screen_deactivate_account_delete_all_messages" = "Delete all my messages";
@@ -1434,8 +1445,6 @@
"screen_create_account_title" = "Create account";
"screen_create_poll_cancel_confirmation_content_ios" = "Your changes wont be saved";
"screen_create_room_add_people_title" = "Invite people";
"screen_create_room_private_option_title" = "Private room";
"screen_create_room_public_option_title" = "Public room";
"screen_create_room_room_name_label" = "Room name";
"screen_create_room_title" = "Create a room";
"screen_deactivate_account_title" = "Deactivate account";

View File

@@ -65,10 +65,13 @@ final class AppSettings {
case threadsEnabled
case developerOptionsEnabled
case linkPreviewsEnabled
case spaceSettingsEnabled
case focusEventOnNotificationTap
case linkNewDeviceEnabled
// Spaces
case spaceSettingsEnabled
case createSpaceEnabled
// Doug's tweaks 🔧
case hideUnreadMessagesBadge
case hideQuietNotificationAlerts
@@ -377,6 +380,14 @@ final class AppSettings {
// MARK: - Feature Flags
// Spaces
@UserPreference(key: UserDefaultsKeys.spaceSettingsEnabled, defaultValue: false, storageType: .userDefaults(store))
var spaceSettingsEnabled
@UserPreference(key: UserDefaultsKeys.createSpaceEnabled, defaultValue: false, storageType: .userDefaults(store))
var createSpaceEnabled
// Others
@UserPreference(key: UserDefaultsKeys.publicSearchEnabled, defaultValue: false, storageType: .userDefaults(store))
var publicSearchEnabled
@@ -403,9 +414,6 @@ final class AppSettings {
@UserPreference(key: UserDefaultsKeys.focusEventOnNotificationTap, defaultValue: false, storageType: .userDefaults(store))
var focusEventOnNotificationTap
@UserPreference(key: UserDefaultsKeys.spaceSettingsEnabled, defaultValue: false, storageType: .userDefaults(store))
var spaceSettingsEnabled
@UserPreference(key: UserDefaultsKeys.linkPreviewsEnabled, defaultValue: false, storageType: .userDefaults(store))
var linkPreviewsEnabled

View File

@@ -288,7 +288,10 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
.store(in: &cancellables)
userSession.clientProxy.spaceService.topLevelSpacesPublisher
.map { $0.isEmpty ? .hidden : nil }
.combineLatest(flowParameters.appSettings.$createSpaceEnabled)
.map { topLevelSpaces, isCreateSpaceEnabled in
!isCreateSpaceEnabled && topLevelSpaces.isEmpty ? .hidden : nil
}
.weakAssign(to: \.chatsTabDetails.barVisibilityOverride, on: self)
.store(in: &cancellables)
}

View File

@@ -168,6 +168,8 @@ internal enum L10n {
internal static var actionCreate: String { return L10n.tr("Localizable", "action_create") }
/// Create a room
internal static var actionCreateARoom: String { return L10n.tr("Localizable", "action_create_a_room") }
/// Create space
internal static var actionCreateSpace: String { return L10n.tr("Localizable", "action_create_space") }
/// Deactivate
internal static var actionDeactivate: String { return L10n.tr("Localizable", "action_deactivate") }
/// Deactivate account
@@ -420,6 +422,8 @@ internal enum L10n {
internal static var commonCopyright: String { return L10n.tr("Localizable", "common_copyright") }
/// Creating room
internal static var commonCreatingRoom: String { return L10n.tr("Localizable", "common_creating_room") }
/// Creating space
internal static var commonCreatingSpace: String { return L10n.tr("Localizable", "common_creating_space") }
/// Request canceled
internal static var commonCurrentUserCanceledKnock: String { return L10n.tr("Localizable", "common_current_user_canceled_knock") }
/// Left room
@@ -696,6 +700,8 @@ internal enum L10n {
internal static var commonSticker: String { return L10n.tr("Localizable", "common_sticker") }
/// Success
internal static var commonSuccess: String { return L10n.tr("Localizable", "common_success") }
/// Suggested
internal static var commonSuggested: String { return L10n.tr("Localizable", "common_suggested") }
/// Suggestions
internal static var commonSuggestions: String { return L10n.tr("Localizable", "common_suggestions") }
/// Syncing
@@ -1559,25 +1565,33 @@ internal enum L10n {
internal static var screenCreateRoomAddPeopleTitle: String { return L10n.tr("Localizable", "screen_create_room_add_people_title") }
/// An error occurred when creating the room
internal static var screenCreateRoomErrorCreatingRoom: String { return L10n.tr("Localizable", "screen_create_room_error_creating_room") }
/// Only people invited can access this room. All messages are end-to-end encrypted.
/// The space could not be created because of an unknown error. Try again later.
internal static var screenCreateRoomErrorCreatingSpace: String { return L10n.tr("Localizable", "screen_create_room_error_creating_space") }
/// New space
internal static var screenCreateRoomNewSpaceTitle: String { return L10n.tr("Localizable", "screen_create_room_new_space_title") }
/// Only people invited can join.
internal static var screenCreateRoomPrivateOptionDescription: String { return L10n.tr("Localizable", "screen_create_room_private_option_description") }
/// Private room
/// Private
internal static var screenCreateRoomPrivateOptionTitle: String { return L10n.tr("Localizable", "screen_create_room_private_option_title") }
/// Anyone can find this room.
/// You can change this anytime in room settings.
internal static var screenCreateRoomPublicOptionDescription: String { return L10n.tr("Localizable", "screen_create_room_public_option_description") }
/// Public room
/// Anyone can join.
internal static var screenCreateRoomPublicOptionShortDescription: String { return L10n.tr("Localizable", "screen_create_room_public_option_short_description") }
/// Public
internal static var screenCreateRoomPublicOptionTitle: String { return L10n.tr("Localizable", "screen_create_room_public_option_title") }
/// Anyone can join this room
/// Anyone can join.
internal static var screenCreateRoomRoomAccessSectionAnyoneOptionDescription: String { return L10n.tr("Localizable", "screen_create_room_room_access_section_anyone_option_description") }
/// Anyone
internal static var screenCreateRoomRoomAccessSectionAnyoneOptionTitle: String { return L10n.tr("Localizable", "screen_create_room_room_access_section_anyone_option_title") }
/// Room Access
internal static var screenCreateRoomRoomAccessSectionHeader: String { return L10n.tr("Localizable", "screen_create_room_room_access_section_header") }
/// Anyone can ask to join the room but an administrator or a moderator will have to accept the request
/// Anyone can ask to join but an administrator or a moderator must accept the request.
internal static var screenCreateRoomRoomAccessSectionKnockingOptionDescription: String { return L10n.tr("Localizable", "screen_create_room_room_access_section_knocking_option_description") }
/// Ask to join
internal static var screenCreateRoomRoomAccessSectionKnockingOptionTitle: String { return L10n.tr("Localizable", "screen_create_room_room_access_section_knocking_option_title") }
/// Who has access
internal static var screenCreateRoomRoomAccessSectionTitle: String { return L10n.tr("Localizable", "screen_create_room_room_access_section_title") }
/// In order for this room to be visible in the public room directory, you will need a room address.
internal static var screenCreateRoomRoomAddressSectionFooter: String { return L10n.tr("Localizable", "screen_create_room_room_address_section_footer") }
/// Room address
@@ -1588,6 +1602,8 @@ internal enum L10n {
internal static var screenCreateRoomRoomVisibilitySectionTitle: String { return L10n.tr("Localizable", "screen_create_room_room_visibility_section_title") }
/// Create a room
internal static var screenCreateRoomTitle: String { return L10n.tr("Localizable", "screen_create_room_title") }
/// New space
internal static var screenCreateRoomTitleNewSpace: String { return L10n.tr("Localizable", "screen_create_room_title_new_space") }
/// Topic (optional)
internal static var screenCreateRoomTopicLabel: String { return L10n.tr("Localizable", "screen_create_room_topic_label") }
/// Please confirm that you want to deactivate your account. This action cannot be undone.
@@ -3113,6 +3129,8 @@ internal enum L10n {
internal static func screenSpaceListDetails(_ p1: Any, _ p2: Any) -> String {
return L10n.tr("Localizable", "screen_space_list_details", String(describing: p1), String(describing: p2))
}
/// Create spaces to organize rooms
internal static var screenSpaceListEmptyStateTitle: String { return L10n.tr("Localizable", "screen_space_list_empty_state_title") }
/// %1$@ space
internal static func screenSpaceListParentSpace(_ p1: Any) -> String {
return L10n.tr("Localizable", "screen_space_list_parent_space", String(describing: p1))

View File

@@ -87,7 +87,11 @@ enum UserAvatarSizeOnScreen {
var value: CGFloat {
switch self {
case .chats, .spaces:
if #available(iOS 26, *) {
return 40
} else {
return 32
}
case .timeline:
return 32
case .readReceipt:

View File

@@ -40,9 +40,13 @@ struct HomeScreen: View {
.backportSharedBackgroundVisibility(.hidden)
ToolbarItem(placement: .primaryAction) {
if #available(iOS 26, *) {
newRoomButton
} else {
newRoomButton
.buttonStyle(.compound(.super, size: .toolbarIcon))
}
}
.backportSharedBackgroundVisibility(.hidden)
}
private var settingsButton: some View {
@@ -71,7 +75,6 @@ struct HomeScreen: View {
} label: {
CompoundIcon(\.plus)
}
.buttonStyle(.compound(.super, size: .toolbarIcon))
.accessibilityLabel(L10n.actionStartChat)
.accessibilityIdentifier(A11yIdentifiers.homeScreen.startChat)
default:

View File

@@ -66,8 +66,10 @@ protocol DeveloperOptionsProtocol: AnyObject {
var linkPreviewsEnabled: Bool { get set }
var spaceSettingsEnabled: Bool { get set }
var linkNewDeviceEnabled: Bool { get set }
var spaceSettingsEnabled: Bool { get set }
var createSpaceEnabled: Bool { get set }
}
extension AppSettings: DeveloperOptionsProtocol { }

View File

@@ -45,15 +45,22 @@ struct DeveloperOptionsScreen: View {
Toggle(isOn: $context.linkNewDeviceEnabled) {
Text("Link new device with QR code")
}
Toggle(isOn: $context.spaceSettingsEnabled) {
Text("Space settings")
}
context.viewState.appHooks
.developerOptionsScreenHook
.generalSectionRows()
}
Section("Spaces") {
Toggle(isOn: $context.spaceSettingsEnabled) {
Text("Space settings")
}
Toggle(isOn: $context.createSpaceEnabled) {
Text("Create space")
}
}
Section("Room List") {
Toggle(isOn: $context.publicSearchEnabled) {
Text("Public search")

View File

@@ -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
}

View File

@@ -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
}
}

View File

@@ -13,12 +13,7 @@ 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 }
@@ -30,7 +25,34 @@ struct SpaceListScreen: View {
}
}
var header: some View {
@ViewBuilder
private var mainContent: some View {
if context.viewState.isCreateSpaceEnabled, context.viewState.topLevelSpaces.isEmpty {
emptyState
} else {
ScrollView {
LazyVStack(spacing: 0) {
header
spaces
}
}
}
}
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

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:78e00b439c03aa3f2349c0a2af4bf373a7af56682af9042afabeab3eea32f021
size 195329
oid sha256:0f8d601c193367fe6e2fcbcafeea76da0481328d4c63d6c6f3a5106f9869cc0f
size 186260

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3071536f84f6e9d94dffb09ebbb1a0dd1fbd5108b475ac26568896b9346a3bb2
size 228448
oid sha256:fc25f9648a4c81cf21d0d420fb08825f96d6a54fc1eedc589c673866a67af784
size 224957

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b8c598b2070478dedfae6611bfdc2f45da8524c148afef32e6eb1687fd3894e6
size 125330
oid sha256:98c9a9ee2f1c0d78a24a5e096c11f72f2467ee22c66f9fa26b84272732eb339c
size 121268

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:985148779908daaa2aa0543d7c858045dcb43ba8ff8f61d01a9bf902bdf9e587
size 158640
oid sha256:33ac15d63ca2d46ba68e087e712442761bc40feecba310825e661b97b59cfba2
size 144352

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c767a4532d238a563d691d7cb72e2702b9af35977cf3970e077b53c41e432880
size 189393
oid sha256:9df32336b119eb58e4aa3a66280b944c6b54714509fab4d843fd16304d5292ab
size 180274

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:41ab865fd4f782b5b06ee46056319117cef7411e455926bf4061a43fd2590a43
size 222650
oid sha256:21fc629bec2b888ab69f895fbb724c862383c67e5c8bfc3702f2a82ed046b5c3
size 219688

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e6c7229a28ba1943611ff7382de9ef01e8f6b5aec3b08f37b310f16f0336f2be
size 122498
oid sha256:f5b29aa43bf4f260ca92827715079a66a2a721329510bdadb6e732ad6bbc067b
size 114041

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:11661d3980269ae9b094edd441e776e0bb8e0a7417657dc03414801142a115ac
size 155952
oid sha256:5e00046d8a037d82931c163483cb96238476af973db9c36b66f6253b2dc94459
size 141659

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:22dde56109718453a649b1d53f6ac7afbd717ec182c310a7e3725023d123bde1
size 198660
oid sha256:4c67f6b214cc6d321d3566b5c02f3f8a226bfd0745cd5092f45c7c3e1a73fb32
size 189590

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:9628aa7e10e09546fd70363122d68a9157cfeba659c0a0902eafe83d2fd1b06f
size 226504
oid sha256:0018ab26fdd70f668853b04b10575aa018cb8dcb7cc3863350108354e0735121
size 227012

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b8c598b2070478dedfae6611bfdc2f45da8524c148afef32e6eb1687fd3894e6
size 125330
oid sha256:55dff7dd30c3bbe8ba5a4791ecd30bad37947f6209daf4ef2c8d0dcc154dc4b0
size 119611

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:985148779908daaa2aa0543d7c858045dcb43ba8ff8f61d01a9bf902bdf9e587
size 158640
oid sha256:33ac15d63ca2d46ba68e087e712442761bc40feecba310825e661b97b59cfba2
size 144352

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3ab01c42e9e3d8aa5305abd1d5e5fe70608802463aa2cabc4448b282ba0a803e
size 144630
oid sha256:fdc1e9766a4dde854f05b4b42c3fe6d9faf451813a0230b10dfc241b20038ad7
size 138315

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b1f86afc9813bab80ab2b15c33051f92c967364f965fb874533a80c737ee022a
size 172262
oid sha256:6f5c3ffd80810d57d9dceb6f64189b30cf5e60628b48e6968896dacfc53a0e17
size 161485

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:310d41f8d5173b4c8b9cd3a08ce559f489103885db83cc9624f9fe3c31e16afc
size 94882
oid sha256:572a0649d2ba4efa3056ce34a822d4b1237891fc15a538f80ac51ff6948952cc
size 86254

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f799b1832fd464baa18b8e1fac7c5662636d595519651e5fd763462098fe0f18
size 125146
oid sha256:968186dc96ec4e01fe52ff68ef441f85f48ad2d776ade28d983e650ec4b585c8
size 106420

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e70b6dff1fb8568014b12407023bdc0bc91f380b87a11310352cc487d55f8876
size 121361
oid sha256:e49adca0a1b9ac22eefa058b81a3643fb16d34474ff85a86e24562ed0d5dbbd0
size 118155

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:86b7e7a0b91f0cbc220c9cb9f98de51c9445c8a4f0ab4feed9ca113f6105a70f
size 125003
oid sha256:4dd649afd0e1220aa783196a449ffa81c498bce7f80fe4cff2d2f312a2867407
size 121797

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:041bf134550c641ed1ec3ec514db24b78df9500fd94efd7fb5bae1d86bbf318b
size 63255
oid sha256:885dc9cb49642f063000a855f098c67be175976ef8ab8520efa924eff516415b
size 61346

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3a20021af2b2e38c5e56976a0132f55a959f5ad8569141c3e9c6d7fe5fe42ead
size 71527
oid sha256:64e9d67eb7cce3b407ffcfd671e50c6fd9f22e3660bb258ca8c7adf28ad3d53d
size 69613

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:9fbdead7fed5259d63060b37954b08fc95c86434d4e9bd206de59df06f1d25d1
size 299991
oid sha256:a1d810fe1bf7305326cca252dbe17b24650faf04e446d34bf88e70d2092f7a05
size 298434

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:760b39b226f578fe3757258fa2643e0c2e47b9abed6bd50004368af69e2c244a
size 309763
oid sha256:411eca90bd743660179df9f8663ce862158fe20faf3b50bd6b603aba4f99a3f2
size 308200

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:2b64c2fa140bef7a99e5fa07ce8bd37bd781e7b16eca0780b93c022dbd522de4
size 208131
oid sha256:f42f9de17a2548676fdcbf416589fc6c1a9e9cffec13286760146d1a9ed1afa2
size 206610

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:35dbbfbdea0a3b046a5cf5eabc4adae2e2d5116ea978d4dc2d6150b6bac34591
size 213044
oid sha256:aad81015a0079425cc6346de9ea142429fc08c407e5a872c6d1a3fd2765edbf2
size 211474

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6548c76259a0435a28203168c9dcbb8fa84da3f28ebe6b03aa720e09a33c4670
size 118603
oid sha256:d605e43413d5f4b87bef5559980f739eb33e9945c144c5ff915834098801655c
size 118807

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6548c76259a0435a28203168c9dcbb8fa84da3f28ebe6b03aa720e09a33c4670
size 118603
oid sha256:d605e43413d5f4b87bef5559980f739eb33e9945c144c5ff915834098801655c
size 118807

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d209719fc05148f50212a1357dc297c437da9405ffc04ea95236d03064bcaca5
size 59939
oid sha256:b6629f155f1f13d3e0452390adbbffb3e344e238173b64fe13764be66753d047
size 60488

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d209719fc05148f50212a1357dc297c437da9405ffc04ea95236d03064bcaca5
size 59939
oid sha256:b6629f155f1f13d3e0452390adbbffb3e344e238173b64fe13764be66753d047
size 60488

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:734d18a6d289e4b726746bdc4a6447d2d44c07b68b5aa3619e41d20743b2be43
size 211278
oid sha256:ca2c5743874b7c404c06b23bf133fb77d5f61812b1d55c2d5170b968915694ac
size 211272

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6dea2512291fb291cb74cddf5acaead1776d7286e0fbd51d65d811ef3abe7f3d
size 262252
oid sha256:7ebf814a7cb3d144e7c5172fc889114d41929dacf8068458b62152f832ff426d
size 260403

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7b8c656ed05bb2a51e925ae50156fc786c291451669942fec45ff0877c8d97b6
size 147337
oid sha256:58da8eeaec2f85afc1ecf3e762c79d94008b5fe725ae72e00aaf965858ef6b7c
size 147164

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:befced025b5ae7210f72dfa9ec01b514ced1a64e20c22120ecde4636fd4f89d4
size 179658
oid sha256:b877cc4e12ac4afefb2ef0793f09919e9b81127a9eaa8cb221ebe2138b6b5a3f
size 179833

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b0aa0475b725e6098fa24ac761fd3f49911b383c9570c8bb13db4f3377a01d95
size 173106
oid sha256:200ee11a5dd56886be8381f1a129b67c250d47a6eaefaedf82127973bb4f8a16
size 173112

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:dade28c297a22c9673d1cfc612068767bf018c204a34911a2e7548f42ccc694d
size 215263
oid sha256:7d348350791ed25e7e5db970a8128dda7f94c41216fb0f46f70f6c8b13540c78
size 213524

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4077cfd59711750fdb1fe0b7db83d0ba40d037ad1f5bb90003f5cf5be256e71c
size 118589
oid sha256:614305ed71ccea6133304c2b4caa9130437eb6ca55c7f01aba34cd78e678cca2
size 118448

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0194852268a016bfa844a4078bf8aa1e800e458a33bf082597f3ad8d81d118b4
size 176329
oid sha256:40bfe29281241156f291ebe633b913768441de7fa93d45e9cc98017373fcc842
size 176498

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0c822fd09de359fbb83a50d234ef6631c716bab8a92002844fbf19209f762154
size 114486

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a639262d2a1168ce5fc49d6d7742ae64d64d49cd49a54893fca17c93c0cc3893
size 121829

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:76cad63f816fd5e18b96434f5a27b68f46fdd2a2effd127b8e2c33108c0a9e98
size 195441
oid sha256:a6836c92e2c4d7a67aa39d51a82612a7831d64e8c68a0e8e9b7a0605d95f4787
size 196165

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:9522d3b3995c364844c505294588d34ef4a06cee98e9a8fe5aaeb2c26b8dc68d
size 228422
oid sha256:d80389d5af6f4ce7d137b587db88504ba40f2573601afc3dc1a64df333c4d0aa
size 229132

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:299562bd2f5598ddc7b09e3c6e720708c2d4ad0995e759dcf33818cbfebb091e
size 198323
oid sha256:43381daca1607ec68fc02e2d1287c9e3323fc8c44b168cb585a1378cc29724e3
size 188246

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:5d1fe9de52ba0ad7a655ee15c5a19394555a29427dc28f7246482313708ad9fe
size 195226
oid sha256:6288614b6c59539ad70d8e8beb7c733fa5c4d5f4bf2b630eefb8394dd5cbc4f1
size 185149

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b88f12d3d4793a94bdc161f7bc4c4e1d9b1a221dc7e8a5a443fb39a7d0444481
size 174371
oid sha256:dff103ed93f80acb3d13a3e3bcfff6c5f79a70042c8e46f0369e526dcfb882bd
size 174382

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e3a4f95cfda70f046faf9b791d33d355933ed3f025304e935fbc5c8cf736b1ed
size 171644
oid sha256:a60bf270de7142d5712b239a6f1c1c80ccb9b67312b06e624509441816c5c3a2
size 159489

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c5207301d8375e69e947fa8e77ab5b00f43cfc4e1127853cbc1f99274c5f6e5c
size 166769
oid sha256:9136ad78475665ced621f4fbc0d3c751d68995c4a860496cacf55a6eb6c5bb16
size 154668

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7dec73a65eceefe889124d4b94069c89f3f751a06193f061a91a37b8fc7d5235
size 509765
oid sha256:562be1e3871ec426f78417067db7f1d2174d2dd2fb4606a4e0b4082bd6202dcb
size 506184

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:cce859f61fe288187d3fc8e034afc4c3a07c2c907c96f28d3a0a3d1f3ad37a00
size 390851
oid sha256:1f33ac286a806f10e523d238e6557eae4412e3d84c21c9f4c33750798cf1a16a
size 387139

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:422c6f3d87a4031d74bf8f622c58511a0ec841b2c26c9d56a9059fb714ccd0e4
size 382604
oid sha256:3aacafe1d219739c45a051b199137043929ddd81df579dbb7c219219f3008ecd
size 378896

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0b08807b17072a7a6010629e07918d301e149a7890522d3cbc14b9342327342e
size 587297
oid sha256:6944621adb83bc10a81794ae91db60ab871419bdd26d94c43bc1bc4c391a2482
size 635186

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7dec73a65eceefe889124d4b94069c89f3f751a06193f061a91a37b8fc7d5235
size 509765
oid sha256:562be1e3871ec426f78417067db7f1d2174d2dd2fb4606a4e0b4082bd6202dcb
size 506184

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6beae96162316db191a1dd7a09b48d6249eb2ccce0d4508f5c684446239aa8c4
size 385232
oid sha256:2c855ee5fec32223a9eb8f42560faae695414bcea4a68a5ec5f8ad760d2f8cfb
size 385582

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b292602f844d1f9d708116cccb22e939ef4c3adc954d280f06febc4274383367
size 368368
oid sha256:6badffb7b672f254741c1d925ac2baa31b93130d246b24be1d61d54d8621e987
size 368799

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4c6fe9f911c9abf3b771f3ef7f8746c9c6c82e931c13df91ec11ab475b32e3b2
size 274720
oid sha256:3a27ec389a180c33f2b757caf980524f02ee1ae40955a0305c03e35ebf8e7117
size 275088

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6813886977fad6fbdfbad11ba57a478f45ed3fec227b3aa09e762306f8452b64
size 435776
oid sha256:e03501c88b6128ce34e543f3b28a95558be09d36305d9e3392e4d931fc8cdf68
size 436291

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:50d4036ff91a5f2658bdf29af91a187d42dc828dafb4e7fbe654f99aa1ad031f
size 251141
oid sha256:fb64f3d4a33fb11f1fb228fc006c7d9534a83a0f9e6c120a170a180fdc607bb4
size 251528

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:05ade6f86f20b00c4636a086547a9132d1df3ffefc122bcd27e609b4fae0833d
size 587550
oid sha256:5ed47ca5cf21dea30341174562e230a88e0fbb9bdc856793790a72449bcc9935
size 635451

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:50050aea5f8a1bc19c3571193fd30b0e91e0746aa753cd349baca1a3a477bb31
size 287226
oid sha256:7d52cdecf4719a25d6602195195796398fe743f6132dad13c308851cd849dcb6
size 287655

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e6ce6433b9c3e7ce8d73326fb2a1f680cccdbac54fa2c6660acff7e7f434865e
size 445921
oid sha256:369b1876575377e211d85f512bc2930913c9ffc24ff2f8586b99881dcf29cf8b
size 441958

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0bb588dd4f8b35178802a5e15d93136c205f0c4f782e90a02cb0c5f5d9cfb63e
size 510782
oid sha256:28e5085968ceb5f458022ae4784a4af1ba3bc6496711b1e55a8bf0d062dd6e6e
size 507456

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:ad7f3125519291a83163c3903cf4fc7a325b72f82e9c6303c995e970e6fec6e7
size 581640
oid sha256:652b34b5201aa2ed3a22fd7947409575df77a321ecd5888a986b2c671b0fc0be
size 578314

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d98e1554989ba9e69a58bd60bcb84cac0cae72108a2f3c2479267af648802e06
size 493668
oid sha256:7137866a53c3ff866fd8fe07dc2edc92cb94ed223cd455b68fc6ebf62cb51753
size 540806

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:1307720ae12d91744e53da3b89a21f80692530faf0b1edb7febdc60eb03c7672
size 501803
oid sha256:35ac85086431c58ebb1a5b710027c21a44b28f7437f07198c0bfc74260c638d9
size 498091

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a23c4aab1780331f388ee561123a67f78ce32242ca81618c05e23166ae16f6a8
size 263583
oid sha256:d3eeb52986d461aa8e2a12f1e07b0ffdacb9e6c30faaf9b0c2c32a98d25bfb06
size 263600

View File

@@ -65,7 +65,7 @@ public extension TitleAndIcon {
public let title: String
public let action: () -> Void
init(title: String, action: @escaping () -> Void) {
public init(title: String, action: @escaping () -> Void) {
self.title = title
self.action = action
}