Various UI fixes

This commit is contained in:
Ilya Laktyushin 2019-09-03 15:22:39 +03:00
parent e94d3b72d0
commit eecae32d48
15 changed files with 2259 additions and 2172 deletions

View file

@ -4679,7 +4679,8 @@ Any member of this group will be able to see messages in the channel.";
"EditTheme.Preview" = "CHAT PREVIEW";
"EditTheme.UploadNewTheme" = "Create from File...";
"EditTheme.UploadEditedTheme" = "Update from File...";
"EditTheme.ThemeTemplateAlert" = "New Theme Added\n\nPress and hold on your theme to edit it or get a sharing link. Users who install your theme will get automatic updates each time you change it.\n\nFor advanced editing purposes, you can find a file with your theme in Saved Messages.";
"EditTheme.ThemeTemplateAlertTitle" = "New Theme Added";
"EditTheme.ThemeTemplateAlertText" = "Press and hold on your theme to edit it or get a sharing link. Users who install your theme will get automatic updates each time you change it.\n\nFor advanced editing purposes, you can find a file with your theme in Saved Messages.";
"EditTheme.FileReadError" = "Invalid theme file";
"EditTheme.Create.TopInfo" = "The theme will be based on your currently selected colors and wallpaper.";
@ -4707,6 +4708,7 @@ Any member of this group will be able to see messages in the channel.";
"EditTheme.Edit.Preview.OutgoingText" = "Or upload a theme file";
"EditTheme.ErrorLinkTaken" = "Sorry, this link is already taken";
"EditTheme.ErrorInvalidCharacters" = "Sorry, this link is invalid.";
"Wallpaper.ErrorNotFound" = "Sorry, this chat background doesn't seem to exist.";
"Theme.ErrorNotFound" = "Sorry, this color theme doesn't seem to exist.";

View file

@ -25,6 +25,11 @@ public enum DeviceMetrics: CaseIterable, Equatable {
}
public init(screenSize: CGSize, statusBarHeight: CGFloat, onScreenNavigationHeight: CGFloat?) {
var screenSize = screenSize
if screenSize.width > screenSize.height {
screenSize = CGSize(width: screenSize.height, height: screenSize.width)
}
let additionalSize = CGSize(width: screenSize.width, height: screenSize.height + 20.0)
for device in DeviceMetrics.allCases {
if let _ = onScreenNavigationHeight, device.onScreenNavigationHeight(inLandscape: false) == nil {
@ -33,7 +38,7 @@ public enum DeviceMetrics: CaseIterable, Equatable {
let width = device.screenSize.width
let height = device.screenSize.height
if ((screenSize.width.isEqual(to: width) && screenSize.height.isEqual(to: height)) || screenSize.height.isEqual(to: width) && screenSize.width.isEqual(to: height)) || ((additionalSize.width.isEqual(to: width) && additionalSize.height.isEqual(to: height)) || additionalSize.height.isEqual(to: width) && additionalSize.width.isEqual(to: height)) {
if ((screenSize.width.isEqual(to: width) && screenSize.height.isEqual(to: height)) || (additionalSize.width.isEqual(to: width) && additionalSize.height.isEqual(to: height))) {
self = device
return
}

View file

@ -15,6 +15,22 @@ public class EditableTextNode: ASEditableTextNode {
self.textView.reloadInputViews()
}
}
public var isRTL: Bool {
if let text = self.textView.text, !text.isEmpty {
let tagger = NSLinguisticTagger(tagSchemes: [.language], options: 0)
tagger.string = text
let lang = tagger.tag(at: 0, scheme: .language, tokenRange: nil, sentenceRange: nil)
if let lang = lang?.rawValue, lang.contains("he") || lang.contains("ar") || lang.contains("fa") {
return true
} else {
return false
}
} else {
return false
}
}
}
public extension UITextView {

View file

@ -379,7 +379,7 @@ public func editThemeController(context: AccountContext, mode: EditThemeControll
let _ = enqueueMessages(account: context.account, peerId: context.account.peerId, messages: [message]).start()
if let navigateToChat = navigateToChat {
presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.EditTheme_ThemeTemplateAlert, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Settings_SavedMessages, action: {
presentControllerImpl?(textAlertController(context: context, title: presentationData.strings.EditTheme_ThemeTemplateAlertTitle, text: presentationData.strings.EditTheme_ThemeTemplateAlertText, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Settings_SavedMessages, action: {
completion()
navigateToChat(context.account.peerId)
}), TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {
@ -524,8 +524,18 @@ public func editThemeController(context: AccountContext, mode: EditThemeControll
return state
}
if case .slugOccupied = error {
presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.EditTheme_ErrorLinkTaken, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
var errorText: String?
switch error {
case .slugOccupied:
errorText = presentationData.strings.EditTheme_ErrorLinkTaken
case .slugInvalid:
errorText = presentationData.strings.EditTheme_ErrorInvalidCharacters
default:
break
}
if let errorText = errorText {
presentControllerImpl?(textAlertController(context: context, title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
}
})
}

View file

@ -98,7 +98,8 @@ private let colors: [UInt32: String] = [
0x80b3c4: "Glacier",
0xfebaad: "Melon",
0xc54b8c: "Mulberry",
0xa9c6c2: "Opal"
0xa9c6c2: "Opal",
0x54a5f8: "Blue"
]
private let adjectives = [
@ -148,7 +149,6 @@ private let adjectives = [
"Frosty",
"Frozen",
"Gentle",
"Golden",
"Heavenly",
"Hyper",
"Icy",
@ -202,7 +202,6 @@ private let adjectives = [
"Twinkling",
"Ultimate",
"Ultra",
"Uptown",
"Velvety",
"Vibrant",
"Vintage",

View file

@ -69,7 +69,6 @@ public final class ThemePreviewController: ViewController {
self.titleView.title = CounterContollerTitle(title: themeName, counter: " ")
self.navigationItem.titleView = titleView
self.statusBar.statusBarStyle = self.previewTheme.rootController.statusBarStyle.style
self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)

View file

@ -160,19 +160,12 @@ final class WallpaperColorPanelNode: ASDisplayNode, UITextFieldDelegate {
}
@objc internal func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if string.count > 1 {
if string.count <= 6 {
var updated = textField.text ?? ""
updated.replaceSubrange(updated.index(updated.startIndex, offsetBy: range.lowerBound) ..< updated.index(updated.startIndex, offsetBy: range.upperBound), with: string)
if updated.count <= 6 && updated.rangeOfCharacter(from: CharacterSet(charactersIn: "0123456789abcdefABCDEF").inverted) == nil {
textField.text = updated.uppercased()
}
}
return false
} else if string.count == 1 {
return (textField.text ?? "").count < 6 && string.rangeOfCharacter(from: CharacterSet(charactersIn: "0123456789abcdefABCDEF").inverted) == nil
var updated = textField.text ?? ""
updated.replaceSubrange(updated.index(updated.startIndex, offsetBy: range.lowerBound) ..< updated.index(updated.startIndex, offsetBy: range.upperBound), with: string)
if updated.count <= 6 && updated.rangeOfCharacter(from: CharacterSet(charactersIn: "0123456789abcdefABCDEF").inverted) == nil {
textField.text = updated.uppercased()
}
return true
return false
}
@objc func textFieldTextChanged(_ sender: UITextField) {

View file

@ -2,18 +2,36 @@ import Foundation
public enum ArabicNumeralStringType {
case western
case eastern
case arabic
case persian
}
public func normalizeArabicNumeralString(_ string: String, type: ArabicNumeralStringType) -> String {
var string = string
let numerals = ["٠": "0", "١": "1", "٢": "2", "٣": "3", "٤": "4", "٥": "5", "٦": "6", "٧": "7", "٨": "8", "٩": "9"]
for (easternNumeral, westernNumeral) in numerals {
let numerals = [
("0", "٠", "۰"),
("1", "١", "۱"),
("2", "٢", "۲"),
("3", "٣", "۳"),
("4", "٤", "۴"),
("5", "٥", "۵"),
("6", "٦", "۶"),
("7", "٧", "۷"),
("8", "٨", "۸"),
("9", "٩", "۹"),
]
for (western, arabic, persian) in numerals {
switch type {
case .western:
string = string.replacingOccurrences(of: easternNumeral, with: westernNumeral)
case .eastern:
string = string.replacingOccurrences(of: westernNumeral, with: easternNumeral)
string = string.replacingOccurrences(of: arabic, with: western)
string = string.replacingOccurrences(of: persian, with: western)
case .arabic:
string = string.replacingOccurrences(of: western, with: arabic)
string = string.replacingOccurrences(of: persian, with: arabic)
case .persian:
string = string.replacingOccurrences(of: western, with: persian)
string = string.replacingOccurrences(of: arabic, with: persian)
}
}

View file

@ -738,8 +738,14 @@ func chatAvailableMessageActionsImpl(postbox: Postbox, accountPeerId: PeerId, me
}
if id.namespace == Namespaces.Message.ScheduledCloud {
optionsMap[id]!.insert(.sendScheduledNow)
optionsMap[id]!.insert(.editScheduledTime)
optionsMap[id]!.insert(.deleteLocally)
if let peer = transaction.getPeer(id.peerId), let channel = peer as? TelegramChannel, !channel.hasPermission(.editAllMessages) {
} else {
optionsMap[id]!.insert(.editScheduledTime)
}
if let peer = transaction.getPeer(id.peerId), let channel = peer as? TelegramChannel, !channel.hasPermission(.deleteAllMessages) {
} else {
optionsMap[id]!.insert(.deleteLocally)
}
} else if id.peerId == accountPeerId {
if !(message.flags.isSending || message.flags.contains(.Failed)) {
optionsMap[id]!.insert(.forward)

View file

@ -402,8 +402,7 @@ final class ChatMessageAttachedContentNode: ASDisplayNode {
if let (media, flags) = mediaAndFlags {
if let file = media as? TelegramMediaFile {
if file.mimeType == "application/x-tgtheme-ios", let size = file.size, size < 16 * 1024 {
let automaticDownload = true
let (_, initialImageWidth, refineLayout) = contentImageLayout(context, presentationData.theme.theme, presentationData.strings, presentationData.dateTimeFormat, message, file, automaticDownload ? .full : .none, associatedData.automaticDownloadPeerType, .constrained(CGSize(width: constrainedSize.width - horizontalInsets.left - horizontalInsets.right, height: constrainedSize.height)), layoutConstants, contentMode)
let (_, initialImageWidth, refineLayout) = contentImageLayout(context, presentationData.theme.theme, presentationData.strings, presentationData.dateTimeFormat, message, file, .full, associatedData.automaticDownloadPeerType, .constrained(CGSize(width: constrainedSize.width - horizontalInsets.left - horizontalInsets.right, height: constrainedSize.height)), layoutConstants, contentMode)
initialWidth = initialImageWidth + horizontalInsets.left + horizontalInsets.right
refineContentImageLayout = refineLayout
} else if file.isInstantVideo {

View file

@ -384,9 +384,14 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode,
self.messageBackgroundNode.layer.animateBounds(from: fromFrame, to: self.messageBackgroundNode.bounds, duration: duration, timingFunction: kCAMediaTimingFunctionSpring)
self.messageBackgroundNode.layer.animatePosition(from: CGPoint(x: (initialWidth - self.messageClipNode.bounds.width) / 2.0, y: delta), to: CGPoint(), duration: duration, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
let textOffset = self.textInputNode.textView.contentSize.height - self.textInputNode.textView.contentOffset.y - self.textInputNode.textView.frame.height
self.fromMessageTextNode.layer.animatePosition(from: CGPoint(x: 0.0, y: delta * 2.0 + textOffset), to: CGPoint(), duration: duration, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
self.toMessageTextNode.layer.animatePosition(from: CGPoint(x: 0.0, y: delta * 2.0 + textOffset), to: CGPoint(), duration: duration, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
var textXOffset: CGFloat = 0.0
let textYOffset = self.textInputNode.textView.contentSize.height - self.textInputNode.textView.contentOffset.y - self.textInputNode.textView.frame.height
if self.textInputNode.textView.numberOfLines == 1 && self.textInputNode.isRTL {
textXOffset = initialWidth - self.messageClipNode.bounds.width
}
self.fromMessageTextNode.layer.animatePosition(from: CGPoint(x: textXOffset, y: delta * 2.0 + textYOffset), to: CGPoint(), duration: duration, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
self.toMessageTextNode.layer.animatePosition(from: CGPoint(x: textXOffset, y: delta * 2.0 + textYOffset), to: CGPoint(), duration: duration, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
let springDuration: Double = 0.42
let springDamping: CGFloat = 104.0
@ -480,9 +485,13 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode,
self.messageBackgroundNode.layer.animateBounds(from: self.messageBackgroundNode.bounds, to: toFrame, duration: duration, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
self.messageBackgroundNode.layer.animatePosition(from: CGPoint(), to: CGPoint(x: (initialWidth - self.messageClipNode.bounds.width) / 2.0, y: delta), duration: duration, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true)
let textOffset = self.textInputNode.textView.contentSize.height - self.textInputNode.textView.contentOffset.y - self.textInputNode.textView.frame.height
self.fromMessageTextNode.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: delta * 2.0 + textOffset), duration: duration, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true)
self.toMessageTextNode.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: delta * 2.0 + textOffset), duration: duration, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true)
var textXOffset: CGFloat = 0.0
let textYOffset = self.textInputNode.textView.contentSize.height - self.textInputNode.textView.contentOffset.y - self.textInputNode.textView.frame.height
if self.textInputNode.textView.numberOfLines == 1 && self.textInputNode.isRTL {
textXOffset = initialWidth - self.messageClipNode.bounds.width
}
self.fromMessageTextNode.layer.animatePosition(from: CGPoint(), to: CGPoint(x: textXOffset, y: delta * 2.0 + textYOffset), duration: duration, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true)
self.toMessageTextNode.layer.animatePosition(from: CGPoint(), to: CGPoint(x: textXOffset, y: delta * 2.0 + textYOffset), duration: duration, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true)
} else {
completedBubble = true
}
@ -560,9 +569,11 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode,
messageFrame.size.width = ceil(layout.size.width - messageFrame.origin.x - sendButtonFrame.width - layout.safeInsets.left - layout.safeInsets.right + 8.0)
}
var messageOriginDelta: CGFloat = 0.0
if self.textInputNode.textView.numberOfLines == 1 || self.textInputNode.textView.attributedText.string.isEmpty {
let textWidth = min(self.toMessageTextNode.textView.sizeThatFits(layout.size).width + 36.0, messageFrame.width)
messageFrame.origin.x += messageFrame.width - textWidth
messageOriginDelta = messageFrame.width - textWidth
messageFrame.origin.x += messageOriginDelta
messageFrame.size.width = textWidth
}
@ -586,6 +597,11 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode,
var textFrame = self.textFieldFrame
textFrame.origin = CGPoint(x: 13.0, y: 6.0 - UIScreenPixel)
textFrame.size.height = self.textInputNode.textView.contentSize.height
if self.textInputNode.isRTL {
textFrame.origin.x -= messageOriginDelta
}
self.fromMessageTextNode.frame = textFrame
self.toMessageTextNode.frame = textFrame

View file

@ -282,7 +282,32 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur
let controller = OverlayStatusController(theme: presentationData.theme, strings: presentationData.strings, type: .loading(cancelled: nil))
present(controller, nil)
let _ = (signal
var cancelImpl: (() -> Void)?
let progressSignal = Signal<Never, NoError> { subscriber in
let controller = OverlayStatusController(theme: presentationData.theme, strings: presentationData.strings, type: .loading(cancelled: {
cancelImpl?()
}))
present(controller, nil)
return ActionDisposable { [weak controller] in
Queue.mainQueue().async() {
controller?.dismiss()
}
}
}
|> runOn(Queue.mainQueue())
|> delay(0.35, queue: Queue.mainQueue())
let disposable = MetaDisposable()
let progressDisposable = progressSignal.start()
cancelImpl = {
disposable.set(nil)
}
disposable.set((signal
|> afterDisposed {
Queue.mainQueue().async {
progressDisposable.dispose()
}
}
|> deliverOnMainQueue).start(next: { [weak controller] dataAndTheme in
controller?.dismiss()
@ -300,7 +325,7 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur
}
present(textAlertController(context: context, title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
controller?.dismiss()
})
}))
dismissInput()
}
}

View file

@ -48,18 +48,15 @@ public final class TelegramRootController: NavigationController {
if presentationData.chatWallpaper != strongSelf.presentationData.chatWallpaper {
let navigationDetailsBackgroundMode: NavigationEmptyDetailsBackgoundMode?
switch presentationData.chatWallpaper {
case .color:
let image = generateTintedImage(image: UIImage(bundleImageName: "Chat List/EmptyMasterDetailIcon"), color: presentationData.theme.chatList.messageTextColor.withAlphaComponent(0.2))
navigationDetailsBackgroundMode = image != nil ? .image(image!) : nil
default:
let image = chatControllerBackgroundImage(theme: presentationData.theme, wallpaper: presentationData.chatWallpaper, mediaBox: strongSelf.context.account.postbox.mediaBox, knockoutMode: strongSelf.context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper)
navigationDetailsBackgroundMode = image != nil ? .wallpaper(image!) : nil
case .color:
let image = generateTintedImage(image: UIImage(bundleImageName: "Chat List/EmptyMasterDetailIcon"), color: presentationData.theme.chatList.messageTextColor.withAlphaComponent(0.2))
navigationDetailsBackgroundMode = image != nil ? .image(image!) : nil
default:
navigationDetailsBackgroundMode = chatControllerBackgroundImage(theme: presentationData.theme, wallpaper: presentationData.chatWallpaper, mediaBox: strongSelf.context.sharedContext.accountManager.mediaBox, knockoutMode: strongSelf.context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper).flatMap(NavigationEmptyDetailsBackgoundMode.wallpaper)
}
strongSelf.updateBackgroundDetailsMode(navigationDetailsBackgroundMode, transition: .immediate)
}
let previousTheme = strongSelf.presentationData.theme
strongSelf.presentationData = presentationData
if previousTheme !== presentationData.theme {