Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ private class StickerPackActionButton: UIView {

// MARK: -

public class ManageStickersViewController: OWSTableViewController2 {
public class ManageStickersViewController: OWSTableViewController2, UITableViewDragDelegate, UITableViewDropDelegate {

typealias DatedStickerPackInfo = StickerManager.DatedStickerPackInfo

Expand All @@ -74,6 +74,10 @@ public class ManageStickersViewController: OWSTableViewController2 {
object: nil,
)

tableView.dragDelegate = self
tableView.dropDelegate = self
tableView.dragInteractionEnabled = true

defaultSeparatorInsetLeading = Self.cellHInnerMargin + iconSize + iconSpacing

updateState()
Expand All @@ -100,6 +104,8 @@ public class ManageStickersViewController: OWSTableViewController2 {
}
}

private var suppressNextPacksDidChange = false

private lazy var updateEvent: DebouncedEvent = {
DebouncedEvents.build(
mode: .firstLast,
Expand Down Expand Up @@ -152,9 +158,9 @@ public class ManageStickersViewController: OWSTableViewController2 {
let packsWithCovers = allPacks.filter {
StickerManager.isStickerInstalled(stickerInfo: $0.coverInfo, transaction: transaction)
}
// Sort sticker packs by "date saved, descending" so that we feature
// packs that the user has just learned about.
installedStickerPacks = packsWithCovers.filter { $0.isInstalled }
installedStickerPacks = StickerManager.orderedInstalledStickerPacks(transaction: transaction).filter {
StickerManager.isStickerInstalled(stickerInfo: $0.coverInfo, transaction: transaction)
}
availableBuiltInStickerPacks = packsWithCovers.filter {
!$0.isInstalled && StickerManager.isDefaultStickerPack(packId: $0.info.packId)
}
Expand Down Expand Up @@ -183,9 +189,7 @@ public class ManageStickersViewController: OWSTableViewController2 {
return source
}

self.installedStickerPackSources = installedStickerPacks.sorted {
$0.dateCreated > $1.dateCreated
}.map {
self.installedStickerPackSources = installedStickerPacks.map {
installedSource($0.info)
}
self.availableBuiltInStickerPackSources = availableBuiltInStickerPacks.sorted {
Expand Down Expand Up @@ -309,6 +313,79 @@ public class ManageStickersViewController: OWSTableViewController2 {
needsTableUpdate = false
}

private var installedSectionIndex: Int { 0 }

private func isInstalledSection(_ indexPath: IndexPath) -> Bool {
indexPath.section == installedSectionIndex && !installedStickerPackSources.isEmpty
}

private func persistInstalledPackOrder() {
let orderedInfos = installedStickerPackSources.compactMap { $0.info }
suppressNextPacksDidChange = true
StickerManager.updateInstalledStickerPackOrder(orderedPackInfos: orderedInfos)
}

// MARK: - UITableViewDragDelegate

public func tableView(_ tableView: UITableView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {
guard isInstalledSection(indexPath), indexPath.row < installedStickerPackSources.count else {
return []
}
guard let info = installedStickerPackSources[indexPath.row].info else {
return []
}

let provider = NSItemProvider(object: info.packId.hexadecimalString as NSString)
let item = UIDragItem(itemProvider: provider)
item.localObject = info
return [item]
}

// MARK: - UITableViewDropDelegate

public func tableView(_ tableView: UITableView, dropSessionDidUpdate session: UIDropSession, withDestinationIndexPath destinationIndexPath: IndexPath?) -> UITableViewDropProposal {
guard session.localDragSession != nil else {
return UITableViewDropProposal(operation: .cancel)
}

let destination = destinationIndexPath ?? IndexPath(row: installedStickerPackSources.count, section: installedSectionIndex)
guard destination.section == installedSectionIndex else {
return UITableViewDropProposal(operation: .cancel)
}

return UITableViewDropProposal(operation: .move, intent: .insertAtDestinationIndexPath)
}

public func tableView(_ tableView: UITableView, performDropWith coordinator: UITableViewDropCoordinator) {
guard let item = coordinator.items.first,
let sourceIndexPath = item.sourceIndexPath,
isInstalledSection(sourceIndexPath)
else {
return
}

let destinationIndexPath = coordinator.destinationIndexPath
?? IndexPath(row: installedStickerPackSources.count - 1, section: installedSectionIndex)
guard destinationIndexPath.section == installedSectionIndex else {
return
}

var destinationRow = destinationIndexPath.row
destinationRow = min(max(destinationRow, 0), installedStickerPackSources.count - 1)

let moved = installedStickerPackSources.remove(at: sourceIndexPath.row)
installedStickerPackSources.insert(moved, at: destinationRow)
persistInstalledPackOrder()

tableView.performBatchUpdates({
tableView.moveRow(
at: sourceIndexPath,
to: IndexPath(row: destinationRow, section: installedSectionIndex),
)
})
coordinator.drop(item.dragItem, toRowAt: IndexPath(row: destinationRow, section: installedSectionIndex))
}

private func buildTableCell(installedStickerPack dataSource: StickerPackDataSource) -> UITableViewCell {
return buildTableCell(dataSource: dataSource, actionIconName: "reply-fill") { [weak self] in
guard let packInfo = dataSource.info else {
Expand Down Expand Up @@ -555,6 +632,10 @@ public class ManageStickersViewController: OWSTableViewController2 {
func packsDidChange() {
AssertIsOnMainThread()

if suppressNextPacksDidChange {
suppressNextPacksDidChange = false
return
}
needsStateUpdate = true
}
}
Expand Down
78 changes: 78 additions & 0 deletions SignalServiceKit/Messages/Stickers/StickerManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ public class StickerManager: NSObject {

public static let store = KeyValueStore(collection: "recentStickers")
public static let emojiMapStore = KeyValueStore(collection: "emojiMap")
private static let installedPackOrderStore = KeyValueStore(collection: "installedStickerPackOrder")
private static let installedPackOrderKey = "installedStickerPackOrder"

public enum InstallMode: Int {
case doNotInstall
Expand Down Expand Up @@ -192,6 +194,64 @@ public class StickerManager: NSObject {
}
}

public class func orderedInstalledStickerPacks(transaction: DBReadTransaction) -> [StickerPackRecord] {
let packs = installedStickerPacks(transaction: transaction)
guard !packs.isEmpty else { return [] }

let orderedIds = installedPackOrderStore.orderedUniqueArray(
forKey: installedPackOrderKey,
tx: transaction,
)
guard !orderedIds.isEmpty else {
return packs.sorted { $0.dateCreated > $1.dateCreated }
}

var packById = [String: StickerPackRecord]()
packById.reserveCapacity(packs.count)
for pack in packs {
packById[pack.info.packId.hexadecimalString] = pack
}

var ordered = [StickerPackRecord]()
ordered.reserveCapacity(packs.count)
var used = Set<String>()

for packId in orderedIds {
if let pack = packById[packId] {
ordered.append(pack)
used.insert(packId)
}
}

let remaining = packs.filter { !used.contains($0.info.packId.hexadecimalString) }
.sorted { $0.dateCreated > $1.dateCreated }
ordered.append(contentsOf: remaining)

return ordered
}

public class func setInstalledStickerPackOrder(
orderedPackInfos: [StickerPackInfo],
transaction: DBWriteTransaction,
) {
let orderedIds = orderedPackInfos.map { $0.packId.hexadecimalString }
installedPackOrderStore.setStringArray(
orderedIds,
key: installedPackOrderKey,
transaction: transaction,
)
}

public class func updateInstalledStickerPackOrder(orderedPackInfos: [StickerPackInfo]) {
SSKEnvironment.shared.databaseStorageRef.write { transaction in
setInstalledStickerPackOrder(
orderedPackInfos: orderedPackInfos,
transaction: transaction,
)
}
packsDidChangeEvent.requestNotify()
}

public class func isStickerPackSaved(stickerPackInfo: StickerPackInfo) -> Bool {
return SSKEnvironment.shared.databaseStorageRef.read { transaction in
return isStickerPackSaved(stickerPackInfo: stickerPackInfo, transaction: transaction)
Expand Down Expand Up @@ -239,6 +299,12 @@ public class StickerManager: NSObject {
)
}

installedPackOrderStore.removeFromOrderedUniqueArray(
key: installedPackOrderKey,
value: stickerPackInfo.packId.hexadecimalString,
tx: transaction,
)

transaction.addSyncCompletion {
packsDidChangeEvent.requestNotify()
}
Expand Down Expand Up @@ -387,6 +453,7 @@ public class StickerManager: NSObject {
}

stickerPack.updateWith(isInstalled: true, tx: transaction)
ensurePackInInstalledOrder(stickerPack.info, transaction: transaction)

let promise = installStickerPackContents(stickerPack: stickerPack, transaction: transaction)

Expand All @@ -400,6 +467,17 @@ public class StickerManager: NSObject {
return promise
}

private class func ensurePackInInstalledOrder(
_ packInfo: StickerPackInfo,
transaction: DBWriteTransaction,
) {
installedPackOrderStore.prependToOrderedUniqueArray(
key: installedPackOrderKey,
value: packInfo.packId.hexadecimalString,
tx: transaction,
)
}

private class func installStickerPackContents(
stickerPack: StickerPackRecord,
transaction: DBReadTransaction,
Expand Down
4 changes: 1 addition & 3 deletions SignalUI/Stickers/StickerPickerView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -462,9 +462,7 @@ private class StickerPickerPageView: UIView {
let oldStickerPacks = stickerPacks

SSKEnvironment.shared.databaseStorageRef.read { transaction in
self.stickerPacks = StickerManager.installedStickerPacks(transaction: transaction).sorted {
$0.dateCreated > $1.dateCreated
}
self.stickerPacks = StickerManager.orderedInstalledStickerPacks(transaction: transaction)
}

// These go (via delegate) as source data to the toolbar.
Expand Down