Skip to content

Commit

Permalink
Merge pull request #1038 from matrix-org/element_4050
Browse files Browse the repository at this point in the history
Support room type (MSC1840)
  • Loading branch information
SBiOSoftWhare committed Mar 12, 2021
2 parents 47f225c + bb7bca7 commit f7d5e5e
Show file tree
Hide file tree
Showing 11 changed files with 229 additions and 7 deletions.
2 changes: 1 addition & 1 deletion CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Changes to be released in next version
*

🙌 Improvements
*
* Support room type as described in MSC1840 (vector-im/element-ios/issues/4050).

🐛 Bugfix
*
Expand Down
12 changes: 12 additions & 0 deletions MatrixSDK.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -1077,6 +1077,8 @@
B14EF3652397E90400758AF0 /* MXServiceTerms.h in Headers */ = {isa = PBXBuildFile; fileRef = 3294FD9C22F321B0007F1E60 /* MXServiceTerms.h */; settings = {ATTRIBUTES = (Public, ); }; };
B165B81125C3307E003CF7F7 /* MXLoginSSOIdentityProviderBrand.h in Headers */ = {isa = PBXBuildFile; fileRef = B165B81025C3307E003CF7F7 /* MXLoginSSOIdentityProviderBrand.h */; settings = {ATTRIBUTES = (Public, ); }; };
B165B81225C3307E003CF7F7 /* MXLoginSSOIdentityProviderBrand.h in Headers */ = {isa = PBXBuildFile; fileRef = B165B81025C3307E003CF7F7 /* MXLoginSSOIdentityProviderBrand.h */; settings = {ATTRIBUTES = (Public, ); }; };
B16F35A225F916A00029AE98 /* MXRoomTypeMapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = B16F35A125F9169F0029AE98 /* MXRoomTypeMapper.swift */; };
B16F35A325F916A00029AE98 /* MXRoomTypeMapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = B16F35A125F9169F0029AE98 /* MXRoomTypeMapper.swift */; };
B17285792100C8EA0052C51E /* MXSendReplyEventStringsLocalizable.h in Headers */ = {isa = PBXBuildFile; fileRef = B17285782100C88A0052C51E /* MXSendReplyEventStringsLocalizable.h */; settings = {ATTRIBUTES = (Public, ); }; };
B172857C2100D4F60052C51E /* MXSendReplyEventDefaultStringLocalizations.h in Headers */ = {isa = PBXBuildFile; fileRef = B172857A2100D4F60052C51E /* MXSendReplyEventDefaultStringLocalizations.h */; settings = {ATTRIBUTES = (Public, ); }; };
B172857D2100D4F60052C51E /* MXSendReplyEventDefaultStringLocalizations.m in Sources */ = {isa = PBXBuildFile; fileRef = B172857B2100D4F60052C51E /* MXSendReplyEventDefaultStringLocalizations.m */; };
Expand Down Expand Up @@ -1142,6 +1144,8 @@
B19A30D724042F2700FB6F35 /* MXSelfVerifyingMasterKeyNotTrustedQRCodeData.m in Sources */ = {isa = PBXBuildFile; fileRef = B19A30D324042F2700FB6F35 /* MXSelfVerifyingMasterKeyNotTrustedQRCodeData.m */; };
B19A30D82404335D00FB6F35 /* MXQRCodeDataTests.m in Sources */ = {isa = PBXBuildFile; fileRef = B19A30AE240425D000FB6F35 /* MXQRCodeDataTests.m */; };
B19A30D92404335D00FB6F35 /* MXQRCodeDataTests.m in Sources */ = {isa = PBXBuildFile; fileRef = B19A30AE240425D000FB6F35 /* MXQRCodeDataTests.m */; };
B1C854EE25E7B497005867D0 /* MXRoomType.h in Headers */ = {isa = PBXBuildFile; fileRef = B1C854ED25E7B492005867D0 /* MXRoomType.h */; settings = {ATTRIBUTES = (Public, ); }; };
B1C854EF25E7B498005867D0 /* MXRoomType.h in Headers */ = {isa = PBXBuildFile; fileRef = B1C854ED25E7B492005867D0 /* MXRoomType.h */; settings = {ATTRIBUTES = (Public, ); }; };
B1DDC9D62418098200D208E3 /* MXIncomingSASTransaction_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = B1DDC9D52418098200D208E3 /* MXIncomingSASTransaction_Private.h */; settings = {ATTRIBUTES = (Public, ); }; };
B1DDC9D72418098200D208E3 /* MXIncomingSASTransaction_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = B1DDC9D52418098200D208E3 /* MXIncomingSASTransaction_Private.h */; };
B1E09A132397FA950057C069 /* MatrixSDK.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B14EF36B2397E90400758AF0 /* MatrixSDK.framework */; };
Expand Down Expand Up @@ -1936,6 +1940,7 @@
B14EECED2578FE3F00448735 /* MXAuthenticationSessionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXAuthenticationSessionTests.swift; sourceTree = "<group>"; };
B14EF36B2397E90400758AF0 /* MatrixSDK.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MatrixSDK.framework; sourceTree = BUILT_PRODUCTS_DIR; };
B165B81025C3307E003CF7F7 /* MXLoginSSOIdentityProviderBrand.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXLoginSSOIdentityProviderBrand.h; sourceTree = "<group>"; };
B16F35A125F9169F0029AE98 /* MXRoomTypeMapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXRoomTypeMapper.swift; sourceTree = "<group>"; };
B17285782100C88A0052C51E /* MXSendReplyEventStringsLocalizable.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = MXSendReplyEventStringsLocalizable.h; path = ../MXSendReplyEventStringsLocalizable.h; sourceTree = "<group>"; };
B172857A2100D4F60052C51E /* MXSendReplyEventDefaultStringLocalizations.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = MXSendReplyEventDefaultStringLocalizations.h; path = ../MXSendReplyEventDefaultStringLocalizations.h; sourceTree = "<group>"; };
B172857B2100D4F60052C51E /* MXSendReplyEventDefaultStringLocalizations.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = MXSendReplyEventDefaultStringLocalizations.m; path = ../MXSendReplyEventDefaultStringLocalizations.m; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1977,6 +1982,7 @@
B19A30CD24042F0800FB6F35 /* MXSelfVerifyingMasterKeyTrustedQRCodeData.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXSelfVerifyingMasterKeyTrustedQRCodeData.m; sourceTree = "<group>"; };
B19A30D224042F2700FB6F35 /* MXSelfVerifyingMasterKeyNotTrustedQRCodeData.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXSelfVerifyingMasterKeyNotTrustedQRCodeData.h; sourceTree = "<group>"; };
B19A30D324042F2700FB6F35 /* MXSelfVerifyingMasterKeyNotTrustedQRCodeData.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXSelfVerifyingMasterKeyNotTrustedQRCodeData.m; sourceTree = "<group>"; };
B1C854ED25E7B492005867D0 /* MXRoomType.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXRoomType.h; sourceTree = "<group>"; };
B1DDC9D52418098200D208E3 /* MXIncomingSASTransaction_Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXIncomingSASTransaction_Private.h; sourceTree = "<group>"; };
B1E09A0E2397FA950057C069 /* MatrixSDKTests-macOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "MatrixSDKTests-macOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
B57EF0A39A7649D55CA1208A /* libPods-MatrixSDK-MatrixSDK-iOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-MatrixSDK-MatrixSDK-iOS.a"; sourceTree = BUILT_PRODUCTS_DIR; };
Expand Down Expand Up @@ -2205,6 +2211,8 @@
323547DB2226FC5700F15F94 /* MXCredentials.m */,
F0173EAA1FCF0E8800B5F6A3 /* MXGroup.h */,
F0173EAB1FCF0E8900B5F6A3 /* MXGroup.m */,
B1C854ED25E7B492005867D0 /* MXRoomType.h */,
B16F35A125F9169F0029AE98 /* MXRoomTypeMapper.swift */,
320DFDCA19DD99B60068622A /* MXRoom.h */,
320DFDCB19DD99B60068622A /* MXRoom.m */,
32C235701F827F3800E38FC5 /* MXRoomOperation.h */,
Expand Down Expand Up @@ -3816,6 +3824,7 @@
32792BD42295A86600F4FC9D /* MXAggregatedReactionsUpdater.h in Headers */,
B146D4D621A5A44E00D8C2C6 /* MXScanRealmInMemoryProvider.h in Headers */,
B19A30C42404268600FB6F35 /* MXVerifyingAnotherUserQRCodeData.h in Headers */,
B1C854EE25E7B497005867D0 /* MXRoomType.h in Headers */,
B11BD45422CB583E0064D8B0 /* MXReplyEventBodyParts.h in Headers */,
32549AF923F2E2790002576B /* MXKeyVerificationReady.h in Headers */,
B17285792100C8EA0052C51E /* MXSendReplyEventStringsLocalizable.h in Headers */,
Expand Down Expand Up @@ -4071,6 +4080,7 @@
B14EF2D42397E90400758AF0 /* MatrixSDK.h in Headers */,
B14EF2D52397E90400758AF0 /* MXReactionOperation.h in Headers */,
B19A30D524042F2700FB6F35 /* MXSelfVerifyingMasterKeyNotTrustedQRCodeData.h in Headers */,
B1C854EF25E7B498005867D0 /* MXRoomType.h in Headers */,
B124BBCD256453C90028996D /* MXMembershipTransitionState.h in Headers */,
B14EF2D72397E90400758AF0 /* MXAggregationsStore.h in Headers */,
B14EF2D82397E90400758AF0 /* MXCredentials.h in Headers */,
Expand Down Expand Up @@ -4756,6 +4766,7 @@
ECB5D98C2552C9B4000AD89C /* MXStopwatch.swift in Sources */,
B1136964230AC9D900E2B2FA /* MXIdentityServerRestClient.m in Sources */,
F0C34CBB1C18C93700C36F09 /* MXSDKOptions.m in Sources */,
B16F35A225F916A00029AE98 /* MXRoomTypeMapper.swift in Sources */,
320BBF441D6C81550079890E /* MXEventsEnumeratorOnArray.m in Sources */,
32618E7220ED2DF500E1D2EA /* MXFilterJSONModel.m in Sources */,
323F8865212D4E480001C73C /* MXMatrixVersions.m in Sources */,
Expand Down Expand Up @@ -4947,6 +4958,7 @@
B14EF1F42397E90400758AF0 /* MXMemoryStore.m in Sources */,
B14EF1F52397E90400758AF0 /* MXAggregationPaginatedResponse.m in Sources */,
B14EF1F62397E90400758AF0 /* MXEventReplace.m in Sources */,
B16F35A325F916A00029AE98 /* MXRoomTypeMapper.swift in Sources */,
B14EF1F72397E90400758AF0 /* MXLoginTerms.m in Sources */,
B14EF1F82397E90400758AF0 /* MXReplyEventParts.m in Sources */,
3A108A8125810C96005EEBE9 /* MXKeyData.m in Sources */,
Expand Down
11 changes: 11 additions & 0 deletions MatrixSDK/Data/MXRoomSummary.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#import "MXEnumConstants.h"
#import "MXUsersTrustLevelSummary.h"
#import "MXMembershipTransitionState.h"
#import "MXRoomType.h"

@class MXSession, MXRoom, MXRoomState, MXEvent;
@protocol MXStore;
Expand Down Expand Up @@ -137,6 +138,16 @@ FOUNDATION_EXPORT NSString *const kMXRoomSummaryDidChangeNotification;

#pragma mark - Data related to room state

/**
The room type string value as provided by the server. Can be nil.
*/
@property (nonatomic) NSString *roomTypeString;

/**
The locally computed room type derivated from `roomTypeString`.
*/
@property (nonatomic) MXRoomType roomType;

/**
The Matrix content URI of the room avatar.
*/
Expand Down
4 changes: 4 additions & 0 deletions MatrixSDK/Data/MXRoomSummary.m
Original file line number Diff line number Diff line change
Expand Up @@ -819,6 +819,8 @@ - (instancetype)initWithCoder:(NSCoder *)aDecoder
{
_roomId = [aDecoder decodeObjectForKey:@"roomId"];

_roomTypeString = [aDecoder decodeObjectForKey:@"roomTypeString"];
_roomType = (MXRoomType)[aDecoder decodeObjectForKey:@"roomType"];
_avatar = [aDecoder decodeObjectForKey:@"avatar"];
_displayname = [aDecoder decodeObjectForKey:@"displayname"];
_topic = [aDecoder decodeObjectForKey:@"topic"];
Expand Down Expand Up @@ -876,6 +878,8 @@ - (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:_roomId forKey:@"roomId"];

[aCoder encodeObject:_roomTypeString forKey:@"roomTypeString"];
[aCoder encodeInteger:_roomType forKey:@"roomType"];
[aCoder encodeObject:_avatar forKey:@"avatar"];
[aCoder encodeObject:_displayname forKey:@"displayname"];
[aCoder encodeObject:_topic forKey:@"topic"];
Expand Down
22 changes: 22 additions & 0 deletions MatrixSDK/Data/MXRoomSummaryUpdater.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,26 @@
*/
@property id<MXRoomNameStringsLocalizable> roomNameStringLocalizations;

/**
Indicate YES to handle room types with nil or empty value.
If YES `defaultRoomType` will be used to define the default room type to use in this case.
YES by default.
*/
@property (nonatomic) BOOL showNilOrEmptyRoomType;

/**
Room type used when the room type of a room is not defined (null or empty).
MXRoomTypeRoom by default.
*/
@property (nonatomic) MXRoomType defaultRoomType;

/**
List of supported room type strings to show to the user. Other room types will be hidden (see MXRoomSummary.hiddenFromUser). It's not necessary to add empty or nil values, this case is handled by `showNilOrEmptyRoomType` property.
Nil by default.
*/
@property (nonatomic) NSArray<NSString *> *showRoomTypeStrings;

@end
72 changes: 67 additions & 5 deletions MatrixSDK/Data/MXRoomSummaryUpdater.m
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,18 @@

#import "NSArray+MatrixSDK.h"

#import "MatrixSDKSwiftHeader.h"

@interface MXRoomSummaryUpdater()

@property (nonatomic) MXRoomTypeMapper *roomTypeMapper;

@end

@implementation MXRoomSummaryUpdater

#pragma mark - Setup

+ (instancetype)roomSummaryUpdaterForSession:(MXSession *)mxSession
{
static NSMapTable<MXSession*, MXRoomSummaryUpdater*> *updaterPerSession;
Expand All @@ -47,6 +57,29 @@ + (instancetype)roomSummaryUpdaterForSession:(MXSession *)mxSession
return updater;
}

- (instancetype)init
{
self = [super init];
if (self)
{
_showNilOrEmptyRoomType = YES;
_defaultRoomType = MXRoomTypeRoom;
_roomTypeMapper = [[MXRoomTypeMapper alloc] initWithDefaultRoomType:_defaultRoomType];
}
return self;
}

#pragma mark - Properties

- (void)setDefaultRoomType:(MXRoomType)defaultRoomType
{
if (_defaultRoomType != defaultRoomType)
{
_defaultRoomType = defaultRoomType;

self.roomTypeMapper.defaultRoomType = defaultRoomType;
}
}

#pragma mark - MXRoomSummaryUpdating

Expand Down Expand Up @@ -170,9 +203,19 @@ - (BOOL)session:(MXSession *)session updateRoomSummary:(MXRoomSummary *)summary
}

case MXEventTypeRoomCreate:
{
MXRoomCreateContent *createContent = [MXRoomCreateContent modelFromJSON:event.content];
summary.creatorUserId = roomState.creatorUserId;
updated = YES;
[self checkRoomCreateStateEventPredecessorAndUpdateObsoleteRoomSummaryIfNeededWithCreateEvent:event summary:summary session:session roomState:roomState];

NSString *roomTypeString = createContent.roomType;

summary.roomTypeString = createContent.roomType;
summary.roomType = [self.roomTypeMapper roomTypeFrom:roomTypeString];
summary.hiddenFromUser = [self shouldHideRoomWithRoomTypeString:roomTypeString];

updated = YES;
[self checkRoomCreateStateEventPredecessorAndUpdateObsoleteRoomSummaryIfNeededWithCreateContent:createContent summary:summary session:session roomState:roomState];
}
break;

default:
Expand Down Expand Up @@ -247,10 +290,8 @@ - (BOOL)checkForTombStoneStateEventAndUpdateRoomSummaryIfNeeded:(MXRoomSummary*)
// Hide tombstoned room predecessor from user only if the user joined the current room
// Important: Room predecessor summary could not be present in memory when making this process,
// in this case it should be processed when checking the room predecessor in `checkForTombStoneStateEventAndUpdateRoomSummaryIfNeeded:session:room:`.
- (void)checkRoomCreateStateEventPredecessorAndUpdateObsoleteRoomSummaryIfNeededWithCreateEvent:(MXEvent*)createEvent summary:(MXRoomSummary*)summary session:(MXSession*)session roomState:(MXRoomState*)roomState
- (void)checkRoomCreateStateEventPredecessorAndUpdateObsoleteRoomSummaryIfNeededWithCreateContent:(MXRoomCreateContent*)createContent summary:(MXRoomSummary*)summary session:(MXSession*)session roomState:(MXRoomState*)roomState
{
MXRoomCreateContent *createContent = [MXRoomCreateContent modelFromJSON:createEvent.content];

if (createContent.roomPredecessorInfo)
{
MXRoomSummary *obsoleteRoomSummary = [session roomSummaryWithRoomId:createContent.roomPredecessorInfo.roomId];
Expand Down Expand Up @@ -565,4 +606,25 @@ - (BOOL)updateSummaryMemberCount:(MXRoomSummary *)summary session:(MXSession *)s
return otherMembers;
}

- (BOOL)shouldHideRoomWithRoomTypeString:(NSString*)roomTypeString
{
BOOL hiddenFromUser = NO;

if (!roomTypeString.length)
{
hiddenFromUser = !self.showNilOrEmptyRoomType;
}
else if (self.showRoomTypeStrings.count)
{
hiddenFromUser = NO == [self.showRoomTypeStrings containsObject:roomTypeString];
}
else
{
hiddenFromUser = YES;
}

return hiddenFromUser;
}


@end
33 changes: 33 additions & 0 deletions MatrixSDK/Data/MXRoomType.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//
// Copyright 2021 The Matrix.org Foundation C.I.C
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

/// MXRoomType identifies the type of room as decribed in MSC1840 (see https://github.com/matrix-org/matrix-doc/pull/1840).
typedef NS_ENUM(NSInteger, MXRoomType) {
// The MXRoomTypeNone can be used when the value of the room type is nil or empty and you do not want to associate a room type for this case (See MXRoomSummaryUpdater.defaultRoomType).
MXRoomTypeNone,
MXRoomTypeRoom,
MXRoomTypeSpace,
// The room type is custom. Refer to the room type string version.
MXRoomTypeCustom
};

/// MXRoomTypeString identifies the known room type string values
typedef NSString *const MXRoomTypeString NS_TYPED_EXTENSIBLE_ENUM;

static MXRoomTypeString const MXRoomTypeStringRoomMSC1840 = @"org.matrix.msc1840.messaging";
static MXRoomTypeString const MXRoomTypeStringRoom = @"m.message";
static MXRoomTypeString const MXRoomTypeStringSpaceMSC1772 = @"org.matrix.msc1772.space";
static MXRoomTypeString const MXRoomTypeStringSpace = @"m.space";
59 changes: 59 additions & 0 deletions MatrixSDK/Data/MXRoomTypeMapper.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
//
// Copyright 2021 The Matrix.org Foundation C.I.C
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

import Foundation

/// MXRoomTypeMapper enables to get the corresponding room type from a room type string
@objcMembers
public class MXRoomTypeMapper: NSObject {

// MARK: - Properties

/// Default room type used when the given room type string is nil or empty
public var defaultRoomType: MXRoomType

// MARK: - Setup

public init(defaultRoomType: MXRoomType) {
self.defaultRoomType = defaultRoomType
super.init()
}

// MARK: - Public

public func roomType(from roomTypeString: String?) -> MXRoomType {
guard let roomTypeString = roomTypeString?.trimmingCharacters(in: .whitespacesAndNewlines) else {
return self.defaultRoomType
}

let roomType: MXRoomType

switch roomTypeString {
case MXRoomTypeString.room.rawValue, MXRoomTypeString.roomMSC1840.rawValue:
roomType = .room
case MXRoomTypeString.space.rawValue, MXRoomTypeString.spaceMSC1772.rawValue:
roomType = .space
case "":
// Use default room type when the value is empty
roomType = self.defaultRoomType
default:
roomType = .custom
}

return roomType
}

}
2 changes: 1 addition & 1 deletion MatrixSDK/Data/Store/MXFileStore/MXFileStore.m
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
#import "MXSDKOptions.h"
#import "MXTools.h"

static NSUInteger const kMXFileVersion = 68;
static NSUInteger const kMXFileVersion = 69;

static NSString *const kMXFileStoreFolder = @"MXFileStore";
static NSString *const kMXFileStoreMedaDataFile = @"MXFileStore";
Expand Down
5 changes: 5 additions & 0 deletions MatrixSDK/JSONModels/MXRoomCreateContent.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,9 @@
*/
@property (nonatomic, readonly) BOOL isFederated;

/**
The room type as described in MSC1840 (https://github.com/matrix-org/matrix-doc/pull/1840).
*/
@property (nonatomic, readonly, nullable) NSString *roomType;

@end
Loading

0 comments on commit f7d5e5e

Please sign in to comment.