mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-07-19 01:31:33 +00:00
Monoforums
This commit is contained in:
parent
cf5223ab46
commit
60f2b98ee8
@ -35,6 +35,7 @@ public enum AvatarNodeClipStyle {
|
||||
case none
|
||||
case round
|
||||
case roundedRect
|
||||
case bubble
|
||||
}
|
||||
|
||||
private class AvatarNodeParameters: NSObject {
|
||||
@ -272,6 +273,25 @@ public final class AvatarEditOverlayNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
public final class AvatarNode: ASDisplayNode {
|
||||
public static func avatarBubbleMask(size: CGSize) -> UIImage! {
|
||||
return generateImage(size, rotatedContext: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
context.setFillColor(UIColor.white.cgColor)
|
||||
AvatarNode.addAvatarBubblePath(context: context, rect: CGRect(origin: CGPoint(), size: size))
|
||||
context.fillPath()
|
||||
})
|
||||
}
|
||||
|
||||
public static func addAvatarBubblePath(context: CGContext, rect: CGRect) {
|
||||
if let path = try? convertSvgPath("M60,30.274903 C60,46.843446 46.568544,60.274904 30,60.274904 C13.431458,60.274904 0,46.843446 0,30.274903 C0,23.634797 2.158635,17.499547 5.810547,12.529785 L6.036133,12.226074 C6.921364,10.896042 7.367402,8.104698 5.548828,5.316895 C3.606939,2.340088 1.186019,0.979668 2.399414,0.470215 C3.148032,0.156204 7.572027,0.000065 10.764648,1.790527 C12.148517,2.56662 13.2296,3.342422 14.09224,4.039734 C14.42622,4.309704 14.892063,4.349773 15.265962,4.138523 C19.618079,1.679604 24.644722,0.274902 30,0.274902 C46.568544,0.274902 60,13.70636 60,30.274903 Z ") {
|
||||
var transform = CGAffineTransformMake(1.0, 0.0, 0.0, -1.0, 0.0, 60.274904)
|
||||
transform = CGAffineTransformScale(transform, rect.width / 60.0, rect.height / 60.0)
|
||||
transform = CGAffineTransformTranslate(transform, rect.minX, rect.minY)
|
||||
let transformedPath = path.copy(using: &transform)!
|
||||
context.addPath(transformedPath)
|
||||
}
|
||||
}
|
||||
|
||||
public static let gradientColors: [[UIColor]] = [
|
||||
[UIColor(rgb: 0xff516a), UIColor(rgb: 0xff885e)],
|
||||
[UIColor(rgb: 0xffa85c), UIColor(rgb: 0xffcd6a)],
|
||||
@ -335,6 +355,7 @@ public final class AvatarNode: ASDisplayNode {
|
||||
private var theme: PresentationTheme?
|
||||
private var overrideImage: AvatarNodeImageOverride?
|
||||
public let imageNode: ImageNode
|
||||
private var imageNodeMask: UIImageView?
|
||||
public var editOverlayNode: AvatarEditOverlayNode?
|
||||
|
||||
private let imageReadyDisposable = MetaDisposable()
|
||||
@ -399,7 +420,7 @@ public final class AvatarNode: ASDisplayNode {
|
||||
self.displaysAsynchronously = true
|
||||
self.disableClearContentsOnHide = true
|
||||
|
||||
self.imageNode.isLayerBacked = true
|
||||
self.imageNode.isUserInteractionEnabled = false
|
||||
self.addSubnode(self.imageNode)
|
||||
|
||||
self.imageNode.contentUpdated = { [weak self] image in
|
||||
@ -434,6 +455,9 @@ public final class AvatarNode: ASDisplayNode {
|
||||
public func updateSize(size: CGSize) {
|
||||
self.imageNode.frame = CGRect(origin: CGPoint(), size: size)
|
||||
self.editOverlayNode?.frame = self.imageNode.frame
|
||||
if let imageNodeMask = self.imageNodeMask {
|
||||
imageNodeMask.frame = CGRect(origin: CGPoint(), size: size)
|
||||
}
|
||||
if !self.displaySuspended {
|
||||
self.setNeedsDisplay()
|
||||
self.editOverlayNode?.setNeedsDisplay()
|
||||
@ -678,6 +702,7 @@ public final class AvatarNode: ASDisplayNode {
|
||||
if self.params == params {
|
||||
return
|
||||
}
|
||||
let previousSize = self.params?.displayDimensions
|
||||
self.params = params
|
||||
|
||||
switch clipStyle {
|
||||
@ -690,6 +715,29 @@ public final class AvatarNode: ASDisplayNode {
|
||||
case .roundedRect:
|
||||
self.imageNode.clipsToBounds = true
|
||||
self.imageNode.cornerRadius = displayDimensions.height * 0.25
|
||||
case .bubble:
|
||||
break
|
||||
}
|
||||
|
||||
if case .bubble = clipStyle {
|
||||
var updateMask = false
|
||||
let imageNodeMask: UIImageView
|
||||
if let current = self.imageNodeMask {
|
||||
imageNodeMask = current
|
||||
updateMask = previousSize != params.displayDimensions
|
||||
} else {
|
||||
imageNodeMask = UIImageView()
|
||||
self.imageNodeMask = imageNodeMask
|
||||
self.imageNode.view.mask = imageNodeMask
|
||||
imageNodeMask.frame = self.imageNode.frame
|
||||
updateMask = true
|
||||
}
|
||||
if updateMask {
|
||||
imageNodeMask.image = AvatarNode.avatarBubbleMask(size: params.displayDimensions)
|
||||
}
|
||||
} else if self.imageNodeMask != nil {
|
||||
self.imageNodeMask = nil
|
||||
self.imageNode.view.mask = nil
|
||||
}
|
||||
|
||||
if let imageCache = genericContext.imageCache as? DirectMediaImageCache, let peer, let smallProfileImage = peer.smallProfileImage, let peerReference = PeerReference(peer._asPeer()) {
|
||||
@ -1472,3 +1520,4 @@ public final class AvatarNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -214,6 +214,9 @@ public func peerAvatarImage(postbox: Postbox, network: Network, peerReference: P
|
||||
case .roundedRect:
|
||||
context.addPath(UIBezierPath(roundedRect: CGRect(x: 0.0, y: 0.0, width: displayDimensions.width, height: displayDimensions.height).insetBy(dx: inset, dy: inset), cornerRadius: floor(displayDimensions.width * 0.25)).cgPath)
|
||||
context.clip()
|
||||
case .bubble:
|
||||
AvatarNode.addAvatarBubblePath(context: context, rect: CGRect(origin: CGPoint(), size: displayDimensions).insetBy(dx: inset, dy: inset))
|
||||
context.clip()
|
||||
}
|
||||
|
||||
var shouldBlur = false
|
||||
@ -265,6 +268,8 @@ public func peerAvatarImage(postbox: Postbox, network: Network, peerReference: P
|
||||
}
|
||||
case .roundedRect:
|
||||
break
|
||||
case .bubble:
|
||||
break
|
||||
}
|
||||
} else {
|
||||
if let emptyColor = emptyColor {
|
||||
@ -279,6 +284,10 @@ public func peerAvatarImage(postbox: Postbox, network: Network, peerReference: P
|
||||
context.beginPath()
|
||||
context.addPath(UIBezierPath(roundedRect: CGRect(x: 0.0, y: 0.0, width: displayDimensions.width, height: displayDimensions.height).insetBy(dx: inset, dy: inset), cornerRadius: floor(displayDimensions.width * 0.25)).cgPath)
|
||||
context.fillPath()
|
||||
case .bubble:
|
||||
context.beginPath()
|
||||
AvatarNode.addAvatarBubblePath(context: context, rect: CGRect(origin: CGPoint(), size: displayDimensions).insetBy(dx: inset, dy: inset))
|
||||
context.clip()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -295,6 +304,10 @@ public func peerAvatarImage(postbox: Postbox, network: Network, peerReference: P
|
||||
context.beginPath()
|
||||
context.addPath(UIBezierPath(roundedRect: CGRect(x: 0.0, y: 0.0, width: displayDimensions.width, height: displayDimensions.height).insetBy(dx: inset, dy: inset), cornerRadius: floor(displayDimensions.width * 0.25)).cgPath)
|
||||
context.fillPath()
|
||||
case .bubble:
|
||||
context.beginPath()
|
||||
AvatarNode.addAvatarBubblePath(context: context, rect: CGRect(origin: CGPoint(), size: displayDimensions).insetBy(dx: inset, dy: inset))
|
||||
context.clip()
|
||||
}
|
||||
}
|
||||
|
||||
@ -332,6 +345,10 @@ public func peerAvatarImage(postbox: Postbox, network: Network, peerReference: P
|
||||
context.beginPath()
|
||||
context.addPath(UIBezierPath(roundedRect: CGRect(x: 0.0, y: 0.0, width: displayDimensions.width, height: displayDimensions.height).insetBy(dx: inset, dy: inset), cornerRadius: floor(displayDimensions.width * 0.25)).cgPath)
|
||||
context.fillPath()
|
||||
case .bubble:
|
||||
context.beginPath()
|
||||
AvatarNode.addAvatarBubblePath(context: context, rect: CGRect(origin: CGPoint(), size: displayDimensions).insetBy(dx: inset, dy: inset))
|
||||
context.clip()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -1318,6 +1318,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
private var inlineNavigationMarkLayer: SimpleLayer?
|
||||
|
||||
public let titleNode: TextNode
|
||||
private var titleBadge: (backgroundView: UIImageView, textNode: TextNode)?
|
||||
public let authorNode: AuthorNode
|
||||
private var compoundHighlightingNode: LinkHighlightingNode?
|
||||
private var textArrowNode: ASImageNode?
|
||||
@ -1835,10 +1836,19 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
self.avatarNode.font = avatarPlaceholderFont(size: avatarFontSize)
|
||||
}
|
||||
}
|
||||
if peer.smallProfileImage != nil && overrideImage == nil {
|
||||
self.avatarNode.setPeerV2(context: item.context, theme: item.presentationData.theme, peer: peer, overrideImage: overrideImage, emptyColor: item.presentationData.theme.list.mediaPlaceholderColor, clipStyle: isForumAvatar ? .roundedRect : .round, synchronousLoad: synchronousLoads, displayDimensions: CGSize(width: avatarDiameter, height: avatarDiameter))
|
||||
let avatarClipStyle: AvatarNodeClipStyle
|
||||
if peerIsMonoforum {
|
||||
avatarClipStyle = .bubble
|
||||
} else if isForumAvatar {
|
||||
avatarClipStyle = .roundedRect
|
||||
} else {
|
||||
self.avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: peer, overrideImage: overrideImage, emptyColor: item.presentationData.theme.list.mediaPlaceholderColor, clipStyle: isForumAvatar ? .roundedRect : .round, synchronousLoad: synchronousLoads, displayDimensions: CGSize(width: 60.0, height: 60.0))
|
||||
avatarClipStyle = .round
|
||||
}
|
||||
|
||||
if peer.smallProfileImage != nil && overrideImage == nil {
|
||||
self.avatarNode.setPeerV2(context: item.context, theme: item.presentationData.theme, peer: peer, overrideImage: overrideImage, emptyColor: item.presentationData.theme.list.mediaPlaceholderColor, clipStyle: avatarClipStyle, synchronousLoad: synchronousLoads, displayDimensions: CGSize(width: avatarDiameter, height: avatarDiameter))
|
||||
} else {
|
||||
self.avatarNode.setPeer(context: item.context, theme: item.presentationData.theme, peer: peer, overrideImage: overrideImage, emptyColor: item.presentationData.theme.list.mediaPlaceholderColor, clipStyle: avatarClipStyle, synchronousLoad: synchronousLoads, displayDimensions: CGSize(width: 60.0, height: 60.0))
|
||||
}
|
||||
|
||||
if peer.isPremium && peer.id != item.context.account.peerId {
|
||||
@ -2028,6 +2038,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
let textLayout = TextNodeWithEntities.asyncLayout(self.textNode)
|
||||
let makeTrailingTextBadgeLayout = TextNode.asyncLayout(self.trailingTextBadgeNode)
|
||||
let titleLayout = TextNode.asyncLayout(self.titleNode)
|
||||
let titleBadgeLayout = TextNode.asyncLayout(self.titleBadge?.textNode)
|
||||
let authorLayout = self.authorNode.asyncLayout()
|
||||
let makeMeasureLayout = TextNode.asyncLayout(self.measureNode)
|
||||
let inputActivitiesLayout = self.inputActivitiesNode.asyncLayout()
|
||||
@ -2226,6 +2237,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
var textLeftCutout: CGFloat = 0.0
|
||||
var dateAttributedString: NSAttributedString?
|
||||
var titleAttributedString: NSAttributedString?
|
||||
var titleBadgeText: String?
|
||||
var badgeContent = ChatListBadgeContent.none
|
||||
var mentionBadgeContent = ChatListBadgeContent.none
|
||||
var statusState = ChatListStatusNodeState.none
|
||||
@ -3001,12 +3013,11 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
} else {
|
||||
textColor = theme.titleColor
|
||||
}
|
||||
//TODO:localize
|
||||
if case let .channel(channel) = itemPeer.peer, channel.flags.contains(.isMonoforum) {
|
||||
titleAttributedString = NSAttributedString(string: "\(displayTitle) Messages", font: titleFont, textColor: textColor)
|
||||
} else {
|
||||
titleAttributedString = NSAttributedString(string: displayTitle, font: titleFont, textColor: textColor)
|
||||
//TODO:localize
|
||||
titleBadgeText = "MESSAGES"
|
||||
}
|
||||
titleAttributedString = NSAttributedString(string: displayTitle, font: titleFont, textColor: textColor)
|
||||
}
|
||||
case .group:
|
||||
titleAttributedString = NSAttributedString(string: item.presentationData.strings.ChatList_ArchivedChatsTitle, font: titleFont, textColor: theme.titleColor)
|
||||
@ -3224,7 +3235,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
default:
|
||||
break
|
||||
}
|
||||
} else if case let .chat(itemPeer) = contentPeer, let peer = itemPeer.chatMainPeer {
|
||||
} else if case let .chat(itemPeer) = contentPeer, let peer = itemPeer.chatOrMonoforumMainPeer {
|
||||
if peer.isSubscription {
|
||||
isSubscription = true
|
||||
}
|
||||
@ -3369,7 +3380,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
} else if case let .peer(peer) = item.content, case let .channel(channel) = peer.peer.peer, channel.flags.contains(.isMonoforum) {
|
||||
if forumThread != nil || !topForumTopicItems.isEmpty {
|
||||
if let forumThread {
|
||||
isFirstForumThreadSelectable = forumThread.isUnread
|
||||
isFirstForumThreadSelectable = false
|
||||
forumThreads.append((id: forumThread.id, threadPeer: forumThread.threadPeer, title: NSAttributedString(string: forumThread.threadPeer?.compactDisplayTitle ?? " ", font: textFont, textColor: forumThread.isUnread || isSearching ? theme.authorNameColor : theme.messageTextColor), iconId: nil, iconColor: nil))
|
||||
}
|
||||
for topicItem in topForumTopicItems {
|
||||
@ -3463,11 +3474,19 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
titleAttributedString = NSAttributedString(string: " ", font: titleFont, textColor: theme.titleColor)
|
||||
}
|
||||
|
||||
let titleRectWidth = rawContentWidth - dateLayout.size.width - 10.0 - statusWidth - titleIconsWidth
|
||||
var titleRectWidth = rawContentWidth - dateLayout.size.width - 10.0 - statusWidth - titleIconsWidth
|
||||
var titleCutout: TextNodeCutout?
|
||||
if !titleLeftCutout.isZero {
|
||||
titleCutout = TextNodeCutout(topLeft: CGSize(width: titleLeftCutout, height: 10.0), topRight: nil, bottomRight: nil)
|
||||
}
|
||||
|
||||
var titleBadgeLayoutAndApply: (TextNodeLayout, () -> TextNode)?
|
||||
if let titleBadgeText {
|
||||
let titleBadgeLayoutAndApplyValue = titleBadgeLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: titleBadgeText, font: Font.semibold(11.0), textColor: theme.titleColor.withMultipliedAlpha(0.4)), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: titleRectWidth, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
|
||||
titleBadgeLayoutAndApply = titleBadgeLayoutAndApplyValue
|
||||
titleRectWidth = max(10.0, titleRectWidth - titleBadgeLayoutAndApplyValue.0.size.width - 8.0)
|
||||
}
|
||||
|
||||
let (titleLayout, titleApply) = titleLayout(TextNodeLayoutArguments(attributedString: titleAttributedString, backgroundColor: nil, maximumNumberOfLines: maxTitleLines, truncationType: .end, constrainedSize: CGSize(width: titleRectWidth, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: titleCutout, insets: UIEdgeInsets()))
|
||||
|
||||
var inputActivitiesSize: CGSize?
|
||||
@ -4244,6 +4263,36 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
let contentDelta = CGPoint(x: contentRect.origin.x - (strongSelf.titleNode.frame.minX - titleOffset), y: contentRect.origin.y - (strongSelf.titleNode.frame.minY - UIScreenPixel))
|
||||
let titleFrame = CGRect(origin: CGPoint(x: contentRect.origin.x + titleOffset, y: contentRect.origin.y + UIScreenPixel), size: titleLayout.size)
|
||||
strongSelf.titleNode.frame = titleFrame
|
||||
|
||||
if let (titleBadgeLayout, titleBadgeApply) = titleBadgeLayoutAndApply {
|
||||
let titleBadgeNode = titleBadgeApply()
|
||||
let backgroundView: UIImageView
|
||||
if let current = strongSelf.titleBadge {
|
||||
backgroundView = current.backgroundView
|
||||
} else {
|
||||
backgroundView = UIImageView(image: generateStretchableFilledCircleImage(radius: 4.0, color: .white)?.withRenderingMode(.alwaysTemplate))
|
||||
strongSelf.titleBadge = (backgroundView, titleBadgeNode)
|
||||
|
||||
strongSelf.mainContentContainerNode.view.addSubview(backgroundView)
|
||||
strongSelf.mainContentContainerNode.addSubnode(titleBadgeNode)
|
||||
}
|
||||
let titleBadgeFrame = CGRect(origin: CGPoint(x: titleFrame.maxX + titleIconsWidth + 10.0, y: titleFrame.minY + floor((titleFrame.height - titleBadgeLayout.size.height) * 0.5)), size: titleBadgeLayout.size)
|
||||
titleBadgeNode.frame = titleBadgeFrame
|
||||
|
||||
var titleBadgeBackgroundFrame = titleBadgeFrame.insetBy(dx: -4.0, dy: -2.0)
|
||||
titleBadgeBackgroundFrame.size.height -= 1.0
|
||||
backgroundView.frame = titleBadgeBackgroundFrame
|
||||
if item.presentationData.theme.overallDarkAppearance {
|
||||
backgroundView.tintColor = theme.titleColor.withMultipliedAlpha(0.1)
|
||||
} else {
|
||||
backgroundView.tintColor = theme.titleColor.withMultipliedAlpha(0.05)
|
||||
}
|
||||
} else if let titleBadge = strongSelf.titleBadge {
|
||||
strongSelf.titleBadge = nil
|
||||
titleBadge.backgroundView.removeFromSuperview()
|
||||
titleBadge.textNode.removeFromSupernode()
|
||||
}
|
||||
|
||||
let authorNodeFrame = CGRect(origin: CGPoint(x: contentRect.origin.x - 1.0, y: contentRect.minY + titleLayout.size.height), size: authorLayout)
|
||||
strongSelf.authorNode.frame = authorNodeFrame
|
||||
let textNodeFrame = CGRect(origin: CGPoint(x: contentRect.origin.x - 1.0, y: contentRect.minY + titleLayout.size.height - 1.0 + UIScreenPixel + (authorLayout.height.isZero ? 0.0 : (authorLayout.height - 3.0))), size: textLayout.size)
|
||||
|
@ -430,6 +430,9 @@ private func generateChatReplyOptionItems(selfController: ChatControllerImpl, ch
|
||||
if message.minAutoremoveOrClearTimeout == viewOnceTimeout {
|
||||
canReplyInAnotherChat = false
|
||||
}
|
||||
if let channel = message.peers[message.id.peerId] as? TelegramChannel, channel.isMonoForum {
|
||||
canReplyInAnotherChat = false
|
||||
}
|
||||
}
|
||||
|
||||
if canReplyInAnotherChat {
|
||||
|
@ -548,7 +548,7 @@ extension ChatControllerImpl {
|
||||
} else if let channel = peer as? TelegramChannel, channel.isMonoForum {
|
||||
if let linkedMonoforumId = channel.linkedMonoforumId, let mainPeer = peerView.peers[linkedMonoforumId] {
|
||||
//TODO:localize
|
||||
strongSelf.state.chatTitleContent = .custom("\(mainPeer.debugDisplayTitle) Messages", nil, false)
|
||||
strongSelf.state.chatTitleContent = .custom(mainPeer.debugDisplayTitle, nil, false)
|
||||
} else {
|
||||
strongSelf.state.chatTitleContent = .custom(channel.debugDisplayTitle, nil, false)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user