# CutBox.app

## Coverage Report

#### Created: 2024-03-12 03:40

    .../Source/App/Services/History/HistoryRepo.swift

    Line

    Count

    Source (jump to first uncovered line)

    1

    //

    2

    //  HistoryRepo.swift

    3

    //  CutBox

    4

    //

    5

    //  Created by Jason Milkins on 29/4/18.

    6

    //  Copyright © 2018-2023 ocodo. All rights reserved.

    7

    //

    8

    9

    import Foundation

    10

    11

    class HistoryRepo {

    12

    13

        /// Clip items store (cached from/to defaults by history service)

    14

    103

        private var store: [[String: String]] = []

    15

    16

        /// dict keys of clip items

    17

    103

        private var historyStoreKey = Constants.kHistoryStoreKey

    18

    103

        private var stringKey = Constants.kStringKey

    19

    103

        private var favoriteKey = Constants.kFavoriteKey

    20

    103

        private var timestampKey = Constants.kTimestampKey

    21

    22

        /// Optional time filter

    23

        var timeFilter: Double?

    24

    25

        /// preferences defaults key for protect favorites flag

    26

        private var kProtectFavorites = "protectFavorites"

    27

    28

        /// Standalone predicate, false for item older than an earliest timestamp

    29

    12

        func timeFilterPredicate(item: [String: String], earliest: String) -> Bool {

    30

    12

            if let timestamp = item[self.timestampKey] {

    31

    12

                return earliest < timestamp

    32

    12

            } else {

    33

    0

                return false

    34

    0

            }

    35

    0

        }

    36

    37

    516

        var items: [String] {

    38

    516

            if let seconds = self.timeFilter {

    39

    4

                let latest = secondsBeforeTimeNowAsISO8601(seconds: seconds)

    40

    4

                return self.dict

    41

    10

                    .filter { timeFilterPredicate(item: $0, earliest: latest) }

    42

    4

                    .map { $0[self.stringKey]! }

    43

    512

            }

    44

    512

    45

    512

            return self.dict

    46

    3.71k

              .map { $0[self.stringKey]! }

    47

    516

        }

    48

    49

    342

        var favorites: [String] {

    50

    342

            return self.dict

    51

    2.86k

                .filter { $0[favoriteKey] == self.favoriteKey }

    52

    342

                .map { $0[self.stringKey]! }

    53

    342

        }

    54

    55

    3

        var favoritesDict: [[String: String]] {

    56

    3

            return self.dict

    57

    40

                .filter { $0[favoriteKey] == self.favoriteKey }

    58

    3

        }

    59

    60

    1.22k

        var dict: [[String: String]] {

    61

    1.22k

            return self.store

    62

    1.22k

        }

    63

    64

        private var defaults: UserDefaults

    65

        private var prefs: CutBoxPreferencesService

    66

    67

        init(defaults: UserDefaults = UserDefaults.standard,

    68

    103

             prefs: CutBoxPreferencesService = CutBoxPreferencesService.shared) {

    69

    103

            self.defaults = defaults

    70

    103

            self.prefs = prefs

    71

    103

        }

    72

    73

    4

        func clear() {

    74

    4

            self.store.removeAll(protectFavorites: prefs.protectFavorites)

    75

    4

            self.saveToDefaults()

    76

    4

        }

    77

    78

    50

        func index(of string: String) -> Int? {

    79

    50

            return items.firstIndex(of: string)

    80

    50

        }

    81

    82

    1.45k

        func insert(_ newElement: String, at index: Int = 0, isFavorite: Bool = false, date: Date? = Date()) {

    83

    1.45k

            var item = [stringKey: newElement]

    84

    1.45k

    85

    1.45k

            if let unwrappedDate = date {

    86

    1.44k

                let timestamp = iso8601Timestamp(fromDate: unwrappedDate)

    87

    1.44k

                item[self.timestampKey] = timestamp

    88

    1.45k

            }

    89

    1.45k

    90

    1.45k

            if isFavorite {

    91

    6

                item[self.favoriteKey] = self.favoriteKey

    92

    1.45k

            }

    93

    1.45k

    94

    1.45k

            self.store.insert(item, at: index)

    95

    1.45k

        }

    96

    97

    4

        func remove(at: Int) {

    98

    4

            if at >= 0 && self.store.count > at {

    99

    4

                self.store.remove(at: at)

    100

    4

                self.saveToDefaults()

    101

    4

            }

    102

    4

        }

    103

    104

    3

        func removeAtIndexes(indexes: IndexSet) {

    105

    3

            self.store.removeAtIndexes(indexes: indexes)

    106

    3

            self.saveToDefaults()

    107

    3

        }

    108

    109

    335

        func findIndexSetOf(string: String) -> IndexSet {

    110

    335

            var indexes = IndexSet()

    111

    2.84k

            for item in dict where item[stringKey] == string {

    112

    2.84k

                if let index = dict.firstIndex(of: item) {

    113

    1

                    indexes.insert(index)

    114

    2.84k

                }

    115

    2.84k

            }

    116

    335

            return indexes

    117

    335

        }

    118

    119

    5

        func toggleFavorite(indexes: IndexSet) {

    120

    11

            for i in indexes {

    121

    11

                toggleFavorite(at: i)

    122

    11

            }

    123

    5

            self.saveToDefaults()

    124

    5

        }

    125

    126

    14

        func toggleFavorite(at i: Int) {

    127

    14

            if self.store[i].keys.contains(favoriteKey) {

    128

    6

                self.store[i].removeValue(forKey: favoriteKey)

    129

    14

            } else {

    130

    8

                self.store[i][favoriteKey] = favoriteKey

    131

    14

            }

    132

    14

        }

    133

    134

    4

        func removeSubrange(_ bounds: Range<Int>) {

    135

    4

            self.store.removeSubrange(bounds)

    136

    4

            self.saveToDefaults()

    137

    4

        }

    138

    139

    13

        func migrateLegacyPasteStore(_ newElements: [String], at: Int = 0) {

    140

    50

            newElements.reversed().forEach {

    141

    50

                if index(of: $0) == nil {

    142

    36

                    self.insert($0)

    143

    50

                }

    144

    50

            }

    145

    13

        }

    146

    147

        /// Used to read user defaults to in memory history store

    148

    93

        func loadFromDefaults() {

    149

    93

            let key = self.historyStoreKey

    150

    93

            if let historyStore = self.defaults.array(forKey: key) as? [[String: String]] {

    151

    55

                self.store = historyStore

    152

    93

            } else {

    153

    38

                self.store = []

    154

    93

            }

    155

    93

        }

    156

    157

        /// Used to persist in memory history store to user defaults

    158

    372

        func saveToDefaults() {

    159

    372

            self.defaults.set(self.store, forKey: self.historyStoreKey)

    160

    372

        }

    161

    162

        /// removes the historyStore completely

    163

    4

        func clearHistory() {

    164

    4

            self.clear()

    165

    4

        }

    166

    167

        /// Clear the history using a timestampPredicate

    168

        ///

    169

        /// Favorites are protected when `prefs.protectFavorites` is enabled

    170

        ///

    171

        /// Items without a timestamp will be ignored.

    172

        ///

    173

        /// see also: `historyOffsetPredicateFactory(offset: TimeInterval?)`

    174

    5

        func clearHistory(timestampPredicate: (String) -> Bool) {

    175

    13

            let keep = self.store.filter {

    176

    13

                if $0[favoriteKey] != nil {

    177

    3

                    if prefs.protectFavorites {

    178

    2

                        return true

    179

    2

                    }

    180

    11

                }

    181

    11

    182

    11

                if let timestamp: String = $0[timestampKey] {

    183

    9

                    return !timestampPredicate(timestamp)

    184

    9

                }

    185

    2

    186

    2

                return true

    187

    11

            }

    188

    5

    189

    5

            self.store = keep

    190

    5

            saveToDefaults()

    191

    5

        }

    192

    193

        /// Size of history repo in bytes

    194

    51

        func bytes() throws -> Int {

    195

    51

            let data = try PropertyListSerialization.data(

    196

    51

                fromPropertyList: store,

    197

    51

                format: .binary,

    198

    51

                options: .bitWidth)

    199

    51

    200

    51

            return data.count

    201

    51

        }

    202

    203

        /// Bytes formatter

    204

    51

        func bytesFormatted() -> String {

    205

    51

            do {

    206

    51

                let i: Int = try bytes()

    207

    51

                let byteFormatter = ByteCountFormatter()

    208

    51

                byteFormatter.allowedUnits = [.useGB, .useMB, .useKB]

    209

    51

                return byteFormatter.string(fromByteCount: Int64(i))

    210

    51

            } catch {

    211

    0

                return ""

    212

    0

            }

    213

    0

        }

    214

    }