diff --git a/packages/frontend/apps/ios/App/App.xcodeproj/project.pbxproj b/packages/frontend/apps/ios/App/App.xcodeproj/project.pbxproj index 1b914cd349..1e8161da73 100644 --- a/packages/frontend/apps/ios/App/App.xcodeproj/project.pbxproj +++ b/packages/frontend/apps/ios/App/App.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + 501FBC562D02F88200507774 /* InfoPlist.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 501FBC552D02F88200507774 /* InfoPlist.xcstrings */; }; + 501FBC582D02F88800507774 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 501FBC572D02F88800507774 /* Localizable.xcstrings */; }; 505B0A342CEB3FB10092FC35 /* Intelligents in Frameworks */ = {isa = PBXBuildFile; productRef = 505B0A332CEB3FB10092FC35 /* Intelligents */; }; 505B0A362CEB48B10092FC35 /* RootViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 505B0A352CEB48B10092FC35 /* RootViewController.swift */; }; 9D1C07272CEC3E9500E1C502 /* IntelligentsPlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D1C07262CEC3E8200E1C502 /* IntelligentsPlugin.swift */; }; @@ -24,6 +26,8 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 501FBC552D02F88200507774 /* InfoPlist.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = InfoPlist.xcstrings; sourceTree = ""; }; + 501FBC572D02F88800507774 /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = ""; }; 504EC3041FED79650016851F /* App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = App.app; sourceTree = BUILT_PRODUCTS_DIR; }; 505B0A312CEB3FAB0092FC35 /* Intelligents */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = Intelligents; sourceTree = ""; }; 505B0A352CEB48B10092FC35 /* RootViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootViewController.swift; sourceTree = ""; }; @@ -133,6 +137,8 @@ 9D90BE1F2CCB9876006677DB /* config.xml */, 9D90BE202CCB9876006677DB /* Info.plist */, 9D90BE222CCB9876006677DB /* Main.storyboard */, + 501FBC552D02F88200507774 /* InfoPlist.xcstrings */, + 501FBC572D02F88800507774 /* Localizable.xcstrings */, 9D90BE232CCB9876006677DB /* public */, ); path = App; @@ -168,7 +174,7 @@ attributes = { BuildIndependentTargetsInParallel = YES; LastSwiftUpdateCheck = 0920; - LastUpgradeCheck = 0920; + LastUpgradeCheck = 1610; TargetAttributes = { 504EC3031FED79650016851F = { CreatedOnToolsVersion = 9.2; @@ -199,10 +205,12 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 501FBC562D02F88200507774 /* InfoPlist.xcstrings in Resources */, 9D90BE292CCB9876006677DB /* Assets.xcassets in Resources */, 9D90BE2A2CCB9876006677DB /* capacitor.config.json in Resources */, 9D90BE2B2CCB9876006677DB /* config.xml in Resources */, 9D90BE2D2CCB9876006677DB /* Main.storyboard in Resources */, + 501FBC582D02F88800507774 /* Localizable.xcstrings in Resources */, 9D90BE2E2CCB9876006677DB /* public in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -278,6 +286,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -288,6 +297,7 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; @@ -295,8 +305,10 @@ CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -310,6 +322,7 @@ DEVELOPMENT_TEAM = 73YMMDVT2M; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; @@ -331,6 +344,7 @@ SDKROOT = iphoneos; SUPPORTED_PLATFORMS = "iphonesimulator iphoneos"; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; name = Debug; @@ -339,6 +353,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -349,6 +364,7 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; @@ -356,8 +372,10 @@ CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -371,6 +389,7 @@ DEVELOPMENT_TEAM = 73YMMDVT2M; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; @@ -385,6 +404,7 @@ SDKROOT = iphoneos; SUPPORTED_PLATFORMS = "iphonesimulator iphoneos"; SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OPTIMIZATION_LEVEL = "-O"; VALIDATE_PRODUCT = YES; }; diff --git a/packages/frontend/apps/ios/App/App.xcodeproj/xcshareddata/xcschemes/App.xcscheme b/packages/frontend/apps/ios/App/App.xcodeproj/xcshareddata/xcschemes/App.xcscheme index 91a0d15ea2..b86c6371a1 100644 --- a/packages/frontend/apps/ios/App/App.xcodeproj/xcshareddata/xcschemes/App.xcscheme +++ b/packages/frontend/apps/ios/App/App.xcodeproj/xcshareddata/xcschemes/App.xcscheme @@ -1,6 +1,6 @@ LSRequiresIPhoneOS + NSCameraUsageDescription + We will take photo with this permission when you asked to do, and we will attach picture to your post or request when possible. UILaunchScreen UIImageName diff --git a/packages/frontend/apps/ios/App/App/InfoPlist.xcstrings b/packages/frontend/apps/ios/App/App/InfoPlist.xcstrings new file mode 100644 index 0000000000..6f59897ec2 --- /dev/null +++ b/packages/frontend/apps/ios/App/App/InfoPlist.xcstrings @@ -0,0 +1,42 @@ +{ + "sourceLanguage" : "en", + "strings" : { + "CFBundleDisplayName" : { + "comment" : "Bundle display name", + "extractionState" : "extracted_with_value", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "AFFiNE" + } + } + } + }, + "CFBundleName" : { + "comment" : "Bundle name", + "extractionState" : "extracted_with_value", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "App" + } + } + } + }, + "NSCameraUsageDescription" : { + "comment" : "Privacy - Camera Usage Description", + "extractionState" : "extracted_with_value", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "We will take photo with this permission when you asked to do, and we will attach picture to your post or request when possible." + } + } + } + } + }, + "version" : "1.0" +} \ No newline at end of file diff --git a/packages/frontend/apps/ios/App/App/Localizable.xcstrings b/packages/frontend/apps/ios/App/App/Localizable.xcstrings new file mode 100644 index 0000000000..fa56d16199 --- /dev/null +++ b/packages/frontend/apps/ios/App/App/Localizable.xcstrings @@ -0,0 +1,7 @@ +{ + "sourceLanguage" : "en", + "strings" : { + + }, + "version" : "1.0" +} \ No newline at end of file diff --git a/packages/frontend/apps/ios/App/App/Packages/Intelligents/Sources/Intelligents/IntelligentsChatController/InputEditView/InputEditView+Camera.swift b/packages/frontend/apps/ios/App/App/Packages/Intelligents/Sources/Intelligents/IntelligentsChatController/InputEditView/InputEditView+Camera.swift index 4548669a85..eff4e8002c 100644 --- a/packages/frontend/apps/ios/App/App/Packages/Intelligents/Sources/Intelligents/IntelligentsChatController/InputEditView/InputEditView+Camera.swift +++ b/packages/frontend/apps/ios/App/App/Packages/Intelligents/Sources/Intelligents/IntelligentsChatController/InputEditView/InputEditView+Camera.swift @@ -5,8 +5,61 @@ // Created by 秋星桥 on 2024/12/6. // +import AVKit import UIKit -extension InputEditView { - @objc func takePhoto() {} +extension InputEditView: UIImagePickerControllerDelegate, UINavigationControllerDelegate { + @objc func takePhoto() { + AVCaptureDevice.requestAccess(for: .video) { _ in + DispatchQueue.main.async { + let ctrl = UIImagePickerController() + ctrl.allowsEditing = false + ctrl.sourceType = .camera + ctrl.mediaTypes = [UTType.movie.identifier, UTType.image.identifier] + ctrl.cameraCaptureMode = .photo + ctrl.delegate = self + self.parentViewController?.present(ctrl, animated: true) + } + } + } + + private func processJPEGImageData(_ image: UIImage) throws -> Data? { + guard let data = image.jpegData(compressionQuality: 0.75) else { + throw NSError(domain: "", code: -1, userInfo: [ + NSLocalizedDescriptionKey: "Failed to compress image data", + ]) + } + return data + } + + func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) { + picker.dismiss(animated: true) { + var itemUrl: URL? + + if itemUrl == nil, + let image = info[.editedImage] as? UIImage ?? info[.originalImage] as? UIImage + { + let tempDir = URL(fileURLWithPath: NSTemporaryDirectory()) + .appendingPathComponent("Camera") + try? FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true) + let tempFile = tempDir + .appendingPathComponent(UUID().uuidString) + .appendingPathExtension("jpeg") + try? self.processJPEGImageData(image)?.write(to: tempFile) + itemUrl = tempFile + } + if itemUrl == nil, + let url = info[.mediaURL] as? URL + { + itemUrl = url + } + + guard let url = itemUrl, FileManager.default.fileExists(atPath: url.path) else { + return + } + guard let image = UIImage(contentsOfFile: url.path) else { return } + try? FileManager.default.removeItem(at: url) + self.viewModel.attachments.append(image) + } + } } diff --git a/packages/frontend/apps/ios/App/App/Packages/Intelligents/Sources/Intelligents/IntelligentsChatController/InputEditView/InputEditView.swift b/packages/frontend/apps/ios/App/App/Packages/Intelligents/Sources/Intelligents/IntelligentsChatController/InputEditView/InputEditView.swift index 4419f72f1f..f469698082 100644 --- a/packages/frontend/apps/ios/App/App/Packages/Intelligents/Sources/Intelligents/IntelligentsChatController/InputEditView/InputEditView.swift +++ b/packages/frontend/apps/ios/App/App/Packages/Intelligents/Sources/Intelligents/IntelligentsChatController/InputEditView/InputEditView.swift @@ -70,6 +70,7 @@ class InputEditView: UIView, UITextViewDelegate { for: .touchUpInside ) + textEditor.returnKeyType = .send textEditor.addSubview(placeholderLabel) placeholderLabel.textColor = .label.withAlphaComponent(0.25) placeholderLabel.font = textEditor.font diff --git a/packages/frontend/apps/ios/App/App/Packages/Intelligents/Sources/Intelligents/IntelligentsChatController/IntelligentsChatController.swift b/packages/frontend/apps/ios/App/App/Packages/Intelligents/Sources/Intelligents/IntelligentsChatController/IntelligentsChatController.swift index 54f5c35721..4e45fff98f 100644 --- a/packages/frontend/apps/ios/App/App/Packages/Intelligents/Sources/Intelligents/IntelligentsChatController/IntelligentsChatController.swift +++ b/packages/frontend/apps/ios/App/App/Packages/Intelligents/Sources/Intelligents/IntelligentsChatController/IntelligentsChatController.swift @@ -11,6 +11,7 @@ public class IntelligentsChatController: UIViewController { let header = Header() let inputBoxKeyboardAdapter = UIView() let inputBox = InputBox() + let progressView = UIActivityIndicatorView() let tableView = ChatTableView() var inputBoxKeyboardAdapterHeightConstraint = NSLayoutConstraint() @@ -98,5 +99,25 @@ public class IntelligentsChatController: UIViewController { tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor), tableView.bottomAnchor.constraint(equalTo: inputBox.topAnchor), ].forEach { $0.isActive = true } + + view.addSubview(progressView) + progressView.hidesWhenStopped = true + progressView.stopAnimating() + progressView.translatesAutoresizingMaskIntoConstraints = false + [ + progressView.centerXAnchor.constraint(equalTo: inputBox.centerXAnchor), + progressView.centerYAnchor.constraint(equalTo: inputBox.centerYAnchor), + ].forEach { $0.isActive = true } + progressView.style = .large + + inputBox.editor.controlBanner.sendButton.addTarget( + self, + action: #selector(send), + for: .touchUpInside + ) + } + + @objc func send() { + assert(Thread.isMainThread) } }