mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-22 22:25:57 +00:00
Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios
This commit is contained in:
@@ -6998,8 +6998,8 @@ Sorry for the inconvenience.";
|
|||||||
"MessageCalendar.Title" = "Calendar";
|
"MessageCalendar.Title" = "Calendar";
|
||||||
"MessageCalendar.DaysSelectedTitle_1" = "1 day selected";
|
"MessageCalendar.DaysSelectedTitle_1" = "1 day selected";
|
||||||
"MessageCalendar.DaysSelectedTitle_any" = "%@ days selected";
|
"MessageCalendar.DaysSelectedTitle_any" = "%@ days selected";
|
||||||
"MessageCalendar.DeleteConfirmation_1" = "Are you sure you want to delete all messages for the selected day?";
|
"MessageCalendar.DeleteAlertText_1" = "Are you sure you want to delete all messages for the selected day?";
|
||||||
"MessageCalendar.DeleteConfirmation_1" = "Are you sure you want to delete all messages for the selected %@ days?";
|
"MessageCalendar.DeleteAlertText_any" = "Are you sure you want to delete all messages for the selected %@ days?";
|
||||||
|
|
||||||
"SharedMedia.PhotoCount_1" = "1 photo";
|
"SharedMedia.PhotoCount_1" = "1 photo";
|
||||||
"SharedMedia.PhotoCount_any" = "%@ photos";
|
"SharedMedia.PhotoCount_any" = "%@ photos";
|
||||||
|
|||||||
@@ -1399,7 +1399,7 @@ public final class CalendarMessageScreen: ViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let _ = info.canClearForMyself ?? info.canClearForEveryone {
|
if let _ = info.canClearForMyself ?? info.canClearForEveryone {
|
||||||
items.append(ActionSheetTextItem(title: strongSelf.presentationData.strings.MessageCalendar_DeleteConfirmation(Int32(selectedCount))))
|
items.append(ActionSheetTextItem(title: strongSelf.presentationData.strings.MessageCalendar_DeleteAlertText(Int32(selectedCount))))
|
||||||
|
|
||||||
if let canClearForEveryone = info.canClearForEveryone {
|
if let canClearForEveryone = info.canClearForEveryone {
|
||||||
let text: String
|
let text: String
|
||||||
|
|||||||
@@ -107,7 +107,13 @@ public final class GridMessageSelectionLayer: CALayer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func updateLayout(size: CGSize) {
|
public func updateLayout(size: CGSize) {
|
||||||
let checkSize = CGSize(width: 28.0, height: 28.0)
|
let checkWidth: CGFloat
|
||||||
|
if size.width <= 60.0 {
|
||||||
|
checkWidth = 22.0
|
||||||
|
} else {
|
||||||
|
checkWidth = 28.0
|
||||||
|
}
|
||||||
|
let checkSize = CGSize(width: checkWidth, height: checkWidth)
|
||||||
let previousSize = self.checkLayer.bounds.size
|
let previousSize = self.checkLayer.bounds.size
|
||||||
self.checkLayer.frame = CGRect(origin: CGPoint(x: self.bounds.size.width - checkSize.width - 2.0, y: 2.0), size: checkSize)
|
self.checkLayer.frame = CGRect(origin: CGPoint(x: self.bounds.size.width - checkSize.width - 2.0, y: 2.0), size: checkSize)
|
||||||
if self.checkLayer.bounds.size != previousSize {
|
if self.checkLayer.bounds.size != previousSize {
|
||||||
|
|||||||
@@ -64,21 +64,33 @@ private enum RecentSessionsEntryStableId: Hashable {
|
|||||||
case devicesInfo
|
case devicesInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private struct SortIndex: Comparable {
|
||||||
|
var section: Int
|
||||||
|
var item: Int
|
||||||
|
|
||||||
|
static func <(lhs: SortIndex, rhs: SortIndex) -> Bool {
|
||||||
|
if lhs.section != rhs.section {
|
||||||
|
return lhs.section < rhs.section
|
||||||
|
}
|
||||||
|
return lhs.item < rhs.item
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private enum RecentSessionsEntry: ItemListNodeEntry {
|
private enum RecentSessionsEntry: ItemListNodeEntry {
|
||||||
case currentSessionHeader(PresentationTheme, String)
|
case currentSessionHeader(SortIndex, String)
|
||||||
case currentSession(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, RecentAccountSession)
|
case currentSession(SortIndex, PresentationStrings, PresentationDateTimeFormat, RecentAccountSession)
|
||||||
case terminateOtherSessions(PresentationTheme, String)
|
case terminateOtherSessions(SortIndex, String)
|
||||||
case terminateAllWebSessions(PresentationTheme, String)
|
case terminateAllWebSessions(SortIndex, String)
|
||||||
case currentAddDevice(PresentationTheme, String)
|
case currentAddDevice(SortIndex, String)
|
||||||
case currentSessionInfo(PresentationTheme, String)
|
case currentSessionInfo(SortIndex, String)
|
||||||
case pendingSessionsHeader(PresentationTheme, String)
|
case pendingSessionsHeader(SortIndex, String)
|
||||||
case pendingSession(index: Int32, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, session: RecentAccountSession, enabled: Bool, editing: Bool, revealed: Bool)
|
case pendingSession(index: Int32, sortIndex: SortIndex, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, session: RecentAccountSession, enabled: Bool, editing: Bool, revealed: Bool)
|
||||||
case pendingSessionsInfo(PresentationTheme, String)
|
case pendingSessionsInfo(SortIndex, String)
|
||||||
case otherSessionsHeader(PresentationTheme, String)
|
case otherSessionsHeader(SortIndex, String)
|
||||||
case addDevice(PresentationTheme, String)
|
case addDevice(SortIndex, String)
|
||||||
case session(index: Int32, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, session: RecentAccountSession, enabled: Bool, editing: Bool, revealed: Bool)
|
case session(index: Int32, sortIndex: SortIndex, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, session: RecentAccountSession, enabled: Bool, editing: Bool, revealed: Bool)
|
||||||
case website(index: Int32, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, website: WebAuthorization, peer: Peer?, enabled: Bool, editing: Bool, revealed: Bool)
|
case website(index: Int32, sortIndex: SortIndex, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, website: WebAuthorization, peer: Peer?, enabled: Bool, editing: Bool, revealed: Bool)
|
||||||
case devicesInfo(PresentationTheme, String)
|
case devicesInfo(SortIndex, String)
|
||||||
|
|
||||||
var section: ItemListSectionId {
|
var section: ItemListSectionId {
|
||||||
switch self {
|
switch self {
|
||||||
@@ -102,7 +114,7 @@ private enum RecentSessionsEntry: ItemListNodeEntry {
|
|||||||
case .terminateAllWebSessions:
|
case .terminateAllWebSessions:
|
||||||
return .index(3)
|
return .index(3)
|
||||||
case .currentAddDevice:
|
case .currentAddDevice:
|
||||||
return .index(3)
|
return .index(4)
|
||||||
case .currentSessionInfo:
|
case .currentSessionInfo:
|
||||||
return .index(5)
|
return .index(5)
|
||||||
case .pendingSessionsHeader:
|
case .pendingSessionsHeader:
|
||||||
@@ -124,88 +136,121 @@ private enum RecentSessionsEntry: ItemListNodeEntry {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var sortIndex: SortIndex {
|
||||||
|
switch self {
|
||||||
|
case let .currentSessionHeader(index, _):
|
||||||
|
return index
|
||||||
|
case let .currentSession(index, _, _, _):
|
||||||
|
return index
|
||||||
|
case let .terminateOtherSessions(index, _):
|
||||||
|
return index
|
||||||
|
case let .terminateAllWebSessions(index, _):
|
||||||
|
return index
|
||||||
|
case let .currentAddDevice(index, _):
|
||||||
|
return index
|
||||||
|
case let .currentSessionInfo(index, _):
|
||||||
|
return index
|
||||||
|
case let .pendingSessionsHeader(index, _):
|
||||||
|
return index
|
||||||
|
case let .pendingSession(_, index, _, _, _, _, _, _):
|
||||||
|
return index
|
||||||
|
case let .pendingSessionsInfo(index, _):
|
||||||
|
return index
|
||||||
|
case let .otherSessionsHeader(index, _):
|
||||||
|
return index
|
||||||
|
case let .addDevice(index, _):
|
||||||
|
return index
|
||||||
|
case let .session(_, index, _, _, _, _, _, _):
|
||||||
|
return index
|
||||||
|
case let .website(_, index, _, _, _, _, _, _, _, _):
|
||||||
|
return index
|
||||||
|
case let .devicesInfo(index, _):
|
||||||
|
return index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static func ==(lhs: RecentSessionsEntry, rhs: RecentSessionsEntry) -> Bool {
|
static func ==(lhs: RecentSessionsEntry, rhs: RecentSessionsEntry) -> Bool {
|
||||||
switch lhs {
|
switch lhs {
|
||||||
case let .currentSessionHeader(lhsTheme, lhsText):
|
case let .currentSessionHeader(lhsSortIndex, lhsText):
|
||||||
if case let .currentSessionHeader(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
if case let .currentSessionHeader(rhsSortIndex, rhsText) = rhs, lhsSortIndex == rhsSortIndex, lhsText == rhsText {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case let .terminateOtherSessions(lhsTheme, lhsText):
|
case let .terminateOtherSessions(lhsSortIndex, lhsText):
|
||||||
if case let .terminateOtherSessions(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
if case let .terminateOtherSessions(rhsSortIndex, rhsText) = rhs, lhsSortIndex == rhsSortIndex, lhsText == rhsText {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case let .terminateAllWebSessions(lhsTheme, lhsText):
|
case let .terminateAllWebSessions(lhsSortIndex, lhsText):
|
||||||
if case let .terminateAllWebSessions(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
if case let .terminateAllWebSessions(rhsSortIndex, rhsText) = rhs, lhsSortIndex == rhsSortIndex, lhsText == rhsText {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case let .currentAddDevice(lhsTheme, lhsText):
|
case let .currentAddDevice(lhsSortIndex, lhsText):
|
||||||
if case let .currentAddDevice(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
if case let .currentAddDevice(rhsSortIndex, rhsText) = rhs, lhsSortIndex == rhsSortIndex, lhsText == rhsText {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case let .currentSessionInfo(lhsTheme, lhsText):
|
case let .currentSessionInfo(lhsSortIndex, lhsText):
|
||||||
if case let .currentSessionInfo(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
if case let .currentSessionInfo(rhsSortIndex, rhsText) = rhs, lhsSortIndex == rhsSortIndex, lhsText == rhsText {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case let .pendingSessionsHeader(lhsTheme, lhsText):
|
case let .pendingSessionsHeader(lhsSortIndex, lhsText):
|
||||||
if case let .pendingSessionsHeader(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
if case let .pendingSessionsHeader(rhsSortIndex, rhsText) = rhs, lhsSortIndex == rhsSortIndex, lhsText == rhsText {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case let .pendingSession(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsSession, lhsEnabled, lhsEditing, lhsRevealed):
|
case let .pendingSession(lhsIndex, lhsSortIndex, lhsStrings, lhsDateTimeFormat, lhsSession, lhsEnabled, lhsEditing, lhsRevealed):
|
||||||
if case let .pendingSession(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsSession, rhsEnabled, rhsEditing, rhsRevealed) = rhs, lhsIndex == rhsIndex, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsSession == rhsSession, lhsEnabled == rhsEnabled, lhsEditing == rhsEditing, lhsRevealed == rhsRevealed {
|
if case let .pendingSession(rhsIndex, rhsSortIndex, rhsStrings, rhsDateTimeFormat, rhsSession, rhsEnabled, rhsEditing, rhsRevealed) = rhs, lhsIndex == rhsIndex, lhsSortIndex == rhsSortIndex, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsSession == rhsSession, lhsEnabled == rhsEnabled, lhsEditing == rhsEditing, lhsRevealed == rhsRevealed {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case let .pendingSessionsInfo(lhsTheme, lhsText):
|
case let .pendingSessionsInfo(lhsSortIndex, lhsText):
|
||||||
if case let .pendingSessionsInfo(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
if case let .pendingSessionsInfo(rhsSortIndex, rhsText) = rhs, lhsSortIndex == rhsSortIndex, lhsText == rhsText {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case let .otherSessionsHeader(lhsTheme, lhsText):
|
case let .otherSessionsHeader(lhsSortIndex, lhsText):
|
||||||
if case let .otherSessionsHeader(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
if case let .otherSessionsHeader(rhsSortIndex, rhsText) = rhs, lhsSortIndex == rhsSortIndex, lhsText == rhsText {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case let .addDevice(lhsTheme, lhsText):
|
case let .addDevice(lhsSortIndex, lhsText):
|
||||||
if case let .addDevice(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
if case let .addDevice(rhsSortIndex, rhsText) = rhs, lhsSortIndex == rhsSortIndex, lhsText == rhsText {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case let .currentSession(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsSession):
|
case let .currentSession(lhsSortIndex, lhsStrings, lhsDateTimeFormat, lhsSession):
|
||||||
if case let .currentSession(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsSession) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsSession == rhsSession {
|
if case let .currentSession(rhsSortIndex, rhsStrings, rhsDateTimeFormat, rhsSession) = rhs, lhsSortIndex == rhsSortIndex, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsSession == rhsSession {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case let .session(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsSession, lhsEnabled, lhsEditing, lhsRevealed):
|
case let .session(lhsIndex, lhsSortIndex, lhsStrings, lhsDateTimeFormat, lhsSession, lhsEnabled, lhsEditing, lhsRevealed):
|
||||||
if case let .session(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsSession, rhsEnabled, rhsEditing, rhsRevealed) = rhs, lhsIndex == rhsIndex, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsSession == rhsSession, lhsEnabled == rhsEnabled, lhsEditing == rhsEditing, lhsRevealed == rhsRevealed {
|
if case let .session(rhsIndex, rhsSortIndex, rhsStrings, rhsDateTimeFormat, rhsSession, rhsEnabled, rhsEditing, rhsRevealed) = rhs, lhsIndex == rhsIndex, lhsSortIndex == rhsSortIndex, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsSession == rhsSession, lhsEnabled == rhsEnabled, lhsEditing == rhsEditing, lhsRevealed == rhsRevealed {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case let .website(lhsIndex, lhsTheme, lhsStrings, lhsDateTimeFormat, lhsNameOrder, lhsWebsite, lhsPeer, lhsEnabled, lhsEditing, lhsRevealed):
|
case let .website(lhsIndex, lhsSortIndex, lhsStrings, lhsDateTimeFormat, lhsNameOrder, lhsWebsite, lhsPeer, lhsEnabled, lhsEditing, lhsRevealed):
|
||||||
if case let .website(rhsIndex, rhsTheme, rhsStrings, rhsDateTimeFormat, rhsNameOrder, rhsWebsite, rhsPeer, rhsEnabled, rhsEditing, rhsRevealed) = rhs, lhsIndex == rhsIndex, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsNameOrder == rhsNameOrder, lhsWebsite == rhsWebsite, arePeersEqual(lhsPeer, rhsPeer), lhsEnabled == rhsEnabled, lhsEditing == rhsEditing, lhsRevealed == rhsRevealed {
|
if case let .website(rhsIndex, rhsSortIndex, rhsStrings, rhsDateTimeFormat, rhsNameOrder, rhsWebsite, rhsPeer, rhsEnabled, rhsEditing, rhsRevealed) = rhs, lhsIndex == rhsIndex, lhsSortIndex == rhsSortIndex, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat, lhsNameOrder == rhsNameOrder, lhsWebsite == rhsWebsite, arePeersEqual(lhsPeer, rhsPeer), lhsEnabled == rhsEnabled, lhsEditing == rhsEditing, lhsRevealed == rhsRevealed {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case let .devicesInfo(lhsTheme, lhsText):
|
case let .devicesInfo(lhsSortIndex, lhsText):
|
||||||
if case let .devicesInfo(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
if case let .devicesInfo(rhsSortIndex, rhsText) = rhs, lhsSortIndex == rhsSortIndex, lhsText == rhsText {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
@@ -214,59 +259,7 @@ private enum RecentSessionsEntry: ItemListNodeEntry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static func <(lhs: RecentSessionsEntry, rhs: RecentSessionsEntry) -> Bool {
|
static func <(lhs: RecentSessionsEntry, rhs: RecentSessionsEntry) -> Bool {
|
||||||
switch lhs.stableId {
|
return lhs.sortIndex < rhs.sortIndex
|
||||||
case let .index(lhsIndex):
|
|
||||||
if case let .index(rhsIndex) = rhs.stableId {
|
|
||||||
return lhsIndex <= rhsIndex
|
|
||||||
} else {
|
|
||||||
if case .pendingSession = rhs, lhsIndex > 5 {
|
|
||||||
return false
|
|
||||||
} else {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case .session:
|
|
||||||
switch lhs {
|
|
||||||
case let .session(lhsIndex, _, _, _, _, _, _, _):
|
|
||||||
if case let .session(rhsIndex, _, _, _, _, _, _, _) = rhs {
|
|
||||||
return lhsIndex <= rhsIndex
|
|
||||||
} else if case .devicesInfo = rhs.stableId {
|
|
||||||
return true
|
|
||||||
} else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
case let .pendingSession(lhsIndex, _, _, _, _, _, _, _):
|
|
||||||
if case let .pendingSession(rhsIndex, _, _, _, _, _, _, _) = rhs {
|
|
||||||
return lhsIndex <= rhsIndex
|
|
||||||
} else if case .session = rhs {
|
|
||||||
return true
|
|
||||||
} else if case .devicesInfo = rhs.stableId {
|
|
||||||
return true
|
|
||||||
} else {
|
|
||||||
if case let .index(rhsIndex) = rhs.stableId {
|
|
||||||
return rhsIndex == 6
|
|
||||||
} else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case let .website(lhsIndex, _, _, _, _, _, _, _, _, _):
|
|
||||||
if case let .website(rhsIndex, _, _, _, _, _, _, _, _, _) = rhs {
|
|
||||||
return lhsIndex <= rhsIndex
|
|
||||||
} else if case .devicesInfo = rhs.stableId {
|
|
||||||
return true
|
|
||||||
} else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
preconditionFailure()
|
|
||||||
}
|
|
||||||
case .devicesInfo:
|
|
||||||
if case .devicesInfo = rhs.stableId {
|
|
||||||
return false
|
|
||||||
} else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func item(presentationData: ItemListPresentationData, arguments: Any) -> ListViewItem {
|
func item(presentationData: ItemListPresentationData, arguments: Any) -> ListViewItem {
|
||||||
@@ -280,16 +273,16 @@ private enum RecentSessionsEntry: ItemListNodeEntry {
|
|||||||
}, action: {
|
}, action: {
|
||||||
arguments.openSession(session)
|
arguments.openSession(session)
|
||||||
})
|
})
|
||||||
case let .terminateOtherSessions(theme, text):
|
case let .terminateOtherSessions(_, text):
|
||||||
return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.blockDestructiveIcon(theme), title: text, sectionId: self.section, height: .generic, color: .destructive, editing: false, action: {
|
return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.blockDestructiveIcon(presentationData.theme), title: text, sectionId: self.section, height: .generic, color: .destructive, editing: false, action: {
|
||||||
arguments.terminateOtherSessions()
|
arguments.terminateOtherSessions()
|
||||||
})
|
})
|
||||||
case let .terminateAllWebSessions(theme, text):
|
case let .terminateAllWebSessions(_, text):
|
||||||
return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.blockDestructiveIcon(theme), title: text, sectionId: self.section, height: .generic, color: .destructive, editing: false, action: {
|
return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.blockDestructiveIcon(presentationData.theme), title: text, sectionId: self.section, height: .generic, color: .destructive, editing: false, action: {
|
||||||
arguments.terminateAllWebSessions()
|
arguments.terminateAllWebSessions()
|
||||||
})
|
})
|
||||||
case let .currentAddDevice(theme, text):
|
case let .currentAddDevice(_, text):
|
||||||
return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.addDeviceIcon(theme), title: text, sectionId: self.section, height: .generic, color: .accent, editing: false, action: {
|
return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.addDeviceIcon(presentationData.theme), title: text, sectionId: self.section, height: .generic, color: .accent, editing: false, action: {
|
||||||
arguments.addDevice()
|
arguments.addDevice()
|
||||||
})
|
})
|
||||||
case let .currentSessionInfo(_, text):
|
case let .currentSessionInfo(_, text):
|
||||||
@@ -313,8 +306,8 @@ private enum RecentSessionsEntry: ItemListNodeEntry {
|
|||||||
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
|
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
|
||||||
case let .otherSessionsHeader(_, text):
|
case let .otherSessionsHeader(_, text):
|
||||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
||||||
case let .addDevice(theme, text):
|
case let .addDevice(_, text):
|
||||||
return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.addDeviceIcon(theme), title: text, sectionId: self.section, height: .generic, color: .accent, editing: false, action: {
|
return ItemListPeerActionItem(presentationData: presentationData, icon: PresentationResourcesItemList.addDeviceIcon(presentationData.theme), title: text, sectionId: self.section, height: .generic, color: .accent, editing: false, action: {
|
||||||
arguments.addDevice()
|
arguments.addDevice()
|
||||||
})
|
})
|
||||||
case let .session(_, _, _, dateTimeFormat, session, enabled, editing, revealed):
|
case let .session(_, _, _, dateTimeFormat, session, enabled, editing, revealed):
|
||||||
@@ -403,41 +396,41 @@ private func recentSessionsControllerEntries(presentationData: PresentationData,
|
|||||||
|
|
||||||
if !sessionsState.sessions.isEmpty {
|
if !sessionsState.sessions.isEmpty {
|
||||||
var existingSessionIds = Set<Int64>()
|
var existingSessionIds = Set<Int64>()
|
||||||
entries.append(.currentSessionHeader(presentationData.theme, presentationData.strings.AuthSessions_CurrentSession))
|
entries.append(.currentSessionHeader(SortIndex(section: 0, item: 0), presentationData.strings.AuthSessions_CurrentSession))
|
||||||
if let index = sessionsState.sessions.firstIndex(where: { $0.hash == 0 }) {
|
if let index = sessionsState.sessions.firstIndex(where: { $0.hash == 0 }) {
|
||||||
existingSessionIds.insert(sessionsState.sessions[index].hash)
|
existingSessionIds.insert(sessionsState.sessions[index].hash)
|
||||||
entries.append(.currentSession(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, sessionsState.sessions[index]))
|
entries.append(.currentSession(SortIndex(section: 0, item: 1), presentationData.strings, presentationData.dateTimeFormat, sessionsState.sessions[index]))
|
||||||
}
|
}
|
||||||
|
|
||||||
var hasAddDevice = false
|
var hasAddDevice = false
|
||||||
if sessionsState.sessions.count > 1 || enableQRLogin {
|
if sessionsState.sessions.count > 1 || enableQRLogin {
|
||||||
if sessionsState.sessions.count > 1 {
|
if sessionsState.sessions.count > 1 {
|
||||||
entries.append(.terminateOtherSessions(presentationData.theme, presentationData.strings.AuthSessions_TerminateOtherSessions))
|
entries.append(.terminateOtherSessions(SortIndex(section: 0, item: 2), presentationData.strings.AuthSessions_TerminateOtherSessions))
|
||||||
entries.append(.currentSessionInfo(presentationData.theme, presentationData.strings.AuthSessions_TerminateOtherSessionsHelp))
|
entries.append(.currentSessionInfo(SortIndex(section: 0, item: 3), presentationData.strings.AuthSessions_TerminateOtherSessionsHelp))
|
||||||
} else if enableQRLogin {
|
} else if enableQRLogin {
|
||||||
hasAddDevice = true
|
hasAddDevice = true
|
||||||
entries.append(.currentAddDevice(presentationData.theme, presentationData.strings.AuthSessions_AddDevice))
|
entries.append(.currentAddDevice(SortIndex(section: 0, item: 4), presentationData.strings.AuthSessions_AddDevice))
|
||||||
entries.append(.currentSessionInfo(presentationData.theme, presentationData.strings.AuthSessions_OtherDevices))
|
entries.append(.currentSessionInfo(SortIndex(section: 0, item: 5), presentationData.strings.AuthSessions_OtherDevices))
|
||||||
}
|
}
|
||||||
|
|
||||||
let filteredPendingSessions: [RecentAccountSession] = sessionsState.sessions.filter({ $0.flags.contains(.passwordPending) })
|
let filteredPendingSessions: [RecentAccountSession] = sessionsState.sessions.filter({ $0.flags.contains(.passwordPending) })
|
||||||
if !filteredPendingSessions.isEmpty {
|
if !filteredPendingSessions.isEmpty {
|
||||||
entries.append(.pendingSessionsHeader(presentationData.theme, presentationData.strings.AuthSessions_IncompleteAttempts))
|
entries.append(.pendingSessionsHeader(SortIndex(section: 0, item: 6), presentationData.strings.AuthSessions_IncompleteAttempts))
|
||||||
for i in 0 ..< filteredPendingSessions.count {
|
for i in 0 ..< filteredPendingSessions.count {
|
||||||
if !existingSessionIds.contains(filteredPendingSessions[i].hash) {
|
if !existingSessionIds.contains(filteredPendingSessions[i].hash) {
|
||||||
existingSessionIds.insert(filteredPendingSessions[i].hash)
|
existingSessionIds.insert(filteredPendingSessions[i].hash)
|
||||||
entries.append(.pendingSession(index: Int32(i), theme: presentationData.theme, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, session: filteredPendingSessions[i], enabled: state.removingSessionId != filteredPendingSessions[i].hash && !state.terminatingOtherSessions, editing: state.editing, revealed: state.sessionIdWithRevealedOptions == filteredPendingSessions[i].hash))
|
entries.append(.pendingSession(index: Int32(i), sortIndex: SortIndex(section: 1, item: i), strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, session: filteredPendingSessions[i], enabled: state.removingSessionId != filteredPendingSessions[i].hash && !state.terminatingOtherSessions, editing: state.editing, revealed: state.sessionIdWithRevealedOptions == filteredPendingSessions[i].hash))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
entries.append(.pendingSessionsInfo(presentationData.theme, presentationData.strings.AuthSessions_IncompleteAttemptsInfo))
|
entries.append(.pendingSessionsInfo(SortIndex(section: 2, item: 0), presentationData.strings.AuthSessions_IncompleteAttemptsInfo))
|
||||||
}
|
}
|
||||||
|
|
||||||
if sessionsState.sessions.count > 1 {
|
if sessionsState.sessions.count > 1 {
|
||||||
entries.append(.otherSessionsHeader(presentationData.theme, presentationData.strings.AuthSessions_OtherSessions))
|
entries.append(.otherSessionsHeader(SortIndex(section: 3, item: 0), presentationData.strings.AuthSessions_OtherSessions))
|
||||||
}
|
}
|
||||||
|
|
||||||
if enableQRLogin && !hasAddDevice {
|
if enableQRLogin && !hasAddDevice {
|
||||||
entries.append(.addDevice(presentationData.theme, presentationData.strings.AuthSessions_AddDevice))
|
entries.append(.addDevice(SortIndex(section: 3, item: 1), presentationData.strings.AuthSessions_AddDevice))
|
||||||
}
|
}
|
||||||
|
|
||||||
let filteredSessions: [RecentAccountSession] = sessionsState.sessions.sorted(by: { lhs, rhs in
|
let filteredSessions: [RecentAccountSession] = sessionsState.sessions.sorted(by: { lhs, rhs in
|
||||||
@@ -447,12 +440,12 @@ private func recentSessionsControllerEntries(presentationData: PresentationData,
|
|||||||
for i in 0 ..< filteredSessions.count {
|
for i in 0 ..< filteredSessions.count {
|
||||||
if !existingSessionIds.contains(filteredSessions[i].hash) {
|
if !existingSessionIds.contains(filteredSessions[i].hash) {
|
||||||
existingSessionIds.insert(filteredSessions[i].hash)
|
existingSessionIds.insert(filteredSessions[i].hash)
|
||||||
entries.append(.session(index: Int32(i), theme: presentationData.theme, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, session: filteredSessions[i], enabled: state.removingSessionId != filteredSessions[i].hash && !state.terminatingOtherSessions, editing: state.editing, revealed: state.sessionIdWithRevealedOptions == filteredSessions[i].hash))
|
entries.append(.session(index: Int32(i), sortIndex: SortIndex(section: 4, item: i), strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, session: filteredSessions[i], enabled: state.removingSessionId != filteredSessions[i].hash && !state.terminatingOtherSessions, editing: state.editing, revealed: state.sessionIdWithRevealedOptions == filteredSessions[i].hash))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if enableQRLogin && !hasAddDevice {
|
if enableQRLogin && !hasAddDevice {
|
||||||
entries.append(.devicesInfo(presentationData.theme, presentationData.strings.AuthSessions_OtherDevices))
|
entries.append(.devicesInfo(SortIndex(section: 5, item: 0), presentationData.strings.AuthSessions_OtherDevices))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -466,10 +459,10 @@ private func recentSessionsControllerEntries(presentationData: PresentationData,
|
|||||||
if let websites = websites, let peers = peers {
|
if let websites = websites, let peers = peers {
|
||||||
var existingSessionIds = Set<Int64>()
|
var existingSessionIds = Set<Int64>()
|
||||||
if websites.count > 0 {
|
if websites.count > 0 {
|
||||||
entries.append(.terminateAllWebSessions(presentationData.theme, presentationData.strings.AuthSessions_LogOutApplications))
|
entries.append(.terminateAllWebSessions(SortIndex(section: 0, item: 0), presentationData.strings.AuthSessions_LogOutApplications))
|
||||||
entries.append(.currentSessionInfo(presentationData.theme, presentationData.strings.AuthSessions_LogOutApplicationsHelp))
|
entries.append(.currentSessionInfo(SortIndex(section: 0, item: 1), presentationData.strings.AuthSessions_LogOutApplicationsHelp))
|
||||||
|
|
||||||
entries.append(.otherSessionsHeader(presentationData.theme, presentationData.strings.AuthSessions_LoggedInWithTelegram))
|
entries.append(.otherSessionsHeader(SortIndex(section: 0, item: 2), presentationData.strings.AuthSessions_LoggedInWithTelegram))
|
||||||
|
|
||||||
let filteredWebsites: [WebAuthorization] = websites.sorted(by: { lhs, rhs in
|
let filteredWebsites: [WebAuthorization] = websites.sorted(by: { lhs, rhs in
|
||||||
return lhs.dateActive > rhs.dateActive
|
return lhs.dateActive > rhs.dateActive
|
||||||
@@ -479,7 +472,7 @@ private func recentSessionsControllerEntries(presentationData: PresentationData,
|
|||||||
let website = websites[i]
|
let website = websites[i]
|
||||||
if !existingSessionIds.contains(website.hash) {
|
if !existingSessionIds.contains(website.hash) {
|
||||||
existingSessionIds.insert(website.hash)
|
existingSessionIds.insert(website.hash)
|
||||||
entries.append(.website(index: Int32(i), theme: presentationData.theme, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, website: website, peer: peers[website.botId], enabled: state.removingSessionId != website.hash && !state.terminatingOtherSessions, editing: state.editing, revealed: state.sessionIdWithRevealedOptions == website.hash))
|
entries.append(.website(index: Int32(i), sortIndex: SortIndex(section: 1, item: i), strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, website: website, peer: peers[website.botId], enabled: state.removingSessionId != website.hash && !state.terminatingOtherSessions, editing: state.editing, revealed: state.sessionIdWithRevealedOptions == website.hash))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -809,6 +809,10 @@ public final class SparseItemGrid: ASDisplayNode {
|
|||||||
self.decelerationAnimator?.isPaused = false
|
self.decelerationAnimator?.isPaused = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func updateShimmerColors() {
|
||||||
|
self.updateVisibleItems(resetScrolling: false, synchronous: .none, restoreScrollPosition: nil)
|
||||||
|
}
|
||||||
|
|
||||||
private func updateVisibleItems(resetScrolling: Bool, synchronous: SparseItemGrid.Synchronous, restoreScrollPosition: (y: CGFloat, index: Int)?) {
|
private func updateVisibleItems(resetScrolling: Bool, synchronous: SparseItemGrid.Synchronous, restoreScrollPosition: (y: CGFloat, index: Int)?) {
|
||||||
guard let layout = self.layout, let items = self.items else {
|
guard let layout = self.layout, let items = self.items else {
|
||||||
return
|
return
|
||||||
@@ -1695,4 +1699,12 @@ public final class SparseItemGrid: ASDisplayNode {
|
|||||||
public func transferVelocity(_ velocity: CGFloat) {
|
public func transferVelocity(_ velocity: CGFloat) {
|
||||||
self.currentViewport?.transferVelocity(velocity)
|
self.currentViewport?.transferVelocity(velocity)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func updatePresentationData(theme: PresentationTheme) {
|
||||||
|
self.theme = theme
|
||||||
|
|
||||||
|
if let currentViewport = self.currentViewport {
|
||||||
|
currentViewport.updateShimmerColors()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -131,6 +131,129 @@ public final class LottieAnimationComponent: Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final class ScrollingTooltipAnimationComponent: Component {
|
||||||
|
public init() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func ==(lhs: ScrollingTooltipAnimationComponent, rhs: ScrollingTooltipAnimationComponent) -> Bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class View: UIView {
|
||||||
|
private var progress: CGFloat = 0.0
|
||||||
|
private var previousTarget: CGFloat = 0.0
|
||||||
|
|
||||||
|
private var animator: DisplayLinkAnimator?
|
||||||
|
|
||||||
|
init() {
|
||||||
|
super.init(frame: CGRect())
|
||||||
|
|
||||||
|
self.isOpaque = false
|
||||||
|
self.backgroundColor = nil
|
||||||
|
|
||||||
|
self.previousTarget = CGFloat.random(in: 0.0 ... 1.0)
|
||||||
|
self.startNextAnimation()
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder aDecoder: NSCoder) {
|
||||||
|
preconditionFailure()
|
||||||
|
}
|
||||||
|
|
||||||
|
func startNextAnimation() {
|
||||||
|
self.animator?.invalidate()
|
||||||
|
|
||||||
|
let previous = self.previousTarget
|
||||||
|
let target = CGFloat.random(in: 0.0 ... 1.0)
|
||||||
|
self.previousTarget = target
|
||||||
|
let animator = DisplayLinkAnimator(duration: 1.0, from: previous, to: target, update: { [weak self] value in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
strongSelf.progress = listViewAnimationCurveEaseInOut(value)
|
||||||
|
strongSelf.setNeedsDisplay()
|
||||||
|
}, completion: { [weak self] in
|
||||||
|
Queue.mainQueue().after(0.3, {
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
strongSelf.startNextAnimation()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
self.animator = animator
|
||||||
|
}
|
||||||
|
|
||||||
|
func update(component: ScrollingTooltipAnimationComponent, availableSize: CGSize, environment: Environment<Empty>, transition: Transition) -> CGSize {
|
||||||
|
return CGSize(width: 32.0, height: 32.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func draw(_ rect: CGRect) {
|
||||||
|
guard let context = UIGraphicsGetCurrentContext() else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let progressValue = self.progress
|
||||||
|
|
||||||
|
let itemSize: CGFloat = 12.0
|
||||||
|
let itemSpacing: CGFloat = 1.0
|
||||||
|
let listItemCount: CGFloat = 100.0
|
||||||
|
let listHeight: CGFloat = itemSize * listItemCount + itemSpacing * (listItemCount - 1)
|
||||||
|
|
||||||
|
context.setFillColor(UIColor(white: 1.0, alpha: 0.3).cgColor)
|
||||||
|
|
||||||
|
let offset: CGFloat = progressValue * listHeight
|
||||||
|
|
||||||
|
var minVisibleItemIndex: Int = Int(floor(offset / (itemSize + itemSpacing)))
|
||||||
|
while true {
|
||||||
|
let itemY: CGFloat = CGFloat(minVisibleItemIndex) * (itemSize + itemSpacing) - offset
|
||||||
|
if itemY >= self.bounds.height {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
for i in 0 ..< 2 {
|
||||||
|
UIBezierPath(roundedRect: CGRect(origin: CGPoint(x: CGFloat(i) * (itemSize + itemSpacing), y: itemY), size: CGSize(width: itemSize, height: itemSize)), cornerRadius: 2.0).fill()
|
||||||
|
}
|
||||||
|
minVisibleItemIndex += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
let gradientFraction: CGFloat = 10.0 / self.bounds.height
|
||||||
|
|
||||||
|
let colorsArray: [CGColor] = ([
|
||||||
|
UIColor(white: 1.0, alpha: 1.0),
|
||||||
|
UIColor(white: 1.0, alpha: 0.0),
|
||||||
|
UIColor(white: 1.0, alpha: 0.0),
|
||||||
|
UIColor(white: 1.0, alpha: 1.0)
|
||||||
|
] as [UIColor]).map(\.cgColor)
|
||||||
|
var locations: [CGFloat] = [0.0, gradientFraction, 1.0 - gradientFraction, 1.0]
|
||||||
|
let gradient = CGGradient(colorsSpace: deviceColorSpace, colors: colorsArray as CFArray, locations: &locations)!
|
||||||
|
context.setBlendMode(.destinationOut)
|
||||||
|
|
||||||
|
context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: 0.0, y: self.bounds.height), options: [])
|
||||||
|
|
||||||
|
context.setBlendMode(.normal)
|
||||||
|
context.setFillColor(UIColor.white.cgColor)
|
||||||
|
|
||||||
|
let indicatorHeight: CGFloat = 10.0
|
||||||
|
|
||||||
|
let indicatorMinY: CGFloat = 0.0
|
||||||
|
let indicatorMaxY: CGFloat = self.bounds.height - indicatorHeight
|
||||||
|
let indicatorX: CGFloat = (itemSize + itemSpacing) * 2.0
|
||||||
|
let indicatorY = indicatorMinY * (1.0 - progress) + indicatorMaxY * progress
|
||||||
|
UIBezierPath(roundedRect: CGRect(origin: CGPoint(x: indicatorX, y: indicatorY), size: CGSize(width: 3.0, height: indicatorHeight)), cornerRadius: 1.5).fill()
|
||||||
|
|
||||||
|
UIBezierPath(roundedRect: CGRect(x: indicatorX - 4.0 - 19.0, y: indicatorY + (indicatorHeight - 8.0) / 2.0, width: 19.0, height: 8.0), cornerRadius: 4.0).fill()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func makeView() -> View {
|
||||||
|
return View()
|
||||||
|
}
|
||||||
|
|
||||||
|
public func update(view: View, availableSize: CGSize, environment: Environment<Empty>, transition: Transition) -> CGSize {
|
||||||
|
return view.update(component: self, availableSize: availableSize, environment: environment, transition: transition)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public final class TooltipComponent: Component {
|
public final class TooltipComponent: Component {
|
||||||
public let icon: AnyComponent<Empty>?
|
public let icon: AnyComponent<Empty>?
|
||||||
public let content: AnyComponent<Empty>
|
public let content: AnyComponent<Empty>
|
||||||
@@ -942,9 +1065,12 @@ public final class SparseItemGridScrollingArea: ASDisplayNode {
|
|||||||
|
|
||||||
displayTooltip.completed()
|
displayTooltip.completed()
|
||||||
|
|
||||||
|
//#if DEBUG
|
||||||
|
//#else
|
||||||
Queue.mainQueue().after(2.0, { [weak self] in
|
Queue.mainQueue().after(2.0, { [weak self] in
|
||||||
self?.dismissLineTooltip()
|
self?.dismissLineTooltip()
|
||||||
})
|
})
|
||||||
|
//#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
private func updateLineTooltip(containerSize: CGSize) {
|
private func updateLineTooltip(containerSize: CGSize) {
|
||||||
@@ -958,9 +1084,7 @@ public final class SparseItemGridScrollingArea: ASDisplayNode {
|
|||||||
transition: .immediate,
|
transition: .immediate,
|
||||||
component: AnyComponent(TooltipComponent(
|
component: AnyComponent(TooltipComponent(
|
||||||
icon: displayTooltip.animation.flatMap { animation in
|
icon: displayTooltip.animation.flatMap { animation in
|
||||||
AnyComponent(LottieAnimationComponent(
|
AnyComponent(ScrollingTooltipAnimationComponent())
|
||||||
name: animation
|
|
||||||
))
|
|
||||||
},
|
},
|
||||||
content: AnyComponent(MultilineText(
|
content: AnyComponent(MultilineText(
|
||||||
text: displayTooltip.text,
|
text: displayTooltip.text,
|
||||||
|
|||||||
12
submodules/TelegramUI/Images.xcassets/Chat/GridPlayIcon.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Chat/GridPlayIcon.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "playsmall.pdf",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
73
submodules/TelegramUI/Images.xcassets/Chat/GridPlayIcon.imageset/playsmall.pdf
vendored
Normal file
73
submodules/TelegramUI/Images.xcassets/Chat/GridPlayIcon.imageset/playsmall.pdf
vendored
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
%PDF-1.7
|
||||||
|
|
||||||
|
1 0 obj
|
||||||
|
<< >>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
2 0 obj
|
||||||
|
<< /Length 3 0 R >>
|
||||||
|
stream
|
||||||
|
/DeviceRGB CS
|
||||||
|
/DeviceRGB cs
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 0.000000 0.283813 cm
|
||||||
|
1.000000 1.000000 1.000000 scn
|
||||||
|
1.533992 9.134618 m
|
||||||
|
0.868123 9.555166 0.000000 9.076683 0.000000 8.289128 c
|
||||||
|
0.000000 0.717780 l
|
||||||
|
0.000000 -0.069775 0.868124 -0.548255 1.533993 -0.127707 c
|
||||||
|
7.527976 3.657967 l
|
||||||
|
8.149319 4.050394 8.149319 4.956517 7.527976 5.348944 c
|
||||||
|
1.533992 9.134618 l
|
||||||
|
h
|
||||||
|
f*
|
||||||
|
n
|
||||||
|
Q
|
||||||
|
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
3 0 obj
|
||||||
|
379
|
||||||
|
endobj
|
||||||
|
|
||||||
|
4 0 obj
|
||||||
|
<< /Annots []
|
||||||
|
/Type /Page
|
||||||
|
/MediaBox [ 0.000000 0.000000 7.994019 9.574585 ]
|
||||||
|
/Resources 1 0 R
|
||||||
|
/Contents 2 0 R
|
||||||
|
/Parent 5 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
5 0 obj
|
||||||
|
<< /Kids [ 4 0 R ]
|
||||||
|
/Count 1
|
||||||
|
/Type /Pages
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
6 0 obj
|
||||||
|
<< /Type /Catalog
|
||||||
|
/Pages 5 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
xref
|
||||||
|
0 7
|
||||||
|
0000000000 65535 f
|
||||||
|
0000000010 00000 n
|
||||||
|
0000000034 00000 n
|
||||||
|
0000000469 00000 n
|
||||||
|
0000000491 00000 n
|
||||||
|
0000000662 00000 n
|
||||||
|
0000000736 00000 n
|
||||||
|
trailer
|
||||||
|
<< /ID [ (some) (id) ]
|
||||||
|
/Root 6 0 R
|
||||||
|
/Size 7
|
||||||
|
>>
|
||||||
|
startxref
|
||||||
|
795
|
||||||
|
%%EOF
|
||||||
@@ -21,6 +21,7 @@ import ComponentFlow
|
|||||||
import TelegramNotices
|
import TelegramNotices
|
||||||
import TelegramUIPreferences
|
import TelegramUIPreferences
|
||||||
import CheckNode
|
import CheckNode
|
||||||
|
import AppBundle
|
||||||
|
|
||||||
private final class FrameSequenceThumbnailNode: ASDisplayNode {
|
private final class FrameSequenceThumbnailNode: ASDisplayNode {
|
||||||
private let context: AccountContext
|
private let context: AccountContext
|
||||||
@@ -711,6 +712,19 @@ private struct Month: Equatable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private let durationFont = Font.regular(12.0)
|
private let durationFont = Font.regular(12.0)
|
||||||
|
private let minDurationImage: UIImage = {
|
||||||
|
let image = generateImage(CGSize(width: 20.0, height: 20.0), rotatedContext: { size, context in
|
||||||
|
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||||
|
context.setFillColor(UIColor(white: 0.0, alpha: 0.5).cgColor)
|
||||||
|
context.fillEllipse(in: CGRect(origin: CGPoint(), size: size))
|
||||||
|
if let image = UIImage(bundleImageName: "Chat/GridPlayIcon") {
|
||||||
|
UIGraphicsPushContext(context)
|
||||||
|
image.draw(in: CGRect(origin: CGPoint(x: (size.width - image.size.width) / 2.0, y: (size.height - image.size.height) / 2.0), size: image.size))
|
||||||
|
UIGraphicsPopContext()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return image!
|
||||||
|
}()
|
||||||
|
|
||||||
private final class DurationLayer: CALayer {
|
private final class DurationLayer: CALayer {
|
||||||
override init() {
|
override init() {
|
||||||
@@ -728,7 +742,10 @@ private final class DurationLayer: CALayer {
|
|||||||
return nullAction
|
return nullAction
|
||||||
}
|
}
|
||||||
|
|
||||||
func update(duration: Int32) {
|
func update(duration: Int32, isMin: Bool) {
|
||||||
|
if isMin {
|
||||||
|
self.contents = minDurationImage.cgImage
|
||||||
|
} else {
|
||||||
let string = NSAttributedString(string: stringForDuration(duration), font: durationFont, textColor: .white)
|
let string = NSAttributedString(string: stringForDuration(duration), font: durationFont, textColor: .white)
|
||||||
let bounds = string.boundingRect(with: CGSize(width: 100.0, height: 100.0), options: .usesLineFragmentOrigin, context: nil)
|
let bounds = string.boundingRect(with: CGSize(width: 100.0, height: 100.0), options: .usesLineFragmentOrigin, context: nil)
|
||||||
let textSize = CGSize(width: ceil(bounds.width), height: ceil(bounds.height))
|
let textSize = CGSize(width: ceil(bounds.width), height: ceil(bounds.height))
|
||||||
@@ -750,11 +767,13 @@ private final class DurationLayer: CALayer {
|
|||||||
})
|
})
|
||||||
self.contents = image?.cgImage
|
self.contents = image?.cgImage
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class ItemLayer: CALayer, SparseItemGridLayer {
|
private final class ItemLayer: CALayer, SparseItemGridLayer {
|
||||||
var item: VisualMediaItem?
|
var item: VisualMediaItem?
|
||||||
var durationLayer: DurationLayer?
|
var durationLayer: DurationLayer?
|
||||||
|
var minFactor: CGFloat = 1.0
|
||||||
var selectionLayer: GridMessageSelectionLayer?
|
var selectionLayer: GridMessageSelectionLayer?
|
||||||
var disposable: Disposable?
|
var disposable: Disposable?
|
||||||
|
|
||||||
@@ -782,15 +801,18 @@ private final class ItemLayer: CALayer, SparseItemGridLayer {
|
|||||||
self.item = item
|
self.item = item
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateDuration(duration: Int32?) {
|
func updateDuration(duration: Int32?, isMin: Bool, minFactor: CGFloat) {
|
||||||
|
self.minFactor = minFactor
|
||||||
|
|
||||||
if let duration = duration {
|
if let duration = duration {
|
||||||
if let durationLayer = self.durationLayer {
|
if let durationLayer = self.durationLayer {
|
||||||
durationLayer.update(duration: duration)
|
durationLayer.update(duration: duration, isMin: isMin)
|
||||||
} else {
|
} else {
|
||||||
let durationLayer = DurationLayer()
|
let durationLayer = DurationLayer()
|
||||||
durationLayer.update(duration: duration)
|
durationLayer.update(duration: duration, isMin: isMin)
|
||||||
self.addSublayer(durationLayer)
|
self.addSublayer(durationLayer)
|
||||||
durationLayer.frame = CGRect(origin: CGPoint(x: self.bounds.width - 3.0, y: self.bounds.height - 3.0), size: CGSize())
|
durationLayer.frame = CGRect(origin: CGPoint(x: self.bounds.width - 3.0, y: self.bounds.height - 3.0), size: CGSize())
|
||||||
|
durationLayer.transform = CATransform3DMakeScale(minFactor, minFactor, 1.0)
|
||||||
}
|
}
|
||||||
} else if let durationLayer = self.durationLayer {
|
} else if let durationLayer = self.durationLayer {
|
||||||
self.durationLayer = nil
|
self.durationLayer = nil
|
||||||
@@ -836,9 +858,9 @@ private final class ItemLayer: CALayer, SparseItemGridLayer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func update(size: CGSize) {
|
func update(size: CGSize) {
|
||||||
if let durationLayer = self.durationLayer {
|
/*if let durationLayer = self.durationLayer {
|
||||||
durationLayer.frame = CGRect(origin: CGPoint(x: size.width - 3.0, y: size.height - 3.0), size: CGSize())
|
durationLayer.frame = CGRect(origin: CGPoint(x: size.width - 3.0, y: size.height - 3.0), size: CGSize())
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1001,12 +1023,12 @@ private final class SparseItemGridBindingImpl: SparseItemGridBinding, ListShimme
|
|||||||
let context: AccountContext
|
let context: AccountContext
|
||||||
let chatLocation: ChatLocation
|
let chatLocation: ChatLocation
|
||||||
let directMediaImageCache: DirectMediaImageCache
|
let directMediaImageCache: DirectMediaImageCache
|
||||||
let strings: PresentationStrings
|
var strings: PresentationStrings
|
||||||
let useListItems: Bool
|
let useListItems: Bool
|
||||||
let listItemInteraction: ListMessageItemInteraction
|
let listItemInteraction: ListMessageItemInteraction
|
||||||
let chatControllerInteraction: ChatControllerInteraction
|
let chatControllerInteraction: ChatControllerInteraction
|
||||||
let chatPresentationData: ChatPresentationData
|
var chatPresentationData: ChatPresentationData
|
||||||
let checkNodeTheme: CheckNodeTheme
|
var checkNodeTheme: CheckNodeTheme
|
||||||
|
|
||||||
var loadHoleImpl: ((SparseItemGrid.HoleAnchor, SparseItemGrid.HoleLocation) -> Signal<Never, NoError>)?
|
var loadHoleImpl: ((SparseItemGrid.HoleAnchor, SparseItemGrid.HoleLocation) -> Signal<Never, NoError>)?
|
||||||
var onTapImpl: ((VisualMediaItem) -> Void)?
|
var onTapImpl: ((VisualMediaItem) -> Void)?
|
||||||
@@ -1036,6 +1058,15 @@ private final class SparseItemGridBindingImpl: SparseItemGridBinding, ListShimme
|
|||||||
self.checkNodeTheme = CheckNodeTheme(theme: presentationData.theme, style: .overlay, hasInset: true)
|
self.checkNodeTheme = CheckNodeTheme(theme: presentationData.theme, style: .overlay, hasInset: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func updatePresentationData(presentationData: PresentationData) {
|
||||||
|
self.strings = presentationData.strings
|
||||||
|
|
||||||
|
let themeData = ChatPresentationThemeData(theme: presentationData.theme, wallpaper: presentationData.chatWallpaper)
|
||||||
|
self.chatPresentationData = ChatPresentationData(theme: themeData, fontSize: presentationData.chatFontSize, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, disableAnimations: true, largeEmoji: presentationData.largeEmoji, chatBubbleCorners: presentationData.chatBubbleCorners, animatedEmojiScale: 1.0)
|
||||||
|
|
||||||
|
self.checkNodeTheme = CheckNodeTheme(theme: presentationData.theme, style: .overlay, hasInset: true)
|
||||||
|
}
|
||||||
|
|
||||||
func getListShimmerImage(height: CGFloat) -> UIImage {
|
func getListShimmerImage(height: CGFloat) -> UIImage {
|
||||||
if let image = self.shimmerImages[height] {
|
if let image = self.shimmerImages[height] {
|
||||||
return image
|
return image
|
||||||
@@ -1278,10 +1309,12 @@ private final class SparseItemGridBindingImpl: SparseItemGridBinding, ListShimme
|
|||||||
}
|
}
|
||||||
|
|
||||||
var duration: Int32?
|
var duration: Int32?
|
||||||
if layer.bounds.width > 80.0, let file = selectedMedia as? TelegramMediaFile, !file.isAnimated {
|
var isMin: Bool = false
|
||||||
|
if let file = selectedMedia as? TelegramMediaFile, !file.isAnimated {
|
||||||
duration = file.duration
|
duration = file.duration
|
||||||
|
isMin = layer.bounds.width < 80.0
|
||||||
}
|
}
|
||||||
layer.updateDuration(duration: duration)
|
layer.updateDuration(duration: duration, isMin: isMin, minFactor: min(1.0, layer.bounds.height / 74.0))
|
||||||
}
|
}
|
||||||
|
|
||||||
if let selectionState = self.chatControllerInteraction.selectionState {
|
if let selectionState = self.chatControllerInteraction.selectionState {
|
||||||
@@ -1455,6 +1488,9 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
|
|||||||
|
|
||||||
private weak var currentGestureItem: SparseItemGridDisplayItem?
|
private weak var currentGestureItem: SparseItemGridDisplayItem?
|
||||||
|
|
||||||
|
private var presentationData: PresentationData
|
||||||
|
private var presentationDataDisposable: Disposable?
|
||||||
|
|
||||||
init(context: AccountContext, chatControllerInteraction: ChatControllerInteraction, peerId: PeerId, contentType: ContentType) {
|
init(context: AccountContext, chatControllerInteraction: ChatControllerInteraction, peerId: PeerId, contentType: ContentType) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.peerId = peerId
|
self.peerId = peerId
|
||||||
@@ -1463,8 +1499,10 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
|
|||||||
self.contentTypePromise = ValuePromise<ContentType>(contentType)
|
self.contentTypePromise = ValuePromise<ContentType>(contentType)
|
||||||
self.stateTag = tagMaskForType(contentType)
|
self.stateTag = tagMaskForType(contentType)
|
||||||
|
|
||||||
|
self.presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
|
||||||
self.contextGestureContainerNode = ContextControllerSourceNode()
|
self.contextGestureContainerNode = ContextControllerSourceNode()
|
||||||
self.itemGrid = SparseItemGrid(theme: self.context.sharedContext.currentPresentationData.with({ $0 }).theme)
|
self.itemGrid = SparseItemGrid(theme: self.presentationData.theme)
|
||||||
self.directMediaImageCache = DirectMediaImageCache(account: context.account)
|
self.directMediaImageCache = DirectMediaImageCache(account: context.account)
|
||||||
|
|
||||||
let useListItems: Bool
|
let useListItems: Bool
|
||||||
@@ -1926,12 +1964,40 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
self.presentationDataDisposable = (self.context.sharedContext.presentationData
|
||||||
|
|> deliverOnMainQueue).start(next: { [weak self] presentationData in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
strongSelf.itemGridBinding.updatePresentationData(presentationData: presentationData)
|
||||||
|
|
||||||
|
strongSelf.itemGrid.updatePresentationData(theme: presentationData.theme)
|
||||||
|
|
||||||
|
strongSelf.itemGrid.forEachVisibleItem { item in
|
||||||
|
guard let itemView = item.view as? ItemView else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if let item = itemView.item {
|
||||||
|
itemView.bind(
|
||||||
|
item: item,
|
||||||
|
presentationData: strongSelf.itemGridBinding.chatPresentationData,
|
||||||
|
context: strongSelf.itemGridBinding.context,
|
||||||
|
chatLocation: strongSelf.itemGridBinding.chatLocation,
|
||||||
|
interaction: strongSelf.itemGridBinding.listItemInteraction,
|
||||||
|
isSelected: strongSelf.chatControllerInteraction.selectionState?.selectedIds.contains(item.message.id),
|
||||||
|
size: itemView.bounds.size
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
self.listDisposable.dispose()
|
self.listDisposable.dispose()
|
||||||
self.hiddenMediaDisposable?.dispose()
|
self.hiddenMediaDisposable?.dispose()
|
||||||
self.animationTimer?.invalidate()
|
self.animationTimer?.invalidate()
|
||||||
|
self.presentationDataDisposable?.dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadHole(anchor: SparseItemGrid.HoleAnchor, at location: SparseItemGrid.HoleLocation) -> Signal<Never, NoError> {
|
func loadHole(anchor: SparseItemGrid.HoleAnchor, at location: SparseItemGrid.HoleLocation) -> Signal<Never, NoError> {
|
||||||
|
|||||||
Reference in New Issue
Block a user