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 }