Commit 7ebc2ad9 by macmini

1.0.3 新增评论

parent f665c00b
{
"originHash" : "e06cc7db9b9551175f0dfeb86241ef403abd4de96001d5f30f1a7735f60b6bfa",
"pins" : [
{
"identity" : "alamofire",
"kind" : "remoteSourceControl",
"location" : "https://github.com/Alamofire/Alamofire.git",
"state" : {
"revision" : "513364f870f6bfc468f9d2ff0a95caccc10044c5",
"version" : "5.10.2"
}
},
{
"identity" : "cryptoswift",
"kind" : "remoteSourceControl",
"location" : "https://github.com/krzyzanowskim/CryptoSwift.git",
"state" : {
"revision" : "e45a26384239e028ec87fbcc788f513b67e10d8f",
"version" : "1.9.0"
}
},
{
"identity" : "objectmapper",
"kind" : "remoteSourceControl",
"location" : "https://github.com/Hearst-DD/ObjectMapper.git",
"state" : {
"revision" : "6021c6035e83a306047348666f6400dc61445d3b",
"version" : "4.4.3"
}
}
],
"version" : 3
}
...@@ -14,10 +14,10 @@ ...@@ -14,10 +14,10 @@
filePath = "Toolboxes/ContentView.swift" filePath = "Toolboxes/ContentView.swift"
startingColumnNumber = "9223372036854775807" startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807"
startingLineNumber = "150" startingLineNumber = "179"
endingLineNumber = "150" endingLineNumber = "179"
landmarkName = "previews" landmarkName = "unknown"
landmarkType = "24"> landmarkType = "0">
</BreakpointContent> </BreakpointContent>
</BreakpointProxy> </BreakpointProxy>
</Breakpoints> </Breakpoints>
......
{
"images" : [
{
"filename" : "haoping.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "haoping@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "haoping@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
...@@ -12,6 +12,7 @@ struct ContentView: View { ...@@ -12,6 +12,7 @@ struct ContentView: View {
@State private var showOverlay = false @State private var showOverlay = false
@State private var selectedTab: Int = 0 @State private var selectedTab: Int = 0
@State private var showRatingAlert = false
// @State private var _showHorizontalGlobe = State<Bool>(initialValue: { // @State private var _showHorizontalGlobe = State<Bool>(initialValue: {
// if let show = UserDefaults.standard.object(forKey: "showHorizontalGlobe") as? Bool { // if let show = UserDefaults.standard.object(forKey: "showHorizontalGlobe") as? Bool {
// return show // return show
...@@ -85,6 +86,14 @@ struct ContentView: View { ...@@ -85,6 +86,14 @@ struct ContentView: View {
.navigationViewStyle(StackNavigationViewStyle()) // 无论设备如何,都保持一致 .navigationViewStyle(StackNavigationViewStyle()) // 无论设备如何,都保持一致
.navigationBarTitle("") .navigationBarTitle("")
.onAppear(){ .onAppear(){
RateTool.check { isSuccess in
RateTool.fetchConfig { success in
if success {
showRateAlert()
}
}
}
MapViewManager.shared.load() MapViewManager.shared.load()
} }
.overlay{ .overlay{
...@@ -94,10 +103,30 @@ struct ContentView: View { ...@@ -94,10 +103,30 @@ struct ContentView: View {
PopupView(isShowing: $showOverlay) PopupView(isShowing: $showOverlay)
.ignoresSafeArea() .ignoresSafeArea()
} }
if showRatingAlert{
RatingAlertView(isPresented: $showRatingAlert)
}
} }
} }
} }
/// 执行第二天的方法(显示评价弹窗)
private func executeMethodOnSecondDay() {
// 显示评价弹窗
showRatingAlert = true;
}
/// 显示评价弹窗(带判断逻辑)
private func showRateAlert() {
// 检查是否已经显示过评价弹窗
if UserDefaults.standard.bool(forKey: "eva_isshowed") {
return
}
executeMethodOnSecondDay()
}
} }
// 自定义 TabBar 项目 // 自定义 TabBar 项目
......
//
// Config.swift
// Toolboxes
//
// Created by 研发2组 on 2025/12/4.
//
import Foundation
import ObjectMapper
/// Pub配置项
struct PubItem {
var event: String = ""
var chance: String = ""
}
extension PubItem: Mappable {
init?(map: Map) {
}
mutating func mapping(map: Map) {
event <- map["event"]
chance <- map["chance"]
}
}
/// 配置模型
struct Config {
var code: Int = 0
var msg: String = ""
var pub: [PubItem] = []
var isSuccess:Bool{
code == 1
}
}
extension Config: Mappable {
init?(map: Map) {
}
mutating func mapping(map: Map) {
code <- map["code"]
msg <- map["msg"]
pub <- map["pub"]
}
}
extension Config: Parsable {
static func parse(json: [String: Any]) -> Config? {
return Mapper<Config>().map(JSON: json)
}
}
...@@ -5,10 +5,17 @@ ...@@ -5,10 +5,17 @@
// Created by 研发2组 on 2024/10/11. // Created by 研发2组 on 2024/10/11.
// //
import SwiftUI
extension String {
import Foundation
import Security
extension String {
/// 尝试解密响应数据
var decrypt: String? {
guard let aesData = AES.dataWithBase64EncodedString(base64EncodedString: self),
let decryptedString = AES.aes256DecryptWithData(data: aesData, key: SecretKey.primary.encryptKey) else {
return nil
}
return decryptedString
}
} }
import SwiftUI import SwiftUI
struct CompassView: View { struct CompassView: View {
@Binding var showPopup: Bool @Binding var showPopup: Bool
...@@ -17,6 +16,7 @@ struct CompassView: View { ...@@ -17,6 +16,7 @@ struct CompassView: View {
@State private var currentHeading: Double = 0.0 @State private var currentHeading: Double = 0.0
@State private var previousHeading: Double = 0.0 @State private var previousHeading: Double = 0.0
var body: some View { var body: some View {
ZStack{
VStack(spacing: 40) { VStack(spacing: 40) {
HStack { HStack {
...@@ -152,8 +152,15 @@ struct CompassView: View { ...@@ -152,8 +152,15 @@ struct CompassView: View {
Spacer() Spacer()
} }
}
.gradientBackground() .gradientBackground()
.onAppear { .onAppear {
showHorizontalGlobe = UserDefaults.standard.bool(forKey: "showHorizontalGlobe") showHorizontalGlobe = UserDefaults.standard.bool(forKey: "showHorizontalGlobe")
showAuxiliaryLine = UserDefaults.standard.bool(forKey: "showAuxiliaryLine") showAuxiliaryLine = UserDefaults.standard.bool(forKey: "showAuxiliaryLine")
currentHeading = compassManager.heading currentHeading = compassManager.heading
......
//
// SecretKey.swift
// HYVvppn
//
// Created by Mr. Liu on 2025/7/31.
//
import Foundation
// 定义密钥类型枚举
enum KeyType {
case encryption // 加密密钥
case decrypt // 解密密钥
case salt // 解密密钥
}
// 密钥管理枚举
enum SecretKey {
case primary
// 获取对应类型的密钥值
func value(for type: KeyType) -> String {
switch self {
case .primary:
switch type {
case .encryption:
return "Q8bkWigT"
case .decrypt:
return "ljda_dj@.dv?sjdf"
case .salt:
return "2513Lii%$nash(ee)66"
}
}
}
// 便捷属性:直接获取加密密钥
var encryptKey: String {
value(for: .encryption)
}
// 便捷属性:直接获取解密密钥
var decryptKey: String {
value(for: .decrypt)
}
// 便捷属性:直接获取salt
var saltKey: String {
value(for: .salt)
}
}
import Foundation
import CommonCrypto
class AES {
static func aes256EncryptWithData(data: Data, key: String) -> Data? {
guard !key.isEmpty else { return nil }
var keyPtr = Array<CChar>(repeating: 0, count: kCCKeySizeAES256)
key.getCString(&keyPtr, maxLength: keyPtr.count, encoding: .utf8)
let dataLength = data.count
let bufferSize = dataLength + kCCBlockSizeAES128
var buffer = [UInt8](repeating: 0, count: bufferSize)
var numBytesEncrypted: size_t = 0
let cryptStatus = CCCrypt(
CCOperation(kCCEncrypt),
CCAlgorithm(kCCAlgorithmAES),
CCOptions(kCCOptionPKCS7Padding | kCCOptionECBMode),
keyPtr, kCCKeySizeAES256,
nil,
[UInt8](data), dataLength,
&buffer, bufferSize,
&numBytesEncrypted
)
guard cryptStatus == kCCSuccess else { return nil }
return Data(bytes: buffer, count: numBytesEncrypted)
}
static func aes256DecryptWithData(data: Data, key: String) -> String? {
guard !key.isEmpty else { return nil }
var keyPtr = Array<CChar>(repeating: 0, count: kCCKeySizeAES256)
key.getCString(&keyPtr, maxLength: keyPtr.count, encoding: .utf8)
let dataLength = data.count
let bufferSize = dataLength + kCCBlockSizeAES128
var buffer = [UInt8](repeating: 0, count: bufferSize)
var numBytesDecrypted: size_t = 0
let cryptStatus = CCCrypt(
CCOperation(kCCDecrypt),
CCAlgorithm(kCCAlgorithmAES),
CCOptions(kCCOptionPKCS7Padding | kCCOptionECBMode),
keyPtr, kCCKeySizeAES256,
nil,
[UInt8](data), dataLength,
&buffer, bufferSize,
&numBytesDecrypted
)
guard cryptStatus == kCCSuccess else { return nil }
let decryptedData = Data(bytes: buffer, count: numBytesDecrypted)
return String(data: decryptedData, encoding: .utf8)
}
static func aes256EncryptWithString(string: String, key: String) -> Data? {
guard !key.isEmpty else { return nil }
let data = string.data(using: .utf8)!
return aes256EncryptWithData(data: data, key: key)
}
static func AES256Encrypt(content: String, key: String) -> String? {
guard let resultData = aes256EncryptWithString(string: content, key: key) else { return nil }
return resultData.base64EncodedString()
}
static func base64EncodedStringWithData(data: Data) -> String {
return data.base64EncodedString()
}
static func dataWithBase64EncodedString(base64EncodedString: String) -> Data? {
return Data(base64Encoded: base64EncodedString)
}
static func base64EncodedStringWithString(str: String) -> String {
let data = str.data(using: .utf8)!
return base64EncodedStringWithData(data: data)
}
static func stringWithBase64EncodedString(base64EncodedString: String) -> String? {
guard let data = dataWithBase64EncodedString(base64EncodedString: base64EncodedString) else { return nil }
return String(data: data, encoding: .utf8)
}
/// 解密 Base64 编码的加密字符串为 Data
static func aes256DecryptBase64ToData(base64EncodedString: String, key: String) -> Data? {
// 将 Base64 编码的字符串转换为 Data
guard let encryptedData = vpn_Base64Decoder.dataWithBase64EncodedString(base64EncodedString) else {
return nil
}
// 解密 Data 并返回
guard let decryptedString = aes256DecryptWithData(data: encryptedData, key: key) else {
return nil
}
return decryptedString.data(using: .utf8)
}
}
//
// vpn_Base64Decoder.swift
// HYVvppn
//
// Created by Mr. Liu on 2025/1/18.
//
import Foundation
class vpn_Base64Decoder {
// Base64解码表
static let base64DecodingTable: [Int8] = {
var table = [Int8](repeating: -2, count: 256)
for (index, char) in "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".utf8.enumerated() {
table[Int(char)] = Int8(index)
}
table[61] = -1 // '=' ASCII值为61
return table
}()
/// 解码Base64字符串为Data
static func dataWithBase64EncodedString(_ base64EncodedString: String) -> Data? {
let length = base64EncodedString.count
let utf8String = Array(base64EncodedString.utf8)
guard !utf8String.isEmpty else {
return nil
}
// 去掉尾部的 '='
var trimmedLength = length
while trimmedLength > 0 && utf8String[trimmedLength - 1] == 61 { // '=' 的 ASCII 值是 61
trimmedLength -= 1
}
let outputLength = trimmedLength * 3 / 4
var output = [UInt8](repeating: 0, count: outputLength)
var inputPoint = 0
var outputPoint = 0
while inputPoint < trimmedLength {
guard let i0 = decodeCharacter(utf8String[inputPoint]),
let i1 = decodeCharacter(utf8String[inputPoint + 1]) else {
return nil
}
let i2 = inputPoint + 2 < trimmedLength ? decodeCharacter(utf8String[inputPoint + 2]) ?? 0 : 0
let i3 = inputPoint + 3 < trimmedLength ? decodeCharacter(utf8String[inputPoint + 3]) ?? 0 : 0
// 修复负值的问题,确保使用UInt8来处理
output[outputPoint] = UInt8((Int(i0) << 2) | (Int(i1) >> 4))
outputPoint += 1
if outputPoint < outputLength {
output[outputPoint] = UInt8(((Int(i1) & 0xF) << 4) | (Int(i2) >> 2))
outputPoint += 1
}
if outputPoint < outputLength {
output[outputPoint] = UInt8(((Int(i2) & 0x3) << 6) | Int(i3))
outputPoint += 1
}
inputPoint += 4
}
return Data(output)
}
/// 解码单个字符为对应的值
private static func decodeCharacter(_ char: UInt8) -> Int8? {
let decoded = base64DecodingTable[Int(char)]
return decoded >= 0 ? decoded : nil
}
}
import CryptoSwift
import Foundation
class MD5 {
// MD5 加密字符串
static func encrypt(string: String) -> String {
let data = string.data(using: .utf8)!
return data.md5().toHexString()
}
}
//
// RateTool.swift
// Toolboxes
//
// Created by 研发2组 on 2025/12/4.
//
import SwiftUI
import Foundation
import Alamofire
class RateTool:NSObject{
//检查网络
static func check(complation: @escaping (Bool) -> ()){
NetworkReachabilityManager.default?.startListening(onUpdatePerforming: { status in
if status == .reachable(.cellular) || status == .reachable(.ethernetOrWiFi){
complation(true)
}
else {
complation(false)
}
})
}
static func fetchConfig(complation: @escaping (Bool) -> ()){
let client = AlamofireClient()
client.config { config in
if let appconfig = config , appconfig.isSuccess {
// 检查pub数组中是否有event为"app_evaluate"且chance为"1"的项
let hasEvaluate = appconfig.pub.contains { item in
item.event == "app_evaluate" && item.chance == "1"
}
complation(hasEvaluate)
}
}
}
}
import Foundation
import Alamofire
public protocol Request {
var path: String { get }
var method: HTTPMethod { get }
var parameters: [String: Any] { get set }
associatedtype Response: Parsable
}
public enum AlamofireError: Error {
case jsonMapping
case errorCode
case connectionFaild
}
protocol Client {
var host: String { get }
func config(handler: @escaping (Config?) -> ())
}
struct AlamofireClient: Client {
let host = "http://common.yhlongtime.top/"
let key = "Q8bkWigT"
func config(handler: @escaping (Config?) -> ()) {
let urlString = "https://common.yhlongtime.top/pubv3/cSearchConfig.php"
let url = URL(string: urlString)!
let headers:HTTPHeaders = ["Content-type":"application/x-www-form-urlencoded",
"Accept":"application/json"]
AF.request(url, method: .post, parameters: ["user_pkname":"com.yhgc.Toolboxes"], headers: headers )
.responseString { (response) in
if response.value == nil{
return
}
if let json = response.value?.decrypt,
let decryptedData = json.data(using: .utf8) {
let jsonString = String(data: decryptedData, encoding: .utf8)
switch response.result {
case .success:
if jsonString == nil {
handler(nil)
return
}
if let jsonData = jsonString!.data(using: .utf8) {
do {
if let dictionary = try JSONSerialization.jsonObject(with: jsonData, options: []) as? [String: Any] {
print("\n---------config dictionary---------\n\(dictionary)")
// 使用Config的parse方法解析
if let config = Config.parse(json: dictionary) {
handler(config)
} else {
handler(nil)
}
} else {
handler(nil)
}
} catch {
print("解析错误:", error)
handler(nil)
}
} else {
handler(nil)
}
case .failure:
handler(nil)
}
return
}
}
}
}
import Foundation
public protocol Parsable {
static func parse(json: [String: Any]) -> Self?
}
//
// RatingAlertView.swift
// Toolboxes
//
// Created by 研发2组 on 2025/12/4.
//
import SwiftUI
import StoreKit
struct RatingAlertView: View {
// 控制弹窗显示状态
@Binding var isPresented: Bool
var body: some View {
ZStack {
// 背景(可选)
Color.gray.opacity(0.2)
.ignoresSafeArea()
VStack(spacing: 24) {
// 提示文字
Text("您的鼓励是我们最大的动力")
.font(.system(size: 17))
.foregroundColor(.black)
.padding(.horizontal, 20)
// 按钮区域
HStack(spacing: 20) {
// 拒绝评价按钮
Button(action: {
isPresented = false
UserDefaults.standard.set(true, forKey: "eva_isshowed")
UserDefaults.standard.synchronize()
}) {
Text("拒绝评价")
.font(.system(size: 16, weight: .medium))
.foregroundColor(.white)
.frame(height: 48)
.frame(maxWidth: .infinity)
.background(Color(hex: "#BBBCBE"))
.cornerRadius(8)
}
// 马上评价按钮
Button(action: {
isPresented = false
// 在这里添加评价逻辑
UserDefaults.standard.set(true, forKey: "eva_isshowed")
UserDefaults.standard.synchronize()
// 可以在这里跳转到App Store评价页面
self.openAppStoreForRating()
}) {
Text("马上评价")
.font(.system(size: 16, weight: .medium))
.foregroundColor(.white)
.frame(height: 48)
.frame(maxWidth: .infinity)
.background(Color.blue)
.cornerRadius(8)
}
}
.padding(.horizontal, 20)
}
.padding(.top, 80)
.padding(.vertical, 30)
.background(Color.white)
.cornerRadius(16)
.shadow(radius: 10)
.frame(maxWidth: 300)
.frame(height: 224)
// 占位图标(可替换为实际图片)
Image("rating.alert.person.circle.fill") // 系统图标占位
.resizable()
.scaledToFit()
.frame(width: 122, height: 120)
.offset(y: -112)
}
}
/// 显示系统原生评价弹窗
private func openAppStoreForRating() {
// 检查 SKStoreReviewController 是否可用(iOS 10.3+)
if #available(iOS 10.3, *) {
// 收起键盘
UIApplication.shared.keyWindow?.endEditing(true)
// 显示系统原生评价弹窗
SKStoreReviewController.requestReview()
}
}
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论