Various improvements

This commit is contained in:
Ilya Laktyushin 2023-07-21 20:22:18 +02:00
parent 2aeeaaee44
commit 709acb3739
20 changed files with 188 additions and 65 deletions

View File

@ -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.";

View File

@ -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]

View File

@ -131,6 +131,10 @@ final class CameraDevice {
device.activeVideoMinFrameDuration = targetFPS.duration
device.activeVideoMaxFrameDuration = targetFPS.duration
}
if device.isLowLightBoostSupported {
device.automaticallyEnablesLowLightBoostWhenAvailable = true
}
}
}

View File

@ -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
}
}
}

View File

@ -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)
)
}

View File

@ -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)
}

View File

@ -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
}
}

View File

@ -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)
}

View File

@ -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 {

View File

@ -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()
}
}

View File

@ -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,

View File

@ -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()))

View File

@ -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)
}
}
}

View File

@ -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

View File

@ -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)
}
}

View File

@ -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 isnt 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 {

View File

@ -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)

View File

@ -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 {

View File

@ -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)

View File

@ -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)