Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 30 additions & 8 deletions Nextcloud.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ class NCAccountSettingsModel: ObservableObject, ViewOnAppearHandling {
init(controller: NCMainTabBarController?, delegate: NCAccountSettingsModelDelegate?) {
self.controller = controller
self.delegate = delegate
if ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] == "1" {
if isXcodeRunningForPreviews {
Task {
await self.database.previewCreateDB()
await self.database.createDBForPreview()
}
}
onViewAppear()
Expand Down
57 changes: 35 additions & 22 deletions iOSClient/Account/Account Settings/NCAccountSettingsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ struct NCAccountSettingsView: View {
@ObservedObject var model: NCAccountSettingsModel

@State private var isExpanded: Bool = false
@State private var showUserStatus = false
@State private var showServerCertificate = false
@State private var showPushCertificate = false
@State private var showDeleteAccountAlert: Bool = false
Expand Down Expand Up @@ -141,31 +140,45 @@ struct NCAccountSettingsView: View {
//
// User Status
if capabilities.userStatusEnabled {
Button(action: {
showUserStatus = true
}, label: {
HStack {
Image(systemName: "moon.fill")
.resizable()
.scaledToFit()
.font(Font.system(.body).weight(.light))
.frame(width: 20, height: 20)
.foregroundStyle(Color(NCBrandColor.shared.iconImageColor))
Text(NSLocalizedString("_set_user_status_", comment: ""))
.lineLimit(1)
.truncationMode(.middle)
.foregroundStyle(Color(NCBrandColor.shared.textColor))
.padding(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 20))
if let account = model.tblAccount?.account {
NavigationLink(destination: NCUserStatusView(account: account)) {
HStack {
Image(systemName: "moon.fill")
.resizable()
.scaledToFit()
.font(Font.system(.body).weight(.light))
.frame(width: 20, height: 20)
.foregroundStyle(Color(NCBrandColor.shared.iconImageColor))
Text(NSLocalizedString("_set_user_status_", comment: ""))
.lineLimit(1)
.truncationMode(.middle)
.foregroundStyle(Color(NCBrandColor.shared.textColor))
.padding(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 20))
}
.font(.subheadline)
}
.font(.subheadline)
})
.sheet(isPresented: $showUserStatus) {
if let account = model.tblAccount?.account {
UserStatusView(showUserStatus: $showUserStatus, account: account)
}

if let account = model.tblAccount?.account {
NavigationLink(destination: NCStatusMessageView(account: account)) {
HStack {
Image(systemName: "message.fill")
.resizable()
.scaledToFit()
.font(Font.system(.body).weight(.light))
.frame(width: 20, height: 20)
.foregroundStyle(Color(NCBrandColor.shared.iconImageColor))
Text(NSLocalizedString("_set_user_status_message_", comment: ""))
.lineLimit(1)
.truncationMode(.middle)
.foregroundStyle(Color(NCBrandColor.shared.textColor))
.padding(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 20))
}
.font(.subheadline)
}
}
.onChange(of: showUserStatus) { }
}

//
// Certificate server
if model.isAdminGroup() {
Expand Down
2 changes: 1 addition & 1 deletion iOSClient/Data/NCManageDatabase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -444,7 +444,7 @@ final class NCManageDatabase: @unchecked Sendable {
// MARK: -
// MARK: SWIFTUI PREVIEW

func previewCreateDB() async {
func createDBForPreview() async {
// Account
let account = "marinofaggiana https://cloudtest.nextcloud.com"
let account2 = "mariorossi https://cloudtest.nextcloud.com"
Expand Down
4 changes: 0 additions & 4 deletions iOSClient/Extensions/View+Extension.swift
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,3 @@ struct ViewFirstAppearModifier: ViewModifier {
}
}
}

var isRunningForPreviews: Bool {
return ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] == "1"
}
7 changes: 7 additions & 0 deletions iOSClient/NCGlobal.swift
Original file line number Diff line number Diff line change
Expand Up @@ -403,3 +403,10 @@ final class NCGlobal: Sendable {
let udMigrationMultiDomains = "migrationMultiDomains"
let udLastVersion = "lastVersion"
}

/**
Indicates whether Xcode is running SwiftUI previews.
*/
var isXcodeRunningForPreviews: Bool {
return ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] == "1"
}
95 changes: 95 additions & 0 deletions iOSClient/StatusMessage/EmojiTextField.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// SPDX-FileCopyrightText: Nextcloud GmbH
// SPDX-FileCopyrightText: 2025 Milen Pivchev
// SPDX-License-Identifier: GPL-3.0-or-later

import SwiftUI

// UIKit-backed emoji-only text field that forces the Emoji keyboard
final class EmojiTextField: UITextField {
override var textInputContextIdentifier: String? { "" } // return non-nil to show the Emoji keyboard

override var textInputMode: UITextInputMode? {
for mode in UITextInputMode.activeInputModes {
if mode.primaryLanguage == "emoji" {
return mode
}
}
return nil
}

override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}

required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}

private func commonInit() {
NotificationCenter.default.addObserver(self, selector: #selector(inputModeDidChange), name: UITextInputMode.currentInputModeDidChangeNotification, object: nil)
addTarget(self, action: #selector(textChanged), for: .editingChanged)
}

@objc func inputModeDidChange(_ notification: Notification) {
guard isFirstResponder else {
return
}

DispatchQueue.main.async { [weak self] in
self?.reloadInputViews()
}
}

// Keep only a single emoji character
@objc private func textChanged() {
guard let t = text, !t.isEmpty else { return }
// Trim to first extended grapheme cluster (so flags/skin tones stay intact)
let first = String(t.prefix(1))
if first != t { text = first }
}
}

struct EmojiField: UIViewRepresentable {
@Binding var text: String

func makeUIView(context: Context) -> EmojiTextField {
let tf = EmojiTextField(frame: .zero)
tf.delegate = context.coordinator
tf.text = text
tf.setContentHuggingPriority(.required, for: .horizontal)
tf.setContentCompressionResistancePriority(.required, for: .horizontal)
return tf
}

func updateUIView(_ uiView: EmojiTextField, context: Context) {
if uiView.text != text {
uiView.text = text
}
}

func makeCoordinator() -> Coordinator { Coordinator(self) }

final class Coordinator: NSObject, UITextFieldDelegate {
var parent: EmojiField
init(_ parent: EmojiField) { self.parent = parent }

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if textField is EmojiTextField {
if string.isEmpty {
textField.text = "😀"
return false
}
textField.text = string
textField.endEditing(true)
}
return true
}

func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return false
}
}
}
Loading
Loading