taler-ios

iOS apps for GNU Taler (wallet)
Log | Files | Refs | README | LICENSE

CopyShare.swift (7390B)


      1 /*
      2  * This file is part of GNU Taler, ©2022-25 Taler Systems S.A.
      3  * See LICENSE.md
      4  */
      5 /**
      6  * @author Marc Stibane
      7  */
      8 import UniformTypeIdentifiers
      9 import SwiftUI
     10 import SymLog
     11 
     12 @MainActor
     13 struct FeedbackButton: View {
     14     private let symLog = SymLogV(0)
     15     let title: String?
     16     let image: UIImage?
     17     let isDisabled: Bool
     18     let action: () -> Void
     19 
     20     @EnvironmentObject private var controller: Controller
     21     @State private var scale: CGFloat = 1.0
     22 
     23     public init(_ title: String? = nil,
     24                   image: UIImage? = nil,
     25                disabled: Bool = false,
     26                  action: @escaping @MainActor () -> Void) {
     27         self.title = title
     28         self.image = image
     29         self.isDisabled = disabled
     30         self.action = action
     31     }
     32 
     33     private func triggerPulse() {
     34         // First scale down quickly
     35         withAnimation(.easeIn(duration: 0.1)) {
     36             scale = 0.8
     37         }
     38         // Then bounce back bigger
     39         DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
     40             withAnimation(.easeOut(duration: 0.25)) {
     41                 scale = 1.15
     42             }
     43         }
     44         // Finally settle to normal
     45         DispatchQueue.main.asyncAfter(deadline: .now() + 0.35) {
     46             withAnimation(.easeInOut(duration: 0.25)) {
     47                 scale = 1.0
     48             }
     49         }
     50     }
     51 
     52     var body: some View {
     53         Button(title ?? EMPTYSTRING) {
     54             symLog.log(title ?? EMPTYSTRING)
     55             controller.hapticFeedback(.medium)
     56             action()
     57             triggerPulse()
     58         }
     59             .buttonStyle(TalerButtonStyle(type: .bordered, disabled: isDisabled))
     60     }
     61 }
     62 // MARK: -
     63 @MainActor
     64 struct CopyButton: View {
     65     private let symLog = SymLogV(0)
     66     let textToCopy: String
     67     @Binding var isCopied: Bool
     68     let image: UIImage?
     69     let vertical: Bool
     70     let title: String?
     71 
     72     @Environment(\.isEnabled) private var isEnabled: Bool
     73     @EnvironmentObject private var controller: Controller
     74 
     75     @State private var scale: CGFloat = 1.0
     76 
     77     init(textToCopy: String, isCopied: Binding<Bool>, vertical: Bool, image: UIImage? = nil) {
     78         self.textToCopy = textToCopy
     79         self._isCopied = isCopied
     80         self.image = image
     81         self.vertical = vertical
     82         self.title = nil
     83     }
     84 
     85     init(textToCopy: String, isCopied: Binding<Bool>, title: String, image: UIImage? = nil) {
     86         self.textToCopy = textToCopy
     87         self._isCopied = isCopied
     88         self.image = image
     89         self.vertical = false
     90         self.title = title
     91     }
     92 
     93     func copyAction() -> Void {
     94         symLog.log(textToCopy)
     95         triggerPulse()
     96         controller.hapticFeedback(.medium)
     97         let strings = [UTType.plainText.identifier : textToCopy]
     98         var items: [[String : Any]] = [strings]
     99         if let image {
    100             let images = [UTType.image.identifier : image]
    101             items.append(images)
    102         }
    103         UIPasteboard.general.setItems(items)
    104         isCopied = true
    105     }
    106 
    107     private func triggerPulse() {
    108         // First scale down quickly
    109         withAnimation(.easeIn(duration: 0.1)) {
    110             scale = 0.8
    111         }
    112         // Then bounce back bigger
    113         DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
    114             withAnimation(.easeOut(duration: 0.25)) {
    115                 scale = 1.15
    116             }
    117         }
    118         // Finally settle to normal
    119         DispatchQueue.main.asyncAfter(deadline: .now() + 0.35) {
    120             withAnimation(.easeInOut(duration: 0.25)) {
    121                 scale = 1.0
    122             }
    123         }
    124     }
    125 
    126     var body: some View {
    127         Button(action: copyAction) {
    128             let image = Image(systemName: COPY1)    // 􀉁
    129                 .accessibility(hidden: true)
    130 
    131             if vertical {
    132                 VStack {
    133                     let shortCopy = String(localized: "Copy.short", defaultValue: "Copy", comment: "5 letters max, else abbreviate")
    134                     image
    135                     Text(shortCopy)
    136                 }
    137             } else {
    138                 let longCopy = String(localized: "Copy.long", defaultValue: "Copy", comment: "may be a bit longer")
    139                 HStack {
    140                     image
    141                     Text(title ?? longCopy)
    142                 }
    143             }
    144         }
    145         .tint(.accentColor)
    146         .talerFont(.body)
    147         .scaleEffect(scale)
    148         .disabled(!isEnabled)
    149     }
    150 }
    151 // MARK: -
    152 struct ShareType: Hashable {
    153     let textToShare: String
    154     let image: UIImage?
    155 }
    156 // MARK: -
    157 @MainActor
    158 struct ShareButton: View {
    159     private let symLog = SymLogV(0)
    160     let textToShare: String
    161     let image: UIImage?
    162     let title: String
    163 
    164     @State private var scale: CGFloat = 1.0
    165 
    166     init(textToShare: String, image: UIImage? = nil) {
    167         self.textToShare = textToShare
    168         self.image = image
    169         self.title = String(localized: "Share")
    170     }
    171     init(textToShare: String, title: String, image: UIImage? = nil) {
    172         self.textToShare = textToShare
    173         self.image = image
    174         self.title = title
    175     }
    176 
    177     @Environment(\.isEnabled) private var isEnabled: Bool
    178     @EnvironmentObject private var controller: Controller
    179 
    180     @MainActor
    181     func dismissAndPost() {
    182         dismissTop()
    183         let shareType = ShareType(textToShare: textToShare, image: image)
    184         let userInfo = [NOTIFICATIONSHARE: shareType]
    185         NotificationCenter.default.post(name: .ShareAction, object: nil, userInfo: userInfo)                // will trigger NavigationLink
    186     }
    187 
    188     func shareAction() -> Void {
    189         symLog.log(textToShare)
    190         controller.hapticFeedback(.soft)
    191         Task {
    192             // First scale down quickly
    193             withAnimation(.easeIn(duration: 0.1)) {
    194                 scale = 0.8
    195             }
    196             // Then bounce back bigger
    197             DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
    198                 withAnimation(.easeOut(duration: 0.25)) {
    199                     scale = 1.15
    200                 }
    201 //              ShareSheet.shareSheet(textToShare: textToShare, image: image)
    202                 dismissAndPost()
    203             }
    204             // Finally settle to normal
    205             DispatchQueue.main.asyncAfter(deadline: .now() + 0.35) {
    206                 withAnimation(.easeInOut(duration: 0.25)) {
    207                     scale = 1.0
    208                 }
    209             }
    210         }
    211     }
    212 
    213     var body: some View {
    214         Button(action: shareAction) {
    215             HStack {
    216                 Image(systemName: SHARE)        // 􀈂
    217                     .accessibility(hidden: true)
    218                 Text(title)
    219             }
    220         }
    221         .tint(.accentColor)
    222         .talerFont(.body)
    223         .scaleEffect(scale)
    224         .disabled(!isEnabled)
    225     }
    226 }
    227 // MARK: -
    228 struct CopyShare: View {
    229     @Environment(\.isEnabled) private var isEnabled: Bool
    230     @State var isCopied: Bool = false
    231 
    232     let textToCopy: String
    233     let image: UIImage?
    234 
    235     var body: some View {
    236         HStack {
    237             CopyButton(textToCopy: textToCopy, isCopied: $isCopied, vertical: false, image: image)
    238                 .buttonStyle(TalerButtonStyle(type: .bordered))
    239             ShareButton(textToShare: textToCopy, image: image)
    240                 .buttonStyle(TalerButtonStyle(type: .bordered))
    241         } // two buttons
    242     }
    243 }
    244 // MARK: -
    245 struct CopyShare_Previews: PreviewProvider {
    246     static var previews: some View {
    247         CopyShare(textToCopy: "Hallö", image: nil)
    248     }
    249 }