Merge branch 'release/1.9.0/master'

This commit is contained in:
Stefan Ceriu 2022-08-24 16:46:13 +03:00
commit 9a2a854f08
292 changed files with 4823 additions and 9337 deletions

View file

@ -91,6 +91,7 @@ jobs:
FASTLANE_USER: ${{ secrets.FASTLANE_USER }} FASTLANE_USER: ${{ secrets.FASTLANE_USER }}
FASTLANE_PASSWORD: ${{ secrets.FASTLANE_PASSWORD }} FASTLANE_PASSWORD: ${{ secrets.FASTLANE_PASSWORD }}
DIAWI_API_TOKEN: ${{ secrets.DIAWI_API_TOKEN }} DIAWI_API_TOKEN: ${{ secrets.DIAWI_API_TOKEN }}
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
- name: Add or update PR comment with Ad-hoc release informations - name: Add or update PR comment with Ad-hoc release informations
uses: NejcZdovc/comment-pr@v1 uses: NejcZdovc/comment-pr@v1

View file

@ -1,2 +1,3 @@
brew "xcodegen" brew "xcodegen"
brew "mint" brew "mint"
brew "getsentry/tools/sentry-cli"

View file

@ -1,3 +1,45 @@
## Changes in 1.9.0 (2022-08-24)
🙌 Improvements
- KeyBackup: Adapt changes from sdk, add an entry into encryption info view of a message. ([#6555](https://github.com/vector-im/element-ios/pull/6555))
- Upgrade MatrixSDK version ([v0.23.16](https://github.com/matrix-org/matrix-ios-sdk/releases/tag/v0.23.16)).
- Display the option "Share invite link" only when the room is accessible by link. ([#6496](https://github.com/vector-im/element-ios/issues/6496))
- New App Layout: Added missing empty states in room list and space bottom sheet ([#6514](https://github.com/vector-im/element-ios/issues/6514))
- Groups: Support for groups has been removed now that Spaces are fully available. ([#6523](https://github.com/vector-im/element-ios/issues/6523))
- Change text when swiping on room from Delete to Leave. ([#6568](https://github.com/vector-im/element-ios/issues/6568))
- New App Layout: added suppport for room invites in the all chats screen ([#6600](https://github.com/vector-im/element-ios/issues/6600))
- App Layout: UI tweaks for Tabs ([#6605](https://github.com/vector-im/element-ios/issues/6605))
- New App Layout: Added onboarding screen ([#6607](https://github.com/vector-im/element-ios/issues/6607))
- App Layout: last UI tweaks before RC ([#6608](https://github.com/vector-im/element-ios/issues/6608))
- App Layout: Activated feature in BuildSettings ([#6616](https://github.com/vector-im/element-ios/issues/6616))
- App Layout: Added usage measures ([#6618](https://github.com/vector-im/element-ios/issues/6618))
🐛 Bugfixes
- RoomViewController: Wait for table view updates before checing read marker visibility. ([#5932](https://github.com/vector-im/element-ios/issues/5932))
- Add a login and signup fallback SSO option for homeservers that don't offer a list of identity providers. ([#6569](https://github.com/vector-im/element-ios/issues/6569))
- App Layout: fixed Cancel and Back on Spaces Bottom Sheet ([#6572](https://github.com/vector-im/element-ios/issues/6572))
- App Layout: updated context menus according to last design update ([#6574](https://github.com/vector-im/element-ios/issues/6574))
- App Layout: reintroduced existing Notification left markers on room cells ([#6578](https://github.com/vector-im/element-ios/issues/6578))
- App Layout: Leaving a Space now sends user to All Chats ([#6581](https://github.com/vector-im/element-ios/issues/6581))
- App Layout: added space invites in space bottom sheet ([#6599](https://github.com/vector-im/element-ios/issues/6599))
⚠️ API Changes
- Reverts #6275, bringing the local DesignKit package back. ([#6586](https://github.com/vector-im/element-ios/pull/6586))
- Communities: GroupsViewController etc have all been removed now that Spaces are available in the app. ([#6523](https://github.com/vector-im/element-ios/issues/6523))
🚧 In development 🚧
- Device manager: Add new session management screen. ([#6585](https://github.com/vector-im/element-ios/issues/6585))
Others
- Sentry: Upload Dsyms to Sentry when building Alpha ([#6413](https://github.com/vector-im/element-ios/pull/6413))
- Analytics: Log all errors to analytics ([#6611](https://github.com/vector-im/element-ios/pull/6611))
## Changes in 1.8.27 (2022-08-12) ## Changes in 1.8.27 (2022-08-12)
Others Others

View file

@ -15,5 +15,5 @@
// //
// Version // Version
MARKETING_VERSION = 1.8.27 MARKETING_VERSION = 1.9.0
CURRENT_PROJECT_VERSION = 1.8.27 CURRENT_PROJECT_VERSION = 1.9.0

View file

@ -265,7 +265,6 @@ final class BuildSettings: NSObject {
static let homeScreenShowFavouritesTab: Bool = true static let homeScreenShowFavouritesTab: Bool = true
static let homeScreenShowPeopleTab: Bool = true static let homeScreenShowPeopleTab: Bool = true
static let homeScreenShowRoomsTab: Bool = true static let homeScreenShowRoomsTab: Bool = true
static let homeScreenShowCommunitiesTab: Bool = true
// MARK: - General Settings Screen // MARK: - General Settings Screen
@ -348,7 +347,6 @@ final class BuildSettings: NSObject {
static let roomSettingsScreenAllowChangingAccessSettings: Bool = true static let roomSettingsScreenAllowChangingAccessSettings: Bool = true
static let roomSettingsScreenAllowChangingHistorySettings: Bool = true static let roomSettingsScreenAllowChangingHistorySettings: Bool = true
static let roomSettingsScreenShowAddressSettings: Bool = true static let roomSettingsScreenShowAddressSettings: Bool = true
static let roomSettingsScreenShowFlairSettings: Bool = true
static let roomSettingsScreenShowAdvancedSettings: Bool = true static let roomSettingsScreenShowAdvancedSettings: Bool = true
static let roomSettingsScreenAdvancedShowEncryptToVerifiedOption: Bool = true static let roomSettingsScreenAdvancedShowEncryptToVerifiedOption: Bool = true
@ -421,5 +419,9 @@ final class BuildSettings: NSObject {
static let syncLocalContacts: Bool = false static let syncLocalContacts: Bool = false
// MARK: - New App Layout // MARK: - New App Layout
static let newAppLayoutEnabled = false static let newAppLayoutEnabled = true
// MARK: - Device manager
static let deviceManagerEnabled = false
} }

28
DesignKit/Common.xcconfig Normal file
View file

@ -0,0 +1,28 @@
//
// Copyright 2021 Vector Creations Ltd
//
// 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.
//
// Configuration settings file format documentation can be found at:
// https://help.apple.com/xcode/#/dev745c5c974
#include "Config/AppIdentifiers.xcconfig"
#include "Config/AppVersion.xcconfig"
PRODUCT_NAME = DesignKit
PRODUCT_BUNDLE_IDENTIFIER = $(BASE_BUNDLE_IDENTIFIER).designkit
INFOPLIST_FILE = DesignKit/Info.plist
SKIP_INSTALL = YES

20
DesignKit/Debug.xcconfig Normal file
View file

@ -0,0 +1,20 @@
//
// Copyright 2021 Vector Creations Ltd
//
// 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.
//
// Configuration settings file format documentation can be found at:
// https://help.apple.com/xcode/#/dev745c5c974
#include "Common.xcconfig"

27
DesignKit/DesignKit.h Normal file
View file

@ -0,0 +1,27 @@
//
// Copyright 2021 New Vector Ltd
//
// 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/Foundation.h>
//! Project version number for DesignKit.
FOUNDATION_EXPORT double DesignKitVersionNumber;
//! Project version string for DesignKit.
FOUNDATION_EXPORT const unsigned char DesignKitVersionString[];
// In this header, you should import all the public headers of your framework using statements like #import <DesignKit/PublicHeader.h>

View file

@ -0,0 +1,55 @@
//
// Copyright 2021 New Vector Ltd
//
// 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 UIKit
public extension UIFont {
// MARK: - Convenient methods
/// Update current font with a SymbolicTraits
func vc_withTraits(_ traits: UIFontDescriptor.SymbolicTraits) -> UIFont {
guard let descriptor = fontDescriptor.withSymbolicTraits(traits) else {
return self
}
return UIFont(descriptor: descriptor, size: 0) // Size 0 means keep the size as it is
}
/// Update current font with a given Weight
func vc_withWeight(weight: Weight) -> UIFont {
// Add the font weight to the descriptor
let weightedFontDescriptor = fontDescriptor.addingAttributes([
UIFontDescriptor.AttributeName.traits: [
UIFontDescriptor.TraitKey.weight: weight
]
])
return UIFont(descriptor: weightedFontDescriptor, size: 0)
}
// MARK: - Shortcuts
var vc_bold: UIFont {
return self.vc_withTraits(.traitBold)
}
var vc_semiBold: UIFont {
return self.vc_withWeight(weight: .semibold)
}
var vc_italic: UIFont {
return self.vc_withTraits(.traitItalic)
}
}

22
DesignKit/Info.plist Normal file
View file

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
</dict>
</plist>

View file

@ -0,0 +1,20 @@
//
// Copyright 2021 Vector Creations Ltd
//
// 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.
//
// Configuration settings file format documentation can be found at:
// https://help.apple.com/xcode/#/dev745c5c974
#include "Common.xcconfig"

View file

@ -17,8 +17,6 @@
import Foundation import Foundation
import UIKit import UIKit
// TODO: Move into element-design-tokens repo.
// Figma Avatar Sizes: https://www.figma.com/file/X4XTH9iS2KGJ2wFKDqkyed/Compound?node-id=1258%3A19678 // Figma Avatar Sizes: https://www.figma.com/file/X4XTH9iS2KGJ2wFKDqkyed/Compound?node-id=1258%3A19678
public enum AvatarSize: Int { public enum AvatarSize: Int {
case xxSmall = 16 case xxSmall = 16

View file

@ -0,0 +1,52 @@
//
// Copyright 2021 New Vector Ltd
//
// 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
import UIKit
/**
Struct for holding colour values for a particular theme.
*/
public struct ColorValues: Colors {
public let accent: UIColor
public let alert: UIColor
public let primaryContent: UIColor
public let secondaryContent: UIColor
public let tertiaryContent: UIColor
public let quarterlyContent: UIColor
public let quinaryContent: UIColor
public let separator: UIColor
public let system: UIColor
public let tile: UIColor
public let navigation: UIColor
public let background: UIColor
public let ems: UIColor
public let namesAndAvatars: [UIColor]
}

View file

@ -0,0 +1,73 @@
//
// Copyright 2021 New Vector Ltd
//
// 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
/// Colors at https://www.figma.com/file/X4XTH9iS2KGJ2wFKDqkyed/Compound?node-id=1255%3A1104
public protocol Colors {
associatedtype ColorType
/// - Focused/Active states
/// - CTAs
var accent: ColorType { get }
/// - Error messages
/// - Content requiring user attention
/// - Notification, alerts
var alert: ColorType { get }
/// - Text
/// - Icons
var primaryContent: ColorType { get }
/// - Text
/// - Icons
var secondaryContent: ColorType { get }
/// - Text
/// - Icons
var tertiaryContent: ColorType { get }
/// - Text
/// - Icons
var quarterlyContent: ColorType { get }
/// - separating lines and other UI components
var quinaryContent: ColorType { get }
/// - System-based areas and backgrounds
var system: ColorType { get }
/// Separating line
var separator: ColorType { get }
/// Cards, tiles
var tile: ColorType { get }
/// Top navigation background on iOS
var navigation: ColorType { get }
/// Background UI color
var background: ColorType { get }
/// Global color: The EMS brand's purple colour.
var ems: ColorType { get }
/// - Names in chat timeline
/// - Avatars default states that include first name letter
var namesAndAvatars: [ColorType] { get }
}

View file

@ -0,0 +1,69 @@
//
// Copyright 2021 New Vector Ltd
//
// 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
import SwiftUI
/**
Struct for holding colors for use in SwiftUI.
*/
public struct ColorSwiftUI: Colors {
public let accent: Color
public let alert: Color
public let primaryContent: Color
public let secondaryContent: Color
public let tertiaryContent: Color
public let quarterlyContent: Color
public let quinaryContent: Color
public let separator: Color
public var system: Color
public let tile: Color
public let navigation: Color
public let background: Color
public var ems: Color
public let namesAndAvatars: [Color]
init(values: ColorValues) {
accent = Color(values.accent)
alert = Color(values.alert)
primaryContent = Color(values.primaryContent)
secondaryContent = Color(values.secondaryContent)
tertiaryContent = Color(values.tertiaryContent)
quarterlyContent = Color(values.quarterlyContent)
quinaryContent = Color(values.quinaryContent)
separator = Color(values.separator)
system = Color(values.system)
tile = Color(values.tile)
navigation = Color(values.navigation)
background = Color(values.background)
ems = Color(values.ems)
namesAndAvatars = values.namesAndAvatars.map({ Color($0) })
}
}

View file

@ -0,0 +1,67 @@
//
// Copyright 2021 New Vector Ltd
//
// 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
import UIKit
/**
ObjC class for holding colors for use in UIKit.
*/
@objcMembers public class ColorsUIKit: NSObject {
public let accent: UIColor
public let alert: UIColor
public let primaryContent: UIColor
public let secondaryContent: UIColor
public let tertiaryContent: UIColor
public let quarterlyContent: UIColor
public let quinaryContent: UIColor
public let separator: UIColor
public let system: UIColor
public let tile: UIColor
public let navigation: UIColor
public let background: UIColor
public let namesAndAvatars: [UIColor]
init(values: ColorValues) {
accent = values.accent
alert = values.alert
primaryContent = values.primaryContent
secondaryContent = values.secondaryContent
tertiaryContent = values.tertiaryContent
quarterlyContent = values.quarterlyContent
quinaryContent = values.quinaryContent
separator = values.separator
system = values.system
tile = values.tile
navigation = values.navigation
background = values.background
namesAndAvatars = values.namesAndAvatars
}
}

View file

@ -0,0 +1,85 @@
//
// Copyright 2021 New Vector Ltd
//
// 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 UIKit
/// Describe fonts used in the application.
/// Font names are based on Element typograhy https://www.figma.com/file/X4XTH9iS2KGJ2wFKDqkyed/Compound?node-id=1362%3A0 which is based on Apple font text styles (UIFont.TextStyle): https://developer.apple.com/documentation/uikit/uifonttextstyle
/// Create a custom TextStyle enum (like DesignKit.Fonts.TextStyle) is also a possiblity
public protocol Fonts {
associatedtype FontType
/// The font for large titles.
var largeTitle: FontType { get }
/// `largeTitle` with a Bold weight.
var largeTitleB: FontType { get }
/// The font for first-level hierarchical headings.
var title1: FontType { get }
/// `title1` with a Bold weight.
var title1B: FontType { get }
/// The font for second-level hierarchical headings.
var title2: FontType { get }
/// `title2` with a Bold weight.
var title2B: FontType { get }
/// The font for third-level hierarchical headings.
var title3: FontType { get }
/// `title3` with a Semi Bold weight.
var title3SB: FontType { get }
/// The font for headings.
var headline: FontType { get }
/// The font for subheadings.
var subheadline: FontType { get }
/// The font for body text.
var body: FontType { get }
/// `body` with a Semi Bold weight.
var bodySB: FontType { get }
/// The font for callouts.
var callout: FontType { get }
/// `callout` with a Semi Bold weight.
var calloutSB: FontType { get }
/// The font for footnotes.
var footnote: FontType { get }
/// `footnote` with a Semi Bold weight.
var footnoteSB: FontType { get }
/// The font for standard captions.
var caption1: FontType { get }
/// `caption1` with a Semi Bold weight.
var caption1SB: FontType { get }
/// The font for alternate captions.
var caption2: FontType { get }
/// `caption2` with a Semi Bold weight.
var caption2SB: FontType { get }
}

View file

@ -0,0 +1,91 @@
//
// Copyright 2021 New Vector Ltd
//
// 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
import SwiftUI
/**
Struct for holding fonts for use in SwiftUI.
*/
public struct FontSwiftUI: Fonts {
public let uiFonts: FontsUIKit
public var largeTitle: Font
public var largeTitleB: Font
public var title1: Font
public var title1B: Font
public var title2: Font
public var title2B: Font
public var title3: Font
public var title3SB: Font
public var headline: Font
public var subheadline: Font
public var body: Font
public var bodySB: Font
public var callout: Font
public var calloutSB: Font
public var footnote: Font
public var footnoteSB: Font
public var caption1: Font
public var caption1SB: Font
public var caption2: Font
public var caption2SB: Font
public init(values: ElementFonts) {
self.uiFonts = FontsUIKit(values: values)
self.largeTitle = values.largeTitle.font
self.largeTitleB = values.largeTitleB.font
self.title1 = values.title1.font
self.title1B = values.title1B.font
self.title2 = values.title2.font
self.title2B = values.title2B.font
self.title3 = values.title3.font
self.title3SB = values.title3SB.font
self.headline = values.headline.font
self.subheadline = values.subheadline.font
self.body = values.body.font
self.bodySB = values.bodySB.font
self.callout = values.callout.font
self.calloutSB = values.calloutSB.font
self.footnote = values.footnote.font
self.footnoteSB = values.footnoteSB.font
self.caption1 = values.caption1.font
self.caption1SB = values.caption1SB.font
self.caption2 = values.caption2.font
self.caption2SB = values.caption2SB.font
}
}

View file

@ -0,0 +1,87 @@
//
// Copyright 2021 New Vector Ltd
//
// 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
import UIKit
/**
ObjC class for holding fonts for use in UIKit.
*/
@objcMembers public class FontsUIKit: NSObject, Fonts {
public var largeTitle: UIFont
public var largeTitleB: UIFont
public var title1: UIFont
public var title1B: UIFont
public var title2: UIFont
public var title2B: UIFont
public var title3: UIFont
public var title3SB: UIFont
public var headline: UIFont
public var subheadline: UIFont
public var body: UIFont
public var bodySB: UIFont
public var callout: UIFont
public var calloutSB: UIFont
public var footnote: UIFont
public var footnoteSB: UIFont
public var caption1: UIFont
public var caption1SB: UIFont
public var caption2: UIFont
public var caption2SB: UIFont
public init(values: ElementFonts) {
self.largeTitle = values.largeTitle.uiFont
self.largeTitleB = values.largeTitleB.uiFont
self.title1 = values.title1.uiFont
self.title1B = values.title1B.uiFont
self.title2 = values.title2.uiFont
self.title2B = values.title2B.uiFont
self.title3 = values.title3.uiFont
self.title3SB = values.title3SB.uiFont
self.headline = values.headline.uiFont
self.subheadline = values.subheadline.uiFont
self.body = values.body.uiFont
self.bodySB = values.bodySB.uiFont
self.callout = values.callout.uiFont
self.calloutSB = values.calloutSB.uiFont
self.footnote = values.footnote.uiFont
self.footnoteSB = values.footnoteSB.uiFont
self.caption1 = values.caption1.uiFont
self.caption1SB = values.caption1SB.uiFont
self.caption2 = values.caption2.uiFont
self.caption2SB = values.caption2SB.uiFont
}
}

View file

@ -14,18 +14,29 @@
// limitations under the License. // limitations under the License.
// //
import Foundation
import UIKit import UIKit
import DesignKit
import DesignTokens
/// Theme v2. May be named again as `Theme` when the migration completed. /// Theme v2. May be named again as `Theme` when the migration completed.
@objc public protocol ThemeV2 { @objc public protocol ThemeV2 {
/// Colors object /// Colors object
var colors: ElementUIColorsResolved { get } var colors: ColorsUIKit { get }
/// Fonts object /// Fonts object
var fonts: ElementUIFonts { get } var fonts: FontsUIKit { get }
/// may contain more design components in future, like icons, audio files etc.
}
/// Theme v2 for SwiftUI.
public protocol ThemeSwiftUIType {
/// Colors object
var colors: ColorSwiftUI { get }
/// Fonts object
var fonts: FontSwiftUI { get }
/// may contain more design components in future, like icons, audio files etc. /// may contain more design components in future, like icons, audio files etc.
} }

View file

@ -0,0 +1,51 @@
//
// Copyright 2021 New Vector Ltd
//
// 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
import UIKit
import SwiftUI
/// Dark theme colors.
public class DarkColors {
private static let values = ColorValues(
accent: UIColor(rgb:0x0DBD8B),
alert: UIColor(rgb:0xFF4B55),
primaryContent: UIColor(rgb:0xFFFFFF),
secondaryContent: UIColor(rgb:0xA9B2BC),
tertiaryContent: UIColor(rgb:0x8E99A4),
quarterlyContent: UIColor(rgb:0x6F7882),
quinaryContent: UIColor(rgb:0x394049),
separator: UIColor(rgb:0x21262C),
system: UIColor(rgb:0x21262C),
tile: UIColor(rgb:0x394049),
navigation: UIColor(rgb:0x21262C),
background: UIColor(rgb:0x15191E),
ems: UIColor(rgb: 0x7E69FF),
namesAndAvatars: [
UIColor(rgb:0x368BD6),
UIColor(rgb:0xAC3BA8),
UIColor(rgb:0x03B381),
UIColor(rgb:0xE64F7A),
UIColor(rgb:0xFF812D),
UIColor(rgb:0x2DC2C5),
UIColor(rgb:0x5C56F5),
UIColor(rgb:0x74D12C)
]
)
public static var uiKit = ColorsUIKit(values: values)
public static var swiftUI = ColorSwiftUI(values: values)
}

View file

@ -0,0 +1,57 @@
//
// Copyright 2021 New Vector Ltd
//
// 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
import UIKit
import SwiftUI
/// Light theme colors.
public class LightColors {
private static let values = ColorValues(
accent: UIColor(rgb:0x0DBD8B),
alert: UIColor(rgb:0xFF4B55),
primaryContent: UIColor(rgb:0x17191C),
secondaryContent: UIColor(rgb:0x737D8C),
tertiaryContent: UIColor(rgb:0x8D97A5),
quarterlyContent: UIColor(rgb:0xC1C6CD),
quinaryContent: UIColor(rgb:0xE3E8F0),
separator: UIColor(rgb:0xE3E8F0),
system: UIColor(rgb:0xF4F6FA),
tile: UIColor(rgb:0xF3F8FD),
navigation: UIColor(rgb:0xF4F6FA),
background: UIColor(rgb:0xFFFFFF),
ems: UIColor(rgb: 0x7E69FF),
namesAndAvatars: [
UIColor(rgb:0x368BD6),
UIColor(rgb:0xAC3BA8),
UIColor(rgb:0x03B381),
UIColor(rgb:0xE64F7A),
UIColor(rgb:0xFF812D),
UIColor(rgb:0x2DC2C5),
UIColor(rgb:0x5C56F5),
UIColor(rgb:0x74D12C)
]
)
public static var uiKit = ColorsUIKit(values: values)
public static var swiftUI = ColorSwiftUI(values: values)
}

View file

@ -0,0 +1,150 @@
//
// Copyright 2021 New Vector Ltd
//
// 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 SwiftUI
/// Fonts at https://www.figma.com/file/X4XTH9iS2KGJ2wFKDqkyed/Compound?node-id=1362%3A0
@objcMembers
public class ElementFonts {
// MARK: - Types
/// A wrapper to provide both a `UIFont` and a SwiftUI `Font` in the same type.
/// The need for this comes from `Font` not adapting for dynamic type until the app
/// is restarted (or working at all in Xcode Previews) when initialised from a `UIFont`
/// (even if that font was created with the appropriate metrics).
public struct SharedFont {
public let uiFont: UIFont
public let font: Font
}
// MARK: - Setup
public init() {
}
// MARK: - Private
/// Returns an instance of the font associated with the text style and scaled appropriately for the content size category defined in the trait collection.
/// Keep this method private method at the moment and create a DesignKit.Fonts.TextStyle if needed.
fileprivate func font(forTextStyle textStyle: UIFont.TextStyle, compatibleWith traitCollection: UITraitCollection? = nil) -> UIFont {
return UIFont.preferredFont(forTextStyle: textStyle, compatibleWith: traitCollection)
}
}
// MARK: - Fonts protocol
extension ElementFonts: Fonts {
public var largeTitle: SharedFont {
let uiFont = self.font(forTextStyle: .largeTitle)
return SharedFont(uiFont: uiFont, font: .largeTitle)
}
public var largeTitleB: SharedFont {
let uiFont = self.largeTitle.uiFont.vc_bold
return SharedFont(uiFont: uiFont, font: .largeTitle.bold())
}
public var title1: SharedFont {
let uiFont = self.font(forTextStyle: .title1)
return SharedFont(uiFont: uiFont, font: .title)
}
public var title1B: SharedFont {
let uiFont = self.title1.uiFont.vc_bold
return SharedFont(uiFont: uiFont, font: .title.bold())
}
public var title2: SharedFont {
let uiFont = self.font(forTextStyle: .title2)
return SharedFont(uiFont: uiFont, font: .title2)
}
public var title2B: SharedFont {
let uiFont = self.title2.uiFont.vc_bold
return SharedFont(uiFont: uiFont, font: .title2.bold())
}
public var title3: SharedFont {
let uiFont = self.font(forTextStyle: .title3)
return SharedFont(uiFont: uiFont, font: .title3)
}
public var title3SB: SharedFont {
let uiFont = self.title3.uiFont.vc_semiBold
return SharedFont(uiFont: uiFont, font: .title3.weight(.semibold))
}
public var headline: SharedFont {
let uiFont = self.font(forTextStyle: .headline)
return SharedFont(uiFont: uiFont, font: .headline)
}
public var subheadline: SharedFont {
let uiFont = self.font(forTextStyle: .subheadline)
return SharedFont(uiFont: uiFont, font: .subheadline)
}
public var body: SharedFont {
let uiFont = self.font(forTextStyle: .body)
return SharedFont(uiFont: uiFont, font: .body)
}
public var bodySB: SharedFont {
let uiFont = self.body.uiFont.vc_semiBold
return SharedFont(uiFont: uiFont, font: .body.weight(.semibold))
}
public var callout: SharedFont {
let uiFont = self.font(forTextStyle: .callout)
return SharedFont(uiFont: uiFont, font: .callout)
}
public var calloutSB: SharedFont {
let uiFont = self.callout.uiFont.vc_semiBold
return SharedFont(uiFont: uiFont, font: .callout.weight(.semibold))
}
public var footnote: SharedFont {
let uiFont = self.font(forTextStyle: .footnote)
return SharedFont(uiFont: uiFont, font: .footnote)
}
public var footnoteSB: SharedFont {
let uiFont = self.footnote.uiFont.vc_semiBold
return SharedFont(uiFont: uiFont, font: .footnote.weight(.semibold))
}
public var caption1: SharedFont {
let uiFont = self.font(forTextStyle: .caption1)
return SharedFont(uiFont: uiFont, font: .caption)
}
public var caption1SB: SharedFont {
let uiFont = self.caption1.uiFont.vc_semiBold
return SharedFont(uiFont: uiFont, font: .caption.weight(.semibold))
}
public var caption2: SharedFont {
let uiFont = self.font(forTextStyle: .caption2)
return SharedFont(uiFont: uiFont, font: .caption2)
}
public var caption2SB: SharedFont {
let uiFont = self.caption2.uiFont.vc_semiBold
return SharedFont(uiFont: uiFont, font: .caption2.weight(.semibold))
}
}

33
DesignKit/target.yml Normal file
View file

@ -0,0 +1,33 @@
name: DesignKit
schemes:
DesignKit:
analyze:
config: Debug
archive:
config: Release
build:
targets:
DesignKit:
- running
- profiling
- analyzing
- archiving
profile:
config: Release
run:
config: Debug
disableMainThreadChecker: true
targets:
DesignKit:
type: framework
platform: iOS
configFiles:
Debug: Debug.xcconfig
Release: Release.xcconfig
sources:
- path: .
- path: ../Riot/Categories/UIColor.swift

View file

@ -3,37 +3,37 @@ GEM
specs: specs:
CFPropertyList (3.0.5) CFPropertyList (3.0.5)
rexml rexml
activesupport (6.1.6) activesupport (6.1.6.1)
concurrent-ruby (~> 1.0, >= 1.0.2) concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 1.6, < 2) i18n (>= 1.6, < 2)
minitest (>= 5.1) minitest (>= 5.1)
tzinfo (~> 2.0) tzinfo (~> 2.0)
zeitwerk (~> 2.3) zeitwerk (~> 2.3)
addressable (2.8.0) addressable (2.8.1)
public_suffix (>= 2.0.2, < 5.0) public_suffix (>= 2.0.2, < 6.0)
algoliasearch (1.27.5) algoliasearch (1.27.5)
httpclient (~> 2.8, >= 2.8.3) httpclient (~> 2.8, >= 2.8.3)
json (>= 1.5.1) json (>= 1.5.1)
artifactory (3.0.15) artifactory (3.0.15)
atomos (0.1.3) atomos (0.1.3)
aws-eventstream (1.2.0) aws-eventstream (1.2.0)
aws-partitions (1.598.0) aws-partitions (1.621.0)
aws-sdk-core (3.131.1) aws-sdk-core (3.134.0)
aws-eventstream (~> 1, >= 1.0.2) aws-eventstream (~> 1, >= 1.0.2)
aws-partitions (~> 1, >= 1.525.0) aws-partitions (~> 1, >= 1.525.0)
aws-sigv4 (~> 1.1) aws-sigv4 (~> 1.1)
jmespath (~> 1, >= 1.6.1) jmespath (~> 1, >= 1.6.1)
aws-sdk-kms (1.57.0) aws-sdk-kms (1.58.0)
aws-sdk-core (~> 3, >= 3.127.0) aws-sdk-core (~> 3, >= 3.127.0)
aws-sigv4 (~> 1.1) aws-sigv4 (~> 1.1)
aws-sdk-s3 (1.114.0) aws-sdk-s3 (1.114.0)
aws-sdk-core (~> 3, >= 3.127.0) aws-sdk-core (~> 3, >= 3.127.0)
aws-sdk-kms (~> 1) aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.4) aws-sigv4 (~> 1.4)
aws-sigv4 (1.5.0) aws-sigv4 (1.5.1)
aws-eventstream (~> 1, >= 1.0.2) aws-eventstream (~> 1, >= 1.0.2)
babosa (1.0.4) babosa (1.0.4)
claide (1.0.3) claide (1.1.0)
clamp (1.3.2) clamp (1.3.2)
cocoapods (1.11.3) cocoapods (1.11.3)
addressable (~> 2.8) addressable (~> 2.8)
@ -82,13 +82,13 @@ GEM
rake (>= 12.0.0, < 14.0.0) rake (>= 12.0.0, < 14.0.0)
domain_name (0.5.20190701) domain_name (0.5.20190701)
unf (>= 0.0.5, < 1.0.0) unf (>= 0.0.5, < 1.0.0)
dotenv (2.7.6) dotenv (2.8.1)
emoji_regex (3.2.3) emoji_regex (3.2.3)
escape (0.0.4) escape (0.0.4)
ethon (0.15.0) ethon (0.15.0)
ffi (>= 1.15.0) ffi (>= 1.15.0)
excon (0.92.3) excon (0.92.4)
faraday (1.10.0) faraday (1.10.2)
faraday-em_http (~> 1.0) faraday-em_http (~> 1.0)
faraday-em_synchrony (~> 1.0) faraday-em_synchrony (~> 1.0)
faraday-excon (~> 1.1) faraday-excon (~> 1.1)
@ -117,7 +117,7 @@ GEM
faraday_middleware (1.2.0) faraday_middleware (1.2.0)
faraday (~> 1.0) faraday (~> 1.0)
fastimage (2.2.6) fastimage (2.2.6)
fastlane (2.206.2) fastlane (2.209.1)
CFPropertyList (>= 2.3, < 4.0.0) CFPropertyList (>= 2.3, < 4.0.0)
addressable (>= 2.8, < 3.0.0) addressable (>= 2.8, < 3.0.0)
artifactory (~> 3.0) artifactory (~> 3.0)
@ -159,16 +159,17 @@ GEM
fastlane-plugin-brew (0.1.1) fastlane-plugin-brew (0.1.1)
fastlane-plugin-diawi (2.1.0) fastlane-plugin-diawi (2.1.0)
rest-client (>= 2.0.0) rest-client (>= 2.0.0)
fastlane-plugin-versioning (0.5.0) fastlane-plugin-sentry (1.12.2)
fastlane-plugin-versioning (0.5.1)
fastlane-plugin-xcodegen (1.1.0) fastlane-plugin-xcodegen (1.1.0)
fastlane-plugin-brew (~> 0.1.1) fastlane-plugin-brew (~> 0.1.1)
ffi (1.15.5) ffi (1.15.5)
fourflusher (2.3.1) fourflusher (2.3.1)
fuzzy_match (2.0.4) fuzzy_match (2.0.4)
gh_inspector (1.1.3) gh_inspector (1.1.3)
google-apis-androidpublisher_v3 (0.22.0) google-apis-androidpublisher_v3 (0.25.0)
google-apis-core (>= 0.5, < 2.a) google-apis-core (>= 0.7, < 2.a)
google-apis-core (0.5.0) google-apis-core (0.7.0)
addressable (~> 2.5, >= 2.5.1) addressable (~> 2.5, >= 2.5.1)
googleauth (>= 0.16.2, < 2.a) googleauth (>= 0.16.2, < 2.a)
httpclient (>= 2.8.1, < 3.a) httpclient (>= 2.8.1, < 3.a)
@ -177,27 +178,27 @@ GEM
retriable (>= 2.0, < 4.a) retriable (>= 2.0, < 4.a)
rexml rexml
webrick webrick
google-apis-iamcredentials_v1 (0.11.0) google-apis-iamcredentials_v1 (0.13.0)
google-apis-core (>= 0.5, < 2.a) google-apis-core (>= 0.7, < 2.a)
google-apis-playcustomapp_v1 (0.8.0) google-apis-playcustomapp_v1 (0.10.0)
google-apis-core (>= 0.5, < 2.a) google-apis-core (>= 0.7, < 2.a)
google-apis-storage_v1 (0.15.0) google-apis-storage_v1 (0.17.0)
google-apis-core (>= 0.5, < 2.a) google-apis-core (>= 0.7, < 2.a)
google-cloud-core (1.6.0) google-cloud-core (1.6.0)
google-cloud-env (~> 1.0) google-cloud-env (~> 1.0)
google-cloud-errors (~> 1.0) google-cloud-errors (~> 1.0)
google-cloud-env (1.6.0) google-cloud-env (1.6.0)
faraday (>= 0.17.3, < 3.0) faraday (>= 0.17.3, < 3.0)
google-cloud-errors (1.2.0) google-cloud-errors (1.2.0)
google-cloud-storage (1.36.2) google-cloud-storage (1.38.0)
addressable (~> 2.8) addressable (~> 2.8)
digest-crc (~> 0.4) digest-crc (~> 0.4)
google-apis-iamcredentials_v1 (~> 0.1) google-apis-iamcredentials_v1 (~> 0.1)
google-apis-storage_v1 (~> 0.1) google-apis-storage_v1 (~> 0.17.0)
google-cloud-core (~> 1.6) google-cloud-core (~> 1.6)
googleauth (>= 0.16.2, < 2.a) googleauth (>= 0.16.2, < 2.a)
mini_mime (~> 1.0) mini_mime (~> 1.0)
googleauth (1.1.3) googleauth (1.2.0)
faraday (>= 0.17.3, < 3.a) faraday (>= 0.17.3, < 3.a)
jwt (>= 1.4, < 3.0) jwt (>= 1.4, < 3.0)
memoist (~> 0.16) memoist (~> 0.16)
@ -209,7 +210,7 @@ GEM
http-cookie (1.0.5) http-cookie (1.0.5)
domain_name (~> 0.5) domain_name (~> 0.5)
httpclient (2.8.3) httpclient (2.8.3)
i18n (1.10.0) i18n (1.12.0)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
jmespath (1.6.1) jmespath (1.6.1)
json (2.6.2) json (2.6.2)
@ -221,7 +222,7 @@ GEM
mini_magick (4.11.0) mini_magick (4.11.0)
mini_mime (1.1.2) mini_mime (1.1.2)
mini_portile2 (2.8.0) mini_portile2 (2.8.0)
minitest (5.15.0) minitest (5.16.3)
molinillo (0.8.0) molinillo (0.8.0)
multi_json (1.15.0) multi_json (1.15.0)
multipart-post (2.0.0) multipart-post (2.0.0)
@ -229,7 +230,7 @@ GEM
nap (1.1.0) nap (1.1.0)
naturally (2.2.1) naturally (2.2.1)
netrc (0.11.0) netrc (0.11.0)
nokogiri (1.13.6) nokogiri (1.13.8)
mini_portile2 (~> 2.8.0) mini_portile2 (~> 2.8.0)
racc (~> 1.4) racc (~> 1.4)
optparse (0.1.1) optparse (0.1.1)
@ -254,9 +255,9 @@ GEM
ruby2_keywords (0.0.5) ruby2_keywords (0.0.5)
rubyzip (2.3.2) rubyzip (2.3.2)
security (0.1.3) security (0.1.3)
signet (0.16.1) signet (0.17.0)
addressable (~> 2.8) addressable (~> 2.8)
faraday (>= 0.17.5, < 3.0) faraday (>= 0.17.5, < 3.a)
jwt (>= 1.5, < 3.0) jwt (>= 1.5, < 3.0)
multi_json (~> 1.10) multi_json (~> 1.10)
simctl (1.6.8) simctl (1.6.8)
@ -278,7 +279,7 @@ GEM
tty-cursor (~> 0.7) tty-cursor (~> 0.7)
typhoeus (1.4.0) typhoeus (1.4.0)
ethon (>= 0.9.0) ethon (>= 0.9.0)
tzinfo (2.0.4) tzinfo (2.0.5)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
uber (0.1.0) uber (0.1.0)
unf (0.1.4) unf (0.1.4)
@ -287,10 +288,10 @@ GEM
unicode-display_width (1.8.0) unicode-display_width (1.8.0)
webrick (1.7.0) webrick (1.7.0)
word_wrap (1.0.0) word_wrap (1.0.0)
xcode-install (2.8.0) xcode-install (2.8.1)
claide (>= 0.9.1, < 1.1.0) claide (>= 0.9.1)
fastlane (>= 2.1.0, < 3.0.0) fastlane (>= 2.1.0, < 3.0.0)
xcodeproj (1.21.0) xcodeproj (1.22.0)
CFPropertyList (>= 2.3.3, < 4.0) CFPropertyList (>= 2.3.3, < 4.0)
atomos (~> 0.1.3) atomos (~> 0.1.3)
claide (>= 1.0.2, < 2.0) claide (>= 1.0.2, < 2.0)
@ -310,10 +311,11 @@ DEPENDENCIES
cocoapods (~> 1.11.2) cocoapods (~> 1.11.2)
fastlane fastlane
fastlane-plugin-diawi fastlane-plugin-diawi
fastlane-plugin-sentry
fastlane-plugin-versioning fastlane-plugin-versioning
fastlane-plugin-xcodegen fastlane-plugin-xcodegen
slather slather
xcode-install xcode-install
BUNDLED WITH BUNDLED WITH
2.3.9 2.3.20

View file

@ -16,7 +16,7 @@ use_frameworks!
# - `{ :specHash => {sdk spec hash}` to depend on specific pod options (:git => …, :podspec => …) for MatrixSDK repo. Used by Fastfile during CI # - `{ :specHash => {sdk spec hash}` to depend on specific pod options (:git => …, :podspec => …) for MatrixSDK repo. Used by Fastfile during CI
# #
# Warning: our internal tooling depends on the name of this variable name, so be sure not to change it # Warning: our internal tooling depends on the name of this variable name, so be sure not to change it
$matrixSDKVersion = '= 0.23.15' $matrixSDKVersion = '= 0.23.16'
# $matrixSDKVersion = :local # $matrixSDKVersion = :local
# $matrixSDKVersion = { :branch => 'develop'} # $matrixSDKVersion = { :branch => 'develop'}
# $matrixSDKVersion = { :specHash => { git: 'https://git.io/fork123', branch: 'fix' } } # $matrixSDKVersion = { :specHash => { git: 'https://git.io/fork123', branch: 'fix' } }

View file

@ -56,9 +56,9 @@ PODS:
- LoggerAPI (1.9.200): - LoggerAPI (1.9.200):
- Logging (~> 1.1) - Logging (~> 1.1)
- Logging (1.4.0) - Logging (1.4.0)
- MatrixSDK (0.23.15): - MatrixSDK (0.23.16):
- MatrixSDK/Core (= 0.23.15) - MatrixSDK/Core (= 0.23.16)
- MatrixSDK/Core (0.23.15): - MatrixSDK/Core (0.23.16):
- AFNetworking (~> 4.0.0) - AFNetworking (~> 4.0.0)
- GZIP (~> 1.3.0) - GZIP (~> 1.3.0)
- libbase58 (~> 0.1.4) - libbase58 (~> 0.1.4)
@ -66,17 +66,17 @@ PODS:
- OLMKit (~> 3.2.5) - OLMKit (~> 3.2.5)
- Realm (= 10.27.0) - Realm (= 10.27.0)
- SwiftyBeaver (= 1.9.5) - SwiftyBeaver (= 1.9.5)
- MatrixSDK/CryptoSDK (0.23.15): - MatrixSDK/CryptoSDK (0.23.16):
- MatrixSDKCrypto (= 0.1.0) - MatrixSDKCrypto (= 0.1.0)
- MatrixSDK/JingleCallStack (0.23.15): - MatrixSDK/JingleCallStack (0.23.16):
- JitsiMeetSDK (= 5.0.2) - JitsiMeetSDK (= 5.0.2)
- MatrixSDK/Core - MatrixSDK/Core
- MatrixSDKCrypto (0.1.0) - MatrixSDKCrypto (0.1.0)
- OLMKit (3.2.5): - OLMKit (3.2.12):
- OLMKit/olmc (= 3.2.5) - OLMKit/olmc (= 3.2.12)
- OLMKit/olmcpp (= 3.2.5) - OLMKit/olmcpp (= 3.2.12)
- OLMKit/olmc (3.2.5) - OLMKit/olmc (3.2.12)
- OLMKit/olmcpp (3.2.5) - OLMKit/olmcpp (3.2.12)
- PostHog (1.4.4) - PostHog (1.4.4)
- ReadMoreTextView (3.0.1) - ReadMoreTextView (3.0.1)
- Realm (10.27.0): - Realm (10.27.0):
@ -92,7 +92,7 @@ PODS:
- Sentry/Core (7.15.0) - Sentry/Core (7.15.0)
- SideMenu (6.5.0) - SideMenu (6.5.0)
- SwiftBase32 (0.9.0) - SwiftBase32 (0.9.0)
- SwiftGen (6.5.1) - SwiftGen (6.6.2)
- SwiftJWT (3.6.200): - SwiftJWT (3.6.200):
- BlueCryptor (~> 1.0) - BlueCryptor (~> 1.0)
- BlueECC (~> 1.1) - BlueECC (~> 1.1)
@ -123,8 +123,8 @@ DEPENDENCIES:
- KeychainAccess (~> 4.2.2) - KeychainAccess (~> 4.2.2)
- KTCenterFlowLayout (~> 1.3.1) - KTCenterFlowLayout (~> 1.3.1)
- libPhoneNumber-iOS (~> 0.9.13) - libPhoneNumber-iOS (~> 0.9.13)
- MatrixSDK (= 0.23.15) - MatrixSDK (= 0.23.16)
- MatrixSDK/JingleCallStack (= 0.23.15) - MatrixSDK/JingleCallStack (= 0.23.16)
- OLMKit - OLMKit
- PostHog (~> 1.4.4) - PostHog (~> 1.4.4)
- ReadMoreTextView (~> 3.0.1) - ReadMoreTextView (~> 3.0.1)
@ -193,12 +193,12 @@ EXTERNAL SOURCES:
CHECKOUT OPTIONS: CHECKOUT OPTIONS:
AnalyticsEvents: AnalyticsEvents:
:commit: b275ccb194a219a61b3100159d51cadbf7c9020c :commit: 53ad46ba1ea1ee8f21139dda3c351890846a202f
:git: https://github.com/matrix-org/matrix-analytics-events.git :git: https://github.com/matrix-org/matrix-analytics-events.git
SPEC CHECKSUMS: SPEC CHECKSUMS:
AFNetworking: 7864c38297c79aaca1500c33288e429c3451fdce AFNetworking: 7864c38297c79aaca1500c33288e429c3451fdce
AnalyticsEvents: 333bf47d67dc628fadd29ce887b7ac93d8bd6e05 AnalyticsEvents: 0cc8cf52da2fd464a2f39b788a295988151116ce
BlueCryptor: b0aee3d9b8f367b49b30de11cda90e1735571c24 BlueCryptor: b0aee3d9b8f367b49b30de11cda90e1735571c24
BlueECC: 0d18e93347d3ec6d41416de21c1ffa4d4cd3c2cc BlueECC: 0d18e93347d3ec6d41416de21c1ffa4d4cd3c2cc
BlueRSA: dfeef51db96bcc4edec654956c1581adbda4e6a3 BlueRSA: dfeef51db96bcc4edec654956c1581adbda4e6a3
@ -221,9 +221,9 @@ SPEC CHECKSUMS:
libPhoneNumber-iOS: 0a32a9525cf8744fe02c5206eb30d571e38f7d75 libPhoneNumber-iOS: 0a32a9525cf8744fe02c5206eb30d571e38f7d75
LoggerAPI: ad9c4a6f1e32f518fdb43a1347ac14d765ab5e3d LoggerAPI: ad9c4a6f1e32f518fdb43a1347ac14d765ab5e3d
Logging: beeb016c9c80cf77042d62e83495816847ef108b Logging: beeb016c9c80cf77042d62e83495816847ef108b
MatrixSDK: 9ed379b45f6809fc573db53a30a4d09f960e5886 MatrixSDK: 522910756169e688afc6ebd12aa549c16bc759dd
MatrixSDKCrypto: 4b9146d5ef484550341be056a164c6930038028e MatrixSDKCrypto: 4b9146d5ef484550341be056a164c6930038028e
OLMKit: 9fb4799c4a044dd2c06bda31ec31a12191ad30b5 OLMKit: da115f16582e47626616874e20f7bb92222c7a51
PostHog: 4b6321b521569092d4ef3a02238d9435dbaeb99f PostHog: 4b6321b521569092d4ef3a02238d9435dbaeb99f
ReadMoreTextView: 19147adf93abce6d7271e14031a00303fe28720d ReadMoreTextView: 19147adf93abce6d7271e14031a00303fe28720d
Realm: 9ca328bd7e700cc19703799785e37f77d1a130f2 Realm: 9ca328bd7e700cc19703799785e37f77d1a130f2
@ -231,7 +231,7 @@ SPEC CHECKSUMS:
Sentry: 63ca44f5e0c8cea0ee5a07686b02e56104f41ef7 Sentry: 63ca44f5e0c8cea0ee5a07686b02e56104f41ef7
SideMenu: f583187d21c5b1dd04c72002be544b555a2627a2 SideMenu: f583187d21c5b1dd04c72002be544b555a2627a2
SwiftBase32: 9399c25a80666dc66b51e10076bf591e3bbb8f17 SwiftBase32: 9399c25a80666dc66b51e10076bf591e3bbb8f17
SwiftGen: a6d22010845f08fe18fbdf3a07a8e380fd22e0ea SwiftGen: 1366a7f71aeef49954ca5a63ba4bef6b0f24138c
SwiftJWT: 88c412708f58c169d431d344c87bc79a87c830ae SwiftJWT: 88c412708f58c169d431d344c87bc79a87c830ae
SwiftLint: e96c0a8c770c7ebbc4d36c55baf9096bb65c4584 SwiftLint: e96c0a8c770c7ebbc4d36c55baf9096bb65c4584
SwiftyBeaver: 84069991dd5dca07d7069100985badaca7f0ce82 SwiftyBeaver: 84069991dd5dca07d7069100985badaca7f0ce82
@ -241,6 +241,6 @@ SPEC CHECKSUMS:
zxcvbn-ios: fef98b7c80f1512ff0eec47ac1fa399fc00f7e3c zxcvbn-ios: fef98b7c80f1512ff0eec47ac1fa399fc00f7e3c
ZXingObjC: fdbb269f25dd2032da343e06f10224d62f537bdb ZXingObjC: fdbb269f25dd2032da343e06f10224d62f537bdb
PODFILE CHECKSUM: 044b4d9f0d9485e1407019fb3c7b267458120a89 PODFILE CHECKSUM: 32a777fedc1a3ac9b62775803835a9704ef6acdd
COCOAPODS: 1.11.2 COCOAPODS: 1.11.2

View file

@ -1,23 +1,5 @@
{ {
"pins" : [ "pins" : [
{
"identity" : "element-design-tokens",
"kind" : "remoteSourceControl",
"location" : "https://github.com/vector-im/element-design-tokens.git",
"state" : {
"revision" : "02ba42d9ec02f90370a6cfc35a68d7312696636c",
"version" : "0.0.2"
}
},
{
"identity" : "element-x-ios",
"kind" : "remoteSourceControl",
"location" : "https://github.com/vector-im/element-x-ios",
"state" : {
"revision" : "0a199ee61126feb8c8a462200cb4749d6eb3ba77",
"version" : "1.0.1-202207011447"
}
},
{ {
"identity" : "maplibre-gl-native-distribution", "identity" : "maplibre-gl-native-distribution",
"kind" : "remoteSourceControl", "kind" : "remoteSourceControl",
@ -62,15 +44,6 @@
"branch" : "main", "branch" : "main",
"revision" : "0ffad3f7b45a6a4760db090d503b00f094bbecc0" "revision" : "0ffad3f7b45a6a4760db090d503b00f094bbecc0"
} }
},
{
"identity" : "swiftui-introspect",
"kind" : "remoteSourceControl",
"location" : "https://github.com/siteline/SwiftUI-Introspect.git",
"state" : {
"revision" : "f2616860a41f9d9932da412a8978fec79c06fe24",
"version" : "0.1.4"
}
} }
], ],
"version" : 2 "version" : 2

View file

@ -171,7 +171,7 @@
<!--All Chats View Controller--> <!--All Chats View Controller-->
<scene sceneID="VNs-EF-YAe"> <scene sceneID="VNs-EF-YAe">
<objects> <objects>
<viewController storyboardIdentifier="AllChatsViewController" extendedLayoutIncludesOpaqueBars="YES" id="EOr-kV-8pa" customClass="AllChatsViewController" customModule="Riot" customModuleProvider="target" sceneMemberID="viewController"> <viewController storyboardIdentifier="AllChatsViewController" extendedLayoutIncludesOpaqueBars="YES" id="EOr-kV-8pa" customClass="AllChatsViewController" customModule="Element" customModuleProvider="target" sceneMemberID="viewController">
<layoutGuides> <layoutGuides>
<viewControllerLayoutGuide type="top" id="5IA-we-z5u"/> <viewControllerLayoutGuide type="top" id="5IA-we-z5u"/>
<viewControllerLayoutGuide type="bottom" id="9LE-SN-vqG"/> <viewControllerLayoutGuide type="bottom" id="9LE-SN-vqG"/>
@ -257,7 +257,6 @@
<segue destination="HnD-LA-psC" kind="relationship" relationship="viewControllers" id="zON-Qp-EVq"/> <segue destination="HnD-LA-psC" kind="relationship" relationship="viewControllers" id="zON-Qp-EVq"/>
<segue destination="IGB-jr-yFz" kind="relationship" relationship="viewControllers" id="CeF-yL-aO0"/> <segue destination="IGB-jr-yFz" kind="relationship" relationship="viewControllers" id="CeF-yL-aO0"/>
<segue destination="HPQ-zg-lZR" kind="relationship" relationship="viewControllers" id="y12-eg-vhO"/> <segue destination="HPQ-zg-lZR" kind="relationship" relationship="viewControllers" id="y12-eg-vhO"/>
<segue destination="SLx-Wj-p7E" kind="relationship" relationship="viewControllers" id="MLn-VT-30W"/>
<segue destination="EOr-kV-8pa" kind="relationship" relationship="viewControllers" id="j67-Px-Dkd"/> <segue destination="EOr-kV-8pa" kind="relationship" relationship="viewControllers" id="j67-Px-Dkd"/>
</connections> </connections>
</tabBarController> </tabBarController>
@ -371,7 +370,7 @@
<!--Placeholder Detail View Controller--> <!--Placeholder Detail View Controller-->
<scene sceneID="2wP-Cu-Wca"> <scene sceneID="2wP-Cu-Wca">
<objects> <objects>
<viewController storyboardIdentifier="EmptyDetailsViewControllerStoryboardId" id="Cpr-Tz-Az0" customClass="PlaceholderDetailViewController" customModule="Riot" customModuleProvider="target" sceneMemberID="viewController"> <viewController storyboardIdentifier="EmptyDetailsViewControllerStoryboardId" id="Cpr-Tz-Az0" customClass="PlaceholderDetailViewController" customModule="Element" customModuleProvider="target" sceneMemberID="viewController">
<layoutGuides> <layoutGuides>
<viewControllerLayoutGuide type="top" id="yS4-RU-AsN"/> <viewControllerLayoutGuide type="top" id="yS4-RU-AsN"/>
<viewControllerLayoutGuide type="bottom" id="VKG-pW-vMU"/> <viewControllerLayoutGuide type="bottom" id="VKG-pW-vMU"/>
@ -403,25 +402,6 @@
</objects> </objects>
<point key="canvasLocation" x="-153" y="-1121"/> <point key="canvasLocation" x="-153" y="-1121"/>
</scene> </scene>
<!--Groups View Controller-->
<scene sceneID="Rqp-Ti-qpu">
<objects>
<viewController storyboardIdentifier="GroupsViewController" id="SLx-Wj-p7E" customClass="GroupsViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="N1j-ye-uQa"/>
<viewControllerLayoutGuide type="bottom" id="FYD-2t-u4k"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Sl8-9u-7yE">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</view>
<tabBarItem key="tabBarItem" title="" image="tab_groups" id="5x2-xc-uIB"/>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="xpc-LV-krz" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="942" y="-1299"/>
</scene>
<!--Rooms View Controller--> <!--Rooms View Controller-->
<scene sceneID="SDg-Pp-8Uj"> <scene sceneID="SDg-Pp-8Uj">
<objects> <objects>
@ -506,7 +486,7 @@
<!--Thread--> <!--Thread-->
<scene sceneID="Opl-gU-pwm"> <scene sceneID="Opl-gU-pwm">
<objects> <objects>
<viewController storyboardIdentifier="ThreadViewControllerStoryboardId" title="Room" hidesBottomBarWhenPushed="YES" id="R2h-H9-hdJ" userLabel="Thread" customClass="ThreadViewController" customModule="Riot" customModuleProvider="target" sceneMemberID="viewController"> <viewController storyboardIdentifier="ThreadViewControllerStoryboardId" title="Room" hidesBottomBarWhenPushed="YES" id="R2h-H9-hdJ" userLabel="Thread" customClass="ThreadViewController" customModule="Element" customModuleProvider="target" sceneMemberID="viewController">
<navigationItem key="navigationItem" id="TFF-nx-BSb"> <navigationItem key="navigationItem" id="TFF-nx-BSb">
<nil key="title"/> <nil key="title"/>
<view key="titleView" contentMode="scaleToFill" id="e4J-vI-jzo" userLabel="Room title view container"> <view key="titleView" contentMode="scaleToFill" id="e4J-vI-jzo" userLabel="Room title view container">
@ -523,16 +503,42 @@
</objects> </objects>
<point key="canvasLocation" x="-153" y="-419"/> <point key="canvasLocation" x="-153" y="-419"/>
</scene> </scene>
<!--Room Invites View Controller-->
<scene sceneID="ied-3I-huo">
<objects>
<viewController storyboardIdentifier="RoomInvitesViewController" extendedLayoutIncludesOpaqueBars="YES" id="BYq-qZ-orN" customClass="RoomInvitesViewController" customModule="Element" customModuleProvider="target" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="NpE-qA-EJG"/>
<viewControllerLayoutGuide type="bottom" id="N2q-zt-KdC"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Ln5-wB-A9e">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
</view>
<tabBarItem key="tabBarItem" title="" image="tab_home" id="vSs-0M-T3r">
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="accessibilityIdentifier" value="TabBarItemHome"/>
</userDefinedRuntimeAttributes>
</tabBarItem>
<navigationItem key="navigationItem" id="FIq-cg-lge"/>
<connections>
<segue destination="WDS-Ip-RQ9" kind="presentation" identifier="presentStartChat" id="rok-Xg-Mhx"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="r4V-Xa-ZoW" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="2734" y="-2050"/>
</scene>
</scenes> </scenes>
<inferredMetricsTieBreakers> <inferredMetricsTieBreakers>
<segue reference="y6u-0X-urR"/> <segue reference="rok-Xg-Mhx"/>
</inferredMetricsTieBreakers> </inferredMetricsTieBreakers>
<resources> <resources>
<image name="launch_screen_logo" width="240" height="240"/> <image name="launch_screen_logo" width="240" height="240"/>
<image name="search_icon" width="24" height="24"/> <image name="search_icon" width="24" height="24"/>
<image name="settings_icon" width="24" height="24"/> <image name="settings_icon" width="24" height="24"/>
<image name="tab_favourites" width="24" height="24"/> <image name="tab_favourites" width="24" height="24"/>
<image name="tab_groups" width="24" height="24"/>
<image name="tab_home" width="20" height="23.5"/> <image name="tab_home" width="20" height="23.5"/>
<image name="tab_people" width="24" height="24"/> <image name="tab_people" width="24" height="24"/>
<image name="tab_rooms" width="24" height="24"/> <image name="tab_rooms" width="24" height="24"/>

View file

@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View file

@ -0,0 +1,23 @@
{
"images" : [
{
"filename" : "all_chats_onboarding1.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "all_chats_onboarding1@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "all_chats_onboarding1@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

View file

@ -0,0 +1,24 @@
{
"images" : [
{
"filename" : "all_chats_onboarding2.svg",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true
}
}

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 47 KiB

View file

@ -0,0 +1,23 @@
{
"images" : [
{
"filename" : "all_chats_onboarding3.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "all_chats_onboarding3@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "all_chats_onboarding3@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 KiB

View file

@ -1,7 +1,7 @@
{ {
"images" : [ "images" : [
{ {
"filename" : "home_my_spaces_action.svg", "filename" : "all_chats_edit_icon.svg",
"idiom" : "universal", "idiom" : "universal",
"scale" : "1x" "scale" : "1x"
}, },

View file

@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.6151 10.5009C11.5759 10.5404 11.5489 10.591 11.5375 10.6463L11.1211 12.6668C11.0799 12.8667 11.2491 13.0456 11.4439 13.0079L13.4123 12.6272C13.4661 12.6168 13.5158 12.5902 13.5551 12.5507L21.9163 4.13132C22.026 4.0208 22.0281 3.83944 21.921 3.72624L20.3691 2.08644C20.2619 1.97323 20.0861 1.97105 19.9763 2.08156L11.6151 10.5009ZM2 6.6862L2 18.9063C2 19.7268 2.31919 20.5137 2.88734 21.0939C3.4555 21.6741 4.22608 22 5.02958 22L16.9964 22C17.7999 22 18.5705 21.6741 19.1386 21.0939C19.7068 20.5137 20.026 19.7268 20.026 18.9063L20.026 11.6103C20.026 11.3368 19.9196 11.0745 19.7302 10.8811C19.5408 10.6878 19.284 10.5791 19.0161 10.5791C18.7483 10.5791 18.4914 10.6878 18.3021 10.8811C18.1127 11.0745 18.0063 11.3368 18.0063 11.6103L18.0063 18.9063C18.0063 19.1798 17.8999 19.4421 17.7105 19.6355C17.5211 19.8289 17.2642 19.9375 16.9964 19.9375L5.02958 19.9375C4.76174 19.9375 4.50488 19.8289 4.3155 19.6355C4.12611 19.4421 4.01972 19.1798 4.01972 18.9063L4.01972 6.6862C4.01972 6.4127 4.12611 6.15041 4.3155 5.95701C4.50488 5.76362 4.76174 5.65497 5.02958 5.65497L12.1743 5.65497C12.4422 5.65497 12.699 5.54632 12.8884 5.35293C13.0778 5.15953 13.1842 4.89724 13.1842 4.62374C13.1842 4.35024 13.0778 4.08794 12.8884 3.89454C12.699 3.70115 12.4422 3.59251 12.1743 3.59251L5.02958 3.59251C4.22608 3.59251 3.4555 3.91845 2.88734 4.49863C2.31919 5.07881 2 5.8657 2 6.6862Z" fill="#0DBD8B"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -0,0 +1,25 @@
{
"images" : [
{
"filename" : "all_chats_empty_list_placeholder_icon.svg",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true,
"template-rendering-intent" : "template"
}
}

View file

@ -0,0 +1,3 @@
<svg width="31" height="32" viewBox="0 0 31 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.02344 17.0081L11.2804 23.4509L25.9769 8.31787" stroke="#737D8C" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 247 B

View file

@ -0,0 +1,25 @@
{
"images" : [
{
"filename" : "all_chats_spaces_icon.svg",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true,
"template-rendering-intent" : "template"
}
}

View file

@ -0,0 +1,6 @@
<svg width="21" height="21" viewBox="0 0 21 21" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="1" y="1" width="7" height="7" rx="1" stroke="#0DBD8B" stroke-width="2"/>
<rect x="1" y="12.1113" width="7" height="7" rx="1" stroke="#0DBD8B" stroke-width="2"/>
<rect x="12.1113" y="1" width="7" height="7" rx="1" stroke="#0DBD8B" stroke-width="2"/>
<rect x="12.1113" y="12.1113" width="7" height="7" rx="3.5" stroke="#0DBD8B" stroke-width="2"/>
</svg>

After

Width:  |  Height:  |  Size: 457 B

View file

@ -1,6 +0,0 @@
<svg width="21" height="21" viewBox="0 0 21 21" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="0.75" y="0.75" width="7.5" height="7.5" rx="1.25" stroke="#0DBD8B" stroke-width="1.5"/>
<rect x="0.75" y="11.8613" width="7.5" height="7.5" rx="1.25" stroke="#0DBD8B" stroke-width="1.5"/>
<rect x="11.1111" width="9" height="9" rx="2" fill="#0DBD8B"/>
<rect x="11.8611" y="11.8613" width="7.5" height="7.5" rx="1.25" stroke="#0DBD8B" stroke-width="1.5"/>
</svg>

Before

Width:  |  Height:  |  Size: 466 B

View file

@ -76,7 +76,7 @@
"joined" = "مُنضَمّ"; "joined" = "مُنضَمّ";
"skip" = "تَخَطِّي"; "skip" = "تَخَطِّي";
"close" = "إغلاق"; "close" = "إغلاق";
"sending" = "يَجري الإرسَال"; "sending" = "جاري الإرسَال";
"send_to" = "إرسَال إلى %@"; "send_to" = "إرسَال إلى %@";
"collapse" = "تضييق"; "collapse" = "تضييق";
"rename" = "إعَادة التسمية"; "rename" = "إعَادة التسمية";
@ -189,8 +189,8 @@
"contacts_address_book_section" = "جِهاتُ الاِتِّصال المَحَلِّيَّة"; "contacts_address_book_section" = "جِهاتُ الاِتِّصال المَحَلِّيَّة";
"directory_search_fail" = "فَشَلَ جَلبُ البَيَانَات"; "directory_search_fail" = "فَشَلَ جَلبُ البَيَانَات";
"directory_searching_title" = "بَحثُ الدَّلِيلِ جَارٍ…"; "directory_searching_title" = "بَحثُ الدَّلِيلِ جَارٍ…";
"directory_search_results_more_than" = ">عُثِرَ عَلَى %tu نَتائج لِ%@"; "directory_search_results_more_than" = ">عُثِرَ عَلَى %1$tu نَتائج لِ%2$@";
"directory_search_results" = "عُثِرَ عَلَى %tu نَتائج لِ%@"; "directory_search_results" = "عُثِرَ عَلَى %1$tu نَتائج لِ%2$@";
"directory_search_results_title" = "تَصَفُحُ نَتائِجِ الدَّلِيل"; "directory_search_results_title" = "تَصَفُحُ نَتائِجِ الدَّلِيل";
"directory_cell_description" = "%tu غُرَف"; "directory_cell_description" = "%tu غُرَف";
@ -350,11 +350,11 @@
"onboarding_use_case_existing_server_message" = "هَل تَتَطلَّع إلى الانْضِمام إلى الخادِم المَوجود؟"; "onboarding_use_case_existing_server_message" = "هَل تَتَطلَّع إلى الانْضِمام إلى الخادِم المَوجود؟";
"onboarding_use_case_skip_button" = "تَخَطي هَذا السُؤال"; "onboarding_use_case_skip_button" = "تَخَطي هَذا السُؤال";
/* The placeholder string contains onboarding_use_case_skip_button as a tappable action */ /* The placeholder string contains onboarding_use_case_skip_button as a tappable action */
"onboarding_use_case_not_sure_yet" = "لَستَ مٌتَأكداً بَعدَ؟ يُمَكَّنَك %@"; "onboarding_use_case_not_sure_yet" = "لَستَ مٌتَأكداً بَعدَ؟ %@";
"onboarding_use_case_community_messaging" = "المُجتَمَعَات"; "onboarding_use_case_community_messaging" = "المُجتَمَعَات";
"onboarding_use_case_work_messaging" = "الفَرِقَ"; "onboarding_use_case_work_messaging" = "الفَرِقَ";
"onboarding_use_case_personal_messaging" = "الأصَدقاء والعائِلة"; "onboarding_use_case_personal_messaging" = "الأصَدقاء والعائِلة";
"onboarding_use_case_message" = "سنساعَدَك على الاتصال."; "onboarding_use_case_message" = "سنساعَدَك على الاتصال";
"onboarding_use_case_title" = "مَنْ الذي ستَتَحَدَّثَ إليه أكْثَر؟"; "onboarding_use_case_title" = "مَنْ الذي ستَتَحَدَّثَ إليه أكْثَر؟";
"onboarding_splash_page_4_message" = "Element رائِع أيضاً لمَكان العَمِلَ. تَثِقُ به أكثر المٌؤسَسات أمانًا في العالم."; "onboarding_splash_page_4_message" = "Element رائِع أيضاً لمَكان العَمِلَ. تَثِقُ به أكثر المٌؤسَسات أمانًا في العالم.";
"onboarding_splash_page_4_title_no_pun" = "مُراسَلة لفَريقك."; "onboarding_splash_page_4_title_no_pun" = "مُراسَلة لفَريقك.";
@ -973,3 +973,58 @@
"attachment_size_prompt_title" = "تأكيد الحَجم للإرسَال"; "attachment_size_prompt_title" = "تأكيد الحَجم للإرسَال";
"message_reply_to_sender_sent_their_location" = "شَارك موقعهُم."; "message_reply_to_sender_sent_their_location" = "شَارك موقعهُم.";
"room_displayname_all_other_members_left" = "%@ (غادر)"; "room_displayname_all_other_members_left" = "%@ (غادر)";
"authentication_verify_email_waiting_title" = "قم بتأكيد بريدك الألكتروني.";
"authentication_verify_email_text_field_placeholder" = "البَريد الإلِكتُرونيّ";
/* The placeholder will show the homeserver's domain */
"authentication_verify_email_input_message" = "%@ يحتاج إلى التَّحَقُّق من حِسابك";
"authentication_verify_email_input_title" = "أدخل البَريد الإلِكتُرونيّ الخاص بك";
"authentication_cancel_flow_confirmation_message" = "لم يتم إنشاء حسابك بعد. إيقاف عملية التَّسجِيل؟";
"authentication_server_selection_generic_error" = "لا يمكن العثور على خادم باستخدام هذا الرابط، يرجى التأكد من صحته.";
"authentication_server_selection_server_url" = "رابط الخادِم الرَّئيسي";
"authentication_server_selection_register_message" = "ما هو عنوان الخادِم الخاص بك؟ هذا مثل منزل لجميع بياناتك";
"authentication_server_selection_register_title" = "اختر الخادِم الرَّئيسي الخاص بك";
"authentication_server_selection_login_message" = "ما هو عنوان الخادِم الخاص بك؟";
"authentication_server_selection_login_title" = "الاتصال بالخادِم الرَّئيسي";
"authentication_server_info_title_login" = "أين تعيش مُحادَثَاتك";
"authentication_server_info_title" = "أين ستعيش مُحادَثَاتك";
"authentication_login_forgot_password" = "نِسيان كَلِمَةُ المُرُور";
"authentication_login_username" = "اِسم مُستَخدِم / البَريد الإلِكتُرونيّ / رَقم الهَاتِف";
"authentication_login_title" = "مرحباً بعَوْدتك!";
"authentication_registration_password_footer" = "يجب أن يتكون من 8 أحرف أو أكثر";
/* The placeholder will show the full Matrix ID that has been entered. */
"authentication_registration_username_footer_available" = "يمكن للآخرين اكتشافك %@";
"authentication_registration_username_footer" = "لا يمكنك تغيير هذا لاحقًا";
"authentication_registration_username" = "اِسم مُستَخدِم";
// MARK: Authentication
"authentication_registration_title" = "أنشئ حِسابك";
"onboarding_celebration_button" = "هيا بنا";
"onboarding_celebration_message" = "توجه إلى الإعدَادَات في أي وقت لتحديث ملف التعريف الخاص بك";
"onboarding_celebration_title" = "تَبَدو جَيدة!";
"onboarding_avatar_accessibility_label" = "صورة المَلفّ شَّخصي";
"onboarding_avatar_message" = "حان الوقت لوضع وجه للاسم";
"onboarding_avatar_title" = "أضف صورة للمَلفّ الشَّخصي";
"onboarding_display_name_max_length" = "اِسم العَرَض الخاص بك يجب أن يكون أقل من 256 حرفاً";
"onboarding_display_name_hint" = "بالإمكان تغيير هذا لاحقاً";
"onboarding_display_name_placeholder" = "اِسم العَرَض";
"onboarding_display_name_message" = "سيَظَهر هذا عند إرسَال الرَسائِل.";
"onboarding_display_name_title" = "اختر اِسم العَرَض";
"onboarding_personalization_skip" = "تَخَطِّي هذه الخَطوة";
"onboarding_personalization_save" = "حِفظ والاِستِمرار";
"onboarding_congratulations_home_button" = "خذني إلى الرَّئيسَة";
"onboarding_congratulations_personalize_button" = "تَخصيص المَلفّ شَّخصي";
/* The placeholder string contains the user's matrix ID */
"onboarding_congratulations_message" = "تَمَّ إنشاء حِسابُك %@";
"onboarding_congratulations_title" = "مَبْروك!";
"saving" = "جاري الحِفظ";
// Activities
"loading" = "تَحْميل";
"confirm" = "تَأْكيد";
"edit" = "تعديل";
"suggest" = "اقْتَرح";
"add" = "إضافة";
"existing" = "الحالي";
"new_word" = "جديد";
"stop" = "إيقاف";
"joining" = "الانْضِمام إلى";

View file

@ -77,6 +77,7 @@
"suggest" = "Suggest"; "suggest" = "Suggest";
"edit" = "Edit"; "edit" = "Edit";
"confirm" = "Confirm"; "confirm" = "Confirm";
"invite_to" = "Invite to %@";
// Activities // Activities
"loading" = "Loading"; "loading" = "Loading";
@ -893,6 +894,9 @@ Tap the + to start adding people.";
"manage_session_not_trusted" = "Not trusted"; "manage_session_not_trusted" = "Not trusted";
"manage_session_sign_out" = "Sign out of this session"; "manage_session_sign_out" = "Sign out of this session";
// User sessions management
"user_sessions_settings" = "Manage sessions";
// AuthenticatedSessionViewControllerFactory // AuthenticatedSessionViewControllerFactory
"authenticated_session_flow_not_supported" = "This app does not support the authentication mechanism on your homeserver."; "authenticated_session_flow_not_supported" = "This app does not support the authentication mechanism on your homeserver.";
@ -2006,6 +2010,7 @@ Tap the + to start adding people.";
"leave_space_only_action" = "Don't leave any rooms"; "leave_space_only_action" = "Don't leave any rooms";
"leave_space_and_all_rooms_action" = "Leave all rooms and spaces"; "leave_space_and_all_rooms_action" = "Leave all rooms and spaces";
"spaces_explore_rooms" = "Explore rooms"; "spaces_explore_rooms" = "Explore rooms";
"spaces_explore_rooms_format" = "Explore %@";
"spaces_suggested_room" = "Suggested"; "spaces_suggested_room" = "Suggested";
"spaces_explore_rooms_room_number" = "%@ rooms"; "spaces_explore_rooms_room_number" = "%@ rooms";
"spaces_explore_rooms_one_room" = "1 room"; "spaces_explore_rooms_one_room" = "1 room";
@ -2176,6 +2181,13 @@ Tap the + to start adding people.";
"all_chats_edit_layout_activity_order" = "Sort by activity"; "all_chats_edit_layout_activity_order" = "Sort by activity";
"all_chats_edit_layout_alphabetical_order" = "Sort A-Z"; "all_chats_edit_layout_alphabetical_order" = "Sort A-Z";
"all_chats_all_filter" = "All"; "all_chats_all_filter" = "All";
"all_chats_empty_view_title" = "%@\nis looking a little empty.";
"all_chats_empty_space_information" = "Spaces are a new way to group rooms and people. Add an existing room, or create a new one, using the bottom-right button.";
"all_chats_empty_view_information" = "The all-in-one secure chat app for teams, friends and organisations. Create a chat, or join an existing room, to get started.";
"all_chats_empty_list_placeholder_title" = "Youre all caught up.";
"all_chats_empty_unreads_placeholder_message" = "This is where you're unread messages will show up, when you have some.";
"all_chats_nothing_found_placeholder_title" = "Nothing found.";
"all_chats_nothing_found_placeholder_message" = "Try adjusting your search.";
"room_recents_recently_viewed_section" = "Recently viewed"; "room_recents_recently_viewed_section" = "Recently viewed";
@ -2184,9 +2196,29 @@ Tap the + to start adding people.";
"all_chats_edit_menu_leave_space" = "Leave %@"; "all_chats_edit_menu_leave_space" = "Leave %@";
"all_chats_edit_menu_space_settings" = "Space settings"; "all_chats_edit_menu_space_settings" = "Space settings";
"all_chats_onboarding_page_title1" = "Welcome to a new view!";
"all_chats_onboarding_page_message1" = "To simplify your Element, tabs are now optional. Manage them using the top-right menu.";
"all_chats_onboarding_page_title2" = "Access Spaces";
"all_chats_onboarding_page_message2" = "Access your Spaces (bottom-left) faster and easier than ever before.";
"all_chats_onboarding_page_title3" = "Give Feedback";
"all_chats_onboarding_page_message3" = "Tap your profile to let us know what you think.";
"all_chats_onboarding_title" = "What's new";
"all_chats_onboarding_try_it" = "Try it out";
// Mark: - Room invites
"room_invites_empty_view_title" = "Nothing new.";
"room_invites_empty_view_information" = "This is where your invites appear.";
// Mark: - Space Selector // Mark: - Space Selector
"space_selector_title" = "My spaces"; "space_selector_title" = "My spaces";
"space_selector_empty_view_title" = "No spaces yet.";
"space_selector_empty_view_information" = "Spaces are a way to group rooms and people. Create a space to get started.";
"space_selector_create_space" = "Create Space";
"space_detail_nav_title" = "Space detail";
"space_invite_nav_title" = "Space invite";
// Mark: - Polls // Mark: - Polls
@ -2320,6 +2352,10 @@ To enable access, tap Settings> Location and select Always";
"location_sharing_live_lab_promotion_text" = "Please note: this is a labs feature using a temporary implementation that allows the history of your shared location to be permanently visible to other people in the room."; "location_sharing_live_lab_promotion_text" = "Please note: this is a labs feature using a temporary implementation that allows the history of your shared location to be permanently visible to other people in the room.";
"location_sharing_live_lab_promotion_activation" = "Enable live location sharing"; "location_sharing_live_lab_promotion_activation" = "Enable live location sharing";
// MARK: User sessions management
"user_sessions_overview_title" = "Sessions";
// MARK: - MatrixKit // MARK: - MatrixKit

View file

@ -2384,7 +2384,7 @@
"onboarding_congratulations_home_button" = "Ir al inicio"; "onboarding_congratulations_home_button" = "Ir al inicio";
"onboarding_congratulations_personalize_button" = "Personalizar perfil"; "onboarding_congratulations_personalize_button" = "Personalizar perfil";
/* The placeholder string contains the user's matrix ID */ /* The placeholder string contains the user's matrix ID */
"onboarding_congratulations_message" = "Has creado tu cuenta, %@."; "onboarding_congratulations_message" = "Tu cuenta %@ ha sido creada";
"onboarding_congratulations_title" = "¡Enhorabuena!"; "onboarding_congratulations_title" = "¡Enhorabuena!";
"saving" = "Guardando"; "saving" = "Guardando";
@ -2494,3 +2494,48 @@
"authentication_choose_password_not_verified_message" = "Comprueba tu bandeja de entrada"; "authentication_choose_password_not_verified_message" = "Comprueba tu bandeja de entrada";
"authentication_choose_password_text_field_placeholder" = "Nueva contraseña"; "authentication_choose_password_text_field_placeholder" = "Nueva contraseña";
"authentication_forgot_password_waiting_button" = "Volver a enviar correo"; "authentication_forgot_password_waiting_button" = "Volver a enviar correo";
// Mark: - Space Selector
"space_selector_title" = "Mis espacios";
"all_chats_edit_menu_space_settings" = "Ajustes del espacio";
"all_chats_edit_menu_leave_space" = "Salir de %@";
"all_chats_user_menu_settings" = "Ajustes de usuario";
"room_recents_recently_viewed_section" = "Visto recientemente";
"all_chats_all_filter" = "Todos";
"all_chats_edit_layout_alphabetical_order" = "Ordenar alfabéticamente";
"all_chats_edit_layout_activity_order" = "Ordenar por última actividad";
"all_chats_edit_layout_show_filters" = "Mostrar filtros";
"all_chats_edit_layout_show_recents" = "Mostrar recientes";
"all_chats_edit_layout_sorting_options_title" = "Ordenar mensajes por";
"all_chats_edit_layout_add_filters_title" = "Filtrar tus mensajes";
"all_chats_edit_layout_add_section_title" = "Añadir sección a inicio";
"all_chats_edit_layout_unreads" = "Sin leer";
"all_chats_edit_layout_recents" = "Recientes";
"all_chats_edit_layout" = "Ajustes de disposición";
"spaces_subspace_creation_visibility_message" = "El espacio se añadirá a %@.";
"spaces_subspace_creation_visibility_title" = "¿Qué tipo de subespacio quieres crear?";
"spaces_create_subspace_title" = "Crear un subespacio";
"spaces_add_subspace_title" = "Crear espacio dentro de %@";
"password_validation_error_contain_symbol" = "Tiene un símbolo.";
"password_validation_error_contain_number" = "Tiene un número.";
"password_validation_error_contain_lowercase_letter" = "Tiene una letra minúscula.";
"password_validation_error_contain_uppercase_letter" = "Tiene una letra mayúscula.";
/* The placeholder will show a number */
"password_validation_error_max_length" = "%d caracteres como máximo.";
/* The placeholder will show a number */
"password_validation_error_min_length" = "Al menos %d caracteres.";
"authentication_verify_msisdn_invalid_phone_number" = "Número de teléfono no válido";
/* The placeholder will show the phone number that was entered. */
"authentication_verify_msisdn_waiting_message" = "Código enviado a %@";
"authentication_verify_msisdn_waiting_title" = "Verifica tu número de teléfono";
/* The placeholder will show the homeserver's domain */
"authentication_verify_msisdn_input_message" = "%@ necesita verificar tu cuenta";
"authentication_verify_msisdn_input_title" = "Escribe tu número de teléfono";
"authentication_choose_password_not_verified_title" = "Correo sin verificar";
"authentication_choose_password_submit_button" = "Restablecer contraseña";
"authentication_choose_password_signout_all_devices" = "Cerrar sesión de todos los dispositivos";
"authentication_choose_password_input_message" = "Asegúrate de que tiene al menos 8 caracteres";
"authentication_choose_password_input_title" = "Elige una nueva contraseña";
/* The placeholder will show the email address that was entered. */
"authentication_forgot_password_waiting_message" = "Sigue las instrucciones enviadas a %@";

View file

@ -2371,3 +2371,35 @@
"authentication_verify_email_text_field_placeholder" = "E-posti aadress"; "authentication_verify_email_text_field_placeholder" = "E-posti aadress";
/* The placeholder will show the homeserver's domain */ /* The placeholder will show the homeserver's domain */
"authentication_verify_email_input_message" = "%@ soovib sinu kasutajakonto verifitseerimist"; "authentication_verify_email_input_message" = "%@ soovib sinu kasutajakonto verifitseerimist";
"location_sharing_map_loading_error" = "Kaardi laadimine ei õnnestu\nSee koduserver ei pruugi olla seadistatud kuvama kaarte";
// Mark: - Space Selector
"space_selector_title" = "Minu kogukonnad";
"all_chats_edit_menu_space_settings" = "Kogukonna seadistused";
"all_chats_edit_menu_leave_space" = "Lahku %@ kogukonnast";
"all_chats_user_menu_settings" = "Kasutaja seadistused";
"room_recents_recently_viewed_section" = "Hiljuti vaadatud";
"all_chats_all_filter" = "Kõik";
"all_chats_edit_layout_alphabetical_order" = "Sorteeri A-Z tähestiku järjekorras";
"all_chats_edit_layout_activity_order" = "Sorteeri aktiivsuse alusel";
"all_chats_edit_layout_show_filters" = "Näita otsinguvalikuid";
"all_chats_edit_layout_show_recents" = "Näita hiljutisi sõnumeid";
"all_chats_edit_layout_sorting_options_title" = "Sõnumite sortimise alus";
"all_chats_edit_layout_pin_spaces_title" = "Lisa oma kogukonnad avalehele";
"all_chats_edit_layout_add_filters_message" = "Liigita sõnumeid automaatselt sinu eelistatud kategooriate alusel";
"all_chats_edit_layout_add_filters_title" = "Jaga oma sõnumeid";
"all_chats_edit_layout_add_section_message" = "Lihtsamaks ligipääsuks lisa valik avalehele";
"all_chats_edit_layout_add_section_title" = "Lisa valik avalehele";
"all_chats_edit_layout_unreads" = "Lugemata";
"all_chats_edit_layout_recents" = "Hiljutised";
"all_chats_edit_layout" = "Paigutuse seadistused";
"all_chats_section_title" = "Vestlused";
// Mark: - All Chats
"all_chats_title" = "Kõik vestlused";
"spaces_subspace_creation_visibility_message" = "Loodud kogukond saab olema osa %@ kogukonnast.";
"spaces_subspace_creation_visibility_title" = "Missugust alamkogukonda sooviksid sa luua?";
"spaces_create_subspace_title" = "Loo alamkogukond";
"spaces_add_subspace_title" = "Loo uus alamkogukond olemasolevas kogukonnas %@";

View file

@ -2415,3 +2415,39 @@
// MARK: Authentication // MARK: Authentication
"authentication_registration_title" = "Fiók létrehozása"; "authentication_registration_title" = "Fiók létrehozása";
"authentication_server_info_title_login" = "Ahol a beszélgetéseid lesznek"; "authentication_server_info_title_login" = "Ahol a beszélgetéseid lesznek";
"location_sharing_map_loading_error" = "A térkép betöltése sikertelen\nEz a matrix szerver nincs beállítva, hogy térképet mutasson";
"location_sharing_invalid_power_level_message" = "Az élő helymegosztáshoz ebben a szobában megfelelő jogosultságokra van szükséged.";
"location_sharing_invalid_power_level_title" = "Nincs jogosultságod a élő helymegosztáshoz";
// Mark: - Space Selector
"space_selector_title" = "Tereim";
"all_chats_edit_menu_space_settings" = "Tér beállítások";
"all_chats_edit_menu_leave_space" = "Elhagy: %@";
"all_chats_user_menu_settings" = "Felhasználói beállítások";
"room_recents_recently_viewed_section" = "Nemrég megtekintett";
"all_chats_all_filter" = "Mind";
"all_chats_edit_layout_alphabetical_order" = "Rendezés A-Z";
"all_chats_edit_layout_activity_order" = "Rendezés aktivitás szerint";
"all_chats_edit_layout_show_filters" = "Szűrők megjelenítése";
"all_chats_edit_layout_show_recents" = "Legfrissebbek megjelenítése";
"all_chats_edit_layout_sorting_options_title" = "Üzenetek rendezése";
"all_chats_edit_layout_pin_spaces_title" = "Terek kitűzése";
"all_chats_edit_layout_add_filters_message" = "Üzeneteid automatikus szűrése az általad választott kategóriákba";
"all_chats_edit_layout_add_filters_title" = "Üzenetek szűrése";
"all_chats_edit_layout_add_section_message" = "Rész kitűzése a kezdő képernyőhöz a könnyebb hozzáféréshez";
"all_chats_edit_layout_add_section_title" = "Rész hozzáadása a kezdőképernyőhöz";
"all_chats_edit_layout_unreads" = "Olvasatlan";
"all_chats_edit_layout_recents" = "Legutóbbiak";
"all_chats_edit_layout" = "Kinézet beállításai";
"all_chats_section_title" = "Csevegések";
// Mark: - All Chats
"all_chats_title" = "Minden beszélgetés";
"spaces_subspace_creation_visibility_message" = "Az elkészített tér hozzá lesz adva ehhez: %@.";
"spaces_subspace_creation_visibility_title" = "Milyen típusú al-teret szeretnél készíteni?";
"spaces_create_subspace_title" = "Téren belüli tér készítése";
"spaces_add_subspace_title" = "Tér készítés itt: %@";
"authentication_choose_password_not_verified_message" = "Nézd meg a bejövő e-mailjeidet";
"authentication_choose_password_not_verified_title" = "E-mail nincs ellenőrizve";

View file

@ -2626,3 +2626,35 @@
"location_sharing_invalid_power_level_title" = "Anda tidak memiliki izin untuk membagikan lokasi"; "location_sharing_invalid_power_level_title" = "Anda tidak memiliki izin untuk membagikan lokasi";
"authentication_choose_password_not_verified_message" = "Periksa kotak masuk Anda"; "authentication_choose_password_not_verified_message" = "Periksa kotak masuk Anda";
"authentication_choose_password_not_verified_title" = "Email belum diverifikasi"; "authentication_choose_password_not_verified_title" = "Email belum diverifikasi";
"location_sharing_map_loading_error" = "Tidak dapat memuat peta\nHomeserver ini tidak diatur untuk menampilkan peta";
// Mark: - Space Selector
"space_selector_title" = "Space saya";
"all_chats_edit_menu_space_settings" = "Pengaturan space";
"all_chats_edit_menu_leave_space" = "Tinggalkan %@";
"all_chats_user_menu_settings" = "Pengaturan pengguna";
"room_recents_recently_viewed_section" = "Baru saja dilihat";
"all_chats_all_filter" = "Semua";
"all_chats_edit_layout_alphabetical_order" = "Urutkan A-Z";
"all_chats_edit_layout_activity_order" = "Urutkan berdasarkan aktivitas";
"all_chats_edit_layout_show_filters" = "Tampikan saringan";
"all_chats_edit_layout_show_recents" = "Tampilkan yang terkini";
"all_chats_edit_layout_sorting_options_title" = "Urutkan pesan berdasarkan";
"all_chats_edit_layout_pin_spaces_title" = "Sematkan space Anda";
"all_chats_edit_layout_add_filters_message" = "Saring pesan Anda ke kategori pilihan Anda secara otomatis";
"all_chats_edit_layout_add_filters_title" = "Saring pesan Anda";
"all_chats_edit_layout_add_section_message" = "Sematkan bagian ke beranda untuk akses yang mudah";
"all_chats_edit_layout_add_section_title" = "Tambahkan bagian ke beranda";
"all_chats_edit_layout_unreads" = "Belum dibaca";
"all_chats_edit_layout_recents" = "Terkini";
"all_chats_edit_layout" = "Preferensi tata letak";
"all_chats_section_title" = "Obrolan";
// Mark: - All Chats
"all_chats_title" = "Semua obrolan";
"spaces_subspace_creation_visibility_message" = "Space yang dibuat akan ditambahkan ke %@.";
"spaces_subspace_creation_visibility_title" = "Tipe subspace apa yang Anda ingin buat?";
"spaces_create_subspace_title" = "Buat sebuah subspace";
"spaces_add_subspace_title" = "Buat space dalam %@";

View file

@ -2399,3 +2399,35 @@
"location_sharing_invalid_power_level_title" = "Non hai l'autorizzazione di condividere la posizione in tempo reale"; "location_sharing_invalid_power_level_title" = "Non hai l'autorizzazione di condividere la posizione in tempo reale";
"authentication_choose_password_not_verified_message" = "Controlla la posta in arrivo"; "authentication_choose_password_not_verified_message" = "Controlla la posta in arrivo";
"authentication_choose_password_not_verified_title" = "Email non verificata"; "authentication_choose_password_not_verified_title" = "Email non verificata";
"location_sharing_map_loading_error" = "Impossibile caricare la mappa\nQuesto homeserver non è configurato per mostrare mappe";
// Mark: - Space Selector
"space_selector_title" = "I miei spazi";
"all_chats_edit_menu_space_settings" = "Impostazioni spazio";
"all_chats_edit_menu_leave_space" = "Esci da %@";
"all_chats_user_menu_settings" = "Impostazioni utente";
"room_recents_recently_viewed_section" = "Viste di recente";
"all_chats_all_filter" = "Tutte";
"all_chats_edit_layout_alphabetical_order" = "Ordina A-Z";
"all_chats_edit_layout_activity_order" = "Ordina per attività";
"all_chats_edit_layout_show_filters" = "Mostra filtri";
"all_chats_edit_layout_show_recents" = "Mostra recenti";
"all_chats_edit_layout_sorting_options_title" = "Ordina messaggi per";
"all_chats_edit_layout_pin_spaces_title" = "Fissa i tuoi spazi";
"all_chats_edit_layout_add_filters_message" = "Filtra automaticamente i tuoi messaggi in categorie di tua scelta";
"all_chats_edit_layout_add_filters_title" = "Filtra i tuoi messaggi";
"all_chats_edit_layout_add_section_message" = "Fissa le sezioni alla pagina principale per un facile accesso";
"all_chats_edit_layout_add_section_title" = "Aggiungi sezione a pagina principale";
"all_chats_edit_layout_unreads" = "Non lette";
"all_chats_edit_layout_recents" = "Recenti";
"all_chats_edit_layout" = "Preferenze layout";
"all_chats_section_title" = "Chat";
// Mark: - All Chats
"all_chats_title" = "Tutte le chat";
"spaces_subspace_creation_visibility_message" = "Lo spazio creato verrà aggiunto a %@.";
"spaces_subspace_creation_visibility_title" = "Che tipo di sottospazio vuoi creare?";
"spaces_create_subspace_title" = "Crea un sottospazio";
"spaces_add_subspace_title" = "Crea spazio all'interno di %@";

View file

@ -2400,3 +2400,35 @@
"location_sharing_invalid_power_level_title" = "Você não tem permissão para compartilhar localização ao vivo"; "location_sharing_invalid_power_level_title" = "Você não tem permissão para compartilhar localização ao vivo";
"authentication_choose_password_not_verified_message" = "Cheque sua inbox"; "authentication_choose_password_not_verified_message" = "Cheque sua inbox";
"authentication_choose_password_not_verified_title" = "Email não verificado"; "authentication_choose_password_not_verified_title" = "Email não verificado";
"location_sharing_map_loading_error" = "Incapaz de carregar mapa\nEste servidocasa não está configurado para exibir mapas";
// Mark: - Space Selector
"space_selector_title" = "Meus espaços";
"all_chats_edit_menu_space_settings" = "Ajustes de espaço";
"all_chats_edit_menu_leave_space" = "Sair de %@";
"all_chats_user_menu_settings" = "Ajustes de usuária(o)";
"room_recents_recently_viewed_section" = "Vistos recentemente";
"all_chats_all_filter" = "Todos";
"all_chats_edit_layout_alphabetical_order" = "Ordenar A-Z";
"all_chats_edit_layout_activity_order" = "Classificar por atividade";
"all_chats_edit_layout_show_filters" = "Mostrar filtros";
"all_chats_edit_layout_show_recents" = "Mostrar recentes";
"all_chats_edit_layout_sorting_options_title" = "Classificar mensagens por";
"all_chats_edit_layout_pin_spaces_title" = "Pinnar seus espaços";
"all_chats_edit_layout_add_filters_message" = "Filtrar automaticamente suas menagens para dentro das categorias de sua escolha";
"all_chats_edit_layout_add_filters_title" = "Filtrar suas mensagens";
"all_chats_edit_layout_add_section_message" = "Pinnar seções a home para acesso fácil";
"all_chats_edit_layout_add_section_title" = "Adicionar seção a home";
"all_chats_edit_layout_unreads" = "Não-lidos";
"all_chats_edit_layout_recents" = "Recentes";
"all_chats_edit_layout" = "Preferências de layout";
"all_chats_section_title" = "Chats";
// Mark: - All Chats
"all_chats_title" = "Todos os chats";
"spaces_subspace_creation_visibility_message" = "O espaço criado vai ser adicionado a %@.";
"spaces_subspace_creation_visibility_title" = "Que tipo de subespaço você quer criar?";
"spaces_create_subspace_title" = "Criar um subespaço";
"spaces_add_subspace_title" = "Criar espaço dentro de %@";

View file

@ -2622,3 +2622,35 @@
"location_sharing_invalid_power_level_title" = "Nemáte oprávnenie na zdieľanie polohy v reálnom čase"; "location_sharing_invalid_power_level_title" = "Nemáte oprávnenie na zdieľanie polohy v reálnom čase";
"authentication_choose_password_not_verified_message" = "Skontrolujte si doručenú poštu"; "authentication_choose_password_not_verified_message" = "Skontrolujte si doručenú poštu";
"authentication_choose_password_not_verified_title" = "E-mail nie je overený"; "authentication_choose_password_not_verified_title" = "E-mail nie je overený";
"location_sharing_map_loading_error" = "Nie je možné načítať mapu\nTento domovský server nemusí byť nakonfigurovaný na zobrazovanie máp";
// Mark: - Space Selector
"space_selector_title" = "Moje priestory";
"all_chats_edit_menu_space_settings" = "Nastavenia priestoru";
"all_chats_edit_menu_leave_space" = "Opustiť %@";
"all_chats_user_menu_settings" = "Používateľské nastavenia";
"room_recents_recently_viewed_section" = "Nedávno zobrazené";
"all_chats_all_filter" = "Všetky";
"all_chats_edit_layout_alphabetical_order" = "Zoradiť A-Z";
"all_chats_edit_layout_activity_order" = "Zoradiť podľa aktivity";
"all_chats_edit_layout_show_filters" = "Zobraziť filtre";
"all_chats_edit_layout_show_recents" = "Zobraziť posledné";
"all_chats_edit_layout_sorting_options_title" = "Zoradiť správy podľa";
"all_chats_edit_layout_pin_spaces_title" = "Pripnite svoje priestory";
"all_chats_edit_layout_add_filters_message" = "Automaticky filtrujte svoje správy do kategórií podľa vlastného výberu";
"all_chats_edit_layout_add_filters_title" = "Filtrujte svoje správy";
"all_chats_edit_layout_add_section_message" = "Pripnite sekcie na domovskú obrazovku, aby ste k nim mali ľahký prístup";
"all_chats_edit_layout_add_section_title" = "Pridať sekciu na domovskú stránku";
"all_chats_edit_layout_unreads" = "Neprečítané";
"all_chats_edit_layout_recents" = "Nedávne";
"all_chats_edit_layout" = "Predvoľby rozmiestnenia";
"all_chats_section_title" = "Konverzácie";
// Mark: - All Chats
"all_chats_title" = "Všetky konverzácie";
"spaces_subspace_creation_visibility_message" = "Vytvorený priestor bude zaradený do %@.";
"spaces_subspace_creation_visibility_title" = "Aký typ podpriestoru chcete vytvoriť?";
"spaces_create_subspace_title" = "Vytvoriť podpriestor";
"spaces_add_subspace_title" = "Vytvoriť priestor v rámci %@";

View file

@ -159,7 +159,7 @@
// People tab // People tab
"people_invites_section" = "ЗАПРОШЕННЯ"; "people_invites_section" = "ЗАПРОШЕННЯ";
"people_conversation_section" = "БЕСІДИ"; "people_conversation_section" = "БЕСІДИ";
"people_no_conversation" = "Нема балачок"; "people_no_conversation" = "Немає бесід";
"room_participants_leave_prompt_msg_for_dm" = "Ви впевнені, що хочете вийти?"; "room_participants_leave_prompt_msg_for_dm" = "Ви впевнені, що хочете вийти?";
"room_participants_leave_prompt_title_for_dm" = "Вийти"; "room_participants_leave_prompt_title_for_dm" = "Вийти";
"client_android_name" = "Element Android"; "client_android_name" = "Element Android";
@ -1039,7 +1039,7 @@
"security_settings_blacklist_unverified_devices_description" = "Звірте всі сеанси користувача, щоб позначити його довіреним і надіслати йому повідомлення."; "security_settings_blacklist_unverified_devices_description" = "Звірте всі сеанси користувача, щоб позначити його довіреним і надіслати йому повідомлення.";
"security_settings_crosssigning_bootstrap" = "Налаштувати"; "security_settings_crosssigning_bootstrap" = "Налаштувати";
"security_settings_crosssigning_info_ok" = "Перехресне підписування готове до використання."; "security_settings_crosssigning_info_ok" = "Перехресне підписування готове до використання.";
"security_settings_crosssigning_info_trusted" = "Перехресне підписування увімкнено. Ви можете робити інших користувачів і свої інші сеанси довіреними на підставі перехресного підпису, але ви не можете перехресно підписувати цим сеансом, бо в нього ще нема закритих ключів перехресного підписування. Доповніть захист цього сеансу."; "security_settings_crosssigning_info_trusted" = "Перехресне підписування увімкнено. Ви можете робити інших користувачів і свої інші сеанси довіреними на підставі перехресного підписування, але ви не можете перехресно підписувати з цього сеансу, оскільки в нього ще немає приватних ключів перехресного підписування. Завершіть захист цього сеансу.";
"security_settings_crosssigning_info_exists" = "Ваш обліковий запис має ідентичність перехресного підписування, але вона ще не довірена цим сеансом. Доповніть захист цього сеансу."; "security_settings_crosssigning_info_exists" = "Ваш обліковий запис має ідентичність перехресного підписування, але вона ще не довірена цим сеансом. Доповніть захист цього сеансу.";
"security_settings_secure_backup_description" = "Зробіть резервну копію своїх ключів шифрування й даних облікового запису на випадок втрати доступу до своїх сеансів. Ваші ключі будуть захищені унікальним ключем безпеки."; "security_settings_secure_backup_description" = "Зробіть резервну копію своїх ключів шифрування й даних облікового запису на випадок втрати доступу до своїх сеансів. Ваші ключі будуть захищені унікальним ключем безпеки.";
"security_settings_crypto_sessions_description_2" = "Якщо не впізнаєте сеанс, скиньте пароль облікового запису Matrix і налаштування безпечного резервного копіювання."; "security_settings_crypto_sessions_description_2" = "Якщо не впізнаєте сеанс, скиньте пароль облікового запису Matrix і налаштування безпечного резервного копіювання.";
@ -1059,7 +1059,7 @@
"settings_confirm_media_size_description" = "Коли це ввімкнено, при надсиланні зображень чи відео вам пропонуватиметься підтвердити їхній розмір."; "settings_confirm_media_size_description" = "Коли це ввімкнено, при надсиланні зображень чи відео вам пропонуватиметься підтвердити їхній розмір.";
"settings_three_pids_management_information_part2" = "Знаходження"; "settings_three_pids_management_information_part2" = "Знаходження";
"settings_config_user_id" = "Ви ввійшли як %@"; "settings_config_user_id" = "Ви ввійшли як %@";
"unknown_devices_alert" = "Кімната містить сеанси, які досі не пройшли звірку.\nТобто нема гарантії, що ці сеанси належать користувачам, від імені яких вони створені.\nРадимо звірити кожен сеанс, перш ніж продовжити; але за потреби можете повторити надсилання повідомлення без звірки."; "unknown_devices_alert" = "Кімната містить невідомі не звірені сеанси.\nТобто немає гарантії, що ці сеанси належать користувачам, від імені яких вони створені.\nРадимо звірити кожен сеанс, перш ніж продовжувати; але за потреби можете повторити надсилання повідомлення без звірки.";
"room_action_camera" = "Зробити світлину або відео"; "room_action_camera" = "Зробити світлину або відео";
"room_member_power_level_short_custom" = "Інше"; "room_member_power_level_short_custom" = "Інше";
"room_member_power_level_custom_in" = "Інше (%@) у %@"; "room_member_power_level_custom_in" = "Інше (%@) у %@";
@ -1479,8 +1479,8 @@
"widget_integration_room_not_visible" = "Кімната %@ недоступна."; "widget_integration_room_not_visible" = "Кімната %@ недоступна.";
"widget_integration_missing_user_id" = "В запиті бракує user_id."; "widget_integration_missing_user_id" = "В запиті бракує user_id.";
"widget_integration_missing_room_id" = "В запиті бракує room_id."; "widget_integration_missing_room_id" = "В запиті бракує room_id.";
"widget_integration_no_permission_in_room" = "У вас нема такого дозволу в цій кімнаті."; "widget_integration_no_permission_in_room" = "У вас немає такого дозволу в цій кімнаті.";
"widget_integration_must_be_in_room" = "Вас нема в цій кімнаті."; "widget_integration_must_be_in_room" = "Вас немає в цій кімнаті.";
"widget_integration_positive_power_level" = "Рівень повноважень має бути цілим додатним числом."; "widget_integration_positive_power_level" = "Рівень повноважень має бути цілим додатним числом.";
"widget_integration_room_not_recognised" = "Кімнату не знайдено."; "widget_integration_room_not_recognised" = "Кімнату не знайдено.";
"widget_integration_failed_to_send_request" = "Не вдалося надіслати запит."; "widget_integration_failed_to_send_request" = "Не вдалося надіслати запит.";
@ -2624,3 +2624,35 @@
"location_sharing_invalid_power_level_title" = "Ви не маєте дозволу ділитися поточним місцем перебування"; "location_sharing_invalid_power_level_title" = "Ви не маєте дозволу ділитися поточним місцем перебування";
"authentication_choose_password_not_verified_message" = "Перевірте свою поштову скриньку"; "authentication_choose_password_not_verified_message" = "Перевірте свою поштову скриньку";
"authentication_choose_password_not_verified_title" = "Електронна адреса не підтверджена"; "authentication_choose_password_not_verified_title" = "Електронна адреса не підтверджена";
"location_sharing_map_loading_error" = "Неможливо завантажити карту\nЦей домашній сервер не налаштовано для показу карт";
// Mark: - Space Selector
"space_selector_title" = "Мої простори";
"all_chats_edit_menu_space_settings" = "Налаштування простору";
"all_chats_user_menu_settings" = "Налаштування користувача";
"all_chats_edit_menu_leave_space" = "Вийти з %@";
"room_recents_recently_viewed_section" = "Останні переглянуті";
"all_chats_all_filter" = "Усі";
"all_chats_edit_layout_alphabetical_order" = "Упорядкувати А-Я";
"all_chats_edit_layout_activity_order" = "Упорядкувати за активністю";
"all_chats_edit_layout_show_filters" = "Показати фільтри";
"all_chats_edit_layout_show_recents" = "Показати найновіші";
"all_chats_edit_layout_sorting_options_title" = "Упорядкувати повідомлення за";
"all_chats_edit_layout_pin_spaces_title" = "Прикріплюйте свої простори";
"all_chats_edit_layout_add_filters_message" = "Автоматично упорядковуйте свої повідомлення за категоріями на ваш вибір";
"all_chats_edit_layout_add_filters_title" = "Упорядковуйте свої повідомлення";
"all_chats_edit_layout_add_section_message" = "Закріпіть розділи в домівці для легкого доступу";
"all_chats_edit_layout_add_section_title" = "Додати розділ у домівку";
"all_chats_edit_layout_unreads" = "Непрочитані";
"all_chats_edit_layout_recents" = "Найновіші";
"all_chats_edit_layout" = "Параметри макета";
"all_chats_section_title" = "Бесіди";
// Mark: - All Chats
"all_chats_title" = "Усі бесіди";
"spaces_subspace_creation_visibility_message" = "Створений простір буде додано до %@.";
"spaces_subspace_creation_visibility_title" = "Який тип підпростору ви хочете створити?";
"spaces_create_subspace_title" = "Створити підпростір";
"spaces_add_subspace_title" = "Створити простір у %@";

View file

@ -228,7 +228,7 @@
// Room Preview // Room Preview
"room_preview_invitation_format" = "您已经通过 %@ 的邀请而加入此房间"; "room_preview_invitation_format" = "您已经通过 %@ 的邀请而加入此房间";
"room_preview_subtitle" = "这是此房间的一个预览。房间交互已禁用。"; "room_preview_subtitle" = "这是此房间的一个预览。房间交互已禁用。";
"room_preview_unlinked_email_warning" = "此邀请已发送至未与此户关联的 %@。你可能希望用一个不同的账户登录,或者把这个电子邮箱加入到您的账户。"; "room_preview_unlinked_email_warning" = "此邀请已发送至未与此户关联的 %@。你可能希望用一个不同的账户登录,或者把这个电子邮箱加入到您的账户。";
"room_preview_try_join_an_unknown_room" = "你正在尝试访问 %@。你要加入以参与讨论吗?"; "room_preview_try_join_an_unknown_room" = "你正在尝试访问 %@。你要加入以参与讨论吗?";
"room_preview_try_join_an_unknown_room_default" = "一间房间"; "room_preview_try_join_an_unknown_room_default" = "一间房间";
// Settings // Settings
@ -498,7 +498,7 @@
"room_resource_limit_exceeded_message_contact_1" = " 请 "; "room_resource_limit_exceeded_message_contact_1" = " 请 ";
"room_resource_limit_exceeded_message_contact_2_link" = "联系您的服务管理员"; "room_resource_limit_exceeded_message_contact_2_link" = "联系您的服务管理员";
"room_resource_limit_exceeded_message_contact_3" = " 以继续使用本服务。"; "room_resource_limit_exceeded_message_contact_3" = " 以继续使用本服务。";
"settings_deactivate_account" = "停用户"; "settings_deactivate_account" = "停用户";
"settings_labs_room_members_lazy_loading" = "延迟加载聊天室成员"; "settings_labs_room_members_lazy_loading" = "延迟加载聊天室成员";
"settings_labs_room_members_lazy_loading_error_message" = "您的主服务器尚不支持延迟加载聊天室成员。请稍后再试。"; "settings_labs_room_members_lazy_loading_error_message" = "您的主服务器尚不支持延迟加载聊天室成员。请稍后再试。";
"settings_deactivate_my_account" = "永久停用账户"; "settings_deactivate_my_account" = "永久停用账户";
@ -522,7 +522,7 @@
"room_resource_usage_limit_reached_message_contact_3" = " 以提高限制。"; "room_resource_usage_limit_reached_message_contact_3" = " 以提高限制。";
// String for App Store // String for App Store
"store_short_description" = "安全、去中心化的聊天及 VoIP 应用"; "store_short_description" = "安全、去中心化的聊天及 VoIP 应用";
"store_full_description" = "Element 是一种新型的通讯与协作应用:\n\n1. 使您可以掌控您的隐私\n2. 使您与 Matrix 网络中的任何人交流,甚至可以通过集成功能与如 Slack 之类的其他应用通讯\n3. 保护您免受广告,大数据挖掘和封闭服务的侵害\n4. 通过端到端加密保证安全,通过交叉签名验证其他人\n\nElement 与其他通讯与协作应用完全不同,因为它是去中心化且开源的。\n\nElement 允许您自托管——或者选择托管商——因此,您能拥有数据和会话的隐私权,所有权和控制权。它允许您访问开放网络;因此,您可以与 Element 用户以外的人交流。并且它非常安全。\n\nElement 之所以可以做到这些,是因为它在 Matrix 上运行——开放,去中心化通讯的标准。\n\n通过让您选择由谁来托管您的会话Element 让您掌控一切。在 Element 应用中,您可以选择不同的托管方式:\n\n1. 在由 Matrix 开发者托管的 matrix.org 公共服务器上获取免费户,或从志愿者托管的上千个公共服务器中选择\n2. 在您自己的硬件上运行服务器,自托管您的会话\n3. 通过订阅 Element Matrix Services 托管平台,简单地在自定义服务器上注册账户\n\n为什么选择 Element\n\n掌控您的数据您来决定存放您的数据和消息的位置。拥有并控制它的是您而不是挖掘您的数据或与第三方分享的巨型企业。\n\n开放通讯与协作您可以与 Matrix 网络中的任何人聊天,不论他们使用 Element 还是其他 Matrix 应用,甚至/即使他们在使用不同的通讯系统,例如 SlackIRC 或 XMPP。\n\n超级安全支持真正的端到端加密仅有会话中的人可以解密消息还有能够验证会话参与方的设备的交叉签名。\n\n完善的通讯方式消息语音和视频通话文件共享屏幕共享和大量集成功能机器人和挂件。建立房间与社区保持联系并完成工作。\n\n随时随地消息历史可在您的全部设备和 https://app.element.io 网页端之间完全同步,无论您在哪里,都可以保持联系。"; "store_full_description" = "Element 是一种新型的通讯与协作应用:\n\n1. 使您可以掌控您的隐私\n2. 使您与 Matrix 网络中的任何人交流,甚至可以通过集成功能与如 Slack 之类的其他应用通讯\n3. 保护您免受广告,大数据挖掘和封闭服务的侵害\n4. 通过端到端加密保证安全,通过交叉签名验证其他人\n\nElement 与其他通讯与协作应用完全不同,因为它是去中心化且开源的。\n\nElement 允许您自托管——或者选择托管商——因此,您能拥有数据和会话的隐私权,所有权和控制权。它允许您访问开放网络;因此,您可以与 Element 用户以外的人交流。并且它非常安全。\n\nElement 之所以可以做到这些,是因为它在 Matrix 上运行——开放,去中心化通讯的标准。\n\n通过让您选择由谁来托管您的会话Element 让您掌控一切。在 Element 应用中,您可以选择不同的托管方式:\n\n1. 在由 Matrix 开发者托管的 matrix.org 公共服务器上获取免费户,或从志愿者托管的上千个公共服务器中选择\n2. 在您自己的硬件上运行服务器,自托管您的会话\n3. 通过订阅 Element Matrix Services 托管平台,简单地在自定义服务器上注册账户\n\n为什么选择 Element\n\n掌控您的数据您来决定存放您的数据和消息的位置。拥有并控制它的是您而不是挖掘您的数据或与第三方分享的巨型企业。\n\n开放通讯与协作您可以与 Matrix 网络中的任何人聊天,不论他们使用 Element 还是其他 Matrix 应用,甚至/即使他们在使用不同的通讯系统,例如 SlackIRC 或 XMPP。\n\n超级安全支持真正的端到端加密仅有会话中的人可以解密消息还有能够验证会话参与方的设备的交叉签名。\n\n完善的通讯方式消息语音和视频通话文件共享屏幕共享和大量集成功能机器人和挂件。建立房间与社区保持联系并完成工作。\n\n随时随地消息历史可在您的全部设备和 https://app.element.io 网页端之间完全同步,无论您在哪里,都可以保持联系。";
"auth_accept_policies" = "请查看并接受此主页服务器的服务条款:"; "auth_accept_policies" = "请查看并接受此主页服务器的服务条款:";
"room_replacement_information" = "这个房间已被替换,不再有效。"; "room_replacement_information" = "这个房间已被替换,不再有效。";
"settings_flair" = "在允许的地方显示个性徽章"; "settings_flair" = "在允许的地方显示个性徽章";
@ -530,7 +530,7 @@
"settings_key_backup_info" = "消息已被端对端安全加密。只有您和持有密钥的接收方可以阅读这些消息。"; "settings_key_backup_info" = "消息已被端对端安全加密。只有您和持有密钥的接收方可以阅读这些消息。";
"settings_key_backup_info_checking" = "正在检查…"; "settings_key_backup_info_checking" = "正在检查…";
"settings_key_backup_info_none" = "您的密钥未从此会话备份。"; "settings_key_backup_info_none" = "您的密钥未从此会话备份。";
"settings_key_backup_info_signout_warning" = "在退出账之前备份你的密钥以避免丢失它们。"; "settings_key_backup_info_signout_warning" = "在退出账之前备份你的密钥以避免丢失它们。";
"settings_key_backup_info_version" = "密钥备份版本:%@"; "settings_key_backup_info_version" = "密钥备份版本:%@";
"settings_key_backup_info_algorithm" = "算法:%@"; "settings_key_backup_info_algorithm" = "算法:%@";
"settings_key_backup_info_valid" = "此会话正在备份密钥。"; "settings_key_backup_info_valid" = "此会话正在备份密钥。";
@ -574,13 +574,13 @@
// GDPR // GDPR
"gdpr_consent_not_given_alert_message" = "要继续使用该 %@ 主服务器,您必须查看并同意其服务条款和条件。"; "gdpr_consent_not_given_alert_message" = "要继续使用该 %@ 主服务器,您必须查看并同意其服务条款和条件。";
"gdpr_consent_not_given_alert_review_now_action" = "立即查看"; "gdpr_consent_not_given_alert_review_now_action" = "立即查看";
"deactivate_account_title" = "停用户"; "deactivate_account_title" = "停用户";
"deactivate_account_informations_part1" = "这将使您的账永久无法使用。 您将无法登录,也无法重新注册相同的用户 ID 。 这将导致您的账号离开其参与的所有房间,并且会从您的身份服务器中删除您的账号详细信息。 "; "deactivate_account_informations_part1" = "这将使您的账永久无法使用。 您将无法登录,也无法重新注册相同的用户 ID 。 这将导致您的账户离开其参与的所有房间,并且会从您的身份服务器中删除您的账户详细信息。 ";
"deactivate_account_informations_part2_emphasize" = "此项操作无法逆转。"; "deactivate_account_informations_part2_emphasize" = "此项操作无法逆转。";
"deactivate_account_informations_part3" = "\n\n正在停用您的账 "; "deactivate_account_informations_part3" = "\n\n正在停用您的账 ";
"deactivate_account_informations_part4_emphasize" = "默认情况下不会导致我们忘记您发送的消息。 "; "deactivate_account_informations_part4_emphasize" = "默认情况下不会导致我们忘记您发送的消息。 ";
"deactivate_account_informations_part5" = "如果您希望我们忘记您的消息,请勾选下面的框\n\nMatrix中的消息可见性与电子邮件类似。 我们忘记您的消息意味着您已发送的消息将不会再与任何新用户或未注册用户共享,但已有权访问这些消息的注册用户仍可访问其副本。"; "deactivate_account_informations_part5" = "如果您希望我们忘记您的消息,请勾选下面的框\n\nMatrix中的消息可见性与电子邮件类似。 我们忘记您的消息意味着您已发送的消息将不会再与任何新用户或未注册用户共享,但已有权访问这些消息的注册用户仍可访问其副本。";
"deactivate_account_forget_messages_information_part1" = "当我的户被停用时,请忘记我发送的所有消息("; "deactivate_account_forget_messages_information_part1" = "当我的户被停用时,请忘记我发送的所有消息(";
"deactivate_account_forget_messages_information_part3" = ": 这会导致将来加入的用户看到的是一段不完整的对话)"; "deactivate_account_forget_messages_information_part3" = ": 这会导致将来加入的用户看到的是一段不完整的对话)";
"deactivate_account_password_alert_message" = "要继续,请输入您的密码"; "deactivate_account_password_alert_message" = "要继续,请输入您的密码";
"rerequest_keys_alert_message" = "请在另一台可以解密消息的设备上启动%@,这样它就可以将密钥发送到此会话。"; "rerequest_keys_alert_message" = "请在另一台可以解密消息的设备上启动%@,这样它就可以将密钥发送到此会话。";
@ -594,22 +594,22 @@
"key_backup_setup_intro_setup_action_with_existing_backup" = "使用密钥备份"; "key_backup_setup_intro_setup_action_with_existing_backup" = "使用密钥备份";
"key_backup_setup_intro_manual_export_info" = "(高级)"; "key_backup_setup_intro_manual_export_info" = "(高级)";
"key_backup_setup_intro_manual_export_action" = "手动导出密钥"; "key_backup_setup_intro_manual_export_action" = "手动导出密钥";
"key_backup_setup_passphrase_title" = "使用一段安全口令保护您的备份"; "key_backup_setup_passphrase_title" = "使用一段安全词组保护您的备份";
"key_backup_setup_passphrase_info" = "我们将会在主服务器上保存一份您的密钥的加密拷贝。设置一个密码口令来保护您的备份的安全。\n\n为了最大的安全性这个密码应当与您的账号密码不同。"; "key_backup_setup_passphrase_info" = "我们将会在主服务器上保存一份您的密钥的加密拷贝。设置一个词组来保护您的备份的安全。\n\n为了最大的安全性它应当与您的Matrix账户密码不同。";
"key_backup_setup_passphrase_passphrase_title" = "输入"; "key_backup_setup_passphrase_passphrase_title" = "输入";
"key_backup_setup_passphrase_passphrase_placeholder" = "输入密码口令"; "key_backup_setup_passphrase_passphrase_placeholder" = "输入词组";
"key_backup_setup_passphrase_passphrase_valid" = "太棒了!"; "key_backup_setup_passphrase_passphrase_valid" = "太棒了!";
"key_backup_setup_passphrase_passphrase_invalid" = "尝试添加一个字符"; "key_backup_setup_passphrase_passphrase_invalid" = "尝试添加一个字符";
"key_backup_setup_passphrase_confirm_passphrase_title" = "确认"; "key_backup_setup_passphrase_confirm_passphrase_title" = "确认";
"key_backup_setup_passphrase_confirm_passphrase_placeholder" = "确认密码口令"; "key_backup_setup_passphrase_confirm_passphrase_placeholder" = "确认词组";
"key_backup_setup_passphrase_confirm_passphrase_valid" = "太棒了!"; "key_backup_setup_passphrase_confirm_passphrase_valid" = "太棒了!";
"key_backup_setup_passphrase_confirm_passphrase_invalid" = "密码口令不匹配"; "key_backup_setup_passphrase_confirm_passphrase_invalid" = "词组不匹配";
"key_backup_setup_passphrase_set_passphrase_action" = "设置密码口令"; "key_backup_setup_passphrase_set_passphrase_action" = "设置词组";
"key_backup_setup_passphrase_setup_recovery_key_info" = "或者使用安全密钥来保护您的备份,将密钥保存在一个安全的地方。"; "key_backup_setup_passphrase_setup_recovery_key_info" = "或者使用安全密钥来保护您的备份,将密钥保存在一个安全的地方。";
"key_backup_setup_passphrase_setup_recovery_key_action" = "(高级) 使用安全密钥进行设置"; "key_backup_setup_passphrase_setup_recovery_key_action" = "(高级)使用安全密钥进行设置";
"key_backup_setup_success_title" = "成功!"; "key_backup_setup_success_title" = "成功!";
// Success from passphrase // Success from passphrase
"key_backup_setup_success_from_passphrase_info" = "您的密钥已备份。\n\n您的安全密钥是一张安全网 - 如果您忘记了密码您可以利用它重获您的已加密信息的访问权。\n\n请将您的安全密钥保存在一个非常安全的地方比如密码管理器中 (或保险箱里)。"; "key_backup_setup_success_from_passphrase_info" = "您的密钥正在备份。\n\n您的安全密钥是一张安全网——如果您忘记了口令词组您可以利用它重获您的已加密信息的访问权。\n\n请将您的安全密钥保存在一个非常安全的地方比如密码管理器中或保险箱里。";
"key_backup_setup_success_from_passphrase_save_recovery_key_action" = "保存安全密钥"; "key_backup_setup_success_from_passphrase_save_recovery_key_action" = "保存安全密钥";
"key_backup_setup_success_from_passphrase_done_action" = "完成"; "key_backup_setup_success_from_passphrase_done_action" = "完成";
// Success from recovery key // Success from recovery key
@ -618,15 +618,15 @@
"key_backup_setup_success_from_recovery_key_make_copy_action" = "制作副本"; "key_backup_setup_success_from_recovery_key_make_copy_action" = "制作副本";
"key_backup_setup_success_from_recovery_key_made_copy_action" = "我已制作一份副本"; "key_backup_setup_success_from_recovery_key_made_copy_action" = "我已制作一份副本";
"key_backup_recover_title" = "安全消息"; "key_backup_recover_title" = "安全消息";
"key_backup_recover_invalid_passphrase_title" = "密码口令不正确"; "key_backup_recover_invalid_passphrase_title" = "安全词组不正确";
"key_backup_recover_invalid_passphrase" = "备份无法用此密码口令解密:请检查您输入的安全口令是否正确。"; "key_backup_recover_invalid_passphrase" = "备份无法用此词组解密:请检查您输入的安全词组是否正确。";
"key_backup_recover_invalid_recovery_key_title" = "安全密钥不匹配"; "key_backup_recover_invalid_recovery_key_title" = "安全密钥不匹配";
"key_backup_recover_invalid_recovery_key" = "备份无法用此密钥解密:请检查您输入的安全密钥是否正确。"; "key_backup_recover_invalid_recovery_key" = "备份无法用此密钥解密:请检查您输入的安全密钥是否正确。";
"key_backup_recover_from_passphrase_info" = "使用安全口令解锁您的安全消息历史"; "key_backup_recover_from_passphrase_info" = "使用安全词组解锁您的安全消息历史";
"key_backup_recover_from_passphrase_passphrase_title" = "输入"; "key_backup_recover_from_passphrase_passphrase_title" = "输入";
"key_backup_recover_from_passphrase_passphrase_placeholder" = "输入密码口令"; "key_backup_recover_from_passphrase_passphrase_placeholder" = "输入词组";
"key_backup_recover_from_passphrase_recover_action" = "解锁历史"; "key_backup_recover_from_passphrase_recover_action" = "解锁历史";
"key_backup_recover_from_passphrase_lost_passphrase_action_part1" = "忘记了您的安全口令?您可以 "; "key_backup_recover_from_passphrase_lost_passphrase_action_part1" = "忘记了您的安全词组?您可以 ";
"key_backup_recover_from_passphrase_lost_passphrase_action_part2" = "使用你的安全密钥"; "key_backup_recover_from_passphrase_lost_passphrase_action_part2" = "使用你的安全密钥";
"key_backup_recover_from_passphrase_lost_passphrase_action_part3" = "。"; "key_backup_recover_from_passphrase_lost_passphrase_action_part3" = "。";
"key_backup_recover_from_recovery_key_info" = "使用安全密钥解锁您的安全消息历史"; "key_backup_recover_from_recovery_key_info" = "使用安全密钥解锁您的安全消息历史";
@ -640,16 +640,16 @@
"key_backup_setup_banner_subtitle" = "开始使用密钥备份"; "key_backup_setup_banner_subtitle" = "开始使用密钥备份";
"key_backup_recover_banner_title" = "永不丢失加密消息"; "key_backup_recover_banner_title" = "永不丢失加密消息";
"key_backup_recover_banner_subtitle" = "使用密钥备份"; "key_backup_recover_banner_subtitle" = "使用密钥备份";
"sign_out_existing_key_backup_alert_title" = "您确定要登出账吗?"; "sign_out_existing_key_backup_alert_title" = "您确定要登出账吗?";
"sign_out_existing_key_backup_alert_sign_out_action" = "登出"; "sign_out_existing_key_backup_alert_sign_out_action" = "登出";
"sign_out_non_existing_key_backup_alert_title" = "如果您此时登出账,您将会失去已加密消息的访问权"; "sign_out_non_existing_key_backup_alert_title" = "如果您此时登出账,您将会失去已加密消息的访问权";
"sign_out_non_existing_key_backup_alert_setup_key_backup_action" = "开始使用密钥备份"; "sign_out_non_existing_key_backup_alert_setup_key_backup_action" = "开始使用密钥备份";
"sign_out_non_existing_key_backup_alert_discard_key_backup_action" = "我不想要我的已加密消息了"; "sign_out_non_existing_key_backup_alert_discard_key_backup_action" = "我不想要我的已加密消息了";
"sign_out_non_existing_key_backup_sign_out_confirmation_alert_title" = "您将会丢失您的已加密消息"; "sign_out_non_existing_key_backup_sign_out_confirmation_alert_title" = "您将会丢失您的已加密消息";
"sign_out_non_existing_key_backup_sign_out_confirmation_alert_message" = "如果您在登出账之前不备份您的密钥,您将会失去已加密消息的访问权。"; "sign_out_non_existing_key_backup_sign_out_confirmation_alert_message" = "如果您在登出账之前不备份您的密钥,您将会失去已加密消息的访问权。";
"sign_out_non_existing_key_backup_sign_out_confirmation_alert_sign_out_action" = "登出"; "sign_out_non_existing_key_backup_sign_out_confirmation_alert_sign_out_action" = "登出";
"sign_out_non_existing_key_backup_sign_out_confirmation_alert_backup_action" = "备份"; "sign_out_non_existing_key_backup_sign_out_confirmation_alert_backup_action" = "备份";
"sign_out_key_backup_in_progress_alert_title" = "密钥备份进行中。如果您此时登出账将会失去已加密消息的访问权。"; "sign_out_key_backup_in_progress_alert_title" = "密钥备份进行中。如果您此时登出账将会失去已加密消息的访问权。";
"sign_out_key_backup_in_progress_alert_discard_key_backup_action" = "我不想要我的已加密消息了"; "sign_out_key_backup_in_progress_alert_discard_key_backup_action" = "我不想要我的已加密消息了";
"sign_out_key_backup_in_progress_alert_cancel_action" = "等待"; "sign_out_key_backup_in_progress_alert_cancel_action" = "等待";
"auth_login_single_sign_on" = "使用单点登录方式登入"; "auth_login_single_sign_on" = "使用单点登录方式登入";
@ -660,21 +660,21 @@
"accessibility_checkbox_label" = "多选框"; "accessibility_checkbox_label" = "多选框";
"auth_add_email_message_2" = "设置邮箱以便恢复账户,并且之后可选让认识的人找到你。"; "auth_add_email_message_2" = "设置邮箱以便恢复账户,并且之后可选让认识的人找到你。";
"auth_add_phone_message_2" = "设置电话号码,并且之后可选让认识的人找到你。"; "auth_add_phone_message_2" = "设置电话号码,并且之后可选让认识的人找到你。";
"auth_add_email_phone_message_2" = "设置邮箱以便恢复账。之后可选用邮箱或者电话号码让认识的人找到你。"; "auth_add_email_phone_message_2" = "设置邮箱以便恢复账。之后可选用邮箱或者电话号码让认识的人找到你。";
"auth_email_is_required" = "未设置身份认证服务器,所以你不能添加邮箱地址来重设你的密码。"; "auth_email_is_required" = "未设置身份认证服务器,所以你不能添加邮箱地址来重设你的密码。";
"auth_phone_is_required" = "未设置身份认证服务器,所以你不能添加电话号码来重设你的密码。"; "auth_phone_is_required" = "未设置身份认证服务器,所以你不能添加电话号码来重设你的密码。";
"auth_forgot_password_error_no_configured_identity_server" = "未设置身份认证服务器:添加服务器以重设你的密码。"; "auth_forgot_password_error_no_configured_identity_server" = "未设置身份认证服务器:添加服务器以重设你的密码。";
"auth_reset_password_error_is_required" = "未设置身份认证服务器:在服务器选项中添加以便重设你的密码。"; "auth_reset_password_error_is_required" = "未设置身份认证服务器:在服务器选项中添加以便重设你的密码。";
"auth_softlogout_signed_out" = "你已登出"; "auth_softlogout_signed_out" = "你已登出";
"auth_softlogout_sign_in" = "登录"; "auth_softlogout_sign_in" = "登录";
"auth_softlogout_reason" = "你的主服务器(%1$@)管理员已将你的账%2$@%3$@)登出。"; "auth_softlogout_reason" = "你的主服务器(%1$@)管理员已将你的账%2$@%3$@)登出。";
"auth_softlogout_recover_encryption_keys" = "登录以恢复单独保存在此设备上的加密密钥。你需要它们才能阅读任何设备上的安全消息。"; "auth_softlogout_recover_encryption_keys" = "登录以恢复单独保存在此设备上的加密密钥。你需要它们才能阅读任何设备上的安全消息。";
"auth_softlogout_clear_data" = "清空个人信息"; "auth_softlogout_clear_data" = "清空个人信息";
"auth_softlogout_clear_data_message_1" = "警告:你的个人信息(包括加密密钥)仍将保存在这台设备上。"; "auth_softlogout_clear_data_message_1" = "警告:你的个人信息(包括加密密钥)仍将保存在这台设备上。";
"auth_softlogout_clear_data_message_2" = "当你不再使用此设备,或者想登录另一个账时,请清空它。"; "auth_softlogout_clear_data_message_2" = "当你不再使用此设备,或者想登录另一个账时,请清空它。";
"auth_softlogout_clear_data_button" = "清空所有数据"; "auth_softlogout_clear_data_button" = "清空所有数据";
"auth_softlogout_clear_data_sign_out_title" = "你确定吗?"; "auth_softlogout_clear_data_sign_out_title" = "你确定吗?";
"auth_softlogout_clear_data_sign_out_msg" = "你确定希望清空所有当前保存在此设备上数据吗?再次登录可以获取你的账数据和消息。"; "auth_softlogout_clear_data_sign_out_msg" = "你确定希望清空所有当前保存在此设备上数据吗?再次登录可以获取你的账数据和消息。";
"auth_softlogout_clear_data_sign_out" = "登出"; "auth_softlogout_clear_data_sign_out" = "登出";
// Errors // Errors
"error_user_already_logged_in" = "您似乎正在尝试连接另一个主服务器。您想要登出吗?"; "error_user_already_logged_in" = "您似乎正在尝试连接另一个主服务器。您想要登出吗?";
@ -709,18 +709,18 @@
"media_type_accessibility_video" = "视频"; "media_type_accessibility_video" = "视频";
"media_type_accessibility_location" = "位置"; "media_type_accessibility_location" = "位置";
"media_type_accessibility_file" = "文件"; "media_type_accessibility_file" = "文件";
"media_type_accessibility_sticker" = "贴"; "media_type_accessibility_sticker" = "贴";
"settings_discovery_settings" = "发现"; "settings_discovery_settings" = "发现";
"settings_identity_server_settings" = "身份认证服务器"; "settings_identity_server_settings" = "身份认证服务器";
"settings_integrations" = "集成"; "settings_integrations" = "集成";
"settings_three_pids_management_information_part1" = "在这里管理你可以用来登录或者恢复账的电子邮箱地址或者电话号码。控制谁可以通过以下途径找到你: "; "settings_three_pids_management_information_part1" = "在这里管理你可以用来登录或者恢复账的电子邮箱地址或者电话号码。控制谁可以通过以下途径找到你: ";
"settings_three_pids_management_information_part2" = "发现"; "settings_three_pids_management_information_part2" = "发现";
"settings_three_pids_management_information_part3" = "。"; "settings_three_pids_management_information_part3" = "。";
"settings_security" = "安全"; "settings_security" = "安全";
"settings_calls_stun_server_fallback_button" = "允许使用通话辅助服务器作为备用手段"; "settings_calls_stun_server_fallback_button" = "允许使用通话辅助服务器作为备用手段";
"settings_calls_stun_server_fallback_description" = "允许在你的主服务器不能提供时使用通话辅助服务器 %@ 作为备用手段你的IP地址在通话时会被分享。"; "settings_calls_stun_server_fallback_description" = "允许在你的主服务器不能提供时使用通话辅助服务器 %@ 作为备用手段你的IP地址在通话时会被分享。";
"settings_integrations_allow_button" = "管理集成"; "settings_integrations_allow_button" = "管理集成";
"settings_integrations_allow_description" = "使用集成管理器(%@)来管理机器人,桥接,小插件和贴包。\n\n集成管理器会收到设置数据而且能修改小插件发送房间邀请以及代表你设置权力级别。"; "settings_integrations_allow_description" = "使用集成管理器(%@)来管理机器人,桥接,小插件和贴包。\n\n集成管理器会收到设置数据而且能修改小插件发送房间邀请以及代表你设置权力级别。";
"settings_labs_message_reaction" = "用emoji表情回应消息"; "settings_labs_message_reaction" = "用emoji表情回应消息";
"settings_labs_enable_cross_signing" = "开启交叉签名按用户验证而不是按设备验证(开发中)"; "settings_labs_enable_cross_signing" = "开启交叉签名按用户验证而不是按设备验证(开发中)";
"settings_add_3pid_password_title_email" = "添加邮箱地址"; "settings_add_3pid_password_title_email" = "添加邮箱地址";
@ -736,9 +736,9 @@
"settings_discovery_three_pids_management_information_part3" = "。"; "settings_discovery_three_pids_management_information_part3" = "。";
"settings_discovery_error_message" = "发生错误。请重试。"; "settings_discovery_error_message" = "发生错误。请重试。";
"settings_discovery_three_pid_details_title_email" = "管理邮箱"; "settings_discovery_three_pid_details_title_email" = "管理邮箱";
"settings_discovery_three_pid_details_information_email" = "设置此邮箱地址的偏好,其他用户可使用它发现你和邀请你加入房间。在“账”中添加或者删除邮箱地址。"; "settings_discovery_three_pid_details_information_email" = "设置此邮箱地址的偏好,其他用户可使用它发现你和邀请你加入房间。在“账”中添加或者删除邮箱地址。";
"settings_discovery_three_pid_details_title_phone_number" = "管理电话号码"; "settings_discovery_three_pid_details_title_phone_number" = "管理电话号码";
"settings_discovery_three_pid_details_information_phone_number" = "设置此电话号码的偏好,其他用户可使用它发现你和邀请你加入房间。在“账”中添加或者删除电话号码。"; "settings_discovery_three_pid_details_information_phone_number" = "设置此电话号码的偏好,其他用户可使用它发现你和邀请你加入房间。在“账”中添加或者删除电话号码。";
"settings_discovery_three_pid_details_share_action" = "分享"; "settings_discovery_three_pid_details_share_action" = "分享";
"settings_discovery_three_pid_details_revoke_action" = "撤回"; "settings_discovery_three_pid_details_revoke_action" = "撤回";
"settings_discovery_three_pid_details_cancel_email_validation_action" = "取消邮箱验证"; "settings_discovery_three_pid_details_cancel_email_validation_action" = "取消邮箱验证";
@ -973,7 +973,7 @@
"user_verification_session_details_information_untrusted_current_user" = "验证这个会话以标记它为已信任并且给与它加密消息的获取权限:"; "user_verification_session_details_information_untrusted_current_user" = "验证这个会话以标记它为已信任并且给与它加密消息的获取权限:";
"user_verification_session_details_information_untrusted_other_user" = " 用新会话登录:"; "user_verification_session_details_information_untrusted_other_user" = " 用新会话登录:";
"user_verification_session_details_additional_information_untrusted_other_user" = "在此用户信任这个会话之前,发往和收到它的消息会有警告标签。或者,你可以手动验证它。"; "user_verification_session_details_additional_information_untrusted_other_user" = "在此用户信任这个会话之前,发往和收到它的消息会有警告标签。或者,你可以手动验证它。";
"user_verification_session_details_additional_information_untrusted_current_user" = "如果你没有登录到这个会话,你的账可能在被盗用。"; "user_verification_session_details_additional_information_untrusted_current_user" = "如果你没有登录到这个会话,你的账可能在被盗用。";
"user_verification_session_details_verify_action_current_user" = "交互式验证"; "user_verification_session_details_verify_action_current_user" = "交互式验证";
"user_verification_session_details_verify_action_other_user" = "手动验证"; "user_verification_session_details_verify_action_other_user" = "手动验证";
"room_participants_action_security_status_complete_security" = "完整安全性"; "room_participants_action_security_status_complete_security" = "完整安全性";
@ -984,7 +984,7 @@
"room_member_power_level_short_moderator" = "协管员"; "room_member_power_level_short_moderator" = "协管员";
"security_settings_crosssigning" = "交叉签名"; "security_settings_crosssigning" = "交叉签名";
"security_settings_crosssigning_info_not_bootstrapped" = "交叉签名还没有被设置。"; "security_settings_crosssigning_info_not_bootstrapped" = "交叉签名还没有被设置。";
"security_settings_crosssigning_info_exists" = "您的户有一个交叉签名身份,但是还没有被这个会话信任。完全安全的会话。"; "security_settings_crosssigning_info_exists" = "您的户有一个交叉签名身份,但是还没有被这个会话信任。完全安全的会话。";
"skip" = "跳过"; "skip" = "跳过";
"room_member_power_level_custom_in" = "%@里的自定义(%@"; "room_member_power_level_custom_in" = "%@里的自定义(%@";
"room_member_power_level_short_custom" = "自定义"; "room_member_power_level_short_custom" = "自定义";
@ -1006,7 +1006,7 @@
"device_verification_security_advice_number" = "比较数字,确保它们以相同的顺序出现。"; "device_verification_security_advice_number" = "比较数字,确保它们以相同的顺序出现。";
// New login // New login
"device_verification_self_verify_alert_title" = "新登录。这是你吗?"; "device_verification_self_verify_alert_title" = "新登录。这是你吗?";
"device_verification_self_verify_alert_message" = "验证访问您的户的新登录名:%@"; "device_verification_self_verify_alert_message" = "验证访问您的户的新登录名:%@";
"device_verification_self_verify_alert_validate_action" = "验证"; "device_verification_self_verify_alert_validate_action" = "验证";
"device_verification_self_verify_start_verify_action" = "开始验证"; "device_verification_self_verify_start_verify_action" = "开始验证";
"device_verification_self_verify_start_information" = "使用此会话验证您的新会话,并授予其访问加密信息的权限。"; "device_verification_self_verify_start_information" = "使用此会话验证您的新会话,并授予其访问加密信息的权限。";
@ -1015,14 +1015,14 @@
"key_verification_self_verify_current_session_alert_message" = "其他用户可能不信任它。"; "key_verification_self_verify_current_session_alert_message" = "其他用户可能不信任它。";
"key_verification_self_verify_current_session_alert_validate_action" = "验证"; "key_verification_self_verify_current_session_alert_validate_action" = "验证";
"key_verification_self_verify_unverified_sessions_alert_title" = "查看您的登录位置"; "key_verification_self_verify_unverified_sessions_alert_title" = "查看您的登录位置";
"key_verification_self_verify_unverified_sessions_alert_message" = "验证您的所有会话以确保您的户和消息的安全。"; "key_verification_self_verify_unverified_sessions_alert_message" = "验证您的所有会话以确保您的户和消息的安全。";
"key_verification_self_verify_unverified_sessions_alert_validate_action" = "检查"; "key_verification_self_verify_unverified_sessions_alert_validate_action" = "检查";
"device_verification_self_verify_wait_title" = "绝对安全"; "device_verification_self_verify_wait_title" = "绝对安全";
"device_verification_self_verify_wait_new_sign_in_title" = "验证此登录名"; "device_verification_self_verify_wait_new_sign_in_title" = "验证此登录名";
"device_verification_self_verify_wait_information" = "从您的其他会话之一验证此会话,授予它访问加密消息的权限。\n\n在您的其他设备上使用最新版的%@:"; "device_verification_self_verify_wait_information" = "从您的其他会话之一验证此会话,授予它访问加密消息的权限。\n\n在您的其他设备上使用最新版的%@:";
"device_verification_self_verify_wait_additional_information" = "这适用于%@和其他支持交叉签名的Matrix客户端。"; "device_verification_self_verify_wait_additional_information" = "这适用于%@和其他支持交叉签名的Matrix客户端。";
"device_verification_self_verify_wait_recover_secrets_without_passphrase" = "使用安全密钥"; "device_verification_self_verify_wait_recover_secrets_without_passphrase" = "使用安全密钥";
"device_verification_self_verify_wait_recover_secrets_with_passphrase" = "使用安全口令或密钥"; "device_verification_self_verify_wait_recover_secrets_with_passphrase" = "使用安全词组或密钥";
"device_verification_self_verify_wait_recover_secrets_additional_information" = "如果您无法访问一个现有会话"; "device_verification_self_verify_wait_recover_secrets_additional_information" = "如果您无法访问一个现有会话";
"key_verification_verify_sas_title_emoji" = "比较emoji"; "key_verification_verify_sas_title_emoji" = "比较emoji";
"key_verification_verify_sas_title_number" = "比较数字"; "key_verification_verify_sas_title_number" = "比较数字";
@ -1062,17 +1062,17 @@
"key_verification_scan_confirmation_scanned_user_information" = "是否%@显示相同的盾牌?"; "key_verification_scan_confirmation_scanned_user_information" = "是否%@显示相同的盾牌?";
"key_verification_scan_confirmation_scanned_device_information" = "另一台设备是否显示相同的盾牌?"; "key_verification_scan_confirmation_scanned_device_information" = "另一台设备是否显示相同的盾牌?";
"user_verification_session_details_verify_action_current_user_manually" = "通过文本手动验证"; "user_verification_session_details_verify_action_current_user_manually" = "通过文本手动验证";
"secrets_recovery_with_passphrase_title" = "安全口令"; "secrets_recovery_with_passphrase_title" = "安全词组";
"secrets_recovery_with_passphrase_information_default" = "通过输入安全口令,访问您的安全信息历史记录和交叉登录身份,以验证其他会话。"; "secrets_recovery_with_passphrase_information_default" = "通过输入安全词组,访问您的安全信息历史记录和交叉登录身份,以验证其他会话。";
"secrets_recovery_with_passphrase_information_verify_device" = "使用您的安全口令验证此设备。"; "secrets_recovery_with_passphrase_information_verify_device" = "使用您的安全词组验证此设备。";
"secrets_recovery_with_passphrase_passphrase_title" = "输入"; "secrets_recovery_with_passphrase_passphrase_title" = "输入";
"secrets_recovery_with_passphrase_passphrase_placeholder" = "输入安全口令"; "secrets_recovery_with_passphrase_passphrase_placeholder" = "输入安全词组";
"secrets_recovery_with_passphrase_recover_action" = "使用密码口令"; "secrets_recovery_with_passphrase_recover_action" = "使用词组";
"secrets_recovery_with_passphrase_lost_passphrase_action_part1" = "忘记了您的安全口令?您可以 "; "secrets_recovery_with_passphrase_lost_passphrase_action_part1" = "忘记了您的安全词组?您可以 ";
"secrets_recovery_with_passphrase_lost_passphrase_action_part2" = "使用您的安全密钥"; "secrets_recovery_with_passphrase_lost_passphrase_action_part2" = "使用您的安全密钥";
"secrets_recovery_with_passphrase_lost_passphrase_action_part3" = "."; "secrets_recovery_with_passphrase_lost_passphrase_action_part3" = ".";
"secrets_recovery_with_passphrase_invalid_passphrase_title" = "无法访问机密存储"; "secrets_recovery_with_passphrase_invalid_passphrase_title" = "无法访问机密存储";
"secrets_recovery_with_passphrase_invalid_passphrase_message" = "请验证您输入的安全口令是否正确。"; "secrets_recovery_with_passphrase_invalid_passphrase_message" = "请验证您输入的安全词组是否正确。";
"secrets_recovery_with_key_title" = "安全密钥"; "secrets_recovery_with_key_title" = "安全密钥";
"secrets_recovery_with_key_information_default" = "通过输入安全密钥访问安全信息历史记录和交叉登录身份,以验证其他会话。"; "secrets_recovery_with_key_information_default" = "通过输入安全密钥访问安全信息历史记录和交叉登录身份,以验证其他会话。";
"secrets_recovery_with_key_information_verify_device" = "使用您的安全密钥验证此设备。"; "secrets_recovery_with_key_information_verify_device" = "使用您的安全密钥验证此设备。";
@ -1082,7 +1082,7 @@
"secrets_recovery_with_key_invalid_recovery_key_title" = "无法访问机密存储"; "secrets_recovery_with_key_invalid_recovery_key_title" = "无法访问机密存储";
"secrets_recovery_with_key_invalid_recovery_key_message" = "请验证您输入的安全密钥是否正确。"; "secrets_recovery_with_key_invalid_recovery_key_message" = "请验证您输入的安全密钥是否正确。";
"rooms_empty_view_information" = "房间非常适合任何群聊,无论是私人的还是公共的。点击+以查找现有房间,或新建房间。"; "rooms_empty_view_information" = "房间非常适合任何群聊,无论是私人的还是公共的。点击+以查找现有房间,或新建房间。";
"security_settings_user_password_description" = "通过输入您的户密码确认您的身份"; "security_settings_user_password_description" = "通过输入您的户密码确认您的身份";
"rooms_empty_view_title" = "房间"; "rooms_empty_view_title" = "房间";
"people_empty_view_information" = "与任何人安全聊天。点击+开始添加人员。"; "people_empty_view_information" = "与任何人安全聊天。点击+开始添加人员。";
"people_empty_view_title" = "用户"; "people_empty_view_title" = "用户";
@ -1103,7 +1103,7 @@
"security_settings_secure_backup_delete" = "删除备份"; "security_settings_secure_backup_delete" = "删除备份";
"security_settings_secure_backup_synchronise" = "同步"; "security_settings_secure_backup_synchronise" = "同步";
"security_settings_secure_backup_setup" = "设置"; "security_settings_secure_backup_setup" = "设置";
"security_settings_secure_backup_description" = "备份你的户数据备份和加密密钥,以防你无法访问会话。 你的密钥将受到唯一的安全密钥保护。"; "security_settings_secure_backup_description" = "备份你的户数据备份和加密密钥,以防你无法访问会话。 你的密钥将受到唯一的安全密钥保护。";
"security_settings_crypto_sessions_description_2" = "如果您未曾发起登录,请更改密码并重置安全备份。"; "security_settings_crypto_sessions_description_2" = "如果您未曾发起登录,请更改密码并重置安全备份。";
"settings_show_NSFW_public_rooms" = "显示 NSFW 公共房间"; "settings_show_NSFW_public_rooms" = "显示 NSFW 公共房间";
"external_link_confirmation_message" = "此链接 %@ 会将您带至另一个网站:%@\n\n是否前往"; "external_link_confirmation_message" = "此链接 %@ 会将您带至另一个网站:%@\n\n是否前往";
@ -1209,15 +1209,15 @@
"major_update_title" = "Riot 现已成为 %@"; "major_update_title" = "Riot 现已成为 %@";
"secrets_reset_reset_action" = "重置"; "secrets_reset_reset_action" = "重置";
"secrets_setup_recovery_passphrase_summary_title" = "保存您的安全密语"; "secrets_setup_recovery_passphrase_summary_title" = "保存您的安全词组";
"secrets_setup_recovery_passphrase_confirm_passphrase_placeholder" = "确认口令"; "secrets_setup_recovery_passphrase_confirm_passphrase_placeholder" = "确认词组";
"secrets_setup_recovery_passphrase_confirm_passphrase_title" = "确认"; "secrets_setup_recovery_passphrase_confirm_passphrase_title" = "确认";
"secrets_setup_recovery_passphrase_additional_information" = "不要使用你的账号密码。"; "secrets_setup_recovery_passphrase_additional_information" = "不要使用你的Matrix账户密码。";
"secrets_setup_recovery_passphrase_validate_action" = "完成"; "secrets_setup_recovery_passphrase_validate_action" = "完成";
// Recovery passphrase // Recovery passphrase
"secrets_setup_recovery_passphrase_title" = "设置安全密语"; "secrets_setup_recovery_passphrase_title" = "设置安全词组";
"secrets_setup_recovery_key_done_action" = "完成"; "secrets_setup_recovery_key_done_action" = "完成";
"secrets_setup_recovery_key_export_action" = "保存"; "secrets_setup_recovery_key_export_action" = "保存";
"secrets_setup_recovery_key_loading" = "正在加载…"; "secrets_setup_recovery_key_loading" = "正在加载…";
@ -1280,7 +1280,7 @@
"pin_protection_confirm_pin" = "验证你的 PIN"; "pin_protection_confirm_pin" = "验证你的 PIN";
"pin_protection_choose_pin" = "创建 PIN 以确保安全"; "pin_protection_choose_pin" = "创建 PIN 以确保安全";
"major_update_done_action" = "知道了"; "major_update_done_action" = "知道了";
"major_update_information" = "我们很激动地宣布,我们改名了!你的应用程序是最新的,你已经登录了你的户。"; "major_update_information" = "我们很激动地宣布,我们改名了!你的应用程序是最新的,你已经登录了你的户。";
"cross_signing_setup_banner_subtitle" = "更容易验证你的其他设备"; "cross_signing_setup_banner_subtitle" = "更容易验证你的其他设备";
// MARK: - Cross-signing // MARK: - Cross-signing
@ -1288,7 +1288,7 @@
// Banner // Banner
"cross_signing_setup_banner_title" = "设置加密"; "cross_signing_setup_banner_title" = "设置加密";
"secrets_reset_authentication_message" = "请输入你的户密码进行确认"; "secrets_reset_authentication_message" = "请输入你的户密码进行确认";
"secrets_reset_warning_message" = "您将重新启动,没有历史记录,消息,受信任的设备或受信任的用户。"; "secrets_reset_warning_message" = "您将重新启动,没有历史记录,消息,受信任的设备或受信任的用户。";
"secrets_reset_warning_title" = "如果你选择全部重置"; "secrets_reset_warning_title" = "如果你选择全部重置";
"secrets_reset_information" = "仅当没有其他设备可用来验证此设备时,才执行此操作。"; "secrets_reset_information" = "仅当没有其他设备可用来验证此设备时,才执行此操作。";
@ -1296,9 +1296,9 @@
// MARK: - Secrets reset // MARK: - Secrets reset
"secrets_reset_title" = "全部重置"; "secrets_reset_title" = "全部重置";
"secrets_setup_recovery_passphrase_summary_information" = "记住你的安全短语。它可以用来解锁你的加密信息和数据。"; "secrets_setup_recovery_passphrase_summary_information" = "记住你的安全词组。它可以用来解锁你的加密信息和数据。";
"secrets_setup_recovery_passphrase_confirm_information" = "再次输入您的安全口令以确认。"; "secrets_setup_recovery_passphrase_confirm_information" = "再次输入您的安全词组以确认。";
"secrets_setup_recovery_passphrase_information" = "输入只有您知道的安全口令,用于保护您的服务器上的秘密。"; "secrets_setup_recovery_passphrase_information" = "输入只有您知道的安全词组,用于保护您的服务器上的秘密。";
"secrets_setup_recovery_key_storage_alert_message" = "✓ 把它打印出来,储存在一个安全的地方\n✓ 把它保存在 USB 钥匙或备份驱动器上\n✓ 把它复制到你的个人云存储"; "secrets_setup_recovery_key_storage_alert_message" = "✓ 把它打印出来,储存在一个安全的地方\n✓ 把它保存在 USB 钥匙或备份驱动器上\n✓ 把它复制到你的个人云存储";
"secrets_setup_recovery_key_storage_alert_title" = "保持安全"; "secrets_setup_recovery_key_storage_alert_title" = "保持安全";
"secrets_setup_recovery_key_information" = "将安全密钥保存在安全的地方。它可以用来解锁你的加密信息和数据。"; "secrets_setup_recovery_key_information" = "将安全密钥保存在安全的地方。它可以用来解锁你的加密信息和数据。";
@ -1324,8 +1324,8 @@
"secure_key_backup_setup_existing_backup_error_unlock_it" = "解锁"; "secure_key_backup_setup_existing_backup_error_unlock_it" = "解锁";
"secure_key_backup_setup_existing_backup_error_info" = "解锁它以在安全备份中复用它,或删除它以在安全备份中创建一个新的消息备份。"; "secure_key_backup_setup_existing_backup_error_info" = "解锁它以在安全备份中复用它,或删除它以在安全备份中创建一个新的消息备份。";
"secure_key_backup_setup_existing_backup_error_title" = "消息的备份已经存在"; "secure_key_backup_setup_existing_backup_error_title" = "消息的备份已经存在";
"secure_key_backup_setup_intro_use_security_passphrase_info" = "输入仅有您知道的安全口令,生成备份用的密钥。"; "secure_key_backup_setup_intro_use_security_passphrase_info" = "输入仅有您知道的秘密词组,生成备份用的密钥。";
"secure_key_backup_setup_intro_use_security_passphrase_title" = "使用安全口令"; "secure_key_backup_setup_intro_use_security_passphrase_title" = "使用安全词组";
"secure_key_backup_setup_intro_use_security_key_info" = "生成安全密钥存储在安全的地方如密码管理器或保险箱。"; "secure_key_backup_setup_intro_use_security_key_info" = "生成安全密钥存储在安全的地方如密码管理器或保险箱。";
"secure_key_backup_setup_intro_use_security_key_title" = "使用安全密钥"; "secure_key_backup_setup_intro_use_security_key_title" = "使用安全密钥";
"secure_key_backup_setup_intro_info" = "通过在你的服务器上备份加密密钥防止丢失对加密消息和数据的访问。"; "secure_key_backup_setup_intro_info" = "通过在你的服务器上备份加密密钥防止丢失对加密消息和数据的访问。";
@ -1515,7 +1515,7 @@
"done" = "完成"; "done" = "完成";
"open" = "打开"; "open" = "打开";
"service_terms_modal_information_description_integration_manager" = "集成管理器允许您添加来自第三方的功能。"; "service_terms_modal_information_description_integration_manager" = "集成管理器允许您添加来自第三方的功能。";
"service_terms_modal_information_description_identity_server" = "身份服务器通过查找电话号码或电子邮件地址帮助您找到联系人,看看他们是否已经有一个户。"; "service_terms_modal_information_description_identity_server" = "身份服务器通过查找电话号码或电子邮件地址帮助您找到联系人,看看他们是否已经有一个户。";
"service_terms_modal_information_title_integration_manager" = "集成管理器"; "service_terms_modal_information_title_integration_manager" = "集成管理器";
// Alert explaining what an identity server / integration manager is. // Alert explaining what an identity server / integration manager is.
@ -1774,11 +1774,11 @@
// E2E import // E2E import
"e2e_import_room_keys" = "导入房间密钥"; "e2e_import_room_keys" = "导入房间密钥";
"e2e_import" = "导入"; "e2e_import" = "导入";
"e2e_passphrase_enter" = "输入密码"; "e2e_passphrase_enter" = "输入口令词组";
// E2E export // E2E export
"e2e_export_room_keys" = "导出房间密钥"; "e2e_export_room_keys" = "导出房间密钥";
"e2e_export" = "导出"; "e2e_export" = "导出";
"e2e_passphrase_empty" = "密码不能为空"; "e2e_passphrase_empty" = "口令词组不能为空";
// Others // Others
"user_id_title" = "用户 ID"; "user_id_title" = "用户 ID";
"offline" = "离线"; "offline" = "离线";
@ -1823,7 +1823,7 @@
"delete" = "删除"; "delete" = "删除";
"create_room" = "创建房间"; "create_room" = "创建房间";
"login" = "登录"; "login" = "登录";
"create_account" = "创建账"; "create_account" = "创建账";
"membership_invite" = "邀请"; "membership_invite" = "邀请";
"membership_leave" = "退出"; "membership_leave" = "退出";
"membership_ban" = "已被封禁"; "membership_ban" = "已被封禁";
@ -1856,7 +1856,7 @@
"contact_mx_users" = "Matrix 用户"; "contact_mx_users" = "Matrix 用户";
"contact_local_contacts" = "本地联系人"; "contact_local_contacts" = "本地联系人";
// Groups // Groups
"e2e_passphrase_confirm" = "确认密码"; "e2e_passphrase_confirm" = "确认口令词组";
"notification_settings_enable_notifications" = "启用通知"; "notification_settings_enable_notifications" = "启用通知";
// Notification settings screen // Notification settings screen
"notification_settings_disable_all" = "禁用通知"; "notification_settings_disable_all" = "禁用通知";
@ -1883,7 +1883,7 @@
"account_msisdn_validation_title" = "等待验证中"; "account_msisdn_validation_title" = "等待验证中";
"account_msisdn_validation_error" = "无法验证此手机号。"; "account_msisdn_validation_error" = "无法验证此手机号。";
"account_error_picture_change_failed" = "头像修改失败"; "account_error_picture_change_failed" = "头像修改失败";
"e2e_passphrase_not_match" = "密码必须匹配"; "e2e_passphrase_not_match" = "口令词组必须匹配";
"not_supported_yet" = "尚未支持"; "not_supported_yet" = "尚未支持";
"local_contacts_access_discovery_warning_title" = "发现用户"; "local_contacts_access_discovery_warning_title" = "发现用户";
"notice_topic_changed" = "%@ 将话题修改为 \"%@\"。"; "notice_topic_changed" = "%@ 将话题修改为 \"%@\"。";
@ -1937,16 +1937,16 @@
"message_reply_to_sender_sent_a_video" = "发送了一段视频。"; "message_reply_to_sender_sent_a_video" = "发送了一段视频。";
"call_invite_expired" = "通话邀请已过期"; "call_invite_expired" = "通话邀请已过期";
"ssl_fingerprint_hash" = "指纹(%@"; "ssl_fingerprint_hash" = "指纹(%@";
"e2e_import_prompt" = "此操作允许您导入此前从其他 Matrix 客户端上导出的加密密钥。您将能够解密任何该客户端能解密的消息。\n该导出文件受密码保护。您应在此处输入密码以解密该文件。"; "e2e_import_prompt" = "此操作允许您导入此前从其他 Matrix 客户端上导出的加密密钥。您将能够解密任何该客户端能解密的消息。\n该导出文件受口令词组保护。您应在此处输入口令词组以解密该文件。";
"e2e_export_prompt" = "此操作允许您将加密房间中接收到的消息导出为一个本地文件。您将来可以将此文件导入到其他 Matrix 客户端中去解密这些消息。\n导出的文件将允许任何能够读取它的人解密您可以看到的任何加密消息因此您应该小心保证其安全。"; "e2e_export_prompt" = "此操作允许您将加密房间中接收到的消息导出为一个本地文件。您将来可以将此文件导入到其他 Matrix 客户端中去解密这些消息。\n导出的文件将允许任何能够读取它的人解密您可以看到的任何加密消息因此您应该小心保证其安全。";
"e2e_passphrase_create" = "创建密码"; "e2e_passphrase_create" = "创建口令词组";
"error_common_message" = "出现错误。请稍后再试。"; "error_common_message" = "出现错误。请稍后再试。";
// Permissions // Permissions
"camera_access_not_granted_for_call" = "视频通话需要摄像头使用权限,但 %@ 无此权限"; "camera_access_not_granted_for_call" = "视频通话需要摄像头使用权限,但 %@ 无此权限";
"microphone_access_not_granted_for_call" = "通话需要麦克风使用权限,但 %@ 无此权限"; "microphone_access_not_granted_for_call" = "通话需要麦克风使用权限,但 %@ 无此权限";
"local_contacts_access_not_granted" = "本地通讯录用户查找功能需要通讯录权限,但 %@ 无此权限"; "local_contacts_access_not_granted" = "本地通讯录用户查找功能需要通讯录权限,但 %@ 无此权限";
"local_contacts_access_discovery_warning" = "为了发现已经使用 Matrix 的联系人,%@ 可以把你地址簿里的邮箱地址和电话号码发送给你选定的 Matrix 身份认证服务器。如果支持的话,个人数据会在发送前被哈希——请检查你的身份认证服务器的隐私条款获知更多细节。"; "local_contacts_access_discovery_warning" = "为了发现已经使用 Matrix 的联系人,%@ 可以把你地址簿里的邮箱地址和电话号码发送给你选定的 Matrix 身份认证服务器。如果支持的话,个人数据会在发送前被哈希——请检查你的身份认证服务器的隐私条款获知更多细节。";
"notification_settings_global_info" = "通知设置已保存在您的账中并在所有支持的客户端中共享(包括桌面通知)。\n\n规则会按顺序应用第一条匹配的规则定义了消息的输出结果。\n因此按字符规则的通知比按房间规则的通知级别更高而这两者都比按发送者规则的通知级别更高。\n对于同一类型的多条规则匹配列表中的第一条优先级最高。"; "notification_settings_global_info" = "通知设置已保存在您的账中并在所有支持的客户端中共享(包括桌面通知)。\n\n规则会按顺序应用第一条匹配的规则定义了消息的输出结果。\n因此按字符规则的通知比按房间规则的通知级别更高而这两者都比按发送者规则的通知级别更高。\n对于同一类型的多条规则匹配列表中的第一条优先级最高。";
"notification_settings_per_word_notifications" = "按字符通知"; "notification_settings_per_word_notifications" = "按字符通知";
"notification_settings_per_word_info" = "单词不区分大小写,并且可能包含 * 通配符。 所以:\nfoo 匹配由单词分隔符包围的字符串 foo例如标点符号和空格或一行的开头/结尾)。\nfoo* 匹配任何以 foo 开头的单词。\n*foo* 匹配任何包含3个字母 foo 的单词。"; "notification_settings_per_word_info" = "单词不区分大小写,并且可能包含 * 通配符。 所以:\nfoo 匹配由单词分隔符包围的字符串 foo例如标点符号和空格或一行的开头/结尾)。\nfoo* 匹配任何以 foo 开头的单词。\n*foo* 匹配任何包含3个字母 foo 的单词。";
"notification_settings_word_to_match" = "匹配的单词"; "notification_settings_word_to_match" = "匹配的单词";
@ -2055,7 +2055,7 @@
"call_consulting_with_user" = "与 %@ 商量"; "call_consulting_with_user" = "与 %@ 商量";
"call_video_with_user" = "与 %@ 进行视频通话"; "call_video_with_user" = "与 %@ 进行视频通话";
"call_voice_with_user" = "与 %@ 进行语音通话"; "call_voice_with_user" = "与 %@ 进行语音通话";
"e2e_passphrase_too_short" = "密码口令太短 (长度至少为 %d 个字符)"; "e2e_passphrase_too_short" = "口令词组太短(字符长度至少为%d";
"microphone_access_not_granted_for_voice_message" = "语音消息需要访问麦克风,但 %@ 无权使用它"; "microphone_access_not_granted_for_voice_message" = "语音消息需要访问麦克风,但 %@ 无权使用它";
"message_reply_to_sender_sent_a_voice_message" = "发送了一条语音消息。"; "message_reply_to_sender_sent_a_voice_message" = "发送了一条语音消息。";
"attachment_large_with_resolution" = "大 %@ (~%@)"; "attachment_large_with_resolution" = "大 %@ (~%@)";
@ -2121,8 +2121,48 @@
"spaces_add_space_title" = "创建空间"; "spaces_add_space_title" = "创建空间";
"space_invite_not_enough_permission" = "你没权限邀请人来此空间"; "space_invite_not_enough_permission" = "你没权限邀请人来此空间";
"room_invite_not_enough_permission" = "你没权限邀请人来此房间"; "room_invite_not_enough_permission" = "你没权限邀请人来此房间";
"room_invite_to_space_option_detail" = "他们可以探索%@,但不会成为其成员%@。";
// MARK: - Share invite link // MARK: - Share invite link
"share_invite_link_action" = "分享邀请链接"; "share_invite_link_action" = "分享邀请链接";
"authentication_verify_email_waiting_hint" = "没收到电子邮件吗?";
/* The placeholder will show the email address that was entered. */
"authentication_verify_email_waiting_message" = "按照发送到%@的说明操作";
"authentication_verify_email_waiting_title" = "验证你的电子邮件。";
"authentication_verify_email_text_field_placeholder" = "电子邮件";
/* The placeholder will show the homeserver's domain */
"authentication_verify_email_input_message" = "%@需要验证你的账户";
"authentication_verify_email_input_title" = "输入你的电子邮件";
"authentication_cancel_flow_confirmation_message" = "账户还未创建。停止注册流程吗?";
"authentication_server_selection_generic_error" = "无法在这个URL上找到服务器请检查其是否正确。";
"authentication_server_selection_server_url" = "主服务器URL";
"authentication_server_selection_register_message" = "你服务器的地址是什么?它就像你全部数据的家";
"authentication_server_selection_register_title" = "选择你的主服务器";
"authentication_server_selection_login_message" = "你服务器的地址是什么?";
"authentication_server_selection_login_title" = "连接到主服务器";
"authentication_server_info_title_login" = "你的对话发生的地方";
"authentication_login_forgot_password" = "忘记密码";
"authentication_login_username" = "用户名/电子邮件/电话";
"authentication_login_title" = "欢迎回来!";
"authentication_server_info_title" = "你的对话发生的地方";
"authentication_registration_password_footer" = "必须是8个或以上的字符";
/* The placeholder will show the full Matrix ID that has been entered. */
"authentication_registration_username_footer_available" = "别人可通过%@发现你";
"authentication_registration_username_footer" = "你可在以后更改";
"authentication_registration_username" = "用户名";
// MARK: Authentication
"authentication_registration_title" = "创建账户";
"onboarding_celebration_button" = "开始吧";
"onboarding_celebration_message" = "随时前往设置更新你的用户资料";
"onboarding_celebration_title" = "看起来不错!";
"onboarding_avatar_accessibility_label" = "用户资料图片";
"onboarding_avatar_message" = "是时候给名称放一张脸了";
"onboarding_avatar_title" = "添加用户资料图片";
"onboarding_display_name_max_length" = "你的显示名称必须少于256个字符";
"onboarding_display_name_hint" = "你可以后更改";
"onboarding_display_name_placeholder" = "显示名称";
"onboarding_display_name_message" = "这会在你发送消息时显示。";
"onboarding_display_name_title" = "选择显示名称";
"onboarding_personalization_skip" = "跳过此步";
"onboarding_personalization_save" = "保存并继续";

View file

@ -1,36 +0,0 @@
/*
Copyright 2017 Vector Creations Ltd
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 <MatrixSDK/MatrixSDK.h>
#import "MatrixKit.h"
/**
Define a `MXGroup` category at Riot level.
*/
@interface MXGroup (Riot)
/**
Set the group avatar in the dedicated MXKImageView.
The riot style implies to use in order :
1 - the default avatar if there is one
2 - the first letter of the group name.
@param mxkImageView the destinated MXKImageView.
@param mxSession the matrix session
*/
- (void)setGroupAvatarImageIn:(MXKImageView*)mxkImageView matrixSession:(MXSession*)mxSession;
@end

View file

@ -1,50 +0,0 @@
/*
Copyright 2017 Vector Creations Ltd
Copyright 2018 New Vector Ltd
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 "MXGroup+Riot.h"
#import "AvatarGenerator.h"
@implementation MXGroup (Riot)
- (void)setGroupAvatarImageIn:(MXKImageView*)mxkImageView matrixSession:(MXSession*)mxSession
{
// Use the group display name to prepare the default avatar image.
NSString *avatarDisplayName = self.profile.name;
UIImage* avatarImage = [AvatarGenerator generateAvatarForMatrixItem:self.groupId withDisplayName:avatarDisplayName];
if (self.profile.avatarUrl && mxSession)
{
mxkImageView.enableInMemoryCache = YES;
[mxkImageView setImageURI:self.profile.avatarUrl
withType:nil
andImageOrientation:UIImageOrientationUp
toFitViewSize:mxkImageView.frame.size
withMethod:MXThumbnailingMethodCrop
previewImage:avatarImage
mediaManager:mxSession.mediaManager];
}
else
{
mxkImageView.image = avatarImage;
}
mxkImageView.contentMode = UIViewContentModeScaleAspectFill;
}
@end

View file

@ -142,7 +142,7 @@ extension UIViewController {
// Even when .never, needs to be true otherwise animation will be broken on iOS11, 12, 13 // Even when .never, needs to be true otherwise animation will be broken on iOS11, 12, 13
navigationController?.navigationBar.prefersLargeTitles = true navigationController?.navigationBar.prefersLargeTitles = true
@unknown default: @unknown default:
MXLog.failure("[UIViewController] setLargeTitleDisplayMode: Missing handler for \(largeTitleDisplayMode)") MXLog.failure("[UIViewController] setLargeTitleDisplayMode: Missing handler", context: largeTitleDisplayMode)
} }
} }

View file

@ -22,6 +22,9 @@ internal typealias AssetImageTypeAlias = ImageAsset.Image
internal class Asset: NSObject { internal class Asset: NSObject {
@objcMembers @objcMembers
@objc(AssetImages) internal class Images: NSObject { @objc(AssetImages) internal class Images: NSObject {
internal static let allChatsOnboarding1 = ImageAsset(name: "all_chats_onboarding1")
internal static let allChatsOnboarding2 = ImageAsset(name: "all_chats_onboarding2")
internal static let allChatsOnboarding3 = ImageAsset(name: "all_chats_onboarding3")
internal static let analyticsCheckmark = ImageAsset(name: "AnalyticsCheckmark") internal static let analyticsCheckmark = ImageAsset(name: "AnalyticsCheckmark")
internal static let analyticsLogo = ImageAsset(name: "AnalyticsLogo") internal static let analyticsLogo = ImageAsset(name: "AnalyticsLogo")
internal static let socialLoginButtonApple = ImageAsset(name: "social_login_button_apple") internal static let socialLoginButtonApple = ImageAsset(name: "social_login_button_apple")
@ -113,9 +116,11 @@ internal class Asset: NSObject {
internal static let roomActionNotificationMuted = ImageAsset(name: "room_action_notification_muted") internal static let roomActionNotificationMuted = ImageAsset(name: "room_action_notification_muted")
internal static let roomActionPriorityHigh = ImageAsset(name: "room_action_priority_high") internal static let roomActionPriorityHigh = ImageAsset(name: "room_action_priority_high")
internal static let roomActionPriorityLow = ImageAsset(name: "room_action_priority_low") internal static let roomActionPriorityLow = ImageAsset(name: "room_action_priority_low")
internal static let allChatsEditIcon = ImageAsset(name: "all_chats_edit_icon")
internal static let allChatsEmptyListPlaceholderIcon = ImageAsset(name: "all_chats_empty_list_placeholder_icon")
internal static let allChatsSpacesIcon = ImageAsset(name: "all_chats_spaces_icon")
internal static let homeEmptyScreenArtwork = ImageAsset(name: "home_empty_screen_artwork") internal static let homeEmptyScreenArtwork = ImageAsset(name: "home_empty_screen_artwork")
internal static let homeEmptyScreenArtworkDark = ImageAsset(name: "home_empty_screen_artwork_dark") internal static let homeEmptyScreenArtworkDark = ImageAsset(name: "home_empty_screen_artwork_dark")
internal static let homeMySpacesAction = ImageAsset(name: "home_my_spaces_action")
internal static let plusFloatingAction = ImageAsset(name: "plus_floating_action") internal static let plusFloatingAction = ImageAsset(name: "plus_floating_action")
internal static let versionCheckCloseIcon = ImageAsset(name: "version_check_close_icon") internal static let versionCheckCloseIcon = ImageAsset(name: "version_check_close_icon")
internal static let versionCheckInfoIcon = ImageAsset(name: "version_check_info_icon") internal static let versionCheckInfoIcon = ImageAsset(name: "version_check_info_icon")

View file

@ -179,6 +179,66 @@ public class VectorL10n: NSObject {
public static var allChatsEditMenuSpaceSettings: String { public static var allChatsEditMenuSpaceSettings: String {
return VectorL10n.tr("Vector", "all_chats_edit_menu_space_settings") return VectorL10n.tr("Vector", "all_chats_edit_menu_space_settings")
} }
/// Youre all caught up.
public static var allChatsEmptyListPlaceholderTitle: String {
return VectorL10n.tr("Vector", "all_chats_empty_list_placeholder_title")
}
/// Spaces are a new way to group rooms and people. Add an existing room, or create a new one, using the bottom-right button.
public static var allChatsEmptySpaceInformation: String {
return VectorL10n.tr("Vector", "all_chats_empty_space_information")
}
/// This is where you're unread messages will show up, when you have some.
public static var allChatsEmptyUnreadsPlaceholderMessage: String {
return VectorL10n.tr("Vector", "all_chats_empty_unreads_placeholder_message")
}
/// The all-in-one secure chat app for teams, friends and organisations. Create a chat, or join an existing room, to get started.
public static var allChatsEmptyViewInformation: String {
return VectorL10n.tr("Vector", "all_chats_empty_view_information")
}
/// %@\nis looking a little empty.
public static func allChatsEmptyViewTitle(_ p1: String) -> String {
return VectorL10n.tr("Vector", "all_chats_empty_view_title", p1)
}
/// Try adjusting your search.
public static var allChatsNothingFoundPlaceholderMessage: String {
return VectorL10n.tr("Vector", "all_chats_nothing_found_placeholder_message")
}
/// Nothing found.
public static var allChatsNothingFoundPlaceholderTitle: String {
return VectorL10n.tr("Vector", "all_chats_nothing_found_placeholder_title")
}
/// To simplify your Element, tabs are now optional. Manage them using the top-right menu.
public static var allChatsOnboardingPageMessage1: String {
return VectorL10n.tr("Vector", "all_chats_onboarding_page_message1")
}
/// Access your Spaces (bottom-left) faster and easier than ever before.
public static var allChatsOnboardingPageMessage2: String {
return VectorL10n.tr("Vector", "all_chats_onboarding_page_message2")
}
/// Tap your profile to let us know what you think.
public static var allChatsOnboardingPageMessage3: String {
return VectorL10n.tr("Vector", "all_chats_onboarding_page_message3")
}
/// Welcome to a new view!
public static var allChatsOnboardingPageTitle1: String {
return VectorL10n.tr("Vector", "all_chats_onboarding_page_title1")
}
/// Access Spaces
public static var allChatsOnboardingPageTitle2: String {
return VectorL10n.tr("Vector", "all_chats_onboarding_page_title2")
}
/// Give Feedback
public static var allChatsOnboardingPageTitle3: String {
return VectorL10n.tr("Vector", "all_chats_onboarding_page_title3")
}
/// What's new
public static var allChatsOnboardingTitle: String {
return VectorL10n.tr("Vector", "all_chats_onboarding_title")
}
/// Try it out
public static var allChatsOnboardingTryIt: String {
return VectorL10n.tr("Vector", "all_chats_onboarding_try_it")
}
/// Chats /// Chats
public static var allChatsSectionTitle: String { public static var allChatsSectionTitle: String {
return VectorL10n.tr("Vector", "all_chats_section_title") return VectorL10n.tr("Vector", "all_chats_section_title")
@ -2491,6 +2551,10 @@ public class VectorL10n: NSObject {
public static func inviteFriendsShareText(_ p1: String, _ p2: String) -> String { public static func inviteFriendsShareText(_ p1: String, _ p2: String) -> String {
return VectorL10n.tr("Vector", "invite_friends_share_text", p1, p2) return VectorL10n.tr("Vector", "invite_friends_share_text", p1, p2)
} }
/// Invite to %@
public static func inviteTo(_ p1: String) -> String {
return VectorL10n.tr("Vector", "invite_to", p1)
}
/// Invite matrix User /// Invite matrix User
public static var inviteUser: String { public static var inviteUser: String {
return VectorL10n.tr("Vector", "invite_user") return VectorL10n.tr("Vector", "invite_user")
@ -5651,6 +5715,14 @@ public class VectorL10n: NSObject {
public static func roomInviteToSpaceOptionTitle(_ p1: String) -> String { public static func roomInviteToSpaceOptionTitle(_ p1: String) -> String {
return VectorL10n.tr("Vector", "room_invite_to_space_option_title", p1) return VectorL10n.tr("Vector", "room_invite_to_space_option_title", p1)
} }
/// This is where your invites appear.
public static var roomInvitesEmptyViewInformation: String {
return VectorL10n.tr("Vector", "room_invites_empty_view_information")
}
/// Nothing new.
public static var roomInvitesEmptyViewTitle: String {
return VectorL10n.tr("Vector", "room_invites_empty_view_title")
}
/// Join /// Join
public static var roomJoinGroupCall: String { public static var roomJoinGroupCall: String {
return VectorL10n.tr("Vector", "room_join_group_call") return VectorL10n.tr("Vector", "room_join_group_call")
@ -7747,6 +7819,10 @@ public class VectorL10n: NSObject {
public static var spaceBetaAnnounceTitle: String { public static var spaceBetaAnnounceTitle: String {
return VectorL10n.tr("Vector", "space_beta_announce_title") return VectorL10n.tr("Vector", "space_beta_announce_title")
} }
/// Space detail
public static var spaceDetailNavTitle: String {
return VectorL10n.tr("Vector", "space_detail_nav_title")
}
/// Spaces are a new way to group rooms and people.\n\nTheyll be here soon. For now, if you join one on another platform, you will be able to access any rooms you join here. /// Spaces are a new way to group rooms and people.\n\nTheyll be here soon. For now, if you join one on another platform, you will be able to access any rooms you join here.
public static var spaceFeatureUnavailableInformation: String { public static var spaceFeatureUnavailableInformation: String {
return VectorL10n.tr("Vector", "space_feature_unavailable_information") return VectorL10n.tr("Vector", "space_feature_unavailable_information")
@ -7763,6 +7839,10 @@ public class VectorL10n: NSObject {
public static var spaceHomeShowAllRooms: String { public static var spaceHomeShowAllRooms: String {
return VectorL10n.tr("Vector", "space_home_show_all_rooms") return VectorL10n.tr("Vector", "space_home_show_all_rooms")
} }
/// Space invite
public static var spaceInviteNavTitle: String {
return VectorL10n.tr("Vector", "space_invite_nav_title")
}
/// You do not have permission to invite people to this space /// You do not have permission to invite people to this space
public static var spaceInviteNotEnoughPermission: String { public static var spaceInviteNotEnoughPermission: String {
return VectorL10n.tr("Vector", "space_invite_not_enough_permission") return VectorL10n.tr("Vector", "space_invite_not_enough_permission")
@ -7791,6 +7871,18 @@ public class VectorL10n: NSObject {
public static var spacePublicJoinRuleDetail: String { public static var spacePublicJoinRuleDetail: String {
return VectorL10n.tr("Vector", "space_public_join_rule_detail") return VectorL10n.tr("Vector", "space_public_join_rule_detail")
} }
/// Create Space
public static var spaceSelectorCreateSpace: String {
return VectorL10n.tr("Vector", "space_selector_create_space")
}
/// Spaces are a way to group rooms and people. Create a space to get started.
public static var spaceSelectorEmptyViewInformation: String {
return VectorL10n.tr("Vector", "space_selector_empty_view_information")
}
/// No spaces yet.
public static var spaceSelectorEmptyViewTitle: String {
return VectorL10n.tr("Vector", "space_selector_empty_view_title")
}
/// My spaces /// My spaces
public static var spaceSelectorTitle: String { public static var spaceSelectorTitle: String {
return VectorL10n.tr("Vector", "space_selector_title") return VectorL10n.tr("Vector", "space_selector_title")
@ -8047,6 +8139,10 @@ public class VectorL10n: NSObject {
public static var spacesExploreRooms: String { public static var spacesExploreRooms: String {
return VectorL10n.tr("Vector", "spaces_explore_rooms") return VectorL10n.tr("Vector", "spaces_explore_rooms")
} }
/// Explore %@
public static func spacesExploreRoomsFormat(_ p1: String) -> String {
return VectorL10n.tr("Vector", "spaces_explore_rooms_format", p1)
}
/// 1 room /// 1 room
public static var spacesExploreRoomsOneRoom: String { public static var spacesExploreRoomsOneRoom: String {
return VectorL10n.tr("Vector", "spaces_explore_rooms_one_room") return VectorL10n.tr("Vector", "spaces_explore_rooms_one_room")
@ -8347,6 +8443,14 @@ public class VectorL10n: NSObject {
public static var userIdTitle: String { public static var userIdTitle: String {
return VectorL10n.tr("Vector", "user_id_title") return VectorL10n.tr("Vector", "user_id_title")
} }
/// Sessions
public static var userSessionsOverviewTitle: String {
return VectorL10n.tr("Vector", "user_sessions_overview_title")
}
/// Manage sessions
public static var userSessionsSettings: String {
return VectorL10n.tr("Vector", "user_sessions_settings")
}
/// If you didnt sign in to this session, your account may be compromised. /// If you didnt sign in to this session, your account may be compromised.
public static var userVerificationSessionDetailsAdditionalInformationUntrustedCurrentUser: String { public static var userVerificationSessionDetailsAdditionalInformationUntrustedCurrentUser: String {
return VectorL10n.tr("Vector", "user_verification_session_details_additional_information_untrusted_current_user") return VectorL10n.tr("Vector", "user_verification_session_details_additional_information_untrusted_current_user")

View file

@ -33,7 +33,7 @@ class MatrixSDKLogger: LoggerProtocol {
static func warning(_ message: @autoclosure () -> Any, _ file: String = #file, _ function: String = #function, line: Int = #line, context: Any? = nil) { static func warning(_ message: @autoclosure () -> Any, _ file: String = #file, _ function: String = #function, line: Int = #line, context: Any? = nil) {
MXLog.warning(message(), file, function, line: line, context: context) MXLog.warning(message(), file, function, line: line, context: context)
} }
static func error(_ message: @autoclosure () -> Any, _ file: String = #file, _ function: String = #function, line: Int = #line, context: Any? = nil) { static func error(_ message: @autoclosure () -> StaticString, _ file: String = #file, _ function: String = #function, line: Int = #line, context: Any? = nil) {
MXLog.error(message(), file, function, line: line, context: context) MXLog.error(message(), file, function, line: line, context: context)
} }
} }

View file

@ -287,9 +287,6 @@ final class RiotSettings: NSObject {
@UserDefault(key: "homeScreenShowRoomsTab", defaultValue: BuildSettings.homeScreenShowRoomsTab, storage: defaults) @UserDefault(key: "homeScreenShowRoomsTab", defaultValue: BuildSettings.homeScreenShowRoomsTab, storage: defaults)
var homeScreenShowRoomsTab var homeScreenShowRoomsTab
@UserDefault(key: "homeScreenShowCommunitiesTab", defaultValue: BuildSettings.homeScreenShowCommunitiesTab, storage: defaults)
var homeScreenShowCommunitiesTab
// MARK: General Settings // MARK: General Settings
@UserDefault(key: "settingsScreenShowChangePassword", defaultValue: BuildSettings.settingsScreenShowChangePassword, storage: defaults) @UserDefault(key: "settingsScreenShowChangePassword", defaultValue: BuildSettings.settingsScreenShowChangePassword, storage: defaults)
@ -342,9 +339,6 @@ final class RiotSettings: NSObject {
@UserDefault(key: "roomSettingsScreenShowAddressSettings", defaultValue: BuildSettings.roomSettingsScreenShowAddressSettings, storage: defaults) @UserDefault(key: "roomSettingsScreenShowAddressSettings", defaultValue: BuildSettings.roomSettingsScreenShowAddressSettings, storage: defaults)
var roomSettingsScreenShowAddressSettings var roomSettingsScreenShowAddressSettings
@UserDefault(key: "roomSettingsScreenShowFlairSettings", defaultValue: BuildSettings.roomSettingsScreenShowFlairSettings, storage: defaults)
var roomSettingsScreenShowFlairSettings
@UserDefault(key: "roomSettingsScreenShowAdvancedSettings", defaultValue: BuildSettings.roomSettingsScreenShowAdvancedSettings, storage: defaults) @UserDefault(key: "roomSettingsScreenShowAdvancedSettings", defaultValue: BuildSettings.roomSettingsScreenShowAdvancedSettings, storage: defaults)
var roomSettingsScreenShowAdvancedSettings var roomSettingsScreenShowAdvancedSettings
@ -382,6 +376,11 @@ final class RiotSettings: NSObject {
/// Number of spaces previously tracked by the `AnalyticsSpaceTracker` instance. /// Number of spaces previously tracked by the `AnalyticsSpaceTracker` instance.
@UserDefault(key: "lastNumberOfTrackedSpaces", defaultValue: nil, storage: defaults) @UserDefault(key: "lastNumberOfTrackedSpaces", defaultValue: nil, storage: defaults)
var lastNumberOfTrackedSpaces: Int? var lastNumberOfTrackedSpaces: Int?
// MARK: - All Chats Onboarding
@UserDefault(key: "allChatsOnboardingHasBeenDisplayed", defaultValue: false, storage: defaults)
var allChatsOnboardingHasBeenDisplayed
} }
// MARK: - RiotSettings notification constants // MARK: - RiotSettings notification constants

View file

@ -1,82 +0,0 @@
//
// Copyright 2022 New Vector Ltd
//
// 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 UIKit
import DesignTokens
extension UIColor {
/// The colors from DesignKit, resolved for light mode only.
static let elementLight = ElementUIColorsResolved(dynamicColors: element, userInterfaceStyle: .light)
/// The colors from DesignKit, resolved for dark mode only.
static let elementDark = ElementUIColorsResolved(dynamicColors: element, userInterfaceStyle: .dark)
}
/// The dynamic colors from DesignKit, resolved to light or dark mode for use in the UIKit themes.
///
/// As Element doesn't (currently) update the app's `UIUserInterfaceStyle` when selecting
/// a custom theme, the dynamic colors provided by DesignKit need resolving for each theme to
/// prevent them from respecting the interface style and rendering in the wrong style.
@objcMembers public class ElementUIColorsResolved: NSObject {
// MARK: Compound
public let accent: UIColor
public let alert: UIColor
public let primaryContent: UIColor
public let secondaryContent: UIColor
public let tertiaryContent: UIColor
public let quaternaryContent: UIColor
public let quinaryContent: UIColor
public let system: UIColor
public let background: UIColor
public let namesAndAvatars: [UIColor]
// MARK: Legacy
public let quarterlyContent: UIColor
public let navigation: UIColor
public let tile: UIColor
public let separator: UIColor
// MARK: Setup
public init(dynamicColors: ElementUIColors, userInterfaceStyle: UIUserInterfaceStyle) {
let traitCollection = UITraitCollection(userInterfaceStyle: userInterfaceStyle)
self.accent = dynamicColors.accent.resolvedColor(with: traitCollection)
self.alert = dynamicColors.alert.resolvedColor(with: traitCollection)
self.primaryContent = dynamicColors.primaryContent.resolvedColor(with: traitCollection)
self.secondaryContent = dynamicColors.secondaryContent.resolvedColor(with: traitCollection)
self.tertiaryContent = dynamicColors.tertiaryContent.resolvedColor(with: traitCollection)
self.quaternaryContent = dynamicColors.quaternaryContent.resolvedColor(with: traitCollection)
self.quinaryContent = dynamicColors.quinaryContent.resolvedColor(with: traitCollection)
self.system = dynamicColors.system.resolvedColor(with: traitCollection)
self.background = dynamicColors.background.resolvedColor(with: traitCollection)
self.namesAndAvatars = dynamicColors.contentAndAvatars
// Legacy colours
self.quarterlyContent = dynamicColors.quaternaryContent.resolvedColor(with: traitCollection)
self.navigation = dynamicColors.system.resolvedColor(with: traitCollection)
if userInterfaceStyle == .light {
self.tile = UIColor(rgb: 0xF3F8FD)
self.separator = dynamicColors.quinaryContent.resolvedColor(with: traitCollection)
} else {
self.tile = dynamicColors.quinaryContent.resolvedColor(with: traitCollection)
self.separator = dynamicColors.system.resolvedColor(with: traitCollection)
}
super.init()
}
}

View file

@ -185,9 +185,9 @@ class DarkTheme: NSObject, Theme {
button.setTitleColor(self.tintColor, for: .normal) button.setTitleColor(self.tintColor, for: .normal)
} }
// MARK: - Theme v2 /// MARK: - Theme v2
var colors = UIColor.elementDark var colors: ColorsUIKit = DarkColors.uiKit
var fonts = UIFont.element var fonts: FontsUIKit = FontsUIKit(values: ElementFonts())
} }

View file

@ -14,6 +14,7 @@
limitations under the License. limitations under the License.
*/ */
import Foundation
import UIKit import UIKit
import DesignKit import DesignKit
@ -193,8 +194,8 @@ class DefaultTheme: NSObject, Theme {
button.setTitleColor(self.tintColor, for: .normal) button.setTitleColor(self.tintColor, for: .normal)
} }
// MARK: - Theme v2 /// MARK: - Theme v2
var colors = UIColor.elementLight var colors: ColorsUIKit = LightColors.uiKit
var fonts = UIFont.element var fonts: FontsUIKit = FontsUIKit(values: ElementFonts())
} }

View file

@ -59,14 +59,14 @@ class URLPreviewStore {
// Load the persistent stores into the container // Load the persistent stores into the container
container.loadPersistentStores { storeDescription, error in container.loadPersistentStores { storeDescription, error in
if let error = error { if let error = error {
MXLog.error("[URLPreviewStore] Core Data container error: \(error.localizedDescription)") MXLog.error("[URLPreviewStore] Core Data container", context: error)
} }
if let url = storeDescription.url { if let url = storeDescription.url {
do { do {
try FileManager.default.excludeItemFromBackup(at: url) try FileManager.default.excludeItemFromBackup(at: url)
} catch { } catch {
MXLog.error("[URLPreviewStore] Cannot exclude Core Data from backup: \(error.localizedDescription)") MXLog.error("[URLPreviewStore] Cannot exclude Core Data from backup", context: error)
} }
} }
} }
@ -130,7 +130,7 @@ class URLPreviewStore {
do { do {
try context.execute(NSBatchDeleteRequest(fetchRequest: request)) try context.execute(NSBatchDeleteRequest(fetchRequest: request))
} catch { } catch {
MXLog.error("[URLPreviewStore] Error executing batch delete request: \(error.localizedDescription)") MXLog.error("[URLPreviewStore] Error executing batch delete request", context: error)
} }
} }
@ -140,7 +140,7 @@ class URLPreviewStore {
_ = try context.execute(NSBatchDeleteRequest(fetchRequest: URLPreviewDataMO.fetchRequest())) _ = try context.execute(NSBatchDeleteRequest(fetchRequest: URLPreviewDataMO.fetchRequest()))
_ = try context.execute(NSBatchDeleteRequest(fetchRequest: URLPreviewUserDataMO.fetchRequest())) _ = try context.execute(NSBatchDeleteRequest(fetchRequest: URLPreviewUserDataMO.fetchRequest()))
} catch { } catch {
MXLog.error("[URLPreviewStore] Error executing batch delete request: \(error.localizedDescription)") MXLog.error("[URLPreviewStore] Error executing batch delete request", context: error)
} }
} }
@ -171,7 +171,7 @@ class URLPreviewStore {
do { do {
try context.save() try context.save()
} catch { } catch {
MXLog.error("[URLPreviewStore] Error saving changes: \(error.localizedDescription)") MXLog.error("[URLPreviewStore] Error saving changes", context: error)
} }
} }
} }

View file

@ -22,6 +22,7 @@ class UserSessionProperties: NSObject {
// MARK: - Constants // MARK: - Constants
private enum Constants { private enum Constants {
static let useCaseKey = "useCase" static let useCaseKey = "useCase"
static let activeFilterKey = "activeFilter"
} }
// MARK: - Properties // MARK: - Properties
@ -64,6 +65,25 @@ class UserSessionProperties: NSObject {
case skipped case skipped
} }
/// The active filter in the All Chats screen.
var allChatsActiveFilter: AllChatsActiveFilter? {
get {
guard let rawValue = dictionary[Constants.activeFilterKey] as? String else { return nil }
return AllChatsActiveFilter(rawValue: rawValue)
} set {
dictionary[Constants.activeFilterKey] = newValue?.rawValue
}
}
/// Represents the active filter in the All Chats screen.
/// Note: The raw string value is used for storage.
public enum AllChatsActiveFilter: String {
case all
case favourites
case people
case unreads
}
// MARK: - Setup // MARK: - Setup
/// Create new properties for the specified user ID. /// Create new properties for the specified user ID.

View file

@ -229,10 +229,11 @@ extension Analytics {
/// Updates any user properties to help with creating cohorts. /// Updates any user properties to help with creating cohorts.
/// ///
/// Only non-nil properties will be updated when calling this method. /// Only non-nil properties will be updated when calling this method.
func updateUserProperties(ftueUseCase: UserSessionProperties.UseCase? = nil, numFavouriteRooms: Int? = nil, numSpaces: Int? = nil) { func updateUserProperties(ftueUseCase: UserSessionProperties.UseCase? = nil, numFavouriteRooms: Int? = nil, numSpaces: Int? = nil, allChatsActiveFilter: UserSessionProperties.AllChatsActiveFilter? = nil) {
let userProperties = AnalyticsEvent.UserProperties(ftueUseCaseSelection: ftueUseCase?.analyticsName, let userProperties = AnalyticsEvent.UserProperties(ftueUseCaseSelection: ftueUseCase?.analyticsName,
numFavouriteRooms: numFavouriteRooms, numFavouriteRooms: numFavouriteRooms,
numSpaces: numSpaces) numSpaces: numSpaces,
allChatsActiveFilter: allChatsActiveFilter?.analyticsName)
client.updateUserProperties(userProperties) client.updateUserProperties(userProperties)
} }
@ -384,7 +385,7 @@ extension Analytics: MXAnalyticsDelegate {
capture(event: event) capture(event: event)
} }
func trackNonFatalIssue(_ issue: String, details: [String : Any]?) { func trackNonFatalIssue(_ issue: String, details: [String: Any]?) {
monitoringClient.trackNonFatalIssue(issue, details: details) monitoringClient.trackNonFatalIssue(issue, details: details)
} }
} }

View file

@ -58,7 +58,10 @@ import AnalyticsEvents
case spaceMembers case spaceMembers
case spaceExploreRooms case spaceExploreRooms
case dialpad case dialpad
case spaceBottomSheet
case invites
case createSpace
/// The screen name reported to the AnalyticsEvent. /// The screen name reported to the AnalyticsEvent.
var screenName: AnalyticsEvent.MobileScreen.ScreenName { var screenName: AnalyticsEvent.MobileScreen.ScreenName {
switch self { switch self {
@ -142,6 +145,12 @@ import AnalyticsEvents
return .SpaceExploreRooms return .SpaceExploreRooms
case .dialpad: case .dialpad:
return .Dialpad return .Dialpad
case .spaceBottomSheet:
return .SpaceBottomSheet
case .invites:
return .Invites
case .createSpace:
return .CreateSpace
} }
} }
} }

View file

@ -24,7 +24,17 @@ import AnalyticsEvents
case threadListFilterItem case threadListFilterItem
case spacePanelSelectedSpace case spacePanelSelectedSpace
case spacePanelSwitchSpace case spacePanelSwitchSpace
case spacePanelSwitchSubSpace
case allChatsRecentsEnabled
case allChatsRecentsDisabled
case allChatsFiltersEnabled
case allChatsFiltersDisabled
case allChatsFilterAll
case allChatsFilterFavourites
case allChatsFilterUnreads
case allChatsFilterPeople
case spaceCreationValidated
/// The element name reported to the AnalyticsEvent. /// The element name reported to the AnalyticsEvent.
var name: AnalyticsEvent.Interaction.Name { var name: AnalyticsEvent.Interaction.Name {
switch self { switch self {
@ -40,6 +50,26 @@ import AnalyticsEvents
return .SpacePanelSelectedSpace return .SpacePanelSelectedSpace
case .spacePanelSwitchSpace: case .spacePanelSwitchSpace:
return .SpacePanelSwitchSpace return .SpacePanelSwitchSpace
case .spacePanelSwitchSubSpace:
return .SpacePanelSwitchSubSpace
case .allChatsRecentsEnabled:
return .MobileAllChatsRecentsEnabled
case .allChatsRecentsDisabled:
return .MobileAllChatsRecentsDisabled
case .allChatsFiltersEnabled:
return .MobileAllChatsFiltersEnabled
case .allChatsFiltersDisabled:
return .MobileAllChatsFiltersDisabled
case .allChatsFilterAll:
return .MobileAllChatsFilterAll
case .allChatsFilterFavourites:
return .MobileAllChatsFilterFavourites
case .allChatsFilterUnreads:
return .MobileAllChatsFilterUnreads
case .allChatsFilterPeople:
return .MobileAllChatsFilterPeople
case .spaceCreationValidated:
return .MobileSpaceCreationValidated
} }
} }
} }

View file

@ -44,6 +44,7 @@ import AnalyticsEvents
case linkShare case linkShare
case exploreRooms case exploreRooms
case spaceMembers case spaceMembers
case spaceBottomSheet
var trigger: AnalyticsEvent.ViewRoom.Trigger? { var trigger: AnalyticsEvent.ViewRoom.Trigger? {
switch self { switch self {
@ -99,6 +100,8 @@ import AnalyticsEvents
return .MobileExploreRooms return .MobileExploreRooms
case .spaceMembers: case .spaceMembers:
return .MobileSpaceMembers return .MobileSpaceMembers
case .spaceBottomSheet:
return .MobileSpaceBottomSheet
} }
} }
} }

View file

@ -0,0 +1,33 @@
//
// Copyright 2022 New Vector Ltd
//
// 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
import AnalyticsEvents
extension UserSessionProperties.AllChatsActiveFilter {
var analyticsName: AnalyticsEvent.UserProperties.AllChatsActiveFilter {
switch self {
case .all:
return .All
case .unreads:
return .Unreads
case .favourites:
return .Favourites
case .people:
return .People
}
}
}

View file

@ -20,12 +20,13 @@ import AnalyticsEvents
extension AnalyticsEvent.UserProperties { extension AnalyticsEvent.UserProperties {
// Initializer for Element. Strips all Web properties. // Initializer for Element. Strips all Web properties.
public init(ftueUseCaseSelection: FtueUseCaseSelection?, numFavouriteRooms: Int?, numSpaces: Int?) { public init(ftueUseCaseSelection: FtueUseCaseSelection?, numFavouriteRooms: Int?, numSpaces: Int?, allChatsActiveFilter: AllChatsActiveFilter?) {
self.init(WebMetaSpaceFavouritesEnabled: nil, self.init(WebMetaSpaceFavouritesEnabled: nil,
WebMetaSpaceHomeAllRooms: nil, WebMetaSpaceHomeAllRooms: nil,
WebMetaSpaceHomeEnabled: nil, WebMetaSpaceHomeEnabled: nil,
WebMetaSpaceOrphansEnabled: nil, WebMetaSpaceOrphansEnabled: nil,
WebMetaSpacePeopleEnabled: nil, WebMetaSpacePeopleEnabled: nil,
allChatsActiveFilter: allChatsActiveFilter,
ftueUseCaseSelection: ftueUseCaseSelection, ftueUseCaseSelection: ftueUseCaseSelection,
numFavouriteRooms: numFavouriteRooms, numFavouriteRooms: numFavouriteRooms,
numSpaces: numSpaces) numSpaces: numSpaces)

View file

@ -81,7 +81,8 @@ class PostHogAnalyticsClient: AnalyticsClientProtocol {
// Merge the updated user properties with the existing ones // Merge the updated user properties with the existing ones
self.pendingUserProperties = AnalyticsEvent.UserProperties(ftueUseCaseSelection: userProperties.ftueUseCaseSelection ?? pendingUserProperties.ftueUseCaseSelection, self.pendingUserProperties = AnalyticsEvent.UserProperties(ftueUseCaseSelection: userProperties.ftueUseCaseSelection ?? pendingUserProperties.ftueUseCaseSelection,
numFavouriteRooms: userProperties.numFavouriteRooms ?? pendingUserProperties.numFavouriteRooms, numFavouriteRooms: userProperties.numFavouriteRooms ?? pendingUserProperties.numFavouriteRooms,
numSpaces: userProperties.numSpaces ?? pendingUserProperties.numSpaces) numSpaces: userProperties.numSpaces ?? pendingUserProperties.numSpaces,
allChatsActiveFilter: userProperties.allChatsActiveFilter ?? pendingUserProperties.allChatsActiveFilter)
} }
// MARK: - Private // MARK: - Private

View file

@ -32,7 +32,9 @@ struct SentryMonitoringClient {
MXLog.debug("[SentryMonitoringClient] Started") MXLog.debug("[SentryMonitoringClient] Started")
SentrySDK.start { options in SentrySDK.start { options in
options.dsn = Self.sentryDSN options.dsn = Self.sentryDSN
options.tracesSampleRate = 1.0
// Collecting only 10% of all events
options.tracesSampleRate = 0.1
options.beforeSend = { event in options.beforeSend = { event in
MXLog.debug("[SentryMonitoringClient] Issue detected: \(event)") MXLog.debug("[SentryMonitoringClient] Issue detected: \(event)")

View file

@ -620,13 +620,6 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
[MXSDKOptions.sharedInstance.profiler resume]; [MXSDKOptions.sharedInstance.profiler resume];
// Force each session to refresh here their publicised groups by user dictionary.
// When these publicised groups are retrieved for a user, they are cached and reused until the app is backgrounded and enters in the foreground again
for (MXSession *session in mxSessionArray)
{
[session markOutdatedPublicisedGroupsByUserData];
}
_isAppForeground = YES; _isAppForeground = YES;
} }
@ -1319,7 +1312,6 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
NSString *roomIdOrAlias; NSString *roomIdOrAlias;
NSString *eventId; NSString *eventId;
NSString *userId; NSString *userId;
NSString *groupId;
// Check permalink to room or event // Check permalink to room or event
if ([pathParams[0] isEqualToString:@"room"] && pathParams.count >= 2) if ([pathParams[0] isEqualToString:@"room"] && pathParams.count >= 2)
@ -1330,11 +1322,6 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
// Is it a link to an event of a room? // Is it a link to an event of a room?
eventId = (pathParams.count >= 3) ? pathParams[2] : nil; eventId = (pathParams.count >= 3) ? pathParams[2] : nil;
} }
else if ([pathParams[0] isEqualToString:@"group"] && pathParams.count >= 2)
{
// The link is the form of "/group/[groupId]"
groupId = pathParams[1];
}
else if (([pathParams[0] hasPrefix:@"#"] || [pathParams[0] hasPrefix:@"!"]) && pathParams.count >= 1) else if (([pathParams[0] hasPrefix:@"#"] || [pathParams[0] hasPrefix:@"!"]) && pathParams.count >= 1)
{ {
// The link is the form of "/#/[roomIdOrAlias]" or "/#/[roomIdOrAlias]/[eventId]" // The link is the form of "/#/[roomIdOrAlias]" or "/#/[roomIdOrAlias]/[eventId]"
@ -1413,7 +1400,9 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
event = eventFromServer; event = eventFromServer;
dispatch_group_leave(eventDispatchGroup); dispatch_group_leave(eventDispatchGroup);
} failure:^(NSError *error) { } failure:^(NSError *error) {
MXLogError(@"[LegacyAppDelegate] handleUniversalLinkWithParameters: event fetch failed: %@", error); MXLogErrorDetails(@"[LegacyAppDelegate] handleUniversalLinkWithParameters: event fetch failed", @{
@"error": error ?: @"unknown"
});
dispatch_group_leave(eventDispatchGroup); dispatch_group_leave(eventDispatchGroup);
}]; }];
} }
@ -1641,44 +1630,6 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
continueUserActivity = YES; continueUserActivity = YES;
} }
else if (groupId)
{
// @FIXME: In case of multi-account, ask the user which one to use
MXKAccount* account = accountManager.activeAccounts.firstObject;
if (account)
{
MXGroup *group = [account.mxSession groupWithGroupId:groupId];
if (!group)
{
// Create a group instance to display its preview
group = [[MXGroup alloc] initWithGroupId:groupId];
}
// Display the group details
[self showGroup:group withMatrixSession:account.mxSession presentationParamters:presentationParameters];
continueUserActivity = YES;
}
else
{
// There is no account. The app will display the AuthenticationVC.
// Wait for a successful login
MXLogDebug(@"[AppDelegate] Universal link: The user is not logged in. Wait for a successful login");
universalLinkFragmentPending = fragment;
// Register an observer in order to handle new account
universalLinkWaitingObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMXKAccountManagerDidAddAccountNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
// Check that 'fragment' has not been cancelled
if ([self->universalLinkFragmentPending isEqualToString:fragment])
{
MXLogDebug(@"[AppDelegate] Universal link: The user is now logged in. Retry the link");
[self handleUniversalLinkWithParameters:universalLinkParameters];
}
}];
}
}
else else
{ {
// Unknown command: Do nothing except coming back to the main screen // Unknown command: Do nothing except coming back to the main screen
@ -3074,26 +3025,7 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
} }
} }
#pragma mark - Matrix Groups handling #pragma mark - VoIP
- (void)showGroup:(MXGroup*)group withMatrixSession:(MXSession*)mxSession presentationParamters:(ScreenPresentationParameters*)presentationParameters
{
void(^showGroup)(void) = ^{
// Select group to display its details (dispatch this action in order to let TabBarController end its refresh)
[self.masterTabBarController selectGroup:group inMatrixSession:mxSession presentationParameters:presentationParameters];
};
if (presentationParameters.restoreInitialDisplay)
{
[self restoreInitialDisplay:^{
showGroup();
}];
}
else
{
showGroup();
}
}
- (void)promptForStunServerFallback - (void)promptForStunServerFallback
{ {

View file

@ -99,7 +99,7 @@ class SessionVerificationListener {
MXLog.debug("[SessionVerificationListener] sessionStateDidChange: Bootstrap succeeded") MXLog.debug("[SessionVerificationListener] sessionStateDidChange: Bootstrap succeeded")
self.completion?(.authenticationIsComplete) self.completion?(.authenticationIsComplete)
} failure: { error in } failure: { error in
MXLog.error("[SessionVerificationListener] sessionStateDidChange: Bootstrap failed. Error: \(error)") MXLog.error("[SessionVerificationListener] sessionStateDidChange: Bootstrap failed", context: error)
crypto.setOutgoingKeyRequestsEnabled(true, onComplete: nil) crypto.setOutgoingKeyRequestsEnabled(true, onComplete: nil)
self.completion?(.authenticationIsComplete) self.completion?(.authenticationIsComplete)
} }
@ -128,7 +128,7 @@ class SessionVerificationListener {
self.completion?(.authenticationIsComplete) self.completion?(.authenticationIsComplete)
} }
} failure: { [weak self] error in } failure: { [weak self] error in
MXLog.error("[SessionVerificationListener] sessionStateDidChange: Fail to refresh crypto state with error: \(error)") MXLog.error("[SessionVerificationListener] sessionStateDidChange: Fail to refresh crypto state", context: error)
crypto.setOutgoingKeyRequestsEnabled(true, onComplete: nil) crypto.setOutgoingKeyRequestsEnabled(true, onComplete: nil)
self?.completion?(.authenticationIsComplete) self?.completion?(.authenticationIsComplete)
} }

View file

@ -35,6 +35,7 @@ typedef NS_ENUM(NSInteger, RecentsDataSourceMode)
RecentsDataSourceModeFavourites, RecentsDataSourceModeFavourites,
RecentsDataSourceModePeople, RecentsDataSourceModePeople,
RecentsDataSourceModeRooms, RecentsDataSourceModeRooms,
RecentsDataSourceModeRoomInvites,
RecentsDataSourceModeAllChats RecentsDataSourceModeAllChats
}; };

View file

@ -174,6 +174,12 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou
- (RecentsDataSourceSections *)makeDataSourceSections - (RecentsDataSourceSections *)makeDataSourceSections
{ {
NSMutableArray *types = [NSMutableArray array]; NSMutableArray *types = [NSMutableArray array];
if (self.recentsDataSourceMode == RecentsDataSourceModeRoomInvites)
{
[types addObject:@(RecentsDataSourceSectionTypeInvites)];
return [[RecentsDataSourceSections alloc] initWithSectionTypes:types.copy];
}
if (self.crossSigningBannerDisplay != CrossSigningBannerDisplayNone) if (self.crossSigningBannerDisplay != CrossSigningBannerDisplayNone)
{ {
[types addObject:@(RecentsDataSourceSectionTypeCrossSigningBanner)]; [types addObject:@(RecentsDataSourceSectionTypeCrossSigningBanner)];
@ -183,7 +189,7 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou
[types addObject:@(RecentsDataSourceSectionTypeSecureBackupBanner)]; [types addObject:@(RecentsDataSourceSectionTypeSecureBackupBanner)];
} }
if (!BuildSettings.newAppLayoutEnabled && self.invitesCellDataArray.count > 0) if (self.invitesCellDataArray.count > 0)
{ {
[types addObject:@(RecentsDataSourceSectionTypeInvites)]; [types addObject:@(RecentsDataSourceSectionTypeInvites)];
} }
@ -229,11 +235,6 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou
[types addObject:@(RecentsDataSourceSectionTypeAllChats)]; [types addObject:@(RecentsDataSourceSectionTypeAllChats)];
} }
if (self.currentSpace == nil && BuildSettings.newAppLayoutEnabled && self.invitesCellDataArray.count > 0)
{
[types addObject:@(RecentsDataSourceSectionTypeInvites)];
}
if (self.currentSpace != nil && self.suggestedRoomCellDataArray.count > 0) if (self.currentSpace != nil && self.suggestedRoomCellDataArray.count > 0)
{ {
[types addObject:@(RecentsDataSourceSectionTypeSuggestedRooms)]; [types addObject:@(RecentsDataSourceSectionTypeSuggestedRooms)];
@ -625,7 +626,13 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou
} }
else if (sectionType == RecentsDataSourceSectionTypeInvites && !(shrinkedSectionsBitMask & RECENTSDATASOURCE_SECTION_INVITES)) else if (sectionType == RecentsDataSourceSectionTypeInvites && !(shrinkedSectionsBitMask & RECENTSDATASOURCE_SECTION_INVITES))
{ {
count = self.invitesCellDataArray.count; if (self.recentsDataSourceMode == RecentsDataSourceModeAllChats)
{
count = 1;
}
else {
count = self.invitesCellDataArray.count;
}
} }
else if (sectionType == RecentsDataSourceSectionTypeSuggestedRooms && !(shrinkedSectionsBitMask & RECENTSDATASOURCE_SECTION_SUGGESTED)) else if (sectionType == RecentsDataSourceSectionTypeSuggestedRooms && !(shrinkedSectionsBitMask & RECENTSDATASOURCE_SECTION_SUGGESTED))
{ {
@ -637,7 +644,7 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou
} }
else if (sectionType == RecentsDataSourceSectionTypeAllChats && !(shrinkedSectionsBitMask & RECENTSDATASOURCE_SECTION_ALL_CHATS)) else if (sectionType == RecentsDataSourceSectionTypeAllChats && !(shrinkedSectionsBitMask & RECENTSDATASOURCE_SECTION_ALL_CHATS))
{ {
count = self.allChatsRoomCellDataArray.count; count = self.allChatsRoomCellDataArray.count ?: 1;
} }
// Adjust this count according to the potential dragged cell. // Adjust this count according to the potential dragged cell.
@ -660,6 +667,7 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou
if (sectionType == RecentsDataSourceSectionTypeSecureBackupBanner || if (sectionType == RecentsDataSourceSectionTypeSecureBackupBanner ||
sectionType == RecentsDataSourceSectionTypeCrossSigningBanner || sectionType == RecentsDataSourceSectionTypeCrossSigningBanner ||
sectionType == RecentsDataSourceSectionTypeBreadcrumbs || sectionType == RecentsDataSourceSectionTypeBreadcrumbs ||
(sectionType == RecentsDataSourceSectionTypeInvites && self.recentsDataSourceMode == RecentsDataSourceModeAllChats) ||
(sectionType == RecentsDataSourceSectionTypeAllChats && !self.allChatsFilterOptions.optionsCount)) (sectionType == RecentsDataSourceSectionTypeAllChats && !self.allChatsFilterOptions.optionsCount))
{ {
return 0.0; return 0.0;
@ -859,6 +867,7 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou
if (sectionType == RecentsDataSourceSectionTypeSecureBackupBanner || if (sectionType == RecentsDataSourceSectionTypeSecureBackupBanner ||
sectionType == RecentsDataSourceSectionTypeCrossSigningBanner || sectionType == RecentsDataSourceSectionTypeCrossSigningBanner ||
sectionType == RecentsDataSourceSectionTypeBreadcrumbs || sectionType == RecentsDataSourceSectionTypeBreadcrumbs ||
(sectionType == RecentsDataSourceSectionTypeInvites && self.recentsDataSourceMode == RecentsDataSourceModeRoomInvites) ||
(sectionType == RecentsDataSourceSectionTypeAllChats && !self.allChatsFilterOptions.optionsCount)) (sectionType == RecentsDataSourceSectionTypeAllChats && !self.allChatsFilterOptions.optionsCount))
{ {
return nil; return nil;
@ -1052,8 +1061,7 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou
return cell; return cell;
} }
else if ((sectionType == RecentsDataSourceSectionTypeConversation && !self.conversationCellDataArray.count) else if ((sectionType == RecentsDataSourceSectionTypeConversation && !self.conversationCellDataArray.count)
|| (sectionType == RecentsDataSourceSectionTypePeople && !self.peopleCellDataArray.count) || (sectionType == RecentsDataSourceSectionTypePeople && !self.peopleCellDataArray.count))
|| (sectionType == RecentsDataSourceSectionTypeAllChats && !self.allChatsRoomCellDataArray.count))
{ {
MXKTableViewCell *tableViewCell = [tableView dequeueReusableCellWithIdentifier:[MXKTableViewCell defaultReuseIdentifier]]; MXKTableViewCell *tableViewCell = [tableView dequeueReusableCellWithIdentifier:[MXKTableViewCell defaultReuseIdentifier]];
if (!tableViewCell) if (!tableViewCell)
@ -1080,6 +1088,23 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou
return tableViewCell; return tableViewCell;
} }
else if (sectionType == RecentsDataSourceSectionTypeAllChats && !self.allChatsRoomCellDataArray.count) {
RecentEmptySectionTableViewCell *tableViewCell = [tableView dequeueReusableCellWithIdentifier:[RecentEmptySectionTableViewCell defaultReuseIdentifier]];
tableViewCell.iconView.image = self.searchPatternsList ? [UIImage systemImageNamed:@"magnifyingglass"] : AssetImages.allChatsEmptyListPlaceholderIcon.image;
tableViewCell.titleLabel.text = self.searchPatternsList ? VectorL10n.allChatsNothingFoundPlaceholderTitle : VectorL10n.allChatsEmptyListPlaceholderTitle;
tableViewCell.messageLabel.text = self.searchPatternsList ? VectorL10n.allChatsNothingFoundPlaceholderMessage : VectorL10n.allChatsEmptyUnreadsPlaceholderMessage;
return tableViewCell;
}
else if (sectionType == RecentsDataSourceSectionTypeInvites && self.recentsDataSourceMode == RecentsDataSourceModeAllChats)
{
RecentsInvitesTableViewCell *tableViewCell = [tableView dequeueReusableCellWithIdentifier:[RecentsInvitesTableViewCell defaultReuseIdentifier]];
tableViewCell.invitesCount = self.recentsListService.invitedRoomListData.counts.numberOfRooms;
return tableViewCell;
}
return [super tableView:tableView cellForRowAtIndexPath:indexPath]; return [super tableView:tableView cellForRowAtIndexPath:indexPath];
} }
@ -1184,10 +1209,17 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou
return self.droppingCellBackGroundView.frame.size.height; return self.droppingCellBackGroundView.frame.size.height;
} }
if ((sectionType == RecentsDataSourceSectionTypeConversation && !self.conversationCellDataArray.count) if ((sectionType == RecentsDataSourceSectionTypeConversation && !self.conversationCellDataArray.count)
|| (sectionType == RecentsDataSourceSectionTypePeople && !self.peopleCellDataArray.count)) || (sectionType == RecentsDataSourceSectionTypePeople && !self.peopleCellDataArray.count))
{ {
return 50.0; return 50.0;
} }
if (sectionType == RecentsDataSourceSectionTypeAllChats && !self.allChatsRoomCellDataArray.count) {
return 300.0;
}
if (sectionType == RecentsDataSourceSectionTypeInvites && self.recentsDataSourceMode == RecentsDataSourceModeAllChats)
{
return 32.0;
}
// Override this method here to use our own cellDataAtIndexPath // Override this method here to use our own cellDataAtIndexPath
id<MXKRecentCellDataStoring> cellData = [self cellDataAtIndexPath:indexPath]; id<MXKRecentCellDataStoring> cellData = [self cellDataAtIndexPath:indexPath];
@ -1498,7 +1530,7 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou
- (BOOL)isDraggableCellAt:(NSIndexPath*)path - (BOOL)isDraggableCellAt:(NSIndexPath*)path
{ {
if (_recentsDataSourceMode == RecentsDataSourceModePeople || _recentsDataSourceMode == RecentsDataSourceModeRooms) if (_recentsDataSourceMode == RecentsDataSourceModePeople || _recentsDataSourceMode == RecentsDataSourceModeRooms || _recentsDataSourceMode == RecentsDataSourceModeRoomInvites)
{ {
return NO; return NO;
} }

View file

@ -1505,6 +1505,10 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro
} }
} }
- (NSString *)tableView:(UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath {
return [VectorL10n leave];
}
#pragma mark - UIScrollViewDelegate #pragma mark - UIScrollViewDelegate
- (void)scrollViewDidScroll:(UIScrollView *)scrollView - (void)scrollViewDidScroll:(UIScrollView *)scrollView
@ -2042,7 +2046,11 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro
} }
else if (section >= self.recentsTableView.numberOfSections) else if (section >= self.recentsTableView.numberOfSections)
{ {
MXLogFailure(@"[RecentsViewController] Section %ld is invalid in a table view with only %ld sections", section, self.recentsTableView.numberOfSections); NSDictionary *details = @{
@"section": @(section),
@"number_of_sections": @(self.recentsTableView.numberOfSections)
};
MXLogFailureDetails(@"[RecentsViewController] Section in a table view is invalid", details);
} }
} }
} }

View file

@ -35,7 +35,7 @@ public class RecentsListService: NSObject, RecentsListServiceProtocol {
private var invitedRoomListDataFetcher: MXRoomListDataFetcher? { private var invitedRoomListDataFetcher: MXRoomListDataFetcher? {
switch mode { switch mode {
case .home, .allChats: case .home, .allChats, .roomInvites:
return invitedRoomListDataFetcherForHome return invitedRoomListDataFetcherForHome
case .people: case .people:
return invitedRoomListDataFetcherForPeople return invitedRoomListDataFetcherForPeople
@ -87,6 +87,7 @@ public class RecentsListService: NSObject, RecentsListServiceProtocol {
.favourites: [.favorited], .favourites: [.favorited],
.people: [.invited, .directPeople], .people: [.invited, .directPeople],
.rooms: [.invited, .conversationRooms, .suggested], .rooms: [.invited, .conversationRooms, .suggested],
.roomInvites: [.invited],
.allChats: [.breadcrumbs, .favorited, .directHome, .invited, .allChats, .lowPriority, .serverNotice, .suggested] .allChats: [.breadcrumbs, .favorited, .directHome, .invited, .allChats, .lowPriority, .serverNotice, .suggested]
] ]
@ -140,7 +141,7 @@ public class RecentsListService: NSObject, RecentsListServiceProtocol {
if space != nil, let fetcher = suggestedRoomListDataFetcher, fetcherTypes.contains(.suggested) { if space != nil, let fetcher = suggestedRoomListDataFetcher, fetcherTypes.contains(.suggested) {
result.append(fetcher) result.append(fetcher)
} }
if let fetcher = breadcrumbsRoomListDataFetcher, fetcherTypes.contains(.breadcrumbs) { if let fetcher = breadcrumbsRoomListDataFetcher, fetcherTypes.contains(.breadcrumbs), shouldShowBreadcrumbs {
result.append(fetcher) result.append(fetcher)
} }
if let fetcher = allChatsRoomListDataFetcher, fetcherTypes.contains(.allChats) { if let fetcher = allChatsRoomListDataFetcher, fetcherTypes.contains(.allChats) {
@ -493,7 +494,7 @@ public class RecentsListService: NSObject, RecentsListServiceProtocol {
} }
private var shouldShowBreadcrumbs: Bool { private var shouldShowBreadcrumbs: Bool {
return fetcherTypesForMode[mode]?.contains(.breadcrumbs) ?? false return AllChatsLayoutSettingsManager.shared.allChatLayoutSettings.sections.contains(.recents) && (fetcherTypesForMode[mode]?.contains(.breadcrumbs) ?? false)
} }
private var shouldShowAllChats: Bool { private var shouldShowAllChats: Bool {

View file

@ -0,0 +1,59 @@
//
// Copyright 2022 New Vector Ltd
//
// 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 UIKit
import Reusable
/// `RecentEmptySectionTableViewCell` can be used as a placeholder for empty sections.
class RecentEmptySectionTableViewCell: UITableViewCell, NibReusable, Themable {
@IBOutlet private var iconBackgroundView: UIView!
@IBOutlet var iconView: UIImageView!
@IBOutlet var titleLabel: UILabel!
@IBOutlet var messageLabel: UILabel!
@objc static func defaultReuseIdentifier() -> String {
return reuseIdentifier
}
// MARK: - Life cycle
override func awakeFromNib() {
super.awakeFromNib()
self.iconBackgroundView.layer.cornerRadius = self.iconBackgroundView.bounds.height / 2
self.iconBackgroundView.layer.masksToBounds = true
self.selectionStyle = .none
update(theme: ThemeService.shared().theme)
}
// MARK: - Themable
func update(theme: Theme) {
self.backgroundColor = theme.colors.background
self.iconBackgroundView.backgroundColor = theme.colors.quinaryContent
self.iconView.tintColor = theme.colors.secondaryContent
self.titleLabel.textColor = theme.colors.primaryContent
self.titleLabel.font = theme.fonts.title3SB
self.messageLabel.textColor = theme.colors.secondaryContent
self.messageLabel.font = theme.fonts.callout
}
}

View file

@ -0,0 +1,91 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="20037" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="20020"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" rowHeight="351" id="L2L-l5-wPx" customClass="RecentEmptySectionTableViewCell" customModule="Element" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="600" height="351"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="L2L-l5-wPx" id="aXz-IR-jj5">
<rect key="frame" x="0.0" y="0.0" width="600" height="351"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="cin-hv-Vgl">
<rect key="frame" x="243" y="105" width="114.5" height="141.5"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="mFA-mv-riA">
<rect key="frame" x="27" y="0.0" width="60" height="60"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="Vpd-pm-OEd">
<rect key="frame" x="14" y="14" width="32" height="32"/>
<constraints>
<constraint firstAttribute="width" constant="32" id="Cjv-wc-xg9"/>
<constraint firstAttribute="height" constant="32" id="hsU-9f-UWJ"/>
</constraints>
</imageView>
</subviews>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<constraints>
<constraint firstItem="Vpd-pm-OEd" firstAttribute="centerY" secondItem="mFA-mv-riA" secondAttribute="centerY" id="0cx-hb-NGV"/>
<constraint firstAttribute="height" constant="60" id="SzX-GE-CAj"/>
<constraint firstAttribute="width" constant="60" id="ZwU-ED-7Gb"/>
<constraint firstItem="Vpd-pm-OEd" firstAttribute="centerX" secondItem="mFA-mv-riA" secondAttribute="centerX" id="jqU-nS-FYb"/>
</constraints>
</view>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Title" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="tjC-HB-9t4">
<rect key="frame" x="40.5" y="84" width="33" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Message Label" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="75X-eX-Cuo">
<rect key="frame" x="0.0" y="121" width="114.5" height="20.5"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstItem="tjC-HB-9t4" firstAttribute="centerX" secondItem="cin-hv-Vgl" secondAttribute="centerX" id="72x-p1-ptG"/>
<constraint firstItem="75X-eX-Cuo" firstAttribute="leading" secondItem="cin-hv-Vgl" secondAttribute="leading" id="IV0-bo-YHM"/>
<constraint firstItem="tjC-HB-9t4" firstAttribute="top" secondItem="mFA-mv-riA" secondAttribute="bottom" constant="24" id="PB9-od-EjQ"/>
<constraint firstItem="mFA-mv-riA" firstAttribute="centerX" secondItem="cin-hv-Vgl" secondAttribute="centerX" id="PqI-9E-GzZ"/>
<constraint firstItem="mFA-mv-riA" firstAttribute="top" secondItem="cin-hv-Vgl" secondAttribute="top" id="ZdF-Xi-kJa"/>
<constraint firstAttribute="trailing" secondItem="75X-eX-Cuo" secondAttribute="trailing" id="fMO-2h-c6M"/>
<constraint firstAttribute="bottom" secondItem="75X-eX-Cuo" secondAttribute="bottom" id="gzR-Ur-XFO"/>
<constraint firstItem="75X-eX-Cuo" firstAttribute="top" secondItem="tjC-HB-9t4" secondAttribute="bottom" constant="16" id="r8q-pa-ZNO"/>
</constraints>
</view>
</subviews>
<constraints>
<constraint firstItem="cin-hv-Vgl" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="aXz-IR-jj5" secondAttribute="leadingMargin" constant="20" id="0wa-1v-ccd"/>
<constraint firstAttribute="trailingMargin" relation="greaterThanOrEqual" secondItem="cin-hv-Vgl" secondAttribute="trailing" constant="20" id="nyk-2q-G86"/>
<constraint firstItem="cin-hv-Vgl" firstAttribute="centerY" secondItem="aXz-IR-jj5" secondAttribute="centerY" id="sHq-1l-ugU"/>
<constraint firstItem="cin-hv-Vgl" firstAttribute="centerX" secondItem="aXz-IR-jj5" secondAttribute="centerX" id="txB-b0-7R8"/>
</constraints>
</tableViewCellContentView>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<accessibility key="accessibilityConfiguration" identifier="RecentTableViewCell"/>
<connections>
<outlet property="iconBackgroundView" destination="mFA-mv-riA" id="ktJ-1O-36v"/>
<outlet property="iconView" destination="Vpd-pm-OEd" id="99X-35-f9k"/>
<outlet property="messageLabel" destination="75X-eX-Cuo" id="GBM-9M-Quw"/>
<outlet property="titleLabel" destination="tjC-HB-9t4" id="SG3-jz-Z3j"/>
</connections>
<point key="canvasLocation" x="-166.40000000000001" y="66.11694152923539"/>
</tableViewCell>
</objects>
<resources>
<systemColor name="systemBackgroundColor">
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
</resources>
</document>

View file

@ -95,7 +95,7 @@
// Notify unreads and bing // Notify unreads and bing
if (roomCellData.hasUnread) if (roomCellData.hasUnread)
{ {
self.missedNotifAndUnreadIndicator.hidden = BuildSettings.newAppLayoutEnabled; self.missedNotifAndUnreadIndicator.hidden = NO;
if (0 < roomCellData.notificationCount) if (0 < roomCellData.notificationCount)
{ {

View file

@ -0,0 +1,71 @@
//
// Copyright 2022 New Vector Ltd
//
// 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 UIKit
import Reusable
/// `RecentsInvitesTableViewCell` can be used as a placeholder to show invites number
class RecentsInvitesTableViewCell: UITableViewCell, NibReusable, Themable {
// MARK: - Outlet
@IBOutlet weak private var badgeLabel: BadgeLabel!
@IBOutlet weak private var titleLabel: UILabel!
// MARK: - Properties
@objc var invitesCount: Int = 0 {
didSet {
badgeLabel.text = "\(invitesCount)"
}
}
// MARK: - NibReusable
@objc static func defaultReuseIdentifier() -> String {
return reuseIdentifier
}
// MARK: - Life cycle
override func awakeFromNib() {
super.awakeFromNib()
setupView()
update(theme: ThemeService.shared().theme)
}
// MARK: - Themable
func update(theme: Theme) {
self.backgroundColor = theme.colors.background
badgeLabel.badgeColor = theme.colors.alert
badgeLabel.textColor = theme.colors.background
badgeLabel.font = theme.fonts.footnoteSB
titleLabel.textColor = theme.colors.accent
}
// MARK: - Private
private func setupView() {
self.selectionStyle = .none
titleLabel.text = VectorL10n.roomRecentsInvitesSection.capitalized
update(theme: ThemeService.shared().theme)
}
}

View file

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="20037" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="20020"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" rowHeight="44" id="L2L-l5-wPx" customClass="RecentsInvitesTableViewCell" customModule="Element" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="403" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="L2L-l5-wPx" id="aXz-IR-jj5">
<rect key="frame" x="0.0" y="0.0" width="403" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="14" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="v7o-A4-W0T" customClass="BadgeLabel" customModule="Element" customModuleProvider="target">
<rect key="frame" x="369" y="12" width="18" height="20.5"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="size" keyPath="padding">
<size key="value" width="13" height="2"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Invites" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="eXI-UE-lYe">
<rect key="frame" x="311" y="12" width="50" height="20.5"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint firstItem="v7o-A4-W0T" firstAttribute="centerY" secondItem="aXz-IR-jj5" secondAttribute="centerY" id="8up-Mj-fMw"/>
<constraint firstItem="v7o-A4-W0T" firstAttribute="leading" secondItem="eXI-UE-lYe" secondAttribute="trailing" constant="8" id="UoH-jb-QUq"/>
<constraint firstAttribute="trailing" secondItem="v7o-A4-W0T" secondAttribute="trailing" constant="16" id="cbs-0p-bQh"/>
<constraint firstItem="eXI-UE-lYe" firstAttribute="centerY" secondItem="aXz-IR-jj5" secondAttribute="centerY" id="pdw-2c-vG6"/>
</constraints>
</tableViewCellContentView>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<accessibility key="accessibilityConfiguration" identifier="RecentTableViewCell"/>
<connections>
<outlet property="badgeLabel" destination="v7o-A4-W0T" id="O6d-wK-nKA"/>
<outlet property="titleLabel" destination="eXI-UE-lYe" id="w0P-0Z-dd8"/>
</connections>
<point key="canvasLocation" x="-324" y="-71.964017991004496"/>
</tableViewCell>
</objects>
<designables>
<designable name="v7o-A4-W0T">
<size key="intrinsicContentSize" width="18" height="20.5"/>
</designable>
</designables>
</document>

View file

@ -17,6 +17,14 @@
import Foundation import Foundation
import Reusable import Reusable
/// `RootTabEmptyViewDisplayMode` defines the way image and text should be displayed
enum RootTabEmptyViewDisplayMode {
/// Default display: fitted for big images
case `default`
/// The image is shrinked to fit icon size and is rendered as templated.
case icon
}
/// `RootTabEmptyView` is a view to display when there is no UI item to display on a screen. /// `RootTabEmptyView` is a view to display when there is no UI item to display on a screen.
@objcMembers @objcMembers
final class RootTabEmptyView: UIView, NibLoadable { final class RootTabEmptyView: UIView, NibLoadable {
@ -25,11 +33,13 @@ final class RootTabEmptyView: UIView, NibLoadable {
// MARK: Outlets // MARK: Outlets
@IBOutlet private weak var iconBackgroundView: UIView!
@IBOutlet private weak var iconView: UIImageView!
@IBOutlet private weak var imageView: UIImageView! @IBOutlet private weak var imageView: UIImageView!
@IBOutlet private weak var titleLabel: UILabel! @IBOutlet private weak var titleLabel: UILabel!
@IBOutlet private weak var informationLabel: UILabel! @IBOutlet private weak var informationLabel: UILabel!
@IBOutlet private(set) weak var contentView: UIView! @IBOutlet private(set) weak var contentView: UIView!
// MARK: Private // MARK: Private
private var theme: Theme! private var theme: Theme!
@ -50,14 +60,25 @@ final class RootTabEmptyView: UIView, NibLoadable {
super.awakeFromNib() super.awakeFromNib()
self.informationLabel.text = VectorL10n.homeEmptyViewInformation self.informationLabel.text = VectorL10n.homeEmptyViewInformation
self.iconBackgroundView.layer.masksToBounds = true
self.iconBackgroundView.layer.cornerRadius = self.iconBackgroundView.bounds.width / 2
self.iconBackgroundView.isHidden = true
} }
// MARK: - Public // MARK: - Public
func fill(with image: UIImage, title: String, informationText: String) { func fill(with image: UIImage, title: String, informationText: String) {
fill(with: image, title: title, informationText: informationText, displayMode: .default)
}
func fill(with image: UIImage, title: String, informationText: String, displayMode: RootTabEmptyViewDisplayMode) {
self.imageView.image = image self.imageView.image = image
self.iconView.image = image.withRenderingMode(.alwaysTemplate)
self.titleLabel.text = title self.titleLabel.text = title
self.informationLabel.text = informationText self.informationLabel.text = informationText
self.imageView.isHidden = displayMode != .default
self.iconBackgroundView.isHidden = displayMode != .icon
} }
} }
@ -71,5 +92,7 @@ extension RootTabEmptyView: Themable {
self.titleLabel.textColor = theme.textPrimaryColor self.titleLabel.textColor = theme.textPrimaryColor
self.informationLabel.textColor = theme.textSecondaryColor self.informationLabel.textColor = theme.textSecondaryColor
self.iconBackgroundView.backgroundColor = theme.colors.quinaryContent
self.iconView.tintColor = theme.textSecondaryColor
} }
} }

View file

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="17156" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES"> <document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="20037" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina3_5" orientation="portrait" appearance="light"/> <device id="retina3_5" orientation="portrait" appearance="light"/>
<dependencies> <dependencies>
<deployment identifier="iOS"/> <deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17126"/> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="20020"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/> <capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/> <capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
@ -11,13 +11,30 @@
<objects> <objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/> <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/> <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="J88-vf-gR0" customClass="RootTabEmptyView" customModule="Riot" customModuleProvider="target"> <view contentMode="scaleToFill" id="J88-vf-gR0" customClass="RootTabEmptyView" customModule="Element" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="320" height="480"/> <rect key="frame" x="0.0" y="0.0" width="320" height="480"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="W21-Nb-gxV"> <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="W21-Nb-gxV">
<rect key="frame" x="0.0" y="-11" width="320" height="471"/> <rect key="frame" x="0.0" y="-11" width="320" height="471"/>
<subviews> <subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="myl-sa-os7">
<rect key="frame" x="130" y="126" width="60" height="60"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="ndc-H7-EUe">
<rect key="frame" x="12" y="12" width="36" height="36"/>
</imageView>
</subviews>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<constraints>
<constraint firstAttribute="bottom" secondItem="ndc-H7-EUe" secondAttribute="bottom" constant="12" id="2id-hV-HE3"/>
<constraint firstItem="ndc-H7-EUe" firstAttribute="top" secondItem="myl-sa-os7" secondAttribute="top" constant="12" id="LnA-73-Dva"/>
<constraint firstAttribute="height" constant="60" id="OVG-5A-zUN"/>
<constraint firstItem="ndc-H7-EUe" firstAttribute="leading" secondItem="myl-sa-os7" secondAttribute="leading" constant="12" id="k9n-OV-f2Z"/>
<constraint firstAttribute="width" constant="60" id="lQF-zc-nbo"/>
<constraint firstAttribute="trailing" secondItem="ndc-H7-EUe" secondAttribute="trailing" constant="12" id="xyS-4P-qXW"/>
</constraints>
</view>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" verticalCompressionResistancePriority="751" image="favourites_empty_screen_artwork" translatesAutoresizingMaskIntoConstraints="NO" id="GiH-8q-wk4"> <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" verticalCompressionResistancePriority="751" image="favourites_empty_screen_artwork" translatesAutoresizingMaskIntoConstraints="NO" id="GiH-8q-wk4">
<rect key="frame" x="20" y="0.0" width="280" height="312"/> <rect key="frame" x="20" y="0.0" width="280" height="312"/>
</imageView> </imageView>
@ -37,9 +54,11 @@
</subviews> </subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints> <constraints>
<constraint firstItem="myl-sa-os7" firstAttribute="centerX" secondItem="GiH-8q-wk4" secondAttribute="centerX" id="1NT-lq-AqZ"/>
<constraint firstItem="GiH-8q-wk4" firstAttribute="centerX" secondItem="W21-Nb-gxV" secondAttribute="centerX" id="4EG-lk-E0u"/> <constraint firstItem="GiH-8q-wk4" firstAttribute="centerX" secondItem="W21-Nb-gxV" secondAttribute="centerX" id="4EG-lk-E0u"/>
<constraint firstAttribute="trailing" secondItem="hPm-Rh-n4I" secondAttribute="trailing" constant="20" id="AM8-hv-gBz"/> <constraint firstAttribute="trailing" secondItem="hPm-Rh-n4I" secondAttribute="trailing" constant="20" id="AM8-hv-gBz"/>
<constraint firstAttribute="height" priority="750" id="C1l-wr-eoZ"/> <constraint firstAttribute="height" priority="750" id="C1l-wr-eoZ"/>
<constraint firstItem="myl-sa-os7" firstAttribute="centerY" secondItem="GiH-8q-wk4" secondAttribute="centerY" id="ITK-6w-HJK"/>
<constraint firstAttribute="width" priority="750" constant="500" id="S2l-qI-mpV"/> <constraint firstAttribute="width" priority="750" constant="500" id="S2l-qI-mpV"/>
<constraint firstItem="GiH-8q-wk4" firstAttribute="top" secondItem="W21-Nb-gxV" secondAttribute="top" id="W3M-PC-qDd"/> <constraint firstItem="GiH-8q-wk4" firstAttribute="top" secondItem="W21-Nb-gxV" secondAttribute="top" id="W3M-PC-qDd"/>
<constraint firstItem="hPm-Rh-n4I" firstAttribute="leading" secondItem="W21-Nb-gxV" secondAttribute="leading" constant="20" id="hGp-9R-Kyf"/> <constraint firstItem="hPm-Rh-n4I" firstAttribute="leading" secondItem="W21-Nb-gxV" secondAttribute="leading" constant="20" id="hGp-9R-Kyf"/>
@ -66,11 +85,13 @@
<nil key="simulatedBottomBarMetrics"/> <nil key="simulatedBottomBarMetrics"/>
<connections> <connections>
<outlet property="contentView" destination="W21-Nb-gxV" id="DUU-Wg-1Dw"/> <outlet property="contentView" destination="W21-Nb-gxV" id="DUU-Wg-1Dw"/>
<outlet property="iconBackgroundView" destination="myl-sa-os7" id="gca-b7-ywm"/>
<outlet property="iconView" destination="ndc-H7-EUe" id="8kB-pU-R3r"/>
<outlet property="imageView" destination="GiH-8q-wk4" id="Ivi-Cn-yK8"/> <outlet property="imageView" destination="GiH-8q-wk4" id="Ivi-Cn-yK8"/>
<outlet property="informationLabel" destination="qtf-2C-k84" id="Gga-9K-vvU"/> <outlet property="informationLabel" destination="qtf-2C-k84" id="Gga-9K-vvU"/>
<outlet property="titleLabel" destination="hPm-Rh-n4I" id="bbO-3C-WCy"/> <outlet property="titleLabel" destination="hPm-Rh-n4I" id="bbO-3C-WCy"/>
</connections> </connections>
<point key="canvasLocation" x="139" y="92"/> <point key="canvasLocation" x="138.75" y="91.25"/>
</view> </view>
</objects> </objects>
<resources> <resources>

View file

@ -18,6 +18,14 @@ import UIKit
class AllChatsFilterOptionListView: UIView, Themable { class AllChatsFilterOptionListView: UIView, Themable {
// MARK: - Constants
private enum Constants {
static let separatorHeight: Double = 1
}
// MARK: - Option definition
class Option { class Option {
let type: AllChatsLayoutFilterType let type: AllChatsLayoutFilterType
let name: String let name: String
@ -84,11 +92,11 @@ class AllChatsFilterOptionListView: UIView, Themable {
func update(theme: Theme) { func update(theme: Theme) {
backgroundColor = theme.colors.background.withAlphaComponent(0.7) backgroundColor = theme.colors.background.withAlphaComponent(0.7)
tabListView.itemFont = theme.fonts.callout tabListView.itemFont = theme.fonts.calloutSB
tabListView.tintColor = theme.colors.accent tabListView.tintColor = theme.colors.accent
tabListView.unselectedItemColor = theme.colors.secondaryContent tabListView.unselectedItemColor = theme.colors.tertiaryContent
separator.backgroundColor = theme.colors.tertiaryContent separator.backgroundColor = theme.colors.system
} }
// MARK: - Private // MARK: - Private
@ -99,10 +107,11 @@ class AllChatsFilterOptionListView: UIView, Themable {
addSubview(separator) addSubview(separator)
separator.translatesAutoresizingMaskIntoConstraints = false separator.translatesAutoresizingMaskIntoConstraints = false
separator.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true separator.bottomAnchor.constraint(equalTo: self.bottomAnchor,
constant: -(TabListView.Constants.cursorHeight - Constants.separatorHeight) / 2).isActive = true
separator.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true separator.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
separator.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true separator.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true
separator.heightAnchor.constraint(equalToConstant: 0.5).isActive = true separator.heightAnchor.constraint(equalToConstant: Constants.separatorHeight).isActive = true
tabListView.delegate = self tabListView.delegate = self
vc_addSubViewMatchingParent(tabListView) vc_addSubViewMatchingParent(tabListView)

View file

@ -22,6 +22,16 @@ protocol TabListViewDelegate: AnyObject {
class TabListView: UIView { class TabListView: UIView {
// MARK: - Constants
enum Constants {
static let cursorHeight: Double = 3
static let itemSpacing: Double = 30
static let cursorPadding: Double = 6
}
// MARK: - Item definition
class Item { class Item {
let id: Any let id: Any
let text: String? let text: String?
@ -36,6 +46,8 @@ class TabListView: UIView {
} }
} }
// MARK: - Properties
weak var delegate: TabListViewDelegate? weak var delegate: TabListViewDelegate?
var items: [Item] = [] { var items: [Item] = [] {
didSet { didSet {
@ -62,11 +74,6 @@ class TabListView: UIView {
// MARK: - Private // MARK: - Private
private enum Constants {
static let cursorHeight: Double = 2
static let itemSpacing: Double = 30
}
private var itemViews: [UIButton] = [] private var itemViews: [UIButton] = []
private let scrollView = UIScrollView(frame: .zero) private let scrollView = UIScrollView(frame: .zero)
private let cursorView = UIView(frame: .zero) private let cursorView = UIView(frame: .zero)
@ -149,6 +156,7 @@ class TabListView: UIView {
cursorView.backgroundColor = tintColor cursorView.backgroundColor = tintColor
cursorView.isUserInteractionEnabled = false cursorView.isUserInteractionEnabled = false
cursorView.layer.masksToBounds = true
scrollView.addSubview(cursorView) scrollView.addSubview(cursorView)
} }
@ -190,21 +198,23 @@ class TabListView: UIView {
let focusedButton = itemViews[Int(integral)] let focusedButton = itemViews[Int(integral)]
let nextButtonIndex = Int(integral) + 1 let nextButtonIndex = Int(integral) + 1
let x: Double let x: CGFloat
let width: Double let width: CGFloat
let focusedButtonFrame: CGRect = titleLabelFrame(with: focusedButton).insetBy(dx: -Constants.cursorPadding, dy: 0)
if nextButtonIndex < itemViews.count { if nextButtonIndex < itemViews.count {
let nextButton = itemViews[nextButtonIndex] let nextButtonFrame = titleLabelFrame(with: itemViews[nextButtonIndex]).insetBy(dx: -Constants.cursorPadding, dy: 0)
x = focusedButton.frame.minX + (nextButton.frame.minX - focusedButton.frame.minX) * fractional x = focusedButtonFrame.minX + (nextButtonFrame.minX - focusedButtonFrame.minX) * fractional
width = focusedButton.frame.width + (nextButton.frame.width - focusedButton.frame.width) * fractional width = focusedButtonFrame.width + (nextButtonFrame.width - focusedButtonFrame.width) * fractional
} else { } else {
x = focusedButton.frame.minX x = focusedButtonFrame.minX
width = focusedButton.frame.width width = focusedButtonFrame.width
} }
cursorView.frame = CGRect(x: x, cursorView.frame = CGRect(x: x,
y: bounds.height - Constants.cursorHeight, y: bounds.height - Constants.cursorHeight,
width: width, width: width,
height: Constants.cursorHeight) height: Constants.cursorHeight)
cursorView.layer.cornerRadius = cursorView.bounds.height / 2
for button in self.itemViews { for button in self.itemViews {
if button == focusedButton { if button == focusedButton {
@ -214,5 +224,16 @@ class TabListView: UIView {
} }
} }
} }
private func titleLabelFrame(with button: UIButton) -> CGRect {
guard let titleLabel = button.titleLabel else {
return button.frame
}
return CGRect(x: button.frame.minX + titleLabel.frame.minX,
y: button.frame.minY + titleLabel.frame.minY,
width: titleLabel.frame.width,
height: titleLabel.frame.height)
}
} }

View file

@ -1,24 +0,0 @@
/*
Copyright 2017 Vector Creations Ltd
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 "MatrixKit.h"
/**
'GroupsDataSource' class inherits from 'MXKSessionGroupsDataSource' to define the Riot groups source.
*/
@interface GroupsDataSource : MXKSessionGroupsDataSource
@end

View file

@ -1,126 +0,0 @@
/*
Copyright 2017 Vector Creations Ltd
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 "GroupsDataSource.h"
#import "GeneratedInterface-Swift.h"
@interface GroupsDataSource() <BetaAnnounceCellDelegate>
@property (nonatomic) NSInteger betaAnnounceSection;
@property (nonatomic) BOOL showBetaAnnounce;
@end
@implementation GroupsDataSource
- (instancetype)initWithMatrixSession:(MXSession *)matrixSession
{
self = [super initWithMatrixSession:matrixSession];
if (self)
{
// TODO: Hide the banner for the moment. Wait for iterations on it.
// _showBetaAnnounce = !RiotSettings.shared.hideSpaceBetaAnnounce;
_showBetaAnnounce = NO;
}
return self;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
NSInteger count = 0;
self.betaAnnounceSection = self.groupInvitesSection = self.joinedGroupsSection = -1;
// Check whether all data sources are ready before rendering groups.
if (self.state == MXKDataSourceStateReady)
{
if (self.showBetaAnnounce)
{
self.betaAnnounceSection = count++;
}
if (groupsInviteCellDataArray.count)
{
self.groupInvitesSection = count++;
}
if (groupsCellDataArray.count)
{
self.joinedGroupsSection = count++;
}
}
return count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.section == self.betaAnnounceSection)
{
BetaAnnounceCell *cell = [tableView dequeueReusableCellWithIdentifier:BetaAnnounceCell.reuseIdentifier forIndexPath:indexPath];
[cell vc_hideSeparator];
[cell updateWithTheme:ThemeService.shared.theme];
cell.delegate = self;
return cell;
}
return [super tableView:tableView cellForRowAtIndexPath:indexPath];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (section == self.betaAnnounceSection)
{
return 1;
}
return [super tableView:tableView numberOfRowsInSection:section];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
NSString* sectionTitle = nil;
// Check whether there are more than 1 section.
if (self.groupInvitesSection != -1)
{
if (section == self.groupInvitesSection)
{
sectionTitle = [VectorL10n groupInviteSection];
}
else if (section == self.joinedGroupsSection)
{
sectionTitle = [VectorL10n groupSection];
}
}
return sectionTitle;
}
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
// Enable edition only for the joined groups.
return (indexPath.section == self.joinedGroupsSection);
}
#pragma mark - BetaAnnounceCellDelegate
- (void)betaAnnounceCellDidTapCloseButton:(BetaAnnounceCell *)cell
{
self.showBetaAnnounce = NO;
RiotSettings.shared.hideSpaceBetaAnnounce = YES;
[self.delegate dataSource:self didCellChange:nil];
}
@end

View file

@ -1,55 +0,0 @@
/*
Copyright 2017 Vector Creations Ltd
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 "MatrixKit.h"
/**
The `GroupsViewController` screen is the view controller displayed when `Groups` tab is selected.
*/
@interface GroupsViewController : MXKGroupListViewController <MXKGroupListViewControllerDelegate>
{
@protected
/**
The group identifier related to the cell which is in editing mode (if any).
*/
NSString *editedGroupId;
/**
Current alert (if any).
*/
UIAlertController *currentAlert;
/**
The image view of the (+) button.
*/
UIImageView* plusButtonImageView;
}
/**
If YES, the table view will scroll at the top on the next data source refresh.
It comes back to NO after each refresh.
*/
@property (nonatomic) BOOL shouldScrollToTopOnRefresh;
/**
Tell whether the search bar at the top of the groups table is enabled. YES by default.
*/
@property (nonatomic) BOOL enableSearchBar;
+ (instancetype)instantiate;
@end

View file

@ -1,646 +0,0 @@
/*
Copyright 2017 Vector Creations Ltd
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 "GroupsViewController.h"
#import "GroupTableViewCell.h"
#import "GroupInviteTableViewCell.h"
#import "GeneratedInterface-Swift.h"
@interface GroupsViewController () <MasterTabBarItemDisplayProtocol>
{
// Tell whether a groups refresh is pending (suspended during editing mode).
BOOL isRefreshPending;
// Observe UIApplicationDidEnterBackgroundNotification to cancel editing mode when app leaves the foreground state.
__weak id UIApplicationDidEnterBackgroundNotificationObserver;
// Observe kAppDelegateDidTapStatusBarNotification to handle tap on clock status bar.
__weak id kAppDelegateDidTapStatusBarNotificationObserver;
MXHTTPOperation *currentRequest;
// The fake search bar displayed at the top of the recents table. We switch on the actual search bar (self.groupsSearchBar)
// when the user selects it.
UISearchBar *tableSearchBar;
// Observe kThemeServiceDidChangeThemeNotification to handle user interface theme change.
__weak id kThemeServiceDidChangeThemeNotificationObserver;
}
@property (nonatomic) AnalyticsScreenTracker *screenTracker;
@end
@implementation GroupsViewController
+ (instancetype)instantiate
{
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]];
GroupsViewController *viewController = [storyboard instantiateViewControllerWithIdentifier:@"GroupsViewController"];
return viewController;
}
- (void)finalizeInit
{
[super finalizeInit];
// Setup `MXKViewControllerHandling` properties
self.enableBarTintColorStatusChange = NO;
self.rageShakeManager = [RageShakeManager sharedManager];
// Enable the search bar in the recents table, and remove the search option from the navigation bar.
_enableSearchBar = YES;
self.enableBarButtonSearch = NO;
// Create the fake search bar
tableSearchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, 600, 44)];
tableSearchBar.autoresizingMask = UIViewAutoresizingFlexibleWidth;
tableSearchBar.showsCancelButton = NO;
tableSearchBar.placeholder = [VectorL10n searchDefaultPlaceholder];
tableSearchBar.delegate = self;
// Set itself as delegate by default.
self.delegate = self;
self.screenTracker = [[AnalyticsScreenTracker alloc] initWithScreen:AnalyticsScreenMyGroups];
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.view.accessibilityIdentifier = @"GroupsVCView";
self.groupsTableView.accessibilityIdentifier = @"GroupsVCTableView";
//Register here the customized cell view class used to render groups
[self.groupsTableView registerNib:GroupTableViewCell.nib forCellReuseIdentifier:GroupTableViewCell.defaultReuseIdentifier];
[self.groupsTableView registerNib:GroupInviteTableViewCell.nib forCellReuseIdentifier:GroupInviteTableViewCell.defaultReuseIdentifier];
[self.groupsTableView registerNib:BetaAnnounceCell.nib forCellReuseIdentifier:BetaAnnounceCell.reuseIdentifier];
// Hide line separators of empty cells
self.groupsTableView.tableFooterView = [[UIView alloc] init];
// Enable self-sizing cells and section headers.
self.groupsTableView.rowHeight = UITableViewAutomaticDimension;
self.groupsTableView.estimatedRowHeight = 74;
self.groupsTableView.sectionHeaderHeight = UITableViewAutomaticDimension;
self.groupsTableView.estimatedSectionHeaderHeight = 30;
self.groupsTableView.estimatedSectionFooterHeight = 0;
MXWeakify(self);
// Observe UIApplicationDidEnterBackgroundNotification to refresh bubbles when app leaves the foreground state.
UIApplicationDidEnterBackgroundNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidEnterBackgroundNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
MXStrongifyAndReturnIfNil(self);
// Leave potential editing mode
[self cancelEditionMode:self->isRefreshPending];
}];
self.groupsSearchBar.autocapitalizationType = UITextAutocapitalizationTypeNone;
self.groupsSearchBar.placeholder = [VectorL10n searchDefaultPlaceholder];
// @TODO: Add programmatically the (+) button.
// plusButtonImageView = [self vc_addFABWithImage:[UIImage imageNamed:@"plus_floating_action"]
// target:self
// action:@selector(onPlusButtonPressed)];
// Observe user interface theme change.
kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
MXStrongifyAndReturnIfNil(self);
[self userInterfaceThemeDidChange];
}];
[self userInterfaceThemeDidChange];
}
- (void)userInterfaceThemeDidChange
{
[ThemeService.shared.theme applyStyleOnNavigationBar:self.navigationController.navigationBar];
self.activityIndicator.backgroundColor = ThemeService.shared.theme.overlayBackgroundColor;
// Use the primary bg color for the recents table view in plain style.
self.groupsTableView.backgroundColor = ThemeService.shared.theme.backgroundColor;
topview.backgroundColor = ThemeService.shared.theme.headerBackgroundColor;
self.view.backgroundColor = ThemeService.shared.theme.backgroundColor;
[ThemeService.shared.theme applyStyleOnSearchBar:tableSearchBar];
[ThemeService.shared.theme applyStyleOnSearchBar:self.groupsSearchBar];
if (self.groupsTableView.dataSource)
{
// Force table refresh
[self cancelEditionMode:YES];
}
[self setNeedsStatusBarAppearanceUpdate];
}
- (UIStatusBarStyle)preferredStatusBarStyle
{
return ThemeService.shared.theme.statusBarStyle;
}
- (void)destroy
{
[super destroy];
if (currentRequest)
{
[currentRequest cancel];
currentRequest = nil;
}
if (currentAlert)
{
[currentAlert dismissViewControllerAnimated:NO completion:nil];
currentAlert = nil;
}
if (UIApplicationDidEnterBackgroundNotificationObserver)
{
[[NSNotificationCenter defaultCenter] removeObserver:UIApplicationDidEnterBackgroundNotificationObserver];
UIApplicationDidEnterBackgroundNotificationObserver = nil;
}
if (kThemeServiceDidChangeThemeNotificationObserver)
{
[[NSNotificationCenter defaultCenter] removeObserver:kThemeServiceDidChangeThemeNotificationObserver];
kThemeServiceDidChangeThemeNotificationObserver = nil;
}
}
- (void)setEditing:(BOOL)editing animated:(BOOL)animated
{
[super setEditing:editing animated:animated];
self.groupsTableView.editing = editing;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self.screenTracker trackScreen];
// Deselect the current selected row, it will be restored on viewDidAppear (if any)
NSIndexPath *indexPath = [self.groupsTableView indexPathForSelectedRow];
if (indexPath)
{
[self.groupsTableView deselectRowAtIndexPath:indexPath animated:NO];
}
MXWeakify(self);
// Observe kAppDelegateDidTapStatusBarNotificationObserver.
kAppDelegateDidTapStatusBarNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kAppDelegateDidTapStatusBarNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
MXStrongifyAndReturnIfNil(self);
[self scrollToTop:YES];
}];
[AppDelegate theDelegate].masterTabBarController.tabBar.tintColor = ThemeService.shared.theme.tintColor;
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
// Leave potential editing mode
[self cancelEditionMode:NO];
if (kAppDelegateDidTapStatusBarNotificationObserver)
{
[[NSNotificationCenter defaultCenter] removeObserver:kAppDelegateDidTapStatusBarNotificationObserver];
kAppDelegateDidTapStatusBarNotificationObserver = nil;
}
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
// Release the current selected item (if any) except if the second view controller is still visible.
if (self.splitViewController.isCollapsed)
{
// Release the current selected group (if any).
[[AppDelegate theDelegate].masterTabBarController releaseSelectedItem];
}
else
{
// In case of split view controller where the primary and secondary view controllers are displayed side-by-side onscreen,
// the selected group (if any) is highlighted.
[self refreshCurrentSelectedCell:YES];
}
}
#pragma mark - Override MXKGroupListViewController
- (void)refreshGroupsTable
{
// Refresh the tabBar icon badges
[[AppDelegate theDelegate].masterTabBarController refreshTabBarBadges];
isRefreshPending = NO;
if (editedGroupId)
{
// Check whether the user didn't leave the room
if ([self.dataSource cellIndexPathWithGroupId:editedGroupId])
{
isRefreshPending = YES;
return;
}
else
{
// Cancel the editing mode, a new refresh will be triggered.
[self cancelEditionMode:YES];
return;
}
}
[self.groupsTableView reloadData];
// Check conditions to display the fake search bar into the table header
if (_enableSearchBar && self.groupsSearchBar.isHidden && self.groupsTableView.tableHeaderView == nil)
{
// Add the search bar by hiding it by default.
self.groupsTableView.tableHeaderView = tableSearchBar;
self.groupsTableView.contentOffset = CGPointMake(0, self.groupsTableView.contentOffset.y + tableSearchBar.frame.size.height);
}
if (_shouldScrollToTopOnRefresh)
{
[self scrollToTop:NO];
_shouldScrollToTopOnRefresh = NO;
}
// In case of split view controller where the primary and secondary view controllers are displayed side-by-side on screen,
// the selected group (if any) is updated.
if (!self.splitViewController.isCollapsed)
{
[self refreshCurrentSelectedCell:NO];
}
}
- (void)hideSearchBar:(BOOL)hidden
{
[super hideSearchBar:hidden];
if (!hidden)
{
// Remove the fake table header view if any
self.groupsTableView.tableHeaderView = nil;
self.groupsTableView.contentInset = UIEdgeInsetsZero;
}
}
#pragma mark -
- (void)refreshCurrentSelectedCell:(BOOL)forceVisible
{
// Update here the index of the current selected cell (if any) - Useful in landscape mode with split view controller.
NSIndexPath *currentSelectedCellIndexPath = nil;
MasterTabBarController *masterTabBarController = [AppDelegate theDelegate].masterTabBarController;
if (masterTabBarController.selectedGroup)
{
// Look for the rank of this selected group in displayed groups
currentSelectedCellIndexPath = [self.dataSource cellIndexPathWithGroupId:masterTabBarController.selectedGroup.groupId];
}
if (currentSelectedCellIndexPath)
{
// Select the right row
[self.groupsTableView selectRowAtIndexPath:currentSelectedCellIndexPath animated:YES scrollPosition:UITableViewScrollPositionNone];
if (forceVisible)
{
// Scroll table view to make the selected row appear at second position
NSInteger topCellIndexPathRow = currentSelectedCellIndexPath.row ? currentSelectedCellIndexPath.row - 1: currentSelectedCellIndexPath.row;
NSIndexPath* indexPath = [NSIndexPath indexPathForRow:topCellIndexPathRow inSection:currentSelectedCellIndexPath.section];
[self.groupsTableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:NO];
}
}
else
{
NSIndexPath *indexPath = [self.groupsTableView indexPathForSelectedRow];
if (indexPath)
{
[self.groupsTableView deselectRowAtIndexPath:indexPath animated:NO];
}
}
}
- (void)cancelEditionMode:(BOOL)forceRefresh
{
if (self.groupsTableView.isEditing || self.isEditing)
{
// Leave editing mode first
isRefreshPending = forceRefresh;
[self setEditing:NO];
}
else if (forceRefresh)
{
// Clean
editedGroupId = nil;
[self refreshGroupsTable];
}
}
#pragma mark - MXKDataSourceDelegate
- (Class<MXKCellRendering>)cellViewClassForCellData:(MXKCellData*)cellData
{
id<MXKGroupCellDataStoring> cellDataStoring = (id<MXKGroupCellDataStoring> )cellData;
if (cellDataStoring.group.membership != MXMembershipInvite)
{
return GroupTableViewCell.class;
}
else
{
return GroupInviteTableViewCell.class;
}
}
- (NSString *)cellReuseIdentifierForCellData:(MXKCellData*)cellData
{
Class class = [self cellViewClassForCellData:cellData];
if ([class respondsToSelector:@selector(defaultReuseIdentifier)])
{
return [class defaultReuseIdentifier];
}
return nil;
}
- (void)dataSource:(MXKDataSource *)dataSource didRecognizeAction:(NSString *)actionIdentifier inCell:(id<MXKCellRendering>)cell userInfo:(NSDictionary *)userInfo
{
// Handle here user actions on groups for Riot app
if ([actionIdentifier isEqualToString:kGroupInviteTableViewCellPreviewButtonPressed])
{
// Retrieve the invited group
MXGroup *invitedGroup = userInfo[kGroupInviteTableViewCellRoomKey];
// Display the room preview
[[AppDelegate theDelegate].masterTabBarController selectGroup:invitedGroup inMatrixSession:self.mainSession];
}
else if ([actionIdentifier isEqualToString:kGroupInviteTableViewCellDeclineButtonPressed])
{
// Retrieve the invited group
MXGroup *invitedGroup = userInfo[kGroupInviteTableViewCellRoomKey];
NSIndexPath *indexPath = [self.dataSource cellIndexPathWithGroupId:invitedGroup.groupId];
if (indexPath)
{
[self.dataSource leaveGroupAtIndexPath:indexPath];
}
}
else
{
// Keep default implementation for other actions if any
if ([super respondsToSelector:@selector(cell:didRecognizeAction:userInfo:)])
{
[super dataSource:dataSource didRecognizeAction:actionIdentifier inCell:cell userInfo:userInfo];
}
}
}
#pragma mark - UITableView delegate
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath;
{
[super tableView:tableView willDisplayCell:cell forRowAtIndexPath:indexPath];
cell.backgroundColor = ThemeService.shared.theme.backgroundColor;
// Update the selected background view
if (ThemeService.shared.theme.selectedBackgroundColor)
{
cell.selectedBackgroundView = [[UIView alloc] init];
cell.selectedBackgroundView.backgroundColor = ThemeService.shared.theme.selectedBackgroundColor;
}
else
{
if (tableView.style == UITableViewStylePlain)
{
cell.selectedBackgroundView = nil;
}
else
{
cell.selectedBackgroundView.backgroundColor = nil;
}
}
}
- (nullable UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
MXKTableViewHeaderFooterWithLabel *sectionHeader;
if (tableView.numberOfSections > 1)
{
sectionHeader = [tableView dequeueReusableHeaderFooterViewWithIdentifier:MXKTableViewHeaderFooterWithLabel.defaultReuseIdentifier];
sectionHeader.mxkContentView.backgroundColor = ThemeService.shared.theme.headerBackgroundColor;
sectionHeader.mxkLabel.textColor = ThemeService.shared.theme.textPrimaryColor;
sectionHeader.mxkLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline];
NSString* title = [self.dataSource tableView:tableView titleForHeaderInSection:section];
NSUInteger count = [self.dataSource tableView:tableView numberOfRowsInSection:section];
if (count)
{
NSString *roomCount = [NSString stringWithFormat:@" %tu", count];
NSMutableAttributedString *mutableSectionTitle = [[NSMutableAttributedString alloc] initWithString:title
attributes:@{NSForegroundColorAttributeName: ThemeService.shared.theme.headerTextPrimaryColor}];
[mutableSectionTitle appendAttributedString:[[NSMutableAttributedString alloc] initWithString:roomCount
attributes:@{NSForegroundColorAttributeName: ThemeService.shared.theme.headerTextSecondaryColor}]];
sectionHeader.mxkLabel.attributedText = mutableSectionTitle;
}
else
{
sectionHeader.mxkLabel.text = title;
}
}
return sectionHeader;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell* cell = [self.groupsTableView cellForRowAtIndexPath:indexPath];
if ([cell isKindOfClass:[GroupInviteTableViewCell class]])
{
// hide the selection
[tableView deselectRowAtIndexPath:indexPath animated:NO];
}
else
{
[super tableView:tableView didSelectRowAtIndexPath:indexPath];
}
}
- (NSArray *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSMutableArray* actions;
// add the swipe to delete only on joined group
if (indexPath.section == self.dataSource.joinedGroupsSection)
{
// Store the identifier of the room related to the edited cell.
id<MXKGroupCellDataStoring> cellData = [self.dataSource cellDataAtIndex:indexPath];
editedGroupId = cellData.group.groupId;
actions = [[NSMutableArray alloc] init];
// Patch: Force the width of the button by adding whitespace characters into the title string.
UITableViewRowAction *leaveAction = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDestructive title:@" " handler:^(UITableViewRowAction *action, NSIndexPath *indexPath){
[self.dataSource leaveGroupAtIndexPath:indexPath];
}];
leaveAction.backgroundColor = [MXKTools convertImageToPatternColor:@"remove_icon_blue" backgroundColor:ThemeService.shared.theme.headerBackgroundColor patternSize:CGSizeMake(74, 74) resourceSize:CGSizeMake(24, 24)];
[actions insertObject:leaveAction atIndex:0];
}
return actions;
}
- (void)tableView:(UITableView*)tableView didEndEditingRowAtIndexPath:(NSIndexPath *)indexPath
{
[self cancelEditionMode:isRefreshPending];
}
#pragma mark - UIScrollViewDelegate
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
[super scrollViewDidScroll:scrollView];
if (scrollView == self.groupsTableView)
{
if (!self.groupsSearchBar.isHidden)
{
if (!self.groupsSearchBar.text.length && (scrollView.contentOffset.y + scrollView.adjustedContentInset.top > self.groupsSearchBar.frame.size.height))
{
// Hide the search bar
[self hideSearchBar:YES];
// Refresh display
[self refreshGroupsTable];
}
}
}
}
#pragma mark - Room handling
- (void)onPlusButtonPressed
{
__weak typeof(self) weakSelf = self;
[currentAlert dismissViewControllerAnimated:NO completion:nil];
currentAlert = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet];
[currentAlert addAction:[UIAlertAction actionWithTitle:[VectorL10n cancel]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
if (weakSelf)
{
typeof(self) self = weakSelf;
self->currentAlert = nil;
}
}]];
[currentAlert popoverPresentationController].sourceView = plusButtonImageView;
[currentAlert popoverPresentationController].sourceRect = plusButtonImageView.bounds;
[currentAlert mxk_setAccessibilityIdentifier:@"GroupsVCCreateRoomAlert"];
[self presentViewController:currentAlert animated:YES completion:nil];
}
#pragma mark - Table view scrolling
- (void)scrollToTop:(BOOL)animated
{
[self.groupsTableView setContentOffset:CGPointMake(-self.groupsTableView.adjustedContentInset.left, -self.groupsTableView.adjustedContentInset.top) animated:animated];
}
#pragma mark - MXKGroupListViewControllerDelegate
- (void)groupListViewController:(MXKGroupListViewController *)groupListViewController didSelectGroup:(MXGroup *)group inMatrixSession:(MXSession *)mxSession
{
// Open the room
[[AppDelegate theDelegate].masterTabBarController selectGroup:group inMatrixSession:mxSession];
}
#pragma mark - UISearchBarDelegate
- (BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar
{
if (searchBar == tableSearchBar)
{
[self hideSearchBar:NO];
[self.groupsSearchBar becomeFirstResponder];
return NO;
}
return YES;
}
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar
{
dispatch_async(dispatch_get_main_queue(), ^{
[self.groupsSearchBar setShowsCancelButton:YES animated:NO];
});
}
- (void)searchBarTextDidEndEditing:(UISearchBar *)searchBar
{
[self.groupsSearchBar setShowsCancelButton:NO animated:NO];
}
#pragma mark - MasterTabBarItemDisplayProtocol
- (NSString *)masterTabBarItemTitle
{
return [VectorL10n titleGroups];
}
@end

View file

@ -1,70 +0,0 @@
/*
Copyright 2017 Vector Creations Ltd
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 "MatrixKit.h"
@interface GroupHomeViewController : MXKViewController <UIGestureRecognizerDelegate, UITextViewDelegate>
@property (weak, nonatomic) IBOutlet UIView *mainHeaderContainer;
@property (weak, nonatomic) IBOutlet MXKImageView *groupAvatar;
@property (weak, nonatomic) IBOutlet UIView *groupAvatarMask;
@property (weak, nonatomic) IBOutlet UILabel *groupName;
@property (weak, nonatomic) IBOutlet UIView *groupNameMask;
@property (weak, nonatomic) IBOutlet UILabel *groupDescription;
@property (weak, nonatomic) IBOutlet UIView *countsContainer;
@property (weak, nonatomic) IBOutlet UIView *membersCountContainer;
@property (weak, nonatomic) IBOutlet UIView *roomsCountContainer;
@property (weak, nonatomic) IBOutlet UILabel *membersCountLabel;
@property (weak, nonatomic) IBOutlet UILabel *roomsCountLabel;
@property (weak, nonatomic) IBOutlet UIView *inviteContainer;
@property (weak, nonatomic) IBOutlet UILabel *inviteLabel;
@property (weak, nonatomic) IBOutlet UIView *buttonsContainer;
@property (weak, nonatomic) IBOutlet UIButton *leftButton;
@property (weak, nonatomic) IBOutlet UIButton *rightButton;
@property (weak, nonatomic) IBOutlet UIView *separatorView;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *separatorViewTopConstraint;
@property (weak, nonatomic) IBOutlet UITextView *groupLongDescription;
@property (strong, readonly, nonatomic) MXGroup *group;
@property (strong, readonly, nonatomic) MXSession *mxSession;
/**
Returns the `UINib` object initialized for a `GroupHomeViewController`.
@return The initialized `UINib` object or `nil` if there were errors during initialization
or the nib file could not be located.
*/
+ (UINib *)nib;
/**
Creates and returns a new `GroupHomeViewController` object.
@discussion This is the designated initializer for programmatic instantiation.
@return An initialized `GroupHomeViewController` object if successful, `nil` otherwise.
*/
+ (instancetype)groupHomeViewController;
/**
Set the group for which the details are displayed.
Provide the related matrix session.
*/
- (void)setGroup:(MXGroup*)group withMatrixSession:(MXSession*)mxSession;
@end

View file

@ -1,903 +0,0 @@
/*
Copyright 2017 Vector Creations Ltd
Copyright 2018 New Vector Ltd
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 "GroupHomeViewController.h"
#import "GeneratedInterface-Swift.h"
#import "ThemeService.h"
#import "Tools.h"
#import "MXGroup+Riot.h"
#import "DTCoreText.h"
@interface GroupHomeViewController ()
{
MXHTTPOperation *currentRequest;
/**
The current visibility of the status bar in this view controller.
*/
BOOL isStatusBarHidden;
// Observe kThemeServiceDidChangeThemeNotification to handle user interface theme change.
id kThemeServiceDidChangeThemeNotificationObserver;
// The options used to load long description html content.
NSDictionary *options;
NSString *groupLongDescriptionString;
// The current pushed view controller
UIViewController *pushedViewController;
}
@property (nonatomic, readonly) DTHTMLAttributedStringBuilderWillFlushCallback longDescriptionSanitizationCallback;
@property (nonatomic) AnalyticsScreenTracker *screenTracker;
@end
@implementation GroupHomeViewController
#pragma mark - Class methods
+ (UINib *)nib
{
return [UINib nibWithNibName:NSStringFromClass(self.class)
bundle:[NSBundle bundleForClass:self.class]];
}
+ (instancetype)groupHomeViewController
{
return [[[self class] alloc] initWithNibName:NSStringFromClass(self.class)
bundle:[NSBundle bundleForClass:self.class]];
}
#pragma mark -
- (void)finalizeInit
{
[super finalizeInit];
// Setup `MXKViewControllerHandling` properties
self.enableBarTintColorStatusChange = NO;
self.rageShakeManager = [RageShakeManager sharedManager];
// Keep visible the status bar by default.
isStatusBarHidden = NO;
// Set up sanitization for the long description
NSArray<NSString *> *allowedHTMLTags = @[
@"font", // custom to matrix for IRC-style font coloring
@"del", // for markdown
@"body", // added internally by DTCoreText
@"h1", @"h2", @"h3", @"h4", @"h5", @"h6", @"blockquote", @"p", @"a", @"ul", @"ol",
@"nl", @"li", @"b", @"i", @"u", @"strong", @"em", @"strike", @"code", @"hr", @"br", @"div",
@"table", @"thead", @"caption", @"tbody", @"tr", @"th", @"td", @"pre",
@"img"
];
MXWeakify(self);
_longDescriptionSanitizationCallback = ^(DTHTMLElement *element) {
MXStrongifyAndReturnIfNil(self);
[element sanitizeWith:allowedHTMLTags bodyFont:self->_groupLongDescription.font imageHandler:[self groupLongDescriptionImageHandler]];
};
self.screenTracker = [[AnalyticsScreenTracker alloc] initWithScreen:AnalyticsScreenGroup];
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self.leftButton setTitle:[VectorL10n decline] forState:UIControlStateNormal];
[self.leftButton setTitle:[VectorL10n decline] forState:UIControlStateHighlighted];
[self.rightButton setTitle:[VectorL10n join] forState:UIControlStateNormal];
[self.rightButton setTitle:[VectorL10n join] forState:UIControlStateHighlighted];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapGesture:)];
[tap setNumberOfTouchesRequired:1];
[tap setNumberOfTapsRequired:1];
[tap setDelegate:self];
[_groupNameMask addGestureRecognizer:tap];
_groupNameMask.userInteractionEnabled = YES;
// Add tap to show the group avatar in fullscreen
tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapGesture:)];
[tap setNumberOfTouchesRequired:1];
[tap setNumberOfTapsRequired:1];
[tap setDelegate:self];
[_groupAvatarMask addGestureRecognizer:tap];
_groupAvatarMask.userInteractionEnabled = YES;
// Observe user interface theme change.
kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
[self userInterfaceThemeDidChange];
}];
[self userInterfaceThemeDidChange];
}
- (void)userInterfaceThemeDidChange
{
[ThemeService.shared.theme applyStyleOnNavigationBar:self.navigationController.navigationBar];
self.activityIndicator.backgroundColor = ThemeService.shared.theme.overlayBackgroundColor;
self.view.backgroundColor = ThemeService.shared.theme.backgroundColor;
self.mainHeaderContainer.backgroundColor = ThemeService.shared.theme.headerBackgroundColor;
_groupName.textColor = ThemeService.shared.theme.textPrimaryColor;
_groupDescription.textColor = ThemeService.shared.theme.baseTextSecondaryColor;
_groupDescription.numberOfLines = 0;
self.inviteLabel.textColor = ThemeService.shared.theme.baseTextSecondaryColor;
self.inviteLabel.numberOfLines = 0;
self.separatorView.backgroundColor = ThemeService.shared.theme.lineBreakColor;
[self.leftButton.layer setCornerRadius:5];
self.leftButton.clipsToBounds = YES;
self.leftButton.backgroundColor = ThemeService.shared.theme.tintColor;
[self.rightButton.layer setCornerRadius:5];
self.rightButton.clipsToBounds = YES;
self.rightButton.backgroundColor = ThemeService.shared.theme.tintColor;
if (_groupLongDescription)
{
_groupLongDescription.textColor = ThemeService.shared.theme.textSecondaryColor;
_groupLongDescription.tintColor = ThemeService.shared.theme.tintColor;
// Update HTML loading options
NSUInteger bgColor = [MXKTools rgbValueWithColor:ThemeService.shared.theme.headerBackgroundColor];
NSString *defaultCSS = [NSString stringWithFormat:@" \
pre,code { \
background-color: #%06lX; \
display: inline; \
font-family: monospace; \
white-space: pre; \
-coretext-fontname: Menlo-Regular; \
font-size: small; \
}", (unsigned long)bgColor];
// Apply the css style with some sanitisation.
options = @{
DTUseiOS6Attributes: @(YES), // Enable it to be able to display the attributed string in a UITextView
DTDefaultFontFamily: _groupLongDescription.font.familyName,
DTDefaultFontName: _groupLongDescription.font.fontName,
DTDefaultFontSize: @(_groupLongDescription.font.pointSize),
DTDefaultTextColor: _groupLongDescription.textColor,
DTDefaultLinkDecoration: @(NO),
DTDefaultStyleSheet: [[DTCSSStylesheet alloc] initWithStyleBlock:defaultCSS],
DTWillFlushBlockCallBack: self.longDescriptionSanitizationCallback
};
}
[self setNeedsStatusBarAppearanceUpdate];
}
- (UIStatusBarStyle)preferredStatusBarStyle
{
return ThemeService.shared.theme.statusBarStyle;
}
- (BOOL)prefersStatusBarHidden
{
// Return the current status bar visibility.
return isStatusBarHidden;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self.screenTracker trackScreen];
// Release the potential pushed view controller
[self releasePushedViewController];
if (_group)
{
// Restore the listeners on the group update.
[self registerOnGroupChangeNotifications];
// Check whether the selected group is stored in the user's session, or if it is a group preview.
// Replace the displayed group instance with the one stored in the session (if any).
MXGroup *storedGroup = [_mxSession groupWithGroupId:_group.groupId];
BOOL isPreview = (!storedGroup);
// Force refresh
[self refreshDisplayWithGroup:(isPreview ? _group : storedGroup)];
// Prepare a block called on successful update in case of a group preview.
// Indeed the group update notifications are triggered by the matrix session only for the user's groups.
void (^success)(void) = ^void(void)
{
[self refreshDisplayWithGroup:self->_group];
};
// Trigger a refresh on the group summary.
[self.mxSession updateGroupSummary:self->_group success:(isPreview ? success : nil) failure:^(NSError *error) {
MXLogDebug(@"[GroupHomeViewController] viewWillAppear: group summary update failed %@", self->_group.groupId);
}];
// Trigger a refresh on the group members (ignore here the invited users).
[self.mxSession updateGroupUsers:self->_group success:(isPreview ? success : nil) failure:^(NSError *error) {
MXLogDebug(@"[GroupHomeViewController] viewWillAppear: group members update failed %@", self->_group.groupId);
}];
// Trigger a refresh on the group rooms.
[self.mxSession updateGroupRooms:self->_group success:(isPreview ? success : nil) failure:^(NSError *error) {
MXLogDebug(@"[GroupHomeViewController] viewWillAppear: group rooms update failed %@", self->_group.groupId);
}];
}
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[self cancelRegistrationOnGroupChangeNotifications];
}
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
// Scroll to the top the long group description.
_groupLongDescription.contentOffset = CGPointZero;
}
- (void)destroy
{
// Release the potential pushed view controller
[self releasePushedViewController];
// Note: all observers are removed during super call.
[super destroy];
_group = nil;
_mxSession = nil;
[currentRequest cancel];
currentRequest = nil;
if (kThemeServiceDidChangeThemeNotificationObserver)
{
[[NSNotificationCenter defaultCenter] removeObserver:kThemeServiceDidChangeThemeNotificationObserver];
kThemeServiceDidChangeThemeNotificationObserver = nil;
}
}
- (void)setGroup:(MXGroup*)group withMatrixSession:(MXSession*)mxSession
{
if (_mxSession != mxSession)
{
[self cancelRegistrationOnGroupChangeNotifications];
_mxSession = mxSession;
[self registerOnGroupChangeNotifications];
}
[self addMatrixSession:mxSession];
[self refreshDisplayWithGroup:group];
}
#pragma mark -
- (void)pushViewController:(UIViewController*)viewController
{
// Keep ref on pushed view controller
pushedViewController = viewController;
// Check whether the view controller is displayed inside a segmented one.
if (self.parentViewController.navigationController)
{
// Hide back button title
[self.parentViewController vc_removeBackTitle];
[self.parentViewController.navigationController pushViewController:viewController animated:YES];
}
else
{
// Hide back button title
[self vc_removeBackTitle];
[self.navigationController pushViewController:viewController animated:YES];
}
}
- (void)releasePushedViewController
{
if (pushedViewController)
{
if ([pushedViewController isKindOfClass:[UINavigationController class]])
{
UINavigationController *navigationController = (UINavigationController*)pushedViewController;
for (id subViewController in navigationController.viewControllers)
{
if ([subViewController respondsToSelector:@selector(destroy)])
{
[subViewController destroy];
}
}
}
else if ([pushedViewController respondsToSelector:@selector(destroy)])
{
[(id)pushedViewController destroy];
}
pushedViewController = nil;
}
}
- (void)registerOnGroupChangeNotifications
{
if (_mxSession)
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didUpdateGroupDetails:) name:kMXSessionDidUpdateGroupSummaryNotification object:_mxSession];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didUpdateGroupDetails:) name:kMXSessionDidUpdateGroupUsersNotification object:_mxSession];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didUpdateGroupDetails:) name:kMXSessionDidUpdateGroupRoomsNotification object:_mxSession];
}
}
- (void)cancelRegistrationOnGroupChangeNotifications
{
// Remove any pending observers
[[NSNotificationCenter defaultCenter] removeObserver:self name:kMXSessionDidUpdateGroupSummaryNotification object:_mxSession];
[[NSNotificationCenter defaultCenter] removeObserver:self name:kMXSessionDidUpdateGroupUsersNotification object:_mxSession];
[[NSNotificationCenter defaultCenter] removeObserver:self name:kMXSessionDidUpdateGroupRoomsNotification object:_mxSession];
}
- (void)didUpdateGroupDetails:(NSNotification *)notif
{
MXGroup *group = notif.userInfo[kMXSessionNotificationGroupKey];
if (group && [group.groupId isEqualToString:_group.groupId])
{
// Update the current displayed group instance with the one stored in the session
[self refreshDisplayWithGroup:group];
}
}
- (void)refreshDisplayWithGroup:(MXGroup*)group
{
_group = group;
// Check whether the view controller has been loaded
if (!self.isViewLoaded)
{
return;
}
if (_group)
{
[_group setGroupAvatarImageIn:_groupAvatar matrixSession:self.mxSession];
_groupName.text = _group.summary.profile.name;
if (!_groupName.text.length)
{
_groupName.text = _group.groupId;
}
_groupDescription.text = _group.summary.profile.shortDescription;
if (_group.users.totalUserCountEstimate == 1)
{
_membersCountLabel.text = [VectorL10n groupHomeOneMemberFormat];
_membersCountContainer.hidden = NO;
}
else if (_group.users.totalUserCountEstimate > 1)
{
_membersCountLabel.text = [VectorL10n groupHomeMultiMembersFormat:_group.users.totalUserCountEstimate];
_membersCountContainer.hidden = NO;
}
else
{
_membersCountLabel.text = nil;
_membersCountContainer.hidden = YES;
}
if (_group.rooms.totalRoomCountEstimate == 1)
{
_roomsCountLabel.text = [VectorL10n groupHomeOneRoomFormat];
_roomsCountContainer.hidden = NO;
}
else if (_group.rooms.totalRoomCountEstimate > 1)
{
_roomsCountLabel.text = [VectorL10n groupHomeMultiRoomsFormat:_group.rooms.totalRoomCountEstimate];
_roomsCountContainer.hidden = NO;
}
else
{
_roomsCountLabel.text = nil;
_roomsCountContainer.hidden = YES;
}
_countsContainer.hidden = (_membersCountContainer.isHidden && _roomsCountContainer.isHidden);
if (_group.membership == MXMembershipInvite)
{
self.inviteContainer.hidden = NO;
if (_group.inviter)
{
NSString *inviter = _group.inviter;
if ([MXTools isMatrixUserIdentifier:inviter])
{
// Get the user that corresponds to this member
MXUser *user = [self.mxSession userWithUserId:inviter];
if (user.displayname.length)
{
inviter = user.displayname;
}
}
self.inviteLabel.text = [VectorL10n groupInvitationFormat:inviter];
}
else
{
self.inviteLabel.text = nil;
}
[self.inviteContainer layoutIfNeeded];
if (_separatorViewTopConstraint.constant != self.inviteContainer.frame.size.height)
{
_separatorViewTopConstraint.constant = self.inviteContainer.frame.size.height;
[self.view setNeedsLayout];
}
}
else
{
self.inviteContainer.hidden = YES;
if (_separatorViewTopConstraint.constant != 0)
{
_separatorViewTopConstraint.constant = 0;
[self.view setNeedsLayout];
}
}
[self refreshGroupLongDescription];
}
else
{
_groupAvatar.image = nil;
_groupName.text = nil;
_groupDescription.text = nil;
self.inviteLabel.text = nil;
_groupLongDescription.text = nil;
self.inviteContainer.hidden = YES;
_separatorViewTopConstraint.constant = 0;
_membersCountLabel.text = nil;
_roomsCountLabel.text = nil;
_countsContainer.hidden = YES;
}
// Round image view for thumbnail
_groupAvatar.layer.cornerRadius = _groupAvatar.frame.size.width / 2;
_groupAvatar.clipsToBounds = YES;
_groupAvatar.defaultBackgroundColor = ThemeService.shared.theme.headerBackgroundColor;
}
- (void)refreshGroupLongDescription
{
if (_group.summary.profile.longDescription.length)
{
groupLongDescriptionString = _group.summary.profile.longDescription;
}
else
{
groupLongDescriptionString = nil;
}
[self renderGroupLongDescription];
}
- (void)renderGroupLongDescription
{
if (groupLongDescriptionString)
{
// Using DTCoreText, which renders static string, helps to avoid code injection attacks
// that could happen with the default HTML renderer of NSAttributedString which is a
// webview.
// The supplied options include a callback to sanitize html tags and load image data.
NSAttributedString *attributedString = [[NSAttributedString alloc] initWithHTMLData:[groupLongDescriptionString dataUsingEncoding:NSUTF8StringEncoding] options:options documentAttributes:NULL];
// Apply additional treatments
NSInteger mxIdsBitMask = (MXKTOOLS_USER_IDENTIFIER_BITWISE | MXKTOOLS_ROOM_IDENTIFIER_BITWISE | MXKTOOLS_ROOM_ALIAS_BITWISE | MXKTOOLS_EVENT_IDENTIFIER_BITWISE | MXKTOOLS_GROUP_IDENTIFIER_BITWISE);
NSMutableAttributedString *mutableStr = [[NSMutableAttributedString alloc] initWithAttributedString:attributedString];
[MXKTools createLinksInMutableAttributedString:mutableStr forEnabledMatrixIds:mxIdsBitMask];
[MXKTools removeDTCoreTextArtifacts:mutableStr];
// Finalize the attributed string by removing DTCoreText artifacts (Trim trailing newlines, replace DTImageTextAttachments...)
_groupLongDescription.attributedText = mutableStr;
_groupLongDescription.contentOffset = CGPointZero;
}
else
{
_groupLongDescription.text = nil;
}
}
- (NSURL *(^)(NSString *sourceURL, CGFloat width, CGFloat height))groupLongDescriptionImageHandler
{
MXWeakify(self);
return ^NSURL *(NSString *sourceURL, CGFloat width, CGFloat height) {
MXStrongifyAndReturnValueIfNil(self, nil);
NSURL *localSourceURL;
if (width != -1 && height != -1)
{
CGSize size = CGSizeMake(width, height);
// Build the cache path for the a thumbnail of this image.
NSString *cacheFilePath = [MXMediaManager thumbnailCachePathForMatrixContentURI:sourceURL
andType:nil
inFolder:kMXMediaManagerDefaultCacheFolder
toFitViewSize:size
withMethod:MXThumbnailingMethodScale];
// Check whether the provided URL is a valid Matrix Content URI.
if (cacheFilePath)
{
// Download the thumbnail if it is not already stored in the cache.
if (![[NSFileManager defaultManager] fileExistsAtPath:cacheFilePath])
{
MXWeakify(self);
[self.mxSession.mediaManager downloadThumbnailFromMatrixContentURI:sourceURL
withType:nil
inFolder:kMXMediaManagerDefaultCacheFolder
toFitViewSize:size
withMethod:MXThumbnailingMethodScale
success:^(NSString *outputFilePath) {
MXStrongifyAndReturnIfNil(self);
[self refreshGroupLongDescription];
}
failure:nil];
}
else
{
// Update the local url
localSourceURL = [NSURL fileURLWithPath:cacheFilePath];
}
}
}
else
{
// Build the cache path for this image.
NSString* cacheFilePath = [MXMediaManager cachePathForMatrixContentURI:sourceURL
andType:nil
inFolder:kMXMediaManagerDefaultCacheFolder];
// Check whether the provided URL is a valid Matrix Content URI.
if (cacheFilePath)
{
// Download the image if it is not already stored in the cache.
if (![[NSFileManager defaultManager] fileExistsAtPath:cacheFilePath])
{
MXWeakify(self);
[self.mxSession.mediaManager downloadMediaFromMatrixContentURI:sourceURL
withType:nil
inFolder:kMXMediaManagerDefaultCacheFolder
success:^(NSString *outputFilePath) {
MXStrongifyAndReturnIfNil(self);
[self refreshGroupLongDescription];
}
failure:nil];
}
else
{
// Update the local path
localSourceURL = [NSURL fileURLWithPath:cacheFilePath];
}
}
}
return localSourceURL;
};
}
- (void)didSelectRoomId:(NSString*)roomId
{
// Check first if the user already joined this room.
if ([self.mxSession roomWithRoomId:roomId])
{
MXKRoomDataSourceManager *roomDataSourceManager = [MXKRoomDataSourceManager sharedManagerForMatrixSession:self.mxSession];
[roomDataSourceManager roomDataSourceForRoom:roomId create:YES onComplete:^(MXKRoomDataSource *roomDataSource) {
// Open this room
RoomViewController *roomViewController = [RoomViewController roomViewController];
roomViewController.showMissedDiscussionsBadge = NO;
[roomViewController displayRoom:roomDataSource];
[self pushViewController:roomViewController];
}];
}
else
{
// Prepare a preview
RoomPreviewData *roomPreviewData = [[RoomPreviewData alloc] initWithRoomId:roomId andSession:self.mxSession];
__weak typeof(self) weakSelf = self;
[self startActivityIndicator];
// Try to get more information about the room before opening its preview
[roomPreviewData peekInRoom:^(BOOL succeeded) {
if (weakSelf)
{
typeof(self) self = weakSelf;
[self stopActivityIndicator];
// Display the room preview
RoomViewController *roomViewController = [RoomViewController roomViewController];
roomViewController.showMissedDiscussionsBadge = NO;
[roomViewController displayRoomPreview:roomPreviewData];
[self pushViewController:roomViewController];
}
}];
}
}
#pragma mark - Action
- (IBAction)onButtonPressed:(id)sender
{
if (!currentRequest)
{
if (sender == self.rightButton)
{
// Accept the invite
__weak typeof(self) weakSelf = self;
[self startActivityIndicator];
currentRequest = [self.mxSession acceptGroupInvite:_group.groupId success:^{
if (weakSelf)
{
typeof(self) self = weakSelf;
self->currentRequest = nil;
[self stopActivityIndicator];
[self refreshDisplayWithGroup:[self->_mxSession groupWithGroupId:self->_group.groupId]];
}
} failure:^(NSError *error) {
MXLogDebug(@"[GroupDetailsViewController] join group (%@) failed", self->_group.groupId);
if (weakSelf)
{
typeof(self) self = weakSelf;
self->currentRequest = nil;
[self stopActivityIndicator];
}
// Alert user
[[AppDelegate theDelegate] showErrorAsAlert:error];
}];
}
else if (sender == self.leftButton)
{
// Decline the invite
__weak typeof(self) weakSelf = self;
[self startActivityIndicator];
currentRequest = [self.mxSession leaveGroup:_group.groupId success:^{
if (weakSelf)
{
typeof(self) self = weakSelf;
self->currentRequest = nil;
[self stopActivityIndicator];
[self withdrawViewControllerAnimated:YES completion:nil];
}
} failure:^(NSError *error) {
MXLogDebug(@"[GroupDetailsViewController] leave group (%@) failed", self->_group.groupId);
if (weakSelf)
{
typeof(self) self = weakSelf;
self->currentRequest = nil;
[self stopActivityIndicator];
}
// Alert user
[[AppDelegate theDelegate] showErrorAsAlert:error];
}];
}
}
}
- (void)handleTapGesture:(UITapGestureRecognizer*)tapGestureRecognizer
{
UIView *view = tapGestureRecognizer.view;
if (view == _groupNameMask && _group.summary.profile.name)
{
if ([_groupName.text isEqualToString:_group.summary.profile.name])
{
// Display group's matrix id
_groupName.text = _group.groupId;
}
else
{
// Restore display name
_groupName.text = _group.summary.profile.name;
}
}
else if (view == _groupAvatarMask)
{
// Show the avatar in full screen
__block MXKImageView * avatarFullScreenView = [[MXKImageView alloc] initWithFrame:CGRectZero];
avatarFullScreenView.stretchable = YES;
MXWeakify(self);
[avatarFullScreenView setRightButtonTitle:[VectorL10n ok] handler:^(MXKImageView* imageView, NSString* buttonTitle) {
MXStrongifyAndReturnIfNil(self);
[avatarFullScreenView dismissSelection];
[avatarFullScreenView removeFromSuperview];
avatarFullScreenView = nil;
self->isStatusBarHidden = NO;
// Trigger status bar update
[self setNeedsStatusBarAppearanceUpdate];
}];
[avatarFullScreenView setImageURI:_group.summary.profile.avatarUrl
withType:nil
andImageOrientation:UIImageOrientationUp
previewImage:self.groupAvatar.image
mediaManager:_mxSession.mediaManager];
[avatarFullScreenView showFullScreen];
isStatusBarHidden = YES;
// Trigger status bar update
[self setNeedsStatusBarAppearanceUpdate];
}
}
#pragma mark - UITextView delegate
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-implementations"
- (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange
{
BOOL shouldInteractWithURL = YES;
// Try to catch universal link supported by the app
// When a link refers to a room alias/id, a user id or an event id, the non-ASCII characters (like '#' in room alias) has been escaped
// to be able to convert it into a legal URL string.
NSString *absoluteURLString = [URL.absoluteString stringByRemovingPercentEncoding];
// If the link can be open it by the app, let it do
if ([Tools isUniversalLink:URL])
{
shouldInteractWithURL = NO;
[[AppDelegate theDelegate] handleUniversalLinkURL:URL];
}
// Open a detail screen about the clicked user
else if ([MXTools isMatrixUserIdentifier:absoluteURLString])
{
shouldInteractWithURL = NO;
NSString *userId = absoluteURLString;
MXKContact *contact;
// Use the contact detail VC for other users
MXUser *user = [self.mxSession userWithUserId:userId];
if (user)
{
contact = [[MXKContact alloc] initMatrixContactWithDisplayName:((user.displayname.length > 0) ? user.displayname : user.userId) andMatrixID:user.userId];
}
else
{
contact = [[MXKContact alloc] initMatrixContactWithDisplayName:userId andMatrixID:userId];
}
ContactDetailsViewController *contactDetailsViewController = [ContactDetailsViewController instantiate];
contactDetailsViewController.enableVoipCall = NO;
contactDetailsViewController.contact = contact;
[self pushViewController:contactDetailsViewController];
}
// Open the clicked room
else if ([MXTools isMatrixRoomIdentifier:absoluteURLString] || [MXTools isMatrixRoomAlias:absoluteURLString])
{
shouldInteractWithURL = NO;
NSString *roomIdOrAlias = absoluteURLString;
NSString *roomId;
if ([roomIdOrAlias hasPrefix:@"#"])
{
// Check whether the room alias can be translated locally into the room id.
MXRoom *room = [self.mxSession roomWithAlias:roomIdOrAlias];
if (room)
{
roomId = room.roomId;
}
}
else
{
roomId = roomIdOrAlias;
}
if (roomId)
{
[self didSelectRoomId:roomId];
}
else
{
// The alias may be not part of user's rooms states
// Ask the HS to resolve the room alias into a room id and then retry
__weak typeof(self) weakSelf = self;
[self startActivityIndicator];
[self.mxSession.matrixRestClient resolveRoomAlias:roomIdOrAlias success:^(MXRoomAliasResolution *resolution) {
if (roomId && weakSelf)
{
typeof(self) self = weakSelf;
[self stopActivityIndicator];
[self didSelectRoomId:resolution.roomId];
}
} failure:^(NSError *error) {
MXLogDebug(@"[GroupHomeViewController] Error: The homeserver failed to resolve the room alias (%@)", roomIdOrAlias);
}];
}
}
// Preview the clicked group
else if ([MXTools isMatrixGroupIdentifier:absoluteURLString])
{
shouldInteractWithURL = NO;
// Open the group or preview it
NSString *fragment = [NSString stringWithFormat:@"/group/%@",
[MXTools encodeURIComponent:absoluteURLString]];
UniversalLink *link = [[UniversalLink alloc] initWithUrl:URL];
[[AppDelegate theDelegate] handleUniversalLinkFragment:fragment fromLink:link];
}
return shouldInteractWithURL;
}
#pragma clang diagnostic pop
@end

View file

@ -1,285 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
<capability name="Aspect ratio constraints" minToolsVersion="5.1"/>
<capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="GroupHomeViewController">
<connections>
<outlet property="buttonsContainer" destination="EqU-yd-Ntb" id="SpC-UD-z6O"/>
<outlet property="countsContainer" destination="hkg-GN-Tej" id="cWs-i0-bVQ"/>
<outlet property="groupAvatar" destination="ehl-88-DfA" id="bKF-ij-hsH"/>
<outlet property="groupAvatarMask" destination="kYc-LP-Nip" id="u4M-Yf-wQy"/>
<outlet property="groupDescription" destination="jXh-2B-Bso" id="daH-OS-1BL"/>
<outlet property="groupLongDescription" destination="oUL-bJ-tfM" id="ZA0-1e-RrG"/>
<outlet property="groupName" destination="5UX-lR-Ac5" id="kzg-n6-ZXM"/>
<outlet property="groupNameMask" destination="mKE-ro-fuy" id="0DZ-3m-LNx"/>
<outlet property="inviteContainer" destination="nR4-eE-qJo" id="rsc-Gd-rLL"/>
<outlet property="inviteLabel" destination="UHk-6Y-MSO" id="JO0-8a-h78"/>
<outlet property="leftButton" destination="jTB-nL-KcP" id="iPW-qf-LbT"/>
<outlet property="mainHeaderContainer" destination="AKb-7n-nhQ" id="RUh-ZE-ul2"/>
<outlet property="membersCountContainer" destination="PgZ-iJ-yNZ" id="YKC-dF-QOE"/>
<outlet property="membersCountLabel" destination="qJ8-8X-Xzm" id="LRd-io-EFg"/>
<outlet property="rightButton" destination="xfk-oh-9db" id="JGC-zW-2vD"/>
<outlet property="roomsCountContainer" destination="xkH-zg-3jY" id="xo8-mh-emk"/>
<outlet property="roomsCountLabel" destination="XuG-pe-Lzn" id="Bbg-xa-lxp"/>
<outlet property="separatorView" destination="dlP-kX-cll" id="TMm-OR-eFJ"/>
<outlet property="separatorViewTopConstraint" destination="tSb-HY-8nq" id="jM0-gg-k1g"/>
<outlet property="view" destination="1TG-Rn-axS" id="WxL-e2-rVK"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="1TG-Rn-axS">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="AKb-7n-nhQ">
<rect key="frame" x="0.0" y="0.0" width="375" height="106.5"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="kYc-LP-Nip">
<rect key="frame" x="8" y="5" width="80" height="80"/>
<subviews>
<view clipsSubviews="YES" contentMode="scaleAspectFill" translatesAutoresizingMaskIntoConstraints="NO" id="ehl-88-DfA" customClass="MXKImageView">
<rect key="frame" x="10" y="10" width="60" height="60"/>
<color key="backgroundColor" red="0.6886889638" green="1" blue="0.74383144840000004" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<accessibility key="accessibilityConfiguration" identifier="MemberAvatar"/>
<constraints>
<constraint firstAttribute="width" secondItem="ehl-88-DfA" secondAttribute="height" multiplier="1:1" id="KSI-3d-yr8"/>
<constraint firstAttribute="width" constant="60" id="RFO-L1-XfN"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<accessibility key="accessibilityConfiguration" identifier="ContactDetailsVCAvatarMask"/>
<constraints>
<constraint firstItem="ehl-88-DfA" firstAttribute="centerY" secondItem="kYc-LP-Nip" secondAttribute="centerY" id="120-Fe-9lk"/>
<constraint firstAttribute="width" secondItem="kYc-LP-Nip" secondAttribute="height" multiplier="1:1" id="HNr-XL-fXe"/>
<constraint firstItem="ehl-88-DfA" firstAttribute="centerX" secondItem="kYc-LP-Nip" secondAttribute="centerX" id="LMN-31-ZCn"/>
<constraint firstAttribute="width" constant="80" id="zV6-TZ-Oan"/>
</constraints>
</view>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="5UX-lR-Ac5">
<rect key="frame" x="92" y="18" width="43.5" height="20.5"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleHeadline"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="jXh-2B-Bso">
<rect key="frame" x="92" y="44.5" width="37.5" height="18"/>
<accessibility key="accessibilityConfiguration" identifier="RoomTopic"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleSubhead"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="mKE-ro-fuy">
<rect key="frame" x="87" y="15" width="53.5" height="25.5"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<accessibility key="accessibilityConfiguration" identifier="ContactDetailsVCNameLabelMask"/>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="hkg-GN-Tej">
<rect key="frame" x="88" y="78.5" width="279" height="20"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="PgZ-iJ-yNZ">
<rect key="frame" x="0.0" y="0.0" width="86.5" height="20"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="tab_groups_selected" translatesAutoresizingMaskIntoConstraints="NO" id="YI6-IK-PkI">
<rect key="frame" x="4" y="2.5" width="15" height="15"/>
<constraints>
<constraint firstAttribute="width" secondItem="YI6-IK-PkI" secondAttribute="height" multiplier="1:1" id="azu-dv-Abo"/>
<constraint firstAttribute="width" constant="15" id="zka-UU-93t"/>
</constraints>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="2 members" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="qJ8-8X-Xzm">
<rect key="frame" x="23" y="3.5" width="63.5" height="14"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleCaption1"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="height" secondItem="qJ8-8X-Xzm" secondAttribute="height" constant="6" id="Aao-5S-pox"/>
<constraint firstItem="qJ8-8X-Xzm" firstAttribute="leading" secondItem="YI6-IK-PkI" secondAttribute="trailing" constant="4" id="NOr-CS-0zv"/>
<constraint firstAttribute="trailing" secondItem="qJ8-8X-Xzm" secondAttribute="trailing" id="ZwB-qD-I8A"/>
<constraint firstItem="YI6-IK-PkI" firstAttribute="leading" secondItem="PgZ-iJ-yNZ" secondAttribute="leading" constant="4" id="bkS-hZ-KQv"/>
<constraint firstItem="qJ8-8X-Xzm" firstAttribute="centerY" secondItem="PgZ-iJ-yNZ" secondAttribute="centerY" id="jdb-bW-4Oy"/>
<constraint firstItem="YI6-IK-PkI" firstAttribute="centerY" secondItem="PgZ-iJ-yNZ" secondAttribute="centerY" id="vCJ-Yz-2eu"/>
</constraints>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="xkH-zg-3jY">
<rect key="frame" x="94.5" y="0.0" width="68" height="20"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="tab_rooms" translatesAutoresizingMaskIntoConstraints="NO" id="aZ9-Gu-utv">
<rect key="frame" x="4" y="2.5" width="15" height="15"/>
<constraints>
<constraint firstAttribute="width" constant="15" id="3df-1e-XMS"/>
<constraint firstAttribute="width" secondItem="aZ9-Gu-utv" secondAttribute="height" multiplier="1:1" id="hnv-qQ-D9X"/>
</constraints>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="2 rooms" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="XuG-pe-Lzn">
<rect key="frame" x="22" y="3.5" width="46" height="14.5"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleCaption1"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="trailing" secondItem="XuG-pe-Lzn" secondAttribute="trailing" id="021-fG-7GQ"/>
<constraint firstItem="XuG-pe-Lzn" firstAttribute="leading" secondItem="aZ9-Gu-utv" secondAttribute="trailing" constant="3" id="3p6-oX-xHD"/>
<constraint firstItem="XuG-pe-Lzn" firstAttribute="centerY" secondItem="xkH-zg-3jY" secondAttribute="centerY" id="GvC-ox-7fH"/>
<constraint firstItem="aZ9-Gu-utv" firstAttribute="leading" secondItem="xkH-zg-3jY" secondAttribute="leading" constant="4" id="bbe-FJ-udn"/>
<constraint firstItem="aZ9-Gu-utv" firstAttribute="centerY" secondItem="xkH-zg-3jY" secondAttribute="centerY" id="fwX-jo-52x"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstItem="PgZ-iJ-yNZ" firstAttribute="leading" secondItem="hkg-GN-Tej" secondAttribute="leading" id="1Hr-5B-w2S"/>
<constraint firstItem="PgZ-iJ-yNZ" firstAttribute="width" relation="lessThanOrEqual" secondItem="hkg-GN-Tej" secondAttribute="width" multiplier="0.5" id="1pU-62-CzD"/>
<constraint firstItem="xkH-zg-3jY" firstAttribute="top" secondItem="hkg-GN-Tej" secondAttribute="top" id="46J-yD-cKS"/>
<constraint firstAttribute="height" secondItem="PgZ-iJ-yNZ" secondAttribute="height" id="5AD-Zs-aXU"/>
<constraint firstItem="xkH-zg-3jY" firstAttribute="leading" secondItem="PgZ-iJ-yNZ" secondAttribute="trailing" constant="8" id="Cds-AK-yuk"/>
<constraint firstItem="xkH-zg-3jY" firstAttribute="width" relation="lessThanOrEqual" secondItem="hkg-GN-Tej" secondAttribute="width" multiplier="0.5" id="Jjt-EP-UFP"/>
<constraint firstItem="xkH-zg-3jY" firstAttribute="height" secondItem="PgZ-iJ-yNZ" secondAttribute="height" id="JkC-AN-RWn"/>
<constraint firstItem="PgZ-iJ-yNZ" firstAttribute="top" secondItem="hkg-GN-Tej" secondAttribute="top" id="ZyS-oe-dDv"/>
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="xkH-zg-3jY" secondAttribute="trailing" id="aLt-Y9-7ZP"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
<constraints>
<constraint firstAttribute="bottom" secondItem="hkg-GN-Tej" secondAttribute="bottom" constant="8" id="04P-82-XhO"/>
<constraint firstItem="hkg-GN-Tej" firstAttribute="top" secondItem="jXh-2B-Bso" secondAttribute="bottom" constant="16" id="FVO-09-Pc5"/>
<constraint firstItem="5UX-lR-Ac5" firstAttribute="top" secondItem="AKb-7n-nhQ" secondAttribute="top" constant="18" id="FlH-ol-0TP"/>
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="kYc-LP-Nip" secondAttribute="bottom" priority="750" constant="10" id="G39-KZ-VFJ"/>
<constraint firstItem="mKE-ro-fuy" firstAttribute="centerX" secondItem="5UX-lR-Ac5" secondAttribute="centerX" id="G8w-Th-eCj"/>
<constraint firstItem="kYc-LP-Nip" firstAttribute="top" secondItem="AKb-7n-nhQ" secondAttribute="top" constant="5" id="IJD-FB-fWm"/>
<constraint firstItem="jXh-2B-Bso" firstAttribute="leading" secondItem="kYc-LP-Nip" secondAttribute="trailing" constant="4" id="Ot2-58-G82"/>
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="jXh-2B-Bso" secondAttribute="trailing" constant="8" id="QeR-3r-KrK"/>
<constraint firstAttribute="trailing" secondItem="hkg-GN-Tej" secondAttribute="trailing" constant="8" id="RHn-bj-Dld"/>
<constraint firstItem="kYc-LP-Nip" firstAttribute="leading" secondItem="AKb-7n-nhQ" secondAttribute="leading" constant="8" id="Y6V-cp-eRq"/>
<constraint firstItem="hkg-GN-Tej" firstAttribute="leading" secondItem="kYc-LP-Nip" secondAttribute="trailing" id="Z8J-Tk-BGi"/>
<constraint firstItem="5UX-lR-Ac5" firstAttribute="leading" secondItem="kYc-LP-Nip" secondAttribute="trailing" constant="4" id="fxX-Ak-zfG"/>
<constraint firstItem="mKE-ro-fuy" firstAttribute="height" secondItem="5UX-lR-Ac5" secondAttribute="height" constant="5" id="jtN-q7-iqn"/>
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="5UX-lR-Ac5" secondAttribute="trailing" constant="8" id="k8p-VY-5zr"/>
<constraint firstItem="mKE-ro-fuy" firstAttribute="centerY" secondItem="5UX-lR-Ac5" secondAttribute="centerY" id="nzz-nn-a5u"/>
<constraint firstItem="mKE-ro-fuy" firstAttribute="width" secondItem="5UX-lR-Ac5" secondAttribute="width" constant="10" id="o6d-6K-UUD"/>
<constraint firstItem="jXh-2B-Bso" firstAttribute="top" secondItem="mKE-ro-fuy" secondAttribute="bottom" constant="4" id="wyz-Qr-ka3"/>
</constraints>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="nR4-eE-qJo">
<rect key="frame" x="0.0" y="106.5" width="375" height="105.5"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="UHk-6Y-MSO">
<rect key="frame" x="167" y="15" width="42" height="20.5"/>
<accessibility key="accessibilityConfiguration" identifier="PreviewLabel"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="EqU-yd-Ntb" userLabel="buttonsContainer">
<rect key="frame" x="30" y="55.5" width="315" height="30"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="jTB-nL-KcP" userLabel="Left Button">
<rect key="frame" x="0.0" y="0.0" width="151" height="30"/>
<color key="backgroundColor" red="0.6886889638" green="1" blue="0.74383144840000004" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<accessibility key="accessibilityConfiguration" identifier="LeftButton"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="16"/>
<state key="normal" title="Left button">
<color key="titleColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
<connections>
<action selector="onButtonPressed:" destination="-1" eventType="touchUpInside" id="gNs-ys-XsX"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="xfk-oh-9db" userLabel="Right Button">
<rect key="frame" x="164" y="0.0" width="151" height="30"/>
<color key="backgroundColor" red="0.6886889638" green="1" blue="0.74383144840000004" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<accessibility key="accessibilityConfiguration" identifier="RightButton"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="16"/>
<state key="normal" title="Right Button">
<color key="titleColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
<connections>
<action selector="onButtonPressed:" destination="-1" eventType="touchUpInside" id="88I-7i-Arx"/>
</connections>
</button>
</subviews>
<constraints>
<constraint firstItem="xfk-oh-9db" firstAttribute="height" secondItem="EqU-yd-Ntb" secondAttribute="height" id="HRj-9b-sf9"/>
<constraint firstItem="xfk-oh-9db" firstAttribute="width" secondItem="EqU-yd-Ntb" secondAttribute="width" multiplier="0.48" id="JXG-Qy-PTf"/>
<constraint firstAttribute="trailing" secondItem="xfk-oh-9db" secondAttribute="trailing" id="KnR-27-pqz"/>
<constraint firstItem="jTB-nL-KcP" firstAttribute="top" secondItem="EqU-yd-Ntb" secondAttribute="top" id="SvJ-kv-poF"/>
<constraint firstItem="xfk-oh-9db" firstAttribute="top" secondItem="EqU-yd-Ntb" secondAttribute="top" id="WI3-b5-AgB"/>
<constraint firstAttribute="height" constant="30" id="ZZd-YY-Gd2"/>
<constraint firstItem="jTB-nL-KcP" firstAttribute="height" secondItem="EqU-yd-Ntb" secondAttribute="height" id="fxc-lx-cMI"/>
<constraint firstItem="jTB-nL-KcP" firstAttribute="leading" secondItem="EqU-yd-Ntb" secondAttribute="leading" id="gQS-yN-cek"/>
<constraint firstItem="jTB-nL-KcP" firstAttribute="width" secondItem="EqU-yd-Ntb" secondAttribute="width" multiplier="0.48" id="qmv-b5-hHq"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstItem="EqU-yd-Ntb" firstAttribute="leading" secondItem="nR4-eE-qJo" secondAttribute="leading" constant="30" id="0A0-CA-Han"/>
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="UHk-6Y-MSO" secondAttribute="trailing" constant="31" id="1AA-2G-22p"/>
<constraint firstItem="EqU-yd-Ntb" firstAttribute="top" secondItem="UHk-6Y-MSO" secondAttribute="bottom" constant="20" id="T4l-2m-fsZ"/>
<constraint firstAttribute="trailing" secondItem="EqU-yd-Ntb" secondAttribute="trailing" constant="30" id="cCk-lA-SDz"/>
<constraint firstItem="UHk-6Y-MSO" firstAttribute="centerX" secondItem="nR4-eE-qJo" secondAttribute="centerX" id="dlW-cX-D6T"/>
<constraint firstItem="UHk-6Y-MSO" firstAttribute="top" secondItem="nR4-eE-qJo" secondAttribute="top" constant="15" id="oNr-AR-g3d"/>
<constraint firstItem="UHk-6Y-MSO" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="nR4-eE-qJo" secondAttribute="leading" constant="31" id="r7D-qk-LEG"/>
<constraint firstAttribute="bottom" secondItem="EqU-yd-Ntb" secondAttribute="bottom" constant="20" id="zlf-mL-iWH"/>
</constraints>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="dlP-kX-cll">
<rect key="frame" x="0.0" y="204.5" width="375" height="1"/>
<color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
<accessibility key="accessibilityConfiguration" identifier="BottomBorderView"/>
<constraints>
<constraint firstAttribute="height" constant="1" id="tUj-3s-NIW"/>
</constraints>
</view>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" editable="NO" adjustsFontForContentSizeCategory="YES" translatesAutoresizingMaskIntoConstraints="NO" id="oUL-bJ-tfM">
<rect key="frame" x="8" y="205.5" width="359" height="453.5"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<string key="text">Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda.</string>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
<dataDetectorType key="dataDetectorTypes" link="YES"/>
<connections>
<outlet property="delegate" destination="-1" id="QYj-x9-kpV"/>
</connections>
</textView>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<accessibility key="accessibilityConfiguration" identifier="SegmentedVCView"/>
<constraints>
<constraint firstItem="AKb-7n-nhQ" firstAttribute="top" secondItem="1TG-Rn-axS" secondAttribute="top" id="3NU-eX-o9z"/>
<constraint firstAttribute="trailing" secondItem="oUL-bJ-tfM" secondAttribute="trailing" constant="8" id="4G6-iX-BjR"/>
<constraint firstAttribute="bottom" secondItem="oUL-bJ-tfM" secondAttribute="bottom" constant="8" id="Cfg-1K-EMs"/>
<constraint firstItem="AKb-7n-nhQ" firstAttribute="leading" secondItem="1TG-Rn-axS" secondAttribute="leading" id="O32-n9-j7o"/>
<constraint firstItem="nR4-eE-qJo" firstAttribute="leading" secondItem="1TG-Rn-axS" secondAttribute="leading" id="QRs-15-AIO"/>
<constraint firstItem="oUL-bJ-tfM" firstAttribute="leading" secondItem="1TG-Rn-axS" secondAttribute="leading" constant="8" id="bG6-iY-kCj"/>
<constraint firstItem="nR4-eE-qJo" firstAttribute="top" secondItem="AKb-7n-nhQ" secondAttribute="bottom" id="e0J-TS-0Z2"/>
<constraint firstAttribute="trailing" secondItem="AKb-7n-nhQ" secondAttribute="trailing" id="jEv-Pg-vBb"/>
<constraint firstAttribute="trailing" secondItem="nR4-eE-qJo" secondAttribute="trailing" id="kB6-fy-TZ7"/>
<constraint firstItem="dlP-kX-cll" firstAttribute="leading" secondItem="1TG-Rn-axS" secondAttribute="leading" id="p9g-6c-wFe"/>
<constraint firstAttribute="trailing" secondItem="dlP-kX-cll" secondAttribute="trailing" id="qdC-Jn-j6W"/>
<constraint firstItem="oUL-bJ-tfM" firstAttribute="top" secondItem="dlP-kX-cll" secondAttribute="bottom" id="qoC-BS-r0k"/>
<constraint firstItem="dlP-kX-cll" firstAttribute="top" secondItem="AKb-7n-nhQ" secondAttribute="bottom" constant="98" id="tSb-HY-8nq"/>
</constraints>
<point key="canvasLocation" x="26.5" y="51.5"/>
</view>
</objects>
<resources>
<image name="tab_groups_selected" width="25" height="25"/>
<image name="tab_rooms" width="25" height="25"/>
</resources>
</document>

Some files were not shown because too many files have changed in this diff Show more