Batch Delete

// FREEBIE
pull/1/head
Michael Kirk 6 years ago
parent c13226d6c6
commit 3058cb8733

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

@ -0,0 +1,23 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "BlueCheckSelected_31x31_@1x.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "BlueCheckSelected_31x31_@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "BlueCheckSelected_31x31_@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

@ -176,11 +176,11 @@ protocol MediaGalleryDataSource: class {
func showAllMedia(focusedItem: MediaGalleryItem)
func dismissMediaDetailViewController(_ mediaDetailViewController: MediaPageViewController, animated isAnimated: Bool, completion: (() -> Void)?)
func delete(message: TSMessage)
func delete(items: [MediaGalleryItem])
}
protocol MediaGalleryDataSourceDelegate: class {
func mediaGalleryDataSource(_ mediaGalleryDataSource: MediaGalleryDataSource, willDelete message: TSMessage)
func mediaGalleryDataSource(_ mediaGalleryDataSource: MediaGalleryDataSource, willDelete items: [MediaGalleryItem])
func mediaGalleryDataSource(_ mediaGalleryDataSource: MediaGalleryDataSource, deletedSections: IndexSet, deletedItems: [IndexPath])
}
@ -725,58 +725,82 @@ class MediaGalleryViewController: UINavigationController, MediaGalleryDataSource
weak var dataSourceDelegate: MediaGalleryDataSourceDelegate?
var deletedMessages: Set<TSMessage> = Set()
func delete(message: TSMessage) {
Logger.info("\(logTag) in \(#function) with message: \(String(describing: message.uniqueId)) attachmentId: \(String(describing: message.attachmentIds.firstObject))")
self.dataSourceDelegate?.mediaGalleryDataSource(self, willDelete: message)
func delete(items: [MediaGalleryItem]) {
AssertIsOnMainThread()
Logger.info("\(logTag) in \(#function) with items: \(items)")
dataSourceDelegate?.mediaGalleryDataSource(self, willDelete: items)
self.editingDatabaseConnection.asyncReadWrite { transaction in
message.remove(with: transaction)
for item in items {
let message = item.message
message.remove(with: transaction)
self.deletedMessages.insert(message)
}
}
self.deletedMessages.insert(message)
var deletedSections: IndexSet = IndexSet()
var deletedIndexPaths: [IndexPath] = []
let originalSections = self.sections
let originalSectionDates = self.sectionDates
guard let itemIndex = galleryItems.index(where: { $0.message == message }) else {
owsFail("\(logTag) in \(#function) removing unknown item.")
return
}
let item: MediaGalleryItem = galleryItems[itemIndex]
self.galleryItems.remove(at: itemIndex)
for item in items {
guard let itemIndex = galleryItems.index(of: item) else {
owsFail("\(logTag) in \(#function) removing unknown item.")
return
}
guard let sectionIndex = sectionDates.index(where: { $0 == item.galleryDate }) else {
owsFail("\(logTag) in \(#function) item with unknown date.")
return
}
self.galleryItems.remove(at: itemIndex)
guard var sectionItems = self.sections[item.galleryDate] else {
owsFail("\(logTag) in \(#function) item with unknown section")
return
}
guard let sectionIndex = sectionDates.index(where: { $0 == item.galleryDate }) else {
owsFail("\(logTag) in \(#function) item with unknown date.")
return
}
if sectionItems == [item] {
// Last item in section. Delete section.
self.sections[item.galleryDate] = nil
self.sectionDates.remove(at: sectionIndex)
guard var sectionItems = self.sections[item.galleryDate] else {
owsFail("\(logTag) in \(#function) item with unknown section")
return
}
deletedSections.insert(sectionIndex + 1)
deletedIndexPaths.append(IndexPath(row: 0, section: sectionIndex + 1))
} else {
guard let sectionRowIndex = sectionItems.index(of: item) else {
owsFail("\(logTag) in \(#function) item with unknown sectionRowIndex")
return
}
sectionItems.remove(at: sectionRowIndex)
self.sections[item.galleryDate] = sectionItems
// We need to calculate the index of the deleted item with respect to it's original position.
guard let originalSectionIndex = originalSectionDates.index(where: { $0 == item.galleryDate }) else {
owsFail("\(logTag) in \(#function) item with unknown date.")
return
}
guard let originalSectionItems = originalSections[item.galleryDate] else {
owsFail("\(logTag) in \(#function) item with unknown section")
return
}
deletedIndexPaths.append(IndexPath(row: sectionRowIndex, section: sectionIndex + 1))
guard let originalSectionRowIndex = originalSectionItems.index(of: item) else {
owsFail("\(logTag) in \(#function) item with unknown sectionRowIndex")
return
}
if sectionItems == [item] {
// Last item in section. Delete section.
self.sections[item.galleryDate] = nil
self.sectionDates.remove(at: sectionIndex)
deletedSections.insert(originalSectionIndex + 1)
deletedIndexPaths.append(IndexPath(row: originalSectionRowIndex, section: originalSectionIndex + 1))
} else {
sectionItems.remove(at: sectionRowIndex)
self.sections[item.galleryDate] = sectionItems
deletedIndexPaths.append(IndexPath(row: originalSectionRowIndex, section: originalSectionIndex + 1))
}
}
self.dataSourceDelegate?.mediaGalleryDataSource(self, deletedSections: deletedSections, deletedItems: deletedIndexPaths)
dataSourceDelegate?.mediaGalleryDataSource(self, deletedSections: deletedSections, deletedItems: deletedIndexPaths)
}
let kGallerySwipeLoadBatchSize: UInt = 5

@ -327,7 +327,7 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou
// else we deleted the last piece of media, return to the conversation view
self.dismissSelf(animated: true)
}
mediaGalleryDataSource.delete(message: deletedItem.message)
mediaGalleryDataSource.delete(items: [deletedItem])
}
actionSheet.addAction(OWSAlerts.cancelAction)
actionSheet.addAction(deleteAction)
@ -502,8 +502,15 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou
return
}
guard let galleryItem = self.mediaGalleryDataSource?.galleryItems.first(where: { $0.message == message }) else {
owsFail("\(logTag) in \(#function) unexpected interaction: \(type(of: conversationViewItem))")
self.presentingViewController?.dismiss(animated: true)
return
}
dismissSelf(animated: true) {
mediaGalleryDataSource.delete(message: message)
mediaGalleryDataSource.delete(items: [galleryItem])
}
}

@ -8,7 +8,7 @@ public protocol MediaTileViewControllerDelegate: class {
func mediaTileViewController(_ viewController: MediaTileViewController, didTapView tappedView: UIView, mediaGalleryItem: MediaGalleryItem)
}
public class MediaTileViewController: UICollectionViewController, MediaGalleryCellDelegate, MediaGalleryDataSourceDelegate {
public class MediaTileViewController: UICollectionViewController, MediaGalleryDataSourceDelegate {
private weak var mediaGalleryDataSource: MediaGalleryDataSource?
@ -88,10 +88,32 @@ public class MediaTileViewController: UICollectionViewController, MediaGalleryCe
collectionView.delegate = self
// TODO iPhoneX
// feels a bit weird to have content smashed all the way to the bottom edge.
collectionView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 20, right: 0)
let footerBar = UIToolbar()
self.footerBar = footerBar
let deleteButton = UIBarButtonItem(barButtonSystemItem: .trash,
target:self,
action:#selector(didPressDelete))
self.deleteButton = deleteButton
let footerItems = [
UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target:nil, action:nil),
deleteButton,
UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target:nil, action:nil),
]
footerBar.setItems(footerItems, animated: false)
self.view.addSubview(self.footerBar)
footerBar.barTintColor = UIColor.ows_signalBrandBlue
footerBar.autoPinWidthToSuperview()
footerBar.autoSetDimension(.height, toSize: footerBarHeight)
footerBar.layoutMargins = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 40)
// footerBar is offscreen until user hits "select"
self.footerBarBottomConstraint = footerBar.autoPinEdge(toSuperviewEdge: .bottom, withInset: -footerBarHeight)
updateSelectButton()
self.view.layoutIfNeeded()
scrollToBottom(animated: false)
}
@ -123,7 +145,7 @@ public class MediaTileViewController: UICollectionViewController, MediaGalleryCe
self.collectionView?.scrollToItem(at: indexPath, at: .centeredVertically, animated: false)
}
// MARK: UIColletionViewDelegate
// MARK: UICollectionViewDelegate
override public func scrollViewDidScroll(_ scrollView: UIScrollView) {
self.autoLoadMoreIfNecessary()
@ -137,42 +159,87 @@ public class MediaTileViewController: UICollectionViewController, MediaGalleryCe
self.isUserScrolling = false
}
private var isUserScrolling: Bool = false {
didSet {
autoLoadMoreIfNecessary()
override public func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool {
Logger.debug("\(self.logTag) in \(#function)")
guard galleryDates.count > 0 else {
return false
}
switch indexPath.section {
case kLoadOlderSectionIdx, loadNewerSectionIdx:
return false
default:
return true
}
}
// MARK: MediaGalleryDataSourceDelegate
override public func collectionView(_ collectionView: UICollectionView, shouldDeselectItemAt indexPath: IndexPath) -> Bool {
func mediaGalleryDataSource(_ mediaGalleryDataSource: MediaGalleryDataSource, willDelete message: TSMessage) {
guard let collectionView = self.collectionView else {
owsFail("\(logTag) in \(#function) collectionView was unexpectedly nil")
return
Logger.debug("\(self.logTag) in \(#function)")
guard galleryDates.count > 0 else {
return false
}
// We've got to lay out the collectionView before any changes are made to the date source
// otherwise we'll fail when we try to remove the deleted sections/rows
collectionView.layoutIfNeeded()
switch indexPath.section {
case kLoadOlderSectionIdx, loadNewerSectionIdx:
return false
default:
return true
}
}
func mediaGalleryDataSource(_ mediaGalleryDataSource: MediaGalleryDataSource, deletedSections: IndexSet, deletedItems: [IndexPath]) {
guard let collectionView = self.collectionView else {
owsFail("\(logTag) in \(#function) collectionView was unexpetedly nil")
public override func collectionView(_ collectionView: UICollectionView, shouldHighlightItemAt indexPath: IndexPath) -> Bool {
Logger.debug("\(self.logTag) in \(#function)")
guard galleryDates.count > 0 else {
return false
}
switch indexPath.section {
case kLoadOlderSectionIdx, loadNewerSectionIdx:
return false
default:
return true
}
}
override public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
Logger.debug("\(self.logTag) in \(#function)")
guard let galleryCell = self.collectionView(collectionView, cellForItemAt: indexPath) as? MediaGalleryCell else {
owsFail("\(logTag) in \(#function) galleryCell was unexpectedly nil")
return
}
guard mediaGalleryDataSource.galleryItemCount > 0 else {
// Show Empty
self.collectionView?.reloadData()
guard let galleryItem = galleryCell.item else {
owsFail("\(logTag) in \(#function) galleryItem was unexpectedly nil")
return
}
// If collectionView hasn't been laid out yet, it won't have the sections/rows to remove.
collectionView.performBatchUpdates({
collectionView.deleteSections(deletedSections)
collectionView.deleteItems(at: deletedItems)
})
if isInBatchSelectMode {
updateDeleteButton()
} else {
collectionView.deselectItem(at: indexPath, animated: true)
self.delegate?.mediaTileViewController(self, didTapView: galleryCell.imageView, mediaGalleryItem: galleryItem)
}
}
public override func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
Logger.debug("\(self.logTag) in \(#function)")
if isInBatchSelectMode {
updateDeleteButton()
}
}
private var isUserScrolling: Bool = false {
didSet {
autoLoadMoreIfNecessary()
}
}
// MARK: UICollectionViewDataSource
@ -288,18 +355,8 @@ public class MediaTileViewController: UICollectionViewController, MediaGalleryCe
owsFail("\(logTag) in \(#function) unexpected cell for loadNewerSectionIdx")
return defaultCell
default:
guard let sectionDate = self.galleryDates[safe: indexPath.section - 1] else {
owsFail("\(logTag) in \(#function) unknown section: \(indexPath.section)")
return defaultCell
}
guard let sectionItems = self.galleryItems[sectionDate] else {
owsFail("\(logTag) in \(#function) no section for date: \(sectionDate)")
return defaultCell
}
guard let galleryItem = sectionItems[safe: indexPath.row] else {
owsFail("\(logTag) in \(#function) no message for row: \(indexPath.row)")
guard let galleryItem = galleryItem(at: indexPath) else {
owsFail("\(logTag) in \(#function) no message for path: \(indexPath)")
return defaultCell
}
@ -308,12 +365,31 @@ public class MediaTileViewController: UICollectionViewController, MediaGalleryCe
return defaultCell
}
cell.configure(item: galleryItem, delegate: self)
cell.configure(item: galleryItem)
return cell
}
}
func galleryItem(at indexPath: IndexPath) -> MediaGalleryItem? {
guard let sectionDate = self.galleryDates[safe: indexPath.section - 1] else {
owsFail("\(logTag) in \(#function) unknown section: \(indexPath.section)")
return nil
}
guard let sectionItems = self.galleryItems[sectionDate] else {
owsFail("\(logTag) in \(#function) no section for date: \(sectionDate)")
return nil
}
guard let galleryItem = sectionItems[safe: indexPath.row] else {
owsFail("\(logTag) in \(#function) no message for row: \(indexPath.row)")
return nil
}
return galleryItem
}
// MARK: UICollectionViewDelegateFlowLayout
public func collectionView(_ collectionView: UICollectionView,
@ -343,11 +419,160 @@ public class MediaTileViewController: UICollectionViewController, MediaGalleryCe
return kMonthHeaderSize
}
}
// MARK: MediaGalleryDelegate
fileprivate func didTapCell(_ cell: MediaGalleryCell, item: MediaGalleryItem) {
Logger.debug("\(logTag) in \(#function)")
self.delegate?.mediaTileViewController(self, didTapView: cell.imageView, mediaGalleryItem: item)
// MARK: Batch Selection
var isInBatchSelectMode = false {
didSet {
collectionView!.allowsMultipleSelection = isInBatchSelectMode
updateSelectButton()
updateDeleteButton()
}
}
func updateDeleteButton() {
guard let collectionView = self.collectionView else {
owsFail("\(logTag) in \(#function) collectionView was unexpectedly nil")
return
}
if let count = collectionView.indexPathsForSelectedItems?.count, count > 0 {
self.deleteButton.isEnabled = true
} else {
self.deleteButton.isEnabled = false
}
}
func updateSelectButton() {
if isInBatchSelectMode {
self.navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(didCancelSelect))
} else {
self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: NSLocalizedString("BUTTON_SELECT", comment: "Button text to enable batch selection mode"),
style: .plain,
target: self,
action: #selector(didTapSelect))
}
}
@objc
func didTapSelect(_ sender: Any) {
isInBatchSelectMode = true
// show toolbar
UIView.animate(withDuration: 0.1, delay: 0, options: .curveEaseInOut, animations: {
self.footerBarBottomConstraint.constant = 0
self.footerBar.superview?.layoutIfNeeded()
}, completion: nil)
// disabled until at least one item is selected
self.deleteButton.isEnabled = false
// Don't allow the user to leave mid-selection, so they realized they have
// to cancel (lose) their selection if they leave.
self.navigationItem.hidesBackButton = true
}
@objc
func didCancelSelect(_ sender: Any) {
isInBatchSelectMode = false
// hide toolbar
UIView.animate(withDuration: 0.1, delay: 0, options: .curveEaseInOut, animations: {
self.footerBarBottomConstraint.constant = self.footerBarHeight
self.footerBar.superview?.layoutIfNeeded()
}, completion: nil)
self.navigationItem.hidesBackButton = false
// deselect any selected
guard let collectionView = self.collectionView else {
owsFail("\(logTag) in \(#function) collectionView was unexpectedly nil")
return
}
collectionView.indexPathsForSelectedItems?.forEach { collectionView.deselectItem(at: $0, animated: false)}
}
@objc
func didPressDelete(_ sender: Any) {
Logger.debug("\(self.logTag) in \(#function)")
guard let collectionView = self.collectionView else {
owsFail("\(logTag) in \(#function) collectionView was unexpectedly nil")
return
}
guard let indexPaths = collectionView.indexPathsForSelectedItems else {
owsFail("\(logTag) in \(#function) indexPaths was unexpectedly nil")
return
}
let items: [MediaGalleryItem] = indexPaths.flatMap { return self.galleryItem(at: $0) }
guard let mediaGalleryDataSource = self.mediaGalleryDataSource else {
owsFail("\(logTag) in \(#function) mediaGalleryDataSource was unexpectedly nil")
return
}
let confirmationTitle: String = {
if indexPaths.count == 1 {
return NSLocalizedString("MEDIA_GALLERY_DELETE_SINGLE_MESSAGE", comment: "Confirmation button text to delete selected media message from the gallery")
} else {
let format = NSLocalizedString("MEDIA_GALLERY_DELETE_MULTIPLE_MESSAGES_FORMAT", comment: "Confirmation button text to delete selected media from the gallery, embeds {{number of messages}}")
return String(format: format, indexPaths.count)
}
}()
let deleteAction = UIAlertAction(title: confirmationTitle, style: .destructive) { _ in
mediaGalleryDataSource.delete(items: items)
}
let actionSheet = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
actionSheet.addAction(deleteAction)
actionSheet.addAction(OWSAlerts.cancelAction)
present(actionSheet, animated: true)
}
var footerBar: UIToolbar!
var deleteButton: UIBarButtonItem!
var footerBarBottomConstraint: NSLayoutConstraint!
var footerBarHeight: CGFloat {
// bottomLayoutGuide accomodates iPhoneX
return 40 + self.bottomLayoutGuide.length
}
// MARK: MediaGalleryDataSourceDelegate
func mediaGalleryDataSource(_ mediaGalleryDataSource: MediaGalleryDataSource, willDelete items: [MediaGalleryItem]) {
Logger.debug("\(self.logTag) in \(#function)")
guard let collectionView = self.collectionView else {
owsFail("\(logTag) in \(#function) collectionView was unexpectedly nil")
return
}
// We've got to lay out the collectionView before any changes are made to the date source
// otherwise we'll fail when we try to remove the deleted sections/rows
collectionView.layoutIfNeeded()
}
func mediaGalleryDataSource(_ mediaGalleryDataSource: MediaGalleryDataSource, deletedSections: IndexSet, deletedItems: [IndexPath]) {
Logger.debug("\(self.logTag) in \(#function) with deletedSections: \(deletedSections) deletedItems: \(deletedItems)")
guard let collectionView = self.collectionView else {
owsFail("\(logTag) in \(#function) collectionView was unexpetedly nil")
return
}
guard mediaGalleryDataSource.galleryItemCount > 0 else {
// Show Empty
self.collectionView?.reloadData()
return
}
collectionView.performBatchUpdates({
collectionView.deleteSections(deletedSections)
collectionView.deleteItems(at: deletedItems)
})
}
// MARK: Lazy Loading
@ -577,10 +802,6 @@ fileprivate class MediaGallerySectionHeader: UICollectionReusableView {
}
}
fileprivate protocol MediaGalleryCellDelegate: class {
func didTapCell(_ cell: MediaGalleryCell, item: MediaGalleryItem)
}
fileprivate class MediaGalleryStaticHeader: UICollectionViewCell {
static let reuseIdentifier = "MediaGalleryStaticHeader"
@ -616,40 +837,69 @@ fileprivate class MediaGalleryCell: UICollectionViewCell {
static let reuseIdentifier = "MediaGalleryCell"
public let imageView: UIImageView
private var tapGesture: UITapGestureRecognizer!
private let badgeView: UIImageView
private let contentTypeBadgeView: UIImageView
private let selectedBadgeView: UIImageView
private var item: MediaGalleryItem?
public weak var delegate: MediaGalleryCellDelegate?
private let highlightView: UIView
fileprivate var item: MediaGalleryItem?
static let videoBadgeImage = #imageLiteral(resourceName: "ic_gallery_badge_video")
static let animatedBadgeImage = #imageLiteral(resourceName: "ic_gallery_badge_gif")
static let selectedBadgeImage = #imageLiteral(resourceName: "selected_blue_circle")
override var isSelected: Bool {
didSet {
self.selectedBadgeView.isHidden = !self.isSelected
self.alpha = self.isSelected ? 0.8 : 1.0
}
}
override var isHighlighted: Bool {
didSet {
self.highlightView.isHidden = !self.isHighlighted
}
}
override init(frame: CGRect) {
self.imageView = UIImageView()
imageView.contentMode = .scaleAspectFill
self.badgeView = UIImageView()
badgeView.isHidden = true
self.contentTypeBadgeView = UIImageView()
contentTypeBadgeView.isHidden = true
super.init(frame: frame)
self.selectedBadgeView = UIImageView()
selectedBadgeView.image = MediaGalleryCell.selectedBadgeImage
selectedBadgeView.isHidden = true
self.tapGesture = UITapGestureRecognizer(target: self, action: #selector(didTap))
self.addGestureRecognizer(tapGesture)
self.highlightView = UIView()
highlightView.alpha = 0.2
highlightView.backgroundColor = .black
highlightView.isHidden = true
super.init(frame: frame)
self.clipsToBounds = true
self.contentView.addSubview(imageView)
self.contentView.addSubview(badgeView)
self.contentView.addSubview(contentTypeBadgeView)
self.contentView.addSubview(selectedBadgeView)
self.contentView.addSubview(highlightView)
imageView.autoPinEdgesToSuperviewEdges()
highlightView.autoPinEdgesToSuperviewEdges()
// Note assets were rendered to match exactly. We don't want to re-size with
// content mode lest they become less legible.
let kBadgeSize = CGSize(width: 18, height: 12)
badgeView.autoPinEdge(toSuperviewEdge: .leading, withInset: 3)
badgeView.autoPinEdge(toSuperviewEdge: .bottom, withInset: 3)
badgeView.autoSetDimensions(to: kBadgeSize)
let kContentTypeBadgeSize = CGSize(width: 18, height: 12)
contentTypeBadgeView.autoPinEdge(toSuperviewEdge: .leading, withInset: 3)
contentTypeBadgeView.autoPinEdge(toSuperviewEdge: .bottom, withInset: 3)
contentTypeBadgeView.autoSetDimensions(to: kContentTypeBadgeSize)
let kSelectedBadgeSize = CGSize(width: 31, height: 31)
selectedBadgeView.autoPinEdge(toSuperviewEdge: .trailing, withInset: 0)
selectedBadgeView.autoPinEdge(toSuperviewEdge: .bottom, withInset: 0)
selectedBadgeView.autoSetDimensions(to: kSelectedBadgeSize)
}
@available(*, unavailable, message: "Unimplemented")
@ -657,21 +907,19 @@ fileprivate class MediaGalleryCell: UICollectionViewCell {
fatalError("init(coder:) has not been implemented")
}
public func configure(item: MediaGalleryItem, delegate: MediaGalleryCellDelegate) {
public func configure(item: MediaGalleryItem) {
self.item = item
self.imageView.image = item.thumbnailImage
if item.isVideo {
self.badgeView.isHidden = false
self.badgeView.image = MediaGalleryCell.videoBadgeImage
self.contentTypeBadgeView.isHidden = false
self.contentTypeBadgeView.image = MediaGalleryCell.videoBadgeImage
} else if item.isAnimated {
self.badgeView.isHidden = false
self.badgeView.image = MediaGalleryCell.animatedBadgeImage
self.contentTypeBadgeView.isHidden = false
self.contentTypeBadgeView.image = MediaGalleryCell.animatedBadgeImage
} else {
assert(item.isImage)
self.badgeView.isHidden = true
self.contentTypeBadgeView.isHidden = true
}
self.delegate = delegate
}
override public func prepareForReuse() {
@ -679,18 +927,7 @@ fileprivate class MediaGalleryCell: UICollectionViewCell {
self.item = nil
self.imageView.image = nil
self.badgeView.isHidden = true
self.delegate = nil
}
// MARK: Events
func didTap(gestureRecognizer: UITapGestureRecognizer) {
guard let item = self.item else {
owsFail("\(logTag) item was unexpectedly nil")
return
}
self.delegate?.didTapCell(self, item: item)
self.contentTypeBadgeView.isHidden = true
self.selectedBadgeView.isHidden = true
}
}

@ -758,9 +758,10 @@ class MessageDetailViewController: OWSViewController, UIScrollViewDelegate, Medi
// MediaGalleryDataSourceDelegate
func mediaGalleryDataSource(_ mediaGalleryDataSource: MediaGalleryDataSource, willDelete message: TSMessage) {
func mediaGalleryDataSource(_ mediaGalleryDataSource: MediaGalleryDataSource, willDelete items: [MediaGalleryItem]) {
Logger.info("\(self.logTag) in \(#function)")
guard message == self.message else {
guard (items.map({ $0.message }) == [self.message]) else {
// Should only be one message we can delete when viewing message details
owsFail("\(logTag) in \(#function) Unexpectedly informed of irrelevant message deletion")
return

@ -262,6 +262,9 @@
/* Label for generic done button. */
"BUTTON_DONE" = "Done";
/* Button text to enable batch selection mode */
"BUTTON_SELECT" = "Select";
/* Alert message when calling and permissions for microphone are missing */
"CALL_AUDIO_PERMISSION_MESSAGE" = "Signal requires access to your microphone to make calls and record voice messages. You can grant this permission in the Settings app.";
@ -983,6 +986,12 @@
/* media picker option to choose from library */
"MEDIA_FROM_LIBRARY_BUTTON" = "Photo Library";
/* Confirmation button text to delete selected media from the gallery, embeds {{number of messages}} */
"MEDIA_GALLERY_DELETE_MULTIPLE_MESSAGES_FORMAT" = "Delete %d Messages";
/* Confirmation button text to delete selected media message from the gallery */
"MEDIA_GALLERY_DELETE_SINGLE_MESSAGE" = "Delete Message";
/* Short sender label for media sent by you */
"MEDIA_GALLERY_SENDER_NAME_YOU" = "You";

Loading…
Cancel
Save