mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-11-14 06:57:51 +00:00
* ios, android, desktop: previews draw fixed sample data instead of calling the core * desktop: database error preview passes the keychain setting * ios: snapshot fixtures use fixed parsed markdown instead of the core * ios: snapshot fixtures: raw string delimiters past the # in markdown JSON * desktop: snapshot test sets the app bar height as the app does * docs: plan to replace preview-only parameters with test measures * ios, desktop: previews run master's code; badges and version views split Preview-only parameters are removed. BadgesCheckOrderView, BadgesHowLongView and VersionView are split into a loader and a layout, which their previews draw. Snapshot tests set a fixed QR picture: the environment value fixedQRCode on iOS, LocalFixedQRCode on desktop. * ios: snapshot fixtures build parsed markdown with initializers The table held the JSON of the Linux core, which the iOS app does not decode. The values are those the core returns for each text. * ci: references of fixed QR codes and version; ios: call overlay preview as on master iOS references from https://github.com/evgeny-simplex/simplex-chat/actions/runs/40944118370, desktop references from https://github.com/evgeny-simplex/simplex-chat/actions/runs/40932566864. ActiveCallOverlay_Previews, its scene and its references are restored as on master. * ios, desktop: snapshot tests render every preview, no exclusion lists BadgesChooseBadgeView, ServersSummaryView and TerminalView are split into a loader and a layout. AddChannelView, AddGroupView, GroupProfileView and UserProfileView layouts take the name and description validators, so desktop previews render without the core library. NewChatView's preview opens the Connect tab, UserProfilesView's preview has its own users. AppearanceSettings, MigrateFromDevice and the duplicate UserProfileLayoutEditOn previews are deleted. * ios, desktop: previews show short links; tests draw the fixed QR code with each app's generator The snapshot tests draw the fixed QR picture for a short address link with generateImage on iOS and qrCodeBitmap on desktop, in place of committed images. Previews of group links, addresses, a pending connection and QR codes pass short links, as the apps show them. The fixture markdown table equals the output of master's parser. * ios, desktop: excluded previews and their views as on master * ci: references of short-link QR codes
149 lines
5.1 KiB
Swift
149 lines
5.1 KiB
Swift
//
|
|
// QRCode.swift
|
|
// SimpleX
|
|
//
|
|
// Created by Evgeny Poberezkin on 30/01/2022.
|
|
// Copyright © 2022 SimpleX Chat. All rights reserved.
|
|
//
|
|
// Spec: spec/client/navigation.md
|
|
|
|
import SwiftUI
|
|
import CoreImage.CIFilterBuiltins
|
|
import SimpleXChat
|
|
|
|
struct MutableQRCode: View {
|
|
@Binding var uri: String
|
|
var small: Bool = false
|
|
var withLogo: Bool = true
|
|
var tintColor = UIColor(red: 0.023, green: 0.176, blue: 0.337, alpha: 1)
|
|
|
|
var body: some View {
|
|
QRCode(uri: uri, small: small, withLogo: withLogo, tintColor: tintColor)
|
|
.id("simplex-qrcode-view-for-\(uri)")
|
|
}
|
|
}
|
|
|
|
struct SimpleXCreatedLinkQRCode: View {
|
|
let link: CreatedConnLink
|
|
@Binding var short: Bool
|
|
var onShare: (() -> Void)? = nil
|
|
|
|
var body: some View {
|
|
QRCode(uri: link.simplexChatUri(short: short), small: short && link.connShortLink != nil, onShare: onShare)
|
|
}
|
|
}
|
|
|
|
struct SimpleXLinkQRCode: View {
|
|
let uri: String
|
|
var withLogo: Bool = true
|
|
var tintColor = UIColor(red: 0.023, green: 0.176, blue: 0.337, alpha: 1)
|
|
var onShare: (() -> Void)? = nil
|
|
|
|
var body: some View {
|
|
QRCode(uri: simplexChatLink(uri), small: uri.count < 200, withLogo: withLogo, tintColor: tintColor, onShare: onShare)
|
|
}
|
|
}
|
|
|
|
private let smallQRRatio: CGFloat = 0.63
|
|
|
|
extension EnvironmentValues {
|
|
struct FixedQRCode: EnvironmentKey {
|
|
static let defaultValue: UIImage? = nil
|
|
}
|
|
|
|
var fixedQRCode: UIImage? {
|
|
get { self[FixedQRCode.self] }
|
|
set { self[FixedQRCode.self] = newValue }
|
|
}
|
|
}
|
|
|
|
struct QRCode: View {
|
|
let uri: String
|
|
var small: Bool = false
|
|
var withLogo: Bool = true
|
|
var tintColor = UIColor(red: 0.023, green: 0.176, blue: 0.337, alpha: 1)
|
|
var onShare: (() -> Void)? = nil
|
|
@Environment(\.fixedQRCode) private var fixedQRCode
|
|
@State private var image: UIImage? = nil
|
|
@State private var makeScreenshotFunc: () -> Void = {}
|
|
@State private var width: CGFloat = .infinity
|
|
|
|
var body: some View {
|
|
ZStack {
|
|
if let image = fixedQRCode ?? image {
|
|
qrCodeImage(image).frame(width: width, height: width)
|
|
GeometryReader { g in
|
|
let w = g.size.width * (small ? smallQRRatio : 1)
|
|
let l = w * (small ? 0.195 : 0.16)
|
|
let m = w * 0.005
|
|
ZStack {
|
|
if withLogo {
|
|
Image("icon-light")
|
|
.resizable()
|
|
.scaledToFit()
|
|
.frame(width: l, height: l)
|
|
.frame(width: l + m, height: l + m)
|
|
.background(.white)
|
|
.clipShape(Circle())
|
|
}
|
|
}
|
|
.onAppear {
|
|
width = w
|
|
makeScreenshotFunc = {
|
|
let size = CGSizeMake(1024 / UIScreen.main.scale, 1024 / UIScreen.main.scale)
|
|
showShareSheet(items: [makeScreenshot(g.frame(in: .local).origin, size)])
|
|
onShare?()
|
|
}
|
|
}
|
|
.frame(width: g.size.width, height: g.size.height)
|
|
}
|
|
} else {
|
|
Color.clear.aspectRatio(small ? 1 / smallQRRatio : 1, contentMode: .fit)
|
|
}
|
|
}
|
|
.onTapGesture(perform: makeScreenshotFunc)
|
|
.task { if fixedQRCode == nil { image = await generateImage(uri, tintColor: tintColor, errorLevel: small ? "M" : "L") } }
|
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
|
}
|
|
}
|
|
|
|
private func qrCodeImage(_ image: UIImage) -> some View {
|
|
Image(uiImage: image)
|
|
.resizable()
|
|
.interpolation(.none)
|
|
.aspectRatio(1, contentMode: .fit)
|
|
.textSelection(.enabled)
|
|
}
|
|
|
|
func generateImage(_ uri: String, tintColor: UIColor, errorLevel: String) async -> UIImage? {
|
|
let context = CIContext()
|
|
let filter = CIFilter.qrCodeGenerator()
|
|
filter.message = Data(uri.utf8)
|
|
filter.correctionLevel = errorLevel
|
|
if let outputImage = filter.outputImage,
|
|
let cgImage = context.createCGImage(outputImage, from: outputImage.extent) {
|
|
return UIImage(cgImage: cgImage).replaceColor(UIColor.black, tintColor)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
extension View {
|
|
func makeScreenshot(_ origin: CGPoint? = nil, _ targetSize: CGSize? = nil) -> UIImage {
|
|
let controller = UIHostingController(rootView: self.edgesIgnoringSafeArea(.all))
|
|
let targetSize = targetSize ?? controller.view.intrinsicContentSize
|
|
let view = controller.view
|
|
view?.bounds = CGRect(origin: origin ?? .zero, size: targetSize)
|
|
view?.backgroundColor = .clear
|
|
let renderer = UIGraphicsImageRenderer(size: targetSize)
|
|
return renderer.image { _ in
|
|
view?.drawHierarchy(in: controller.view.bounds, afterScreenUpdates: true)
|
|
}
|
|
}
|
|
}
|
|
|
|
struct QRCode_Previews: PreviewProvider {
|
|
static var previews: some View {
|
|
QRCode(uri: "https://smp6.simplex.im/a#lrdvu2d8A1GumSmoKb2krQmtKhWXq-tyGpHuM7aMwsw", small: true)
|
|
}
|
|
}
|