mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-11-07 01:10:09 +00:00
Stories
This commit is contained in:
parent
c302b7d4a5
commit
04b05b510a
@ -94,7 +94,6 @@ swift_library(
|
|||||||
"//submodules/QrCodeUI",
|
"//submodules/QrCodeUI",
|
||||||
"//submodules/TelegramUI/Components/ActionPanelComponent",
|
"//submodules/TelegramUI/Components/ActionPanelComponent",
|
||||||
"//submodules/TelegramUI/Components/Stories/StoryContainerScreen",
|
"//submodules/TelegramUI/Components/Stories/StoryContainerScreen",
|
||||||
"//submodules/TelegramUI/Components/Stories/StoryContentComponent",
|
|
||||||
"//submodules/TelegramUI/Components/Stories/StoryPeerListComponent",
|
"//submodules/TelegramUI/Components/Stories/StoryPeerListComponent",
|
||||||
"//submodules/TelegramUI/Components/FullScreenEffectView",
|
"//submodules/TelegramUI/Components/FullScreenEffectView",
|
||||||
"//submodules/TelegramUI/Components/Stories/AvatarStoryIndicatorComponent",
|
"//submodules/TelegramUI/Components/Stories/AvatarStoryIndicatorComponent",
|
||||||
|
|||||||
@ -46,7 +46,6 @@ import ChatListTitleView
|
|||||||
import InviteLinksUI
|
import InviteLinksUI
|
||||||
import ChatFolderLinkPreviewScreen
|
import ChatFolderLinkPreviewScreen
|
||||||
import StoryContainerScreen
|
import StoryContainerScreen
|
||||||
import StoryContentComponent
|
|
||||||
import FullScreenEffectView
|
import FullScreenEffectView
|
||||||
|
|
||||||
private final class ContextControllerContentSourceImpl: ContextControllerContentSource {
|
private final class ContextControllerContentSourceImpl: ContextControllerContentSource {
|
||||||
|
|||||||
@ -38,7 +38,6 @@ swift_library(
|
|||||||
"//submodules/QrCodeUI:QrCodeUI",
|
"//submodules/QrCodeUI:QrCodeUI",
|
||||||
"//submodules/LocalizedPeerData:LocalizedPeerData",
|
"//submodules/LocalizedPeerData:LocalizedPeerData",
|
||||||
"//submodules/TelegramUI/Components/Stories/StoryContainerScreen",
|
"//submodules/TelegramUI/Components/Stories/StoryContainerScreen",
|
||||||
"//submodules/TelegramUI/Components/Stories/StoryContentComponent",
|
|
||||||
"//submodules/TelegramUI/Components/Stories/StoryPeerListComponent",
|
"//submodules/TelegramUI/Components/Stories/StoryPeerListComponent",
|
||||||
"//submodules/TelegramUI/Components/ChatListTitleView",
|
"//submodules/TelegramUI/Components/ChatListTitleView",
|
||||||
"//submodules/TelegramUI/Components/ChatListHeaderComponent",
|
"//submodules/TelegramUI/Components/ChatListHeaderComponent",
|
||||||
|
|||||||
@ -20,7 +20,6 @@ import StickerResources
|
|||||||
import ContextUI
|
import ContextUI
|
||||||
import QrCodeUI
|
import QrCodeUI
|
||||||
import StoryContainerScreen
|
import StoryContainerScreen
|
||||||
import StoryContentComponent
|
|
||||||
import ChatListHeaderComponent
|
import ChatListHeaderComponent
|
||||||
|
|
||||||
private final class HeaderContextReferenceContentSource: ContextReferenceContentSource {
|
private final class HeaderContextReferenceContentSource: ContextReferenceContentSource {
|
||||||
|
|||||||
@ -25,7 +25,6 @@ swift_library(
|
|||||||
"//submodules/MediaResources:MediaResources",
|
"//submodules/MediaResources:MediaResources",
|
||||||
"//submodules/WebsiteType:WebsiteType",
|
"//submodules/WebsiteType:WebsiteType",
|
||||||
"//submodules/TelegramUI/Components/Stories/StoryContainerScreen",
|
"//submodules/TelegramUI/Components/Stories/StoryContainerScreen",
|
||||||
"//submodules/TelegramUI/Components/Stories/StoryContentComponent",
|
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
|||||||
@ -15,7 +15,6 @@ import GalleryUI
|
|||||||
import MediaResources
|
import MediaResources
|
||||||
import WebsiteType
|
import WebsiteType
|
||||||
import StoryContainerScreen
|
import StoryContainerScreen
|
||||||
import StoryContentComponent
|
|
||||||
|
|
||||||
public enum ChatMessageGalleryControllerData {
|
public enum ChatMessageGalleryControllerData {
|
||||||
case url(String)
|
case url(String)
|
||||||
|
|||||||
@ -598,19 +598,35 @@ public func notificationsPeerCategoryController(context: AccountContext, categor
|
|||||||
updateNotificationsView({})
|
updateNotificationsView({})
|
||||||
})
|
})
|
||||||
}, removePeerFromExceptions: {
|
}, removePeerFromExceptions: {
|
||||||
let _ = (
|
if case .stories = mode.mode {
|
||||||
context.engine.peers.removeCustomNotificationSettings(peerIds: [peerId])
|
let _ = (
|
||||||
|> map { _ -> EnginePeer? in }
|
context.engine.peers.removeCustomStoryNotificationSettings(peerIds: [peerId])
|
||||||
|> then(context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)))
|
|> map { _ -> EnginePeer? in }
|
||||||
).start(next: { peer in
|
|> then(context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)))
|
||||||
guard let peer = peer else {
|
).start(next: { peer in
|
||||||
return
|
guard let peer = peer else {
|
||||||
}
|
return
|
||||||
updateState { value in
|
}
|
||||||
return value.withUpdatedPeerDisplayPreviews(peer, .default).withUpdatedPeerSound(peer, .default).withUpdatedPeerMuteInterval(peer, nil)
|
updateState { value in
|
||||||
}
|
return value.withUpdatedPeerStoryNotifications(peer, .default)
|
||||||
updateNotificationsView({})
|
}
|
||||||
})
|
updateNotificationsView({})
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
let _ = (
|
||||||
|
context.engine.peers.removeCustomNotificationSettings(peerIds: [peerId])
|
||||||
|
|> map { _ -> EnginePeer? in }
|
||||||
|
|> then(context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)))
|
||||||
|
).start(next: { peer in
|
||||||
|
guard let peer = peer else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
updateState { value in
|
||||||
|
return value.withUpdatedPeerDisplayPreviews(peer, .default).withUpdatedPeerSound(peer, .default).withUpdatedPeerMuteInterval(peer, nil)
|
||||||
|
}
|
||||||
|
updateNotificationsView({})
|
||||||
|
})
|
||||||
|
}
|
||||||
}, modifiedPeer: {
|
}, modifiedPeer: {
|
||||||
|
|
||||||
}))
|
}))
|
||||||
@ -699,21 +715,39 @@ public func notificationsPeerCategoryController(context: AccountContext, categor
|
|||||||
|
|
||||||
let values = stateValue.with { $0.mode.settings.values }
|
let values = stateValue.with { $0.mode.settings.values }
|
||||||
|
|
||||||
let _ = (context.engine.peers.ensurePeersAreLocallyAvailable(peers: values.map { $0.peer })
|
if case .stories = mode.mode {
|
||||||
|> deliverOnMainQueue).start(completed: {
|
let _ = (context.engine.peers.ensurePeersAreLocallyAvailable(peers: values.map { $0.peer })
|
||||||
updateNotificationsDisposable.set(nil)
|
|
||||||
updateState { state in
|
|
||||||
var state = state
|
|
||||||
for value in values {
|
|
||||||
state = state.withUpdatedPeerMuteInterval(value.peer, nil).withUpdatedPeerSound(value.peer, .default).withUpdatedPeerDisplayPreviews(value.peer, .default)
|
|
||||||
}
|
|
||||||
return state
|
|
||||||
}
|
|
||||||
let _ = (context.engine.peers.removeCustomNotificationSettings(peerIds: values.map(\.peer.id))
|
|
||||||
|> deliverOnMainQueue).start(completed: {
|
|> deliverOnMainQueue).start(completed: {
|
||||||
updateNotificationsView({})
|
updateNotificationsDisposable.set(nil)
|
||||||
|
updateState { state in
|
||||||
|
var state = state
|
||||||
|
for value in values {
|
||||||
|
state = state.withUpdatedPeerStoryNotifications(value.peer, .default)
|
||||||
|
}
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
let _ = (context.engine.peers.removeCustomStoryNotificationSettings(peerIds: values.map(\.peer.id))
|
||||||
|
|> deliverOnMainQueue).start(completed: {
|
||||||
|
updateNotificationsView({})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
} else {
|
||||||
|
let _ = (context.engine.peers.ensurePeersAreLocallyAvailable(peers: values.map { $0.peer })
|
||||||
|
|> deliverOnMainQueue).start(completed: {
|
||||||
|
updateNotificationsDisposable.set(nil)
|
||||||
|
updateState { state in
|
||||||
|
var state = state
|
||||||
|
for value in values {
|
||||||
|
state = state.withUpdatedPeerMuteInterval(value.peer, nil).withUpdatedPeerSound(value.peer, .default).withUpdatedPeerDisplayPreviews(value.peer, .default)
|
||||||
|
}
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
let _ = (context.engine.peers.removeCustomNotificationSettings(peerIds: values.map(\.peer.id))
|
||||||
|
|> deliverOnMainQueue).start(completed: {
|
||||||
|
updateNotificationsView({})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
]), ActionSheetItemGroup(items: [
|
]), ActionSheetItemGroup(items: [
|
||||||
ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in
|
ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in
|
||||||
@ -726,17 +760,31 @@ public func notificationsPeerCategoryController(context: AccountContext, categor
|
|||||||
return current.withUpdatedRevealedPeerId(peerId)
|
return current.withUpdatedRevealedPeerId(peerId)
|
||||||
}
|
}
|
||||||
}, removePeer: { peer in
|
}, removePeer: { peer in
|
||||||
let _ = (context.engine.peers.ensurePeersAreLocallyAvailable(peers: [peer])
|
if case .stories = mode.mode {
|
||||||
|> deliverOnMainQueue).start(completed: {
|
let _ = (context.engine.peers.ensurePeersAreLocallyAvailable(peers: [peer])
|
||||||
updateNotificationsDisposable.set(nil)
|
|
||||||
updateState { value in
|
|
||||||
return value.withUpdatedPeerMuteInterval(peer, nil).withUpdatedPeerSound(peer, .default).withUpdatedPeerDisplayPreviews(peer, .default)
|
|
||||||
}
|
|
||||||
let _ = (context.engine.peers.removeCustomNotificationSettings(peerIds: [peer.id])
|
|
||||||
|> deliverOnMainQueue).start(completed: {
|
|> deliverOnMainQueue).start(completed: {
|
||||||
updateNotificationsView({})
|
updateNotificationsDisposable.set(nil)
|
||||||
|
updateState { value in
|
||||||
|
return value.withUpdatedPeerStoryNotifications(peer, .default)
|
||||||
|
}
|
||||||
|
let _ = (context.engine.peers.removeCustomStoryNotificationSettings(peerIds: [peer.id])
|
||||||
|
|> deliverOnMainQueue).start(completed: {
|
||||||
|
updateNotificationsView({})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
} else {
|
||||||
|
let _ = (context.engine.peers.ensurePeersAreLocallyAvailable(peers: [peer])
|
||||||
|
|> deliverOnMainQueue).start(completed: {
|
||||||
|
updateNotificationsDisposable.set(nil)
|
||||||
|
updateState { value in
|
||||||
|
return value.withUpdatedPeerMuteInterval(peer, nil).withUpdatedPeerSound(peer, .default).withUpdatedPeerDisplayPreviews(peer, .default)
|
||||||
|
}
|
||||||
|
let _ = (context.engine.peers.removeCustomNotificationSettings(peerIds: [peer.id])
|
||||||
|
|> deliverOnMainQueue).start(completed: {
|
||||||
|
updateNotificationsView({})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
}, updatedExceptionMode: { mode in
|
}, updatedExceptionMode: { mode in
|
||||||
_ = (notificationExceptions.get() |> take(1) |> deliverOnMainQueue).start(next: { (users, groups, channels, stories) in
|
_ = (notificationExceptions.get() |> take(1) |> deliverOnMainQueue).start(next: { (users, groups, channels, stories) in
|
||||||
switch mode {
|
switch mode {
|
||||||
|
|||||||
@ -1103,7 +1103,7 @@ func _internal_markStoryAsSeen(account: Account, peerId: PeerId, id: Int32, asPi
|
|||||||
|
|
||||||
account.stateManager.injectStoryUpdates(updates: [.read(peerId: peerId, maxId: id)])
|
account.stateManager.injectStoryUpdates(updates: [.read(peerId: peerId, maxId: id)])
|
||||||
|
|
||||||
#if DEBUG && true
|
#if DEBUG && false
|
||||||
if "".isEmpty {
|
if "".isEmpty {
|
||||||
return .complete()
|
return .complete()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -302,6 +302,15 @@ public extension TelegramEngine {
|
|||||||
}
|
}
|
||||||
|> ignoreValues
|
|> ignoreValues
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func removeCustomStoryNotificationSettings(peerIds: [PeerId]) -> Signal<Never, NoError> {
|
||||||
|
return self.account.postbox.transaction { transaction -> Void in
|
||||||
|
for peerId in peerIds {
|
||||||
|
_internal_updatePeerStoriesMutedSetting(account: self.account, transaction: transaction, peerId: peerId, isMuted: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|> ignoreValues
|
||||||
|
}
|
||||||
|
|
||||||
public func channelAdminEventLog(peerId: PeerId) -> ChannelAdminEventLogContext {
|
public func channelAdminEventLog(peerId: PeerId) -> ChannelAdminEventLogContext {
|
||||||
return ChannelAdminEventLogContext(postbox: self.account.postbox, network: self.account.network, peerId: peerId)
|
return ChannelAdminEventLogContext(postbox: self.account.postbox, network: self.account.network, peerId: peerId)
|
||||||
|
|||||||
@ -359,7 +359,6 @@ swift_library(
|
|||||||
"//submodules/TelegramUI/Components/ChatFolderLinkPreviewScreen",
|
"//submodules/TelegramUI/Components/ChatFolderLinkPreviewScreen",
|
||||||
"//submodules/TelegramUI/Components/SliderContextItem:SliderContextItem",
|
"//submodules/TelegramUI/Components/SliderContextItem:SliderContextItem",
|
||||||
"//submodules/TelegramUI/Components/Stories/StoryContainerScreen",
|
"//submodules/TelegramUI/Components/Stories/StoryContainerScreen",
|
||||||
"//submodules/TelegramUI/Components/Stories/StoryContentComponent",
|
|
||||||
"//submodules/TelegramUI/Components/CameraScreen",
|
"//submodules/TelegramUI/Components/CameraScreen",
|
||||||
"//submodules/TelegramUI/Components/MediaEditorScreen",
|
"//submodules/TelegramUI/Components/MediaEditorScreen",
|
||||||
"//submodules/TelegramUI/Components/ChatScheduleTimeController",
|
"//submodules/TelegramUI/Components/ChatScheduleTimeController",
|
||||||
|
|||||||
@ -249,12 +249,7 @@ public enum NotificationExceptionMode : Equatable {
|
|||||||
if let value = values[peerId] {
|
if let value = values[peerId] {
|
||||||
switch storyNotifications {
|
switch storyNotifications {
|
||||||
case .default:
|
case .default:
|
||||||
switch value.settings.storiesMuted {
|
values.removeValue(forKey: peerId)
|
||||||
case .none:
|
|
||||||
values.removeValue(forKey: peerId)
|
|
||||||
default:
|
|
||||||
values[peerId] = value.updateSettings({$0.withUpdatedStoriesMuted(storiesMuted)}).withUpdatedDate(Date().timeIntervalSince1970)
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
values[peerId] = value.updateSettings({$0.withUpdatedStoriesMuted(storiesMuted)}).withUpdatedDate(Date().timeIntervalSince1970)
|
values[peerId] = value.updateSettings({$0.withUpdatedStoriesMuted(storiesMuted)}).withUpdatedDate(Date().timeIntervalSince1970)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -36,7 +36,6 @@ swift_library(
|
|||||||
"//submodules/InvisibleInkDustNode",
|
"//submodules/InvisibleInkDustNode",
|
||||||
"//submodules/MediaPickerUI",
|
"//submodules/MediaPickerUI",
|
||||||
"//submodules/TelegramUI/Components/Stories/StoryContainerScreen",
|
"//submodules/TelegramUI/Components/Stories/StoryContainerScreen",
|
||||||
"//submodules/TelegramUI/Components/Stories/StoryContentComponent",
|
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
|||||||
@ -26,7 +26,6 @@ import AppBundle
|
|||||||
import InvisibleInkDustNode
|
import InvisibleInkDustNode
|
||||||
import MediaPickerUI
|
import MediaPickerUI
|
||||||
import StoryContainerScreen
|
import StoryContainerScreen
|
||||||
import StoryContentComponent
|
|
||||||
|
|
||||||
private let mediaBadgeBackgroundColor = UIColor(white: 0.0, alpha: 0.6)
|
private let mediaBadgeBackgroundColor = UIColor(white: 0.0, alpha: 0.6)
|
||||||
private let mediaBadgeTextColor = UIColor.white
|
private let mediaBadgeTextColor = UIColor.white
|
||||||
|
|||||||
@ -6,7 +6,6 @@ import SwiftSignalKit
|
|||||||
import AccountContext
|
import AccountContext
|
||||||
import TelegramCore
|
import TelegramCore
|
||||||
import Postbox
|
import Postbox
|
||||||
import StoryContainerScreen
|
|
||||||
|
|
||||||
private struct StoryKey: Hashable {
|
private struct StoryKey: Hashable {
|
||||||
var peerId: EnginePeer.Id
|
var peerId: EnginePeer.Id
|
||||||
@ -245,34 +244,27 @@ public final class StoryContentContextImpl: StoryContentContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let allItems = mappedItems.map { item in
|
||||||
|
return StoryContentItem(
|
||||||
|
position: nil,
|
||||||
|
peerId: peer.id,
|
||||||
|
storyItem: item
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
self.nextItems = nextItems
|
self.nextItems = nextItems
|
||||||
self.sliceValue = StoryContentContextState.FocusedSlice(
|
self.sliceValue = StoryContentContextState.FocusedSlice(
|
||||||
peer: peer,
|
peer: peer,
|
||||||
additionalPeerData: additionalPeerData,
|
additionalPeerData: additionalPeerData,
|
||||||
item: StoryContentItem(
|
item: StoryContentItem(
|
||||||
id: AnyHashable(mappedItem.id),
|
|
||||||
position: focusedIndex,
|
position: focusedIndex,
|
||||||
component: AnyComponent(StoryItemContentComponent(
|
|
||||||
context: context,
|
|
||||||
peer: peer,
|
|
||||||
item: mappedItem
|
|
||||||
)),
|
|
||||||
centerInfoComponent: AnyComponent(StoryAuthorInfoComponent(
|
|
||||||
context: context,
|
|
||||||
peer: peer,
|
|
||||||
timestamp: mappedItem.timestamp
|
|
||||||
)),
|
|
||||||
rightInfoComponent: AnyComponent(StoryAvatarInfoComponent(
|
|
||||||
context: context,
|
|
||||||
peer: peer
|
|
||||||
)),
|
|
||||||
peerId: peer.id,
|
peerId: peer.id,
|
||||||
storyItem: mappedItem,
|
storyItem: mappedItem
|
||||||
isMy: peerId == context.account.peerId
|
|
||||||
),
|
),
|
||||||
totalCount: mappedItems.count,
|
totalCount: mappedItems.count,
|
||||||
previousItemId: previousItemId,
|
previousItemId: previousItemId,
|
||||||
nextItemId: nextItemId
|
nextItemId: nextItemId,
|
||||||
|
allItems: allItems
|
||||||
)
|
)
|
||||||
self.isReady = true
|
self.isReady = true
|
||||||
self.updated.set(.single(Void()))
|
self.updated.set(.single(Void()))
|
||||||
@ -849,6 +841,10 @@ public final class StoryContentContextImpl: StoryContentContext {
|
|||||||
if let nextItemId = slice.nextItemId {
|
if let nextItemId = slice.nextItemId {
|
||||||
currentState.centralPeerContext.currentFocusedId = nextItemId
|
currentState.centralPeerContext.currentFocusedId = nextItemId
|
||||||
}
|
}
|
||||||
|
case let .id(id):
|
||||||
|
if slice.allItems.contains(where: { $0.storyItem.id == id }) {
|
||||||
|
currentState.centralPeerContext.currentFocusedId = id
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -961,34 +957,20 @@ public final class SingleStoryContentContextImpl: StoryContentContext {
|
|||||||
isCloseFriends: itemValue.isCloseFriends
|
isCloseFriends: itemValue.isCloseFriends
|
||||||
)
|
)
|
||||||
|
|
||||||
|
let mainItem = StoryContentItem(
|
||||||
|
position: 0,
|
||||||
|
peerId: peer.id,
|
||||||
|
storyItem: mappedItem
|
||||||
|
)
|
||||||
let stateValue = StoryContentContextState(
|
let stateValue = StoryContentContextState(
|
||||||
slice: StoryContentContextState.FocusedSlice(
|
slice: StoryContentContextState.FocusedSlice(
|
||||||
peer: peer,
|
peer: peer,
|
||||||
additionalPeerData: additionalPeerData,
|
additionalPeerData: additionalPeerData,
|
||||||
item: StoryContentItem(
|
item: mainItem,
|
||||||
id: AnyHashable(item.id),
|
|
||||||
position: 0,
|
|
||||||
component: AnyComponent(StoryItemContentComponent(
|
|
||||||
context: context,
|
|
||||||
peer: peer,
|
|
||||||
item: mappedItem
|
|
||||||
)),
|
|
||||||
centerInfoComponent: AnyComponent(StoryAuthorInfoComponent(
|
|
||||||
context: context,
|
|
||||||
peer: peer,
|
|
||||||
timestamp: item.timestamp
|
|
||||||
)),
|
|
||||||
rightInfoComponent: AnyComponent(StoryAvatarInfoComponent(
|
|
||||||
context: context,
|
|
||||||
peer: peer
|
|
||||||
)),
|
|
||||||
peerId: peer.id,
|
|
||||||
storyItem: mappedItem,
|
|
||||||
isMy: peer.id == context.account.peerId
|
|
||||||
),
|
|
||||||
totalCount: 1,
|
totalCount: 1,
|
||||||
previousItemId: nil,
|
previousItemId: nil,
|
||||||
nextItemId: nil
|
nextItemId: nil,
|
||||||
|
allItems: [mainItem]
|
||||||
),
|
),
|
||||||
previousSlice: nil,
|
previousSlice: nil,
|
||||||
nextSlice: nil
|
nextSlice: nil
|
||||||
@ -1124,34 +1106,27 @@ public final class PeerStoryListContentContextImpl: StoryContentContext {
|
|||||||
let item = state.items[focusedIndex]
|
let item = state.items[focusedIndex]
|
||||||
self.focusedId = item.id
|
self.focusedId = item.id
|
||||||
|
|
||||||
|
let allItems = state.items.map { stateItem -> StoryContentItem in
|
||||||
|
return StoryContentItem(
|
||||||
|
position: nil,
|
||||||
|
peerId: peer.id,
|
||||||
|
storyItem: stateItem
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
stateValue = StoryContentContextState(
|
stateValue = StoryContentContextState(
|
||||||
slice: StoryContentContextState.FocusedSlice(
|
slice: StoryContentContextState.FocusedSlice(
|
||||||
peer: peer,
|
peer: peer,
|
||||||
additionalPeerData: additionalPeerData,
|
additionalPeerData: additionalPeerData,
|
||||||
item: StoryContentItem(
|
item: StoryContentItem(
|
||||||
id: AnyHashable(item.id),
|
|
||||||
position: nil,
|
position: nil,
|
||||||
component: AnyComponent(StoryItemContentComponent(
|
|
||||||
context: context,
|
|
||||||
peer: peer,
|
|
||||||
item: item
|
|
||||||
)),
|
|
||||||
centerInfoComponent: AnyComponent(StoryPositionInfoComponent(
|
|
||||||
context: context,
|
|
||||||
position: focusedIndex,
|
|
||||||
totalCount: state.totalCount
|
|
||||||
)),
|
|
||||||
rightInfoComponent: AnyComponent(StoryAvatarInfoComponent(
|
|
||||||
context: context,
|
|
||||||
peer: peer
|
|
||||||
)),
|
|
||||||
peerId: peer.id,
|
peerId: peer.id,
|
||||||
storyItem: item,
|
storyItem: item
|
||||||
isMy: peerId == self.context.account.peerId
|
|
||||||
),
|
),
|
||||||
totalCount: state.totalCount,
|
totalCount: state.totalCount,
|
||||||
previousItemId: focusedIndex == 0 ? nil : state.items[focusedIndex - 1].id,
|
previousItemId: focusedIndex == 0 ? nil : state.items[focusedIndex - 1].id,
|
||||||
nextItemId: (focusedIndex == state.items.count - 1) ? nil : state.items[focusedIndex + 1].id
|
nextItemId: (focusedIndex == state.items.count - 1) ? nil : state.items[focusedIndex + 1].id,
|
||||||
|
allItems: allItems
|
||||||
),
|
),
|
||||||
previousSlice: nil,
|
previousSlice: nil,
|
||||||
nextSlice: nil
|
nextSlice: nil
|
||||||
@ -1283,15 +1258,19 @@ public final class PeerStoryListContentContextImpl: StoryContentContext {
|
|||||||
case .peer:
|
case .peer:
|
||||||
break
|
break
|
||||||
case let .item(direction):
|
case let .item(direction):
|
||||||
let indexDifference: Int
|
var indexDifference: Int?
|
||||||
switch direction {
|
switch direction {
|
||||||
case .next:
|
case .next:
|
||||||
indexDifference = 1
|
indexDifference = 1
|
||||||
case .previous:
|
case .previous:
|
||||||
indexDifference = -1
|
indexDifference = -1
|
||||||
|
case let .id(id):
|
||||||
|
if let listState = self.listState, let focusedId = self.focusedId, let index = listState.items.firstIndex(where: { $0.id == focusedId }), let nextIndex = listState.items.firstIndex(where: { $0.id == id }) {
|
||||||
|
indexDifference = nextIndex - index
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let listState = self.listState, let focusedId = self.focusedId {
|
if let indexDifference, let listState = self.listState, let focusedId = self.focusedId {
|
||||||
if let index = listState.items.firstIndex(where: { $0.id == focusedId }) {
|
if let index = listState.items.firstIndex(where: { $0.id == focusedId }) {
|
||||||
var nextIndex = index + indexDifference
|
var nextIndex = index + indexDifference
|
||||||
if nextIndex < 0 {
|
if nextIndex < 0 {
|
||||||
@ -75,14 +75,14 @@ private final class StoryContainerScreenComponent: Component {
|
|||||||
let content: StoryContentContext
|
let content: StoryContentContext
|
||||||
let focusedItemPromise: Promise<StoryId?>
|
let focusedItemPromise: Promise<StoryId?>
|
||||||
let transitionIn: StoryContainerScreen.TransitionIn?
|
let transitionIn: StoryContainerScreen.TransitionIn?
|
||||||
let transitionOut: (EnginePeer.Id, AnyHashable) -> StoryContainerScreen.TransitionOut?
|
let transitionOut: (EnginePeer.Id, Int32) -> StoryContainerScreen.TransitionOut?
|
||||||
|
|
||||||
init(
|
init(
|
||||||
context: AccountContext,
|
context: AccountContext,
|
||||||
content: StoryContentContext,
|
content: StoryContentContext,
|
||||||
focusedItemPromise: Promise<StoryId?>,
|
focusedItemPromise: Promise<StoryId?>,
|
||||||
transitionIn: StoryContainerScreen.TransitionIn?,
|
transitionIn: StoryContainerScreen.TransitionIn?,
|
||||||
transitionOut: @escaping (EnginePeer.Id, AnyHashable) -> StoryContainerScreen.TransitionOut?
|
transitionOut: @escaping (EnginePeer.Id, Int32) -> StoryContainerScreen.TransitionOut?
|
||||||
) {
|
) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.content = content
|
self.content = content
|
||||||
@ -322,7 +322,7 @@ private final class StoryContainerScreenComponent: Component {
|
|||||||
private func commitHorizontalPan(velocity: CGPoint) {
|
private func commitHorizontalPan(velocity: CGPoint) {
|
||||||
if var itemSetPanState = self.itemSetPanState {
|
if var itemSetPanState = self.itemSetPanState {
|
||||||
if let component = self.component, let stateValue = component.content.stateValue, let _ = stateValue.slice {
|
if let component = self.component, let stateValue = component.content.stateValue, let _ = stateValue.slice {
|
||||||
var direction: StoryContentContextNavigation.Direction?
|
var direction: StoryContentContextNavigation.PeerDirection?
|
||||||
if abs(velocity.x) > 10.0 {
|
if abs(velocity.x) > 10.0 {
|
||||||
if velocity.x < 0.0 {
|
if velocity.x < 0.0 {
|
||||||
if stateValue.nextSlice != nil {
|
if stateValue.nextSlice != nil {
|
||||||
@ -397,16 +397,25 @@ private final class StoryContainerScreenComponent: Component {
|
|||||||
let velocity = recognizer.velocity(in: self)
|
let velocity = recognizer.velocity(in: self)
|
||||||
|
|
||||||
self.verticalPanState = nil
|
self.verticalPanState = nil
|
||||||
self.state?.updated(transition: Transition(animation: .curve(duration: 0.3, curve: .spring)))
|
var updateState = true
|
||||||
|
|
||||||
if translation.y > 100.0 || velocity.y > 10.0 {
|
if translation.y > 100.0 || velocity.y > 10.0 {
|
||||||
|
self.state?.updated(transition: Transition(animation: .curve(duration: 0.3, curve: .spring)))
|
||||||
self.environment?.controller()?.dismiss()
|
self.environment?.controller()?.dismiss()
|
||||||
} else if translation.y < -100.0 || velocity.y < -40.0 {
|
} else if translation.y < -100.0 || velocity.y < -40.0 {
|
||||||
if let component = self.component, let stateValue = component.content.stateValue, let slice = stateValue.slice, let itemSetView = self.visibleItemSetViews[slice.peer.id] {
|
if let component = self.component, let stateValue = component.content.stateValue, let slice = stateValue.slice, let itemSetView = self.visibleItemSetViews[slice.peer.id] {
|
||||||
if let itemSetComponentView = itemSetView.view.view as? StoryItemSetContainerComponent.View {
|
if let itemSetComponentView = itemSetView.view.view as? StoryItemSetContainerComponent.View {
|
||||||
itemSetComponentView.activateInput()
|
if itemSetComponentView.activateInput() {
|
||||||
|
updateState = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if updateState || "".isEmpty {
|
||||||
|
self.state?.updated(transition: Transition(animation: .curve(duration: 0.3, curve: .spring)))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.state?.updated(transition: Transition(animation: .curve(duration: 0.3, curve: .spring)))
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
@ -532,7 +541,7 @@ private final class StoryContainerScreenComponent: Component {
|
|||||||
func animateOut(completion: @escaping () -> Void) {
|
func animateOut(completion: @escaping () -> Void) {
|
||||||
self.isAnimatingOut = true
|
self.isAnimatingOut = true
|
||||||
|
|
||||||
if !self.dismissWithoutTransitionOut, let component = self.component, let stateValue = component.content.stateValue, let slice = stateValue.slice, let itemSetView = self.visibleItemSetViews[slice.peer.id], let itemSetComponentView = itemSetView.view.view as? StoryItemSetContainerComponent.View, let transitionOut = component.transitionOut(slice.peer.id, slice.item.id) {
|
if !self.dismissWithoutTransitionOut, let component = self.component, let stateValue = component.content.stateValue, let slice = stateValue.slice, let itemSetView = self.visibleItemSetViews[slice.peer.id], let itemSetComponentView = itemSetView.view.view as? StoryItemSetContainerComponent.View, let transitionOut = component.transitionOut(slice.peer.id, slice.item.storyItem.id) {
|
||||||
self.state?.updated(transition: .immediate)
|
self.state?.updated(transition: .immediate)
|
||||||
|
|
||||||
let transition = Transition(animation: .curve(duration: 0.25, curve: .easeInOut))
|
let transition = Transition(animation: .curve(duration: 0.25, curve: .easeInOut))
|
||||||
@ -552,7 +561,7 @@ private final class StoryContainerScreenComponent: Component {
|
|||||||
focusedItemPromise.set(.single(nil))
|
focusedItemPromise.set(.single(nil))
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
if let component = self.component, let stateValue = component.content.stateValue, let slice = stateValue.slice, let transitionOut = component.transitionOut(slice.peer.id, slice.item.id) {
|
if let component = self.component, let stateValue = component.content.stateValue, let slice = stateValue.slice, let transitionOut = component.transitionOut(slice.peer.id, slice.item.storyItem.id) {
|
||||||
transitionOut.completed()
|
transitionOut.completed()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -828,12 +837,14 @@ private final class StoryContainerScreenComponent: Component {
|
|||||||
self.commitHorizontalPan(velocity: CGPoint(x: 100.0, y: 0.0))
|
self.commitHorizontalPan(velocity: CGPoint(x: 100.0, y: 0.0))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let mappedDirection: StoryContentContextNavigation.Direction
|
let mappedDirection: StoryContentContextNavigation.ItemDirection
|
||||||
switch direction {
|
switch direction {
|
||||||
case .previous:
|
case .previous:
|
||||||
mappedDirection = .previous
|
mappedDirection = .previous
|
||||||
case .next:
|
case .next:
|
||||||
mappedDirection = .next
|
mappedDirection = .next
|
||||||
|
case let .id(id):
|
||||||
|
mappedDirection = .id(id)
|
||||||
}
|
}
|
||||||
component.content.navigate(navigation: .item(mappedDirection))
|
component.content.navigate(navigation: .item(mappedDirection))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import TelegramCore
|
|||||||
import Postbox
|
import Postbox
|
||||||
import TelegramPresentationData
|
import TelegramPresentationData
|
||||||
|
|
||||||
public final class StoryContentItem {
|
public final class StoryContentItem: Equatable {
|
||||||
public final class ExternalState {
|
public final class ExternalState {
|
||||||
public init() {
|
public init() {
|
||||||
}
|
}
|
||||||
@ -66,61 +66,31 @@ public final class StoryContentItem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public let id: AnyHashable
|
|
||||||
public let position: Int?
|
public let position: Int?
|
||||||
public let component: AnyComponent<StoryContentItem.Environment>
|
|
||||||
public let centerInfoComponent: AnyComponent<Empty>?
|
|
||||||
public let rightInfoComponent: AnyComponent<Empty>?
|
|
||||||
public let peerId: EnginePeer.Id?
|
public let peerId: EnginePeer.Id?
|
||||||
public let storyItem: EngineStoryItem
|
public let storyItem: EngineStoryItem
|
||||||
public let isMy: Bool
|
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
id: AnyHashable,
|
|
||||||
position: Int?,
|
position: Int?,
|
||||||
component: AnyComponent<StoryContentItem.Environment>,
|
|
||||||
centerInfoComponent: AnyComponent<Empty>?,
|
|
||||||
rightInfoComponent: AnyComponent<Empty>?,
|
|
||||||
peerId: EnginePeer.Id?,
|
peerId: EnginePeer.Id?,
|
||||||
storyItem: EngineStoryItem,
|
storyItem: EngineStoryItem
|
||||||
isMy: Bool
|
|
||||||
) {
|
) {
|
||||||
self.id = id
|
|
||||||
self.position = position
|
self.position = position
|
||||||
self.component = component
|
|
||||||
self.centerInfoComponent = centerInfoComponent
|
|
||||||
self.rightInfoComponent = rightInfoComponent
|
|
||||||
self.peerId = peerId
|
self.peerId = peerId
|
||||||
self.storyItem = storyItem
|
self.storyItem = storyItem
|
||||||
self.isMy = isMy
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public static func ==(lhs: StoryContentItem, rhs: StoryContentItem) -> Bool {
|
||||||
public final class StoryContentItemSlice {
|
if lhs.position != rhs.position {
|
||||||
public let id: AnyHashable
|
return false
|
||||||
public let focusedItemId: AnyHashable?
|
}
|
||||||
public let items: [StoryContentItem]
|
if lhs.peerId != rhs.peerId {
|
||||||
public let totalCount: Int
|
return false
|
||||||
public let previousItemId: AnyHashable?
|
}
|
||||||
public let nextItemId: AnyHashable?
|
if lhs.storyItem != rhs.storyItem {
|
||||||
public let update: (StoryContentItemSlice, AnyHashable) -> Signal<StoryContentItemSlice, NoError>
|
return false
|
||||||
|
}
|
||||||
public init(
|
return true
|
||||||
id: AnyHashable,
|
|
||||||
focusedItemId: AnyHashable?,
|
|
||||||
items: [StoryContentItem],
|
|
||||||
totalCount: Int,
|
|
||||||
previousItemId: AnyHashable?,
|
|
||||||
nextItemId: AnyHashable?,
|
|
||||||
update: @escaping (StoryContentItemSlice, AnyHashable) -> Signal<StoryContentItemSlice, NoError>
|
|
||||||
) {
|
|
||||||
self.id = id
|
|
||||||
self.focusedItemId = focusedItemId
|
|
||||||
self.items = items
|
|
||||||
self.totalCount = totalCount
|
|
||||||
self.previousItemId = previousItemId
|
|
||||||
self.nextItemId = nextItemId
|
|
||||||
self.update = update
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,6 +119,7 @@ public final class StoryContentContextState {
|
|||||||
public let totalCount: Int
|
public let totalCount: Int
|
||||||
public let previousItemId: Int32?
|
public let previousItemId: Int32?
|
||||||
public let nextItemId: Int32?
|
public let nextItemId: Int32?
|
||||||
|
public let allItems: [StoryContentItem]
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
peer: EnginePeer,
|
peer: EnginePeer,
|
||||||
@ -156,7 +127,8 @@ public final class StoryContentContextState {
|
|||||||
item: StoryContentItem,
|
item: StoryContentItem,
|
||||||
totalCount: Int,
|
totalCount: Int,
|
||||||
previousItemId: Int32?,
|
previousItemId: Int32?,
|
||||||
nextItemId: Int32?
|
nextItemId: Int32?,
|
||||||
|
allItems: [StoryContentItem]
|
||||||
) {
|
) {
|
||||||
self.peer = peer
|
self.peer = peer
|
||||||
self.additionalPeerData = additionalPeerData
|
self.additionalPeerData = additionalPeerData
|
||||||
@ -164,6 +136,7 @@ public final class StoryContentContextState {
|
|||||||
self.totalCount = totalCount
|
self.totalCount = totalCount
|
||||||
self.previousItemId = previousItemId
|
self.previousItemId = previousItemId
|
||||||
self.nextItemId = nextItemId
|
self.nextItemId = nextItemId
|
||||||
|
self.allItems = allItems
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func ==(lhs: FocusedSlice, rhs: FocusedSlice) -> Bool {
|
public static func ==(lhs: FocusedSlice, rhs: FocusedSlice) -> Bool {
|
||||||
@ -173,7 +146,7 @@ public final class StoryContentContextState {
|
|||||||
if lhs.additionalPeerData != rhs.additionalPeerData {
|
if lhs.additionalPeerData != rhs.additionalPeerData {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if lhs.item.storyItem != rhs.item.storyItem {
|
if lhs.item != rhs.item {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if lhs.totalCount != rhs.totalCount {
|
if lhs.totalCount != rhs.totalCount {
|
||||||
@ -185,6 +158,9 @@ public final class StoryContentContextState {
|
|||||||
if lhs.nextItemId != rhs.nextItemId {
|
if lhs.nextItemId != rhs.nextItemId {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.allItems != rhs.allItems {
|
||||||
|
return false
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -205,13 +181,19 @@ public final class StoryContentContextState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public enum StoryContentContextNavigation {
|
public enum StoryContentContextNavigation {
|
||||||
public enum Direction {
|
public enum ItemDirection {
|
||||||
|
case previous
|
||||||
|
case next
|
||||||
|
case id(Int32)
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum PeerDirection {
|
||||||
case previous
|
case previous
|
||||||
case next
|
case next
|
||||||
}
|
}
|
||||||
|
|
||||||
case item(Direction)
|
case item(ItemDirection)
|
||||||
case peer(Direction)
|
case peer(PeerDirection)
|
||||||
}
|
}
|
||||||
|
|
||||||
public protocol StoryContentContext: AnyObject {
|
public protocol StoryContentContext: AnyObject {
|
||||||
|
|||||||
@ -10,7 +10,6 @@ import PhotoResources
|
|||||||
import SwiftSignalKit
|
import SwiftSignalKit
|
||||||
import UniversalMediaPlayer
|
import UniversalMediaPlayer
|
||||||
import TelegramUniversalVideoContent
|
import TelegramUniversalVideoContent
|
||||||
import StoryContainerScreen
|
|
||||||
import HierarchyTrackingLayer
|
import HierarchyTrackingLayer
|
||||||
import ButtonComponent
|
import ButtonComponent
|
||||||
import MultilineTextComponent
|
import MultilineTextComponent
|
||||||
@ -19,6 +18,14 @@ import TelegramPresentationData
|
|||||||
final class StoryItemContentComponent: Component {
|
final class StoryItemContentComponent: Component {
|
||||||
typealias EnvironmentType = StoryContentItem.Environment
|
typealias EnvironmentType = StoryContentItem.Environment
|
||||||
|
|
||||||
|
final class Hint {
|
||||||
|
let synchronousLoad: Bool
|
||||||
|
|
||||||
|
init(synchronousLoad: Bool) {
|
||||||
|
self.synchronousLoad = synchronousLoad
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let context: AccountContext
|
let context: AccountContext
|
||||||
let peer: EnginePeer
|
let peer: EnginePeer
|
||||||
let item: EngineStoryItem
|
let item: EngineStoryItem
|
||||||
@ -56,7 +63,7 @@ final class StoryItemContentComponent: Component {
|
|||||||
private var unsupportedText: ComponentView<Empty>?
|
private var unsupportedText: ComponentView<Empty>?
|
||||||
private var unsupportedButton: ComponentView<Empty>?
|
private var unsupportedButton: ComponentView<Empty>?
|
||||||
|
|
||||||
private var isProgressPaused: Bool = false
|
private var isProgressPaused: Bool = true
|
||||||
private var currentProgressTimer: SwiftSignalKit.Timer?
|
private var currentProgressTimer: SwiftSignalKit.Timer?
|
||||||
private var currentProgressTimerValue: Double = 0.0
|
private var currentProgressTimerValue: Double = 0.0
|
||||||
private var videoProgressDisposable: Disposable?
|
private var videoProgressDisposable: Disposable?
|
||||||
@ -82,7 +89,7 @@ final class StoryItemContentComponent: Component {
|
|||||||
guard let self else {
|
guard let self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.updateIsProgressPaused()
|
self.updateIsProgressPaused(update: true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,6 +104,17 @@ final class StoryItemContentComponent: Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func performActionAfterImageContentLoaded(update: Bool) {
|
private func performActionAfterImageContentLoaded(update: Bool) {
|
||||||
|
self.initializeVideoIfReady(update: update)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func initializeVideoIfReady(update: Bool) {
|
||||||
|
if self.videoNode != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if self.isProgressPaused {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
guard let component = self.component, let environment = self.environment, let currentMessageMedia = self.currentMessageMedia else {
|
guard let component = self.component, let environment = self.environment, let currentMessageMedia = self.currentMessageMedia else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -152,12 +170,26 @@ final class StoryItemContentComponent: Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let videoNode = self.videoNode {
|
||||||
|
if self.videoProgressDisposable == nil {
|
||||||
|
self.videoProgressDisposable = (videoNode.status
|
||||||
|
|> deliverOnMainQueue).start(next: { [weak self] status in
|
||||||
|
guard let self, let status else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
self.videoPlaybackStatus = status
|
||||||
|
self.updateVideoPlaybackProgress()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override func setIsProgressPaused(_ isProgressPaused: Bool) {
|
override func setIsProgressPaused(_ isProgressPaused: Bool) {
|
||||||
if self.isProgressPaused != isProgressPaused {
|
if self.isProgressPaused != isProgressPaused {
|
||||||
self.isProgressPaused = isProgressPaused
|
self.isProgressPaused = isProgressPaused
|
||||||
self.updateIsProgressPaused()
|
self.updateIsProgressPaused(update: true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -176,7 +208,7 @@ final class StoryItemContentComponent: Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func updateIsProgressPaused() {
|
private func updateIsProgressPaused(update: Bool) {
|
||||||
if let videoNode = self.videoNode {
|
if let videoNode = self.videoNode {
|
||||||
var canPlay = !self.isProgressPaused && self.contentLoaded && self.hierarchyTrackingLayer.isInHierarchy
|
var canPlay = !self.isProgressPaused && self.contentLoaded && self.hierarchyTrackingLayer.isInHierarchy
|
||||||
if let component = self.component {
|
if let component = self.component {
|
||||||
@ -192,6 +224,7 @@ final class StoryItemContentComponent: Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.initializeVideoIfReady(update: update)
|
||||||
self.updateVideoPlaybackProgress()
|
self.updateVideoPlaybackProgress()
|
||||||
self.updateProgressTimer()
|
self.updateProgressTimer()
|
||||||
}
|
}
|
||||||
@ -324,6 +357,11 @@ final class StoryItemContentComponent: Component {
|
|||||||
let environment = environment[StoryContentItem.Environment.self].value
|
let environment = environment[StoryContentItem.Environment.self].value
|
||||||
self.environment = environment
|
self.environment = environment
|
||||||
|
|
||||||
|
var synchronousLoad = false
|
||||||
|
if let hint = transition.userData(Hint.self) {
|
||||||
|
synchronousLoad = hint.synchronousLoad
|
||||||
|
}
|
||||||
|
|
||||||
let peerReference = PeerReference(component.peer._asPeer())
|
let peerReference = PeerReference(component.peer._asPeer())
|
||||||
|
|
||||||
var messageMedia: EngineMedia?
|
var messageMedia: EngineMedia?
|
||||||
@ -353,7 +391,7 @@ final class StoryItemContentComponent: Component {
|
|||||||
postbox: component.context.account.postbox,
|
postbox: component.context.account.postbox,
|
||||||
userLocation: .other,
|
userLocation: .other,
|
||||||
photoReference: .story(peer: peerReference, id: component.item.id, media: image),
|
photoReference: .story(peer: peerReference, id: component.item.id, media: image),
|
||||||
synchronousLoad: true,
|
synchronousLoad: synchronousLoad,
|
||||||
highQuality: true
|
highQuality: true
|
||||||
)
|
)
|
||||||
if let representation = image.representations.last {
|
if let representation = image.representations.last {
|
||||||
@ -377,7 +415,7 @@ final class StoryItemContentComponent: Component {
|
|||||||
videoReference: .story(peer: peerReference, id: component.item.id, media: file),
|
videoReference: .story(peer: peerReference, id: component.item.id, media: file),
|
||||||
onlyFullSize: false,
|
onlyFullSize: false,
|
||||||
useLargeThumbnail: true,
|
useLargeThumbnail: true,
|
||||||
synchronousLoad: true,
|
synchronousLoad: synchronousLoad,
|
||||||
autoFetchFullSizeThumbnail: true,
|
autoFetchFullSizeThumbnail: true,
|
||||||
overlayColor: nil,
|
overlayColor: nil,
|
||||||
nilForEmptyResult: false,
|
nilForEmptyResult: false,
|
||||||
@ -458,20 +496,6 @@ final class StoryItemContentComponent: Component {
|
|||||||
self.imageNode.frame = CGRect(origin: CGPoint(), size: availableSize)
|
self.imageNode.frame = CGRect(origin: CGPoint(), size: availableSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let videoNode = self.videoNode {
|
|
||||||
if self.videoProgressDisposable == nil {
|
|
||||||
self.videoProgressDisposable = (videoNode.status
|
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] status in
|
|
||||||
guard let self, let status else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
self.videoPlaybackStatus = status
|
|
||||||
self.updateVideoPlaybackProgress()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch component.item.media {
|
switch component.item.media {
|
||||||
case .image, .file:
|
case .image, .file:
|
||||||
if let unsupportedText = self.unsupportedText {
|
if let unsupportedText = self.unsupportedText {
|
||||||
@ -565,7 +589,7 @@ final class StoryItemContentComponent: Component {
|
|||||||
self.backgroundColor = UIColor(rgb: 0x181818)
|
self.backgroundColor = UIColor(rgb: 0x181818)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.updateIsProgressPaused()
|
self.updateIsProgressPaused(update: false)
|
||||||
|
|
||||||
return availableSize
|
return availableSize
|
||||||
}
|
}
|
||||||
@ -29,6 +29,7 @@ import TextFormat
|
|||||||
import LocalMediaResources
|
import LocalMediaResources
|
||||||
import SaveToCameraRoll
|
import SaveToCameraRoll
|
||||||
import BundleIconComponent
|
import BundleIconComponent
|
||||||
|
import PeerListItemComponent
|
||||||
|
|
||||||
public final class StoryItemSetContainerComponent: Component {
|
public final class StoryItemSetContainerComponent: Component {
|
||||||
public final class ExternalState {
|
public final class ExternalState {
|
||||||
@ -42,6 +43,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
public enum NavigationDirection {
|
public enum NavigationDirection {
|
||||||
case previous
|
case previous
|
||||||
case next
|
case next
|
||||||
|
case id(Int32)
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct PinchState: Equatable {
|
public struct PinchState: Equatable {
|
||||||
@ -181,19 +183,33 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
|
|
||||||
struct ItemLayout {
|
struct ItemLayout {
|
||||||
var size: CGSize
|
var size: CGSize
|
||||||
|
var contentFrame: CGRect
|
||||||
|
var contentVisualScale: CGFloat
|
||||||
|
|
||||||
init(size: CGSize) {
|
init(
|
||||||
|
size: CGSize,
|
||||||
|
contentFrame: CGRect,
|
||||||
|
contentVisualScale: CGFloat
|
||||||
|
) {
|
||||||
self.size = size
|
self.size = size
|
||||||
|
self.contentFrame = contentFrame
|
||||||
|
self.contentVisualScale = contentVisualScale
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final class VisibleItem {
|
final class VisibleItem {
|
||||||
let externalState = StoryContentItem.ExternalState()
|
let externalState = StoryContentItem.ExternalState()
|
||||||
|
let contentContainerView: UIView
|
||||||
let view = ComponentView<StoryContentItem.Environment>()
|
let view = ComponentView<StoryContentItem.Environment>()
|
||||||
var currentProgress: Double = 0.0
|
var currentProgress: Double = 0.0
|
||||||
var requestedNext: Bool = false
|
var requestedNext: Bool = false
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
|
self.contentContainerView = UIView()
|
||||||
|
self.contentContainerView.clipsToBounds = true
|
||||||
|
if #available(iOS 13.0, *) {
|
||||||
|
self.contentContainerView.layer.cornerCurve = .continuous
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -224,10 +240,35 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class View: UIView, UIGestureRecognizerDelegate {
|
private final class Scroller: UIScrollView {
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
super.init(frame: frame)
|
||||||
|
|
||||||
|
if #available(iOSApplicationExtension 11.0, iOS 11.0, *) {
|
||||||
|
self.contentInsetAdjustmentBehavior = .never
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder aDecoder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override func touchesShouldCancel(in view: UIView) -> Bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/*@objc func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||||
|
return false
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class View: UIView, UIScrollViewDelegate, UIGestureRecognizerDelegate {
|
||||||
let sendMessageContext: StoryItemSetContainerSendMessage
|
let sendMessageContext: StoryItemSetContainerSendMessage
|
||||||
|
|
||||||
let contentContainerView: UIView
|
private let scroller: Scroller
|
||||||
|
|
||||||
|
let itemsContainerView: UIView
|
||||||
|
let controlsContainerView: UIView
|
||||||
let topContentGradientLayer: SimpleGradientLayer
|
let topContentGradientLayer: SimpleGradientLayer
|
||||||
let bottomContentGradientLayer: SimpleGradientLayer
|
let bottomContentGradientLayer: SimpleGradientLayer
|
||||||
let contentDimView: UIView
|
let contentDimView: UIView
|
||||||
@ -249,6 +290,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
let inputPanelExternalState = MessageInputPanelComponent.ExternalState()
|
let inputPanelExternalState = MessageInputPanelComponent.ExternalState()
|
||||||
private let inputPanelBackground = ComponentView<Empty>()
|
private let inputPanelBackground = ComponentView<Empty>()
|
||||||
|
|
||||||
|
var preparingToDisplayViewList: Bool = false
|
||||||
var displayViewList: Bool = false
|
var displayViewList: Bool = false
|
||||||
var viewList: ViewList?
|
var viewList: ViewList?
|
||||||
|
|
||||||
@ -257,9 +299,10 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
var itemLayout: ItemLayout?
|
var itemLayout: ItemLayout?
|
||||||
var ignoreScrolling: Bool = false
|
var ignoreScrolling: Bool = false
|
||||||
|
|
||||||
var visibleItems: [AnyHashable: VisibleItem] = [:]
|
var visibleItems: [Int32: VisibleItem] = [:]
|
||||||
|
var trulyValidIds: [Int32] = []
|
||||||
var preloadContexts: [AnyHashable: Disposable] = [:]
|
var scrollingOffsetX: CGFloat = 0.0
|
||||||
|
var scrollingCenterX: CGFloat = 0.0
|
||||||
|
|
||||||
var reactionItems: [ReactionItem]?
|
var reactionItems: [ReactionItem]?
|
||||||
var reactionContextNode: ReactionContextNode?
|
var reactionContextNode: ReactionContextNode?
|
||||||
@ -282,13 +325,25 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
|
|
||||||
let transitionCloneContainerView: UIView
|
let transitionCloneContainerView: UIView
|
||||||
|
|
||||||
|
private var awaitingSwitchToId: (from: Int32, to: Int32)?
|
||||||
|
private var animateNextNavigationId: Int32?
|
||||||
|
private var initializedOffset: Bool = false
|
||||||
|
|
||||||
override init(frame: CGRect) {
|
override init(frame: CGRect) {
|
||||||
self.sendMessageContext = StoryItemSetContainerSendMessage()
|
self.sendMessageContext = StoryItemSetContainerSendMessage()
|
||||||
|
|
||||||
self.contentContainerView = UIView()
|
self.itemsContainerView = UIView()
|
||||||
self.contentContainerView.clipsToBounds = true
|
|
||||||
|
self.scroller = Scroller()
|
||||||
|
self.scroller.alwaysBounceHorizontal = true
|
||||||
|
self.scroller.showsVerticalScrollIndicator = false
|
||||||
|
self.scroller.showsHorizontalScrollIndicator = false
|
||||||
|
self.scroller.decelerationRate = .fast
|
||||||
|
|
||||||
|
self.controlsContainerView = SparseContainerView()
|
||||||
|
self.controlsContainerView.clipsToBounds = true
|
||||||
if #available(iOS 13.0, *) {
|
if #available(iOS 13.0, *) {
|
||||||
self.contentContainerView.layer.cornerCurve = .continuous
|
self.controlsContainerView.layer.cornerCurve = .continuous
|
||||||
}
|
}
|
||||||
|
|
||||||
self.topContentGradientLayer = SimpleGradientLayer()
|
self.topContentGradientLayer = SimpleGradientLayer()
|
||||||
@ -304,18 +359,26 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
|
|
||||||
super.init(frame: frame)
|
super.init(frame: frame)
|
||||||
|
|
||||||
self.addSubview(self.contentContainerView)
|
self.clipsToBounds = true
|
||||||
self.contentContainerView.addSubview(self.contentDimView)
|
|
||||||
self.contentContainerView.layer.addSublayer(self.topContentGradientLayer)
|
self.itemsContainerView.addSubview(self.scroller)
|
||||||
|
self.scroller.delegate = self
|
||||||
|
self.itemsContainerView.addGestureRecognizer(self.scroller.panGestureRecognizer)
|
||||||
|
|
||||||
|
self.addSubview(self.itemsContainerView)
|
||||||
|
self.addSubview(self.controlsContainerView)
|
||||||
|
|
||||||
|
self.controlsContainerView.addSubview(self.contentDimView)
|
||||||
|
self.controlsContainerView.layer.addSublayer(self.topContentGradientLayer)
|
||||||
self.layer.addSublayer(self.bottomContentGradientLayer)
|
self.layer.addSublayer(self.bottomContentGradientLayer)
|
||||||
|
|
||||||
self.closeButton.addSubview(self.closeButtonIconView)
|
self.closeButton.addSubview(self.closeButtonIconView)
|
||||||
self.contentContainerView.addSubview(self.closeButton)
|
self.controlsContainerView.addSubview(self.closeButton)
|
||||||
self.closeButton.addTarget(self, action: #selector(self.closePressed), for: .touchUpInside)
|
self.closeButton.addTarget(self, action: #selector(self.closePressed), for: .touchUpInside)
|
||||||
|
|
||||||
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:)))
|
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:)))
|
||||||
tapRecognizer.delegate = self
|
tapRecognizer.delegate = self
|
||||||
self.contentContainerView.addGestureRecognizer(tapRecognizer)
|
self.itemsContainerView.addGestureRecognizer(tapRecognizer)
|
||||||
|
|
||||||
self.audioRecorderDisposable = (self.sendMessageContext.audioRecorder.get()
|
self.audioRecorderDisposable = (self.sendMessageContext.audioRecorder.get()
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] audioRecorder in
|
|> deliverOnMainQueue).start(next: { [weak self] audioRecorder in
|
||||||
@ -430,7 +493,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.contentContainerView.frame.contains(point) {
|
if self.controlsContainerView.frame.contains(point) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -448,7 +511,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
guard let component = self.component else {
|
guard let component = self.component else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
guard let visibleItem = self.visibleItems[component.slice.item.id] else {
|
guard let visibleItem = self.visibleItems[component.slice.item.storyItem.id] else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if let itemView = visibleItem.view.view as? StoryContentItem.View {
|
if let itemView = visibleItem.view.view as? StoryContentItem.View {
|
||||||
@ -460,7 +523,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
guard let component = self.component else {
|
guard let component = self.component else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
guard let visibleItem = self.visibleItems[component.slice.item.id] else {
|
guard let visibleItem = self.visibleItems[component.slice.item.storyItem.id] else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if let itemView = visibleItem.view.view as? StoryContentItem.View {
|
if let itemView = visibleItem.view.view as? StoryContentItem.View {
|
||||||
@ -481,8 +544,21 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
self.sendMessageContext.currentInputMode = .text
|
self.sendMessageContext.currentInputMode = .text
|
||||||
self.endEditing(true)
|
self.endEditing(true)
|
||||||
} else if self.displayViewList {
|
} else if self.displayViewList {
|
||||||
self.displayViewList = false
|
let point = recognizer.location(in: self)
|
||||||
self.state?.updated(transition: Transition(animation: .curve(duration: 0.4, curve: .spring)))
|
|
||||||
|
for (id, visibleItem) in self.visibleItems {
|
||||||
|
if visibleItem.contentContainerView.convert(visibleItem.contentContainerView.bounds, to: self).contains(point) {
|
||||||
|
if id == component.slice.item.storyItem.id {
|
||||||
|
self.displayViewList = false
|
||||||
|
self.state?.updated(transition: Transition(animation: .curve(duration: 0.4, curve: .spring)))
|
||||||
|
} else {
|
||||||
|
self.animateNextNavigationId = id
|
||||||
|
component.navigate(.id(id))
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
} else if let captionItem = self.captionItem, captionItem.externalState.isExpanded {
|
} else if let captionItem = self.captionItem, captionItem.externalState.isExpanded {
|
||||||
if let captionItemView = captionItem.view.view as? StoryContentCaptionComponent.View {
|
if let captionItemView = captionItem.view.view as? StoryContentCaptionComponent.View {
|
||||||
captionItemView.collapse(transition: Transition(animation: .curve(duration: 0.4, curve: .spring)))
|
captionItemView.collapse(transition: Transition(animation: .curve(duration: 0.4, curve: .spring)))
|
||||||
@ -515,7 +591,118 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
if let inputView = self.inputPanel.view, let inputViewHitTest = inputView.hitTest(self.convert(point, to: inputView), with: event) {
|
if let inputView = self.inputPanel.view, let inputViewHitTest = inputView.hitTest(self.convert(point, to: inputView), with: event) {
|
||||||
return inputViewHitTest
|
return inputViewHitTest
|
||||||
}
|
}
|
||||||
return super.hitTest(point, with: event)
|
guard let result = super.hitTest(point, with: event) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if result === self.scroller {
|
||||||
|
return self.itemsContainerView
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
public func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||||
|
if !self.ignoreScrolling {
|
||||||
|
self.scrollingOffsetX = scrollView.contentOffset.x
|
||||||
|
|
||||||
|
self.adjustScroller()
|
||||||
|
self.updateScrolling(transition: .immediate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
|
||||||
|
if !decelerate {
|
||||||
|
self.snapScrolling()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
|
||||||
|
self.snapScrolling()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func snapScrolling() {
|
||||||
|
self.scroller.setContentOffset(CGPoint(x: self.scrollingCenterX, y: 0.0), animated: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func adjustScroller() {
|
||||||
|
guard let component = self.component, let itemLayout = self.itemLayout else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
self.ignoreScrolling = true
|
||||||
|
|
||||||
|
self.scroller.isScrollEnabled = self.displayViewList
|
||||||
|
|
||||||
|
let itemSpacing: CGFloat = 12.0
|
||||||
|
let centralVisibleItemWidth = itemLayout.contentFrame.width * itemLayout.contentVisualScale
|
||||||
|
let sideVisibleItemWidth = centralVisibleItemWidth - 30.0
|
||||||
|
let fullItemScrollDistance = centralVisibleItemWidth * 0.5 + itemSpacing + sideVisibleItemWidth * 0.5
|
||||||
|
|
||||||
|
var additionalInitializationDistance: CGFloat = 0.0
|
||||||
|
if let (switchFromId, switchToId) = self.awaitingSwitchToId {
|
||||||
|
if component.slice.item.storyItem.id == switchToId {
|
||||||
|
self.awaitingSwitchToId = nil
|
||||||
|
|
||||||
|
if let previousIndex = component.slice.allItems.firstIndex(where: { $0.storyItem.id == switchFromId }), let centralIndex = component.slice.allItems.firstIndex(where: { $0.storyItem.id == switchToId }) {
|
||||||
|
let fractionDistance = CGFloat(previousIndex - centralIndex)
|
||||||
|
|
||||||
|
let currentOffset = self.scrollingCenterX - self.scrollingOffsetX
|
||||||
|
additionalInitializationDistance = -(currentOffset - fractionDistance * fullItemScrollDistance)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.initializedOffset = false
|
||||||
|
} else {
|
||||||
|
self.ignoreScrolling = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let centralIndex = component.slice.allItems.firstIndex(where: { $0.storyItem.id == component.slice.item.storyItem.id }) {
|
||||||
|
var leftWidth: CGFloat = 0.0
|
||||||
|
var rightWidth: CGFloat = 0.0
|
||||||
|
if centralIndex != 0 {
|
||||||
|
leftWidth = 600.0
|
||||||
|
}
|
||||||
|
if centralIndex != component.slice.allItems.count - 1 {
|
||||||
|
rightWidth = 600.0
|
||||||
|
}
|
||||||
|
|
||||||
|
self.scrollingCenterX = leftWidth
|
||||||
|
self.scroller.contentSize = CGSize(width: leftWidth + itemLayout.size.width + rightWidth, height: 1.0)
|
||||||
|
|
||||||
|
if !self.initializedOffset {
|
||||||
|
self.initializedOffset = true
|
||||||
|
self.scrollingOffsetX = leftWidth + additionalInitializationDistance
|
||||||
|
self.scroller.contentOffset = CGPoint(x: self.scrollingOffsetX, y: 0.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
var lowestFraction: (Int, CGFloat)?
|
||||||
|
|
||||||
|
for index in 0 ..< component.slice.allItems.count {
|
||||||
|
let offsetFraction: CGFloat = (self.scrollingCenterX - self.scrollingOffsetX) / fullItemScrollDistance
|
||||||
|
let centerFraction: CGFloat = CGFloat(index - centralIndex)
|
||||||
|
|
||||||
|
let combinedFraction = abs(offsetFraction + centerFraction)
|
||||||
|
|
||||||
|
if let (_, lowestValue) = lowestFraction {
|
||||||
|
if combinedFraction < lowestValue {
|
||||||
|
lowestFraction = (index, combinedFraction)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
lowestFraction = (index, combinedFraction)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let (index, _) = lowestFraction, index != centralIndex {
|
||||||
|
let fixedId = component.slice.allItems[index].storyItem.id
|
||||||
|
component.navigate(.id(fixedId))
|
||||||
|
self.awaitingSwitchToId = (component.slice.item.storyItem.id, fixedId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.ignoreScrolling = false
|
||||||
}
|
}
|
||||||
|
|
||||||
private func isProgressPaused() -> Bool {
|
private func isProgressPaused() -> Bool {
|
||||||
@ -563,76 +750,178 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var validIds: [AnyHashable] = []
|
var validIds: [Int32] = []
|
||||||
let focusedItem = component.slice.item
|
var trulyValidIds: [Int32] = []
|
||||||
|
|
||||||
validIds.append(focusedItem.id)
|
let centralItemFrame = itemLayout.contentFrame.center.offsetBy(dx: 0.0, dy: 0.0)
|
||||||
|
|
||||||
var itemTransition = transition
|
|
||||||
let visibleItem: VisibleItem
|
|
||||||
if let current = self.visibleItems[focusedItem.id] {
|
|
||||||
visibleItem = current
|
|
||||||
} else {
|
|
||||||
itemTransition = .immediate
|
|
||||||
visibleItem = VisibleItem()
|
|
||||||
self.visibleItems[focusedItem.id] = visibleItem
|
|
||||||
}
|
|
||||||
|
|
||||||
let itemEnvironment = StoryContentItem.Environment(
|
let centralVisibleItemWidth = itemLayout.contentFrame.width * itemLayout.contentVisualScale
|
||||||
externalState: visibleItem.externalState,
|
let sideVisibleItemWidth = centralVisibleItemWidth - 30.0
|
||||||
sharedState: component.storyItemSharedState,
|
let sideVisibleItemScale = itemLayout.contentVisualScale * (sideVisibleItemWidth / centralVisibleItemWidth)
|
||||||
theme: component.theme,
|
|
||||||
presentationProgressUpdated: { [weak self, weak visibleItem] progress, canSwitch in
|
let itemSpacing: CGFloat = 12.0
|
||||||
guard let self = self, let component = self.component else {
|
|
||||||
return
|
let fullItemScrollDistance = centralVisibleItemWidth * 0.5 + itemSpacing + sideVisibleItemWidth * 0.5
|
||||||
}
|
let halfItemScrollDistance = sideVisibleItemWidth * 0.5 + itemSpacing + sideVisibleItemWidth * 0.5
|
||||||
guard let visibleItem else {
|
|
||||||
return
|
if let centralIndex = component.slice.allItems.firstIndex(where: { $0.storyItem.id == component.slice.item.storyItem.id }) {
|
||||||
}
|
for index in 0 ..< component.slice.allItems.count {
|
||||||
visibleItem.currentProgress = progress
|
let item = component.slice.allItems[index]
|
||||||
|
|
||||||
if let navigationStripView = self.navigationStrip.view as? MediaNavigationStripComponent.View {
|
let offsetFraction: CGFloat = (self.scrollingCenterX - self.scrollingOffsetX) / fullItemScrollDistance
|
||||||
navigationStripView.updateCurrentItemProgress(value: progress, transition: .immediate)
|
let centerIndexOffset = index - centralIndex
|
||||||
|
let centerFraction: CGFloat = CGFloat(centerIndexOffset)
|
||||||
|
|
||||||
|
let combinedFraction = offsetFraction + centerFraction
|
||||||
|
let combinedFractionSign: CGFloat = combinedFraction < 0.0 ? -1.0 : 1.0
|
||||||
|
|
||||||
|
let fractionDistanceToCenter: CGFloat = min(1.0, abs(combinedFraction))
|
||||||
|
|
||||||
|
var itemPosition = centralItemFrame
|
||||||
|
itemPosition.x += min(1.0, abs(combinedFraction)) * combinedFractionSign * fullItemScrollDistance
|
||||||
|
itemPosition.x += max(0.0, abs(combinedFraction) - 1.0) * combinedFractionSign * halfItemScrollDistance
|
||||||
|
|
||||||
|
var itemVisible = true
|
||||||
|
if abs(centerIndexOffset) > 2 {
|
||||||
|
itemVisible = false
|
||||||
}
|
}
|
||||||
if progress >= 1.0 && canSwitch && !visibleItem.requestedNext {
|
if itemLayout.contentVisualScale >= 1.0 - 0.001 && !self.preparingToDisplayViewList {
|
||||||
visibleItem.requestedNext = true
|
if index != centralIndex {
|
||||||
|
itemVisible = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var reevaluateVisibilityOnCompletion = false
|
||||||
|
if !itemVisible {
|
||||||
|
if transition.animation.isImmediate {
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
if self.visibleItems[item.storyItem.id] == nil {
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
reevaluateVisibilityOnCompletion = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let scaleFraction: CGFloat = abs(max(-1.0, min(1.0, combinedFraction)))
|
||||||
|
let itemScale = itemLayout.contentVisualScale * (1.0 - scaleFraction) + sideVisibleItemScale * scaleFraction
|
||||||
|
|
||||||
|
validIds.append(item.storyItem.id)
|
||||||
|
if itemVisible {
|
||||||
|
trulyValidIds.append(item.storyItem.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
var itemTransition = transition
|
||||||
|
let visibleItem: VisibleItem
|
||||||
|
if let current = self.visibleItems[item.storyItem.id] {
|
||||||
|
visibleItem = current
|
||||||
|
} else {
|
||||||
|
itemTransition = .immediate
|
||||||
|
visibleItem = VisibleItem()
|
||||||
|
self.visibleItems[item.storyItem.id] = visibleItem
|
||||||
|
}
|
||||||
|
|
||||||
|
let itemEnvironment = StoryContentItem.Environment(
|
||||||
|
externalState: visibleItem.externalState,
|
||||||
|
sharedState: component.storyItemSharedState,
|
||||||
|
theme: component.theme,
|
||||||
|
presentationProgressUpdated: { [weak self, weak visibleItem] progress, canSwitch in
|
||||||
|
guard let self = self, let component = self.component else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
guard let visibleItem else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
visibleItem.currentProgress = progress
|
||||||
|
|
||||||
|
if let navigationStripView = self.navigationStrip.view as? MediaNavigationStripComponent.View {
|
||||||
|
navigationStripView.updateCurrentItemProgress(value: progress, transition: .immediate)
|
||||||
|
}
|
||||||
|
if progress >= 1.0 && canSwitch && !visibleItem.requestedNext {
|
||||||
|
visibleItem.requestedNext = true
|
||||||
|
|
||||||
|
component.navigate(.next)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
markAsSeen: { [weak self] id in
|
||||||
|
guard let self, let component = self.component else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
component.markAsSeen(id)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
let _ = visibleItem.view.update(
|
||||||
|
transition: itemTransition.withUserData(StoryItemContentComponent.Hint(
|
||||||
|
synchronousLoad: index == centralIndex
|
||||||
|
)),
|
||||||
|
component: AnyComponent(StoryItemContentComponent(
|
||||||
|
context: component.context,
|
||||||
|
peer: component.slice.peer,
|
||||||
|
item: item.storyItem
|
||||||
|
)),
|
||||||
|
environment: {
|
||||||
|
itemEnvironment
|
||||||
|
},
|
||||||
|
containerSize: itemLayout.size
|
||||||
|
)
|
||||||
|
if let view = visibleItem.view.view {
|
||||||
|
if visibleItem.contentContainerView.superview == nil {
|
||||||
|
self.itemsContainerView.addSubview(visibleItem.contentContainerView)
|
||||||
|
visibleItem.contentContainerView.addSubview(view)
|
||||||
|
}
|
||||||
|
|
||||||
component.navigate(.next)
|
itemTransition.setPosition(view: view, position: CGPoint(x: itemLayout.contentFrame.size.width * 0.5, y: itemLayout.contentFrame.size.height * 0.5))
|
||||||
|
itemTransition.setBounds(view: view, bounds: CGRect(origin: CGPoint(), size: itemLayout.contentFrame.size))
|
||||||
|
|
||||||
|
let itemId = item.storyItem.id
|
||||||
|
itemTransition.setPosition(view: visibleItem.contentContainerView, position: itemPosition, completion: { [weak self] _ in
|
||||||
|
guard reevaluateVisibilityOnCompletion, let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !self.trulyValidIds.contains(itemId), let visibleItem = self.visibleItems[itemId] {
|
||||||
|
self.visibleItems.removeValue(forKey: itemId)
|
||||||
|
visibleItem.contentContainerView.removeFromSuperview()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
itemTransition.setBounds(view: visibleItem.contentContainerView, bounds: CGRect(origin: CGPoint(), size: itemLayout.contentFrame.size))
|
||||||
|
|
||||||
|
var transform = CATransform3DMakeScale(itemScale, itemScale, 1.0)
|
||||||
|
if let pinchState = component.pinchState {
|
||||||
|
let pinchOffset = CGPoint(
|
||||||
|
x: pinchState.location.x - itemLayout.contentFrame.width / 2.0,
|
||||||
|
y: pinchState.location.y - itemLayout.contentFrame.height / 2.0
|
||||||
|
)
|
||||||
|
transform = CATransform3DTranslate(
|
||||||
|
transform,
|
||||||
|
pinchState.offset.x - pinchOffset.x * (pinchState.scale - 1.0),
|
||||||
|
pinchState.offset.y - pinchOffset.y * (pinchState.scale - 1.0),
|
||||||
|
0.0
|
||||||
|
)
|
||||||
|
transform = CATransform3DScale(transform, pinchState.scale, pinchState.scale, 0.0)
|
||||||
|
}
|
||||||
|
itemTransition.setTransform(view: visibleItem.contentContainerView, transform: transform)
|
||||||
|
itemTransition.setCornerRadius(layer: visibleItem.contentContainerView.layer, cornerRadius: 12.0 * (1.0 / itemScale))
|
||||||
|
itemTransition.setAlpha(view: visibleItem.contentContainerView, alpha: 1.0 * (1.0 - fractionDistanceToCenter) + 0.75 * fractionDistanceToCenter)
|
||||||
|
|
||||||
|
var itemProgressPaused = self.isProgressPaused()
|
||||||
|
if index != centralIndex {
|
||||||
|
itemProgressPaused = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if let view = view as? StoryContentItem.View {
|
||||||
|
view.setIsProgressPaused(itemProgressPaused)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
markAsSeen: { [weak self] id in
|
|
||||||
guard let self, let component = self.component else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
component.markAsSeen(id)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
let _ = visibleItem.view.update(
|
|
||||||
transition: itemTransition,
|
|
||||||
component: focusedItem.component,
|
|
||||||
environment: {
|
|
||||||
itemEnvironment
|
|
||||||
},
|
|
||||||
containerSize: itemLayout.size
|
|
||||||
)
|
|
||||||
if let view = visibleItem.view.view {
|
|
||||||
if view.superview == nil {
|
|
||||||
self.contentContainerView.insertSubview(view, at: 0)
|
|
||||||
}
|
|
||||||
itemTransition.setFrame(view: view, frame: CGRect(origin: CGPoint(), size: itemLayout.size))
|
|
||||||
|
|
||||||
if let view = view as? StoryContentItem.View {
|
|
||||||
view.setIsProgressPaused(self.isProgressPaused())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var removeIds: [AnyHashable] = []
|
self.trulyValidIds = trulyValidIds
|
||||||
|
|
||||||
|
var removeIds: [Int32] = []
|
||||||
for (id, visibleItem) in self.visibleItems {
|
for (id, visibleItem) in self.visibleItems {
|
||||||
if !validIds.contains(id) {
|
if !validIds.contains(id) {
|
||||||
removeIds.append(id)
|
removeIds.append(id)
|
||||||
if let view = visibleItem.view.view {
|
visibleItem.contentContainerView.removeFromSuperview()
|
||||||
view.removeFromSuperview()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for id in removeIds {
|
for id in removeIds {
|
||||||
@ -641,29 +930,43 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func updateIsProgressPaused() {
|
func updateIsProgressPaused() {
|
||||||
for (_, visibleItem) in self.visibleItems {
|
let isProgressPaused = self.isProgressPaused()
|
||||||
|
var centralId: Int32?
|
||||||
|
if let component = self.component {
|
||||||
|
centralId = component.slice.item.storyItem.id
|
||||||
|
}
|
||||||
|
|
||||||
|
for (id, visibleItem) in self.visibleItems {
|
||||||
if let view = visibleItem.view.view {
|
if let view = visibleItem.view.view {
|
||||||
if let view = view as? StoryContentItem.View {
|
if let view = view as? StoryContentItem.View {
|
||||||
view.setIsProgressPaused(self.isProgressPaused())
|
view.setIsProgressPaused(isProgressPaused || id != centralId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func activateInput() {
|
func activateInput() -> Bool {
|
||||||
guard let component = self.component else {
|
guard let component = self.component else {
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
if component.slice.peer.id == component.context.account.peerId {
|
if component.slice.peer.id == component.context.account.peerId {
|
||||||
if let views = component.slice.item.storyItem.views, !views.seenPeers.isEmpty {
|
if let views = component.slice.item.storyItem.views, !views.seenPeers.isEmpty {
|
||||||
self.displayViewList = true
|
self.displayViewList = true
|
||||||
|
if component.verticalPanFraction == 0.0 {
|
||||||
|
self.preparingToDisplayViewList = true
|
||||||
|
self.updateScrolling(transition: .immediate)
|
||||||
|
self.preparingToDisplayViewList = false
|
||||||
|
}
|
||||||
self.state?.updated(transition: Transition(animation: .curve(duration: 0.4, curve: .spring)))
|
self.state?.updated(transition: Transition(animation: .curve(duration: 0.4, curve: .spring)))
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if let inputPanelView = self.inputPanel.view as? MessageInputPanelComponent.View {
|
if let inputPanelView = self.inputPanel.view as? MessageInputPanelComponent.View {
|
||||||
inputPanelView.activateInput()
|
inputPanelView.activateInput()
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func animateIn(transitionIn: StoryContainerScreen.TransitionIn) {
|
func animateIn(transitionIn: StoryContainerScreen.TransitionIn) {
|
||||||
@ -681,7 +984,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
}
|
}
|
||||||
if let viewListView = self.viewList?.view.view {
|
if let viewListView = self.viewList?.view.view {
|
||||||
viewListView.layer.animatePosition(
|
viewListView.layer.animatePosition(
|
||||||
from: CGPoint(x: 0.0, y: self.bounds.height - self.contentContainerView.frame.maxY),
|
from: CGPoint(x: 0.0, y: self.bounds.height - self.controlsContainerView.frame.maxY),
|
||||||
to: CGPoint(),
|
to: CGPoint(),
|
||||||
duration: 0.3,
|
duration: 0.3,
|
||||||
timingFunction: kCAMediaTimingFunctionSpring,
|
timingFunction: kCAMediaTimingFunctionSpring,
|
||||||
@ -700,9 +1003,9 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
captionItemView.layer.animateAlpha(from: 0.0, to: captionItemView.alpha, duration: 0.28)
|
captionItemView.layer.animateAlpha(from: 0.0, to: captionItemView.alpha, duration: 0.28)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let sourceView = transitionIn.sourceView {
|
if let component = self.component, let sourceView = transitionIn.sourceView, let contentContainerView = self.visibleItems[component.slice.item.storyItem.id]?.contentContainerView {
|
||||||
let sourceLocalFrame = sourceView.convert(transitionIn.sourceRect, to: self)
|
let sourceLocalFrame = sourceView.convert(transitionIn.sourceRect, to: self)
|
||||||
let innerSourceLocalFrame = CGRect(origin: CGPoint(x: sourceLocalFrame.minX - self.contentContainerView.frame.minX, y: sourceLocalFrame.minY - self.contentContainerView.frame.minY), size: sourceLocalFrame.size)
|
let innerSourceLocalFrame = CGRect(origin: CGPoint(x: sourceLocalFrame.minX - contentContainerView.frame.minX, y: sourceLocalFrame.minY - contentContainerView.frame.minY), size: sourceLocalFrame.size)
|
||||||
|
|
||||||
if let centerInfoView = self.centerInfoItem?.view.view {
|
if let centerInfoView = self.centerInfoItem?.view.view {
|
||||||
centerInfoView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
|
centerInfoView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
|
||||||
@ -719,17 +1022,27 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.contentContainerView.layer.animatePosition(from: sourceLocalFrame.center, to: self.contentContainerView.center, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
|
contentContainerView.layer.animatePosition(from: sourceLocalFrame.center, to: contentContainerView.center, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
|
||||||
self.contentContainerView.layer.animateBounds(from: CGRect(origin: CGPoint(x: innerSourceLocalFrame.minX, y: innerSourceLocalFrame.minY), size: sourceLocalFrame.size), to: self.contentContainerView.bounds, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
|
contentContainerView.layer.animateBounds(from: CGRect(origin: CGPoint(x: innerSourceLocalFrame.minX, y: innerSourceLocalFrame.minY), size: sourceLocalFrame.size), to: contentContainerView.bounds, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
|
||||||
self.contentContainerView.layer.animate(
|
contentContainerView.layer.animate(
|
||||||
from: transitionIn.sourceCornerRadius as NSNumber,
|
from: transitionIn.sourceCornerRadius as NSNumber,
|
||||||
to: self.contentContainerView.layer.cornerRadius as NSNumber,
|
to: contentContainerView.layer.cornerRadius as NSNumber,
|
||||||
keyPath: "cornerRadius",
|
keyPath: "cornerRadius",
|
||||||
timingFunction: kCAMediaTimingFunctionSpring,
|
timingFunction: kCAMediaTimingFunctionSpring,
|
||||||
duration: 0.3
|
duration: 0.3
|
||||||
)
|
)
|
||||||
|
|
||||||
if let component = self.component, let visibleItemView = self.visibleItems[component.slice.item.id]?.view.view {
|
self.controlsContainerView.layer.animatePosition(from: sourceLocalFrame.center, to: self.controlsContainerView.center, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
|
||||||
|
self.controlsContainerView.layer.animateBounds(from: CGRect(origin: CGPoint(x: innerSourceLocalFrame.minX, y: innerSourceLocalFrame.minY), size: sourceLocalFrame.size), to: self.controlsContainerView.bounds, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
|
||||||
|
self.controlsContainerView.layer.animate(
|
||||||
|
from: transitionIn.sourceCornerRadius as NSNumber,
|
||||||
|
to: self.controlsContainerView.layer.cornerRadius as NSNumber,
|
||||||
|
keyPath: "cornerRadius",
|
||||||
|
timingFunction: kCAMediaTimingFunctionSpring,
|
||||||
|
duration: 0.3
|
||||||
|
)
|
||||||
|
|
||||||
|
if let component = self.component, let visibleItemView = self.visibleItems[component.slice.item.storyItem.id]?.view.view {
|
||||||
let innerScale = innerSourceLocalFrame.width / visibleItemView.bounds.width
|
let innerScale = innerSourceLocalFrame.width / visibleItemView.bounds.width
|
||||||
let innerFromFrame = CGRect(origin: CGPoint(x: innerSourceLocalFrame.minX, y: innerSourceLocalFrame.minY), size: CGSize(width: innerSourceLocalFrame.width, height: visibleItemView.bounds.height * innerScale))
|
let innerFromFrame = CGRect(origin: CGPoint(x: innerSourceLocalFrame.minX, y: innerSourceLocalFrame.minY), size: CGSize(width: innerSourceLocalFrame.width, height: visibleItemView.bounds.height * innerScale))
|
||||||
|
|
||||||
@ -777,7 +1090,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
if let viewListView = self.viewList?.view.view {
|
if let viewListView = self.viewList?.view.view {
|
||||||
viewListView.layer.animatePosition(
|
viewListView.layer.animatePosition(
|
||||||
from: CGPoint(),
|
from: CGPoint(),
|
||||||
to: CGPoint(x: 0.0, y: self.bounds.height - self.contentContainerView.frame.maxY),
|
to: CGPoint(x: 0.0, y: self.bounds.height - self.controlsContainerView.frame.maxY),
|
||||||
duration: 0.3,
|
duration: 0.3,
|
||||||
timingFunction: kCAMediaTimingFunctionSpring,
|
timingFunction: kCAMediaTimingFunctionSpring,
|
||||||
removeOnCompletion: false,
|
removeOnCompletion: false,
|
||||||
@ -797,11 +1110,11 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
captionItemView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false)
|
captionItemView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let sourceView = transitionOut.destinationView {
|
if let component = self.component, let sourceView = transitionOut.destinationView, let contentContainerView = self.visibleItems[component.slice.item.storyItem.id]?.contentContainerView {
|
||||||
let sourceLocalFrame = sourceView.convert(transitionOut.destinationRect, to: self)
|
let sourceLocalFrame = sourceView.convert(transitionOut.destinationRect, to: self)
|
||||||
let innerSourceLocalFrame = CGRect(origin: CGPoint(x: sourceLocalFrame.minX - self.contentContainerView.frame.minX, y: sourceLocalFrame.minY - self.contentContainerView.frame.minY), size: sourceLocalFrame.size)
|
let innerSourceLocalFrame = CGRect(origin: CGPoint(x: sourceLocalFrame.minX - contentContainerView.frame.minX, y: sourceLocalFrame.minY - contentContainerView.frame.minY), size: sourceLocalFrame.size)
|
||||||
|
|
||||||
let contentSourceFrame = self.contentContainerView.frame
|
let contentSourceFrame = contentContainerView.frame
|
||||||
|
|
||||||
if let centerInfoView = self.centerInfoItem?.view.view {
|
if let centerInfoView = self.centerInfoItem?.view.view {
|
||||||
centerInfoView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false)
|
centerInfoView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false)
|
||||||
@ -818,7 +1131,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
|
|
||||||
let transitionSourceContainerView = UIView(frame: self.bounds)
|
let transitionSourceContainerView = UIView(frame: self.bounds)
|
||||||
transitionSourceContainerView.isUserInteractionEnabled = false
|
transitionSourceContainerView.isUserInteractionEnabled = false
|
||||||
self.insertSubview(transitionSourceContainerView, aboveSubview: self.contentContainerView)
|
self.insertSubview(transitionSourceContainerView, aboveSubview: self.itemsContainerView)
|
||||||
|
|
||||||
transitionSourceContainerView.addSubview(transitionViewImpl)
|
transitionSourceContainerView.addSubview(transitionViewImpl)
|
||||||
|
|
||||||
@ -891,10 +1204,21 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.contentContainerView.layer.animatePosition(from: self.contentContainerView.center, to: sourceLocalFrame.center, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
|
contentContainerView.layer.animatePosition(from: contentContainerView.center, to: sourceLocalFrame.center, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
|
||||||
self.contentContainerView.layer.animateBounds(from: self.contentContainerView.bounds, to: CGRect(origin: CGPoint(x: innerSourceLocalFrame.minX, y: innerSourceLocalFrame.minY), size: sourceLocalFrame.size), duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
|
contentContainerView.layer.animateBounds(from: contentContainerView.bounds, to: CGRect(origin: CGPoint(x: innerSourceLocalFrame.minX, y: innerSourceLocalFrame.minY), size: sourceLocalFrame.size), duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
|
||||||
self.contentContainerView.layer.animate(
|
contentContainerView.layer.animate(
|
||||||
from: self.contentContainerView.layer.cornerRadius as NSNumber,
|
from: contentContainerView.layer.cornerRadius as NSNumber,
|
||||||
|
to: transitionOut.destinationCornerRadius as NSNumber,
|
||||||
|
keyPath: "cornerRadius",
|
||||||
|
timingFunction: kCAMediaTimingFunctionSpring,
|
||||||
|
duration: 0.3,
|
||||||
|
removeOnCompletion: false
|
||||||
|
)
|
||||||
|
|
||||||
|
self.controlsContainerView.layer.animatePosition(from: self.controlsContainerView.center, to: sourceLocalFrame.center, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
|
||||||
|
self.controlsContainerView.layer.animateBounds(from: self.controlsContainerView.bounds, to: CGRect(origin: CGPoint(x: innerSourceLocalFrame.minX, y: innerSourceLocalFrame.minY), size: sourceLocalFrame.size), duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
|
||||||
|
self.controlsContainerView.layer.animate(
|
||||||
|
from: self.controlsContainerView.layer.cornerRadius as NSNumber,
|
||||||
to: transitionOut.destinationCornerRadius as NSNumber,
|
to: transitionOut.destinationCornerRadius as NSNumber,
|
||||||
keyPath: "cornerRadius",
|
keyPath: "cornerRadius",
|
||||||
timingFunction: kCAMediaTimingFunctionSpring,
|
timingFunction: kCAMediaTimingFunctionSpring,
|
||||||
@ -912,7 +1236,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
|
|
||||||
let transitionSourceContainerView = UIView(frame: self.bounds)
|
let transitionSourceContainerView = UIView(frame: self.bounds)
|
||||||
transitionSourceContainerView.isUserInteractionEnabled = false
|
transitionSourceContainerView.isUserInteractionEnabled = false
|
||||||
self.insertSubview(transitionSourceContainerView, belowSubview: self.contentContainerView)
|
self.insertSubview(transitionSourceContainerView, belowSubview: self.itemsContainerView)
|
||||||
|
|
||||||
transitionSourceContainerView.addSubview(transitionViewImpl)
|
transitionSourceContainerView.addSubview(transitionViewImpl)
|
||||||
|
|
||||||
@ -954,7 +1278,8 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
transitionViewImpl.alpha = 1.0
|
transitionViewImpl.alpha = 1.0
|
||||||
transitionViewImpl.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1)
|
transitionViewImpl.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1)
|
||||||
}
|
}
|
||||||
self.contentContainerView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
|
contentContainerView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
|
||||||
|
self.controlsContainerView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
|
||||||
|
|
||||||
for transitionViewImpl in transitionViewsImpl {
|
for transitionViewImpl in transitionViewsImpl {
|
||||||
transition.setFrame(view: transitionViewImpl, frame: sourceLocalFrame)
|
transition.setFrame(view: transitionViewImpl, frame: sourceLocalFrame)
|
||||||
@ -967,7 +1292,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let component = self.component, let visibleItemView = self.visibleItems[component.slice.item.id]?.view.view {
|
if let component = self.component, let visibleItemView = self.visibleItems[component.slice.item.storyItem.id]?.view.view {
|
||||||
let innerScale = innerSourceLocalFrame.width / visibleItemView.bounds.width
|
let innerScale = innerSourceLocalFrame.width / visibleItemView.bounds.width
|
||||||
|
|
||||||
var adjustedInnerSourceLocalFrame = innerSourceLocalFrame
|
var adjustedInnerSourceLocalFrame = innerSourceLocalFrame
|
||||||
@ -1031,6 +1356,12 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
|
|
||||||
if self.component?.slice.item.storyItem.id != component.slice.item.storyItem.id {
|
if self.component?.slice.item.storyItem.id != component.slice.item.storyItem.id {
|
||||||
component.markAsSeen(StoryId(peerId: component.slice.peer.id, id: component.slice.item.storyItem.id))
|
component.markAsSeen(StoryId(peerId: component.slice.peer.id, id: component.slice.item.storyItem.id))
|
||||||
|
self.initializedOffset = false
|
||||||
|
}
|
||||||
|
var itemsTransition = transition
|
||||||
|
if let animateNextNavigationId = self.animateNextNavigationId, animateNextNavigationId == component.slice.item.storyItem.id {
|
||||||
|
self.animateNextNavigationId = nil
|
||||||
|
itemsTransition = transition.withAnimation(.curve(duration: 0.3, curve: .spring))
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.topContentGradientLayer.colors == nil {
|
if self.topContentGradientLayer.colors == nil {
|
||||||
@ -1074,8 +1405,6 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
self.contentDimView.backgroundColor = UIColor(white: 0.0, alpha: 0.3)
|
self.contentDimView.backgroundColor = UIColor(white: 0.0, alpha: 0.3)
|
||||||
}
|
}
|
||||||
|
|
||||||
//self.updatePreloads()
|
|
||||||
|
|
||||||
let wasPanning = self.component?.isPanning ?? false
|
let wasPanning = self.component?.isPanning ?? false
|
||||||
self.component = component
|
self.component = component
|
||||||
self.state = state
|
self.state = state
|
||||||
@ -1416,7 +1745,9 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
|
|
||||||
viewList.view.parentState = state
|
viewList.view.parentState = state
|
||||||
let viewListSize = viewList.view.update(
|
let viewListSize = viewList.view.update(
|
||||||
transition: viewListTransition,
|
transition: viewListTransition.withUserData(PeerListItemComponent.TransitionHint(
|
||||||
|
synchronousLoad: false
|
||||||
|
)),
|
||||||
component: AnyComponent(StoryItemSetViewListComponent(
|
component: AnyComponent(StoryItemSetViewListComponent(
|
||||||
externalState: viewList.externalState,
|
externalState: viewList.externalState,
|
||||||
context: component.context,
|
context: component.context,
|
||||||
@ -1439,6 +1770,11 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
|
|
||||||
if !self.displayViewList {
|
if !self.displayViewList {
|
||||||
self.displayViewList = true
|
self.displayViewList = true
|
||||||
|
|
||||||
|
self.preparingToDisplayViewList = true
|
||||||
|
self.updateScrolling(transition: .immediate)
|
||||||
|
self.preparingToDisplayViewList = false
|
||||||
|
|
||||||
self.state?.updated(transition: Transition(animation: .curve(duration: 0.4, curve: .spring)))
|
self.state?.updated(transition: Transition(animation: .curve(duration: 0.4, curve: .spring)))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1711,8 +2047,17 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
|
|
||||||
let contentFrame = CGRect(origin: CGPoint(x: 0.0, y: component.containerInsets.top - (contentSize.height - contentVisualHeight) * 0.5), size: contentSize)
|
let contentFrame = CGRect(origin: CGPoint(x: 0.0, y: component.containerInsets.top - (contentSize.height - contentVisualHeight) * 0.5), size: contentSize)
|
||||||
|
|
||||||
transition.setPosition(view: self.contentContainerView, position: contentFrame.center)
|
let itemLayout = ItemLayout(
|
||||||
transition.setBounds(view: self.contentContainerView, bounds: CGRect(origin: CGPoint(), size: contentFrame.size))
|
size: availableSize,
|
||||||
|
contentFrame: contentFrame,
|
||||||
|
contentVisualScale: contentVisualScale
|
||||||
|
)
|
||||||
|
self.itemLayout = itemLayout
|
||||||
|
|
||||||
|
transition.setFrame(view: self.itemsContainerView, frame: CGRect(origin: CGPoint(), size: CGSize(width: availableSize.width, height: component.containerInsets.top + floor(contentVisualHeight))))
|
||||||
|
|
||||||
|
transition.setPosition(view: self.controlsContainerView, position: contentFrame.center)
|
||||||
|
transition.setBounds(view: self.controlsContainerView, bounds: CGRect(origin: CGPoint(), size: contentFrame.size))
|
||||||
|
|
||||||
var transform = CATransform3DMakeScale(contentVisualScale, contentVisualScale, 1.0)
|
var transform = CATransform3DMakeScale(contentVisualScale, contentVisualScale, 1.0)
|
||||||
if let pinchState = component.pinchState {
|
if let pinchState = component.pinchState {
|
||||||
@ -1728,10 +2073,9 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
)
|
)
|
||||||
transform = CATransform3DScale(transform, pinchState.scale, pinchState.scale, 0.0)
|
transform = CATransform3DScale(transform, pinchState.scale, pinchState.scale, 0.0)
|
||||||
}
|
}
|
||||||
transition.setTransform(view: self.contentContainerView, transform: transform)
|
transition.setTransform(view: self.controlsContainerView, transform: transform)
|
||||||
|
|
||||||
//transition.setScale(view: self.contentContainerView, scale: contentVisualScale)
|
transition.setCornerRadius(layer: self.controlsContainerView.layer, cornerRadius: 12.0 * (1.0 / contentVisualScale))
|
||||||
transition.setCornerRadius(layer: self.contentContainerView.layer, cornerRadius: 12.0 * (1.0 / contentVisualScale))
|
|
||||||
|
|
||||||
if self.closeButtonIconView.image == nil {
|
if self.closeButtonIconView.image == nil {
|
||||||
self.closeButtonIconView.image = UIImage(bundleImageName: "Media Gallery/Close")?.withRenderingMode(.alwaysTemplate)
|
self.closeButtonIconView.image = UIImage(bundleImageName: "Media Gallery/Close")?.withRenderingMode(.alwaysTemplate)
|
||||||
@ -1741,9 +2085,10 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
let closeButtonFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: 50.0, height: 64.0))
|
let closeButtonFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: 50.0, height: 64.0))
|
||||||
transition.setFrame(view: self.closeButton, frame: closeButtonFrame)
|
transition.setFrame(view: self.closeButton, frame: closeButtonFrame)
|
||||||
transition.setFrame(view: self.closeButtonIconView, frame: CGRect(origin: CGPoint(x: floor((closeButtonFrame.width - image.size.width) * 0.5), y: floor((closeButtonFrame.height - image.size.height) * 0.5)), size: image.size))
|
transition.setFrame(view: self.closeButtonIconView, frame: CGRect(origin: CGPoint(x: floor((closeButtonFrame.width - image.size.width) * 0.5), y: floor((closeButtonFrame.height - image.size.height) * 0.5)), size: image.size))
|
||||||
transition.setAlpha(view: self.closeButton, alpha: (component.hideUI || self.displayViewList || self.isEditingStory) ? 0.0 : 1.0)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
transition.setAlpha(view: self.controlsContainerView, alpha: (component.hideUI || self.isEditingStory || self.displayViewList) ? 0.0 : 1.0)
|
||||||
|
|
||||||
let focusedItem: StoryContentItem? = component.slice.item
|
let focusedItem: StoryContentItem? = component.slice.item
|
||||||
let _ = focusedItem
|
let _ = focusedItem
|
||||||
/*if let currentSlice = self.currentSlice, let item = currentSlice.items.first(where: { $0.id == self.focusedItemId }) {
|
/*if let currentSlice = self.currentSlice, let item = currentSlice.items.first(where: { $0.id == self.focusedItemId }) {
|
||||||
@ -1751,13 +2096,12 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
}*/
|
}*/
|
||||||
|
|
||||||
var currentRightInfoItem: InfoItem?
|
var currentRightInfoItem: InfoItem?
|
||||||
if let focusedItem {
|
if focusedItem != nil {
|
||||||
if let rightInfoComponent = focusedItem.rightInfoComponent {
|
let rightInfoComponent = AnyComponent(StoryAvatarInfoComponent(context: component.context, peer: component.slice.peer))
|
||||||
if let rightInfoItem = self.rightInfoItem, rightInfoItem.component == focusedItem.rightInfoComponent {
|
if let rightInfoItem = self.rightInfoItem, rightInfoItem.component == rightInfoComponent {
|
||||||
currentRightInfoItem = rightInfoItem
|
currentRightInfoItem = rightInfoItem
|
||||||
} else {
|
} else {
|
||||||
currentRightInfoItem = InfoItem(component: rightInfoComponent)
|
currentRightInfoItem = InfoItem(component: rightInfoComponent)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1772,13 +2116,12 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var currentCenterInfoItem: InfoItem?
|
var currentCenterInfoItem: InfoItem?
|
||||||
if let focusedItem {
|
if focusedItem != nil {
|
||||||
if let centerInfoComponent = focusedItem.centerInfoComponent {
|
let centerInfoComponent = AnyComponent(StoryAuthorInfoComponent(context: component.context, peer: component.slice.peer, timestamp: component.slice.item.storyItem.timestamp))
|
||||||
if let centerInfoItem = self.centerInfoItem, centerInfoItem.component == focusedItem.centerInfoComponent {
|
if let centerInfoItem = self.centerInfoItem, centerInfoItem.component == centerInfoComponent {
|
||||||
currentCenterInfoItem = centerInfoItem
|
currentCenterInfoItem = centerInfoItem
|
||||||
} else {
|
} else {
|
||||||
currentCenterInfoItem = InfoItem(component: centerInfoComponent)
|
currentCenterInfoItem = InfoItem(component: centerInfoComponent)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1806,7 +2149,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
if let view = currentCenterInfoItem.view.view {
|
if let view = currentCenterInfoItem.view.view {
|
||||||
var animateIn = false
|
var animateIn = false
|
||||||
if view.superview == nil {
|
if view.superview == nil {
|
||||||
self.contentContainerView.insertSubview(view, belowSubview: self.closeButton)
|
self.controlsContainerView.insertSubview(view, belowSubview: self.closeButton)
|
||||||
animateIn = true
|
animateIn = true
|
||||||
}
|
}
|
||||||
transition.setFrame(view: view, frame: CGRect(origin: CGPoint(x: 0.0, y: 10.0), size: centerInfoItemSize))
|
transition.setFrame(view: view, frame: CGRect(origin: CGPoint(x: 0.0, y: 10.0), size: centerInfoItemSize))
|
||||||
@ -1815,7 +2158,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
//view.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
//view.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||||
}
|
}
|
||||||
|
|
||||||
transition.setAlpha(view: view, alpha: (component.hideUI || self.displayViewList || self.isEditingStory) ? 0.0 : 1.0)
|
transition.setAlpha(view: view, alpha: self.isEditingStory ? 0.0 : 1.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1836,7 +2179,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
if let view = currentRightInfoItem.view.view {
|
if let view = currentRightInfoItem.view.view {
|
||||||
var animateIn = false
|
var animateIn = false
|
||||||
if view.superview == nil {
|
if view.superview == nil {
|
||||||
self.contentContainerView.addSubview(view)
|
self.controlsContainerView.addSubview(view)
|
||||||
animateIn = true
|
animateIn = true
|
||||||
}
|
}
|
||||||
transition.setFrame(view: view, frame: CGRect(origin: CGPoint(x: contentFrame.width - 6.0 - rightInfoItemSize.width, y: 14.0), size: rightInfoItemSize))
|
transition.setFrame(view: view, frame: CGRect(origin: CGPoint(x: contentFrame.width - 6.0 - rightInfoItemSize.width, y: 14.0), size: rightInfoItemSize))
|
||||||
@ -1846,7 +2189,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
view.layer.animateScale(from: 0.5, to: 1.0, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
|
view.layer.animateScale(from: 0.5, to: 1.0, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring)
|
||||||
}
|
}
|
||||||
|
|
||||||
transition.setAlpha(view: view, alpha: (component.hideUI || self.displayViewList || self.isEditingStory) ? 0.0 : 1.0)
|
transition.setAlpha(view: view, alpha: self.isEditingStory ? 0.0 : 1.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1901,7 +2244,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
let closeFriendIconFrame = CGRect(origin: CGPoint(x: contentFrame.width - 6.0 - 52.0 - closeFriendIconSize.width, y: 21.0), size: closeFriendIconSize)
|
let closeFriendIconFrame = CGRect(origin: CGPoint(x: contentFrame.width - 6.0 - 52.0 - closeFriendIconSize.width, y: 21.0), size: closeFriendIconSize)
|
||||||
if let closeFriendIconView = closeFriendIcon.view {
|
if let closeFriendIconView = closeFriendIcon.view {
|
||||||
if closeFriendIconView.superview == nil {
|
if closeFriendIconView.superview == nil {
|
||||||
self.contentContainerView.addSubview(closeFriendIconView)
|
self.controlsContainerView.addSubview(closeFriendIconView)
|
||||||
closeFriendIconTransition.setFrame(view: closeFriendIconView, frame: closeFriendIconFrame)
|
closeFriendIconTransition.setFrame(view: closeFriendIconView, frame: closeFriendIconFrame)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1914,12 +2257,8 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
transition.setFrame(layer: self.topContentGradientLayer, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: contentFrame.width, height: gradientHeight)))
|
transition.setFrame(layer: self.topContentGradientLayer, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: contentFrame.width, height: gradientHeight)))
|
||||||
transition.setAlpha(layer: self.topContentGradientLayer, alpha: (component.hideUI || self.displayViewList || self.isEditingStory) ? 0.0 : 1.0)
|
transition.setAlpha(layer: self.topContentGradientLayer, alpha: (component.hideUI || self.displayViewList || self.isEditingStory) ? 0.0 : 1.0)
|
||||||
|
|
||||||
let itemSize = CGSize(width: contentFrame.width, height: floorToScreenPixels(contentFrame.width * 1.77778))
|
|
||||||
let itemLayout = ItemLayout(size: itemSize) //ItemLayout(size: CGSize(width: contentFrame.width, height: availableSize.height - component.containerInsets.top - 44.0 - bottomContentInsetWithoutInput))
|
|
||||||
self.itemLayout = itemLayout
|
|
||||||
|
|
||||||
let inputPanelFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - inputPanelSize.width) / 2.0), y: availableSize.height - inputPanelBottomInset - inputPanelSize.height), size: inputPanelSize)
|
let inputPanelFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - inputPanelSize.width) / 2.0), y: availableSize.height - inputPanelBottomInset - inputPanelSize.height), size: inputPanelSize)
|
||||||
var inputPanelAlpha: CGFloat = focusedItem?.isMy == true || component.hideUI || self.isEditingStory ? 0.0 : 1.0
|
var inputPanelAlpha: CGFloat = component.slice.peer.id == component.context.account.peerId || component.hideUI || self.isEditingStory ? 0.0 : 1.0
|
||||||
if case .regular = component.metrics.widthClass {
|
if case .regular = component.metrics.widthClass {
|
||||||
inputPanelAlpha *= component.visibilityFraction
|
inputPanelAlpha *= component.visibilityFraction
|
||||||
}
|
}
|
||||||
@ -1929,7 +2268,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var inputPanelOffset: CGFloat = 0.0
|
var inputPanelOffset: CGFloat = 0.0
|
||||||
if focusedItem?.isMy == false && !self.inputPanelExternalState.isEditing {
|
if component.slice.peer.id != component.context.account.peerId && !self.inputPanelExternalState.isEditing {
|
||||||
let bandingOffset = scrollingRubberBandingOffset(offset: component.verticalPanFraction * availableSize.height, bandingStart: 0.0, range: 10.0)
|
let bandingOffset = scrollingRubberBandingOffset(offset: component.verticalPanFraction * availableSize.height, bandingStart: 0.0, range: 10.0)
|
||||||
inputPanelOffset = -max(0.0, min(10.0, bandingOffset))
|
inputPanelOffset = -max(0.0, min(10.0, bandingOffset))
|
||||||
}
|
}
|
||||||
@ -2002,7 +2341,7 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
let captionFrame = CGRect(origin: CGPoint(x: 0.0, y: contentFrame.height - captionSize.height), size: captionSize)
|
let captionFrame = CGRect(origin: CGPoint(x: 0.0, y: contentFrame.height - captionSize.height), size: captionSize)
|
||||||
if let captionItemView = captionItem.view.view {
|
if let captionItemView = captionItem.view.view {
|
||||||
if captionItemView.superview == nil {
|
if captionItemView.superview == nil {
|
||||||
self.contentContainerView.insertSubview(captionItemView, aboveSubview: self.contentDimView)
|
self.controlsContainerView.insertSubview(captionItemView, aboveSubview: self.contentDimView)
|
||||||
}
|
}
|
||||||
captionItemTransition.setFrame(view: captionItemView, frame: captionFrame)
|
captionItemTransition.setFrame(view: captionItemView, frame: captionFrame)
|
||||||
captionItemTransition.setAlpha(view: captionItemView, alpha: (component.hideUI || self.displayViewList || self.isEditingStory || self.inputPanelExternalState.isEditing) ? 0.0 : 1.0)
|
captionItemTransition.setAlpha(view: captionItemView, alpha: (component.hideUI || self.displayViewList || self.isEditingStory || self.inputPanelExternalState.isEditing) ? 0.0 : 1.0)
|
||||||
@ -2272,7 +2611,12 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
transition.setAlpha(view: self.contentDimView, alpha: dimAlpha)
|
transition.setAlpha(view: self.contentDimView, alpha: dimAlpha)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.updateScrolling(transition: transition)
|
self.ignoreScrolling = true
|
||||||
|
transition.setFrame(view: self.scroller, frame: CGRect(origin: CGPoint(), size: availableSize))
|
||||||
|
self.ignoreScrolling = false
|
||||||
|
|
||||||
|
self.adjustScroller()
|
||||||
|
self.updateScrolling(transition: itemsTransition)
|
||||||
|
|
||||||
if let focusedItem, let visibleItem = self.visibleItems[focusedItem.storyItem.id], let index = focusedItem.position {
|
if let focusedItem, let visibleItem = self.visibleItems[focusedItem.storyItem.id], let index = focusedItem.position {
|
||||||
let navigationStripSideInset: CGFloat = 8.0
|
let navigationStripSideInset: CGFloat = 8.0
|
||||||
@ -2294,15 +2638,15 @@ public final class StoryItemSetContainerComponent: Component {
|
|||||||
if let navigationStripView = self.navigationStrip.view {
|
if let navigationStripView = self.navigationStrip.view {
|
||||||
if navigationStripView.superview == nil {
|
if navigationStripView.superview == nil {
|
||||||
navigationStripView.isUserInteractionEnabled = false
|
navigationStripView.isUserInteractionEnabled = false
|
||||||
self.contentContainerView.addSubview(navigationStripView)
|
self.controlsContainerView.addSubview(navigationStripView)
|
||||||
}
|
}
|
||||||
transition.setFrame(view: navigationStripView, frame: CGRect(origin: CGPoint(x: navigationStripSideInset, y: navigationStripTopInset), size: CGSize(width: availableSize.width - navigationStripSideInset * 2.0, height: 2.0)))
|
transition.setFrame(view: navigationStripView, frame: CGRect(origin: CGPoint(x: navigationStripSideInset, y: navigationStripTopInset), size: CGSize(width: availableSize.width - navigationStripSideInset * 2.0, height: 2.0)))
|
||||||
transition.setAlpha(view: navigationStripView, alpha: (component.hideUI || self.displayViewList || self.isEditingStory) ? 0.0 : 1.0)
|
transition.setAlpha(view: navigationStripView, alpha: self.isEditingStory ? 0.0 : 1.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
component.externalState.derivedMediaSize = contentFrame.size
|
component.externalState.derivedMediaSize = contentFrame.size
|
||||||
if focusedItem?.isMy == true {
|
if component.slice.peer.id == component.context.account.peerId {
|
||||||
component.externalState.derivedBottomInset = availableSize.height - contentFrame.maxY
|
component.externalState.derivedBottomInset = availableSize.height - contentFrame.maxY
|
||||||
} else {
|
} else {
|
||||||
component.externalState.derivedBottomInset = availableSize.height - min(inputPanelFrame.minY, contentFrame.maxY)
|
component.externalState.derivedBottomInset = availableSize.height - min(inputPanelFrame.minY, contentFrame.maxY)
|
||||||
|
|||||||
@ -1,33 +0,0 @@
|
|||||||
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
|
|
||||||
|
|
||||||
swift_library(
|
|
||||||
name = "StoryContentComponent",
|
|
||||||
module_name = "StoryContentComponent",
|
|
||||||
srcs = glob([
|
|
||||||
"Sources/**/*.swift",
|
|
||||||
]),
|
|
||||||
copts = [
|
|
||||||
"-warnings-as-errors",
|
|
||||||
],
|
|
||||||
deps = [
|
|
||||||
"//submodules/Display",
|
|
||||||
"//submodules/AsyncDisplayKit",
|
|
||||||
"//submodules/ComponentFlow",
|
|
||||||
"//submodules/TelegramUI/Components/Stories/StoryContainerScreen",
|
|
||||||
"//submodules/SSignalKit/SwiftSignalKit",
|
|
||||||
"//submodules/AccountContext",
|
|
||||||
"//submodules/TelegramCore",
|
|
||||||
"//submodules/Postbox",
|
|
||||||
"//submodules/PhotoResources",
|
|
||||||
"//submodules/MediaPlayer:UniversalMediaPlayer",
|
|
||||||
"//submodules/TelegramUniversalVideoContent",
|
|
||||||
"//submodules/AvatarNode",
|
|
||||||
"//submodules/Components/HierarchyTrackingLayer",
|
|
||||||
"//submodules/TelegramUI/Components/ButtonComponent",
|
|
||||||
"//submodules/Components/MultilineTextComponent",
|
|
||||||
"//submodules/TelegramPresentationData",
|
|
||||||
],
|
|
||||||
visibility = [
|
|
||||||
"//visibility:public",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
@ -95,7 +95,6 @@ import ICloudResources
|
|||||||
import LegacyCamera
|
import LegacyCamera
|
||||||
import LegacyInstantVideoController
|
import LegacyInstantVideoController
|
||||||
import StoryContainerScreen
|
import StoryContainerScreen
|
||||||
import StoryContentComponent
|
|
||||||
import MoreHeaderButton
|
import MoreHeaderButton
|
||||||
import VolumeButtons
|
import VolumeButtons
|
||||||
import ChatAvatarNavigationNode
|
import ChatAvatarNavigationNode
|
||||||
|
|||||||
@ -23,7 +23,6 @@ import UndoUI
|
|||||||
import WebsiteType
|
import WebsiteType
|
||||||
import GalleryData
|
import GalleryData
|
||||||
import StoryContainerScreen
|
import StoryContainerScreen
|
||||||
import StoryContentComponent
|
|
||||||
|
|
||||||
func openChatMessageImpl(_ params: OpenChatMessageParams) -> Bool {
|
func openChatMessageImpl(_ params: OpenChatMessageParams) -> Bool {
|
||||||
var story: TelegramMediaStory?
|
var story: TelegramMediaStory?
|
||||||
|
|||||||
@ -31,7 +31,6 @@ import PremiumUI
|
|||||||
import AuthorizationUI
|
import AuthorizationUI
|
||||||
import ChatFolderLinkPreviewScreen
|
import ChatFolderLinkPreviewScreen
|
||||||
import StoryContainerScreen
|
import StoryContainerScreen
|
||||||
import StoryContentComponent
|
|
||||||
|
|
||||||
private func defaultNavigationForPeerId(_ peerId: PeerId?, navigation: ChatControllerInteractionNavigateToPeer) -> ChatControllerInteractionNavigateToPeer {
|
private func defaultNavigationForPeerId(_ peerId: PeerId?, navigation: ChatControllerInteractionNavigateToPeer) -> ChatControllerInteractionNavigateToPeer {
|
||||||
if case .default = navigation {
|
if case .default = navigation {
|
||||||
|
|||||||
@ -89,7 +89,6 @@ import SendInviteLinkScreen
|
|||||||
import PeerInfoVisualMediaPaneNode
|
import PeerInfoVisualMediaPaneNode
|
||||||
import PeerInfoStoryGridScreen
|
import PeerInfoStoryGridScreen
|
||||||
import StoryContainerScreen
|
import StoryContainerScreen
|
||||||
import StoryContentComponent
|
|
||||||
import ChatAvatarNavigationNode
|
import ChatAvatarNavigationNode
|
||||||
import PeerReportScreen
|
import PeerReportScreen
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user