taler-ios

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

CopyShare.swift (6692B)


      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 @MainActor
    149 struct ShareButton: View {
    150     private let symLog = SymLogV(0)
    151     let textToShare: String
    152     let image: UIImage?
    153     let title: String
    154 
    155     @State private var scale: CGFloat = 1.0
    156 
    157     init(textToShare: String, image: UIImage? = nil) {
    158         self.textToShare = textToShare
    159         self.image = image
    160         self.title = String(localized: "Share")
    161     }
    162     init(textToShare: String, title: String, image: UIImage? = nil) {
    163         self.textToShare = textToShare
    164         self.image = image
    165         self.title = title
    166     }
    167 
    168     @Environment(\.isEnabled) private var isEnabled: Bool
    169     @EnvironmentObject private var controller: Controller
    170 
    171     func shareAction() -> Void {
    172         symLog.log(textToShare)
    173         controller.hapticFeedback(.soft)
    174         Task {
    175             // First scale down quickly
    176             withAnimation(.easeIn(duration: 0.1)) {
    177                 scale = 0.8
    178             }
    179             // Then bounce back bigger
    180             DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
    181                 withAnimation(.easeOut(duration: 0.25)) {
    182                     scale = 1.15
    183                 }
    184                 ShareSheet.shareSheet(textToShare: textToShare, image: image)
    185             }
    186             // Finally settle to normal
    187             DispatchQueue.main.asyncAfter(deadline: .now() + 0.35) {
    188                 withAnimation(.easeInOut(duration: 0.25)) {
    189                     scale = 1.0
    190                 }
    191             }
    192         }
    193     }
    194 
    195     var body: some View {
    196         Button(action: shareAction) {
    197             HStack {
    198                 Image(systemName: SHARE)        // 􀈂
    199                     .accessibility(hidden: true)
    200                 Text(title)
    201             }
    202         }
    203         .tint(.accentColor)
    204         .talerFont(.body)
    205         .scaleEffect(scale)
    206         .disabled(!isEnabled)
    207     }
    208 }
    209 // MARK: -
    210 struct CopyShare: View {
    211     @Environment(\.isEnabled) private var isEnabled: Bool
    212 
    213     let textToCopy: String
    214     let image: UIImage?
    215 
    216     var body: some View {
    217         HStack {
    218             CopyButton(textToCopy: textToCopy, vertical: false, image: image)
    219                 .buttonStyle(TalerButtonStyle(type: .bordered))
    220             ShareButton(textToShare: textToCopy, image: image)
    221                 .buttonStyle(TalerButtonStyle(type: .bordered))
    222         } // two buttons
    223     }
    224 }
    225 // MARK: -
    226 struct CopyShare_Previews: PreviewProvider {
    227     static var previews: some View {
    228         CopyShare(textToCopy: "Hallö", image: nil)
    229     }
    230 }