mirror of
https://github.com/vector-im/element-ios.git
synced 2024-09-29 07:42:40 +00:00
Refactored creation of UserSessionListItemViewData, added inactive session icon
This commit is contained in:
parent
0b85beb0f4
commit
899539fbeb
10 changed files with 153 additions and 59 deletions
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "user_session_list_item_inactive_session.svg",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
<svg width="8" height="15" viewBox="0 0 8 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M1.33333 0.833984C0.6 0.833984 0 1.43398 0 2.16732L0.00666682 4.28732C0.00666682 4.64065 0.146667 4.97398 0.393333 5.22732L2.66667 7.50065L0.393333 9.78732C0.146667 10.034 0.00666682 10.374 0.00666682 10.7273L0 12.834C0 13.5673 0.6 14.1673 1.33333 14.1673H6.66667C7.4 14.1673 8 13.5673 8 12.834V10.7273C8 10.374 7.86 10.034 7.61333 9.78732L5.33333 7.50065L7.60667 5.23398C7.86 4.98065 8 4.64065 8 4.28732V2.16732C8 1.43398 7.4 0.833984 6.66667 0.833984H1.33333ZM6.66667 10.774V12.1673C6.66667 12.534 6.36667 12.834 6 12.834H2C1.63333 12.834 1.33333 12.534 1.33333 12.1673V10.774C1.33333 10.594 1.40667 10.4273 1.52667 10.3007L4 7.83398L6.47333 10.3073C6.59333 10.4273 6.66667 10.6007 6.66667 10.774Z" fill="#737D8C"/>
|
||||
</svg>
|
After Width: | Height: | Size: 828 B |
|
@ -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
|
||||
|
||||
class InactiveUserSessionLastActivityFormatter {
|
||||
private static var dateFormatter: DateFormatter = {
|
||||
let dateFormatter = DateFormatter()
|
||||
dateFormatter.locale = Locale.current
|
||||
dateFormatter.dateStyle = .medium
|
||||
dateFormatter.doesRelativeDateFormatting = true
|
||||
return dateFormatter
|
||||
}()
|
||||
|
||||
func lastActivityDateString(from lastActivityTimestamp: TimeInterval) -> String {
|
||||
let date = Date(timeIntervalSince1970: lastActivityTimestamp)
|
||||
|
||||
return InactiveUserSessionLastActivityFormatter.dateFormatter.string(from: date)
|
||||
}
|
||||
}
|
|
@ -40,7 +40,6 @@ struct UserOtherSessions: View {
|
|||
} header: {
|
||||
UserOtherSessionsHeaderView(viewData: header)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.padding(.horizontal, 16.0)
|
||||
.padding(.top, 24.0)
|
||||
}
|
||||
case .clearFilter:
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
import SwiftUI
|
||||
|
||||
struct UserOtherSessionsHeaderViewData {
|
||||
struct UserOtherSessionsHeaderViewData: Hashable {
|
||||
var title: String?
|
||||
var subtitle: String
|
||||
var iconName: String?
|
||||
|
@ -24,6 +24,10 @@ struct UserOtherSessionsHeaderViewData {
|
|||
|
||||
struct UserOtherSessionsHeaderView: View {
|
||||
|
||||
private var backgroundShape: RoundedRectangle {
|
||||
RoundedRectangle(cornerRadius: 8)
|
||||
}
|
||||
|
||||
@Environment(\.theme) private var theme
|
||||
|
||||
let viewData: UserOtherSessionsHeaderViewData
|
||||
|
@ -33,13 +37,15 @@ struct UserOtherSessionsHeaderView: View {
|
|||
if let iconName = viewData.iconName {
|
||||
Image(iconName)
|
||||
.foregroundColor(.red)
|
||||
.frame(width: 40, height: 40)
|
||||
.background(theme.colors.background)
|
||||
.clipShape(backgroundShape)
|
||||
.shapedBorder(color: theme.colors.quinaryContent, borderWidth: 1.0, shape: backgroundShape)
|
||||
}
|
||||
VStack(alignment: .leading, spacing: 0, content: {
|
||||
if let title = viewData.title {
|
||||
Text(title)
|
||||
.font(.callout)
|
||||
.textCase(.uppercase)
|
||||
.font(theme.fonts.footnote)
|
||||
.font(theme.fonts.calloutSB)
|
||||
.foregroundColor(theme.colors.primaryContent)
|
||||
.padding(.bottom, 9.0)
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@ class MockUserSessionsOverviewService: UserSessionsOverviewServiceProtocol {
|
|||
deviceType: .desktop,
|
||||
isVerified: true,
|
||||
lastSeenIP: "1.0.0.1",
|
||||
lastSeenTimestamp: Date().timeIntervalSince1970 - 130_000,
|
||||
lastSeenTimestamp: Date().timeIntervalSince1970 - 8000000,
|
||||
isActive: false,
|
||||
isCurrent: false),
|
||||
UserSessionInfo(id: "2",
|
||||
|
|
|
@ -100,6 +100,6 @@ class UserSessionsOverviewViewModel: UserSessionsOverviewViewModelType, UserSess
|
|||
|
||||
extension Collection where Element == UserSessionInfo {
|
||||
func asViewData() -> [UserSessionListItemViewData] {
|
||||
map { UserSessionListItemViewData(session: $0) }
|
||||
map { UserSessionListItemViewDataFactory().create(from: $0)}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,11 +42,16 @@ struct UserSessionListItem: View {
|
|||
.font(theme.fonts.bodySB)
|
||||
.foregroundColor(theme.colors.primaryContent)
|
||||
.multilineTextAlignment(.leading)
|
||||
|
||||
Text(viewData.sessionDetails)
|
||||
.font(theme.fonts.caption1)
|
||||
.foregroundColor(theme.colors.secondaryContent)
|
||||
.multilineTextAlignment(.leading)
|
||||
HStack {
|
||||
if let sessionDetailsIcon = viewData.sessionDetailsIcon {
|
||||
Image(sessionDetailsIcon)
|
||||
.padding(.leading, 2)
|
||||
}
|
||||
Text(viewData.sessionDetails)
|
||||
.font(theme.fonts.caption1)
|
||||
.foregroundColor(theme.colors.secondaryContent)
|
||||
.multilineTextAlignment(.leading)
|
||||
}
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
|
@ -69,7 +74,7 @@ struct UserSessionListPreview: View {
|
|||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
ForEach(userSessionsOverviewService.overviewData.otherSessions) { userSessionInfo in
|
||||
let viewData = UserSessionListItemViewData(session: userSessionInfo)
|
||||
let viewData = UserSessionListItemViewDataFactory().create(from: userSessionInfo)
|
||||
|
||||
UserSessionListItem(viewData: viewData, onBackgroundTap: { _ in
|
||||
|
||||
|
|
|
@ -18,61 +18,17 @@ import Foundation
|
|||
|
||||
/// View data for UserSessionListItem
|
||||
struct UserSessionListItemViewData: Identifiable, Hashable {
|
||||
private static let userSessionNameFormatter = UserSessionNameFormatter()
|
||||
private static let lastActivityDateFormatter = UserSessionLastActivityFormatter()
|
||||
|
||||
var id: String {
|
||||
sessionId
|
||||
}
|
||||
|
||||
let sessionId: String
|
||||
|
||||
|
||||
let sessionName: String
|
||||
|
||||
let sessionDetails: String
|
||||
|
||||
let deviceAvatarViewData: DeviceAvatarViewData
|
||||
|
||||
init(sessionId: String,
|
||||
sessionDisplayName: String?,
|
||||
deviceType: DeviceType,
|
||||
isVerified: Bool,
|
||||
lastActivityDate: TimeInterval?) {
|
||||
self.sessionId = sessionId
|
||||
sessionName = Self.userSessionNameFormatter.sessionName(deviceType: deviceType, sessionDisplayName: sessionDisplayName)
|
||||
sessionDetails = Self.buildSessionDetails(isVerified: isVerified, lastActivityDate: lastActivityDate)
|
||||
deviceAvatarViewData = DeviceAvatarViewData(deviceType: deviceType, isVerified: isVerified)
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private static func buildSessionDetails(isVerified: Bool, lastActivityDate: TimeInterval?) -> String {
|
||||
let sessionDetailsString: String
|
||||
|
||||
let sessionStatusText = isVerified ? VectorL10n.userSessionVerifiedShort : VectorL10n.userSessionUnverifiedShort
|
||||
|
||||
var lastActivityDateString: String?
|
||||
|
||||
if let lastActivityDate = lastActivityDate {
|
||||
lastActivityDateString = Self.lastActivityDateFormatter.lastActivityDateString(from: lastActivityDate)
|
||||
}
|
||||
|
||||
if let lastActivityDateString = lastActivityDateString, lastActivityDateString.isEmpty == false {
|
||||
sessionDetailsString = VectorL10n.userSessionItemDetails(sessionStatusText, lastActivityDateString)
|
||||
} else {
|
||||
sessionDetailsString = sessionStatusText
|
||||
}
|
||||
|
||||
return sessionDetailsString
|
||||
}
|
||||
}
|
||||
|
||||
extension UserSessionListItemViewData {
|
||||
init(session: UserSessionInfo) {
|
||||
self.init(sessionId: session.id,
|
||||
sessionDisplayName: session.name,
|
||||
deviceType: session.deviceType,
|
||||
isVerified: session.isVerified,
|
||||
lastActivityDate: session.lastSeenTimestamp)
|
||||
}
|
||||
let sessionDetailsIcon: String?
|
||||
}
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
//
|
||||
// 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
|
||||
|
||||
struct UserSessionListItemViewDataFactory {
|
||||
|
||||
private static let userSessionNameFormatter = UserSessionNameFormatter()
|
||||
private static let lastActivityDateFormatter = UserSessionLastActivityFormatter()
|
||||
private static let inactiveSessionDateFormatter = InactiveUserSessionLastActivityFormatter()
|
||||
|
||||
func create(from session: UserSessionInfo) -> UserSessionListItemViewData {
|
||||
let sessionName = UserSessionListItemViewDataFactory.userSessionNameFormatter.sessionName(deviceType: session.deviceType,
|
||||
sessionDisplayName: session.name)
|
||||
let sessionDetails = buildSessionDetails(isVerified: session.isVerified,
|
||||
lastActivityDate: session.lastSeenTimestamp,
|
||||
isActive: session.isActive)
|
||||
let deviceAvatarViewData = DeviceAvatarViewData(deviceType: session.deviceType,
|
||||
isVerified: session.isVerified)
|
||||
return UserSessionListItemViewData(sessionId: session.id,
|
||||
sessionName: sessionName,
|
||||
sessionDetails: sessionDetails,
|
||||
deviceAvatarViewData: deviceAvatarViewData,
|
||||
sessionDetailsIcon: getSessionDetailsIcon(isActive: session.isActive))
|
||||
}
|
||||
|
||||
private func buildSessionDetails(isVerified: Bool, lastActivityDate: TimeInterval?, isActive: Bool) -> String {
|
||||
if isActive {
|
||||
return activeSessionDetails(isVerified: isVerified, lastActivityDate: lastActivityDate)
|
||||
} else {
|
||||
return inactiveSessionDetails(lastActivityDate: lastActivityDate)
|
||||
}
|
||||
}
|
||||
|
||||
private func inactiveSessionDetails(lastActivityDate: TimeInterval?) -> String {
|
||||
if let lastActivityDate = lastActivityDate {
|
||||
let lastActivityDateString = Self.inactiveSessionDateFormatter.lastActivityDateString(from: lastActivityDate)
|
||||
return "Inactive for 90+ days (\(lastActivityDateString))"
|
||||
}
|
||||
return "Inactive for 90+ days"
|
||||
}
|
||||
|
||||
private func activeSessionDetails(isVerified: Bool, lastActivityDate: TimeInterval?) -> String {
|
||||
let sessionDetailsString: String
|
||||
|
||||
let sessionStatusText = isVerified ? VectorL10n.userSessionVerifiedShort : VectorL10n.userSessionUnverifiedShort
|
||||
|
||||
var lastActivityDateString: String?
|
||||
|
||||
if let lastActivityDate = lastActivityDate {
|
||||
lastActivityDateString = Self.lastActivityDateFormatter.lastActivityDateString(from: lastActivityDate)
|
||||
}
|
||||
|
||||
if let lastActivityDateString = lastActivityDateString, lastActivityDateString.isEmpty == false {
|
||||
sessionDetailsString = VectorL10n.userSessionItemDetails(sessionStatusText, lastActivityDateString)
|
||||
} else {
|
||||
sessionDetailsString = sessionStatusText
|
||||
}
|
||||
|
||||
return sessionDetailsString
|
||||
}
|
||||
|
||||
private func getSessionDetailsIcon(isActive: Bool) -> String? {
|
||||
isActive ? nil : Asset.Images.userSessionListItemInactiveSession.name
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue