taler-ios

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

CopyShare.swift (7157B)


      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     let image: UIImage?
     68     let vertical: Bool
     69     let title: String?
     70 
     71     @Environment(\.isEnabled) private var isEnabled: Bool
     72     @EnvironmentObject private var controller: Controller
     73 
     74     @State private var scale: CGFloat = 1.0
     75 
     76     init(textToCopy: String, vertical: Bool, image: UIImage? = nil) {
     77         self.textToCopy = textToCopy
     78         self.image = image
     79         self.vertical = vertical
     80         self.title = nil
     81     }
     82 
     83     init(textToCopy: String, title: String, image: UIImage? = nil) {
     84         self.textToCopy = textToCopy
     85         self.image = image
     86         self.vertical = false
     87         self.title = title
     88     }
     89 
     90     func copyAction() -> Void {
     91         symLog.log(textToCopy)
     92         triggerPulse()
     93         controller.hapticFeedback(.medium)
     94         let strings = [UTType.plainText.identifier : textToCopy]
     95         var items: [[String : Any]] = [strings]
     96         if let image {
     97             let images = [UTType.image.identifier : image]
     98             items.append(images)
     99         }
    100         UIPasteboard.general.setItems(items)
    101     }
    102 
    103     private func triggerPulse() {
    104         // First scale down quickly
    105         withAnimation(.easeIn(duration: 0.1)) {
    106             scale = 0.8
    107         }
    108         // Then bounce back bigger
    109         DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
    110             withAnimation(.easeOut(duration: 0.25)) {
    111                 scale = 1.15
    112             }
    113         }
    114         // Finally settle to normal
    115         DispatchQueue.main.asyncAfter(deadline: .now() + 0.35) {
    116             withAnimation(.easeInOut(duration: 0.25)) {
    117                 scale = 1.0
    118             }
    119         }
    120     }
    121 
    122     var body: some View {
    123         Button(action: copyAction) {
    124             let image = Image(systemName: COPY1)    // 􀉁
    125                 .accessibility(hidden: true)
    126 
    127             if vertical {
    128                 VStack {
    129                     let shortCopy = String(localized: "Copy.short", defaultValue: "Copy", comment: "5 letters max, else abbreviate")
    130                     image
    131                     Text(shortCopy)
    132                 }
    133             } else {
    134                 let longCopy = String(localized: "Copy.long", defaultValue: "Copy", comment: "may be a bit longer")
    135                 HStack {
    136                     image
    137                     Text(title ?? longCopy)
    138                 }
    139             }
    140         }
    141         .tint(.accentColor)
    142         .talerFont(.body)
    143         .scaleEffect(scale)
    144         .disabled(!isEnabled)
    145     }
    146 }
    147 // MARK: -
    148 struct ShareType: Hashable {
    149     let textToShare: String
    150     let image: UIImage?
    151 }
    152 // MARK: -
    153 @MainActor
    154 struct ShareButton: View {
    155     private let symLog = SymLogV(0)
    156     let textToShare: String
    157     let image: UIImage?
    158     let title: String
    159 
    160     @State private var scale: CGFloat = 1.0
    161 
    162     init(textToShare: String, image: UIImage? = nil) {
    163         self.textToShare = textToShare
    164         self.image = image
    165         self.title = String(localized: "Share")
    166     }
    167     init(textToShare: String, title: String, image: UIImage? = nil) {
    168         self.textToShare = textToShare
    169         self.image = image
    170         self.title = title
    171     }
    172 
    173     @Environment(\.isEnabled) private var isEnabled: Bool
    174     @EnvironmentObject private var controller: Controller
    175 
    176     @MainActor
    177     func dismissAndPost() {
    178         dismissTop()
    179         let shareType = ShareType(textToShare: textToShare, image: image)
    180         let userInfo = [NOTIFICATIONSHARE: shareType]
    181         NotificationCenter.default.post(name: .ShareAction, object: nil, userInfo: userInfo)                // will trigger NavigationLink
    182     }
    183 
    184     func shareAction() -> Void {
    185         symLog.log(textToShare)
    186         controller.hapticFeedback(.soft)
    187         Task {
    188             // First scale down quickly
    189             withAnimation(.easeIn(duration: 0.1)) {
    190                 scale = 0.8
    191             }
    192             // Then bounce back bigger
    193             DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
    194                 withAnimation(.easeOut(duration: 0.25)) {
    195                     scale = 1.15
    196                 }
    197 //              ShareSheet.shareSheet(textToShare: textToShare, image: image)
    198                 dismissAndPost()
    199             }
    200             // Finally settle to normal
    201             DispatchQueue.main.asyncAfter(deadline: .now() + 0.35) {
    202                 withAnimation(.easeInOut(duration: 0.25)) {
    203                     scale = 1.0
    204                 }
    205             }
    206         }
    207     }
    208 
    209     var body: some View {
    210         Button(action: shareAction) {
    211             HStack {
    212                 Image(systemName: SHARE)        // 􀈂
    213                     .accessibility(hidden: true)
    214                 Text(title)
    215             }
    216         }
    217         .tint(.accentColor)
    218         .talerFont(.body)
    219         .scaleEffect(scale)
    220         .disabled(!isEnabled)
    221     }
    222 }
    223 // MARK: -
    224 struct CopyShare: View {
    225     @Environment(\.isEnabled) private var isEnabled: Bool
    226 
    227     let textToCopy: String
    228     let image: UIImage?
    229 
    230     var body: some View {
    231         HStack {
    232             CopyButton(textToCopy: textToCopy, vertical: false, image: image)
    233                 .buttonStyle(TalerButtonStyle(type: .bordered))
    234             ShareButton(textToShare: textToCopy, image: image)
    235                 .buttonStyle(TalerButtonStyle(type: .bordered))
    236         } // two buttons
    237     }
    238 }
    239 // MARK: -
    240 struct CopyShare_Previews: PreviewProvider {
    241     static var previews: some View {
    242         CopyShare(textToCopy: "Hallö", image: nil)
    243     }
    244 }