Stickers Import Improvements

This commit is contained in:
Ilya Laktyushin 2021-06-18 15:15:36 +03:00
parent 437cd445d1
commit 0c246e83b6
11 changed files with 4285 additions and 4238 deletions

View File

@ -6507,11 +6507,12 @@ Sorry for the inconvenience.";
"ImportStickerPack.RemoveFromImport" = "Remove From Import";
"ImportStickerPack.ChooseName" = "Choose Name";
"ImportStickerPack.ChooseNameDescription" = "Please choose a name for your set.";
"ImportStickerPack.NamePlaceholder" = "Name";
"ImportStickerPack.GeneratingLink" = "generating link...";
"ImportStickerPack.CheckingLink" = "checking availability...";
"ImportStickerPack.ChooseLink" = "Choose Link";
"ImportStickerPack.ChooseLinkDescription" = "You can use a-z, 0-9 and underscores.";
"ImportStickerPack.LinkTaken" = "Sorry, this link is alreay taken.";
"ImportStickerPack.LinkTaken" = "Sorry, this link is already taken.";
"ImportStickerPack.LinkAvailable" = "Link is available.";
"ImportStickerPack.ImportingStickers" = "Importing Stickers";
"ImportStickerPack.Of" = "%1$@ of %2$@ Imported";

View File

@ -30,6 +30,7 @@ swift_library(
"//submodules/UndoUI:UndoUI",
"//submodules/ContextUI:ContextUI",
"//submodules/RadialStatusNode:RadialStatusNode",
"//submodules/StickerPackPreviewUI:StickerPackPreviewUI",
],
visibility = [
"//visibility:public",

View File

@ -74,7 +74,8 @@ public final class ImportStickerPackController: ViewController, StandalonePresen
self.controllerNode.presentInGlobalOverlay = { [weak self] controller, arguments in
self?.presentInGlobalOverlay(controller, with: arguments)
}
self.controllerNode.navigationController = self.parentNavigationController
Queue.mainQueue().after(0.1) {
self.controllerNode.updateStickerPack(self.stickerPack)
}

View File

@ -15,6 +15,7 @@ import AccountContext
import ContextUI
import RadialStatusNode
import UndoUI
import StickerPackPreviewUI
private struct StickerPackPreviewGridEntry: Comparable, Identifiable {
let index: Int
@ -77,6 +78,8 @@ final class ImportStickerPackControllerNode: ViewControllerTracingNode, UIScroll
private var interaction: StickerPackPreviewInteraction!
weak var navigationController: NavigationController?
var present: ((ViewController, Any?) -> Void)?
var presentInGlobalOverlay: ((ViewController, Any?) -> Void)?
var dismiss: (() -> Void)?
@ -583,7 +586,7 @@ final class ImportStickerPackControllerNode: ViewControllerTracingNode, UIScroll
self.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .animated(duration: 0.2, curve: .easeInOut))
}
self.disposable.set((self.context.engine.stickers.createStickerSet(title: title, shortName: shortName, stickers: stickers, thumbnail: thumbnailSticker, isAnimated: stickerPack.isAnimated)
self.disposable.set((self.context.engine.stickers.createStickerSet(title: title, shortName: shortName, stickers: stickers, thumbnail: thumbnailSticker, isAnimated: stickerPack.isAnimated, software: stickerPack.software)
|> deliverOnMainQueue).start(next: { [weak self] status in
if let strongSelf = self {
if case let .complete(info, items) = status {
@ -620,12 +623,19 @@ final class ImportStickerPackControllerNode: ViewControllerTracingNode, UIScroll
strongSelf.cancelButtonNode.isUserInteractionEnabled = false
let navigationController = strongSelf.navigationController
let context = strongSelf.context
Queue.mainQueue().after(1.0) {
var firstItem: StickerPackItem?
if let firstStickerItem = firstStickerItem, let resource = firstStickerItem.resource as? TelegramMediaResource {
firstItem = StickerPackItem(index: ItemCollectionItemIndex(index: 0, id: 0), file: TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: stickerPack.isAnimated ? "application/x-tgsticker": "image/png", size: nil, attributes: [.FileName(fileName: stickerPack.isAnimated ? "sticker.tgs" : "sticker.png"), .ImageSize(size: firstStickerItem.dimensions)]), indexKeys: [])
}
strongSelf.presentInGlobalOverlay?(UndoOverlayController(presentationData: strongSelf.presentationData, content: .stickersModified(title: strongSelf.presentationData.strings.StickerPackActionInfo_AddedTitle, text: strongSelf.presentationData.strings.StickerPackActionInfo_AddedText(info.title).0, undo: false, info: info, topItem: firstItem ?? items.first, context: strongSelf.context), elevatedLayout: false, action: { _ in return true}), nil)
strongSelf.presentInGlobalOverlay?(UndoOverlayController(presentationData: strongSelf.presentationData, content: .stickersModified(title: strongSelf.presentationData.strings.StickerPackActionInfo_AddedTitle, text: strongSelf.presentationData.strings.StickerPackActionInfo_AddedText(info.title).0, undo: false, info: info, topItem: firstItem ?? items.first, context: strongSelf.context), elevatedLayout: false, action: { _ in
(navigationController?.viewControllers.last as? ViewController)?.present(StickerPackScreen(context: context, mode: .settings, mainStickerPack: .id(id: info.id.id, accessHash: info.accessHash), stickerPacks: [], parentNavigationController: navigationController, actionPerformed: { _, _, _ in
}), in: .window(.root))
return true
}), nil)
strongSelf.cancel?()
}
} else if case let .progress(progress, count, total) = status {
@ -645,7 +655,7 @@ final class ImportStickerPackControllerNode: ViewControllerTracingNode, UIScroll
@objc func installActionButtonPressed() {
var proceedImpl: ((String, String?) -> Void)?
let titleController = importStickerPackTitleController(context: self.context, title: self.presentationData.strings.ImportStickerPack_ChooseName, text: self.presentationData.strings.ImportStickerPack_ChooseNameDescription, placeholder: "", value: nil, maxLength: 128, apply: { [weak self] title in
let titleController = importStickerPackTitleController(context: self.context, title: self.presentationData.strings.ImportStickerPack_ChooseName, text: self.presentationData.strings.ImportStickerPack_ChooseNameDescription, placeholder: self.presentationData.strings.ImportStickerPack_NamePlaceholder, value: nil, maxLength: 128, apply: { [weak self] title in
if let strongSelf = self, let title = title {
strongSelf.shortNameSuggestionDisposable.set((strongSelf.context.engine.stickers.getStickerSetShortNameSuggestion(title: title)
|> deliverOnMainQueue).start(next: { suggestedShortName in

View File

@ -12,18 +12,28 @@ import UrlEscaping
import ActivityIndicator
private class TextField: UITextField, UIScrollViewDelegate {
let placeholderLabel: ImmediateTextNode
var placeholderString: NSAttributedString? {
didSet {
self.placeholderLabel.attributedText = self.placeholderString
self.setNeedsLayout()
}
}
fileprivate func updatePrefixWidth(_ prefixWidth: CGFloat) {
let previousPrefixWidth = self.prefixWidth
guard previousPrefixWidth != prefixWidth else {
return
}
self.prefixWidth = prefixWidth
let leftOffset = prefixWidth
if let scrollView = self.scrollView {
if scrollView.contentInset.left != leftOffset {
scrollView.contentInset = UIEdgeInsets(top: 0.0, left: leftOffset, bottom: 0.0, right: 0.0)
if scrollView.contentInset.left != prefixWidth {
scrollView.contentInset = UIEdgeInsets(top: 0.0, left: prefixWidth, bottom: 0.0, right: 0.0)
}
if leftOffset.isZero {
if prefixWidth.isZero {
scrollView.contentOffset = CGPoint()
} else if self.prefixWidth != previousPrefixWidth {
scrollView.contentOffset = CGPoint(x: -leftOffset, y: 0.0)
} else if prefixWidth != previousPrefixWidth {
scrollView.contentOffset = CGPoint(x: -prefixWidth, y: 0.0)
}
self.updatePrefixPosition(transition: .immediate)
}
@ -45,10 +55,17 @@ private class TextField: UITextField, UIScrollViewDelegate {
self.prefixLabel.displaysAsynchronously = false
self.prefixLabel.maximumNumberOfLines = 1
self.prefixLabel.truncationMode = .byTruncatingTail
self.placeholderLabel = ImmediateTextNode()
self.placeholderLabel.isUserInteractionEnabled = false
self.placeholderLabel.displaysAsynchronously = false
self.placeholderLabel.maximumNumberOfLines = 1
self.placeholderLabel.truncationMode = .byTruncatingTail
super.init(frame: CGRect())
self.addSubnode(self.prefixLabel)
self.addSubnode(self.placeholderLabel)
}
required init?(coder aDecoder: NSCoder) {
@ -88,6 +105,17 @@ private class TextField: UITextField, UIScrollViewDelegate {
}
}
func selectWhole() {
guard let scrollView = self.scrollView else {
return
}
// if scrollView.contentSize.width > scrollView.frame.width - scrollView.contentInset.left {
// scrollView.contentOffset = CGPoint(x: -scrollView.contentInset.left + scrollView.contentSize.width - (scrollView.frame.width - scrollView.contentInset.left), y: 0.0)
// self.updatePrefixPosition()
// }
self.selectAll(nil)
}
var fixAutoScroll: CGPoint?
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if let fixAutoScroll = self.fixAutoScroll {
@ -164,13 +192,10 @@ private class TextField: UITextField, UIScrollViewDelegate {
return
}
var placeholderOffset: CGFloat = 0.0
if #available(iOS 14.0, *) {
placeholderOffset = 1.0
} else {
}
let textRect = self.textRect(forBounds: bounds)
let labelSize = self.placeholderLabel.updateLayout(textRect.size)
self.placeholderLabel.frame = CGRect(origin: CGPoint(x: textRect.minX, y: floorToScreenPixels((bounds.height - labelSize.height) / 2.0)), size: labelSize)
let prefixSize = self.prefixLabel.updateLayout(CGSize(width: floor(bounds.size.width * 0.7), height: bounds.size.height))
let prefixBounds = bounds.insetBy(dx: 4.0, dy: 4.0)
@ -182,7 +207,6 @@ private class TextField: UITextField, UIScrollViewDelegate {
private final class ImportStickerPackTitleInputFieldNode: ASDisplayNode, UITextFieldDelegate {
private var theme: PresentationTheme
private let backgroundNode: ASImageNode
// private let textInputNode: EditableTextNode
private let textInputNode: TextField
private let clearButton: HighlightableButtonNode
@ -199,6 +223,7 @@ private final class ImportStickerPackTitleInputFieldNode: ASDisplayNode, UITextF
}
set {
self.textInputNode.attributedText = NSAttributedString(string: newValue, font: Font.regular(14.0), textColor: self.theme.actionSheet.inputTextColor)
self.textInputNode.placeholderLabel.isHidden = !newValue.isEmpty
if self.textInputNode.isFirstResponder {
self.clearButton.isHidden = newValue.isEmpty
} else {
@ -234,6 +259,7 @@ private final class ImportStickerPackTitleInputFieldNode: ASDisplayNode, UITextF
self.textInputNode.font = Font.regular(14.0)
self.textInputNode.typingAttributes = [NSAttributedString.Key.font: Font.regular(14.0), NSAttributedString.Key.foregroundColor: theme.actionSheet.inputTextColor]
self.textInputNode.clipsToBounds = true
self.textInputNode.placeholderString = NSAttributedString(string: placeholder, font: Font.regular(14.0), textColor: theme.actionSheet.secondaryTextColor)
// self.textInputNode.textContainerInset = UIEdgeInsets(top: self.inputInsets.top, left: 0.0, bottom: self.inputInsets.bottom, right: 0.0)
self.textInputNode.keyboardAppearance = theme.rootController.keyboardColor.keyboardAppearance
self.textInputNode.keyboardType = keyboardType
@ -265,7 +291,7 @@ private final class ImportStickerPackTitleInputFieldNode: ASDisplayNode, UITextF
}
func selectAll() {
self.textInputNode.selectAll(nil)
self.textInputNode.selectWhole()
}
func updateTheme(_ theme: PresentationTheme) {
@ -287,7 +313,7 @@ private final class ImportStickerPackTitleInputFieldNode: ASDisplayNode, UITextF
let backgroundFrame = CGRect(origin: CGPoint(x: backgroundInsets.left, y: backgroundInsets.top), size: CGSize(width: width - backgroundInsets.left - backgroundInsets.right, height: panelHeight - backgroundInsets.top - backgroundInsets.bottom))
transition.updateFrame(node: self.backgroundNode, frame: backgroundFrame)
transition.updateFrame(view: self.textInputNode, frame: CGRect(origin: CGPoint(x: backgroundFrame.minX + inputInsets.left, y: backgroundFrame.minY), size: CGSize(width: backgroundFrame.size.width - inputInsets.left - inputInsets.right - 20.0, height: backgroundFrame.size.height)))
transition.updateFrame(view: self.textInputNode, frame: CGRect(origin: CGPoint(x: backgroundFrame.minX + inputInsets.left, y: backgroundFrame.minY), size: CGSize(width: backgroundFrame.size.width - inputInsets.left - inputInsets.right - 22.0, height: backgroundFrame.size.height)))
if let image = self.clearButton.image(for: []) {
transition.updateFrame(node: self.clearButton, frame: CGRect(origin: CGPoint(x: backgroundFrame.maxX - 8.0 - image.size.width, y: backgroundFrame.minY + floor((backgroundFrame.size.height - image.size.height) / 2.0)), size: image.size))
@ -315,7 +341,8 @@ private final class ImportStickerPackTitleInputFieldNode: ASDisplayNode, UITextF
func textFieldDidUpdateText(_ text: String) {
self.updateTextNodeText(animated: true)
self.textChanged?(text)
self.clearButton.isHidden = (text).isEmpty
self.clearButton.isHidden = text.isEmpty
self.textInputNode.placeholderLabel.isHidden = !text.isEmpty
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {

View File

@ -6316,9 +6316,9 @@ public extension Api {
}
}
public struct stickers {
public static func createStickerSet(flags: Int32, userId: Api.InputUser, title: String, shortName: String, thumb: Api.InputDocument?, stickers: [Api.InputStickerSetItem]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.messages.StickerSet>) {
public static func createStickerSet(flags: Int32, userId: Api.InputUser, title: String, shortName: String, thumb: Api.InputDocument?, stickers: [Api.InputStickerSetItem], software: String?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.messages.StickerSet>) {
let buffer = Buffer()
buffer.appendInt32(-251435136)
buffer.appendInt32(-1876841625)
serializeInt32(flags, buffer: buffer, boxed: false)
userId.serialize(buffer, true)
serializeString(title, buffer: buffer, boxed: false)
@ -6329,7 +6329,8 @@ public extension Api {
for item in stickers {
item.serialize(buffer, true)
}
return (FunctionDescription(name: "stickers.createStickerSet", parameters: [("flags", flags), ("userId", userId), ("title", title), ("shortName", shortName), ("thumb", thumb), ("stickers", stickers)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.StickerSet? in
if Int(flags) & Int(1 << 3) != 0 {serializeString(software!, buffer: buffer, boxed: false)}
return (FunctionDescription(name: "stickers.createStickerSet", parameters: [("flags", flags), ("userId", userId), ("title", title), ("shortName", shortName), ("thumb", thumb), ("stickers", stickers), ("software", software)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.StickerSet? in
let reader = BufferReader(buffer)
var result: Api.messages.StickerSet?
if let signature = reader.readInt32() {

View File

@ -95,7 +95,7 @@ public enum CreateStickerSetStatus {
case complete(StickerPackCollectionInfo, [ItemCollectionItem])
}
func _internal_createStickerSet(account: Account, title: String, shortName: String, stickers: [ImportSticker], thumbnail: ImportSticker?, isAnimated: Bool) -> Signal<CreateStickerSetStatus, CreateStickerSetError> {
func _internal_createStickerSet(account: Account, title: String, shortName: String, stickers: [ImportSticker], thumbnail: ImportSticker?, isAnimated: Bool, software: String?) -> Signal<CreateStickerSetStatus, CreateStickerSetError> {
return account.postbox.loadedPeerWithId(account.peerId)
|> castError(CreateStickerSetError.self)
|> mapToSignal { peer -> Signal<CreateStickerSetStatus, CreateStickerSetError> in
@ -140,7 +140,10 @@ func _internal_createStickerSet(account: Account, title: String, shortName: Stri
flags |= (1 << 2)
thumbnailDocument = .inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: resource.fileReference ?? Data()))
}
return account.network.request(Api.functions.stickers.createStickerSet(flags: flags, userId: inputUser, title: title, shortName: shortName, thumb: thumbnailDocument, stickers: inputStickers))
if let software = software, !software.isEmpty {
flags |= (1 << 3)
}
return account.network.request(Api.functions.stickers.createStickerSet(flags: flags, userId: inputUser, title: title, shortName: shortName, thumb: thumbnailDocument, stickers: inputStickers, software: software))
|> mapError { error -> CreateStickerSetError in
return .generic
}

View File

@ -80,7 +80,9 @@ func _internal_removeStickerPacksInteractively(postbox: Postbox, ids: [ItemColle
let items = transaction.getItemCollectionItems(collectionId: id)
addSynchronizeInstalledStickerPacksOperation(transaction: transaction, namespace: namespace, content: content, noDelay: false)
transaction.removeItemCollection(collectionId: id)
for id in ids {
transaction.removeItemCollection(collectionId: id)
}
return index.flatMap { ($0, items) }
} else {
return nil

View File

@ -70,8 +70,8 @@ public extension TelegramEngine {
return _internal_stickerPacksAttachedToMedia(account: self.account, media: media)
}
public func createStickerSet(title: String, shortName: String, stickers: [ImportSticker], thumbnail: ImportSticker?, isAnimated: Bool) -> Signal<CreateStickerSetStatus, CreateStickerSetError> {
return _internal_createStickerSet(account: self.account, title: title, shortName: shortName, stickers: stickers, thumbnail: thumbnail, isAnimated: isAnimated)
public func createStickerSet(title: String, shortName: String, stickers: [ImportSticker], thumbnail: ImportSticker?, isAnimated: Bool, software: String?) -> Signal<CreateStickerSetStatus, CreateStickerSetError> {
return _internal_createStickerSet(account: self.account, title: title, shortName: shortName, stickers: stickers, thumbnail: thumbnail, isAnimated: isAnimated, software: software)
}
public func getStickerSetShortNameSuggestion(title: String) -> Signal<String?, NoError> {