mirror of
https://github.com/TelegramMessenger/Telegram-iOS.git
synced 2026-07-06 03:33:41 +02:00
Cloud themes support
This commit is contained in:
parent
5d86aaa057
commit
ddf709ab5b
79 changed files with 1475 additions and 749 deletions
|
|
@ -166,6 +166,7 @@ public enum ResolvedUrl {
|
|||
case cancelAccountReset(phone: String, hash: String)
|
||||
case share(url: String?, text: String?, to: String?)
|
||||
case wallpaper(WallpaperUrlParameter)
|
||||
case theme(String)
|
||||
}
|
||||
|
||||
public enum NavigateToChatKeepStack {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import AccountContext
|
|||
public func textAlertController(context: AccountContext, title: String?, text: String, actions: [TextAlertAction], actionLayout: TextAlertContentActionLayout = .horizontal) -> AlertController {
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
let controller = standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: title, text: text, actions: actions)
|
||||
let controller = standardTextAlertController(theme: AlertControllerTheme(presentationTheme: presentationData.theme), title: title, text: text, actions: actions, actionLayout: actionLayout)
|
||||
let presentationDataDisposable = context.sharedContext.presentationData.start(next: { [weak controller] presentationData in
|
||||
controller?.theme = AlertControllerTheme(presentationTheme: presentationData.theme)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -366,44 +366,7 @@ open class NavigationController: UINavigationController, ContainableController,
|
|||
}
|
||||
}
|
||||
|
||||
if let _ = self.controllerView.emptyDetailView {
|
||||
// transition.updateFrame(view: navigationBackgroundView, frame: navigationBackgroundFrame)
|
||||
// transition.updateFrame(view: navigationSeparatorView, frame: CGRect(origin: CGPoint(x: navigationBackgroundFrame.minX, y: navigationBackgroundFrame.maxY), size: CGSize(width: navigationBackgroundFrame.width, height: UIScreenPixel)))
|
||||
// if let image = emptyDetailView.image {
|
||||
// transition.updateFrame(view: emptyDetailView, frame: CGRect(origin: CGPoint(x: masterData.0.maxX + floor((lastControllerFrameAndLayout.0.size.width - image.size.width) / 2.0), y: floor((lastControllerFrameAndLayout.0.size.height - image.size.height) / 2.0)), size: image.size))
|
||||
// }
|
||||
} else {
|
||||
// let navigationBackgroundView = UIView()
|
||||
// navigationBackgroundView.backgroundColor = self.theme.navigationBar.c
|
||||
// let navigationSeparatorView = UIView()
|
||||
// navigationSeparatorView.backgroundColor = self.theme.navigationBar.separatorColor
|
||||
// let emptyDetailView = UIImageView()
|
||||
// emptyDetailView.image = self.theme.emptyDetailIcon
|
||||
// emptyDetailView.alpha = 0.0
|
||||
//
|
||||
// self.controllerView.navigationBackgroundView = navigationBackgroundView
|
||||
// self.controllerView.navigationSeparatorView = navigationSeparatorView
|
||||
// self.controllerView.emptyDetailView = emptyDetailView
|
||||
//
|
||||
// self.controllerView.insertSubview(navigationBackgroundView, at: 0)
|
||||
// self.controllerView.insertSubview(navigationSeparatorView, at: 1)
|
||||
// self.controllerView.insertSubview(emptyDetailView, at: 0)
|
||||
|
||||
// navigationBackgroundView.frame = navigationBackgroundFrame
|
||||
// navigationSeparatorView.frame = CGRect(origin: CGPoint(x: navigationBackgroundFrame.minX, y: navigationBackgroundFrame.maxY), size: CGSize(width: navigationBackgroundFrame.width, height: UIScreenPixel))
|
||||
//
|
||||
// transition.animatePositionAdditive(layer: navigationBackgroundView.layer, offset: CGPoint(x: navigationBackgroundFrame.width, y: 0.0))
|
||||
// transition.animatePositionAdditive(layer: navigationSeparatorView.layer, offset: CGPoint(x: navigationBackgroundFrame.width, y: 0.0))
|
||||
|
||||
// if let image = emptyDetailView.image {
|
||||
// emptyDetailView.frame = CGRect(origin: CGPoint(x: masterData.0.maxX + floor((lastControllerFrameAndLayout.0.size.width - image.size.width) / 2.0), y: floor((lastControllerFrameAndLayout.0.size.height - image.size.height) / 2.0)), size: image.size)
|
||||
// }
|
||||
//
|
||||
// transition.updateAlpha(layer: emptyDetailView.layer, alpha: 1.0)
|
||||
}
|
||||
|
||||
if let blackout = self.masterDetailsBlackout {
|
||||
|
||||
let blackoutFrame: CGRect
|
||||
switch blackout {
|
||||
case .details:
|
||||
|
|
@ -493,15 +456,15 @@ open class NavigationController: UINavigationController, ContainableController,
|
|||
} else if case .masterDetail = layoutConfiguration, i == 1 {
|
||||
controller.navigationBar?.previousItem = .close
|
||||
} else {
|
||||
controller.navigationBar?.previousItem = .item(viewControllers[i - 1].navigationItem)
|
||||
controller.navigationBar?.previousItem = .item(self.viewControllers[i - 1].navigationItem)
|
||||
}
|
||||
if i < self._viewControllers.count - 1 {
|
||||
controller.updateNavigationCustomData((viewControllers[i + 1] as? ViewController)?.customData, progress: 1.0, transition: transition)
|
||||
controller.updateNavigationCustomData((self.viewControllers[i + 1] as? ViewController)?.customData, progress: 1.0, transition: transition)
|
||||
} else {
|
||||
controller.updateNavigationCustomData(nil, progress: 1.0, transition: transition)
|
||||
}
|
||||
}
|
||||
viewControllers[i].navigation_setNavigationController(self)
|
||||
self.viewControllers[i].navigation_setNavigationController(self)
|
||||
|
||||
if i == 0, let (_, layout) = firstControllerFrameAndLayout {
|
||||
controllersAndFrames.append((true, self._viewControllers[i], layout))
|
||||
|
|
@ -526,7 +489,15 @@ open class NavigationController: UINavigationController, ContainableController,
|
|||
frame = lastControllerFrameAndLayout.0
|
||||
}
|
||||
let isAppearing = record.controller.view.superview == nil
|
||||
(record.controller as? ViewController)?.containerLayoutUpdated(layout, transition: isAppearing ? .immediate : transition)
|
||||
if let controller = record.controller as? ViewController {
|
||||
let updatedLayout: ContainerViewLayout
|
||||
if previousControllers.count == self.viewControllers.count + 1, previousControllers[previousControllers.count - 2].controller === controller {
|
||||
updatedLayout = layout.withUpdatedInputHeight(controller.hasActiveInput ? layout.inputHeight : nil)
|
||||
} else {
|
||||
updatedLayout = layout
|
||||
}
|
||||
controller.containerLayoutUpdated(updatedLayout, transition: isAppearing ? .immediate : transition)
|
||||
}
|
||||
if isAppearing {
|
||||
if isMaster {
|
||||
appearingMasterController = record
|
||||
|
|
|
|||
|
|
@ -83,9 +83,13 @@ public extension UIColor {
|
|||
var red: CGFloat = 0.0
|
||||
var green: CGFloat = 0.0
|
||||
var blue: CGFloat = 0.0
|
||||
self.getRed(&red, green: &green, blue: &blue, alpha: nil)
|
||||
|
||||
return (UInt32(red * 255.0) << 16) | (UInt32(green * 255.0) << 8) | (UInt32(blue * 255.0))
|
||||
if self.getRed(&red, green: &green, blue: &blue, alpha: nil) {
|
||||
return (UInt32(max(0.0, red) * 255.0) << 16) | (UInt32(max(0.0, green) * 255.0) << 8) | (UInt32(max(0.0, blue) * 255.0))
|
||||
} else if self.getWhite(&red, alpha: nil) {
|
||||
return (UInt32(max(0.0, red) * 255.0) << 16) | (UInt32(max(0.0, red) * 255.0) << 8) | (UInt32(max(0.0, red) * 255.0))
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
var argb: UInt32 {
|
||||
|
|
@ -93,9 +97,13 @@ public extension UIColor {
|
|||
var green: CGFloat = 0.0
|
||||
var blue: CGFloat = 0.0
|
||||
var alpha: CGFloat = 0.0
|
||||
self.getRed(&red, green: &green, blue: &blue, alpha: &alpha)
|
||||
|
||||
return (UInt32(alpha * 255.0) << 24) | (UInt32(red * 255.0) << 16) | (UInt32(green * 255.0) << 8) | (UInt32(blue * 255.0))
|
||||
if self.getRed(&red, green: &green, blue: &blue, alpha: &alpha) {
|
||||
return (UInt32(alpha * 255.0) << 24) | (UInt32(max(0.0, red) * 255.0) << 16) | (UInt32(max(0.0, green) * 255.0) << 8) | (UInt32(max(0.0, blue) * 255.0))
|
||||
} else if self.getWhite(&red, alpha: &alpha) {
|
||||
return (UInt32(max(0.0, alpha) * 255.0) << 24) | (UInt32(max(0.0, red) * 255.0) << 16) | (UInt32(max(0.0, red) * 255.0) << 8) | (UInt32(max(0.0, red) * 255.0))
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
var hsv: (CGFloat, CGFloat, CGFloat) {
|
||||
|
|
|
|||
|
|
@ -927,7 +927,7 @@ public class ItemListAvatarAndNameInfoItemNode: ListViewItemNode, ItemListItemNo
|
|||
if strongSelf.arrowNode.supernode == nil {
|
||||
strongSelf.addSubnode(strongSelf.arrowNode)
|
||||
}
|
||||
strongSelf.arrowNode.frame = CGRect(origin: CGPoint(x: params.width - params.rightInset - 15.0 - arrowImage.size.width, y: floor((layout.contentSize.height - arrowImage.size.height) / 2.0)), size: arrowImage.size)
|
||||
strongSelf.arrowNode.frame = CGRect(origin: CGPoint(x: params.width - params.rightInset - 7.0 - arrowImage.size.width, y: floor((layout.contentSize.height - arrowImage.size.height) / 2.0)), size: arrowImage.size)
|
||||
} else if strongSelf.arrowNode.supernode != nil {
|
||||
strongSelf.arrowNode.removeFromSupernode()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -196,7 +196,7 @@ class ItemListPeerActionItemNode: ListViewItemNode {
|
|||
|
||||
strongSelf.iconNode.image = item.icon
|
||||
if let image = item.icon {
|
||||
transition.updateFrame(node: strongSelf.iconNode, frame: CGRect(origin: CGPoint(x: params.leftInset + editingOffset + floor((leftInset - params.leftInset - image.size.width) / 2.0), y: floor((contentSize.height - image.size.height) / 2.0)), size: image.size))
|
||||
transition.updateFrame(node: strongSelf.iconNode, frame: CGRect(origin: CGPoint(x: params.leftInset + editingOffset + floor((leftInset - params.leftInset - image.size.width) / 2.0) + 3.0, y: floor((contentSize.height - image.size.height) / 2.0)), size: image.size))
|
||||
}
|
||||
|
||||
if strongSelf.backgroundNode.supernode == nil {
|
||||
|
|
|
|||
|
|
@ -859,7 +859,7 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
|
|||
|
||||
if let labelArrowNode = self.labelArrowNode {
|
||||
if let image = labelArrowNode.image {
|
||||
let labelArrowNodeFrame = CGRect(origin: CGPoint(x: revealOffset + params.width - rightLabelInset - image.size.width, y: labelArrowNode.frame.minY), size: image.size)
|
||||
let labelArrowNodeFrame = CGRect(origin: CGPoint(x: revealOffset + params.width - rightLabelInset - image.size.width + 8.0, y: labelArrowNode.frame.minY), size: image.size)
|
||||
transition.updateFrame(node: labelArrowNode, frame: labelArrowNodeFrame)
|
||||
rightLabelInset += 19.0
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ public enum ItemListNavigationButtonStyle {
|
|||
public enum ItemListNavigationButtonContentIcon {
|
||||
case search
|
||||
case add
|
||||
case action
|
||||
}
|
||||
|
||||
public enum ItemListNavigationButtonContent: Equatable {
|
||||
|
|
@ -309,6 +310,8 @@ open class ItemListController<Entry: ItemListNodeEntry>: ViewController, KeyShor
|
|||
image = PresentationResourcesRootController.navigationCompactSearchIcon(controllerState.theme)
|
||||
case .add:
|
||||
image = PresentationResourcesRootController.navigationAddIcon(controllerState.theme)
|
||||
case .action:
|
||||
image = PresentationResourcesRootController.navigationShareIcon(controllerState.theme)
|
||||
}
|
||||
item = UIBarButtonItem(image: image, style: leftNavigationButton.style.barButtonItemStyle, target: strongSelf, action: #selector(strongSelf.leftNavigationButtonPressed))
|
||||
}
|
||||
|
|
@ -364,6 +367,8 @@ open class ItemListController<Entry: ItemListNodeEntry>: ViewController, KeyShor
|
|||
image = PresentationResourcesRootController.navigationCompactSearchIcon(controllerState.theme)
|
||||
case .add:
|
||||
image = PresentationResourcesRootController.navigationAddIcon(controllerState.theme)
|
||||
case .action:
|
||||
image = PresentationResourcesRootController.navigationShareIcon(controllerState.theme)
|
||||
}
|
||||
item = UIBarButtonItem(image: image, style: style.barButtonItemStyle, target: strongSelf, action: action)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -431,7 +431,7 @@ public class ItemListDisclosureItemNode: ListViewItemNode, ItemListItemNode {
|
|||
}
|
||||
|
||||
if let arrowImage = strongSelf.arrowNode.image {
|
||||
strongSelf.arrowNode.frame = CGRect(origin: CGPoint(x: params.width - params.rightInset - 15.0 - arrowImage.size.width, y: floorToScreenPixels((height - arrowImage.size.height) / 2.0)), size: arrowImage.size)
|
||||
strongSelf.arrowNode.frame = CGRect(origin: CGPoint(x: params.width - params.rightInset - 7.0 - arrowImage.size.width, y: floorToScreenPixels((height - arrowImage.size.height) / 2.0)), size: arrowImage.size)
|
||||
}
|
||||
|
||||
switch item.disclosureStyle {
|
||||
|
|
|
|||
|
|
@ -32,12 +32,13 @@
|
|||
@property (nonatomic) bool disableStickers;
|
||||
@property (nonatomic) bool hasSilentPosting;
|
||||
@property (nonatomic) bool hasSchedule;
|
||||
@property (nonatomic, copy) void (^presentScheduleController)(void (^)(int32_t));
|
||||
|
||||
@property (nonatomic, strong) NSArray *underlyingViews;
|
||||
@property (nonatomic, assign) bool openEditor;
|
||||
|
||||
@property (nonatomic, copy) void (^cameraPressed)(TGAttachmentCameraView *cameraView);
|
||||
@property (nonatomic, copy) void (^sendPressed)(TGMediaAsset *currentItem, bool asFiles, TGMediaPickerGalleryCompletionMode mode);
|
||||
@property (nonatomic, copy) void (^sendPressed)(TGMediaAsset *currentItem, bool asFiles, bool silentPosting, int32_t scheduleTime);
|
||||
@property (nonatomic, copy) void (^avatarCompletionBlock)(UIImage *image);
|
||||
|
||||
@property (nonatomic, copy) void (^editorOpened)(void);
|
||||
|
|
|
|||
|
|
@ -264,7 +264,7 @@ const NSUInteger TGAttachmentDisplayedAssetLimit = 500;
|
|||
{
|
||||
if (strongSelf->_selectionContext.allowGrouping)
|
||||
[[NSUserDefaults standardUserDefaults] setObject:@(!strongSelf->_selectionContext.grouping) forKey:@"TG_mediaGroupingDisabled_v0"];
|
||||
strongSelf.sendPressed(nil, false, false);
|
||||
strongSelf.sendPressed(nil, false, false, 0);
|
||||
}
|
||||
}];
|
||||
[_sendMediaItemView setHidden:true animated:false];
|
||||
|
|
@ -276,7 +276,7 @@ const NSUInteger TGAttachmentDisplayedAssetLimit = 500;
|
|||
{
|
||||
__strong TGAttachmentCarouselItemView *strongSelf = weakSelf;
|
||||
if (strongSelf != nil && strongSelf.sendPressed != nil)
|
||||
strongSelf.sendPressed(nil, true, false);
|
||||
strongSelf.sendPressed(nil, true, false, 0);
|
||||
}];
|
||||
_sendFileItemView.requiresDivider = false;
|
||||
[_sendFileItemView setHidden:true animated:false];
|
||||
|
|
@ -774,14 +774,14 @@ const NSUInteger TGAttachmentDisplayedAssetLimit = 500;
|
|||
strongSelf->_galleryMixin = nil;
|
||||
};
|
||||
|
||||
mixin.completeWithItem = ^(TGMediaPickerGalleryItem *item, TGMediaPickerGalleryCompletionMode mode)
|
||||
mixin.completeWithItem = ^(TGMediaPickerGalleryItem *item, bool silentPosting, int32_t scheduleTime)
|
||||
{
|
||||
__strong TGAttachmentCarouselItemView *strongSelf = weakSelf;
|
||||
if (strongSelf != nil && strongSelf.sendPressed != nil)
|
||||
{
|
||||
if (strongSelf->_selectionContext.allowGrouping)
|
||||
[[NSUserDefaults standardUserDefaults] setObject:@(!strongSelf->_selectionContext.grouping) forKey:@"TG_mediaGroupingDisabled_v0"];
|
||||
strongSelf.sendPressed(item.asset, strongSelf.asFile, mode);
|
||||
strongSelf.sendPressed(item.asset, strongSelf.asFile, silentPosting, scheduleTime);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -802,7 +802,7 @@ const NSUInteger TGAttachmentDisplayedAssetLimit = 500;
|
|||
thumbnailImage = cell.imageView.image;
|
||||
|
||||
TGMediaPickerModernGalleryMixin *mixin = [[TGMediaPickerModernGalleryMixin alloc] initWithContext:_context item:asset fetchResult:_fetchResult parentController:self.parentController thumbnailImage:thumbnailImage selectionContext:_selectionContext editingContext:_editingContext suggestionContext:self.suggestionContext hasCaptions:(_allowCaptions && !_forProfilePhoto) allowCaptionEntities:self.allowCaptionEntities hasTimer:self.hasTimer onlyCrop:self.onlyCrop inhibitDocumentCaptions:_inhibitDocumentCaptions inhibitMute:self.inhibitMute asFile:self.asFile itemsLimit:TGAttachmentDisplayedAssetLimit recipientName:self.recipientName hasSilentPosting:self.hasSilentPosting hasSchedule:self.hasSchedule];
|
||||
|
||||
mixin.presentScheduleController = self.presentScheduleController;
|
||||
__weak TGAttachmentCarouselItemView *weakSelf = self;
|
||||
mixin.thumbnailSignalForItem = ^SSignal *(id item)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ typedef enum
|
|||
@property (nonatomic, assign) bool inhibitMute;
|
||||
@property (nonatomic, assign) bool hasSilentPosting;
|
||||
@property (nonatomic, assign) bool hasSchedule;
|
||||
@property (nonatomic, copy) void (^presentScheduleController)(void (^)(int32_t));
|
||||
|
||||
@property (nonatomic, assign) bool liveVideoUploadEnabled;
|
||||
@property (nonatomic, assign) bool shouldShowFileTipIfNeeded;
|
||||
|
|
@ -67,7 +68,7 @@ typedef enum
|
|||
|
||||
@property (nonatomic, copy) NSDictionary *(^descriptionGenerator)(id, NSString *, NSArray *, NSString *);
|
||||
@property (nonatomic, copy) void (^avatarCompletionBlock)(UIImage *image);
|
||||
@property (nonatomic, copy) void (^completionBlock)(NSArray *signals, TGMediaPickerGalleryCompletionMode mode);
|
||||
@property (nonatomic, copy) void (^completionBlock)(NSArray *signals, bool silentPosting, int32_t scheduleTime);
|
||||
@property (nonatomic, copy) void (^singleCompletionBlock)(id<TGMediaEditableItem> item, TGMediaEditingContext *editingContext);
|
||||
@property (nonatomic, copy) void (^dismissalBlock)(void);
|
||||
@property (nonatomic, copy) void (^selectionBlock)(TGMediaAsset *asset, UIImage *);
|
||||
|
|
@ -85,7 +86,7 @@ typedef enum
|
|||
- (NSArray *)resultSignalsWithCurrentItem:(TGMediaAsset *)currentItem descriptionGenerator:(id (^)(id, NSString *, NSArray *, NSString *))descriptionGenerator;
|
||||
|
||||
- (void)completeWithAvatarImage:(UIImage *)image;
|
||||
- (void)completeWithCurrentItem:(TGMediaAsset *)currentItem mode:(TGMediaPickerGalleryCompletionMode)mode;
|
||||
- (void)completeWithCurrentItem:(TGMediaAsset *)currentItem silentPosting:(bool)silentPosting scheduleTime:(int32_t)scheduleTime;
|
||||
|
||||
- (void)dismiss;
|
||||
|
||||
|
|
|
|||
|
|
@ -122,6 +122,7 @@
|
|||
pickerController.onlyCrop = strongController.onlyCrop;
|
||||
pickerController.hasSilentPosting = strongController.hasSilentPosting;
|
||||
pickerController.hasSchedule = strongController.hasSchedule;
|
||||
pickerController.presentScheduleController = strongController.presentScheduleController;
|
||||
[strongController pushViewController:pickerController animated:true];
|
||||
};
|
||||
[groupsController loadViewIfNeeded];
|
||||
|
|
@ -213,6 +214,11 @@
|
|||
self.pickerController.hasSchedule = hasSchedule;
|
||||
}
|
||||
|
||||
- (void)setPresentScheduleController:(void (^)(void (^)(int32_t)))presentScheduleController {
|
||||
_presentScheduleController = [presentScheduleController copy];
|
||||
self.pickerController.presentScheduleController = presentScheduleController;
|
||||
}
|
||||
|
||||
- (void)setOnlyCrop:(bool)onlyCrop
|
||||
{
|
||||
_onlyCrop = onlyCrop;
|
||||
|
|
@ -457,7 +463,7 @@
|
|||
{
|
||||
__strong TGMediaAssetsController *strongSelf = weakSelf;
|
||||
if (strongSelf != nil)
|
||||
[strongSelf completeWithCurrentItem:nil mode:TGMediaPickerGalleryCompletionModeGeneric];
|
||||
[strongSelf completeWithCurrentItem:nil silentPosting:false scheduleTime:0];
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -538,12 +544,12 @@
|
|||
self.avatarCompletionBlock(image);
|
||||
}
|
||||
|
||||
- (void)completeWithCurrentItem:(TGMediaAsset *)currentItem mode:(TGMediaPickerGalleryCompletionMode)mode
|
||||
- (void)completeWithCurrentItem:(TGMediaAsset *)currentItem silentPosting:(bool)silentPosting scheduleTime:(int32_t)scheduleTime
|
||||
{
|
||||
if (self.completionBlock != nil)
|
||||
{
|
||||
NSArray *signals = [self resultSignalsWithCurrentItem:currentItem descriptionGenerator:self.descriptionGenerator];
|
||||
self.completionBlock(signals, mode);
|
||||
self.completionBlock(signals, silentPosting, scheduleTime);
|
||||
}
|
||||
else if (self.singleCompletionBlock != nil)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -311,13 +311,13 @@
|
|||
strongSelf->_galleryMixin = nil;
|
||||
};
|
||||
|
||||
mixin.completeWithItem = ^(TGMediaPickerGalleryItem *item, TGMediaPickerGalleryCompletionMode mode)
|
||||
mixin.completeWithItem = ^(TGMediaPickerGalleryItem *item, bool silentPosting, int32_t scheduleTime)
|
||||
{
|
||||
__strong TGMediaAssetsPickerController *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
return;
|
||||
|
||||
[(TGMediaAssetsController *)strongSelf.navigationController completeWithCurrentItem:item.asset mode:mode];
|
||||
[(TGMediaAssetsController *)strongSelf.navigationController completeWithCurrentItem:item.asset silentPosting:silentPosting scheduleTime:scheduleTime];
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -341,7 +341,7 @@
|
|||
bool asFile = (_intent == TGMediaAssetsControllerSendFileIntent);
|
||||
|
||||
TGMediaPickerModernGalleryMixin *mixin = [self _galleryMixinForContext:_context item:asset thumbnailImage:thumbnailImage selectionContext:self.selectionContext editingContext:self.editingContext suggestionContext:self.suggestionContext hasCaptions:self.captionsEnabled allowCaptionEntities:self.allowCaptionEntities inhibitDocumentCaptions:self.inhibitDocumentCaptions asFile:asFile];
|
||||
|
||||
mixin.presentScheduleController = self.presentScheduleController;
|
||||
__weak TGMediaAssetsPickerController *weakSelf = self;
|
||||
mixin.thumbnailSignalForItem = ^SSignal *(id item)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -4,12 +4,6 @@
|
|||
@class TGMediaAsset;
|
||||
@class TGMediaSelectionContext;
|
||||
|
||||
typedef NS_ENUM(NSUInteger, TGMediaPickerGalleryCompletionMode) {
|
||||
TGMediaPickerGalleryCompletionModeGeneric,
|
||||
TGMediaPickerGalleryCompletionModeSilent,
|
||||
TGMediaPickerGalleryCompletionModeSchedule
|
||||
};
|
||||
|
||||
@interface TGMediaAssetsPreheatMixin : NSObject
|
||||
|
||||
@property (nonatomic, copy) NSInteger (^assetCount)(void);
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@
|
|||
@property (nonatomic, strong) NSString *recipientName;
|
||||
@property (nonatomic, assign) bool hasSilentPosting;
|
||||
@property (nonatomic, assign) bool hasSchedule;
|
||||
@property (nonatomic, copy) void (^presentScheduleController)(void (^)(int32_t));
|
||||
|
||||
@property (nonatomic, strong) TGMediaAssetsPallete *pallete;
|
||||
|
||||
|
|
|
|||
|
|
@ -22,11 +22,13 @@
|
|||
@property (nonatomic, copy) void (^didTransitionOut)();
|
||||
@property (nonatomic, copy) UIView *(^referenceViewForItem)(TGMediaPickerGalleryItem *);
|
||||
|
||||
@property (nonatomic, copy) void (^completeWithItem)(TGMediaPickerGalleryItem *item, TGMediaPickerGalleryCompletionMode mode);
|
||||
@property (nonatomic, copy) void (^completeWithItem)(TGMediaPickerGalleryItem *item, bool silentPosting, int32_t scheduleTime);
|
||||
|
||||
@property (nonatomic, copy) void (^editorOpened)(void);
|
||||
@property (nonatomic, copy) void (^editorClosed)(void);
|
||||
|
||||
@property (nonatomic, copy) void (^presentScheduleController)(void (^)(int32_t));
|
||||
|
||||
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context item:(id)item fetchResult:(TGMediaAssetFetchResult *)fetchResult parentController:(TGViewController *)parentController thumbnailImage:(UIImage *)thumbnailImage selectionContext:(TGMediaSelectionContext *)selectionContext editingContext:(TGMediaEditingContext *)editingContext suggestionContext:(TGSuggestionContext *)suggestionContext hasCaptions:(bool)hasCaptions allowCaptionEntities:(bool)allowCaptionEntities hasTimer:(bool)hasTimer onlyCrop:(bool)onlyCrop inhibitDocumentCaptions:(bool)inhibitDocumentCaptions inhibitMute:(bool)inhibitMute asFile:(bool)asFile itemsLimit:(NSUInteger)itemsLimit recipientName:(NSString *)recipientName hasSilentPosting:(bool)hasSilentPosting hasSchedule:(bool)hasSchedule;
|
||||
|
||||
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context item:(id)item momentList:(TGMediaAssetMomentList *)momentList parentController:(TGViewController *)parentController thumbnailImage:(UIImage *)thumbnailImage selectionContext:(TGMediaSelectionContext *)selectionContext editingContext:(TGMediaEditingContext *)editingContext suggestionContext:(TGSuggestionContext *)suggestionContext hasCaptions:(bool)hasCaptions allowCaptionEntities:(bool)allowCaptionEntities hasTimer:(bool)hasTimer onlyCrop:(bool)onlyCrop inhibitDocumentCaptions:(bool)inhibitDocumentCaptions inhibitMute:(bool)inhibitMute asFile:(bool)asFile itemsLimit:(NSUInteger)itemsLimit hasSilentPosting:(bool)hasSilentPosting hasSchedule:(bool)hasSchedule;
|
||||
|
|
|
|||
|
|
@ -136,7 +136,7 @@
|
|||
strongSelf->_galleryModel.dismiss(true, false);
|
||||
|
||||
if (strongSelf.completeWithItem != nil)
|
||||
strongSelf.completeWithItem(item, false);
|
||||
strongSelf.completeWithItem(item, false, 0);
|
||||
};
|
||||
|
||||
model.interfaceView.doneLongPressed = ^(TGMediaPickerGalleryItem *item) {
|
||||
|
|
@ -156,27 +156,33 @@
|
|||
strongSelf->_galleryModel.dismiss(true, false);
|
||||
|
||||
if (strongSelf.completeWithItem != nil)
|
||||
strongSelf.completeWithItem(item, TGMediaPickerGalleryCompletionModeGeneric);
|
||||
strongSelf.completeWithItem(item, false, 0);
|
||||
};
|
||||
controller.sendSilently = ^{
|
||||
__strong TGMediaPickerModernGalleryMixin *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
return;
|
||||
|
||||
strongSelf->_galleryModel.dismiss(true, TGMediaPickerGalleryCompletionModeSilent);
|
||||
|
||||
if (strongSelf.completeWithItem != nil)
|
||||
strongSelf.completeWithItem(item, true);
|
||||
};
|
||||
controller.schedule = ^{
|
||||
__strong TGMediaPickerModernGalleryMixin *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
return;
|
||||
|
||||
strongSelf->_galleryModel.dismiss(true, false);
|
||||
|
||||
if (strongSelf.completeWithItem != nil)
|
||||
strongSelf.completeWithItem(item, TGMediaPickerGalleryCompletionModeSchedule);
|
||||
strongSelf.completeWithItem(item, true, 0);
|
||||
};
|
||||
controller.schedule = ^{
|
||||
__strong TGMediaPickerModernGalleryMixin *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
return;
|
||||
|
||||
strongSelf.presentScheduleController(^(int32_t time) {
|
||||
__strong TGMediaPickerModernGalleryMixin *strongSelf = weakSelf;
|
||||
if (strongSelf == nil)
|
||||
return;
|
||||
|
||||
strongSelf->_galleryModel.dismiss(true, false);
|
||||
|
||||
if (strongSelf.completeWithItem != nil)
|
||||
strongSelf.completeWithItem(item, false, time);
|
||||
});
|
||||
};
|
||||
|
||||
TGOverlayControllerWindow *controllerWindow = [[TGOverlayControllerWindow alloc] initWithManager:[strongSelf->_context makeOverlayWindowManager] parentController:strongSelf->_parentController contentController:controller];
|
||||
|
|
|
|||
|
|
@ -115,12 +115,21 @@
|
|||
_containerView.layer.cornerRadius = 12.0;
|
||||
[self.view addSubview:_containerView];
|
||||
|
||||
__weak TGMediaPickerSendActionSheetController *weakSelf = self;
|
||||
if (_canSendSilently) {
|
||||
_sendSilentlyButton = [[TGMediaPickerSendActionSheetItemView alloc] initWithTitle:TGLocalized(@"Conversation.SendMessage.SendSilently") icon:TGComponentsImageNamed(@"MediaMute")];
|
||||
_sendSilentlyButton.pressed = ^{
|
||||
__strong TGMediaPickerSendActionSheetController *strongSelf = weakSelf;
|
||||
[strongSelf sendSilentlyPressed];
|
||||
};
|
||||
[_containerView addSubview:_sendSilentlyButton];
|
||||
}
|
||||
|
||||
_scheduleButton = [[TGMediaPickerSendActionSheetItemView alloc] initWithTitle:TGLocalized(@"Conversation.SendMessage.ScheduleMessage") icon:TGComponentsImageNamed(@"MediaSchedule")];
|
||||
_scheduleButton.pressed = ^{
|
||||
__strong TGMediaPickerSendActionSheetController *strongSelf = weakSelf;
|
||||
[strongSelf schedulePressed];
|
||||
};
|
||||
[_containerView addSubview:_scheduleButton];
|
||||
|
||||
TGMediaAssetsPallete *pallete = nil;
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@
|
|||
|
||||
[TGPassportAttachMenu _displayCameraWithView:cameraView menuController:strongController parentController:strongParentController context:context intent:intent uploadAction:uploadAction];
|
||||
};
|
||||
carouselItem.sendPressed = ^(TGMediaAsset *currentItem, __unused bool asFiles, __unused TGMediaPickerGalleryCompletionMode mode)
|
||||
carouselItem.sendPressed = ^(TGMediaAsset *currentItem, __unused bool asFiles, __unused bool silentPosting, __unused int32_t scheduleTime)
|
||||
{
|
||||
__strong TGMenuSheetController *strongController = weakController;
|
||||
if (strongController == nil)
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ public struct LegacyAttachmentMenuMediaEditing: OptionSet {
|
|||
public static let imageOrVideo = LegacyAttachmentMenuMediaEditing(rawValue: 1 << 0)
|
||||
}
|
||||
|
||||
public func legacyAttachmentMenu(context: AccountContext, peer: Peer, editMediaOptions: LegacyAttachmentMenuMediaEditing?, saveEditedPhotos: Bool, allowGrouping: Bool, theme: PresentationTheme, strings: PresentationStrings, parentController: LegacyController, recentlyUsedInlineBots: [Peer], initialCaption: String, openGallery: @escaping () -> Void, openCamera: @escaping (TGAttachmentCameraView?, TGMenuSheetController?) -> Void, openFileGallery: @escaping () -> Void, openWebSearch: @escaping () -> Void, openMap: @escaping () -> Void, openContacts: @escaping () -> Void, openPoll: @escaping () -> Void, presentSelectionLimitExceeded: @escaping () -> Void, presentCantSendMultipleFiles: @escaping () -> Void, sendMessagesWithSignals: @escaping ([Any]?, Bool) -> Void, selectRecentlyUsedInlineBot: @escaping (Peer) -> Void) -> TGMenuSheetController {
|
||||
public func legacyAttachmentMenu(context: AccountContext, peer: Peer, editMediaOptions: LegacyAttachmentMenuMediaEditing?, saveEditedPhotos: Bool, allowGrouping: Bool, theme: PresentationTheme, strings: PresentationStrings, parentController: LegacyController, recentlyUsedInlineBots: [Peer], initialCaption: String, openGallery: @escaping () -> Void, openCamera: @escaping (TGAttachmentCameraView?, TGMenuSheetController?) -> Void, openFileGallery: @escaping () -> Void, openWebSearch: @escaping () -> Void, openMap: @escaping () -> Void, openContacts: @escaping () -> Void, openPoll: @escaping () -> Void, presentSelectionLimitExceeded: @escaping () -> Void, presentCantSendMultipleFiles: @escaping () -> Void, presentScheduleController: @escaping (@escaping (Int32) -> Void) -> Void, sendMessagesWithSignals: @escaping ([Any]?, Bool, Int32) -> Void, selectRecentlyUsedInlineBot: @escaping (Peer) -> Void) -> TGMenuSheetController {
|
||||
let isSecretChat = peer.id.namespace == Namespaces.Peer.SecretChat
|
||||
|
||||
let controller = TGMenuSheetController(context: parentController.context, dark: false)!
|
||||
|
|
@ -77,7 +77,12 @@ public func legacyAttachmentMenu(context: AccountContext, peer: Peer, editMediaO
|
|||
carouselItem.hasSilentPosting = !isSecretChat
|
||||
}
|
||||
carouselItem.hasSchedule = !isSecretChat
|
||||
carouselItem.sendPressed = { [weak controller, weak carouselItem] currentItem, asFiles, mode in
|
||||
carouselItem.presentScheduleController = { done in
|
||||
presentScheduleController { time in
|
||||
done?(time)
|
||||
}
|
||||
}
|
||||
carouselItem.sendPressed = { [weak controller, weak carouselItem] currentItem, asFiles, silentPosting, scheduleTime in
|
||||
if let controller = controller, let carouselItem = carouselItem {
|
||||
let intent: TGMediaAssetsControllerIntent = asFiles ? TGMediaAssetsControllerSendFileIntent : TGMediaAssetsControllerSendMediaIntent
|
||||
let signals = TGMediaAssetsController.resultSignals(for: carouselItem.selectionContext, editingContext: carouselItem.editingContext, intent: intent, currentItem: currentItem, storeAssets: true, useMediaCache: false, descriptionGenerator: legacyAssetPickerItemGenerator(), saveEditedPhotos: saveEditedPhotos)
|
||||
|
|
@ -85,7 +90,7 @@ public func legacyAttachmentMenu(context: AccountContext, peer: Peer, editMediaO
|
|||
presentCantSendMultipleFiles()
|
||||
} else {
|
||||
controller.dismiss(animated: true)
|
||||
sendMessagesWithSignals(signals, false)
|
||||
sendMessagesWithSignals(signals, silentPosting, scheduleTime)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ public func guessMimeTypeByFileExtension(_ ext: String) -> String {
|
|||
return TGMimeTypeMap.mimeType(forExtension: ext) ?? "application/binary"
|
||||
}
|
||||
|
||||
public func configureLegacyAssetPicker(_ controller: TGMediaAssetsController, context: AccountContext, peer: Peer, captionsEnabled: Bool = true, storeCreatedAssets: Bool = true, showFileTooltip: Bool = false, initialCaption: String, presentWebSearch: (() -> Void)?, presentSelectionLimitExceeded: @escaping () -> Void) {
|
||||
public func configureLegacyAssetPicker(_ controller: TGMediaAssetsController, context: AccountContext, peer: Peer, captionsEnabled: Bool = true, storeCreatedAssets: Bool = true, showFileTooltip: Bool = false, initialCaption: String, presentWebSearch: (() -> Void)?, presentSelectionLimitExceeded: @escaping () -> Void, presentScheduleController: @escaping (@escaping (Int32) -> Void) -> Void) {
|
||||
let isSecretChat = peer.id.namespace == Namespaces.Peer.SecretChat
|
||||
|
||||
controller.captionsEnabled = captionsEnabled
|
||||
|
|
@ -30,6 +30,11 @@ public func configureLegacyAssetPicker(_ controller: TGMediaAssetsController, co
|
|||
controller.hasSilentPosting = !isSecretChat
|
||||
}
|
||||
controller.hasSchedule = !isSecretChat
|
||||
controller.presentScheduleController = { done in
|
||||
presentScheduleController { time in
|
||||
done?(time)
|
||||
}
|
||||
}
|
||||
controller.dismissalBlock = {
|
||||
}
|
||||
controller.selectionLimitExceeded = {
|
||||
|
|
|
|||
|
|
@ -1005,7 +1005,7 @@ final class SecureIdAuthFormFieldNode: ASDisplayNode {
|
|||
transition.updateFrame(node: self.highlightedBackgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: -(hasPrevious ? UIScreenPixel : 0.0)), size: CGSize(width: width, height: height + (hasPrevious ? UIScreenPixel : 0.0))))
|
||||
|
||||
if let image = self.disclosureNode.image {
|
||||
self.disclosureNode.frame = CGRect(origin: CGPoint(x: width - 15.0 - image.size.width, y: floor((height - image.size.height) / 2.0)), size: image.size)
|
||||
self.disclosureNode.frame = CGRect(origin: CGPoint(x: width - 7.0 - image.size.width, y: floor((height - image.size.height) / 2.0)), size: image.size)
|
||||
}
|
||||
|
||||
if let image = self.checkNode.image {
|
||||
|
|
|
|||
|
|
@ -240,7 +240,7 @@ final class SecureIdAuthListFieldNode: ASDisplayNode {
|
|||
transition.updateFrame(node: self.highlightedBackgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: -(hasPrevious ? UIScreenPixel : 0.0)), size: CGSize(width: width, height: height + (hasPrevious ? UIScreenPixel : 0.0))))
|
||||
|
||||
if let image = self.disclosureNode.image {
|
||||
self.disclosureNode.frame = CGRect(origin: CGPoint(x: width - 15.0 - image.size.width, y: floor((height - image.size.height) / 2.0)), size: image.size)
|
||||
self.disclosureNode.frame = CGRect(origin: CGPoint(x: width - 7.0 - image.size.width, y: floor((height - image.size.height) / 2.0)), size: image.size)
|
||||
}
|
||||
|
||||
return height
|
||||
|
|
|
|||
|
|
@ -273,7 +273,7 @@ class ItemListSecretChatKeyItemNode: ListViewItemNode {
|
|||
}
|
||||
|
||||
if let arrowImage = strongSelf.arrowNode.image {
|
||||
strongSelf.arrowNode.frame = CGRect(origin: CGPoint(x: params.width - params.rightInset - 15.0 - arrowImage.size.width, y: 15.0), size: arrowImage.size)
|
||||
strongSelf.arrowNode.frame = CGRect(origin: CGPoint(x: params.width - params.rightInset - 7.0 - arrowImage.size.width, y: 15.0), size: arrowImage.size)
|
||||
}
|
||||
|
||||
switch item.disclosureStyle {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
09B4A9B823102B7A005C2E08 /* CreateThemeController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09B4A9B723102B7A005C2E08 /* CreateThemeController.swift */; };
|
||||
D03E465223075D930049C28B /* SettingsUI.h in Headers */ = {isa = PBXBuildFile; fileRef = D03E465023075D930049C28B /* SettingsUI.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
D03E466823075E660049C28B /* TabBarAccountSwitchControllerNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03E465C23075E630049C28B /* TabBarAccountSwitchControllerNode.swift */; };
|
||||
D03E466923075E660049C28B /* LogoutOptionsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03E465D23075E630049C28B /* LogoutOptionsController.swift */; };
|
||||
|
|
@ -25,7 +26,6 @@
|
|||
D03E467F23075EE90049C28B /* NotificationExceptionSettingsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03E467C23075EE90049C28B /* NotificationExceptionSettingsController.swift */; };
|
||||
D03E468023075EE90049C28B /* NotificationExceptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03E467D23075EE90049C28B /* NotificationExceptions.swift */; };
|
||||
D03E468123075EE90049C28B /* NotificationExceptionControllerNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03E467E23075EE90049C28B /* NotificationExceptionControllerNode.swift */; };
|
||||
D03E468423075EF60049C28B /* LanguageSuggestionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03E468323075EF60049C28B /* LanguageSuggestionController.swift */; };
|
||||
D03E468A23075F010049C28B /* SettingsSearchRecentQueries.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03E468523075EFF0049C28B /* SettingsSearchRecentQueries.swift */; };
|
||||
D03E468B23075F010049C28B /* SettingsSearchItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03E468623075F000049C28B /* SettingsSearchItem.swift */; };
|
||||
D03E468C23075F010049C28B /* SettingsSearchableItems.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03E468723075F000049C28B /* SettingsSearchableItems.swift */; };
|
||||
|
|
@ -191,6 +191,7 @@
|
|||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
09B4A9B723102B7A005C2E08 /* CreateThemeController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateThemeController.swift; sourceTree = "<group>"; };
|
||||
D03E464D23075D930049C28B /* SettingsUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SettingsUI.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
D03E465023075D930049C28B /* SettingsUI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SettingsUI.h; sourceTree = "<group>"; };
|
||||
D03E465123075D930049C28B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
|
|
@ -211,7 +212,6 @@
|
|||
D03E467C23075EE90049C28B /* NotificationExceptionSettingsController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationExceptionSettingsController.swift; sourceTree = "<group>"; };
|
||||
D03E467D23075EE90049C28B /* NotificationExceptions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationExceptions.swift; sourceTree = "<group>"; };
|
||||
D03E467E23075EE90049C28B /* NotificationExceptionControllerNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationExceptionControllerNode.swift; sourceTree = "<group>"; };
|
||||
D03E468323075EF60049C28B /* LanguageSuggestionController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LanguageSuggestionController.swift; sourceTree = "<group>"; };
|
||||
D03E468523075EFF0049C28B /* SettingsSearchRecentQueries.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsSearchRecentQueries.swift; sourceTree = "<group>"; };
|
||||
D03E468623075F000049C28B /* SettingsSearchItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsSearchItem.swift; sourceTree = "<group>"; };
|
||||
D03E468723075F000049C28B /* SettingsSearchableItems.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsSearchableItems.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -484,7 +484,6 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
D03E467423075E6C0049C28B /* Search */,
|
||||
D03E467523075E920049C28B /* Language Suggestion */,
|
||||
D03E467623075EA90049C28B /* Notifications */,
|
||||
D03E468223075EEB0049C28B /* Privacy and Security */,
|
||||
D03E46B423075F610049C28B /* Data and Storage */,
|
||||
|
|
@ -527,14 +526,6 @@
|
|||
path = Search;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D03E467523075E920049C28B /* Language Suggestion */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D03E468323075EF60049C28B /* LanguageSuggestionController.swift */,
|
||||
);
|
||||
path = "Language Suggestion";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D03E467623075EA90049C28B /* Notifications */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
|
@ -658,6 +649,7 @@
|
|||
D03E46E523075FD40049C28B /* WallpaperGalleryToolbarNode.swift */,
|
||||
D03E46EB23075FD60049C28B /* WallpaperPatternPanelNode.swift */,
|
||||
D03E46EE23075FD60049C28B /* WallpaperSearchRecentQueries.swift */,
|
||||
09B4A9B723102B7A005C2E08 /* CreateThemeController.swift */,
|
||||
);
|
||||
path = Themes;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -939,7 +931,7 @@
|
|||
D03E468E23075F010049C28B /* SettingsSearchRecentItem.swift in Sources */,
|
||||
D03E467223075E660049C28B /* ChangePhoneNumberCodeController.swift in Sources */,
|
||||
D03E466823075E660049C28B /* TabBarAccountSwitchControllerNode.swift in Sources */,
|
||||
D03E468423075EF60049C28B /* LanguageSuggestionController.swift in Sources */,
|
||||
09B4A9B823102B7A005C2E08 /* CreateThemeController.swift in Sources */,
|
||||
D03E471623075FE40049C28B /* WallpaperColorPanelNode.swift in Sources */,
|
||||
D03E470023075FE40049C28B /* ThemeColorsGridControllerItem.swift in Sources */,
|
||||
D03E468B23075F010049C28B /* SettingsSearchItem.swift in Sources */,
|
||||
|
|
|
|||
|
|
@ -1,368 +0,0 @@
|
|||
import Foundation
|
||||
import UIKit
|
||||
import SwiftSignalKit
|
||||
import AsyncDisplayKit
|
||||
import Display
|
||||
import TelegramCore
|
||||
import TelegramPresentationData
|
||||
import ActivityIndicator
|
||||
import AccountContext
|
||||
|
||||
struct LanguageSuggestionControllerStrings {
|
||||
let ChooseLanguage: String
|
||||
let Other: String
|
||||
let English: String
|
||||
|
||||
init(localization: SuggestedLocalizationInfo) {
|
||||
var chooseLanguage = "Choose Your Language"
|
||||
var other = "Other"
|
||||
var english = "English"
|
||||
|
||||
for entry in localization.extractedEntries {
|
||||
switch entry {
|
||||
case let .string(key, value):
|
||||
switch key {
|
||||
case "Localization.ChooseLanguage":
|
||||
chooseLanguage = value
|
||||
case "Localization.LanguageOther":
|
||||
other = value
|
||||
case "Localization.EnglishLanguageName":
|
||||
english = value
|
||||
default:
|
||||
break
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
self.ChooseLanguage = chooseLanguage
|
||||
self.Other = other
|
||||
self.English = english
|
||||
}
|
||||
|
||||
init(bundle: Bundle?) {
|
||||
var chooseLanguage = "Choose Your Language"
|
||||
var other = "Other"
|
||||
var english = "English"
|
||||
|
||||
if let bundle = bundle {
|
||||
for key in LanguageSuggestionControllerStrings.keys {
|
||||
let value = bundle.localizedString(forKey: key, value: nil, table: nil)
|
||||
if value != key {
|
||||
switch key {
|
||||
case "Localization.ChooseLanguage":
|
||||
chooseLanguage = value
|
||||
case "Localization.LanguageOther":
|
||||
other = value
|
||||
case "Localization.EnglishLanguageName":
|
||||
english = value
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.ChooseLanguage = chooseLanguage
|
||||
self.Other = other
|
||||
self.English = english
|
||||
}
|
||||
|
||||
static let keys: [String] = ["Localization.ChooseLanguage",
|
||||
"Localization.LanguageOther",
|
||||
"Localization.EnglishLanguageName"]
|
||||
}
|
||||
|
||||
private enum LanguageSuggestionItemType {
|
||||
case localization(String)
|
||||
case disclosure
|
||||
case action
|
||||
}
|
||||
|
||||
private struct LanguageSuggestionItem {
|
||||
public let type: LanguageSuggestionItemType
|
||||
public let title: String
|
||||
public let subtitle: String?
|
||||
public let action: () -> Void
|
||||
|
||||
public init(type: LanguageSuggestionItemType, title: String, subtitle: String?, action: @escaping () -> Void) {
|
||||
self.type = type
|
||||
self.title = title
|
||||
self.subtitle = subtitle
|
||||
self.action = action
|
||||
}
|
||||
}
|
||||
|
||||
private final class LanguageSuggestionItemNode: HighlightableButtonNode {
|
||||
private let backgroundNode: ASDisplayNode
|
||||
private let separatorNode: ASDisplayNode
|
||||
private let subtitleNode: ASTextNode
|
||||
private let iconNode: ASImageNode
|
||||
|
||||
let item: LanguageSuggestionItem
|
||||
|
||||
override var isSelected: Bool {
|
||||
didSet {
|
||||
if case .localization = self.item.type {
|
||||
self.iconNode.isHidden = !self.isSelected
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init(theme: PresentationTheme, item: LanguageSuggestionItem) {
|
||||
self.item = item
|
||||
|
||||
self.backgroundNode = ASDisplayNode()
|
||||
self.backgroundNode.isLayerBacked = true
|
||||
self.backgroundNode.backgroundColor = theme.actionSheet.opaqueItemHighlightedBackgroundColor
|
||||
self.backgroundNode.alpha = 0.0
|
||||
|
||||
self.separatorNode = ASDisplayNode()
|
||||
self.separatorNode.backgroundColor = theme.actionSheet.opaqueItemSeparatorColor
|
||||
|
||||
self.subtitleNode = ASTextNode()
|
||||
|
||||
self.iconNode = ASImageNode()
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.subtitleNode)
|
||||
self.addSubnode(self.separatorNode)
|
||||
self.addSubnode(self.iconNode)
|
||||
|
||||
var color: UIColor = theme.actionSheet.primaryTextColor
|
||||
var alignment: ASHorizontalAlignment = .left
|
||||
var inset: CGFloat = 19.0
|
||||
var icon: UIImage?
|
||||
switch item.type {
|
||||
case .action:
|
||||
alignment = .middle
|
||||
color = theme.actionSheet.controlAccentColor
|
||||
inset = 0.0
|
||||
case .disclosure:
|
||||
icon = PresentationResourcesItemList.disclosureArrowImage(theme)
|
||||
case .localization:
|
||||
icon = PresentationResourcesItemList.checkIconImage(theme)
|
||||
}
|
||||
|
||||
self.iconNode.image = icon
|
||||
self.contentHorizontalAlignment = alignment
|
||||
self.setTitle(item.title, with: Font.regular(17.0), with: color, for: [])
|
||||
|
||||
var titleVerticalOffset: CGFloat = 0.0
|
||||
if let subtitle = item.subtitle {
|
||||
self.subtitleNode.attributedText = NSAttributedString(string: subtitle, font: Font.regular(14.0), textColor: theme.actionSheet.secondaryTextColor)
|
||||
titleVerticalOffset = 20.0
|
||||
}
|
||||
self.contentEdgeInsets = UIEdgeInsets(top: 0.0, left: inset, bottom: titleVerticalOffset, right: 0.0)
|
||||
|
||||
self.highligthedChanged = { [weak self] value in
|
||||
if let strongSelf = self {
|
||||
if value {
|
||||
if strongSelf.backgroundNode.supernode == nil {
|
||||
strongSelf.insertSubnode(strongSelf.backgroundNode, at: 0)
|
||||
}
|
||||
strongSelf.backgroundNode.layer.removeAnimation(forKey: "opacity")
|
||||
strongSelf.backgroundNode.alpha = 1.0
|
||||
} else if !strongSelf.backgroundNode.alpha.isZero {
|
||||
strongSelf.backgroundNode.alpha = 0.0
|
||||
strongSelf.backgroundNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
super.didLoad()
|
||||
|
||||
self.addTarget(self, action: #selector(self.pressed), forControlEvents: .touchUpInside)
|
||||
}
|
||||
|
||||
@objc func pressed() {
|
||||
self.item.action()
|
||||
}
|
||||
|
||||
public func updateLayout(_ constrainedSize: CGSize) -> CGSize {
|
||||
let bounds = CGRect(origin: CGPoint(), size: CGSize(width: constrainedSize.width, height: self.item.subtitle != nil ? 58.0 : 44.0))
|
||||
self.backgroundNode.frame = bounds
|
||||
|
||||
let subtitleSize = self.subtitleNode.measure(bounds.size)
|
||||
self.subtitleNode.frame = CGRect(origin: CGPoint(x: 19.0, y: 31.0), size: subtitleSize)
|
||||
self.separatorNode.frame = CGRect(x: 0.0, y: bounds.height - UIScreenPixel, width: bounds.width, height: UIScreenPixel)
|
||||
if let icon = self.iconNode.image {
|
||||
self.iconNode.frame = CGRect(origin: CGPoint(x: bounds.width - icon.size.width - 19.0, y: floorToScreenPixels((bounds.height - icon.size.height) / 2.0)), size: icon.size)
|
||||
}
|
||||
return bounds.size
|
||||
}
|
||||
}
|
||||
|
||||
private final class LanguageSuggestionAlertContentNode: AlertContentNode {
|
||||
private var validLayout: CGSize?
|
||||
|
||||
private let titleNode: ASTextNode
|
||||
private let subtitleNode: ASTextNode
|
||||
private let titleSeparatorNode: ASDisplayNode
|
||||
private let activityIndicator: ActivityIndicator
|
||||
|
||||
private var nodes: [LanguageSuggestionItemNode]
|
||||
|
||||
private let disposable = MetaDisposable()
|
||||
|
||||
override var dismissOnOutsideTap: Bool {
|
||||
return self.isUserInteractionEnabled
|
||||
}
|
||||
|
||||
init(theme: PresentationTheme, strings: LanguageSuggestionControllerStrings, englishStrings: LanguageSuggestionControllerStrings, suggestedLocalization: LocalizationInfo, openSelection: @escaping () -> Void, applyLocalization: @escaping (String, () -> Void) -> Void, dismiss: @escaping () -> Void) {
|
||||
let selectedLocalization = ValuePromise(suggestedLocalization.languageCode, ignoreRepeated: true)
|
||||
|
||||
self.titleNode = ASTextNode()
|
||||
self.titleNode.attributedText = NSAttributedString(string: strings.ChooseLanguage, font: Font.bold(17.0), textColor: theme.actionSheet.primaryTextColor, paragraphAlignment: .center)
|
||||
self.titleNode.maximumNumberOfLines = 2
|
||||
|
||||
self.subtitleNode = ASTextNode()
|
||||
self.subtitleNode.attributedText = NSAttributedString(string: englishStrings.ChooseLanguage, font: Font.regular(14.0), textColor: theme.actionSheet.secondaryTextColor, paragraphAlignment: .center)
|
||||
self.subtitleNode.maximumNumberOfLines = 2
|
||||
|
||||
self.titleSeparatorNode = ASDisplayNode()
|
||||
self.titleSeparatorNode.backgroundColor = theme.actionSheet.opaqueItemSeparatorColor
|
||||
|
||||
self.activityIndicator = ActivityIndicator(type: .custom(theme.actionSheet.controlAccentColor, 22.0, 1.0, false))
|
||||
self.activityIndicator.isHidden = true
|
||||
|
||||
var items: [LanguageSuggestionItem] = []
|
||||
items.append(LanguageSuggestionItem(type: .localization(suggestedLocalization.languageCode), title: suggestedLocalization.localizedTitle, subtitle: suggestedLocalization.title, action: {
|
||||
selectedLocalization.set(suggestedLocalization.languageCode)
|
||||
}))
|
||||
items.append(LanguageSuggestionItem(type: .localization("en"), title: strings.English, subtitle: englishStrings.English, action: {
|
||||
selectedLocalization.set("en")
|
||||
}))
|
||||
items.append(LanguageSuggestionItem(type: .disclosure, title: strings.Other, subtitle: englishStrings.Other != strings.Other ? englishStrings.Other : nil, action: {
|
||||
openSelection()
|
||||
}))
|
||||
|
||||
var applyImpl: (() -> Void)?
|
||||
items.append(LanguageSuggestionItem(type: .action, title: "OK", subtitle: nil, action: {
|
||||
applyImpl?()
|
||||
}))
|
||||
|
||||
self.nodes = items.map { LanguageSuggestionItemNode(theme: theme, item: $0) }
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.titleNode)
|
||||
self.addSubnode(self.subtitleNode)
|
||||
self.addSubnode(self.titleSeparatorNode)
|
||||
self.addSubnode(self.activityIndicator)
|
||||
for node in self.nodes {
|
||||
self.addSubnode(node)
|
||||
}
|
||||
|
||||
self.disposable.set(selectedLocalization.get().start(next: { [weak self] selectedCode in
|
||||
if let strongSelf = self {
|
||||
for node in strongSelf.nodes {
|
||||
if case let .localization(code) = node.item.type {
|
||||
node.isSelected = code == selectedCode
|
||||
}
|
||||
}
|
||||
}
|
||||
}))
|
||||
|
||||
applyImpl = { [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.isUserInteractionEnabled = false
|
||||
|
||||
_ = (selectedLocalization.get()
|
||||
|> take(1)).start(next: { selectedCode in
|
||||
applyLocalization(selectedCode, { [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.activityIndicator.isHidden = false
|
||||
if let lastNode = strongSelf.nodes.last {
|
||||
lastNode.isHidden = true
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.disposable.dispose()
|
||||
}
|
||||
|
||||
override func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize {
|
||||
var size = size
|
||||
size.width = min(size.width, 270.0)
|
||||
|
||||
self.validLayout = size
|
||||
|
||||
var origin: CGPoint = CGPoint(x: 0.0, y: 17.0)
|
||||
|
||||
let titleSize = self.titleNode.measure(size)
|
||||
transition.updateFrame(node: self.titleNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - titleSize.width) / 2.0), y: origin.y), size: titleSize))
|
||||
origin.y += titleSize.height + 3.0
|
||||
|
||||
let subtitleSize = self.subtitleNode.measure(size)
|
||||
transition.updateFrame(node: self.subtitleNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - subtitleSize.width) / 2.0), y: origin.y), size: subtitleSize))
|
||||
origin.y += subtitleSize.height + 17.0
|
||||
transition.updateFrame(node: self.titleSeparatorNode, frame: CGRect(x: 0.0, y: origin.y - UIScreenPixel, width: size.width, height: UIScreenPixel))
|
||||
|
||||
var lastNodeSize: CGSize?
|
||||
for node in self.nodes {
|
||||
let size = node.updateLayout(size)
|
||||
transition.updateFrame(node: node, frame: CGRect(origin: origin, size: size))
|
||||
origin.y += size.height
|
||||
lastNodeSize = size
|
||||
}
|
||||
|
||||
if let lastSize = lastNodeSize {
|
||||
let indicatorSize = self.activityIndicator.measure(CGSize(width: 100.0, height: 100.0))
|
||||
transition.updateFrame(node: self.activityIndicator, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - indicatorSize.width) / 2.0), y: origin.y - lastSize.height + floorToScreenPixels((lastSize.height - indicatorSize.height) / 2.0)), size: indicatorSize))
|
||||
}
|
||||
|
||||
return CGSize(width: size.width, height: origin.y - UIScreenPixel)
|
||||
}
|
||||
}
|
||||
|
||||
func languageSuggestionController(context: AccountContext, suggestedLocalization: SuggestedLocalizationInfo, currentLanguageCode: String, openSelection: @escaping () -> Void) -> AlertController? {
|
||||
guard let localization = suggestedLocalization.availableLocalizations.filter({ $0.languageCode == suggestedLocalization.languageCode }).first else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let theme = context.sharedContext.currentPresentationData.with { $0 }.theme
|
||||
let strings = LanguageSuggestionControllerStrings(localization: suggestedLocalization)
|
||||
guard let mainPath = Bundle.main.path(forResource: "en", ofType: "lproj") else {
|
||||
return nil
|
||||
}
|
||||
let englishStrings = LanguageSuggestionControllerStrings(bundle: Bundle(path: mainPath))
|
||||
|
||||
let disposable = MetaDisposable()
|
||||
|
||||
var dismissImpl: ((Bool) -> Void)?
|
||||
let contentNode = LanguageSuggestionAlertContentNode(theme: theme, strings: strings, englishStrings: englishStrings, suggestedLocalization: localization, openSelection: {
|
||||
dismissImpl?(true)
|
||||
openSelection()
|
||||
}, applyLocalization: { languageCode, startActivity in
|
||||
if languageCode == currentLanguageCode {
|
||||
dismissImpl?(true)
|
||||
} else {
|
||||
startActivity()
|
||||
disposable.set((downloadAndApplyLocalization(accountManager: context.sharedContext.accountManager, postbox: context.account.postbox, network: context.account.network, languageCode: languageCode)
|
||||
|> deliverOnMainQueue).start(completed: {
|
||||
dismissImpl?(true)
|
||||
}))
|
||||
}
|
||||
}, dismiss: {
|
||||
dismissImpl?(true)
|
||||
})
|
||||
let controller = AlertController(theme: AlertControllerTheme(presentationTheme: theme), contentNode: contentNode)
|
||||
dismissImpl = { [weak controller] animated in
|
||||
if animated {
|
||||
controller?.dismissAnimated()
|
||||
} else {
|
||||
controller?.dismiss()
|
||||
}
|
||||
}
|
||||
return controller
|
||||
}
|
||||
|
|
@ -20,7 +20,7 @@ import ItemListPeerActionItem
|
|||
import TelegramStringFormatting
|
||||
|
||||
private final class NotificationExceptionState : Equatable {
|
||||
let mode:NotificationExceptionMode
|
||||
let mode: NotificationExceptionMode
|
||||
let isSearchMode: Bool
|
||||
let revealedPeerId: PeerId?
|
||||
let editing: Bool
|
||||
|
|
@ -93,7 +93,15 @@ public struct NotificationExceptionWrapper : Equatable {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public enum NotificationExceptionMode : Equatable {
|
||||
fileprivate enum Mode {
|
||||
case users
|
||||
case groups
|
||||
case channels
|
||||
}
|
||||
|
||||
public static func == (lhs: NotificationExceptionMode, rhs: NotificationExceptionMode) -> Bool {
|
||||
switch lhs {
|
||||
case let .users(lhsValue):
|
||||
|
|
@ -117,6 +125,17 @@ public enum NotificationExceptionMode : Equatable {
|
|||
}
|
||||
}
|
||||
|
||||
fileprivate var mode: Mode {
|
||||
switch self {
|
||||
case .users:
|
||||
return .users
|
||||
case .groups:
|
||||
return .groups
|
||||
case .channels:
|
||||
return .channels
|
||||
}
|
||||
}
|
||||
|
||||
var isEmpty: Bool {
|
||||
switch self {
|
||||
case let .users(value), let .groups(value), let .channels(value):
|
||||
|
|
@ -271,7 +290,7 @@ private func notificationsExceptionEntries(presentationData: PresentationData, s
|
|||
var entries: [NotificationExceptionEntry] = []
|
||||
|
||||
if !state.isSearchMode {
|
||||
entries.append(.addException(presentationData.theme, presentationData.strings, state.editing))
|
||||
entries.append(.addException(presentationData.theme, presentationData.strings, state.mode.mode, state.editing))
|
||||
}
|
||||
|
||||
var existingPeerIds = Set<PeerId>()
|
||||
|
|
@ -486,7 +505,7 @@ private enum NotificationExceptionEntry : ItemListNodeEntry {
|
|||
case search(PresentationTheme, PresentationStrings)
|
||||
case peer(index: Int, peer: Peer, theme: PresentationTheme, strings: PresentationStrings, dateFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, description: String, notificationSettings: TelegramPeerNotificationSettings, revealed: Bool, editing: Bool, isSearching: Bool)
|
||||
case addPeer(index: Int, peer: Peer, theme: PresentationTheme, strings: PresentationStrings, dateFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder)
|
||||
case addException(PresentationTheme, PresentationStrings, Bool)
|
||||
case addException(PresentationTheme, PresentationStrings, NotificationExceptionMode.Mode, Bool)
|
||||
case removeAll(PresentationTheme, PresentationStrings)
|
||||
|
||||
func item(_ arguments: NotificationExceptionArguments) -> ListViewItem {
|
||||
|
|
@ -495,8 +514,17 @@ private enum NotificationExceptionEntry : ItemListNodeEntry {
|
|||
return NotificationSearchItem(theme: theme, placeholder: strings.Common_Search, activate: {
|
||||
arguments.activateSearch()
|
||||
})
|
||||
case let .addException(theme, strings, editing):
|
||||
return ItemListPeerActionItem(theme: theme, icon: PresentationResourcesItemList.addExceptionIcon(theme), title: strings.Notification_Exceptions_AddException, alwaysPlain: true, sectionId: self.section, editing: editing, action: {
|
||||
case let .addException(theme, strings, mode, editing):
|
||||
let icon: UIImage?
|
||||
switch mode {
|
||||
case .users:
|
||||
icon = PresentationResourcesItemList.addPersonIcon(theme)
|
||||
case .groups:
|
||||
icon = PresentationResourcesItemList.createGroupIcon(theme)
|
||||
case .channels:
|
||||
icon = PresentationResourcesItemList.addChannelIcon(theme)
|
||||
}
|
||||
return ItemListPeerActionItem(theme: theme, icon: icon, title: strings.Notification_Exceptions_AddException, alwaysPlain: true, sectionId: self.section, editing: editing, action: {
|
||||
arguments.selectPeer()
|
||||
})
|
||||
case let .peer(_, peer, theme, strings, dateTimeFormat, nameDisplayOrder, value, _, revealed, editing, isSearching):
|
||||
|
|
@ -543,10 +571,10 @@ private enum NotificationExceptionEntry : ItemListNodeEntry {
|
|||
default:
|
||||
return false
|
||||
}
|
||||
case let .addException(lhsTheme, lhsStrings, lhsEditing):
|
||||
case let .addException(lhsTheme, lhsStrings, lhsMode, lhsEditing):
|
||||
switch rhs {
|
||||
case let .addException(rhsTheme, rhsStrings, rhsEditing):
|
||||
return lhsTheme === rhsTheme && lhsStrings === rhsStrings && lhsEditing == rhsEditing
|
||||
case let .addException(rhsTheme, rhsStrings, rhsMode, rhsEditing):
|
||||
return lhsTheme === rhsTheme && lhsStrings === rhsStrings && lhsMode == rhsMode && lhsEditing == rhsEditing
|
||||
default:
|
||||
return false
|
||||
}
|
||||
|
|
|
|||
189
submodules/SettingsUI/Sources/Themes/CreateThemeController.swift
Normal file
189
submodules/SettingsUI/Sources/Themes/CreateThemeController.swift
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
import SwiftSignalKit
|
||||
import Postbox
|
||||
import TelegramCore
|
||||
import TelegramPresentationData
|
||||
import TelegramUIPreferences
|
||||
import ItemListUI
|
||||
import AlertUI
|
||||
import AccountContext
|
||||
|
||||
private final class CreateThemeControllerArguments {
|
||||
let context: AccountContext
|
||||
let updateState: ((CreateThemeControllerState) -> CreateThemeControllerState) -> Void
|
||||
|
||||
init(context: AccountContext, updateState: @escaping ((CreateThemeControllerState) -> CreateThemeControllerState) -> Void) {
|
||||
self.context = context
|
||||
self.updateState = updateState
|
||||
}
|
||||
}
|
||||
|
||||
private enum CreateThemeControllerSection: Int32 {
|
||||
case chatPreview
|
||||
case info
|
||||
}
|
||||
|
||||
private enum CreateThemeControllerEntry: ItemListNodeEntry {
|
||||
case chatPreviewHeader(PresentationTheme, String)
|
||||
case chatPreview(PresentationTheme, PresentationTheme, TelegramWallpaper, PresentationFontSize, PresentationStrings, PresentationDateTimeFormat, PresentationPersonNameOrder)
|
||||
case title(PresentationTheme, PresentationStrings, String, String)
|
||||
case slug(PresentationTheme, PresentationStrings, String, String, Bool)
|
||||
|
||||
var section: ItemListSectionId {
|
||||
switch self {
|
||||
case .chatPreviewHeader, .chatPreview:
|
||||
return CreateThemeControllerSection.chatPreview.rawValue
|
||||
case .title, .slug:
|
||||
return CreateThemeControllerSection.info.rawValue
|
||||
}
|
||||
}
|
||||
|
||||
var stableId: Int32 {
|
||||
switch self {
|
||||
case .chatPreviewHeader:
|
||||
return 0
|
||||
case .chatPreview:
|
||||
return 1
|
||||
case .title:
|
||||
return 2
|
||||
case .slug:
|
||||
return 3
|
||||
}
|
||||
}
|
||||
|
||||
static func ==(lhs: CreateThemeControllerEntry, rhs: CreateThemeControllerEntry) -> Bool {
|
||||
switch lhs {
|
||||
case let .chatPreviewHeader(lhsTheme, lhsText):
|
||||
if case let .chatPreviewHeader(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .chatPreview(lhsTheme, lhsComponentTheme, lhsWallpaper, lhsFontSize, lhsStrings, lhsTimeFormat, lhsNameOrder):
|
||||
if case let .chatPreview(rhsTheme, rhsComponentTheme, rhsWallpaper, rhsFontSize, rhsStrings, rhsTimeFormat, rhsNameOrder) = rhs, lhsComponentTheme === rhsComponentTheme, lhsTheme === rhsTheme, lhsWallpaper == rhsWallpaper, lhsFontSize == rhsFontSize, lhsStrings === rhsStrings, lhsTimeFormat == rhsTimeFormat, lhsNameOrder == rhsNameOrder {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .title(lhsTheme, lhsStrings, lhsTitle, lhsValue):
|
||||
if case let .title(rhsTheme, rhsStrings, rhsTitle, rhsValue) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsTitle == rhsTitle, lhsValue == rhsValue {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .slug(lhsTheme, lhsStrings, lhsTitle, lhsValue, lhsEnabled):
|
||||
if case let .slug(rhsTheme, rhsStrings, rhsTitle, rhsValue, rhsEnabled) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsTitle == rhsTitle, lhsValue == rhsValue, lhsEnabled == rhsEnabled {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static func <(lhs: CreateThemeControllerEntry, rhs: CreateThemeControllerEntry) -> Bool {
|
||||
return lhs.stableId < rhs.stableId
|
||||
}
|
||||
|
||||
func item(_ arguments: CreateThemeControllerArguments) -> ListViewItem {
|
||||
switch self {
|
||||
case let .chatPreviewHeader(theme, text):
|
||||
return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section)
|
||||
case let .chatPreview(theme, componentTheme, wallpaper, fontSize, strings, dateTimeFormat, nameDisplayOrder):
|
||||
return ThemeSettingsChatPreviewItem(context: arguments.context, theme: theme, componentTheme: componentTheme, strings: strings, sectionId: self.section, fontSize: fontSize, wallpaper: wallpaper, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder)
|
||||
case let .title(theme, strings, title, text):
|
||||
return ItemListSingleLineInputItem(theme: theme, strings: strings, title: NSAttributedString(), text: text, placeholder: title, sectionId: self.section, textUpdated: { value in
|
||||
arguments.updateState { current in
|
||||
var state = current
|
||||
state.title = value
|
||||
return state
|
||||
}
|
||||
}, action: {})
|
||||
case let .slug(theme, strings, title, text, enabled):
|
||||
return ItemListSingleLineInputItem(theme: theme, strings: strings, title: NSAttributedString(string: "t.me/addtheme/", textColor: theme.list.itemPrimaryTextColor), text: text, placeholder: "Short Name", type: .username, enabled: enabled, sectionId: self.section, textUpdated: { value in
|
||||
arguments.updateState { current in
|
||||
var state = current
|
||||
state.slug = value
|
||||
return state
|
||||
}
|
||||
}, action: {})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private enum CreateThemeControllerMode {
|
||||
case create
|
||||
case update
|
||||
}
|
||||
|
||||
private struct CreateThemeControllerState: Equatable {
|
||||
var mode: CreateThemeControllerMode
|
||||
var title: String
|
||||
var slug: String
|
||||
|
||||
var isComplete: Bool {
|
||||
if self.title.isEmpty || self.slug.isEmpty {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
private func createThemeControllerEntries(presentationData: PresentationData, theme: PresentationTheme, state: CreateThemeControllerState) -> [CreateThemeControllerEntry] {
|
||||
var entries: [CreateThemeControllerEntry] = []
|
||||
|
||||
entries.append(.chatPreviewHeader(presentationData.theme, "Preview".uppercased()))
|
||||
entries.append(.chatPreview(presentationData.theme, theme, theme.chat.defaultWallpaper, presentationData.fontSize, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder))
|
||||
|
||||
entries.append(.title(presentationData.theme, presentationData.strings, "Title", state.title))
|
||||
entries.append(.slug(presentationData.theme, presentationData.strings, "Slug", state.slug, true))
|
||||
|
||||
return entries
|
||||
}
|
||||
|
||||
public func createThemeController(context: AccountContext, theme: PresentationTheme, resource: MediaResource) -> ViewController {
|
||||
let initialState = CreateThemeControllerState(mode: .create, title: "", slug: "")
|
||||
let statePromise = ValuePromise(initialState, ignoreRepeated: true)
|
||||
let stateValue = Atomic(value: initialState)
|
||||
let updateState: ((CreateThemeControllerState) -> CreateThemeControllerState) -> Void = { f in
|
||||
statePromise.set(stateValue.modify { f($0) })
|
||||
}
|
||||
|
||||
var pushControllerImpl: ((ViewController) -> Void)?
|
||||
var presentControllerImpl: ((ViewController, Any?) -> Void)?
|
||||
var dismissImpl: (() -> Void)?
|
||||
|
||||
let arguments = CreateThemeControllerArguments(context: context, updateState: { f in
|
||||
updateState(f)
|
||||
})
|
||||
|
||||
let signal = combineLatest(queue: .mainQueue(), context.sharedContext.presentationData, statePromise.get())
|
||||
|> map { presentationData, state -> (ItemListControllerState, (ItemListNodeState<CreateThemeControllerEntry>, CreateThemeControllerEntry.ItemGenerationArguments)) in
|
||||
let leftNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Cancel), style: .regular, enabled: true, action: {
|
||||
dismissImpl?()
|
||||
})
|
||||
let rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Done), style: .bold, enabled: state.isComplete, action: {
|
||||
let _ = (createTheme(account: context.account, resource: resource, title: state.title, slug: state.slug) |> deliverOnMainQueue).start(completed: {
|
||||
dismissImpl?()
|
||||
})
|
||||
})
|
||||
|
||||
let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text("Publish Theme"), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: false)
|
||||
let listState = ItemListNodeState(entries: createThemeControllerEntries(presentationData: presentationData, theme: theme, state: state), style: .blocks, emptyStateItem: nil, animateChanges: false)
|
||||
|
||||
return (controllerState, (listState, arguments))
|
||||
}
|
||||
|
||||
let controller = ItemListController(context: context, state: signal)
|
||||
pushControllerImpl = { [weak controller] c in
|
||||
(controller?.navigationController as? NavigationController)?.pushViewController(c)
|
||||
}
|
||||
presentControllerImpl = { [weak controller] c, a in
|
||||
controller?.present(c, in: .window(.root), with: a)
|
||||
}
|
||||
dismissImpl = { [weak controller] in
|
||||
let _ = controller?.dismiss()
|
||||
}
|
||||
return controller
|
||||
}
|
||||
|
|
@ -11,10 +11,15 @@ import AccountContext
|
|||
import ShareController
|
||||
import CounterContollerTitleView
|
||||
|
||||
public enum ThemePreviewSource {
|
||||
case theme(TelegramTheme)
|
||||
case media(AnyMediaReference)
|
||||
}
|
||||
|
||||
public final class ThemePreviewController: ViewController {
|
||||
private let context: AccountContext
|
||||
private let previewTheme: PresentationTheme
|
||||
private let media: AnyMediaReference
|
||||
private let source: ThemePreviewSource
|
||||
|
||||
private var controllerNode: ThemePreviewControllerNode {
|
||||
return self.displayNode as! ThemePreviewControllerNode
|
||||
|
|
@ -25,21 +30,28 @@ public final class ThemePreviewController: ViewController {
|
|||
private var presentationData: PresentationData
|
||||
private var presentationDataDisposable: Disposable?
|
||||
|
||||
public init(context: AccountContext, previewTheme: PresentationTheme, media: AnyMediaReference) {
|
||||
public init(context: AccountContext, previewTheme: PresentationTheme, source: ThemePreviewSource) {
|
||||
self.context = context
|
||||
self.previewTheme = previewTheme
|
||||
self.media = media
|
||||
self.source = source
|
||||
|
||||
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationTheme: self.previewTheme, presentationStrings: self.presentationData.strings))
|
||||
|
||||
let themeName: String
|
||||
if case let .theme(theme) = source {
|
||||
themeName = theme.title
|
||||
} else {
|
||||
themeName = previewTheme.name.string
|
||||
}
|
||||
|
||||
if let author = previewTheme.author {
|
||||
let titleView = CounterContollerTitleView(theme: self.previewTheme)
|
||||
titleView.title = CounterContollerTitle(title: self.previewTheme.name.string, counter: author)
|
||||
titleView.title = CounterContollerTitle(title: themeName, counter: author)
|
||||
self.navigationItem.titleView = titleView
|
||||
} else {
|
||||
self.title = previewTheme.name.string
|
||||
self.title = themeName
|
||||
}
|
||||
self.statusBar.statusBarStyle = self.previewTheme.rootController.statusBarStyle.style
|
||||
self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
|
||||
|
|
@ -83,6 +95,13 @@ public final class ThemePreviewController: ViewController {
|
|||
}
|
||||
}, apply: { [weak self] in
|
||||
if let strongSelf = self {
|
||||
let theme: PresentationThemeReference
|
||||
if case let .theme(info) = strongSelf.source {
|
||||
theme = .cloud(info)
|
||||
} else {
|
||||
theme = .builtin(.day)
|
||||
}
|
||||
|
||||
let _ = (strongSelf.context.sharedContext.accountManager.transaction { transaction -> Void in
|
||||
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.presentationThemeSettings, { entry in
|
||||
let current: PresentationThemeSettings
|
||||
|
|
@ -92,7 +111,7 @@ public final class ThemePreviewController: ViewController {
|
|||
current = PresentationThemeSettings.defaultSettings
|
||||
}
|
||||
|
||||
return PresentationThemeSettings(chatWallpaper: .color(0xffffff), theme: .builtin(.day), themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: current.themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
|
||||
return PresentationThemeSettings(chatWallpaper: .color(0xffffff), theme: theme, themeSpecificAccentColors: current.themeSpecificAccentColors, themeSpecificChatWallpapers: current.themeSpecificChatWallpapers, fontSize: current.fontSize, automaticThemeSwitchSetting: current.automaticThemeSwitchSetting, largeEmoji: current.largeEmoji, disableAnimations: current.disableAnimations)
|
||||
})
|
||||
}).start(completed: { [weak self] in
|
||||
if let strongSelf = self {
|
||||
|
|
@ -122,7 +141,22 @@ public final class ThemePreviewController: ViewController {
|
|||
}
|
||||
|
||||
@objc private func actionPressed() {
|
||||
let controller = ShareController(context: self.context, subject: .media(self.media))
|
||||
let subject: ShareControllerSubject
|
||||
let preferredAction: ShareControllerPreferredAction
|
||||
switch self.source {
|
||||
case let .theme(theme):
|
||||
subject = .url("https://t.me/addtheme/\(theme.slug)")
|
||||
preferredAction = .default
|
||||
case let .media(media):
|
||||
subject = .media(media)
|
||||
preferredAction = .custom(action: ShareControllerAction(title: "Publish", action: { [weak self] in
|
||||
if let strongSelf = self, let file = media.media as? TelegramMediaFile {
|
||||
let controller = createThemeController(context: strongSelf.context, theme: strongSelf.previewTheme, resource: file.resource)
|
||||
strongSelf.present(controller, in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet), blockInteraction: true)
|
||||
}
|
||||
}))
|
||||
}
|
||||
let controller = ShareController(context: self.context, subject: subject, preferredAction: preferredAction)
|
||||
self.present(controller, in: .window(.root), blockInteraction: true)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -195,22 +195,30 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||
peers[otherPeerId] = TelegramUser(id: otherPeerId, accessHash: nil, firstName: "", lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [])
|
||||
|
||||
let replyMessageId = MessageId(peerId: peerId, namespace: 0, id: 3)
|
||||
messages[replyMessageId] = Message(stableId: 3, stableVersion: 0, id: replyMessageId, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [])
|
||||
messages[replyMessageId] = Message(stableId: 3, stableVersion: 0, id: replyMessageId, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_ReplyText, attributes: [], media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [])
|
||||
|
||||
//let chatPresentationData = ChatPresentationData(theme: ChatPresentationThemeData(theme: self.previewTheme, wallpaper: self.previewTheme.chat.defaultWallpaper), fontSize: self.presentationData.fontSize, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: false, largeEmoji: false)
|
||||
let message1 = Message(stableId: 4, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 4), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66003, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [ReplyMessageAttribute(messageId: replyMessageId)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
|
||||
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message1, theme: self.previewTheme, strings: self.presentationData.strings, wallpaper: self.previewTheme.chat.defaultWallpaper, fontSize: self.presentationData.fontSize, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil))
|
||||
|
||||
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: Message(stableId: 4, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 4), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66003, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: "", attributes: [ReplyMessageAttribute(messageId: replyMessageId)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), theme: self.previewTheme, strings: self.presentationData.strings, wallpaper: self.previewTheme.chat.defaultWallpaper, fontSize: self.presentationData.fontSize, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil))
|
||||
let message2 = Message(stableId: 3, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 3), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66002, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_2_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
|
||||
|
||||
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: Message(stableId: 3, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 3), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66002, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), theme: self.previewTheme, strings: self.presentationData.strings, wallpaper: self.previewTheme.chat.defaultWallpaper, fontSize: self.presentationData.fontSize, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil))
|
||||
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message2, theme: self.previewTheme, strings: self.presentationData.strings, wallpaper: self.previewTheme.chat.defaultWallpaper, fontSize: self.presentationData.fontSize, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil))
|
||||
|
||||
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: Message(stableId: 2, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 2), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66001, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), theme: self.previewTheme, strings: self.presentationData.strings, wallpaper: self.previewTheme.chat.defaultWallpaper, fontSize: self.presentationData.fontSize, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil))
|
||||
let message3 = Message(stableId: 2, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 2), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66001, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_3_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
|
||||
|
||||
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message3, theme: self.previewTheme, strings: self.presentationData.strings, wallpaper: self.previewTheme.chat.defaultWallpaper, fontSize: self.presentationData.fontSize, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil))
|
||||
|
||||
let voiceAttributes: [TelegramMediaFileAttribute] = [.Audio(isVoice: true, duration: 14, title: nil, performer: nil, waveform: MemoryBuffer())]
|
||||
let voiceMedia = TelegramMediaFile(fileId: MediaId(namespace: 0, id: 0), partialReference: nil, resource: LocalFileMediaResource(fileId: 0), previewRepresentations: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: nil, attributes: voiceAttributes)
|
||||
|
||||
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66001, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [voiceMedia], peers: peers, associatedMessages: messages, associatedMessageIds: []), theme: self.previewTheme, strings: self.presentationData.strings, wallpaper: self.previewTheme.chat.defaultWallpaper, fontSize: self.presentationData.fontSize, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: FileMediaResourceStatus(mediaStatus: .playbackStatus(.playing), fetchStatus: .Local)))
|
||||
let message4 = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66001, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [voiceMedia], peers: peers, associatedMessages: messages, associatedMessageIds: [])
|
||||
|
||||
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: "", attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: []), theme: self.previewTheme, strings: self.presentationData.strings, wallpaper: self.previewTheme.chat.defaultWallpaper, fontSize: self.presentationData.fontSize, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil))
|
||||
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message4, theme: self.previewTheme, strings: self.presentationData.strings, wallpaper: self.previewTheme.chat.defaultWallpaper, fontSize: self.presentationData.fontSize, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: FileMediaResourceStatus(mediaStatus: .playbackStatus(.playing), fetchStatus: .Local)))
|
||||
|
||||
//items.append(ChatMessageItem(presentationData: chatPresentationData, context: self.context, chatLocation: .peer(peerId), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .contact, automaticDownloadNetworkType: .cellular, isRecentActions: false, forcedResourceStatus: FileMediaResourceStatus(mediaStatus: .playbackStatus(.playing), fetchStatus: .Local)), controllerInteraction: controllerInteraction, content: .message(message: , read: true, selection: .none, attributes: ChatMessageEntryAttributes()), disableDate: false))
|
||||
|
||||
let message5 = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 0), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_4_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])
|
||||
items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message5, theme: self.previewTheme, strings: self.presentationData.strings, wallpaper: self.previewTheme.chat.defaultWallpaper, fontSize: self.presentationData.fontSize, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil))
|
||||
|
||||
let params = ListViewItemLayoutParams(width: layout.size.width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right)
|
||||
if let messageNodes = self.messageNodes {
|
||||
|
|
@ -260,8 +268,8 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||
self.scrollNode.frame = bounds
|
||||
|
||||
let toolbarHeight = 49.0 + layout.intrinsicInsets.bottom
|
||||
self.chatListBackgroundNode.frame = CGRect(x: 0.0, y: 0.0, width: bounds.width, height: bounds.height)
|
||||
self.chatBackgroundNode.frame = CGRect(x: bounds.width, y: 0.0, width: bounds.width, height: bounds.height)
|
||||
self.chatListBackgroundNode.frame = CGRect(x: bounds.width, y: 0.0, width: bounds.width, height: bounds.height)
|
||||
self.chatBackgroundNode.frame = CGRect(x: 0.0, y: 0.0, width: bounds.width, height: bounds.height)
|
||||
|
||||
self.scrollNode.view.contentSize = CGSize(width: bounds.width * 2.0, height: bounds.height)
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ private func generateBorderImage(theme: PresentationTheme, bordered: Bool, selec
|
|||
let lineWidth: CGFloat
|
||||
if selected {
|
||||
var accentColor = theme.list.itemAccentColor
|
||||
if accentColor.rgb == UIColor.white.rgb {
|
||||
if accentColor.rgb == 0xffffff {
|
||||
accentColor = UIColor(rgb: 0x999999)
|
||||
}
|
||||
context.setStrokeColor(accentColor.cgColor)
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import TelegramCore
|
|||
import TelegramPresentationData
|
||||
import TelegramUIPreferences
|
||||
import ItemListUI
|
||||
import AlertUI
|
||||
import AccountContext
|
||||
|
||||
private final class ThemeSettingsControllerArguments {
|
||||
|
|
@ -294,16 +295,15 @@ private struct ThemeSettingsState: Equatable {
|
|||
}
|
||||
}
|
||||
|
||||
private func themeSettingsControllerEntries(presentationData: PresentationData, theme: PresentationTheme, themeReference: PresentationThemeReference, themeSpecificAccentColors: [Int64: PresentationThemeAccentColor], autoNightSettings: AutomaticThemeSwitchSetting, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, dateTimeFormat: PresentationDateTimeFormat, largeEmoji: Bool, disableAnimations: Bool, availableAppIcons: [PresentationAppIcon], currentAppIconName: String?) -> [ThemeSettingsControllerEntry] {
|
||||
private func themeSettingsControllerEntries(presentationData: PresentationData, theme: PresentationTheme, themeReference: PresentationThemeReference, themeSpecificAccentColors: [Int64: PresentationThemeAccentColor], availableThemes: [PresentationThemeReference], autoNightSettings: AutomaticThemeSwitchSetting, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, dateTimeFormat: PresentationDateTimeFormat, largeEmoji: Bool, disableAnimations: Bool, availableAppIcons: [PresentationAppIcon], currentAppIconName: String?) -> [ThemeSettingsControllerEntry] {
|
||||
var entries: [ThemeSettingsControllerEntry] = []
|
||||
|
||||
entries.append(.themeListHeader(presentationData.theme, strings.Appearance_ColorTheme.uppercased()))
|
||||
entries.append(.chatPreview(presentationData.theme, theme, wallpaper, fontSize, presentationData.strings, dateTimeFormat, presentationData.nameDisplayOrder))
|
||||
|
||||
let availableThemes: [PresentationThemeReference] = [.builtin(.dayClassic), .builtin(.day), .builtin(.night), .builtin(.nightAccent)]
|
||||
entries.append(.themeItem(presentationData.theme, presentationData.strings, availableThemes, themeReference, themeSpecificAccentColors, themeSpecificAccentColors[themeReference.index]))
|
||||
|
||||
if theme.name != .builtin(.dayClassic) {
|
||||
if case let .builtin(theme) = themeReference, theme != .dayClassic {
|
||||
entries.append(.accentColor(presentationData.theme, themeReference, strings.Appearance_AccentColor, themeSpecificAccentColors[themeReference.index]))
|
||||
}
|
||||
|
||||
|
|
@ -349,6 +349,8 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
|
|||
var pushControllerImpl: ((ViewController) -> Void)?
|
||||
var presentControllerImpl: ((ViewController, Any?) -> Void)?
|
||||
|
||||
var moreImpl: (() -> Void)?
|
||||
|
||||
let _ = telegramWallpapers(postbox: context.account.postbox, network: context.account.network).start()
|
||||
|
||||
let currentAppIcon: PresentationAppIcon?
|
||||
|
|
@ -426,9 +428,14 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
|
|||
context.sharedContext.applicationBindings.requestSetAlternateIconName(name, { _ in
|
||||
})
|
||||
})
|
||||
|
||||
let signal = combineLatest(queue: .mainQueue(), context.sharedContext.presentationData, context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.presentationThemeSettings]), availableAppIcons, currentAppIconName.get(), statePromise.get())
|
||||
|> map { presentationData, sharedData, availableAppIcons, currentAppIconName, state -> (ItemListControllerState, (ItemListNodeState<ThemeSettingsControllerEntry>, ThemeSettingsControllerEntry.ItemGenerationArguments)) in
|
||||
|
||||
let savedThemes = telegramThemes(postbox: context.account.postbox, network: context.account.network)
|
||||
|> map { themes -> [PresentationThemeReference] in
|
||||
return themes.map { .cloud($0) }
|
||||
}
|
||||
|
||||
let signal = combineLatest(queue: .mainQueue(), context.sharedContext.presentationData, context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.presentationThemeSettings]), savedThemes, availableAppIcons, currentAppIconName.get(), statePromise.get())
|
||||
|> map { presentationData, sharedData, savedThemes, availableAppIcons, currentAppIconName, state -> (ItemListControllerState, (ItemListNodeState<ThemeSettingsControllerEntry>, ThemeSettingsControllerEntry.ItemGenerationArguments)) in
|
||||
let settings = (sharedData.entries[ApplicationSpecificSharedDataKeys.presentationThemeSettings] as? PresentationThemeSettings) ?? PresentationThemeSettings.defaultSettings
|
||||
|
||||
let fontSize = settings.fontSize
|
||||
|
|
@ -446,8 +453,15 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
|
|||
wallpaper = settings.chatWallpaper
|
||||
}
|
||||
|
||||
let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(presentationData.strings.Appearance_Title), leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back))
|
||||
let listState = ItemListNodeState(entries: themeSettingsControllerEntries(presentationData: presentationData, theme: theme, themeReference: settings.theme, themeSpecificAccentColors: settings.themeSpecificAccentColors, autoNightSettings: settings.automaticThemeSwitchSetting, strings: presentationData.strings, wallpaper: wallpaper, fontSize: fontSize, dateTimeFormat: dateTimeFormat, largeEmoji: largeEmoji, disableAnimations: disableAnimations, availableAppIcons: availableAppIcons, currentAppIconName: currentAppIconName), style: .blocks, ensureVisibleItemTag: focusOnItemTag, animateChanges: false)
|
||||
let rightNavigationButton = ItemListNavigationButton(content: .icon(.action), style: .regular, enabled: true, action: {
|
||||
moreImpl?()
|
||||
})
|
||||
|
||||
let defaultThemes: [PresentationThemeReference] = [.builtin(.dayClassic), .builtin(.day), .builtin(.night), .builtin(.nightAccent)]
|
||||
let availableThemes = defaultThemes + savedThemes
|
||||
|
||||
let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(presentationData.strings.Appearance_Title), leftNavigationButton: nil, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back))
|
||||
let listState = ItemListNodeState(entries: themeSettingsControllerEntries(presentationData: presentationData, theme: theme, themeReference: settings.theme, themeSpecificAccentColors: settings.themeSpecificAccentColors, availableThemes: availableThemes, autoNightSettings: settings.automaticThemeSwitchSetting, strings: presentationData.strings, wallpaper: wallpaper, fontSize: fontSize, dateTimeFormat: dateTimeFormat, largeEmoji: largeEmoji, disableAnimations: disableAnimations, availableAppIcons: availableAppIcons, currentAppIconName: currentAppIconName), style: .blocks, ensureVisibleItemTag: focusOnItemTag, animateChanges: false)
|
||||
|
||||
return (controllerState, (listState, arguments))
|
||||
}
|
||||
|
|
@ -460,6 +474,46 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
|
|||
presentControllerImpl = { [weak controller] c, a in
|
||||
controller?.present(c, in: .window(.root), with: a)
|
||||
}
|
||||
moreImpl = { [weak controller] in
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
let actionSheet = ActionSheetController(presentationTheme: presentationData.theme)
|
||||
var items: [ActionSheetItem] = []
|
||||
items.append(ActionSheetButtonItem(title: "Create New Theme", color: .accent, action: { [weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
|
||||
var randomId: Int64 = 0
|
||||
arc4random_buf(&randomId, 8)
|
||||
let path = NSTemporaryDirectory() + "\(randomId)"
|
||||
|
||||
let encoder = PresentationThemeEncoder()
|
||||
let theme = presentationData.theme
|
||||
guard let strings = try? encoder.encode(theme), let _ = try? strings.write(toFile: path, atomically: true, encoding: .utf8) else {
|
||||
return
|
||||
}
|
||||
|
||||
let id = arc4random64()
|
||||
let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: id), partialReference: nil, resource: LocalFileReferenceMediaResource(localFilePath: path, randomId: id), previewRepresentations: [], immediateThumbnailData: nil, mimeType: "application/x-tgtheme-ios", size: nil, attributes: [.FileName(fileName: "\(theme.name.string).tgios-theme")])
|
||||
let message = EnqueueMessage.message(text: "", attributes: [], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil)
|
||||
|
||||
let _ = enqueueMessages(account: context.account, peerId: context.account.peerId, messages: [message]).start()
|
||||
|
||||
presentControllerImpl?(textAlertController(context: context, title: nil, text: "A new theme template has been added to your Saved Messages.", actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Settings_SavedMessages, action: {
|
||||
if let controller = controller, let navigationController = controller.navigationController as? NavigationController {
|
||||
context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(context.account.peerId)))
|
||||
}
|
||||
}), TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})], actionLayout: .vertical), nil)
|
||||
}))
|
||||
items.append(ActionSheetButtonItem(title: "Upload Theme", color: .accent, action: { [weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
|
||||
}))
|
||||
actionSheet.setItemGroups([ActionSheetItemGroup(items:items), ActionSheetItemGroup(items: [
|
||||
ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, action: { [weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
})
|
||||
])])
|
||||
presentControllerImpl?(actionSheet, nil)
|
||||
}
|
||||
return controller
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ private func generateBorderImage(theme: PresentationTheme, bordered: Bool, selec
|
|||
let lineWidth: CGFloat
|
||||
if selected {
|
||||
var accentColor = theme.list.itemAccentColor
|
||||
if accentColor.rgb == UIColor.white.rgb {
|
||||
if accentColor.rgb == 0xffffff {
|
||||
accentColor = UIColor(rgb: 0x999999)
|
||||
}
|
||||
context.setStrokeColor(accentColor.cgColor)
|
||||
|
|
@ -40,32 +40,40 @@ private func generateBorderImage(theme: PresentationTheme, bordered: Bool, selec
|
|||
|
||||
private func generateThemeIconImage(theme: PresentationThemeReference, accentColor: UIColor?) -> UIImage {
|
||||
return generateImage(CGSize(width: 98.0, height: 62.0), rotatedContext: { size, context in
|
||||
guard case let .builtin(theme) = theme else {
|
||||
return
|
||||
}
|
||||
let bounds = CGRect(origin: CGPoint(), size: size)
|
||||
|
||||
let background: UIColor
|
||||
let incomingFill: UIColor
|
||||
let outgoingFill: UIColor
|
||||
switch theme {
|
||||
case .dayClassic:
|
||||
case let .builtin(theme):
|
||||
switch theme {
|
||||
case .dayClassic:
|
||||
background = UIColor(rgb: 0xd6e2ee)
|
||||
incomingFill = UIColor(rgb: 0xffffff)
|
||||
outgoingFill = UIColor(rgb: 0xe1ffc7)
|
||||
case .day:
|
||||
background = .white
|
||||
incomingFill = UIColor(rgb: 0xd5dde6)
|
||||
outgoingFill = accentColor ?? UIColor(rgb: 0x007aff)
|
||||
case .night:
|
||||
background = UIColor(rgb: 0x000000)
|
||||
incomingFill = UIColor(rgb: 0x1f1f1f)
|
||||
outgoingFill = accentColor ?? UIColor(rgb: 0x313131)
|
||||
case .nightAccent:
|
||||
let accentColor = accentColor ?? UIColor(rgb: 0x007aff)
|
||||
background = accentColor.withMultiplied(hue: 1.024, saturation: 0.573, brightness: 0.18)
|
||||
incomingFill = accentColor.withMultiplied(hue: 1.024, saturation: 0.585, brightness: 0.25)
|
||||
outgoingFill = accentColor.withMultiplied(hue: 1.019, saturation: 0.731, brightness: 0.59)
|
||||
}
|
||||
case let .cloud(theme):
|
||||
background = UIColor(rgb: 0xd6e2ee)
|
||||
incomingFill = UIColor(rgb: 0xffffff)
|
||||
outgoingFill = UIColor(rgb: 0xe1ffc7)
|
||||
default:
|
||||
background = UIColor(rgb: 0xd6e2ee)
|
||||
incomingFill = UIColor(rgb: 0xffffff)
|
||||
outgoingFill = UIColor(rgb: 0xe1ffc7)
|
||||
case .day:
|
||||
background = .white
|
||||
incomingFill = UIColor(rgb: 0xd5dde6)
|
||||
outgoingFill = accentColor ?? UIColor(rgb: 0x007aff)
|
||||
case .night:
|
||||
background = UIColor(rgb: 0x000000)
|
||||
incomingFill = UIColor(rgb: 0x1f1f1f)
|
||||
outgoingFill = accentColor ?? UIColor(rgb: 0x313131)
|
||||
case .nightAccent:
|
||||
let accentColor = accentColor ?? UIColor(rgb: 0x007aff)
|
||||
background = accentColor.withMultiplied(hue: 1.024, saturation: 0.573, brightness: 0.18)
|
||||
incomingFill = accentColor.withMultiplied(hue: 1.024, saturation: 0.585, brightness: 0.25)
|
||||
outgoingFill = accentColor.withMultiplied(hue: 1.019, saturation: 0.731, brightness: 0.59)
|
||||
}
|
||||
|
||||
context.setFillColor(background.cgColor)
|
||||
|
|
@ -336,19 +344,22 @@ class ThemeSettingsThemeItemNode: ListViewItemNode, ItemListItemNode {
|
|||
}
|
||||
|
||||
let name: String?
|
||||
if case let .builtin(theme) = theme {
|
||||
switch theme {
|
||||
case .dayClassic:
|
||||
name = item.strings.Appearance_ThemeCarouselClassic
|
||||
case .day:
|
||||
name = item.strings.Appearance_ThemeCarouselDay
|
||||
case .night:
|
||||
name = item.strings.Appearance_ThemeCarouselNewNight
|
||||
case .nightAccent:
|
||||
name = item.strings.Appearance_ThemeCarouselTintedNight
|
||||
}
|
||||
} else {
|
||||
name = nil
|
||||
switch theme {
|
||||
case let .builtin(theme):
|
||||
switch theme {
|
||||
case .dayClassic:
|
||||
name = item.strings.Appearance_ThemeCarouselClassic
|
||||
case .day:
|
||||
name = item.strings.Appearance_ThemeCarouselDay
|
||||
case .night:
|
||||
name = item.strings.Appearance_ThemeCarouselNewNight
|
||||
case .nightAccent:
|
||||
name = item.strings.Appearance_ThemeCarouselTintedNight
|
||||
}
|
||||
case let .cloud(theme):
|
||||
name = theme.title
|
||||
default:
|
||||
name = nil
|
||||
}
|
||||
|
||||
if let name = name {
|
||||
|
|
|
|||
|
|
@ -238,6 +238,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||
dict[967122427] = { return Api.Update.parse_updateNewScheduledMessage($0) }
|
||||
dict[-1870238482] = { return Api.Update.parse_updateDeleteScheduledMessages($0) }
|
||||
dict[357013699] = { return Api.Update.parse_updateMessageReactions($0) }
|
||||
dict[-2112423005] = { return Api.Update.parse_updateTheme($0) }
|
||||
dict[1558266229] = { return Api.PopularContact.parse_popularContact($0) }
|
||||
dict[-373643672] = { return Api.FolderPeer.parse_folderPeer($0) }
|
||||
dict[367766557] = { return Api.ChannelParticipant.parse_channelParticipant($0) }
|
||||
|
|
@ -263,6 +264,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||
dict[-748155807] = { return Api.ContactStatus.parse_contactStatus($0) }
|
||||
dict[1679398724] = { return Api.SecureFile.parse_secureFileEmpty($0) }
|
||||
dict[-534283678] = { return Api.SecureFile.parse_secureFile($0) }
|
||||
dict[-199313886] = { return Api.account.Themes.parse_themesNotModified($0) }
|
||||
dict[2137482273] = { return Api.account.Themes.parse_themes($0) }
|
||||
dict[236446268] = { return Api.PhotoSize.parse_photoSizeEmpty($0) }
|
||||
dict[2009052699] = { return Api.PhotoSize.parse_photoSize($0) }
|
||||
dict[-374917894] = { return Api.PhotoSize.parse_photoCachedSize($0) }
|
||||
|
|
@ -274,6 +277,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||
dict[1359533640] = { return Api.messages.FoundStickerSets.parse_foundStickerSets($0) }
|
||||
dict[471437699] = { return Api.account.WallPapers.parse_wallPapersNotModified($0) }
|
||||
dict[1881892265] = { return Api.account.WallPapers.parse_wallPapers($0) }
|
||||
dict[1012306921] = { return Api.InputTheme.parse_inputTheme($0) }
|
||||
dict[-175567375] = { return Api.InputTheme.parse_inputThemeSlug($0) }
|
||||
dict[1158290442] = { return Api.messages.FoundGifs.parse_foundGifs($0) }
|
||||
dict[-1199954735] = { return Api.MessageReactions.parse_messageReactions($0) }
|
||||
dict[-1132476723] = { return Api.FileLocation.parse_fileLocationToBeDeprecated($0) }
|
||||
|
|
@ -420,7 +425,6 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||
dict[-2128640689] = { return Api.account.SentEmailCode.parse_sentEmailCode($0) }
|
||||
dict[-1038136962] = { return Api.EncryptedFile.parse_encryptedFileEmpty($0) }
|
||||
dict[1248893260] = { return Api.EncryptedFile.parse_encryptedFile($0) }
|
||||
dict[-557924733] = { return Api.CodeSettings.parse_codeSettings($0) }
|
||||
dict[-391902247] = { return Api.SecureValueError.parse_secureValueErrorData($0) }
|
||||
dict[12467706] = { return Api.SecureValueError.parse_secureValueErrorFrontSide($0) }
|
||||
dict[-2037765467] = { return Api.SecureValueError.parse_secureValueErrorReverseSide($0) }
|
||||
|
|
@ -715,6 +719,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||
dict[1363483106] = { return Api.DialogPeer.parse_dialogPeerFolder($0) }
|
||||
dict[-104284986] = { return Api.WebDocument.parse_webDocumentNoProxy($0) }
|
||||
dict[475467473] = { return Api.WebDocument.parse_webDocument($0) }
|
||||
dict[1211967244] = { return Api.Theme.parse_themeDocumentNotModified($0) }
|
||||
dict[1464749545] = { return Api.Theme.parse_theme($0) }
|
||||
dict[-1290580579] = { return Api.contacts.Found.parse_found($0) }
|
||||
dict[-368018716] = { return Api.ChannelAdminLogEventsFilter.parse_channelAdminLogEventsFilter($0) }
|
||||
dict[1889961234] = { return Api.PeerNotifySettings.parse_peerNotifySettingsEmpty($0) }
|
||||
|
|
@ -944,6 +950,8 @@ public struct Api {
|
|||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.SecureFile:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.account.Themes:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.PhotoSize:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.messages.Stickers:
|
||||
|
|
@ -954,6 +962,8 @@ public struct Api {
|
|||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.account.WallPapers:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.InputTheme:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.messages.FoundGifs:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.MessageReactions:
|
||||
|
|
@ -1068,8 +1078,6 @@ public struct Api {
|
|||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.EncryptedFile:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.CodeSettings:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.SecureValueError:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.NotifyPeer:
|
||||
|
|
@ -1302,6 +1310,8 @@ public struct Api {
|
|||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.WebDocument:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.Theme:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.contacts.Found:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.ChannelAdminLogEventsFilter:
|
||||
|
|
|
|||
|
|
@ -4022,6 +4022,7 @@ public extension Api {
|
|||
case updateNewScheduledMessage(message: Api.Message)
|
||||
case updateDeleteScheduledMessages(peer: Api.Peer, messages: [Int32])
|
||||
case updateMessageReactions(peer: Api.Peer, msgId: Int32, reactions: Api.MessageReactions)
|
||||
case updateTheme(theme: Api.Theme)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
|
|
@ -4648,6 +4649,12 @@ public extension Api {
|
|||
serializeInt32(msgId, buffer: buffer, boxed: false)
|
||||
reactions.serialize(buffer, true)
|
||||
break
|
||||
case .updateTheme(let theme):
|
||||
if boxed {
|
||||
buffer.appendInt32(-2112423005)
|
||||
}
|
||||
theme.serialize(buffer, true)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -4801,6 +4808,8 @@ public extension Api {
|
|||
return ("updateDeleteScheduledMessages", [("peer", peer), ("messages", messages)])
|
||||
case .updateMessageReactions(let peer, let msgId, let reactions):
|
||||
return ("updateMessageReactions", [("peer", peer), ("msgId", msgId), ("reactions", reactions)])
|
||||
case .updateTheme(let theme):
|
||||
return ("updateTheme", [("theme", theme)])
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -6067,6 +6076,19 @@ public extension Api {
|
|||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_updateTheme(_ reader: BufferReader) -> Update? {
|
||||
var _1: Api.Theme?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.Theme
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.Update.updateTheme(theme: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
public enum PopularContact: TypeConstructorDescription {
|
||||
|
|
@ -6964,6 +6986,64 @@ public extension Api {
|
|||
}
|
||||
}
|
||||
|
||||
}
|
||||
public enum InputTheme: TypeConstructorDescription {
|
||||
case inputTheme(id: Int64, accessHash: Int64)
|
||||
case inputThemeSlug(slug: String)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .inputTheme(let id, let accessHash):
|
||||
if boxed {
|
||||
buffer.appendInt32(1012306921)
|
||||
}
|
||||
serializeInt64(id, buffer: buffer, boxed: false)
|
||||
serializeInt64(accessHash, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputThemeSlug(let slug):
|
||||
if boxed {
|
||||
buffer.appendInt32(-175567375)
|
||||
}
|
||||
serializeString(slug, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .inputTheme(let id, let accessHash):
|
||||
return ("inputTheme", [("id", id), ("accessHash", accessHash)])
|
||||
case .inputThemeSlug(let slug):
|
||||
return ("inputThemeSlug", [("slug", slug)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_inputTheme(_ reader: BufferReader) -> InputTheme? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
var _2: Int64?
|
||||
_2 = reader.readInt64()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.InputTheme.inputTheme(id: _1!, accessHash: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputThemeSlug(_ reader: BufferReader) -> InputTheme? {
|
||||
var _1: String?
|
||||
_1 = parseString(reader)
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.InputTheme.inputThemeSlug(slug: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
public enum MessageReactions: TypeConstructorDescription {
|
||||
case messageReactions(flags: Int32, results: [Api.ReactionCount])
|
||||
|
|
@ -10430,40 +10510,6 @@ public extension Api {
|
|||
}
|
||||
}
|
||||
|
||||
}
|
||||
public enum CodeSettings: TypeConstructorDescription {
|
||||
case codeSettings(flags: Int32)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .codeSettings(let flags):
|
||||
if boxed {
|
||||
buffer.appendInt32(-557924733)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .codeSettings(let flags):
|
||||
return ("codeSettings", [("flags", flags)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_codeSettings(_ reader: BufferReader) -> CodeSettings? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.CodeSettings.codeSettings(flags: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
public enum SecureValueError: TypeConstructorDescription {
|
||||
case secureValueErrorData(type: Api.SecureValueType, dataHash: Buffer, field: String, text: String)
|
||||
|
|
@ -18178,6 +18224,74 @@ public extension Api {
|
|||
}
|
||||
}
|
||||
|
||||
}
|
||||
public enum Theme: TypeConstructorDescription {
|
||||
case themeDocumentNotModified
|
||||
case theme(flags: Int32, id: Int64, accessHash: Int64, slug: String, title: String, document: Api.Document)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .themeDocumentNotModified:
|
||||
if boxed {
|
||||
buffer.appendInt32(1211967244)
|
||||
}
|
||||
|
||||
break
|
||||
case .theme(let flags, let id, let accessHash, let slug, let title, let document):
|
||||
if boxed {
|
||||
buffer.appendInt32(1464749545)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt64(id, buffer: buffer, boxed: false)
|
||||
serializeInt64(accessHash, buffer: buffer, boxed: false)
|
||||
serializeString(slug, buffer: buffer, boxed: false)
|
||||
serializeString(title, buffer: buffer, boxed: false)
|
||||
document.serialize(buffer, true)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .themeDocumentNotModified:
|
||||
return ("themeDocumentNotModified", [])
|
||||
case .theme(let flags, let id, let accessHash, let slug, let title, let document):
|
||||
return ("theme", [("flags", flags), ("id", id), ("accessHash", accessHash), ("slug", slug), ("title", title), ("document", document)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_themeDocumentNotModified(_ reader: BufferReader) -> Theme? {
|
||||
return Api.Theme.themeDocumentNotModified
|
||||
}
|
||||
public static func parse_theme(_ reader: BufferReader) -> Theme? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int64?
|
||||
_2 = reader.readInt64()
|
||||
var _3: Int64?
|
||||
_3 = reader.readInt64()
|
||||
var _4: String?
|
||||
_4 = parseString(reader)
|
||||
var _5: String?
|
||||
_5 = parseString(reader)
|
||||
var _6: Api.Document?
|
||||
if let signature = reader.readInt32() {
|
||||
_6 = Api.parse(reader, signature: signature) as? Api.Document
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
let _c5 = _5 != nil
|
||||
let _c6 = _6 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 {
|
||||
return Api.Theme.theme(flags: _1!, id: _2!, accessHash: _3!, slug: _4!, title: _5!, document: _6!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
public enum ChannelAdminLogEventsFilter: TypeConstructorDescription {
|
||||
case channelAdminLogEventsFilter(flags: Int32)
|
||||
|
|
|
|||
|
|
@ -415,6 +415,62 @@ public struct account {
|
|||
}
|
||||
}
|
||||
|
||||
}
|
||||
public enum Themes: TypeConstructorDescription {
|
||||
case themesNotModified
|
||||
case themes(hash: Int32, themes: [Api.Theme])
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .themesNotModified:
|
||||
if boxed {
|
||||
buffer.appendInt32(-199313886)
|
||||
}
|
||||
|
||||
break
|
||||
case .themes(let hash, let themes):
|
||||
if boxed {
|
||||
buffer.appendInt32(2137482273)
|
||||
}
|
||||
serializeInt32(hash, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(themes.count))
|
||||
for item in themes {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .themesNotModified:
|
||||
return ("themesNotModified", [])
|
||||
case .themes(let hash, let themes):
|
||||
return ("themes", [("hash", hash), ("themes", themes)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_themesNotModified(_ reader: BufferReader) -> Themes? {
|
||||
return Api.account.Themes.themesNotModified
|
||||
}
|
||||
public static func parse_themes(_ reader: BufferReader) -> Themes? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: [Api.Theme]?
|
||||
if let _ = reader.readInt32() {
|
||||
_2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Theme.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.account.Themes.themes(hash: _1!, themes: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
public enum WallPapers: TypeConstructorDescription {
|
||||
case wallPapersNotModified
|
||||
|
|
@ -2868,6 +2924,33 @@ public extension Api {
|
|||
})
|
||||
}
|
||||
|
||||
public static func forwardMessages(flags: Int32, fromPeer: Api.InputPeer, id: [Int32], randomId: [Int64], toPeer: Api.InputPeer, scheduleDate: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-637606386)
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
fromPeer.serialize(buffer, true)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(id.count))
|
||||
for item in id {
|
||||
serializeInt32(item, buffer: buffer, boxed: false)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(randomId.count))
|
||||
for item in randomId {
|
||||
serializeInt64(item, buffer: buffer, boxed: false)
|
||||
}
|
||||
toPeer.serialize(buffer, true)
|
||||
if Int(flags) & Int(1 << 10) != 0 {serializeInt32(scheduleDate!, buffer: buffer, boxed: false)}
|
||||
return (FunctionDescription(name: "messages.forwardMessages", parameters: [("flags", flags), ("fromPeer", fromPeer), ("id", id), ("randomId", randomId), ("toPeer", toPeer), ("scheduleDate", scheduleDate)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.Updates?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.Updates
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
public static func sendInlineBotResult(flags: Int32, peer: Api.InputPeer, replyToMsgId: Int32?, randomId: Int64, queryId: Int64, id: String, scheduleDate: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(570955184)
|
||||
|
|
@ -2888,6 +2971,31 @@ public extension Api {
|
|||
})
|
||||
}
|
||||
|
||||
public static func editMessage(flags: Int32, peer: Api.InputPeer, id: Int32, message: String?, media: Api.InputMedia?, replyMarkup: Api.ReplyMarkup?, entities: [Api.MessageEntity]?, scheduleDate: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(1224152952)
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
peer.serialize(buffer, true)
|
||||
serializeInt32(id, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 11) != 0 {serializeString(message!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 14) != 0 {media!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 2) != 0 {replyMarkup!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 3) != 0 {buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(entities!.count))
|
||||
for item in entities! {
|
||||
item.serialize(buffer, true)
|
||||
}}
|
||||
if Int(flags) & Int(1 << 15) != 0 {serializeInt32(scheduleDate!, buffer: buffer, boxed: false)}
|
||||
return (FunctionDescription(name: "messages.editMessage", parameters: [("flags", flags), ("peer", peer), ("id", id), ("message", message), ("media", media), ("replyMarkup", replyMarkup), ("entities", entities), ("scheduleDate", scheduleDate)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.Updates?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.Updates
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
public static func sendMultiMedia(flags: Int32, peer: Api.InputPeer, replyToMsgId: Int32?, multiMedia: [Api.InputSingleMedia], scheduleDate: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-872345397)
|
||||
|
|
@ -2910,33 +3018,6 @@ public extension Api {
|
|||
})
|
||||
}
|
||||
|
||||
public static func forwardMessages(flags: Int32, fromPeer: Api.InputPeer, id: [Int32], randomId: [Int64], toPeer: Api.InputPeer, scheduleDate: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-637606386)
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
fromPeer.serialize(buffer, true)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(id.count))
|
||||
for item in id {
|
||||
serializeInt32(item, buffer: buffer, boxed: false)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(randomId.count))
|
||||
for item in randomId {
|
||||
serializeInt64(item, buffer: buffer, boxed: false)
|
||||
}
|
||||
toPeer.serialize(buffer, true)
|
||||
if Int(flags) & Int(1 << 10) != 0 {serializeInt32(scheduleDate!, buffer: buffer, boxed: false)}
|
||||
return (FunctionDescription(name: "messages.forwardMessages", parameters: [("flags", flags), ("fromPeer", fromPeer), ("id", id), ("randomId", randomId), ("toPeer", toPeer), ("scheduleDate", scheduleDate)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.Updates?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.Updates
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
public static func getScheduledHistory(peer: Api.InputPeer, hash: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.messages.Messages>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-490575781)
|
||||
|
|
@ -3009,31 +3090,6 @@ public extension Api {
|
|||
})
|
||||
}
|
||||
|
||||
public static func editMessage(flags: Int32, peer: Api.InputPeer, id: Int32, message: String?, media: Api.InputMedia?, replyMarkup: Api.ReplyMarkup?, entities: [Api.MessageEntity]?, scheduleDate: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(1224152952)
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
peer.serialize(buffer, true)
|
||||
serializeInt32(id, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 11) != 0 {serializeString(message!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 14) != 0 {media!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 2) != 0 {replyMarkup!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 3) != 0 {buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(entities!.count))
|
||||
for item in entities! {
|
||||
item.serialize(buffer, true)
|
||||
}}
|
||||
if Int(flags) & Int(1 << 15) != 0 {serializeInt32(scheduleDate!, buffer: buffer, boxed: false)}
|
||||
return (FunctionDescription(name: "messages.editMessage", parameters: [("flags", flags), ("peer", peer), ("id", id), ("message", message), ("media", media), ("replyMarkup", replyMarkup), ("entities", entities), ("scheduleDate", scheduleDate)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.Updates?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.Updates
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
public static func sendReaction(flags: Int32, peer: Api.InputPeer, msgId: Int32, reaction: String?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(627641572)
|
||||
|
|
@ -5628,6 +5684,119 @@ public extension Api {
|
|||
return result
|
||||
})
|
||||
}
|
||||
|
||||
public static func uploadTheme(flags: Int32, file: Api.InputFile, thumb: Api.InputFile?, fileName: String, mimeType: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Document>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(473805619)
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
file.serialize(buffer, true)
|
||||
if Int(flags) & Int(1 << 0) != 0 {thumb!.serialize(buffer, true)}
|
||||
serializeString(fileName, buffer: buffer, boxed: false)
|
||||
serializeString(mimeType, buffer: buffer, boxed: false)
|
||||
return (FunctionDescription(name: "account.uploadTheme", parameters: [("flags", flags), ("file", file), ("thumb", thumb), ("fileName", fileName), ("mimeType", mimeType)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Document? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.Document?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.Document
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
public static func createTheme(slug: String, title: String, document: Api.InputDocument) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Theme>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(729808255)
|
||||
serializeString(slug, buffer: buffer, boxed: false)
|
||||
serializeString(title, buffer: buffer, boxed: false)
|
||||
document.serialize(buffer, true)
|
||||
return (FunctionDescription(name: "account.createTheme", parameters: [("slug", slug), ("title", title), ("document", document)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Theme? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.Theme?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.Theme
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
public static func updateTheme(flags: Int32, theme: Api.InputTheme, slug: String?, title: String?, document: Api.InputDocument?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Theme>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-124161674)
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
theme.serialize(buffer, true)
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeString(slug!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 1) != 0 {serializeString(title!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 2) != 0 {document!.serialize(buffer, true)}
|
||||
return (FunctionDescription(name: "account.updateTheme", parameters: [("flags", flags), ("theme", theme), ("slug", slug), ("title", title), ("document", document)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Theme? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.Theme?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.Theme
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
public static func saveTheme(theme: Api.InputTheme, unsave: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-229175188)
|
||||
theme.serialize(buffer, true)
|
||||
unsave.serialize(buffer, true)
|
||||
return (FunctionDescription(name: "account.saveTheme", parameters: [("theme", theme), ("unsave", unsave)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.Bool?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.Bool
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
public static func installTheme(format: String, theme: Api.InputTheme) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-520600676)
|
||||
serializeString(format, buffer: buffer, boxed: false)
|
||||
theme.serialize(buffer, true)
|
||||
return (FunctionDescription(name: "account.installTheme", parameters: [("format", format), ("theme", theme)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.Bool?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.Bool
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
public static func getTheme(format: String, theme: Api.InputTheme, documentId: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Theme>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-1919060949)
|
||||
serializeString(format, buffer: buffer, boxed: false)
|
||||
theme.serialize(buffer, true)
|
||||
serializeInt64(documentId, buffer: buffer, boxed: false)
|
||||
return (FunctionDescription(name: "account.getTheme", parameters: [("format", format), ("theme", theme), ("documentId", documentId)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Theme? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.Theme?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.Theme
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
public static func getThemes(format: String, hash: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.account.Themes>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(676939512)
|
||||
serializeString(format, buffer: buffer, boxed: false)
|
||||
serializeInt32(hash, buffer: buffer, boxed: false)
|
||||
return (FunctionDescription(name: "account.getThemes", parameters: [("format", format), ("hash", hash)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.account.Themes? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.account.Themes?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.account.Themes
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
}
|
||||
public struct langpack {
|
||||
public static func getLangPack(langPack: String, langCode: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.LangPackDifference>) {
|
||||
|
|
|
|||
|
|
@ -122,12 +122,14 @@ private var declaredEncodables: Void = {
|
|||
declareEncodable(CachedLocalizationInfos.self, f: { CachedLocalizationInfos(decoder: $0) })
|
||||
declareEncodable(CachedSecureIdConfiguration.self, f: { CachedSecureIdConfiguration(decoder: $0) })
|
||||
declareEncodable(CachedWallpapersConfiguration.self, f: { CachedWallpapersConfiguration(decoder: $0) })
|
||||
declareEncodable(CachedThemesConfiguration.self, f: { CachedThemesConfiguration(decoder: $0) })
|
||||
declareEncodable(SynchronizeGroupedPeersOperation.self, f: { SynchronizeGroupedPeersOperation(decoder: $0) })
|
||||
declareEncodable(ContentPrivacySettings.self, f: { ContentPrivacySettings(decoder: $0) })
|
||||
declareEncodable(TelegramDeviceContactImportedData.self, f: { TelegramDeviceContactImportedData(decoder: $0) })
|
||||
declareEncodable(SecureFileMediaResource.self, f: { SecureFileMediaResource(decoder: $0) })
|
||||
declareEncodable(CachedStickerQueryResult.self, f: { CachedStickerQueryResult(decoder: $0) })
|
||||
declareEncodable(TelegramWallpaper.self, f: { TelegramWallpaper(decoder: $0) })
|
||||
declareEncodable(TelegramTheme.self, f: { TelegramTheme(decoder: $0) })
|
||||
declareEncodable(SynchronizeMarkAllUnseenPersonalMessagesOperation.self, f: { SynchronizeMarkAllUnseenPersonalMessagesOperation(decoder: $0) })
|
||||
declareEncodable(SynchronizeAppLogEventsOperation.self, f: { SynchronizeAppLogEventsOperation(decoder: $0) })
|
||||
declareEncodable(CachedRecentPeers.self, f: { CachedRecentPeers(decoder: $0) })
|
||||
|
|
|
|||
|
|
@ -1314,6 +1314,8 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo
|
|||
messageIds.append(MessageId(peerId: peer.peerId, namespace: Namespaces.Message.ScheduledCloud, id: message))
|
||||
}
|
||||
updatedState.deleteMessages(messageIds)
|
||||
case let .updateTheme(theme):
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
|
|
|||
|
|
@ -398,7 +398,6 @@ func enqueueMessages(transaction: Transaction, account: Account, peerId: PeerId,
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
storeMessages.append(StoreMessage(peerId: peerId, namespace: messageNamespace, globallyUniqueId: randomId, groupingKey: localGroupingKey, timestamp: effectiveTimestamp, flags: flags, tags: tags, globalTags: globalTags, localTags: localTags, forwardInfo: nil, authorId: authorId, text: text, attributes: attributes, media: mediaList))
|
||||
case let .forward(source, grouping, requestedAttributes):
|
||||
let sourceMessage = transaction.getMessage(source)
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ public struct Namespaces {
|
|||
public static let CloudWallpapers: Int32 = 6
|
||||
public static let CloudSavedStickers: Int32 = 7
|
||||
public static let RecentlyUsedHashtags: Int32 = 8
|
||||
public static let CloudThemes: Int32 = 9
|
||||
}
|
||||
|
||||
struct CachedItemCollection {
|
||||
|
|
@ -70,6 +71,7 @@ public struct Namespaces {
|
|||
public static let cachedStickerQueryResults: Int8 = 5
|
||||
public static let cachedSecureIdConfiguration: Int8 = 6
|
||||
public static let cachedWallpapersConfiguration: Int8 = 7
|
||||
public static let cachedThemesConfiguration: Int8 = 7
|
||||
}
|
||||
|
||||
struct UnorderedItemList {
|
||||
|
|
|
|||
90
submodules/TelegramCore/TelegramCore/Theme.swift
Normal file
90
submodules/TelegramCore/TelegramCore/Theme.swift
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
import Foundation
|
||||
#if os(macOS)
|
||||
import PostboxMac
|
||||
import SwiftSignalKitMac
|
||||
import TelegramApiMac
|
||||
#else
|
||||
import Postbox
|
||||
import SwiftSignalKit
|
||||
import TelegramApi
|
||||
#endif
|
||||
|
||||
public final class TelegramTheme: OrderedItemListEntryContents, Equatable {
|
||||
public let id: Int64
|
||||
public let accessHash: Int64
|
||||
public let slug: String
|
||||
public let title: String
|
||||
public let file: TelegramMediaFile
|
||||
public let isCreator: Bool
|
||||
public let isDefault: Bool
|
||||
|
||||
public init(id: Int64, accessHash: Int64, slug: String, title: String, file: TelegramMediaFile, isCreator: Bool, isDefault: Bool) {
|
||||
self.id = id
|
||||
self.accessHash = accessHash
|
||||
self.slug = slug
|
||||
self.title = title
|
||||
self.file = file
|
||||
self.isCreator = isCreator
|
||||
self.isDefault = isDefault
|
||||
}
|
||||
|
||||
public init(decoder: PostboxDecoder) {
|
||||
self.id = decoder.decodeInt64ForKey("id", orElse: 0)
|
||||
self.accessHash = decoder.decodeInt64ForKey("accessHash", orElse: 0)
|
||||
self.slug = decoder.decodeStringForKey("slug", orElse: "")
|
||||
self.title = decoder.decodeStringForKey("title", orElse: "")
|
||||
self.file = decoder.decodeObjectForKey("file", decoder: { TelegramMediaFile(decoder: $0) }) as! TelegramMediaFile
|
||||
self.isCreator = decoder.decodeInt32ForKey("isCreator", orElse: 0) != 0
|
||||
self.isDefault = decoder.decodeInt32ForKey("isDefault", orElse: 0) != 0
|
||||
}
|
||||
|
||||
public func encode(_ encoder: PostboxEncoder) {
|
||||
encoder.encodeInt64(self.id, forKey: "id")
|
||||
encoder.encodeInt64(self.accessHash, forKey: "accessHash")
|
||||
encoder.encodeString(self.slug, forKey: "slug")
|
||||
encoder.encodeString(self.title, forKey: "title")
|
||||
encoder.encodeObject(self.file, forKey: "file")
|
||||
encoder.encodeInt32(self.isCreator ? 1 : 0, forKey: "isCreator")
|
||||
encoder.encodeInt32(self.isDefault ? 1 : 0, forKey: "isDefault")
|
||||
}
|
||||
|
||||
public static func ==(lhs: TelegramTheme, rhs: TelegramTheme) -> Bool {
|
||||
if lhs.id != rhs.id {
|
||||
return false
|
||||
}
|
||||
if lhs.accessHash != rhs.accessHash {
|
||||
return false
|
||||
}
|
||||
if lhs.slug != rhs.slug {
|
||||
return false
|
||||
}
|
||||
if lhs.title != rhs.title {
|
||||
return false
|
||||
}
|
||||
if lhs.file.id != rhs.file.id {
|
||||
return false
|
||||
}
|
||||
if lhs.isCreator != rhs.isCreator {
|
||||
return false
|
||||
}
|
||||
if lhs.isDefault != rhs.isDefault {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
extension TelegramTheme {
|
||||
convenience init?(apiTheme: Api.Theme) {
|
||||
switch apiTheme {
|
||||
case let .theme(flags, id, accessHash, slug, title, document):
|
||||
if let file = telegramMediaFileFromApiDocument(document) {
|
||||
self.init(id: id, accessHash: accessHash, slug: slug, title: title, file: file, isCreator: (flags & 1 << 0) != 0, isDefault: (flags & 1 << 1) != 0)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
226
submodules/TelegramCore/TelegramCore/Themes.swift
Normal file
226
submodules/TelegramCore/TelegramCore/Themes.swift
Normal file
|
|
@ -0,0 +1,226 @@
|
|||
import Foundation
|
||||
#if os(macOS)
|
||||
import PostboxMac
|
||||
import SwiftSignalKitMac
|
||||
import TelegramApiMac
|
||||
#else
|
||||
import Postbox
|
||||
import SwiftSignalKit
|
||||
import TelegramApi
|
||||
#endif
|
||||
|
||||
final class CachedThemesConfiguration: PostboxCoding {
|
||||
let hash: Int32
|
||||
|
||||
init(hash: Int32) {
|
||||
self.hash = hash
|
||||
}
|
||||
|
||||
init(decoder: PostboxDecoder) {
|
||||
self.hash = decoder.decodeInt32ForKey("hash", orElse: 0)
|
||||
}
|
||||
|
||||
func encode(_ encoder: PostboxEncoder) {
|
||||
encoder.encodeInt32(self.hash, forKey: "hash")
|
||||
}
|
||||
}
|
||||
|
||||
#if os(macOS)
|
||||
private let themeFormat = "macos"
|
||||
private let themeFileExtension = "palette"
|
||||
#else
|
||||
private let themeFormat = "ios"
|
||||
private let themeFileExtension = "tgios-theme"
|
||||
#endif
|
||||
|
||||
public func telegramThemes(postbox: Postbox, network: Network, forceUpdate: Bool = false) -> Signal<[TelegramTheme], NoError> {
|
||||
let fetch: ([TelegramTheme]?, Int32?) -> Signal<[TelegramTheme], NoError> = { current, hash in
|
||||
network.request(Api.functions.account.getThemes(format: themeFormat, hash: hash ?? 0))
|
||||
|> retryRequest
|
||||
|> mapToSignal { result -> Signal<([TelegramTheme], Int32), NoError> in
|
||||
switch result {
|
||||
case let .themes(hash, themes):
|
||||
let result = themes.compactMap { TelegramTheme(apiTheme: $0) }
|
||||
if result == current {
|
||||
return .complete()
|
||||
} else {
|
||||
return .single((result, hash))
|
||||
}
|
||||
case .themesNotModified:
|
||||
return .complete()
|
||||
}
|
||||
}
|
||||
|> mapToSignal { items, hash -> Signal<[TelegramTheme], NoError> in
|
||||
return postbox.transaction { transaction -> [TelegramTheme] in
|
||||
var entries: [OrderedItemListEntry] = []
|
||||
for item in items {
|
||||
var intValue = Int32(entries.count)
|
||||
let id = MemoryBuffer(data: Data(bytes: &intValue, count: 4))
|
||||
entries.append(OrderedItemListEntry(id: id, contents: item))
|
||||
}
|
||||
transaction.replaceOrderedItemListItems(collectionId: Namespaces.OrderedItemList.CloudThemes, items: entries)
|
||||
transaction.putItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedThemesConfiguration, key: ValueBoxKey(length: 0)), entry: CachedThemesConfiguration(hash: hash), collectionSpec: ItemCacheCollectionSpec(lowWaterItemCount: 1, highWaterItemCount: 1))
|
||||
return items
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if forceUpdate {
|
||||
return fetch(nil, nil)
|
||||
} else {
|
||||
return postbox.transaction { transaction -> ([TelegramTheme], Int32?) in
|
||||
let configuration = transaction.retrieveItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedThemesConfiguration, key: ValueBoxKey(length: 0))) as? CachedThemesConfiguration
|
||||
let items = transaction.getOrderedListItems(collectionId: Namespaces.OrderedItemList.CloudThemes)
|
||||
return (items.map { $0.contents as! TelegramTheme }, configuration?.hash)
|
||||
}
|
||||
|> mapToSignal { current, hash -> Signal<[TelegramTheme], NoError> in
|
||||
return .single(current)
|
||||
|> then(fetch(current, hash))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum GetThemeError {
|
||||
case generic
|
||||
}
|
||||
|
||||
public func getTheme(account: Account, slug: String) -> Signal<TelegramTheme, GetThemeError> {
|
||||
return account.network.request(Api.functions.account.getTheme(format: themeFormat, theme: .inputThemeSlug(slug: slug), documentId: 0))
|
||||
|> mapError { _ -> GetThemeError in return .generic }
|
||||
|> mapToSignal { theme -> Signal<TelegramTheme, GetThemeError> in
|
||||
if let theme = TelegramTheme(apiTheme: theme) {
|
||||
return .single(theme)
|
||||
} else {
|
||||
return .fail(.generic)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func saveTheme(account: Account, theme: TelegramTheme) -> Signal<Void, NoError> {
|
||||
return saveUnsaveTheme(account: account, theme: theme, unsave: false)
|
||||
}
|
||||
|
||||
public func deleteTheme(account: Account, theme: TelegramTheme) -> Signal<Void, NoError> {
|
||||
return saveUnsaveTheme(account: account, theme: theme, unsave: true)
|
||||
}
|
||||
|
||||
private func saveUnsaveTheme(account: Account, theme: TelegramTheme, unsave: Bool) -> Signal<Void, NoError> {
|
||||
return account.network.request(Api.functions.account.saveTheme(theme: Api.InputTheme.inputTheme(id: theme.id, accessHash: theme.accessHash), unsave: unsave ? Api.Bool.boolTrue : Api.Bool.boolFalse))
|
||||
|> `catch` { _ -> Signal<Api.Bool, NoError> in
|
||||
return .complete()
|
||||
}
|
||||
|> mapToSignal { _ -> Signal<Void, NoError> in
|
||||
return .single(Void())
|
||||
}
|
||||
}
|
||||
|
||||
public func installTheme(account: Account, theme: TelegramTheme) -> Signal<Void, NoError> {
|
||||
return account.network.request(Api.functions.account.installTheme(format: themeFormat, theme: Api.InputTheme.inputTheme(id: theme.id, accessHash: theme.accessHash)))
|
||||
|> `catch` { _ -> Signal<Api.Bool, NoError> in
|
||||
return .complete()
|
||||
}
|
||||
|> mapToSignal { _ -> Signal<Void, NoError> in
|
||||
return .complete()
|
||||
}
|
||||
}
|
||||
|
||||
public enum UploadThemeStatus {
|
||||
case progress(Float)
|
||||
case complete(TelegramMediaFile)
|
||||
}
|
||||
|
||||
public enum UploadThemeError {
|
||||
case generic
|
||||
}
|
||||
|
||||
private struct UploadedThemeData {
|
||||
fileprivate let resource: MediaResource
|
||||
fileprivate let content: UploadedThemeDataContent
|
||||
}
|
||||
|
||||
private enum UploadedThemeDataContent {
|
||||
case result(MultipartUploadResult)
|
||||
case error
|
||||
}
|
||||
|
||||
private func uploadedTheme(postbox: Postbox, network: Network, resource: MediaResource) -> Signal<UploadedThemeData, NoError> {
|
||||
return multipartUpload(network: network, postbox: postbox, source: .resource(.standalone(resource: resource)), encrypt: false, tag: TelegramMediaResourceFetchTag(statsCategory: .file), hintFileSize: nil, hintFileIsLarge: false)
|
||||
|> map { result -> UploadedThemeData in
|
||||
return UploadedThemeData(resource: resource, content: .result(result))
|
||||
}
|
||||
|> `catch` { _ -> Signal<UploadedThemeData, NoError> in
|
||||
return .single(UploadedThemeData(resource: resource, content: .error))
|
||||
}
|
||||
}
|
||||
|
||||
private func uploadTheme(account: Account, resource: MediaResource) -> Signal<UploadThemeStatus, UploadThemeError> {
|
||||
let fileName = "theme.\(themeFileExtension)"
|
||||
let mimeType = "application/x-tgtheme-\(themeFormat)"
|
||||
|
||||
return uploadedTheme(postbox: account.postbox, network: account.network, resource: resource)
|
||||
|> mapError { _ -> UploadThemeError in return .generic }
|
||||
|> mapToSignal { result -> Signal<(UploadThemeStatus, MediaResource?), UploadThemeError> in
|
||||
switch result.content {
|
||||
case .error:
|
||||
return .fail(.generic)
|
||||
case let .result(resultData):
|
||||
switch resultData {
|
||||
case let .progress(progress):
|
||||
return .single((.progress(progress), result.resource))
|
||||
case let .inputFile(file):
|
||||
return account.network.request(Api.functions.account.uploadTheme(flags: 0, file: file, thumb: nil, fileName: fileName, mimeType: mimeType))
|
||||
|> mapError { _ in return UploadThemeError.generic }
|
||||
|> mapToSignal { document -> Signal<(UploadThemeStatus, MediaResource?), UploadThemeError> in
|
||||
if let file = telegramMediaFileFromApiDocument(document) {
|
||||
return .single((.complete(file), result.resource))
|
||||
} else {
|
||||
return .fail(.generic)
|
||||
}
|
||||
}
|
||||
default:
|
||||
return .fail(.generic)
|
||||
}
|
||||
}
|
||||
}
|
||||
|> map { result, _ -> UploadThemeStatus in
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
public enum CreateThemeError {
|
||||
case generic
|
||||
}
|
||||
|
||||
public func createTheme(account: Account, resource: MediaResource, title: String, slug: String) -> Signal<TelegramTheme, CreateThemeError> {
|
||||
return uploadTheme(account: account, resource: resource)
|
||||
|> mapError { _ in return CreateThemeError.generic }
|
||||
|> mapToSignal { status -> Signal<TelegramTheme, CreateThemeError> in
|
||||
switch status {
|
||||
case let .complete(file):
|
||||
if let resource = file.resource as? CloudDocumentMediaResource {
|
||||
return account.network.request(Api.functions.account.createTheme(slug: slug, title: title, document: .inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: resource.fileReference))))
|
||||
|> mapError { _ in return CreateThemeError.generic }
|
||||
|> mapToSignal { apiTheme -> Signal<TelegramTheme, CreateThemeError> in
|
||||
if let theme = TelegramTheme(apiTheme: apiTheme) {
|
||||
return .single(theme)
|
||||
} else {
|
||||
return .fail(.generic)
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
return .fail(.generic)
|
||||
}
|
||||
default:
|
||||
return .complete()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func updateTheme(account: Account, theme: TelegramTheme, title: String?, resource: MediaResource?) -> Signal<Bool, NoError> {
|
||||
guard title != nil || resource != nil else {
|
||||
return .complete()
|
||||
}
|
||||
|
||||
return .never()
|
||||
}
|
||||
|
|
@ -26,7 +26,7 @@ final class CachedWallpapersConfiguration: PostboxCoding {
|
|||
}
|
||||
|
||||
public func telegramWallpapers(postbox: Postbox, network: Network, forceUpdate: Bool = false) -> Signal<[TelegramWallpaper], NoError> {
|
||||
let fetch: ([TelegramWallpaper]?, Int32?) -> Signal<[TelegramWallpaper], NoError> = { list, hash in
|
||||
let fetch: ([TelegramWallpaper]?, Int32?) -> Signal<[TelegramWallpaper], NoError> = { current, hash in
|
||||
network.request(Api.functions.account.getWallPapers(hash: hash ?? 0))
|
||||
|> retryRequest
|
||||
|> mapToSignal { result -> Signal<([TelegramWallpaper], Int32), NoError> in
|
||||
|
|
@ -49,7 +49,7 @@ public func telegramWallpapers(postbox: Postbox, network: Network, forceUpdate:
|
|||
items.append(.builtin(WallpaperSettings()))
|
||||
}
|
||||
|
||||
if items == list {
|
||||
if items == current {
|
||||
return .complete()
|
||||
} else {
|
||||
return .single((items, hash))
|
||||
|
|
@ -85,9 +85,9 @@ public func telegramWallpapers(postbox: Postbox, network: Network, forceUpdate:
|
|||
return (items.map { $0.contents as! TelegramWallpaper }, configuration?.hash)
|
||||
}
|
||||
}
|
||||
|> mapToSignal { list, hash -> Signal<[TelegramWallpaper], NoError> in
|
||||
return .single(list)
|
||||
|> then(fetch(list, hash))
|
||||
|> mapToSignal { current, hash -> Signal<[TelegramWallpaper], NoError> in
|
||||
return .single(current)
|
||||
|> then(fetch(current, hash))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -101,7 +101,7 @@ public enum UploadWallpaperError {
|
|||
case generic
|
||||
}
|
||||
|
||||
public struct UploadedWallpaperData {
|
||||
private struct UploadedWallpaperData {
|
||||
fileprivate let resource: MediaResource
|
||||
fileprivate let content: UploadedWallpaperDataContent
|
||||
}
|
||||
|
|
@ -134,9 +134,9 @@ public func uploadWallpaper(account: Account, resource: MediaResource, mimeType:
|
|||
return .single((.progress(progress), result.resource))
|
||||
case let .inputFile(file):
|
||||
return account.network.request(Api.functions.account.uploadWallPaper(file: file, mimeType: mimeType, settings: apiWallpaperSettings(settings)))
|
||||
|> mapError {_ in return UploadWallpaperError.generic}
|
||||
|> mapToSignal { wallpaper -> Signal<(UploadWallpaperStatus, MediaResource?), UploadWallpaperError> in
|
||||
return .single((.complete(TelegramWallpaper(apiWallpaper: wallpaper)), result.resource))
|
||||
|> mapError { _ in return UploadWallpaperError.generic }
|
||||
|> map { wallpaper -> (UploadWallpaperStatus, MediaResource?) in
|
||||
return (.complete(TelegramWallpaper(apiWallpaper: wallpaper)), result.resource)
|
||||
}
|
||||
default:
|
||||
return .fail(.generic)
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@
|
|||
0962E66F21B6147600245FD9 /* AppConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0962E66E21B6147600245FD9 /* AppConfiguration.swift */; };
|
||||
0962E67521B6437600245FD9 /* SplitTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0962E67421B6437600245FD9 /* SplitTest.swift */; };
|
||||
0962E68121BAA20E00245FD9 /* SearchBotsConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0962E68021BAA20E00245FD9 /* SearchBotsConfiguration.swift */; };
|
||||
09B4A9B4230FB70B005C2E08 /* Themes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09B4A9B3230FB70B005C2E08 /* Themes.swift */; };
|
||||
09B4A9B6230FBB2B005C2E08 /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09B4A9B5230FBB2B005C2E08 /* Theme.swift */; };
|
||||
09EC0DE922C6825D00E7185B /* AppUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09EC0DE822C6825D00E7185B /* AppUpdate.swift */; };
|
||||
09EDAD382213120C0012A50B /* AutodownloadSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09EDAD372213120C0012A50B /* AutodownloadSettings.swift */; };
|
||||
09EDAD3A22131D010012A50B /* ManagedAutodownloadSettingsUpdates.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09EDAD3922131D010012A50B /* ManagedAutodownloadSettingsUpdates.swift */; };
|
||||
|
|
@ -808,6 +810,8 @@
|
|||
0962E66E21B6147600245FD9 /* AppConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppConfiguration.swift; sourceTree = "<group>"; };
|
||||
0962E67421B6437600245FD9 /* SplitTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplitTest.swift; sourceTree = "<group>"; };
|
||||
0962E68021BAA20E00245FD9 /* SearchBotsConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchBotsConfiguration.swift; sourceTree = "<group>"; };
|
||||
09B4A9B3230FB70B005C2E08 /* Themes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Themes.swift; sourceTree = "<group>"; };
|
||||
09B4A9B5230FBB2B005C2E08 /* Theme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Theme.swift; sourceTree = "<group>"; };
|
||||
09EC0DE822C6825D00E7185B /* AppUpdate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppUpdate.swift; sourceTree = "<group>"; };
|
||||
09EDAD372213120C0012A50B /* AutodownloadSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutodownloadSettings.swift; sourceTree = "<group>"; };
|
||||
09EDAD3922131D010012A50B /* ManagedAutodownloadSettingsUpdates.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManagedAutodownloadSettingsUpdates.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -1270,6 +1274,7 @@
|
|||
0962E66E21B6147600245FD9 /* AppConfiguration.swift */,
|
||||
0962E68021BAA20E00245FD9 /* SearchBotsConfiguration.swift */,
|
||||
09EDAD372213120C0012A50B /* AutodownloadSettings.swift */,
|
||||
09B4A9B5230FBB2B005C2E08 /* Theme.swift */,
|
||||
);
|
||||
name = Settings;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -1667,6 +1672,7 @@
|
|||
D099E221229420D600561B75 /* BlockedPeersContext.swift */,
|
||||
D098907E22942E3B0053F151 /* ActiveSessionsContext.swift */,
|
||||
D069257022D8B526002FC021 /* SecretChatSettings.swift */,
|
||||
09B4A9B3230FB70B005C2E08 /* Themes.swift */,
|
||||
);
|
||||
name = Settings;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -2148,6 +2154,7 @@
|
|||
D03B0CE01D62249100955575 /* StoreMessage_Telegram.swift in Sources */,
|
||||
D03E416C2304D5B30049C28B /* ValidateAddressNameInteractive.swift in Sources */,
|
||||
D08774FE1E3E3A3500A97350 /* GlobalNotificationSettings.swift in Sources */,
|
||||
09B4A9B6230FBB2B005C2E08 /* Theme.swift in Sources */,
|
||||
D023E67821540624008C27D1 /* UpdateMessageMedia.swift in Sources */,
|
||||
D0EE7FC420986C5300981319 /* SecureIdPassportRegistrationValue.swift in Sources */,
|
||||
D03B0CB91D62233400955575 /* Either.swift in Sources */,
|
||||
|
|
@ -2155,6 +2162,7 @@
|
|||
0962E66D21B5C56F00245FD9 /* JSON.swift in Sources */,
|
||||
D0338740223BD48B007A2CE4 /* ContactsSettings.swift in Sources */,
|
||||
D03B0CBD1D62234300955575 /* Regex.swift in Sources */,
|
||||
09B4A9B4230FB70B005C2E08 /* Themes.swift in Sources */,
|
||||
D00BDA191EE593D600C64C5E /* TelegramChannelAdminRights.swift in Sources */,
|
||||
09EDAD3A22131D010012A50B /* ManagedAutodownloadSettingsUpdates.swift in Sources */,
|
||||
D0EA188220D3D2B1001AEE19 /* RemoteStorageConfiguration.swift in Sources */,
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta
|
|||
|
||||
let badgeFillColor: UIColor
|
||||
let badgeTextColor: UIColor
|
||||
let secondaryBadgeTextColor: UIColor
|
||||
let outgoingBubbleFillColor: UIColor
|
||||
let outgoingBubbleHighlightedFillColor: UIColor
|
||||
let outgoingScamColor: UIColor
|
||||
|
|
@ -23,6 +24,7 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta
|
|||
if accentColor.rgb == UIColor.white.rgb {
|
||||
badgeFillColor = .white
|
||||
badgeTextColor = .black
|
||||
secondaryBadgeTextColor = .black
|
||||
outgoingBubbleFillColor = UIColor(rgb: 0x313131)
|
||||
outgoingBubbleHighlightedFillColor = UIColor(rgb: 0x464646)
|
||||
outgoingScamColor = destructiveColor
|
||||
|
|
@ -41,6 +43,7 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta
|
|||
if lightness > 0.7 {
|
||||
outgoingScamColor = .black
|
||||
|
||||
secondaryBadgeTextColor = .black
|
||||
outgoingPrimaryTextColor = .black
|
||||
outgoingSecondaryTextColor = UIColor(rgb: 0x000000, alpha: 0.5)
|
||||
outgoingLinkTextColor = .black
|
||||
|
|
@ -48,6 +51,7 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta
|
|||
} else {
|
||||
outgoingScamColor = .white
|
||||
|
||||
secondaryBadgeTextColor = .white
|
||||
outgoingPrimaryTextColor = .white
|
||||
outgoingSecondaryTextColor = UIColor(rgb: 0xffffff, alpha: 0.5)
|
||||
outgoingLinkTextColor = .white
|
||||
|
|
@ -153,7 +157,7 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta
|
|||
itemCheckColors: PresentationThemeFillStrokeForeground(
|
||||
fillColor: accentColor,
|
||||
strokeColor: UIColor(rgb: 0xffffff, alpha: 0.5),
|
||||
foregroundColor: badgeTextColor
|
||||
foregroundColor: secondaryBadgeTextColor
|
||||
),
|
||||
controlSecondaryColor: UIColor(rgb: 0xffffff, alpha: 0.5),
|
||||
freeInputField: PresentationInputFieldTheme(
|
||||
|
|
@ -188,7 +192,7 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta
|
|||
failedForegroundColor: .white,
|
||||
muteIconColor: UIColor(rgb: 0x8e8e92),
|
||||
unreadBadgeActiveBackgroundColor: accentColor,
|
||||
unreadBadgeActiveTextColor: badgeTextColor,
|
||||
unreadBadgeActiveTextColor: secondaryBadgeTextColor,
|
||||
unreadBadgeInactiveBackgroundColor: UIColor(rgb: 0x666666),
|
||||
unreadBadgeInactiveTextColor:UIColor(rgb: 0x000000),
|
||||
pinnedBadgeColor: UIColor(rgb: 0x767677),
|
||||
|
|
@ -207,7 +211,7 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta
|
|||
|
||||
let message = PresentationThemeChatMessage(
|
||||
incoming: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0x262628), highlightedFill: UIColor(rgb: 0x353539), stroke: UIColor(rgb: 0x262628)), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0x262628), highlightedFill: UIColor(rgb: 0x353539), stroke: UIColor(rgb: 0x262628))), primaryTextColor: .white, secondaryTextColor: UIColor(rgb: 0xffffff, alpha: 0.5), linkTextColor: accentColor, linkHighlightColor: accentColor.withAlphaComponent(0.5), scamColor: destructiveColor, textHighlightColor: UIColor(rgb: 0xffe438), accentTextColor: accentColor, accentControlColor: accentColor, mediaActiveControlColor: accentColor, mediaInactiveControlColor: accentColor.withAlphaComponent(0.4), pendingActivityColor: UIColor(rgb: 0xffffff, alpha: 0.5), fileTitleColor: accentColor, fileDescriptionColor: UIColor(rgb: 0xffffff, alpha: 0.5), fileDurationColor: UIColor(rgb: 0xffffff, alpha: 0.5), mediaPlaceholderColor: UIColor(rgb: 0x1f1f1f).mixedWith(.white, alpha: 0.05), polls: PresentationThemeChatBubblePolls(radioButton: UIColor(rgb: 0x737373), radioProgress: accentColor, highlight: accentColor.withAlphaComponent(0.12), separator: UIColor(rgb: 0x000000), bar: accentColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: UIColor(rgb: 0x000000, alpha: 0.5), withoutWallpaper: UIColor(rgb: 0x000000, alpha: 0.5)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xb2b2b2, alpha: 0.18)), actionButtonsTextColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xffffff))),
|
||||
outgoing: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, highlightedFill: outgoingBubbleHighlightedFillColor, stroke: outgoingBubbleFillColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, highlightedFill: outgoingBubbleHighlightedFillColor, stroke: outgoingBubbleFillColor)), primaryTextColor: outgoingPrimaryTextColor, secondaryTextColor: outgoingSecondaryTextColor, linkTextColor: outgoingLinkTextColor, linkHighlightColor: UIColor.white.withAlphaComponent(0.5), scamColor: outgoingScamColor, textHighlightColor: UIColor(rgb: 0xffe438), accentTextColor: .white, accentControlColor: .white, mediaActiveControlColor: .white, mediaInactiveControlColor: UIColor(rgb: 0xffffff, alpha: 0.3), pendingActivityColor: UIColor(rgb: 0xffffff, alpha: 0.5), fileTitleColor: .white, fileDescriptionColor: UIColor(rgb: 0xffffff, alpha: 0.5), fileDurationColor: UIColor(rgb: 0xffffff, alpha: 0.5), mediaPlaceholderColor: UIColor(rgb: 0x313131).mixedWith(.white, alpha: 0.05), polls: PresentationThemeChatBubblePolls(radioButton: .white, radioProgress: .white, highlight: UIColor(white: 1.0, alpha: 0.12), separator: UIColor(white: 1.0, alpha: 0.3), bar: .white), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: UIColor(rgb: 0x000000, alpha: 0.5), withoutWallpaper: UIColor(rgb: 0x000000, alpha: 0.5)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xb2b2b2, alpha: 0.18)), actionButtonsTextColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xffffff))),
|
||||
outgoing: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, highlightedFill: outgoingBubbleHighlightedFillColor, stroke: outgoingBubbleFillColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, highlightedFill: outgoingBubbleHighlightedFillColor, stroke: outgoingBubbleFillColor)), primaryTextColor: outgoingPrimaryTextColor, secondaryTextColor: outgoingSecondaryTextColor, linkTextColor: outgoingLinkTextColor, linkHighlightColor: UIColor.white.withAlphaComponent(0.5), scamColor: outgoingScamColor, textHighlightColor: UIColor(rgb: 0xffe438), accentTextColor: outgoingPrimaryTextColor, accentControlColor: outgoingPrimaryTextColor, mediaActiveControlColor: outgoingPrimaryTextColor, mediaInactiveControlColor: outgoingSecondaryTextColor, pendingActivityColor: outgoingSecondaryTextColor, fileTitleColor: outgoingPrimaryTextColor, fileDescriptionColor: outgoingSecondaryTextColor, fileDurationColor: outgoingSecondaryTextColor, mediaPlaceholderColor: UIColor(rgb: 0x313131).mixedWith(.white, alpha: 0.05), polls: PresentationThemeChatBubblePolls(radioButton: outgoingPrimaryTextColor, radioProgress: outgoingPrimaryTextColor, highlight: outgoingPrimaryTextColor.withAlphaComponent(0.12), separator: outgoingSecondaryTextColor, bar: outgoingPrimaryTextColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: UIColor(rgb: 0x000000, alpha: 0.5), withoutWallpaper: UIColor(rgb: 0x000000, alpha: 0.5)), actionButtonsStrokeColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xb2b2b2, alpha: 0.18)), actionButtonsTextColor: PresentationThemeVariableColor(color: UIColor(rgb: 0xffffff))),
|
||||
freeform: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0x1f1f1f), highlightedFill: UIColor(rgb: 0x2a2a2a), stroke: UIColor(rgb: 0x1f1f1f)), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0x1f1f1f), highlightedFill: UIColor(rgb: 0x2a2a2a), stroke: UIColor(rgb: 0x1f1f1f))),
|
||||
infoPrimaryTextColor: .white,
|
||||
infoLinkTextColor: accentColor,
|
||||
|
|
@ -320,7 +324,7 @@ private func makeDarkPresentationTheme(accentColor: UIColor, baseColor: Presenta
|
|||
inputPlaceholderColor: UIColor(rgb: 0x8f8f8f),
|
||||
inputTextColor: .white,
|
||||
inputClearButtonColor: UIColor(rgb: 0x8f8f8f),
|
||||
checkContentColor: .white
|
||||
checkContentColor: secondaryBadgeTextColor
|
||||
)
|
||||
|
||||
let contextMenu = PresentationThemeContextMenu(
|
||||
|
|
|
|||
|
|
@ -16,16 +16,22 @@ private func makeDefaultDayPresentationTheme(accentColor: UIColor, serviceBackgr
|
|||
let outgoingCheckColor: UIColor
|
||||
let outgoingControlColor: UIColor
|
||||
let outgoingBubbleFillColor: UIColor
|
||||
if accentColor.lightness > 0.7 {
|
||||
let outgoingBubbleStrokeColor: UIColor
|
||||
if accentColor.lightness > 0.705 {
|
||||
outgoingPrimaryTextColor = .black
|
||||
outgoingSecondaryTextColor = UIColor(rgb: 0x000000, alpha: 0.65)
|
||||
outgoingSecondaryTextColor = UIColor(rgb: 0x000000, alpha: 0.55)
|
||||
outgoingLinkTextColor = .black
|
||||
outgoingCheckColor = .black
|
||||
outgoingControlColor = outgoingPrimaryTextColor
|
||||
outgoingBubbleFillColor = accentColor
|
||||
if outgoingBubbleFillColor.rgb == 0xffffff {
|
||||
outgoingBubbleStrokeColor = UIColor(rgb: 0xc8c7cc)
|
||||
} else {
|
||||
outgoingBubbleStrokeColor = outgoingBubbleFillColor
|
||||
}
|
||||
|
||||
let hsv = accentColor.hsv
|
||||
accentColor = UIColor(hue: hsv.0, saturation: hsv.1 * 1.1, brightness: min(hsv.2, 0.7), alpha: 1.0)
|
||||
accentColor = UIColor(hue: hsv.0, saturation: min(1.0, hsv.1 * 1.1), brightness: min(hsv.2, 0.6), alpha: 1.0)
|
||||
} else {
|
||||
outgoingPrimaryTextColor = .white
|
||||
outgoingSecondaryTextColor = UIColor(rgb: 0xffffff, alpha: 0.65)
|
||||
|
|
@ -33,6 +39,7 @@ private func makeDefaultDayPresentationTheme(accentColor: UIColor, serviceBackgr
|
|||
outgoingCheckColor = .white
|
||||
outgoingControlColor = outgoingPrimaryTextColor
|
||||
outgoingBubbleFillColor = accentColor
|
||||
outgoingBubbleStrokeColor = outgoingBubbleFillColor
|
||||
}
|
||||
|
||||
let rootTabBar = PresentationThemeRootTabBar(
|
||||
|
|
@ -201,8 +208,8 @@ private func makeDefaultDayPresentationTheme(accentColor: UIColor, serviceBackgr
|
|||
)
|
||||
|
||||
let messageDay = PresentationThemeChatMessage(
|
||||
incoming: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xffffff), highlightedFill: UIColor(rgb: 0xdadade), stroke: UIColor(rgb: 0xffffff)), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xf1f1f4), highlightedFill: UIColor(rgb: 0xdadade), stroke: UIColor(rgb: 0xf1f1f4))), primaryTextColor: .black, secondaryTextColor: UIColor(rgb: 0x525252, alpha: 0.6), linkTextColor: UIColor(rgb: 0x004bad), linkHighlightColor: accentColor.withAlphaComponent(0.3), scamColor: destructiveColor, textHighlightColor: UIColor(rgb: 0xffe438), accentTextColor: accentColor, accentControlColor: accentColor, mediaActiveControlColor: accentColor, mediaInactiveControlColor: UIColor(rgb: 0xcacaca), pendingActivityColor: UIColor(rgb: 0x525252, alpha: 0.6), fileTitleColor: UIColor(rgb: 0x0b8bed), fileDescriptionColor: UIColor(rgb: 0x999999), fileDurationColor: UIColor(rgb: 0x525252, alpha: 0.6), mediaPlaceholderColor: UIColor(rgb: 0xffffff).withMultipliedBrightnessBy(0.95), polls: PresentationThemeChatBubblePolls(radioButton: UIColor(rgb: 0xc8c7cc), radioProgress: accentColor, highlight: accentColor.withAlphaComponent(0.12), separator: UIColor(rgb: 0xc8c7cc), bar: accentColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: serviceBackgroundColor, withoutWallpaper: UIColor(rgb: 0xffffff, alpha: 0.8)), actionButtonsStrokeColor: PresentationThemeVariableColor(withWallpaper: .clear, withoutWallpaper: accentColor), actionButtonsTextColor: PresentationThemeVariableColor(withWallpaper: .white, withoutWallpaper: accentColor)),
|
||||
outgoing: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, highlightedFill: outgoingBubbleFillColor.withMultipliedBrightnessBy(0.7), stroke: outgoingBubbleFillColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, highlightedFill: outgoingBubbleFillColor.withMultipliedBrightnessBy(0.7), stroke: outgoingBubbleFillColor)), primaryTextColor: outgoingPrimaryTextColor, secondaryTextColor: outgoingSecondaryTextColor, linkTextColor: outgoingLinkTextColor, linkHighlightColor: UIColor(rgb: 0xffffff, alpha: 0.3), scamColor: outgoingPrimaryTextColor, textHighlightColor: UIColor(rgb: 0xffe438), accentTextColor: outgoingPrimaryTextColor, accentControlColor: outgoingPrimaryTextColor, mediaActiveControlColor: outgoingPrimaryTextColor, mediaInactiveControlColor: outgoingSecondaryTextColor, pendingActivityColor: outgoingSecondaryTextColor, fileTitleColor: outgoingPrimaryTextColor, fileDescriptionColor: outgoingSecondaryTextColor, fileDurationColor: outgoingSecondaryTextColor, mediaPlaceholderColor: accentColor.withMultipliedBrightnessBy(0.95), polls: PresentationThemeChatBubblePolls(radioButton: outgoingSecondaryTextColor, radioProgress: outgoingPrimaryTextColor, highlight: outgoingPrimaryTextColor.withAlphaComponent(0.12), separator: outgoingSecondaryTextColor, bar: outgoingPrimaryTextColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: serviceBackgroundColor, withoutWallpaper: UIColor(rgb: 0xffffff, alpha: 0.8)), actionButtonsStrokeColor: PresentationThemeVariableColor(withWallpaper: .clear, withoutWallpaper: accentColor), actionButtonsTextColor: PresentationThemeVariableColor(withWallpaper: .white, withoutWallpaper: accentColor)),
|
||||
incoming: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xffffff), highlightedFill: UIColor(rgb: 0xdadade), stroke: UIColor(rgb: 0xffffff)), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xf1f1f4), highlightedFill: UIColor(rgb: 0xdadade), stroke: UIColor(rgb: 0xf1f1f4))), primaryTextColor: .black, secondaryTextColor: UIColor(rgb: 0x525252, alpha: 0.6), linkTextColor: UIColor(rgb: 0x004bad), linkHighlightColor: accentColor.withAlphaComponent(0.3), scamColor: destructiveColor, textHighlightColor: UIColor(rgb: 0xffe438), accentTextColor: accentColor, accentControlColor: accentColor, mediaActiveControlColor: accentColor, mediaInactiveControlColor: UIColor(rgb: 0xcacaca), pendingActivityColor: UIColor(rgb: 0x525252, alpha: 0.6), fileTitleColor: accentColor, fileDescriptionColor: UIColor(rgb: 0x999999), fileDurationColor: UIColor(rgb: 0x525252, alpha: 0.6), mediaPlaceholderColor: UIColor(rgb: 0xffffff).withMultipliedBrightnessBy(0.95), polls: PresentationThemeChatBubblePolls(radioButton: UIColor(rgb: 0xc8c7cc), radioProgress: accentColor, highlight: accentColor.withAlphaComponent(0.12), separator: UIColor(rgb: 0xc8c7cc), bar: accentColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: serviceBackgroundColor, withoutWallpaper: UIColor(rgb: 0xffffff, alpha: 0.8)), actionButtonsStrokeColor: PresentationThemeVariableColor(withWallpaper: .clear, withoutWallpaper: accentColor), actionButtonsTextColor: PresentationThemeVariableColor(withWallpaper: .white, withoutWallpaper: accentColor)),
|
||||
outgoing: PresentationThemePartedColors(bubble: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, highlightedFill: outgoingBubbleFillColor.withMultipliedBrightnessBy(0.7), stroke: outgoingBubbleFillColor), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: outgoingBubbleFillColor, highlightedFill: outgoingBubbleFillColor.withMultipliedBrightnessBy(0.7), stroke: outgoingBubbleStrokeColor)), primaryTextColor: outgoingPrimaryTextColor, secondaryTextColor: outgoingSecondaryTextColor, linkTextColor: outgoingLinkTextColor, linkHighlightColor: UIColor(rgb: 0xffffff, alpha: 0.3), scamColor: outgoingPrimaryTextColor, textHighlightColor: UIColor(rgb: 0xffe438), accentTextColor: outgoingPrimaryTextColor, accentControlColor: outgoingPrimaryTextColor, mediaActiveControlColor: outgoingPrimaryTextColor, mediaInactiveControlColor: outgoingSecondaryTextColor, pendingActivityColor: outgoingSecondaryTextColor, fileTitleColor: outgoingPrimaryTextColor, fileDescriptionColor: outgoingSecondaryTextColor, fileDurationColor: outgoingSecondaryTextColor, mediaPlaceholderColor: accentColor.withMultipliedBrightnessBy(0.95), polls: PresentationThemeChatBubblePolls(radioButton: outgoingSecondaryTextColor, radioProgress: outgoingPrimaryTextColor, highlight: outgoingPrimaryTextColor.withAlphaComponent(0.12), separator: outgoingSecondaryTextColor, bar: outgoingPrimaryTextColor), actionButtonsFillColor: PresentationThemeVariableColor(withWallpaper: serviceBackgroundColor, withoutWallpaper: UIColor(rgb: 0xffffff, alpha: 0.8)), actionButtonsStrokeColor: PresentationThemeVariableColor(withWallpaper: .clear, withoutWallpaper: accentColor), actionButtonsTextColor: PresentationThemeVariableColor(withWallpaper: .white, withoutWallpaper: accentColor)),
|
||||
freeform: PresentationThemeBubbleColor(withWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xE5E5EA), highlightedFill: UIColor(rgb: 0xDADADE), stroke: UIColor(rgb: 0xE5E5EA)), withoutWallpaper: PresentationThemeBubbleColorComponents(fill: UIColor(rgb: 0xE5E5EA), highlightedFill: UIColor(rgb: 0xdadade), stroke: UIColor(rgb: 0xE5E5EA))),
|
||||
infoPrimaryTextColor: UIColor(rgb: 0x000000),
|
||||
infoLinkTextColor: UIColor(rgb: 0x004bad),
|
||||
|
|
|
|||
|
|
@ -16,6 +16,10 @@ public func makePresentationTheme(themeReference: PresentationThemeReference, ac
|
|||
case .day:
|
||||
theme = makeDefaultDayPresentationTheme(accentColor: accentColor, serviceBackgroundColor: serviceBackgroundColor, baseColor: baseColor, day: true, preview: preview)
|
||||
}
|
||||
case let .local(resource):
|
||||
theme = makeDefaultDayPresentationTheme(serviceBackgroundColor: serviceBackgroundColor, baseColor: baseColor, day: false, preview: preview)
|
||||
case let .cloud(info):
|
||||
theme = makeDefaultDayPresentationTheme(serviceBackgroundColor: serviceBackgroundColor, baseColor: baseColor, day: false, preview: preview)
|
||||
}
|
||||
return theme
|
||||
}
|
||||
|
|
|
|||
|
|
@ -338,6 +338,8 @@ private func automaticThemeShouldSwitchNow(_ parameters: AutomaticThemeSwitchPar
|
|||
default:
|
||||
break
|
||||
}
|
||||
default:
|
||||
return false
|
||||
}
|
||||
switch parameters.trigger {
|
||||
case .none:
|
||||
|
|
|
|||
|
|
@ -2,19 +2,6 @@ import Foundation
|
|||
import UIKit
|
||||
import Display
|
||||
|
||||
private func generateArrowImage(_ theme: PresentationTheme) -> UIImage? {
|
||||
return generateImage(CGSize(width: 7.0, height: 13.0), rotatedContext: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
context.setStrokeColor(theme.list.disclosureArrowColor.cgColor)
|
||||
context.setLineWidth(1.98)
|
||||
context.setLineCap(.round)
|
||||
context.setLineJoin(.round)
|
||||
context.translateBy(x: 1.0, y: 1.0)
|
||||
|
||||
let _ = try? drawSvgPath(context, path: "M0,0 L4.79819816,5.27801798 L4.79819816,5.27801798 C4.91262453,5.40388698 4.91262453,5.59611302 4.79819816,5.72198202 L0,11 S ")
|
||||
})
|
||||
}
|
||||
|
||||
public func generateItemListCheckIcon(color: UIColor) -> UIImage? {
|
||||
return generateImage(CGSize(width: 12.0, height: 10.0), rotatedContext: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
|
|
@ -29,19 +16,14 @@ public func generateItemListCheckIcon(color: UIColor) -> UIImage? {
|
|||
}
|
||||
|
||||
public func generateItemListPlusIcon(_ color: UIColor) -> UIImage? {
|
||||
return generateImage(CGSize(width: 18.0, height: 18.0), rotatedContext: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
context.setFillColor(color.cgColor)
|
||||
context.setLineCap(.round)
|
||||
let lineWidth: CGFloat = 2.0
|
||||
context.fill(CGRect(x: floorToScreenPixels((18.0 - lineWidth) / 2.0), y: 0.0, width: lineWidth, height: 18.0))
|
||||
context.fill(CGRect(x: 0.0, y: floorToScreenPixels((18.0 - lineWidth) / 2.0), width: 18.0, height: lineWidth))
|
||||
})
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat List/AddIcon"), color: color)
|
||||
}
|
||||
|
||||
public struct PresentationResourcesItemList {
|
||||
public static func disclosureArrowImage(_ theme: PresentationTheme) -> UIImage? {
|
||||
return theme.image(PresentationResourceKey.itemListDisclosureArrow.rawValue, generateArrowImage)
|
||||
return theme.image(PresentationResourceKey.itemListDisclosureArrow.rawValue, { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Item List/DisclosureArrow"), color: theme.list.disclosureArrowColor)
|
||||
})
|
||||
}
|
||||
|
||||
public static func checkIconImage(_ theme: PresentationTheme) -> UIImage? {
|
||||
|
|
@ -113,9 +95,9 @@ public struct PresentationResourcesItemList {
|
|||
})
|
||||
}
|
||||
|
||||
public static func addExceptionIcon(_ theme: PresentationTheme) -> UIImage? {
|
||||
public static func addChannelIcon(_ theme: PresentationTheme) -> UIImage? {
|
||||
return theme.image(PresentationResourceKey.itemListAddExceptionIcon.rawValue, { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Item List/AddExceptionIcon"), color: theme.list.itemAccentColor)
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Item List/AddChannelIcon"), color: theme.list.itemAccentColor)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ public struct PresentationResourcesRootController {
|
|||
|
||||
public static func navigationCallIcon(_ theme: PresentationTheme) -> UIImage? {
|
||||
return theme.image(PresentationResourceKey.navigationCallIcon.rawValue, { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Info/CallButton"), color: theme.rootController.navigationBar.accentTextColor)
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Call List/CallIcon"), color: theme.rootController.navigationBar.accentTextColor)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -66,14 +66,15 @@ public struct PresentationResourcesRootController {
|
|||
|
||||
public static func navigationCompactSearchIcon(_ theme: PresentationTheme) -> UIImage? {
|
||||
return theme.image(PresentationResourceKey.navigationCompactSearchIcon.rawValue, { theme in
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat List/SearchIcon"), color: theme.rootController.navigationBar.accentTextColor).flatMap({ image in
|
||||
let factor: CGFloat = 0.8
|
||||
let size = CGSize(width: floor(image.size.width * factor), height: floor(image.size.height * factor))
|
||||
return generateImage(size, contextGenerator: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
context.draw(image.cgImage!, in: CGRect(origin: CGPoint(), size: size))
|
||||
})
|
||||
})
|
||||
return generateTintedImage(image: UIImage(bundleImageName: "Chat List/SearchIcon"), color: theme.rootController.navigationBar.accentTextColor)
|
||||
// return generateTintedImage(image: UIImage(bundleImageName: "Chat List/SearchIcon"), color: theme.rootController.navigationBar.accentTextColor).flatMap({ image in
|
||||
// let factor: CGFloat = 0.8
|
||||
// let size = CGSize(width: floor(image.size.width * factor), height: floor(image.size.height * factor))
|
||||
// return generateImage(size, contextGenerator: { size, context in
|
||||
// context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
// context.draw(image.cgImage!, in: CGRect(origin: CGPoint(), size: size))
|
||||
// })
|
||||
// })
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
12
submodules/TelegramUI/Images.xcassets/Call List/CallIcon.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Call List/CallIcon.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "ic_addcall.pdf"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
BIN
submodules/TelegramUI/Images.xcassets/Call List/CallIcon.imageset/ic_addcall.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Call List/CallIcon.imageset/ic_addcall.pdf
vendored
Normal file
Binary file not shown.
|
|
@ -2,16 +2,7 @@
|
|||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "DialogList_Muted@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
"filename" : "ic_mutedchat.pdf"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 208 B |
BIN
submodules/TelegramUI/Images.xcassets/Chat List/PeerMutedIcon.imageset/ic_mutedchat.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Chat List/PeerMutedIcon.imageset/ic_mutedchat.pdf
vendored
Normal file
Binary file not shown.
|
|
@ -2,17 +2,7 @@
|
|||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "ConversationChannelInlineShareIcon@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "ConversationChannelInlineShareIcon@3x.png",
|
||||
"scale" : "3x"
|
||||
"filename" : "ic_chat_share.pdf"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 423 B |
Binary file not shown.
|
Before Width: | Height: | Size: 622 B |
BIN
submodules/TelegramUI/Images.xcassets/Chat/Message/ShareIcon.imageset/ic_chat_share.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Chat/Message/ShareIcon.imageset/ic_chat_share.pdf
vendored
Normal file
Binary file not shown.
12
submodules/TelegramUI/Images.xcassets/Item List/AddChannelIcon.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Item List/AddChannelIcon.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "ic_addchannel.pdf"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
BIN
submodules/TelegramUI/Images.xcassets/Item List/AddChannelIcon.imageset/ic_addchannel.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Item List/AddChannelIcon.imageset/ic_addchannel.pdf
vendored
Normal file
Binary file not shown.
|
|
@ -1,22 +0,0 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "plus@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "plus@3x.png",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.1 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 927 B |
12
submodules/TelegramUI/Images.xcassets/Item List/DisclosureArrow.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Item List/DisclosureArrow.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "ic_open.pdf"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
BIN
submodules/TelegramUI/Images.xcassets/Item List/DisclosureArrow.imageset/ic_open.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Item List/DisclosureArrow.imageset/ic_open.pdf
vendored
Normal file
Binary file not shown.
|
|
@ -4267,6 +4267,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||
self.validLayout = layout
|
||||
self.chatTitleView?.layout = layout
|
||||
|
||||
if self.hasScheduledMessages, let h = layout.inputHeight, h > 100.0 {
|
||||
print()
|
||||
}
|
||||
|
||||
self.chatDisplayNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationHeight, transition: transition, listViewTransaction: { updateSizeAndInsets, additionalScrollDistance, scrollToTop in
|
||||
self.chatDisplayNode.historyNode.updateLayout(transition: transition, updateSizeAndInsets: updateSizeAndInsets, additionalScrollDistance: additionalScrollDistance, scrollToTop: scrollToTop)
|
||||
})
|
||||
|
|
@ -4554,11 +4558,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||
self.chatDisplayNode.updateChatPresentationInterfaceState(updatedChatPresentationInterfaceState, transition: transition, interactive: interactive)
|
||||
}
|
||||
|
||||
if let selectionState = self.presentationInterfaceState.interfaceState.selectionState, !selectionState.selectedIds.isEmpty {
|
||||
self.chatTitleView?.titleContent = .custom(self.presentationData.strings.Conversation_SelectedMessages(Int32(selectionState.selectedIds.count)))
|
||||
} else {
|
||||
|
||||
}
|
||||
// if let selectionState = self.presentationInterfaceState.interfaceState.selectionState, !selectionState.selectedIds.isEmpty {
|
||||
// self.chatTitleView?.titleContent = .custom(self.presentationData.strings.Conversation_SelectedMessages(Int32(selectionState.selectedIds.count)))
|
||||
// } else {
|
||||
//
|
||||
// }
|
||||
|
||||
if let button = leftNavigationButtonForChatInterfaceState(updatedChatPresentationInterfaceState, strings: updatedChatPresentationInterfaceState.strings, currentButton: self.leftNavigationButton, target: self, selector: #selector(self.leftNavigationButtonAction)) {
|
||||
if self.leftNavigationButton != button {
|
||||
|
|
@ -4892,14 +4896,14 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||
return result
|
||||
}
|
||||
let controller = legacyAttachmentMenu(context: strongSelf.context, peer: peer, editMediaOptions: menuEditMediaOptions, saveEditedPhotos: settings.storeEditedPhotos, allowGrouping: true, theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, parentController: legacyController, recentlyUsedInlineBots: strongSelf.recentlyUsedInlineBotsValue, initialCaption: inputText.string, openGallery: {
|
||||
self?.presentMediaPicker(fileMode: false, editingMedia: editMediaOptions != nil, completion: { signals, silentPosting in
|
||||
self?.presentMediaPicker(fileMode: false, editingMedia: editMediaOptions != nil, completion: { signals, silentPosting, scheduleTime in
|
||||
if !inputText.string.isEmpty {
|
||||
//strongSelf.clearInputText()
|
||||
}
|
||||
if editMediaOptions != nil {
|
||||
self?.editMessageMediaWithLegacySignals(signals)
|
||||
} else {
|
||||
self?.enqueueMediaMessages(signals: signals, silentPosting: silentPosting)
|
||||
self?.enqueueMediaMessages(signals: signals, silentPosting: silentPosting, scheduleTime: scheduleTime > 0 ? scheduleTime : nil)
|
||||
}
|
||||
})
|
||||
}, openCamera: { [weak self] cameraView, menuController in
|
||||
|
|
@ -4919,6 +4923,26 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||
if let strongSelf = self, let (host, port, username, password, secret) = parseProxyUrl(code) {
|
||||
strongSelf.openResolved(ResolvedUrl.proxy(host: host, port: port, username: username, password: password, secret: secret))
|
||||
}
|
||||
}, presentScheduleController: { [weak self] done in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
let mode: ChatScheduleTimeControllerMode
|
||||
if case let .peer(peerId) = strongSelf.presentationInterfaceState.chatLocation, peerId == strongSelf.context.account.peerId {
|
||||
mode = .reminders
|
||||
} else {
|
||||
mode = .scheduledMessages
|
||||
}
|
||||
let controller = ChatScheduleTimeController(context: strongSelf.context, mode: mode, completion: { [weak self] time in
|
||||
if let strongSelf = self {
|
||||
done(time)
|
||||
if !strongSelf.presentationInterfaceState.isScheduledMessages {
|
||||
let controller = ChatControllerImpl(context: strongSelf.context, chatLocation: strongSelf.chatLocation, subject: .scheduledMessages)
|
||||
(strongSelf.navigationController as? NavigationController)?.pushViewController(controller)
|
||||
}
|
||||
}
|
||||
})
|
||||
strongSelf.present(controller, in: .window(.root))
|
||||
})
|
||||
}
|
||||
}, openFileGallery: {
|
||||
|
|
@ -4941,16 +4965,34 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||
return
|
||||
}
|
||||
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: nil, text: strongSelf.presentationData.strings.Chat_AttachmentMultipleFilesDisabled, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||
}, sendMessagesWithSignals: { [weak self] signals, mode in
|
||||
}, presentScheduleController: { [weak self] done in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
let mode: ChatScheduleTimeControllerMode
|
||||
if case let .peer(peerId) = strongSelf.presentationInterfaceState.chatLocation, peerId == strongSelf.context.account.peerId {
|
||||
mode = .reminders
|
||||
} else {
|
||||
mode = .scheduledMessages
|
||||
}
|
||||
let controller = ChatScheduleTimeController(context: strongSelf.context, mode: mode, completion: { [weak self] time in
|
||||
if let strongSelf = self {
|
||||
done(time)
|
||||
if !strongSelf.presentationInterfaceState.isScheduledMessages {
|
||||
let controller = ChatControllerImpl(context: strongSelf.context, chatLocation: strongSelf.chatLocation, subject: .scheduledMessages)
|
||||
(strongSelf.navigationController as? NavigationController)?.pushViewController(controller)
|
||||
}
|
||||
}
|
||||
})
|
||||
strongSelf.present(controller, in: .window(.root))
|
||||
}, sendMessagesWithSignals: { [weak self] signals, silentPosting, scheduleTime in
|
||||
if !inputText.string.isEmpty {
|
||||
//strongSelf.clearInputText()
|
||||
}
|
||||
if editMediaOptions != nil {
|
||||
self?.editMessageMediaWithLegacySignals(signals!)
|
||||
} else {
|
||||
var silentPosting = false
|
||||
//if mode ==
|
||||
self?.enqueueMediaMessages(signals: signals, silentPosting: silentPosting)
|
||||
self?.enqueueMediaMessages(signals: signals, silentPosting: silentPosting, scheduleTime: scheduleTime > 0 ? scheduleTime : nil)
|
||||
}
|
||||
}, selectRecentlyUsedInlineBot: { [weak self] peer in
|
||||
if let strongSelf = self, let addressName = peer.addressName {
|
||||
|
|
@ -4986,11 +5028,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||
ActionSheetButtonItem(title: self.presentationData.strings.Conversation_FilePhotoOrVideo, action: { [weak self, weak actionSheet] in
|
||||
actionSheet?.dismissAnimated()
|
||||
if let strongSelf = self {
|
||||
strongSelf.presentMediaPicker(fileMode: true, editingMedia: editingMessage, completion: { signals, silentPosting in
|
||||
strongSelf.presentMediaPicker(fileMode: true, editingMedia: editingMessage, completion: { signals, silentPosting, scheduleTime in
|
||||
if editingMessage {
|
||||
self?.editMessageMediaWithLegacySignals(signals)
|
||||
} else {
|
||||
self?.enqueueMediaMessages(signals: signals, silentPosting: silentPosting)
|
||||
self?.enqueueMediaMessages(signals: signals, silentPosting: silentPosting, scheduleTime: scheduleTime > 0 ? scheduleTime : nil)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -5054,7 +5096,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||
self.present(actionSheet, in: .window(.root))
|
||||
}
|
||||
|
||||
private func presentMediaPicker(fileMode: Bool, editingMedia: Bool, completion: @escaping ([Any], Bool) -> Void) {
|
||||
private func presentMediaPicker(fileMode: Bool, editingMedia: Bool, completion: @escaping ([Any], Bool, Int32) -> Void) {
|
||||
let postbox = self.context.account.postbox
|
||||
let _ = (self.context.sharedContext.accountManager.transaction { transaction -> Signal<(GeneratedMediaStoreSettings, SearchBotsConfiguration), NoError> in
|
||||
let entry = transaction.getSharedData(ApplicationSpecificSharedDataKeys.generatedMediaStoreSettings) as? GeneratedMediaStoreSettings
|
||||
|
|
@ -5108,13 +5150,32 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||
return
|
||||
}
|
||||
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: nil, text: strongSelf.presentationData.strings.Chat_AttachmentLimitReached, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||
}, presentScheduleController: { [weak self] done in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
let mode: ChatScheduleTimeControllerMode
|
||||
if case let .peer(peerId) = strongSelf.presentationInterfaceState.chatLocation, peerId == strongSelf.context.account.peerId {
|
||||
mode = .reminders
|
||||
} else {
|
||||
mode = .scheduledMessages
|
||||
}
|
||||
let controller = ChatScheduleTimeController(context: strongSelf.context, mode: mode, completion: { [weak self] time in
|
||||
if let strongSelf = self {
|
||||
done(time)
|
||||
if !strongSelf.presentationInterfaceState.isScheduledMessages {
|
||||
let controller = ChatControllerImpl(context: strongSelf.context, chatLocation: strongSelf.chatLocation, subject: .scheduledMessages)
|
||||
(strongSelf.navigationController as? NavigationController)?.pushViewController(controller)
|
||||
}
|
||||
}
|
||||
})
|
||||
strongSelf.present(controller, in: .window(.root))
|
||||
})
|
||||
controller.descriptionGenerator = legacyAssetPickerItemGenerator()
|
||||
controller.completionBlock = { [weak legacyController] signals, mode in
|
||||
controller.completionBlock = { [weak legacyController] signals, silentPosting, scheduleTime in
|
||||
if let legacyController = legacyController {
|
||||
legacyController.dismiss()
|
||||
var silentPosting = false
|
||||
completion(signals!, silentPosting)
|
||||
completion(signals!, silentPosting, scheduleTime)
|
||||
}
|
||||
}
|
||||
controller.dismissalBlock = { [weak legacyController] in
|
||||
|
|
@ -5381,7 +5442,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||
} else {
|
||||
mode = .scheduledMessages
|
||||
}
|
||||
let controller = ChatScheduleTimeController(context: self.context, mode: mode, completion: { [weak self] time in
|
||||
let controller = ChatScheduleTimeController(context: self.context, mode: mode, dismissByTapOutside: false, completion: { [weak self] time in
|
||||
if let strongSelf = self {
|
||||
strongSelf.sendMessages(strongSelf.transformEnqueueMessages(messages, silentPosting: false, scheduleTime: time), commit: true)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ func chatHistoryEntriesForView(location: ChatLocation, view: MessageHistoryView,
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var groupBucket: [(Message, Bool, ChatHistoryMessageSelection, ChatMessageEntryAttributes)] = []
|
||||
loop: for entry in view.entries {
|
||||
for media in entry.message.media {
|
||||
|
|
|
|||
|
|
@ -400,7 +400,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
|||
let reactionRecognizer = ReactionSwipeGestureRecognizer(target: nil, action: nil)
|
||||
self.reactionRecognizer = reactionRecognizer
|
||||
reactionRecognizer.availableReactions = { [weak self] in
|
||||
guard let strongSelf = self, let item = strongSelf.item, !Namespaces.Message.allScheduled.contains(item.message.id.namespace) else {
|
||||
guard let strongSelf = self, let item = strongSelf.item, !item.presentationData.isPreview && !Namespaces.Message.allScheduled.contains(item.message.id.namespace) else {
|
||||
return []
|
||||
}
|
||||
if strongSelf.selectionNode != nil {
|
||||
|
|
|
|||
|
|
@ -817,6 +817,8 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
|||
})
|
||||
case .wallpaper:
|
||||
break
|
||||
case .theme:
|
||||
break
|
||||
}
|
||||
}
|
||||
}))
|
||||
|
|
|
|||
|
|
@ -22,14 +22,16 @@ final class ChatScheduleTimeController: ViewController {
|
|||
private let context: AccountContext
|
||||
private let mode: ChatScheduleTimeControllerMode
|
||||
private let currentTime: Int32?
|
||||
private let dismissByTapOutside: Bool
|
||||
private let completion: (Int32) -> Void
|
||||
|
||||
private var presentationDataDisposable: Disposable?
|
||||
|
||||
init(context: AccountContext, mode: ChatScheduleTimeControllerMode, currentTime: Int32? = nil, completion: @escaping (Int32) -> Void) {
|
||||
init(context: AccountContext, mode: ChatScheduleTimeControllerMode, currentTime: Int32? = nil, dismissByTapOutside: Bool = true, completion: @escaping (Int32) -> Void) {
|
||||
self.context = context
|
||||
self.mode = mode
|
||||
self.currentTime = currentTime
|
||||
self.dismissByTapOutside = dismissByTapOutside
|
||||
self.completion = completion
|
||||
|
||||
super.init(navigationBarPresentationData: nil)
|
||||
|
|
@ -53,7 +55,7 @@ final class ChatScheduleTimeController: ViewController {
|
|||
}
|
||||
|
||||
override public func loadDisplayNode() {
|
||||
self.displayNode = ChatScheduleTimeControllerNode(context: self.context, mode: self.mode, currentTime: self.currentTime)
|
||||
self.displayNode = ChatScheduleTimeControllerNode(context: self.context, mode: self.mode, currentTime: self.currentTime, dismissByTapOutside: self.dismissByTapOutside)
|
||||
self.controllerNode.completion = { [weak self] time in
|
||||
self?.completion(time + 5)
|
||||
self?.dismiss()
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ class ChatScheduleTimeControllerNode: ViewControllerTracingNode, UIScrollViewDel
|
|||
private let context: AccountContext
|
||||
private let mode: ChatScheduleTimeControllerMode
|
||||
private var presentationData: PresentationData
|
||||
private let dismissByTapOutside: Bool
|
||||
|
||||
private let dimNode: ASDisplayNode
|
||||
private let wrappingScrollNode: ASScrollNode
|
||||
|
|
@ -32,10 +33,11 @@ class ChatScheduleTimeControllerNode: ViewControllerTracingNode, UIScrollViewDel
|
|||
var dismiss: (() -> Void)?
|
||||
var cancel: (() -> Void)?
|
||||
|
||||
init(context: AccountContext, mode: ChatScheduleTimeControllerMode, currentTime: Int32?) {
|
||||
init(context: AccountContext, mode: ChatScheduleTimeControllerMode, currentTime: Int32?, dismissByTapOutside: Bool) {
|
||||
self.context = context
|
||||
self.mode = mode
|
||||
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
self.dismissByTapOutside = dismissByTapOutside
|
||||
|
||||
self.wrappingScrollNode = ASScrollNode()
|
||||
self.wrappingScrollNode.view.alwaysBounceVertical = true
|
||||
|
|
@ -208,7 +210,7 @@ class ChatScheduleTimeControllerNode: ViewControllerTracingNode, UIScrollViewDel
|
|||
}
|
||||
|
||||
@objc func dimTapGesture(_ recognizer: UITapGestureRecognizer) {
|
||||
if case .ended = recognizer.state {
|
||||
if self.dismissByTapOutside, case .ended = recognizer.state {
|
||||
self.cancelButtonPressed()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import ShareController
|
|||
import LegacyUI
|
||||
import LegacyMediaPickerUI
|
||||
|
||||
func presentedLegacyCamera(context: AccountContext, peer: Peer, cameraView: TGAttachmentCameraView?, menuController: TGMenuSheetController?, parentController: ViewController, editingMedia: Bool, saveCapturedPhotos: Bool, mediaGrouping: Bool, initialCaption: String, sendMessagesWithSignals: @escaping ([Any]?) -> Void, recognizedQRCode: @escaping (String) -> Void = { _ in }) {
|
||||
func presentedLegacyCamera(context: AccountContext, peer: Peer, cameraView: TGAttachmentCameraView?, menuController: TGMenuSheetController?, parentController: ViewController, editingMedia: Bool, saveCapturedPhotos: Bool, mediaGrouping: Bool, initialCaption: String, sendMessagesWithSignals: @escaping ([Any]?) -> Void, recognizedQRCode: @escaping (String) -> Void = { _ in }, presentScheduleController: @escaping (@escaping (Int32) -> Void) -> Void) {
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
let legacyController = LegacyController(presentation: .custom, theme: presentationData.theme)
|
||||
legacyController.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .portrait, compactSize: .portrait)
|
||||
|
|
|
|||
|
|
@ -138,7 +138,9 @@ private func chatMessageGalleryControllerData(context: AccountContext, message:
|
|||
if let file = galleryMedia as? TelegramMediaFile {
|
||||
if let fileName = file.fileName {
|
||||
let ext = (fileName as NSString).pathExtension.lowercased()
|
||||
if ext == "wav" || ext == "opus" {
|
||||
if ext == "tgios-theme" {
|
||||
return .theme(file)
|
||||
} else if ext == "wav" || ext == "opus" {
|
||||
return .audio(file)
|
||||
} else if ext == "json", let fileSize = file.size, fileSize < 1024 * 1024 {
|
||||
if let path = context.account.postbox.mediaBox.completedResourcePath(file.resource), let _ = LOTComposition(filePath: path) {
|
||||
|
|
@ -365,7 +367,7 @@ func openChatMessageImpl(_ params: OpenChatMessageParams) -> Bool {
|
|||
return nil
|
||||
}))
|
||||
case let .theme(media):
|
||||
let controller = ThemePreviewController(context: params.context, previewTheme: makeDefaultDayPresentationTheme(accentColor: nil, serviceBackgroundColor: .black, baseColor: nil, day: true, preview: false), media: .message(message: MessageReference(params.message), media: media))
|
||||
let controller = ThemePreviewController(context: params.context, previewTheme: makeDefaultDayPresentationTheme(accentColor: nil, serviceBackgroundColor: .black, baseColor: nil, day: true, preview: false), source: .media(.message(message: MessageReference(params.message), media: media)))
|
||||
params.present(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import Postbox
|
|||
import Display
|
||||
import SwiftSignalKit
|
||||
import TelegramUIPreferences
|
||||
import TelegramPresentationData
|
||||
import AccountContext
|
||||
import OverlayStatusController
|
||||
import AlertUI
|
||||
|
|
@ -236,5 +237,22 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur
|
|||
controller?.dismiss()
|
||||
})
|
||||
dismissInput()
|
||||
case let .theme(slug):
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
let signal = getTheme(account: context.account, slug: slug)
|
||||
let controller = OverlayStatusController(theme: presentationData.theme, strings: presentationData.strings, type: .loading(cancelled: nil))
|
||||
present(controller, nil)
|
||||
|
||||
let _ = (signal
|
||||
|> deliverOnMainQueue).start(next: { [weak controller] theme in
|
||||
controller?.dismiss()
|
||||
let previewTheme = makePresentationTheme(themeReference: .cloud(theme), accentColor: nil, serviceBackgroundColor: .black, baseColor: nil)
|
||||
let previewController = ThemePreviewController(context: context, previewTheme: previewTheme, source: .theme(theme))
|
||||
present(previewController, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
}, error: { [weak controller] error in
|
||||
controller?.dismiss()
|
||||
})
|
||||
dismissInput()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -538,6 +538,22 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur
|
|||
convertedUrl = "https://t.me/bg/\(parameter)\(mode)"
|
||||
}
|
||||
}
|
||||
} else if parsedUrl.host == "addtheme" {
|
||||
if let components = URLComponents(string: "/?" + query) {
|
||||
var parameter: String?
|
||||
if let queryItems = components.queryItems {
|
||||
for queryItem in queryItems {
|
||||
if let value = queryItem.value {
|
||||
if queryItem.name == "slug" {
|
||||
parameter = value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if let parameter = parameter {
|
||||
convertedUrl = "https://t.me/addtheme/\(parameter)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if parsedUrl.host == "resolve" {
|
||||
|
|
|
|||
|
|
@ -28,11 +28,17 @@ public struct WallpaperPresentationOptions: OptionSet {
|
|||
|
||||
public enum PresentationThemeReference: PostboxCoding, Equatable {
|
||||
case builtin(PresentationBuiltinThemeReference)
|
||||
case local(LocalFileMediaResource)
|
||||
case cloud(TelegramTheme)
|
||||
|
||||
public init(decoder: PostboxDecoder) {
|
||||
switch decoder.decodeInt32ForKey("v", orElse: 0) {
|
||||
case 0:
|
||||
self = .builtin(PresentationBuiltinThemeReference(rawValue: decoder.decodeInt32ForKey("t", orElse: 0))!)
|
||||
case 1:
|
||||
self = .local(decoder.decodeObjectForKey("resource", decoder: { LocalFileMediaResource(decoder: $0) }) as! LocalFileMediaResource)
|
||||
case 2:
|
||||
self = .cloud(decoder.decodeObjectForKey("theme", decoder: { TelegramTheme(decoder: $0) }) as! TelegramTheme)
|
||||
default:
|
||||
assertionFailure()
|
||||
self = .builtin(.dayClassic)
|
||||
|
|
@ -44,6 +50,12 @@ public enum PresentationThemeReference: PostboxCoding, Equatable {
|
|||
case let .builtin(reference):
|
||||
encoder.encodeInt32(0, forKey: "v")
|
||||
encoder.encodeInt32(reference.rawValue, forKey: "t")
|
||||
case let .local(resource):
|
||||
encoder.encodeInt32(1, forKey: "v")
|
||||
encoder.encodeObject(resource, forKey: "resource")
|
||||
case let .cloud(theme):
|
||||
encoder.encodeInt32(2, forKey: "v")
|
||||
encoder.encodeObject(theme, forKey: "theme")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -55,6 +67,18 @@ public enum PresentationThemeReference: PostboxCoding, Equatable {
|
|||
} else {
|
||||
return false
|
||||
}
|
||||
case let .local(lhsResource):
|
||||
if case let .local(rhsResource) = rhs, lhsResource.isEqual(to: rhsResource) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .cloud(lhsTheme):
|
||||
if case let .cloud(rhsTheme) = rhs, lhsTheme == rhsTheme {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -65,6 +89,12 @@ public enum PresentationThemeReference: PostboxCoding, Equatable {
|
|||
case let .builtin(reference):
|
||||
namespace = 0
|
||||
id = reference.rawValue
|
||||
case let .local(resource):
|
||||
namespace = 1
|
||||
id = 1//1reference.rawValue
|
||||
case let .cloud(theme):
|
||||
namespace = 2
|
||||
id = Int32(clamping: theme.id)
|
||||
}
|
||||
|
||||
return (Int64(namespace) << 32) | Int64(bitPattern: UInt64(UInt32(bitPattern: id)))
|
||||
|
|
@ -302,6 +332,14 @@ public struct PresentationThemeSettings: PreferencesEntry {
|
|||
for (_, chatWallpaper) in self.themeSpecificChatWallpapers {
|
||||
resources.append(contentsOf: wallpaperResources(chatWallpaper))
|
||||
}
|
||||
switch self.theme {
|
||||
case .builtin:
|
||||
break
|
||||
case let .local(resource):
|
||||
resources.append(resource.id)
|
||||
case let .cloud(theme):
|
||||
resources.append(theme.file.resource.id)
|
||||
}
|
||||
return resources
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ public enum ParsedInternalUrl {
|
|||
case cancelAccountReset(phone: String, hash: String)
|
||||
case share(url: String?, text: String?, to: String?)
|
||||
case wallpaper(WallpaperUrlParameter)
|
||||
case theme(String)
|
||||
}
|
||||
|
||||
private enum ParsedUrl {
|
||||
|
|
@ -202,6 +203,8 @@ public func parseInternalUrl(query: String) -> ParsedInternalUrl? {
|
|||
parameter = .slug(component, options, color, intensity)
|
||||
}
|
||||
return .wallpaper(parameter)
|
||||
} else if pathComponents[0] == "addtheme" {
|
||||
return .theme(pathComponents[1])
|
||||
} else if pathComponents.count == 3 && pathComponents[0] == "c" {
|
||||
if let channelId = Int32(pathComponents[1]), let messageId = Int32(pathComponents[2]) {
|
||||
return .privateMessage(MessageId(peerId: PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId), namespace: Namespaces.Message.Cloud, id: messageId))
|
||||
|
|
@ -305,6 +308,8 @@ private func resolveInternalUrl(account: Account, url: ParsedInternalUrl) -> Sig
|
|||
return .single(.share(url: url, text: text, to: to))
|
||||
case let .wallpaper(parameter):
|
||||
return .single(.wallpaper(parameter))
|
||||
case let .theme(slug):
|
||||
return .single(.theme(slug))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue