Merge branch 'nt/keyboard-shortcuts'
This commit is contained in:
commit
b66d4da50c
@ -124,6 +124,38 @@ class ThreadMapping: NSObject {
|
||||
return threads[indexPath.item]
|
||||
}
|
||||
|
||||
@objc(indexPathAfterThread:)
|
||||
func indexPath(after thread: TSThread?) -> IndexPath? {
|
||||
guard !threads.isEmpty else { return nil }
|
||||
|
||||
let firstIndexPath = IndexPath(item: 0, section: kSection)
|
||||
|
||||
guard let thread = thread else { return firstIndexPath }
|
||||
guard let index = threads.firstIndex(where: { $0.uniqueId == thread.uniqueId}) else { return firstIndexPath }
|
||||
|
||||
if index < (threads.count - 1) {
|
||||
return IndexPath(item: index + 1, section: kSection)
|
||||
} else {
|
||||
return firstIndexPath
|
||||
}
|
||||
}
|
||||
|
||||
@objc(indexPathBeforeThread:)
|
||||
func indexPath(before thread: TSThread?) -> IndexPath? {
|
||||
guard !threads.isEmpty else { return nil }
|
||||
|
||||
let lastIndexPath = IndexPath(item: threads.count - 1, section: kSection)
|
||||
|
||||
guard let thread = thread else { return lastIndexPath }
|
||||
guard let index = threads.firstIndex(where: { $0.uniqueId == thread.uniqueId}) else { return lastIndexPath }
|
||||
|
||||
if index > 0 {
|
||||
return IndexPath(item: index - 1, section: kSection)
|
||||
} else {
|
||||
return lastIndexPath
|
||||
}
|
||||
}
|
||||
|
||||
let threadFinder = AnyThreadFinder()
|
||||
|
||||
@objc
|
||||
|
||||
@ -226,41 +226,41 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
- (nullable NSArray<UIKeyCommand *> *)keyCommands
|
||||
{
|
||||
// We're permissive about what modifier key we accept for the "send message" hotkey.
|
||||
// We accept command-return, option-return.
|
||||
//
|
||||
// We don't support control-return because it doesn't work.
|
||||
//
|
||||
// We don't support shift-return because it is often used for "newline" in other
|
||||
// messaging apps.
|
||||
// We don't define discoverability title for these key commands as they're
|
||||
// considered "default" functionality and shouldn't clutter the shortcut
|
||||
// list that is rendered when you hold down the command key.
|
||||
|
||||
return @[
|
||||
[self keyCommandWithInput:@"\r"
|
||||
modifierFlags:UIKeyModifierCommand
|
||||
action:@selector(modifiedReturnPressed:)
|
||||
discoverabilityTitle:@"Send Message"],
|
||||
// "Alternate" is option.
|
||||
[self keyCommandWithInput:@"\r"
|
||||
modifierFlags:UIKeyModifierAlternate
|
||||
action:@selector(modifiedReturnPressed:)
|
||||
discoverabilityTitle:@"Send Message"],
|
||||
// An unmodified return can only be sent by a hardware keyboard,
|
||||
// return on the software keyboard will not trigger this command.
|
||||
// Return, send message
|
||||
[UIKeyCommand keyCommandWithInput:@"\r" modifierFlags:0 action:@selector(unmodifedReturnPressed:)],
|
||||
// Alt + Return, inserts a new line
|
||||
[UIKeyCommand keyCommandWithInput:@"\r"
|
||||
modifierFlags:UIKeyModifierAlternate
|
||||
action:@selector(modifiedReturnPressed:)],
|
||||
// Shift + Return, inserts a new line
|
||||
[UIKeyCommand keyCommandWithInput:@"\r"
|
||||
modifierFlags:UIKeyModifierShift
|
||||
action:@selector(modifiedReturnPressed:)],
|
||||
];
|
||||
}
|
||||
|
||||
- (UIKeyCommand *)keyCommandWithInput:(NSString *)input
|
||||
modifierFlags:(UIKeyModifierFlags)modifierFlags
|
||||
action:(SEL)action
|
||||
discoverabilityTitle:(NSString *)discoverabilityTitle
|
||||
- (void)unmodifedReturnPressed:(UIKeyCommand *)sender
|
||||
{
|
||||
return [UIKeyCommand keyCommandWithInput:input
|
||||
modifierFlags:modifierFlags
|
||||
action:action
|
||||
discoverabilityTitle:discoverabilityTitle];
|
||||
OWSLogInfo(@"unmodifedReturnPressed: %@", sender.input);
|
||||
|
||||
[self.inputTextViewDelegate inputTextViewSendMessagePressed];
|
||||
}
|
||||
|
||||
- (void)modifiedReturnPressed:(UIKeyCommand *)sender
|
||||
{
|
||||
OWSLogInfo(@"modifiedReturnPressed: %@", sender.input);
|
||||
[self.inputTextViewDelegate inputTextViewSendMessagePressed];
|
||||
|
||||
self.text = [self.text stringByAppendingString:@"\n"];
|
||||
|
||||
[self.inputTextViewDelegate textViewDidChange:self];
|
||||
[self.textViewToolbarDelegate textViewDidChange:self];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@ -86,6 +86,8 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
- (void)clearTextMessageAnimated:(BOOL)isAnimated;
|
||||
- (void)clearDesiredKeyboard;
|
||||
- (void)toggleDefaultKeyboard;
|
||||
- (void)showStickerKeyboard;
|
||||
- (void)showAttachmentKeyboard;
|
||||
|
||||
- (void)updateFontSizes;
|
||||
|
||||
|
||||
@ -1212,6 +1212,24 @@ const CGFloat kMaxTextViewHeight = 98;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)showStickerKeyboard
|
||||
{
|
||||
OWSAssertIsOnMainThread();
|
||||
|
||||
if (self.desiredKeyboardType != KeyboardType_Sticker) {
|
||||
[self toggleKeyboardType:KeyboardType_Sticker];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)showAttachmentKeyboard
|
||||
{
|
||||
OWSAssertIsOnMainThread();
|
||||
|
||||
if (self.desiredKeyboardType != KeyboardType_Attachment) {
|
||||
[self toggleKeyboardType:KeyboardType_Attachment];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - ConversationTextViewToolbarDelegate
|
||||
|
||||
- (void)setBounds:(CGRect)bounds
|
||||
|
||||
@ -33,6 +33,15 @@ typedef NS_ENUM(NSUInteger, ConversationViewAction) {
|
||||
- (void)peekSetup;
|
||||
- (void)popped;
|
||||
|
||||
#pragma mark - Keyboard Shortcuts
|
||||
|
||||
- (void)showConversationSettings;
|
||||
- (void)focusInputToolbar;
|
||||
- (void)openAllMedia;
|
||||
- (void)openStickerKeyboard;
|
||||
- (void)openAttachmentKeyboard;
|
||||
- (void)openGifSearch;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark - Internal Methods. Used in extensions
|
||||
|
||||
@ -1748,7 +1748,42 @@ typedef enum : NSUInteger {
|
||||
settingsVC.conversationSettingsViewDelegate = self;
|
||||
[settingsVC configureWithThread:self.thread];
|
||||
settingsVC.showVerificationOnAppear = showVerification;
|
||||
[self.navigationController pushViewController:settingsVC animated:YES];
|
||||
|
||||
[self.navigationController setViewControllers:[self.viewControllersUpToSelf arrayByAddingObject:settingsVC]
|
||||
animated:YES];
|
||||
}
|
||||
|
||||
- (void)showConversationSettingsAndShowAllMedia
|
||||
{
|
||||
OWSConversationSettingsViewController *settingsVC = [OWSConversationSettingsViewController new];
|
||||
settingsVC.conversationSettingsViewDelegate = self;
|
||||
[settingsVC configureWithThread:self.thread];
|
||||
|
||||
MediaTileViewController *allMedia = [[MediaTileViewController alloc] initWithThread:self.thread];
|
||||
|
||||
[self.navigationController
|
||||
setViewControllers:[self.viewControllersUpToSelf arrayByAddingObjectsFromArray:@[ settingsVC, allMedia ]]
|
||||
animated:YES];
|
||||
}
|
||||
|
||||
- (NSArray<UIViewController *> *)viewControllersUpToSelf
|
||||
{
|
||||
OWSAssertIsOnMainThread();
|
||||
OWSAssertDebug(self.navigationController);
|
||||
|
||||
if (self.navigationController.topViewController == self) {
|
||||
return self.navigationController.viewControllers;
|
||||
}
|
||||
|
||||
NSArray *viewControllers = self.navigationController.viewControllers;
|
||||
NSUInteger index = [viewControllers indexOfObject:self];
|
||||
|
||||
if (index == NSNotFound) {
|
||||
OWSFailDebug(@"Unexpectedly missing from view hierarhy");
|
||||
return viewControllers;
|
||||
}
|
||||
|
||||
return [viewControllers subarrayWithRange:NSMakeRange(0, index + 1)];
|
||||
}
|
||||
|
||||
#pragma mark - DisappearingTimerConfigurationViewDelegate
|
||||
@ -5338,6 +5373,44 @@ typedef enum : NSUInteger {
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Keyboard Shortcuts
|
||||
|
||||
- (void)focusInputToolbar
|
||||
{
|
||||
OWSAssertIsOnMainThread();
|
||||
|
||||
[self.inputToolbar clearDesiredKeyboard];
|
||||
[self popKeyBoard];
|
||||
}
|
||||
|
||||
- (void)openAllMedia
|
||||
{
|
||||
OWSAssertIsOnMainThread();
|
||||
|
||||
[self showConversationSettingsAndShowAllMedia];
|
||||
}
|
||||
|
||||
- (void)openStickerKeyboard
|
||||
{
|
||||
OWSAssertIsOnMainThread();
|
||||
|
||||
[self.inputToolbar showStickerKeyboard];
|
||||
}
|
||||
|
||||
- (void)openAttachmentKeyboard
|
||||
{
|
||||
OWSAssertIsOnMainThread();
|
||||
|
||||
[self.inputToolbar showAttachmentKeyboard];
|
||||
}
|
||||
|
||||
- (void)openGifSearch
|
||||
{
|
||||
OWSAssertIsOnMainThread();
|
||||
|
||||
[self showGifPicker];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@ -25,8 +25,15 @@ typedef NS_ENUM(NSInteger, ConversationListViewControllerSection) {
|
||||
focusMessageId:(nullable NSString *)focusMessageId
|
||||
animated:(BOOL)isAnimated;
|
||||
|
||||
// Used by force-touch Springboard icon shortcut
|
||||
// Used by force-touch Springboard icon shortcut and key commands
|
||||
- (void)showNewConversationView;
|
||||
- (void)showNewGroupView;
|
||||
- (void)showAppSettings;
|
||||
- (void)focusSearch;
|
||||
- (void)selectPreviousConversation;
|
||||
- (void)selectNextConversation;
|
||||
- (void)archiveSelectedConversation;
|
||||
- (void)unarchiveSelectedConversation;
|
||||
|
||||
@property (nonatomic) TSThread *lastViewedThread;
|
||||
|
||||
|
||||
@ -751,9 +751,7 @@ NSString *const kArchiveButtonPseudoGroup = @"kArchiveButtonPseudoGroup";
|
||||
OWSAssertDebug(avatarImage);
|
||||
|
||||
UIButton *avatarButton = [AvatarImageButton buttonWithType:UIButtonTypeCustom];
|
||||
[avatarButton addTarget:self
|
||||
action:@selector(settingsButtonPressed:)
|
||||
forControlEvents:UIControlEventTouchUpInside];
|
||||
[avatarButton addTarget:self action:@selector(showAppSettings) forControlEvents:UIControlEventTouchUpInside];
|
||||
[avatarButton setImage:avatarImage forState:UIControlStateNormal];
|
||||
[avatarButton autoSetDimension:ALDimensionWidth toSize:kAvatarSize];
|
||||
[avatarButton autoSetDimension:ALDimensionHeight toSize:kAvatarSize];
|
||||
@ -766,7 +764,7 @@ NSString *const kArchiveButtonPseudoGroup = @"kArchiveButtonPseudoGroup";
|
||||
settingsButton = [[UIBarButtonItem alloc] initWithImage:image
|
||||
style:UIBarButtonItemStylePlain
|
||||
target:self
|
||||
action:@selector(settingsButtonPressed:)
|
||||
action:@selector(showAppSettings)
|
||||
accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"settings")];
|
||||
}
|
||||
settingsButton.accessibilityLabel = CommonStrings.openSettingsButton;
|
||||
@ -792,12 +790,6 @@ NSString *const kArchiveButtonPseudoGroup = @"kArchiveButtonPseudoGroup";
|
||||
}
|
||||
}
|
||||
|
||||
- (void)settingsButtonPressed:(id)sender
|
||||
{
|
||||
OWSNavigationController *navigationController = [AppSettingsViewController inModalNavigationController];
|
||||
[self presentFormSheetViewController:navigationController animated:YES completion:nil];
|
||||
}
|
||||
|
||||
- (nullable UIViewController *)previewingContext:(id<UIViewControllerPreviewing>)previewingContext
|
||||
viewControllerForLocation:(CGPoint)location
|
||||
{
|
||||
@ -853,6 +845,143 @@ NSString *const kArchiveButtonPseudoGroup = @"kArchiveButtonPseudoGroup";
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)showNewGroupView
|
||||
{
|
||||
OWSAssertIsOnMainThread();
|
||||
|
||||
OWSLogInfo(@"");
|
||||
|
||||
NewGroupViewController *viewController = [NewGroupViewController new];
|
||||
|
||||
[self.contactsManager requestSystemContactsOnceWithCompletion:^(NSError *_Nullable error) {
|
||||
if (error) {
|
||||
OWSLogError(@"Error when requesting contacts: %@", error);
|
||||
}
|
||||
// Even if there is an error fetching contacts we proceed to the next screen.
|
||||
// As the compose view will present the proper thing depending on contact access.
|
||||
//
|
||||
// We just want to make sure contact access is *complete* before showing the compose
|
||||
// screen to avoid flicker.
|
||||
OWSNavigationController *modal = [[OWSNavigationController alloc] initWithRootViewController:viewController];
|
||||
[self.navigationController presentFormSheetViewController:modal animated:YES completion:nil];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)showAppSettings
|
||||
{
|
||||
OWSAssertIsOnMainThread();
|
||||
|
||||
OWSLogInfo(@"");
|
||||
|
||||
OWSNavigationController *navigationController = [AppSettingsViewController inModalNavigationController];
|
||||
[self presentFormSheetViewController:navigationController animated:YES completion:nil];
|
||||
}
|
||||
|
||||
- (void)focusSearch
|
||||
{
|
||||
OWSAssertIsOnMainThread();
|
||||
|
||||
OWSLogInfo(@"");
|
||||
|
||||
// If we have presented a conversation list (the archive) search there instead.
|
||||
if (self.presentedConversationListViewController) {
|
||||
[self.presentedConversationListViewController focusSearch];
|
||||
return;
|
||||
}
|
||||
|
||||
[self.searchBar becomeFirstResponder];
|
||||
}
|
||||
|
||||
- (void)selectPreviousConversation
|
||||
{
|
||||
OWSAssertIsOnMainThread();
|
||||
|
||||
OWSLogInfo(@"");
|
||||
|
||||
// If we have presented a conversation list (the archive) navigate through that instead.
|
||||
if (self.presentedConversationListViewController) {
|
||||
[self.presentedConversationListViewController selectPreviousConversation];
|
||||
return;
|
||||
}
|
||||
|
||||
TSThread *_Nullable currentThread = self.conversationSplitViewController.selectedThread;
|
||||
NSIndexPath *_Nullable previousIndexPath = [self.threadMapping indexPathBeforeThread:currentThread];
|
||||
if (previousIndexPath) {
|
||||
[self presentThread:[self threadForIndexPath:previousIndexPath] action:ConversationViewActionNone animated:YES];
|
||||
[self.tableView selectRowAtIndexPath:previousIndexPath
|
||||
animated:YES
|
||||
scrollPosition:UITableViewScrollPositionNone];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)selectNextConversation
|
||||
{
|
||||
OWSAssertIsOnMainThread();
|
||||
|
||||
OWSLogInfo(@"");
|
||||
|
||||
// If we have presented a conversation list (the archive) navigate through that instead.
|
||||
if (self.presentedConversationListViewController) {
|
||||
[self.presentedConversationListViewController selectNextConversation];
|
||||
return;
|
||||
}
|
||||
|
||||
TSThread *_Nullable currentThread = self.conversationSplitViewController.selectedThread;
|
||||
NSIndexPath *_Nullable nextIndexPath = [self.threadMapping indexPathAfterThread:currentThread];
|
||||
if (nextIndexPath) {
|
||||
[self presentThread:[self threadForIndexPath:nextIndexPath] action:ConversationViewActionNone animated:YES];
|
||||
[self.tableView selectRowAtIndexPath:nextIndexPath animated:YES scrollPosition:UITableViewScrollPositionNone];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)archiveSelectedConversation
|
||||
{
|
||||
OWSAssertIsOnMainThread();
|
||||
|
||||
OWSLogInfo(@"");
|
||||
|
||||
TSThread *_Nullable selectedThread = self.conversationSplitViewController.selectedThread;
|
||||
|
||||
if (!selectedThread) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (selectedThread.isArchived) {
|
||||
return;
|
||||
}
|
||||
|
||||
[self.conversationSplitViewController closeSelectedConversationAnimated:YES];
|
||||
|
||||
[self.databaseStorage writeWithBlock:^(SDSAnyWriteTransaction *transaction) {
|
||||
[selectedThread archiveThreadWithTransaction:transaction];
|
||||
}];
|
||||
[self updateViewState];
|
||||
}
|
||||
|
||||
- (void)unarchiveSelectedConversation
|
||||
{
|
||||
OWSAssertIsOnMainThread();
|
||||
|
||||
OWSLogInfo(@"");
|
||||
|
||||
TSThread *_Nullable selectedThread = self.conversationSplitViewController.selectedThread;
|
||||
|
||||
if (!selectedThread) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!selectedThread.isArchived) {
|
||||
return;
|
||||
}
|
||||
|
||||
[self.conversationSplitViewController closeSelectedConversationAnimated:YES];
|
||||
|
||||
[self.databaseStorage writeWithBlock:^(SDSAnyWriteTransaction *transaction) {
|
||||
[selectedThread unarchiveThreadWithTransaction:transaction];
|
||||
}];
|
||||
[self updateViewState];
|
||||
}
|
||||
|
||||
- (void)showCameraView
|
||||
{
|
||||
[self ows_askForCameraPermissions:^(BOOL cameraGranted) {
|
||||
@ -1508,6 +1637,20 @@ NSString *const kArchiveButtonPseudoGroup = @"kArchiveButtonPseudoGroup";
|
||||
[self showViewController:conversationList sender:self];
|
||||
}
|
||||
|
||||
- (nullable ConversationListViewController *)presentedConversationListViewController
|
||||
{
|
||||
UIViewController *_Nullable topViewController = self.navigationController.topViewController;
|
||||
if (topViewController == self) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (![topViewController isKindOfClass:[ConversationListViewController class]]) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
return (ConversationListViewController *)topViewController;
|
||||
}
|
||||
|
||||
- (NSString *)currentGrouping
|
||||
{
|
||||
switch (self.conversationListMode) {
|
||||
|
||||
@ -68,11 +68,6 @@ class ConversationSplitViewController: UISplitViewController {
|
||||
return Theme.isDarkThemeEnabled ? .lightContent : .default
|
||||
}
|
||||
|
||||
@objc
|
||||
func showNewConversationView() {
|
||||
conversationListVC.showNewConversationView()
|
||||
}
|
||||
|
||||
@objc(closeSelectedConversationAnimated:)
|
||||
func closeSelectedConversation(animated: Bool) {
|
||||
guard let selectedConversationViewController = selectedConversationViewController else { return }
|
||||
@ -153,6 +148,235 @@ class ConversationSplitViewController: UISplitViewController {
|
||||
}
|
||||
currentDetailViewController = vc
|
||||
}
|
||||
|
||||
// MARK: - Keyboard Shortcuts
|
||||
|
||||
override var canBecomeFirstResponder: Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
let globalKeyCommands = [
|
||||
UIKeyCommand(
|
||||
input: "n",
|
||||
modifierFlags: .command,
|
||||
action: #selector(showNewConversationView),
|
||||
discoverabilityTitle: NSLocalizedString(
|
||||
"KEY_COMMAND_NEW_MESSAGE",
|
||||
comment: "A keyboard command to present the new message dialog."
|
||||
)
|
||||
),
|
||||
UIKeyCommand(
|
||||
input: "g",
|
||||
modifierFlags: .command,
|
||||
action: #selector(showNewGroupView),
|
||||
discoverabilityTitle: NSLocalizedString(
|
||||
"KEY_COMMAND_NEW_GROUP",
|
||||
comment: "A keyboard command to present the new group dialog."
|
||||
)
|
||||
),
|
||||
UIKeyCommand(
|
||||
input: ",",
|
||||
modifierFlags: .command,
|
||||
action: #selector(showAppSettings),
|
||||
discoverabilityTitle: NSLocalizedString(
|
||||
"KEY_COMMAND_SETTINGS",
|
||||
comment: "A keyboard command to present the application settings dialog."
|
||||
)
|
||||
),
|
||||
UIKeyCommand(
|
||||
input: "f",
|
||||
modifierFlags: .command,
|
||||
action: #selector(focusSearch),
|
||||
discoverabilityTitle: NSLocalizedString(
|
||||
"KEY_COMMAND_SEARCH",
|
||||
comment: "A keyboard command to begin a search on the conversation list."
|
||||
)
|
||||
),
|
||||
UIKeyCommand(
|
||||
input: UIKeyCommand.inputUpArrow,
|
||||
modifierFlags: .alternate,
|
||||
action: #selector(selectPreviousConversation),
|
||||
discoverabilityTitle: NSLocalizedString(
|
||||
"KEY_COMMAND_PREVIOUS_CONVERSATION",
|
||||
comment: "A keyboard command to jump to the previous conversation in the list."
|
||||
)
|
||||
),
|
||||
UIKeyCommand(
|
||||
input: UIKeyCommand.inputDownArrow,
|
||||
modifierFlags: .alternate,
|
||||
action: #selector(selectNextConversation),
|
||||
discoverabilityTitle: NSLocalizedString(
|
||||
"KEY_COMMAND_NEXT_CONVERSATION",
|
||||
comment: "A keyboard command to jump to the next conversation in the list."
|
||||
)
|
||||
)
|
||||
]
|
||||
|
||||
let selectedConversationKeyCommands = [
|
||||
UIKeyCommand(
|
||||
input: "i",
|
||||
modifierFlags: [.command, .shift],
|
||||
action: #selector(openConversationSettings),
|
||||
discoverabilityTitle: NSLocalizedString(
|
||||
"KEY_COMMAND_CONVERSATION_INFO",
|
||||
comment: "A keyboard command to open the current conversation's settings."
|
||||
)
|
||||
),
|
||||
UIKeyCommand(
|
||||
input: "m",
|
||||
modifierFlags: [.command, .shift],
|
||||
action: #selector(openAllMedia),
|
||||
discoverabilityTitle: NSLocalizedString(
|
||||
"KEY_COMMAND_ALL_MEDIA",
|
||||
comment: "A keyboard command to open the current conversation's all media view."
|
||||
)
|
||||
),
|
||||
UIKeyCommand(
|
||||
input: "g",
|
||||
modifierFlags: [.command, .shift],
|
||||
action: #selector(openGifSearch),
|
||||
discoverabilityTitle: NSLocalizedString(
|
||||
"KEY_COMMAND_GIF_SEARCH",
|
||||
comment: "A keyboard command to open the current conversations GIF picker."
|
||||
)
|
||||
),
|
||||
UIKeyCommand(
|
||||
input: "u",
|
||||
modifierFlags: .command,
|
||||
action: #selector(openAttachmentKeyboard),
|
||||
discoverabilityTitle: NSLocalizedString(
|
||||
"KEY_COMMAND_ATTACHMENTS",
|
||||
comment: "A keyboard command to open the current conversation's attachment picker."
|
||||
)
|
||||
),
|
||||
UIKeyCommand(
|
||||
input: "s",
|
||||
modifierFlags: [.command, .shift],
|
||||
action: #selector(openStickerKeyboard),
|
||||
discoverabilityTitle: NSLocalizedString(
|
||||
"KEY_COMMAND_STICKERS",
|
||||
comment: "A keyboard command to open the current conversation's sticker picker."
|
||||
)
|
||||
),
|
||||
UIKeyCommand(
|
||||
input: "a",
|
||||
modifierFlags: [.command, .shift],
|
||||
action: #selector(archiveSelectedConversation),
|
||||
discoverabilityTitle: NSLocalizedString(
|
||||
"KEY_COMMAND_ARCHIVE",
|
||||
comment: "A keyboard command to archive the current coversation."
|
||||
)
|
||||
),
|
||||
UIKeyCommand(
|
||||
input: "u",
|
||||
modifierFlags: [.command, .shift],
|
||||
action: #selector(unarchiveSelectedConversation),
|
||||
discoverabilityTitle: NSLocalizedString(
|
||||
"KEY_COMMAND_UNARCHIVE",
|
||||
comment: "A keyboard command to unarchive the current coversation."
|
||||
)
|
||||
),
|
||||
UIKeyCommand(
|
||||
input: "t",
|
||||
modifierFlags: [.command, .shift],
|
||||
action: #selector(focusInputToolbar),
|
||||
discoverabilityTitle: NSLocalizedString(
|
||||
"KEY_COMMAND_FOCUS_COMPOSER",
|
||||
comment: "A keyboard command to focus the current conversation's input field."
|
||||
)
|
||||
)
|
||||
]
|
||||
|
||||
override var keyCommands: [UIKeyCommand]? {
|
||||
// If there is a modal presented over us, or another window above us, don't respond to keyboard commands.
|
||||
guard presentedViewController == nil || view.window?.isKeyWindow != true else { return nil }
|
||||
|
||||
if selectedThread != nil {
|
||||
return selectedConversationKeyCommands + globalKeyCommands
|
||||
} else {
|
||||
return globalKeyCommands
|
||||
}
|
||||
}
|
||||
|
||||
@objc func showNewConversationView() {
|
||||
conversationListVC.showNewConversationView()
|
||||
}
|
||||
|
||||
@objc func showNewGroupView() {
|
||||
conversationListVC.showNewGroupView()
|
||||
}
|
||||
|
||||
@objc func showAppSettings() {
|
||||
conversationListVC.showAppSettings()
|
||||
}
|
||||
|
||||
@objc func focusSearch() {
|
||||
conversationListVC.focusSearch()
|
||||
}
|
||||
|
||||
@objc func selectPreviousConversation() {
|
||||
conversationListVC.selectPreviousConversation()
|
||||
}
|
||||
|
||||
@objc func selectNextConversation(_ sender: UIKeyCommand) {
|
||||
conversationListVC.selectNextConversation()
|
||||
}
|
||||
|
||||
@objc func archiveSelectedConversation() {
|
||||
conversationListVC.archiveSelectedConversation()
|
||||
}
|
||||
|
||||
@objc func unarchiveSelectedConversation() {
|
||||
conversationListVC.unarchiveSelectedConversation()
|
||||
}
|
||||
|
||||
@objc func openConversationSettings() {
|
||||
guard let selectedConversationViewController = selectedConversationViewController else {
|
||||
return owsFailDebug("unexpectedly missing selected conversation")
|
||||
}
|
||||
|
||||
selectedConversationViewController.showConversationSettings()
|
||||
}
|
||||
|
||||
@objc func focusInputToolbar() {
|
||||
guard let selectedConversationViewController = selectedConversationViewController else {
|
||||
return owsFailDebug("unexpectedly missing selected conversation")
|
||||
}
|
||||
|
||||
selectedConversationViewController.focusInputToolbar()
|
||||
}
|
||||
|
||||
@objc func openAllMedia() {
|
||||
guard let selectedConversationViewController = selectedConversationViewController else {
|
||||
return owsFailDebug("unexpectedly missing selected conversation")
|
||||
}
|
||||
|
||||
selectedConversationViewController.openAllMedia()
|
||||
}
|
||||
|
||||
@objc func openStickerKeyboard() {
|
||||
guard let selectedConversationViewController = selectedConversationViewController else {
|
||||
return owsFailDebug("unexpectedly missing selected conversation")
|
||||
}
|
||||
|
||||
selectedConversationViewController.openStickerKeyboard()
|
||||
}
|
||||
|
||||
@objc func openAttachmentKeyboard() {
|
||||
guard let selectedConversationViewController = selectedConversationViewController else {
|
||||
return owsFailDebug("unexpectedly missing selected conversation")
|
||||
}
|
||||
|
||||
selectedConversationViewController.openAttachmentKeyboard()
|
||||
}
|
||||
|
||||
@objc func openGifSearch() {
|
||||
guard let selectedConversationViewController = selectedConversationViewController else {
|
||||
return owsFailDebug("unexpectedly missing selected conversation")
|
||||
}
|
||||
|
||||
selectedConversationViewController.openGifSearch()
|
||||
}
|
||||
}
|
||||
|
||||
extension ConversationSplitViewController: UISplitViewControllerDelegate {
|
||||
|
||||
@ -241,6 +241,34 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
#pragma mark - Methods
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated
|
||||
{
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
if (self.navigationController.viewControllers.count == 1) {
|
||||
self.navigationItem.leftBarButtonItem =
|
||||
[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemStop
|
||||
target:self
|
||||
action:@selector(dismissPressed)];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)dismissPressed
|
||||
{
|
||||
[self.groupNameTextField resignFirstResponder];
|
||||
|
||||
if (!self.hasUnsavedChanges) {
|
||||
// If user made no changes, dismiss.
|
||||
[self dismissViewControllerAnimated:YES completion:nil];
|
||||
return;
|
||||
}
|
||||
|
||||
__weak NewGroupViewController *weakSelf = self;
|
||||
[OWSAlerts showPendingChangesAlertWithDiscardAction:^{
|
||||
[weakSelf dismissViewControllerAnimated:YES completion:nil];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)viewDidAppear:(BOOL)animated
|
||||
{
|
||||
[super viewDidAppear:animated];
|
||||
|
||||
@ -1269,6 +1269,48 @@
|
||||
/* Slider label when disappearing messages is off */
|
||||
"KEEP_MESSAGES_FOREVER" = "Messages do not disappear.";
|
||||
|
||||
/* A keyboard command to open the current conversation's all media view. */
|
||||
"KEY_COMMAND_ALL_MEDIA" = "Go to All Media";
|
||||
|
||||
/* A keyboard command to archive the current coversation. */
|
||||
"KEY_COMMAND_ARCHIVE" = "Archive Conversation";
|
||||
|
||||
/* A keyboard command to open the current conversation's attachment picker. */
|
||||
"KEY_COMMAND_ATTACHMENTS" = "Show Attachments";
|
||||
|
||||
/* A keyboard command to open the current conversation's settings. */
|
||||
"KEY_COMMAND_CONVERSATION_INFO" = "Go to Conversation Info";
|
||||
|
||||
/* A keyboard command to focus the current conversation's input field. */
|
||||
"KEY_COMMAND_FOCUS_COMPOSER" = "Focus Input Bar";
|
||||
|
||||
/* A keyboard command to open the current conversations GIF picker. */
|
||||
"KEY_COMMAND_GIF_SEARCH" = "Go to GIF Search";
|
||||
|
||||
/* A keyboard command to present the new group dialog. */
|
||||
"KEY_COMMAND_NEW_GROUP" = "New Group";
|
||||
|
||||
/* A keyboard command to present the new message dialog. */
|
||||
"KEY_COMMAND_NEW_MESSAGE" = "New Message";
|
||||
|
||||
/* A keyboard command to jump to the next conversation in the list. */
|
||||
"KEY_COMMAND_NEXT_CONVERSATION" = "Go to Next Conversation";
|
||||
|
||||
/* A keyboard command to jump to the previous conversation in the list. */
|
||||
"KEY_COMMAND_PREVIOUS_CONVERSATION" = "Go to Previous Conversation";
|
||||
|
||||
/* A keyboard command to begin a search on the conversation list. */
|
||||
"KEY_COMMAND_SEARCH" = "Search";
|
||||
|
||||
/* A keyboard command to present the application settings dialog. */
|
||||
"KEY_COMMAND_SETTINGS" = "Settings";
|
||||
|
||||
/* A keyboard command to open the current conversation's sticker picker. */
|
||||
"KEY_COMMAND_STICKERS" = "Show Stickers";
|
||||
|
||||
/* A keyboard command to unarchive the current coversation. */
|
||||
"KEY_COMMAND_UNARCHIVE" = "Unarchive Conversation";
|
||||
|
||||
/* Confirmation button within contextual alert */
|
||||
"LEAVE_BUTTON_TITLE" = "Leave";
|
||||
|
||||
@ -1338,9 +1380,6 @@
|
||||
/* Confirmation button text to delete selected media message from the gallery */
|
||||
"MEDIA_GALLERY_DELETE_SINGLE_MESSAGE" = "Delete Message";
|
||||
|
||||
/* embeds {{sender name}} and {{sent datetime}}, e.g. 'Sarah on 10/30/18, 3:29' */
|
||||
"MEDIA_GALLERY_LANDSCAPE_TITLE_FORMAT" = "%@ on %@";
|
||||
|
||||
/* Format for the 'more items' indicator for media galleries. Embeds {{the number of additional items}}. */
|
||||
"MEDIA_GALLERY_MORE_ITEMS_FORMAT" = "+%@";
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user