mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-15 21:45:19 +00:00
Various improvements
This commit is contained in:
parent
2aeeaaee44
commit
709acb3739
@ -9711,3 +9711,7 @@ Sorry for the inconvenience.";
|
||||
|
||||
"Story.Privacy.SaveSettings" = "Save Settings";
|
||||
"Story.Privacy.PostStory" = "Post Story";
|
||||
|
||||
"Story.Editor.Draft" = "Draft";
|
||||
"Story.Views.ViewsExpired" = "List of viewers becomes unavailable **24 hours** after the story expires.";
|
||||
"Story.Views.NoViews" = "Nobody has viewed\nyour story yet.";
|
||||
|
@ -371,7 +371,7 @@ public enum AvatarBackgroundColor {
|
||||
case violet
|
||||
}
|
||||
|
||||
public func generateAvatarImage(size: CGSize, icon: UIImage?, iconScale: CGFloat = 1.0, cornerRadius: CGFloat? = nil, circleCorners: Bool = false, color: AvatarBackgroundColor) -> UIImage? {
|
||||
public func generateAvatarImage(size: CGSize, icon: UIImage?, iconScale: CGFloat = 1.0, cornerRadius: CGFloat? = nil, circleCorners: Bool = false, color: AvatarBackgroundColor, customColors: [UIColor]? = nil) -> UIImage? {
|
||||
return generateImage(size, rotatedContext: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
context.beginPath()
|
||||
@ -405,10 +405,14 @@ public func generateAvatarImage(size: CGSize, icon: UIImage?, iconScale: CGFloat
|
||||
}
|
||||
|
||||
let colorsArray: NSArray
|
||||
if colorIndex == -1 {
|
||||
colorsArray = AvatarNode.grayscaleColors.map(\.cgColor) as NSArray
|
||||
if let customColors {
|
||||
colorsArray = customColors.map(\.cgColor) as NSArray
|
||||
} else {
|
||||
colorsArray = AvatarNode.gradientColors[colorIndex % AvatarNode.gradientColors.count].map(\.cgColor) as NSArray
|
||||
if colorIndex == -1 {
|
||||
colorsArray = AvatarNode.grayscaleColors.map(\.cgColor) as NSArray
|
||||
} else {
|
||||
colorsArray = AvatarNode.gradientColors[colorIndex % AvatarNode.gradientColors.count].map(\.cgColor) as NSArray
|
||||
}
|
||||
}
|
||||
|
||||
var locations: [CGFloat] = [1.0, 0.0]
|
||||
|
@ -131,6 +131,10 @@ final class CameraDevice {
|
||||
device.activeVideoMinFrameDuration = targetFPS.duration
|
||||
device.activeVideoMaxFrameDuration = targetFPS.duration
|
||||
}
|
||||
|
||||
if device.isLowLightBoostSupported {
|
||||
device.automaticallyEnablesLowLightBoostWhenAvailable = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -207,13 +207,10 @@ final class CameraOutput: NSObject {
|
||||
}
|
||||
|
||||
func configureVideoStabilization() {
|
||||
if let videoDataOutputConnection = self.videoOutput.connection(with: .video), videoDataOutputConnection.isVideoStabilizationSupported {
|
||||
videoDataOutputConnection.preferredVideoStabilizationMode = .standard
|
||||
// if #available(iOS 13.0, *) {
|
||||
// videoDataOutputConnection.preferredVideoStabilizationMode = .cinematicExtended
|
||||
// } else {
|
||||
// videoDataOutputConnection.preferredVideoStabilizationMode = .cinematic
|
||||
// }
|
||||
if let videoDataOutputConnection = self.videoOutput.connection(with: .video) {
|
||||
if videoDataOutputConnection.isVideoStabilizationSupported {
|
||||
videoDataOutputConnection.preferredVideoStabilizationMode = .standard
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1650,6 +1650,8 @@ private final class DrawingScreenComponent: CombinedComponent {
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0 - (hasFlip ? 46.0 : 0.0), y: topInset))
|
||||
.appear(.default(scale: true))
|
||||
.disappear(.default(scale: true))
|
||||
.opacity(!controlsAreVisible ? 0.0 : 1.0)
|
||||
.shadow(component.sourceHint == .storyEditor ? Shadow(color: UIColor(rgb: 0x000000, alpha: 0.35), radius: 2.0, offset: .zero) : nil)
|
||||
)
|
||||
}
|
||||
|
||||
@ -1682,6 +1684,7 @@ private final class DrawingScreenComponent: CombinedComponent {
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0 + (isFilled != nil ? 46.0 : 0.0), y: topInset))
|
||||
.appear(.default(scale: true))
|
||||
.disappear(.default(scale: true))
|
||||
.opacity(!controlsAreVisible ? 0.0 : 1.0)
|
||||
.shadow(component.sourceHint == .storyEditor ? Shadow(color: UIColor(rgb: 0x000000, alpha: 0.35), radius: 2.0, offset: .zero) : nil)
|
||||
)
|
||||
}
|
||||
|
@ -205,13 +205,6 @@ public final class DrawingStickerEntityView: DrawingEntityView {
|
||||
break
|
||||
}
|
||||
|
||||
// switch image.imageOrientation {
|
||||
// case .leftMirrored, .rightMirrored:
|
||||
// context.scaleBy(x: -1, y: 1)
|
||||
// default:
|
||||
// context.scaleBy(x: 1, y: -1)
|
||||
// }
|
||||
|
||||
context.draw(image.cgImage!, in: imageRect)
|
||||
}
|
||||
|
||||
|
@ -693,7 +693,7 @@ public final class DrawingTextEntityView: DrawingEntityView, UITextViewDelegate
|
||||
|
||||
func getRenderImage() -> UIImage? {
|
||||
let rect = self.bounds
|
||||
UIGraphicsBeginImageContextWithOptions(rect.size, false, 2.0)
|
||||
UIGraphicsBeginImageContextWithOptions(rect.size, false, 3.0)
|
||||
self.textView.drawHierarchy(in: rect, afterScreenUpdates: true)
|
||||
let image = UIGraphicsGetImageFromCurrentImageContext()
|
||||
UIGraphicsEndImageContext()
|
||||
@ -743,7 +743,7 @@ public final class DrawingTextEntityView: DrawingEntityView, UITextViewDelegate
|
||||
entity.referenceDrawingSize = CGSize(width: itemSize * 4.0, height: itemSize * 4.0)
|
||||
entity.scale = scale
|
||||
entity.position = textPosition.offsetBy(
|
||||
dx: (emojiTextPosition.x * cos(rotation) + emojiTextPosition.y * sin(rotation)) * scale,
|
||||
dx: (emojiTextPosition.x * cos(rotation) - emojiTextPosition.y * sin(rotation)) * scale,
|
||||
dy: (emojiTextPosition.y * cos(rotation) + emojiTextPosition.x * sin(rotation)) * scale
|
||||
)
|
||||
entity.rotation = rotation
|
||||
@ -1174,7 +1174,8 @@ private class DrawingTextLayoutManager: NSLayoutManager {
|
||||
addPath.addLine(to: CGPoint(x: d.x + self.radius, y: d.y))
|
||||
path.append(addPath)
|
||||
}
|
||||
|
||||
last = cur
|
||||
} else {
|
||||
last = cur
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ enum MediaPickerGridItemContent: Equatable {
|
||||
final class MediaPickerGridItem: GridItem {
|
||||
let content: MediaPickerGridItemContent
|
||||
let interaction: MediaPickerInteraction
|
||||
let strings: PresentationStrings
|
||||
let theme: PresentationTheme
|
||||
let selectable: Bool
|
||||
let enableAnimations: Bool
|
||||
@ -34,9 +35,10 @@ final class MediaPickerGridItem: GridItem {
|
||||
|
||||
let section: GridSection? = nil
|
||||
|
||||
init(content: MediaPickerGridItemContent, interaction: MediaPickerInteraction, theme: PresentationTheme, selectable: Bool, enableAnimations: Bool, stories: Bool) {
|
||||
init(content: MediaPickerGridItemContent, interaction: MediaPickerInteraction, strings: PresentationStrings, theme: PresentationTheme, selectable: Bool, enableAnimations: Bool, stories: Bool) {
|
||||
self.content = content
|
||||
self.interaction = interaction
|
||||
self.strings = strings
|
||||
self.theme = theme
|
||||
self.selectable = selectable
|
||||
self.enableAnimations = enableAnimations
|
||||
@ -55,7 +57,7 @@ final class MediaPickerGridItem: GridItem {
|
||||
return node
|
||||
case let .draft(draft, index):
|
||||
let node = MediaPickerGridItemNode()
|
||||
node.setup(interaction: self.interaction, draft: draft, index: index, theme: self.theme, selectable: self.selectable, enableAnimations: self.enableAnimations, stories: self.stories)
|
||||
node.setup(interaction: self.interaction, draft: draft, index: index, strings: self.strings, theme: self.theme, selectable: self.selectable, enableAnimations: self.enableAnimations, stories: self.stories)
|
||||
return node
|
||||
}
|
||||
}
|
||||
@ -71,7 +73,7 @@ final class MediaPickerGridItem: GridItem {
|
||||
case let .media(media, index):
|
||||
node.setup(interaction: self.interaction, media: media, index: index, theme: self.theme, selectable: self.selectable, enableAnimations: self.enableAnimations, stories: self.stories)
|
||||
case let .draft(draft, index):
|
||||
node.setup(interaction: self.interaction, draft: draft, index: index, theme: self.theme, selectable: self.selectable, enableAnimations: self.enableAnimations, stories: self.stories)
|
||||
node.setup(interaction: self.interaction, draft: draft, index: index, strings: self.strings, theme: self.theme, selectable: self.selectable, enableAnimations: self.enableAnimations, stories: self.stories)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -287,7 +289,7 @@ final class MediaPickerGridItemNode: GridItemNode {
|
||||
}
|
||||
}
|
||||
|
||||
func setup(interaction: MediaPickerInteraction, draft: MediaEditorDraft, index: Int, theme: PresentationTheme, selectable: Bool, enableAnimations: Bool, stories: Bool) {
|
||||
func setup(interaction: MediaPickerInteraction, draft: MediaEditorDraft, index: Int, strings: PresentationStrings, theme: PresentationTheme, selectable: Bool, enableAnimations: Bool, stories: Bool) {
|
||||
self.interaction = interaction
|
||||
self.theme = theme
|
||||
self.selectable = selectable
|
||||
@ -310,7 +312,7 @@ final class MediaPickerGridItemNode: GridItemNode {
|
||||
}
|
||||
|
||||
if self.draftNode.supernode == nil {
|
||||
self.draftNode.attributedText = NSAttributedString(string: "Draft", font: Font.semibold(12.0), textColor: .white)
|
||||
self.draftNode.attributedText = NSAttributedString(string: strings.Story_Editor_Draft, font: Font.semibold(12.0), textColor: .white)
|
||||
self.addSubnode(self.draftNode)
|
||||
}
|
||||
|
||||
|
@ -61,8 +61,8 @@ private struct MediaPickerGridEntry: Comparable, Identifiable {
|
||||
return lhs.stableId < rhs.stableId
|
||||
}
|
||||
|
||||
func item(context: AccountContext, interaction: MediaPickerInteraction, theme: PresentationTheme) -> MediaPickerGridItem {
|
||||
return MediaPickerGridItem(content: self.content, interaction: interaction, theme: theme, selectable: self.selectable, enableAnimations: context.sharedContext.energyUsageSettings.fullTranslucency, stories: self.stories)
|
||||
func item(context: AccountContext, interaction: MediaPickerInteraction, strings: PresentationStrings, theme: PresentationTheme) -> MediaPickerGridItem {
|
||||
return MediaPickerGridItem(content: self.content, interaction: interaction, strings: strings, theme: theme, selectable: self.selectable, enableAnimations: context.sharedContext.energyUsageSettings.fullTranslucency, stories: self.stories)
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,12 +72,12 @@ private struct MediaPickerGridTransaction {
|
||||
let updates: [GridNodeUpdateItem]
|
||||
let scrollToItem: GridNodeScrollToItem?
|
||||
|
||||
init(previousList: [MediaPickerGridEntry], list: [MediaPickerGridEntry], context: AccountContext, interaction: MediaPickerInteraction, theme: PresentationTheme, scrollToItem: GridNodeScrollToItem?) {
|
||||
init(previousList: [MediaPickerGridEntry], list: [MediaPickerGridEntry], context: AccountContext, interaction: MediaPickerInteraction, strings: PresentationStrings, theme: PresentationTheme, scrollToItem: GridNodeScrollToItem?) {
|
||||
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: previousList, rightList: list)
|
||||
|
||||
self.deletions = deleteIndices
|
||||
self.insertions = indicesAndItems.map { GridNodeInsertItem(index: $0.0, item: $0.1.item(context: context, interaction: interaction, theme: theme), previousIndex: $0.2) }
|
||||
self.updates = updateIndices.map { GridNodeUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, interaction: interaction, theme: theme)) }
|
||||
self.insertions = indicesAndItems.map { GridNodeInsertItem(index: $0.0, item: $0.1.item(context: context, interaction: interaction, strings: strings, theme: theme), previousIndex: $0.2) }
|
||||
self.updates = updateIndices.map { GridNodeUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(context: context, interaction: interaction, strings: strings, theme: theme)) }
|
||||
|
||||
self.scrollToItem = scrollToItem
|
||||
}
|
||||
@ -671,7 +671,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
||||
scrollToItem = GridNodeScrollToItem(index: entries.count - 1, position: .bottom(0.0), transition: .immediate, directionHint: .down, adjustForSection: false)
|
||||
}
|
||||
|
||||
let transaction = MediaPickerGridTransaction(previousList: previousEntries, list: entries, context: controller.context, interaction: interaction, theme: self.presentationData.theme, scrollToItem: scrollToItem)
|
||||
let transaction = MediaPickerGridTransaction(previousList: previousEntries, list: entries, context: controller.context, interaction: interaction, strings: self.presentationData.strings, theme: self.presentationData.theme, scrollToItem: scrollToItem)
|
||||
self.enqueueTransaction(transaction)
|
||||
|
||||
if !self.didSetReady {
|
||||
|
@ -1956,7 +1956,7 @@ public class CameraScreen: ViewController {
|
||||
}
|
||||
|
||||
func presentDualCameraTooltip() {
|
||||
guard let sourceView = self.componentHost.findTaggedView(tag: dualButtonTag) else {
|
||||
guard let sourceView = self.componentHost.findTaggedView(tag: dualButtonTag), self.cameraState.isDualCameraEnabled else {
|
||||
return
|
||||
}
|
||||
|
||||
@ -2131,6 +2131,8 @@ public class CameraScreen: ViewController {
|
||||
self.maybePresentTooltips()
|
||||
} else if case .notDetermined = self.cameraAuthorizationStatus {
|
||||
self.requestDeviceAccess()
|
||||
} else if case .notDetermined = self.microphoneAuthorizationStatus {
|
||||
self.requestDeviceAccess()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -253,6 +253,7 @@ public final class MediaEditor {
|
||||
self.subject = subject
|
||||
if let values {
|
||||
self.values = values
|
||||
self.updateRenderChain()
|
||||
} else {
|
||||
self.values = MediaEditorValues(
|
||||
originalDimensions: subject.dimensions,
|
||||
|
@ -12,6 +12,39 @@ import TelegramAnimatedStickerNode
|
||||
import YuvConversion
|
||||
import StickerResources
|
||||
|
||||
private func prerenderTextTransformations(entity: DrawingTextEntity, image: UIImage, colorSpace: CGColorSpace) -> MediaEditorComposerStaticEntity {
|
||||
let imageSize = image.size
|
||||
|
||||
let angle = -entity.rotation
|
||||
let scale = entity.scale
|
||||
|
||||
let rotatedSize = CGSize(
|
||||
width: abs(imageSize.width * cos(angle)) + abs(imageSize.height * sin(angle)),
|
||||
height: abs(imageSize.width * sin(angle)) + abs(imageSize.height * cos(angle))
|
||||
)
|
||||
let newSize = CGSize(width: rotatedSize.width * scale, height: rotatedSize.height * scale)
|
||||
|
||||
let newImage = generateImage(newSize, contextGenerator: { size, context in
|
||||
context.setAllowsAntialiasing(true)
|
||||
context.setShouldAntialias(true)
|
||||
context.clear(CGRect(origin: .zero, size: size))
|
||||
context.translateBy(x: newSize.width * 0.5, y: newSize.height * 0.5)
|
||||
context.rotate(by: angle)
|
||||
context.scaleBy(x: scale, y: scale)
|
||||
let drawRect = CGRect(
|
||||
x: -imageSize.width * 0.5,
|
||||
y: -imageSize.height * 0.5,
|
||||
width: imageSize.width,
|
||||
height: imageSize.height
|
||||
)
|
||||
if let cgImage = image.cgImage {
|
||||
context.draw(cgImage, in: drawRect)
|
||||
}
|
||||
})!
|
||||
|
||||
return MediaEditorComposerStaticEntity(image: CIImage(image: newImage, options: [.colorSpace: colorSpace])!, position: entity.position, scale: 1.0, rotation: 0.0, baseSize: nil, baseScale: 0.333, mirrored: false)
|
||||
}
|
||||
|
||||
func composerEntitiesForDrawingEntity(account: Account, entity: DrawingEntity, colorSpace: CGColorSpace, tintColor: UIColor? = nil) -> [MediaEditorComposerEntity] {
|
||||
if let entity = entity as? DrawingStickerEntity {
|
||||
let content: MediaEditorComposerStickerEntity.Content
|
||||
@ -35,7 +68,9 @@ func composerEntitiesForDrawingEntity(account: Account, entity: DrawingEntity, c
|
||||
return [MediaEditorComposerStaticEntity(image: image, position: CGPoint(x: entity.drawingSize.width * 0.5, y: entity.drawingSize.height * 0.5), scale: 1.0, rotation: 0.0, baseSize: entity.drawingSize, baseScale: nil, mirrored: false)]
|
||||
} else if let entity = entity as? DrawingTextEntity {
|
||||
var entities: [MediaEditorComposerEntity] = []
|
||||
entities.append(MediaEditorComposerStaticEntity(image: image, position: entity.position, scale: entity.scale, rotation: entity.rotation, baseSize: nil, baseScale: 0.5, mirrored: false))
|
||||
entities.append(prerenderTextTransformations(entity: entity, image: renderImage, colorSpace: colorSpace))
|
||||
|
||||
// entities.append(MediaEditorComposerStaticEntity(image: image, position: entity.position, scale: entity.scale, rotation: entity.rotation, baseSize: nil, baseScale: 0.333, mirrored: false))
|
||||
if let renderSubEntities = entity.renderSubEntities {
|
||||
for subEntity in renderSubEntities {
|
||||
entities.append(contentsOf: composerEntitiesForDrawingEntity(account: account, entity: subEntity, colorSpace: colorSpace, tintColor: entity.color.toUIColor()))
|
||||
|
@ -2740,7 +2740,27 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
|
||||
PHImageManager.default().requestImage(for: asset, targetSize: PHImageManagerMaximumSize, contentMode: .default, options: options) { [weak self] image, _ in
|
||||
if let self, let image {
|
||||
Queue.mainQueue().async {
|
||||
self.interaction?.insertEntity(DrawingStickerEntity(content: .image(image, .rectangle)), scale: 2.5)
|
||||
func roundedImageWithTransparentCorners(image: UIImage, cornerRadius: CGFloat) -> UIImage? {
|
||||
let rect = CGRect(origin: .zero, size: image.size)
|
||||
UIGraphicsBeginImageContextWithOptions(image.size, false, image.scale)
|
||||
let context = UIGraphicsGetCurrentContext()
|
||||
|
||||
if let context = context {
|
||||
let path = UIBezierPath(roundedRect: rect, cornerRadius: cornerRadius)
|
||||
|
||||
context.addPath(path.cgPath)
|
||||
context.clip()
|
||||
|
||||
image.draw(in: rect)
|
||||
}
|
||||
|
||||
let newImage = UIGraphicsGetImageFromCurrentImageContext()
|
||||
UIGraphicsEndImageContext()
|
||||
|
||||
return newImage
|
||||
}
|
||||
let updatedImage = roundedImageWithTransparentCorners(image: image, cornerRadius: 180.0)!
|
||||
self.interaction?.insertEntity(DrawingStickerEntity(content: .image(updatedImage, .rectangle)), scale: 2.5)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -146,26 +146,26 @@ final class CategoryListItemComponent: Component {
|
||||
}
|
||||
|
||||
if (self.component?.iconName != component.iconName || self.component?.color != component.color), let iconName = component.iconName {
|
||||
let mappedColor: AvatarBackgroundColor
|
||||
let colors: [UIColor]
|
||||
var iconScale: CGFloat = 1.0
|
||||
switch component.color {
|
||||
case .blue:
|
||||
mappedColor = .blue
|
||||
colors = [UIColor(rgb: 0x4faaff), UIColor(rgb: 0x017aff)]
|
||||
case .yellow:
|
||||
mappedColor = .yellow
|
||||
colors = [UIColor(rgb: 0xffb643), UIColor(rgb: 0xf69a36)]
|
||||
case .green:
|
||||
mappedColor = .green
|
||||
colors = [UIColor(rgb: 0x87d93a), UIColor(rgb: 0x31b73b)]
|
||||
case .purple:
|
||||
mappedColor = .purple
|
||||
colors = [UIColor(rgb: 0xc36eff), UIColor(rgb: 0x8c61fa)]
|
||||
case .red:
|
||||
mappedColor = .red
|
||||
colors = [UIColor(rgb: 0xc36eff), UIColor(rgb: 0x8c61fa)]
|
||||
case .violet:
|
||||
mappedColor = .violet
|
||||
colors = [UIColor(rgb: 0xc36eff), UIColor(rgb: 0x8c61fa)]
|
||||
}
|
||||
|
||||
iconScale = 1.0
|
||||
|
||||
self.iconView.image = generateAvatarImage(size: CGSize(width: 40.0, height: 40.0), icon: generateTintedImage(image: UIImage(bundleImageName: iconName), color: .white), iconScale: iconScale, cornerRadius: 20.0, color: mappedColor)
|
||||
self.iconView.image = generateAvatarImage(size: CGSize(width: 40.0, height: 40.0), icon: generateTintedImage(image: UIImage(bundleImageName: iconName), color: .white), iconScale: iconScale, cornerRadius: 20.0, color: .blue, customColors: colors.reversed())
|
||||
}
|
||||
|
||||
self.component = component
|
||||
|
@ -1678,7 +1678,9 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
self.sendMessageContext.setup(context: component.context, view: self, inputPanelExternalState: self.inputPanelExternalState, keyboardInputData: component.keyboardInputData)
|
||||
}
|
||||
|
||||
var itemChanged = false
|
||||
if self.component?.slice.item.storyItem.id != component.slice.item.storyItem.id {
|
||||
itemChanged = self.component != nil
|
||||
self.initializedOffset = false
|
||||
|
||||
if let inputPanelView = self.inputPanel.view as? MessageInputPanelComponent.View {
|
||||
@ -2441,7 +2443,7 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
transition.setAlpha(view: soundButtonView, alpha: soundAlpha)
|
||||
|
||||
if isVideo {
|
||||
headerRightOffset -= soundButtonSize.width + 16.0
|
||||
headerRightOffset -= soundButtonSize.width + 13.0
|
||||
}
|
||||
}
|
||||
|
||||
@ -2460,7 +2462,7 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
|
||||
if let storyPrivacyIcon {
|
||||
let privacyIcon: ComponentView<Empty>
|
||||
var privacyIconTransition = transition
|
||||
var privacyIconTransition: Transition = itemChanged ? .immediate : .easeInOut(duration: 0.2)
|
||||
if let current = self.privacyIcon {
|
||||
privacyIcon = current
|
||||
} else {
|
||||
@ -3026,7 +3028,6 @@ public final class StoryItemSetContainerComponent: Component {
|
||||
}
|
||||
component.controller()?.push(controller)
|
||||
}), elevatedLayout: false, animateInAsReplacement: false, action: { _ in true })
|
||||
//strongSelf.currentUndoController = undoController
|
||||
component.controller()?.present(undoController, in: .current)
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,8 @@ import StoryFooterPanelComponent
|
||||
import PeerListItemComponent
|
||||
import AnimatedStickerComponent
|
||||
import AvatarNode
|
||||
import Markdown
|
||||
import ButtonComponent
|
||||
|
||||
final class StoryItemSetViewListComponent: Component {
|
||||
final class AnimationHint {
|
||||
@ -58,6 +60,7 @@ final class StoryItemSetViewListComponent: Component {
|
||||
let moreAction: (UIView, ContextGesture?) -> Void
|
||||
let openPeer: (EnginePeer) -> Void
|
||||
let openPeerStories: (EnginePeer, AvatarNode) -> Void
|
||||
let openPremiumIntro: () -> Void
|
||||
|
||||
init(
|
||||
externalState: ExternalState,
|
||||
@ -76,7 +79,8 @@ final class StoryItemSetViewListComponent: Component {
|
||||
deleteAction: @escaping () -> Void,
|
||||
moreAction: @escaping (UIView, ContextGesture?) -> Void,
|
||||
openPeer: @escaping (EnginePeer) -> Void,
|
||||
openPeerStories: @escaping (EnginePeer, AvatarNode) -> Void
|
||||
openPeerStories: @escaping (EnginePeer, AvatarNode) -> Void,
|
||||
openPremiumIntro: @escaping () -> Void
|
||||
) {
|
||||
self.externalState = externalState
|
||||
self.context = context
|
||||
@ -95,6 +99,7 @@ final class StoryItemSetViewListComponent: Component {
|
||||
self.moreAction = moreAction
|
||||
self.openPeer = openPeer
|
||||
self.openPeerStories = openPeerStories
|
||||
self.openPremiumIntro = openPremiumIntro
|
||||
}
|
||||
|
||||
static func ==(lhs: StoryItemSetViewListComponent, rhs: StoryItemSetViewListComponent) -> Bool {
|
||||
@ -826,32 +831,39 @@ final class StoryItemSetViewListComponent: Component {
|
||||
transition: emptyTransition,
|
||||
component: AnyComponent(AnimatedStickerComponent(
|
||||
account: component.context.account,
|
||||
animation: AnimatedStickerComponent.Animation(source: .bundle(name: "ChatListNoResults"), loop: true),
|
||||
animation: AnimatedStickerComponent.Animation(source: .bundle(name: "Burn"), loop: true),
|
||||
size: CGSize(width: 140.0, height: 140.0)
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: 140.0, height: 140.0)
|
||||
)
|
||||
|
||||
let fontSize: CGFloat = 16.0
|
||||
let body = MarkdownAttributeSet(font: Font.regular(fontSize), textColor: component.theme.list.itemSecondaryTextColor)
|
||||
let bold = MarkdownAttributeSet(font: Font.semibold(fontSize), textColor: component.theme.list.itemSecondaryTextColor)
|
||||
let link = MarkdownAttributeSet(font: Font.semibold(fontSize), textColor: component.theme.list.itemAccentColor)
|
||||
let attributes = MarkdownAttributes(body: body, bold: bold, link: link, linkAttribute: { _ in nil })
|
||||
|
||||
let text: String
|
||||
if component.storyItem.expirationTimestamp <= Int32(Date().timeIntervalSince1970) {
|
||||
text = "List of viewers isn’t available after\n24 hours of story expiration."
|
||||
text = component.strings.Story_Views_ViewsExpired
|
||||
} else {
|
||||
text = "Nobody has viewed\nyour story yet."
|
||||
text = component.strings.Story_Views_NoViews
|
||||
}
|
||||
let textSize = emptyText.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: text, font: Font.regular(17.0), textColor: component.theme.list.itemSecondaryTextColor)),
|
||||
text: .markdown(text: text, attributes: attributes),
|
||||
horizontalAlignment: .center,
|
||||
maximumNumberOfLines: 0
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: min(300.0, availableSize.width - 16.0 * 2.0), height: 1000.0)
|
||||
containerSize: CGSize(width: min(330.0, availableSize.width - 16.0 * 2.0), height: 1000.0)
|
||||
)
|
||||
|
||||
|
||||
let emptyContentSpacing: CGFloat = 20.0
|
||||
var emptyContentY = navigationBarFrame.minY + floor((availableSize.height - navigationBarFrame.minY - (emptyIconSize.height - emptyContentSpacing - textSize.height)) * 0.5) - 60.0
|
||||
let emptyContentHeight = emptyIconSize.height + emptyContentSpacing + textSize.height
|
||||
var emptyContentY = navigationBarFrame.minY + floor((availableSize.height - navigationBarFrame.minY - emptyContentHeight) * 0.5)
|
||||
|
||||
if let emptyIconView = emptyIcon.view {
|
||||
if emptyIconView.superview == nil {
|
||||
@ -866,6 +878,7 @@ final class StoryItemSetViewListComponent: Component {
|
||||
self.insertSubview(emptyTextView, belowSubview: self.scrollView)
|
||||
}
|
||||
emptyTransition.setFrame(view: emptyTextView, frame: CGRect(origin: CGPoint(x: floor((availableSize.width - textSize.width) * 0.5), y: emptyContentY), size: textSize))
|
||||
emptyContentY += textSize.height + emptyContentSpacing * 2.0
|
||||
}
|
||||
} else {
|
||||
if let emptyIcon = self.emptyIcon {
|
||||
|
@ -56,7 +56,7 @@ final class StoryPrivacyIconComponent: Component {
|
||||
let colors: [CGColor]
|
||||
var icon: UIImage?
|
||||
|
||||
if let previousPrivacy, previousPrivacy != component.privacy {
|
||||
if let previousPrivacy, previousPrivacy != component.privacy, !transition.animation.isImmediate {
|
||||
let disappearingBackgroundView = UIImageView(image: self.image)
|
||||
disappearingBackgroundView.frame = self.bounds
|
||||
self.insertSubview(disappearingBackgroundView, at: 0)
|
||||
|
@ -76,6 +76,12 @@ public final class TextFieldComponent: Component {
|
||||
}
|
||||
}
|
||||
|
||||
public enum FormatMenuAvailability: Equatable {
|
||||
case available
|
||||
case locked
|
||||
case none
|
||||
}
|
||||
|
||||
public let context: AccountContext
|
||||
public let strings: PresentationStrings
|
||||
public let externalState: ExternalState
|
||||
@ -83,6 +89,8 @@ public final class TextFieldComponent: Component {
|
||||
public let textColor: UIColor
|
||||
public let insets: UIEdgeInsets
|
||||
public let hideKeyboard: Bool
|
||||
public let formatMenuAvailability: FormatMenuAvailability
|
||||
public let lockedFormatAction: () -> Void
|
||||
public let present: (ViewController) -> Void
|
||||
public let paste: (PasteData) -> Void
|
||||
|
||||
@ -94,6 +102,8 @@ public final class TextFieldComponent: Component {
|
||||
textColor: UIColor,
|
||||
insets: UIEdgeInsets,
|
||||
hideKeyboard: Bool,
|
||||
formatMenuAvailability: FormatMenuAvailability,
|
||||
lockedFormatAction: @escaping () -> Void,
|
||||
present: @escaping (ViewController) -> Void,
|
||||
paste: @escaping (PasteData) -> Void
|
||||
) {
|
||||
@ -104,6 +114,8 @@ public final class TextFieldComponent: Component {
|
||||
self.textColor = textColor
|
||||
self.insets = insets
|
||||
self.hideKeyboard = hideKeyboard
|
||||
self.formatMenuAvailability = formatMenuAvailability
|
||||
self.lockedFormatAction = lockedFormatAction
|
||||
self.present = present
|
||||
self.paste = paste
|
||||
}
|
||||
@ -127,6 +139,9 @@ public final class TextFieldComponent: Component {
|
||||
if lhs.hideKeyboard != rhs.hideKeyboard {
|
||||
return false
|
||||
}
|
||||
if lhs.formatMenuAvailability != rhs.formatMenuAvailability {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@ -386,8 +401,23 @@ public final class TextFieldComponent: Component {
|
||||
guard let component = self.component, !textView.attributedText.string.isEmpty && textView.selectedRange.length > 0 else {
|
||||
return UIMenu(children: suggestedActions)
|
||||
}
|
||||
|
||||
let strings = component.strings
|
||||
|
||||
if case .none = component.formatMenuAvailability {
|
||||
return UIMenu(children: suggestedActions)
|
||||
}
|
||||
|
||||
if case .locked = component.formatMenuAvailability {
|
||||
var updatedActions = suggestedActions
|
||||
let formatAction = UIAction(title: strings.TextFormat_Format, image: nil) { [weak self] action in
|
||||
if let self {
|
||||
self.component?.lockedFormatAction()
|
||||
}
|
||||
}
|
||||
updatedActions.insert(formatAction, at: 1)
|
||||
return UIMenu(children: updatedActions)
|
||||
}
|
||||
|
||||
var actions: [UIAction] = [
|
||||
UIAction(title: strings.TextFormat_Bold, image: nil) { [weak self] action in
|
||||
if let self {
|
||||
|
@ -249,7 +249,7 @@ public final class AccountContextImpl: AccountContext {
|
||||
self.account = account
|
||||
self.engine = TelegramEngine(account: account)
|
||||
|
||||
self.userLimits = EngineConfiguration.UserLimits(UserLimitsConfiguration.defaultValue)
|
||||
self.userLimits = EngineConfiguration.UserLimits(UserLimitsConfiguration.defaultValue, isPremium: false)
|
||||
|
||||
self.downloadedMediaStoreManager = DownloadedMediaStoreManagerImpl(postbox: account.postbox, accountManager: sharedContext.accountManager)
|
||||
|
||||
|
@ -82,8 +82,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
||||
|
||||
var displayUndo = true
|
||||
var undoText = presentationData.strings.Undo_Undo
|
||||
var undoTextColor = UIColor(rgb: 0x5ac8fa)
|
||||
undoTextColor = presentationData.theme.list.itemAccentColor.withMultiplied(hue: 1.0, saturation: 0.64, brightness: 1.08)
|
||||
var undoTextColor = presentationData.theme.list.itemAccentColor.withMultiplied(hue: 0.933, saturation: 0.61, brightness: 1.0)
|
||||
|
||||
if presentationData.theme.overallDarkAppearance {
|
||||
self.animationBackgroundColor = presentationData.theme.rootController.tabBar.backgroundColor
|
||||
@ -174,16 +173,20 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
||||
|
||||
let body = MarkdownAttributeSet(font: Font.regular(14.0), textColor: .white)
|
||||
let bold = MarkdownAttributeSet(font: Font.semibold(14.0), textColor: .white)
|
||||
let attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: body, bold: bold, link: body, linkAttribute: { _ in return nil }), textAlignment: .natural)
|
||||
let link = MarkdownAttributeSet(font: Font.semibold(14.0), textColor: undoTextColor)
|
||||
let attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: body, bold: bold, link: link, linkAttribute: { _ in return nil }), textAlignment: .natural)
|
||||
self.textNode.attributedText = attributedText
|
||||
if let customUndoText {
|
||||
|
||||
undoText = customUndoText
|
||||
displayUndo = true
|
||||
} else {
|
||||
displayUndo = false
|
||||
}
|
||||
self.originalRemainingSeconds = 4.5
|
||||
|
||||
if text.contains("](") {
|
||||
isUserInteractionEnabled = true
|
||||
}
|
||||
case let .succeed(text):
|
||||
self.avatarNode = nil
|
||||
self.iconNode = nil
|
||||
@ -211,7 +214,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
||||
|
||||
let body = MarkdownAttributeSet(font: Font.regular(14.0), textColor: .white)
|
||||
let bold = MarkdownAttributeSet(font: Font.semibold(14.0), textColor: .white)
|
||||
let link = MarkdownAttributeSet(font: Font.regular(14.0), textColor: undoTextColor)
|
||||
let link = MarkdownAttributeSet(font: Font.semibold(14.0), textColor: undoTextColor)
|
||||
let attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: body, bold: bold, link: link, linkAttribute: { contents in
|
||||
return ("URL", contents)
|
||||
}), textAlignment: .natural)
|
||||
@ -257,11 +260,16 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
||||
|
||||
let body = MarkdownAttributeSet(font: Font.regular(14.0), textColor: .white)
|
||||
let bold = MarkdownAttributeSet(font: Font.semibold(14.0), textColor: .white)
|
||||
let attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: body, bold: bold, link: body, linkAttribute: { _ in return nil }), textAlignment: .natural)
|
||||
let link = MarkdownAttributeSet(font: Font.semibold(14.0), textColor: undoTextColor)
|
||||
let attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: body, bold: bold, link: link, linkAttribute: { _ in return nil }), textAlignment: .natural)
|
||||
self.textNode.attributedText = attributedText
|
||||
self.textNode.maximumNumberOfLines = 2
|
||||
displayUndo = false
|
||||
self.originalRemainingSeconds = 3
|
||||
|
||||
if text.contains("](") {
|
||||
isUserInteractionEnabled = true
|
||||
}
|
||||
case let .banned(text):
|
||||
self.avatarNode = nil
|
||||
self.iconNode = nil
|
||||
@ -820,12 +828,17 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
||||
|
||||
let body = MarkdownAttributeSet(font: Font.regular(14.0), textColor: .white)
|
||||
let bold = MarkdownAttributeSet(font: Font.semibold(14.0), textColor: .white)
|
||||
let attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: body, bold: bold, link: body, linkAttribute: { _ in return nil }), textAlignment: .natural)
|
||||
let link = MarkdownAttributeSet(font: Font.semibold(14.0), textColor: undoTextColor)
|
||||
let attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: body, bold: bold, link: link, linkAttribute: { _ in return nil }), textAlignment: .natural)
|
||||
self.textNode.attributedText = attributedText
|
||||
self.textNode.maximumNumberOfLines = 2
|
||||
|
||||
displayUndo = false
|
||||
self.originalRemainingSeconds = 3
|
||||
|
||||
if text.contains("](") {
|
||||
isUserInteractionEnabled = true
|
||||
}
|
||||
case let .inviteRequestSent(title, text):
|
||||
self.avatarNode = nil
|
||||
self.iconNode = nil
|
||||
@ -897,7 +910,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
|
||||
|
||||
let body = MarkdownAttributeSet(font: Font.regular(14.0), textColor: .white)
|
||||
let bold = MarkdownAttributeSet(font: Font.semibold(14.0), textColor: .white)
|
||||
let link = MarkdownAttributeSet(font: Font.regular(14.0), textColor: undoTextColor)
|
||||
let link = MarkdownAttributeSet(font: Font.semibold(14.0), textColor: undoTextColor)
|
||||
let attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: body, bold: bold, link: link, linkAttribute: { contents in
|
||||
return ("URL", contents)
|
||||
}), textAlignment: .natural)
|
||||
|
Loading…
x
Reference in New Issue
Block a user