diff --git a/.github/download-app-store.svg b/.github/download-app-store.svg new file mode 100755 index 00000000..73e72b54 --- /dev/null +++ b/.github/download-app-store.svg @@ -0,0 +1,46 @@ + + Download on the App Store + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 18c91471..62772993 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -59,8 +59,8 @@ representative at an online or offline event. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported to the community leaders responsible for enforcement at -. +reported to the community leaders responsible for enforcement: [@aaronhma](https://github.com/aaronhma), [@Visual-Studio-Coder](https://github.com/Visual-Studio-Coder). + All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of the @@ -116,7 +116,7 @@ the community. This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0, available at -https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. +. Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity). @@ -124,5 +124,5 @@ enforcement ladder](https://github.com/mozilla/diversity). [homepage]: https://www.contributor-covenant.org For answers to common questions about this code of conduct, see the FAQ at -https://www.contributor-covenant.org/faq. Translations are available at -https://www.contributor-covenant.org/translations. +. Translations are available at +. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..ab0922e3 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,17 @@ +# Contributing + +## We 💖 Open Source + +Thank you for taking the time to improve the best QR code app on the App Store! + +- [💡 Feature Request ↗](https://github.com/Visual-Studio-Coder/QR-Share-Pro/issues/new?assignees=&labels=&projects=&template=feature_request.md&title=) +- [⌨️ Contribute ↗](https://github.com/Visual-Studio-Coder/QR-Share-Pro/pulls) +- [🐞 Report a Bug ↗](https://github.com/Visual-Studio-Coder/QR-Share-Pro/issues/new?assignees=&labels=&projects=&template=bug_report.md&title=) + +## Got some Swift programming experience? + +Here's some of the design choices for our app: + +### [QRSharePro](QRSharePro/) - main app + +### [Share](Share/) - share sheet app extension diff --git a/QRSharePro/Home.swift b/QRSharePro/Home.swift index 9e46be1a..59493fac 100644 --- a/QRSharePro/Home.swift +++ b/QRSharePro/Home.swift @@ -7,7 +7,7 @@ struct Home: View { @EnvironmentObject var qrCodeStore: QRCodeStore @Environment(\.colorScheme) var colorScheme @Environment(\.requestReview) var requestReview - + @State private var showingAboutAppSheet = false @State private var text = "" @State private var textIsEmptyWithAnimation = true @@ -17,16 +17,16 @@ struct Home: View { @State private var showPermissionsError = false @State private var qrCodeImage: UIImage = UIImage() @State private var animatedText = "" - + let fullText = "Start typing to\ngenerate a QR code..." let timer = Timer.publish(every: 0.05, on: .main, in: .common).autoconnect() - + @FocusState private var isFocused - + @ObservedObject var accentColorManager = AccentColorManager.shared - + private var allIcons: [AppIcon] = [AppIcon(iconURL: "AppIcon", iconName: "Sky Blue (Default)"), AppIcon(iconURL: "AppIcon2", iconName: "Terminal Green"), AppIcon(iconURL: "AppIcon3", iconName: "Holographic Pink")] - + private func changeColor(to iconName: String) { switch iconName { case "AppIcon2": @@ -37,44 +37,44 @@ struct Home: View { AccentColorManager.shared.accentColor = Color(#colorLiteral(red: 0.3860174716, green: 0.7137812972, blue: 0.937712729, alpha: 1)) } } - + private func changeAppIcon(to iconURL: String) { let iconName = iconURL == "AppIcon" ? nil : iconURL - + UIApplication.shared.setAlternateIconName(iconName) { error in if let error = error { print(error.localizedDescription) } } - + changeColor(to: iconName ?? "AppIcon") } - + let context = CIContext() let filter = CIFilter.qrCodeGenerator() - + func save() async throws { qrCodeStore.save(history: qrCodeStore.history) } - + func generateQRCode(from string: String) { let data = Data(string.utf8) filter.setValue(data, forKey: "inputMessage") - + if let qrCode = filter.outputImage { let transform = CGAffineTransform(scaleX: 10, y: 10) let scaledQrCode = qrCode.transformed(by: transform) - + if let cgImage = context.createCGImage(scaledQrCode, from: scaledQrCode.extent) { qrCodeImage = UIImage(cgImage: cgImage) } } } - + var appVersion: String { (Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String) ?? "1.0" } - + var body: some View { NavigationStack { ScrollView { @@ -89,10 +89,10 @@ struct Home: View { ShareLink(item: Image(uiImage: qrCodeImage), preview: SharePreview(text, image: Image(uiImage: qrCodeImage))) { Label("Share QR Code", systemImage: "square.and.arrow.up") } - + Divider() } - + Button { if text.count > 3000 { showExceededLimitAlert = true @@ -103,10 +103,10 @@ struct Home: View { } else { UIImageWriteToSavedPhotosAlbum(qrCodeImage, nil, nil, nil) showSavedAlert = true - + let newCode = QRCode(text: text, originalURL: text, qrCode: qrCodeImage.pngData()) qrCodeStore.history.append(newCode) - + Task { do { try await save() @@ -114,7 +114,7 @@ struct Home: View { print(error.localizedDescription) } } - + showHistorySavedAlert = true } } @@ -122,14 +122,14 @@ struct Home: View { } label: { Label("Save to Photos", systemImage: "square.and.arrow.down") } - + Button { if text.count > 3000 { showExceededLimitAlert = true } else { let newCode = QRCode(text: text, originalURL: text, qrCode: qrCodeImage.pngData()) qrCodeStore.history.append(newCode) - + Task { do { try await save() @@ -137,7 +137,7 @@ struct Home: View { print(error.localizedDescription) } } - + showHistorySavedAlert = true } } label: { @@ -153,7 +153,7 @@ struct Home: View { .bold() .onReceive(timer) { _ in let hapticGenerator = UIImpactFeedbackGenerator(style: .light) - + if animatedText.count < fullText.count { animatedText.append(fullText[fullText.index(fullText.startIndex, offsetBy: animatedText.count)]) hapticGenerator.impactOccurred() @@ -163,17 +163,17 @@ struct Home: View { } } } - + HStack { Spacer() - + Text("\(text.count)/3000 characters") .foregroundStyle(text.count > 3000 ? .red : .secondary) .bold() } .padding(.top, 3) .padding(.trailing) - + TextField("Create your own QR code...", text: $text) .focused($isFocused) .padding() @@ -184,7 +184,7 @@ struct Home: View { .autocorrectionDisabled() .onChange(of: text) { newValue in generateQRCode(from: newValue) - + withAnimation { textIsEmptyWithAnimation = newValue.isEmpty } @@ -194,14 +194,14 @@ struct Home: View { } .onSubmit { isFocused = false - + if text.count > 3000 { showExceededLimitAlert = true } } .padding(.horizontal) .padding(.bottom, 5) - + Menu { Button { if text.count > 3000 { @@ -213,10 +213,10 @@ struct Home: View { } else { UIImageWriteToSavedPhotosAlbum(qrCodeImage, nil, nil, nil) showSavedAlert = true - + let newCode = QRCode(text: text, originalURL: text, qrCode: qrCodeImage.pngData()) qrCodeStore.history.append(newCode) - + Task { do { try await save() @@ -230,14 +230,14 @@ struct Home: View { } label: { Label("Save to Photos", systemImage: "square.and.arrow.down") } - + Button { if text.count > 3000 { showExceededLimitAlert = true } else { let newCode = QRCode(text: text, originalURL: text, qrCode: qrCodeImage.pngData()) qrCodeStore.history.append(newCode) - + Task { do { try await save() @@ -245,7 +245,7 @@ struct Home: View { print(error.localizedDescription) } } - + showHistorySavedAlert = true } } label: { @@ -283,17 +283,17 @@ struct Home: View { } .alert("Saved to History!", isPresented: $showHistorySavedAlert) { } - + .toolbar { ToolbarItem(placement: .topBarLeading) { let qrCodeImage = Image(uiImage: qrCodeImage) - + ShareLink(item: qrCodeImage, preview: SharePreview(text, image: qrCodeImage)) { Label("Share", systemImage: "square.and.arrow.up") } .disabled(text.isEmpty) } - + ToolbarItem(placement: .topBarTrailing) { Button { showingAboutAppSheet = true @@ -313,17 +313,17 @@ struct Home: View { .frame(width: 50, height: 50) .clipShape(RoundedRectangle(cornerRadius: 16)) .shadow(color: .accentColor, radius: 5) - + VStack(alignment: .leading) { Text("QR Share Pro") .bold() - + Text("Version \(appVersion)") .foregroundStyle(.secondary) } - + Spacer() - + Image(systemName: "square.and.arrow.up") .font(.title) .bold() @@ -331,7 +331,7 @@ struct Home: View { } .tint(.primary) } - + Button { if let url = URL(string: "https://github.com/Visual-Studio-Coder/QR-Share-Pro/blob/master/PRIVACY.md") { UIApplication.shared.open(url) @@ -345,7 +345,7 @@ struct Home: View { } } .tint(.primary) - + Button { requestReview() } label: { @@ -358,7 +358,7 @@ struct Home: View { } .tint(.primary) } - + Section("App Icon & Theme") { ForEach(allIcons) { i in Button { @@ -369,20 +369,20 @@ struct Home: View { Image(systemName: i.iconURL == (UserDefaults.standard.string(forKey: "appIcon") ?? "AppIcon") ? "checkmark.circle.fill" : "circle") .font(.title2) .tint(.accentColor) - + Image(uiImage: #imageLiteral(resourceName: i.iconURL)) .resizable() .frame(width: 50, height: 50) .clipShape(RoundedRectangle(cornerRadius: 16)) .shadow(radius: 50) - + Text(i.iconName) .tint(.primary) } } } } - + Section("Credits") { Button { if let url = URL(string: "https://github.com/Visual-Studio-Coder") { @@ -399,7 +399,7 @@ struct Home: View { } } .tint(.primary) - + Button { if let url = URL(string: "https://aaronhma.com") { UIApplication.shared.open(url) @@ -413,7 +413,7 @@ struct Home: View { } } .tint(.primary) - + Button { if let url = URL(string: "https://github.com/Visual-Studio-Coder/QR-Share-Pro/graphs/contributors") { UIApplication.shared.open(url) @@ -428,7 +428,7 @@ struct Home: View { } .tint(.primary) } - + Section("Product Improvement") { Button { if let url = URL(string: "https://github.com/Visual-Studio-Coder/QR-Share-Pro/issues/new?assignees=&labels=&projects=&template=feature_request.md&title=") { @@ -443,9 +443,9 @@ struct Home: View { } } .tint(.primary) - + Button { - if let url = URL(string: "https://github.com/Visual-Studio-Coder/QR-Share-Pro") { + if let url = URL(string: "https://github.com/Visual-Studio-Coder/QR-Share-Pro/pulls") { UIApplication.shared.open(url) } } label: { @@ -457,7 +457,7 @@ struct Home: View { } } .tint(.primary) - + Button { if let url = URL(string: "https://github.com/Visual-Studio-Coder/QR-Share-Pro/issues/new?assignees=&labels=&projects=&template=bug_report.md&title=") { UIApplication.shared.open(url) @@ -485,7 +485,7 @@ struct Home: View { .tint(accentColorManager.accentColor) } } - + ToolbarItemGroup(placement: .keyboard) { Button("Clear") { text = "" @@ -512,7 +512,7 @@ struct Home: View { #Preview { Group { @StateObject var qrCodeStore = QRCodeStore() - + Home() .environmentObject(qrCodeStore) } diff --git a/README.md b/README.md index fe12ccc1..232cacb2 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,29 @@ -# QR Share Pro - QR codes done *right*. -Get ready to revolutionize the way you share links and text with QR Share Pro! Say goodbye to tedious copying and pasting. With QR Share Pro, you can instantly convert web URLs and text into sleek, scannable QR codes directly from the share menu. +# QR Share Pro - QR codes done *right* -And with our built-in scanner, you can quickly scan any QR code while we automatically unshorten any URLs that hide or obscure the end destination link. We also remove all trackers in links for added privacy and peace of mind. +## Created by [Aaron Ma](https://aaronhma.com/), [Vaibhav Satishkumar](https://github.com/Visual-Studio-Coder) -Organize all your created QR codes in one convenient place with the History tab, making it easy to access them whenever you need. Best of all, QR Share Pro is completely free and open-source, with no annoying ads or subscriptions to clutter your experience. +[![Download on the App Store](.github/download-app-store.svg)](https://apps.apple.com/us/app/qr-share-pro/id6479589995) + +***iPhone 8** or later running **iOS 16** or later required* + +## We 💖 Open Source + +Thank you for taking the time to improve the best QR code app on the App Store! + +- [💡 Feature Request ↗](https://github.com/Visual-Studio-Coder/QR-Share-Pro/issues/new?assignees=&labels=&projects=&template=feature_request.md&title=) +- [⌨️ Contribute ↗](https://github.com/Visual-Studio-Coder/QR-Share-Pro/pulls) +- [🐞 Report a Bug ↗](https://github.com/Visual-Studio-Coder/QR-Share-Pro/issues/new?assignees=&labels=&projects=&template=bug_report.md&title=) + +Got some Swift programming experience? [Learn more about contributing →](CONTRIBUTING.md) + +## About + +**TL;DR: The only QR code app on the store that cares about your privacy. No creepy data selling, 100% free, no ads, no in-app purchases. [Learn more →](PRIVACY.md)** + +QR Share Pro is the next-generation way to scan, share, and generate QR codes. We offer a quick method to generate codes from anywhere on your phone by simply clicking on the share menu and tapping our icon. You will be presented with a beautiful QR code that is as versatile as it looks. It is even added to your history tab after you generate it. + +QR Share Pro also features a versatile scanning function that safely unshortens URLs in QR Codes. Most QR codes contain tracking links such as [https://qrco.de](https://qrco.de), which actually redirect you to the real site, typically after collecting data such as your IP address and other metrics. By unshortening these links, we show you exactly where the QR code is actually taking you, protecting you from phishing scams. We also eliminate URL trackers (such as google analytics) from URLs before unshortening to provide users the maximum privacy. + +The history feature is also very useful. Every time you create or scan a QR code using the app, we promptly add it to your history. You can even access the location where you scanned the code, which can be extremely helpful. + +The best part about all of this? We take the best efforts to make sure your information stays private on-device, so nobody else knows what you're doing. [Learn more →](PRIVACY.md)