Prepare Gotcha 1.0 for release
This commit is contained in:
177
ios/Sources/ServerScreens.swift
Normal file
177
ios/Sources/ServerScreens.swift
Normal file
@@ -0,0 +1,177 @@
|
||||
import UIKit
|
||||
|
||||
@MainActor
|
||||
final class ServersViewController: UITableViewController {
|
||||
private let context: AppContext
|
||||
private var servers: [ServerRow] = []
|
||||
|
||||
init(context: AppContext) {
|
||||
self.context = context
|
||||
super.init(style: .plain)
|
||||
title = "Servers"
|
||||
tableView.backgroundColor = .systemGroupedBackground
|
||||
tableView.separatorInset = .zero
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
required init?(coder: NSCoder) { fatalError("init(coder:) is not supported") }
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
servers = context.core.servers()
|
||||
tableView.reloadData()
|
||||
tableView.backgroundView = servers.isEmpty
|
||||
? EmptyBackgroundView(title: "No servers", detail: "Add a Gitea server to get started.")
|
||||
: nil
|
||||
navigationItem.rightBarButtonItem = UIBarButtonItem(
|
||||
systemItem: .add,
|
||||
primaryAction: UIAction { [weak self] _ in self?.showAddServer() }
|
||||
)
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
servers.count
|
||||
}
|
||||
|
||||
override func tableView(
|
||||
_ tableView: UITableView,
|
||||
cellForRowAt indexPath: IndexPath
|
||||
) -> UITableViewCell {
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "server")
|
||||
?? UITableViewCell(style: .subtitle, reuseIdentifier: "server")
|
||||
let server = servers[indexPath.row]
|
||||
configureTextCell(cell, title: server.name, detail: server.url)
|
||||
cell.accessoryType = .disclosureIndicator
|
||||
return cell
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
tableView.deselectRow(at: indexPath, animated: true)
|
||||
do {
|
||||
try context.selectServer(index: UInt32(indexPath.row))
|
||||
} catch {
|
||||
show(error: error)
|
||||
}
|
||||
}
|
||||
|
||||
private func showAddServer() {
|
||||
let controller = AddServerViewController(context: context) { [weak self] in
|
||||
guard let self else { return }
|
||||
self.servers = self.context.core.servers()
|
||||
self.tableView.reloadData()
|
||||
}
|
||||
present(UINavigationController(rootViewController: controller), animated: true)
|
||||
}
|
||||
}
|
||||
@MainActor
|
||||
final class AddServerViewController: UITableViewController, UITextFieldDelegate {
|
||||
private let context: AppContext
|
||||
private let completion: () -> Void
|
||||
private let nameField = UITextField()
|
||||
private let urlField = UITextField()
|
||||
private let tokenField = UITextField()
|
||||
private var saveButton: UIBarButtonItem!
|
||||
|
||||
init(context: AppContext, completion: @escaping () -> Void) {
|
||||
self.context = context
|
||||
self.completion = completion
|
||||
super.init(style: .insetGrouped)
|
||||
title = "Add Server"
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
required init?(coder: NSCoder) { fatalError("init(coder:) is not supported") }
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
navigationItem.leftBarButtonItem = UIBarButtonItem(
|
||||
systemItem: .cancel,
|
||||
primaryAction: UIAction { [weak self] _ in self?.dismiss(animated: true) }
|
||||
)
|
||||
saveButton = UIBarButtonItem(
|
||||
title: "Add",
|
||||
style: .done,
|
||||
target: self,
|
||||
action: #selector(save)
|
||||
)
|
||||
navigationItem.rightBarButtonItem = saveButton
|
||||
configure(nameField, placeholder: "Work", contentType: .name)
|
||||
configure(urlField, placeholder: "https://gitea.example.com", contentType: .URL)
|
||||
urlField.keyboardType = .URL
|
||||
urlField.autocapitalizationType = .none
|
||||
configure(tokenField, placeholder: "Access token", contentType: nil)
|
||||
tokenField.isSecureTextEntry = true
|
||||
tokenField.autocapitalizationType = .none
|
||||
tokenField.returnKeyType = .done
|
||||
}
|
||||
|
||||
override func numberOfSections(in tableView: UITableView) -> Int { 3 }
|
||||
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 1 }
|
||||
|
||||
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
||||
["Name", "Server URL", "Access token"][section]
|
||||
}
|
||||
|
||||
override func tableView(
|
||||
_ tableView: UITableView,
|
||||
cellForRowAt indexPath: IndexPath
|
||||
) -> UITableViewCell {
|
||||
let cell = UITableViewCell(style: .default, reuseIdentifier: nil)
|
||||
let field = [nameField, urlField, tokenField][indexPath.section]
|
||||
field.translatesAutoresizingMaskIntoConstraints = false
|
||||
cell.contentView.addSubview(field)
|
||||
NSLayoutConstraint.activate([
|
||||
field.leadingAnchor.constraint(equalTo: cell.contentView.leadingAnchor, constant: 16),
|
||||
field.trailingAnchor.constraint(equalTo: cell.contentView.trailingAnchor, constant: -16),
|
||||
field.topAnchor.constraint(equalTo: cell.contentView.topAnchor),
|
||||
field.bottomAnchor.constraint(equalTo: cell.contentView.bottomAnchor),
|
||||
cell.contentView.heightAnchor.constraint(greaterThanOrEqualToConstant: 48),
|
||||
])
|
||||
return cell
|
||||
}
|
||||
|
||||
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
|
||||
if textField === nameField { urlField.becomeFirstResponder() }
|
||||
else if textField === urlField { tokenField.becomeFirstResponder() }
|
||||
else { save() }
|
||||
return true
|
||||
}
|
||||
|
||||
@objc private func save() {
|
||||
view.endEditing(true)
|
||||
saveButton.isEnabled = false
|
||||
let spinner = UIActivityIndicatorView(style: .medium)
|
||||
spinner.startAnimating()
|
||||
navigationItem.rightBarButtonItem = UIBarButtonItem(customView: spinner)
|
||||
Task {
|
||||
do {
|
||||
let index = try await context.core.addServer(
|
||||
name: nameField.text ?? "",
|
||||
url: urlField.text ?? "",
|
||||
token: tokenField.text ?? ""
|
||||
)
|
||||
try context.didAddServer(index: index)
|
||||
completion()
|
||||
dismiss(animated: true)
|
||||
} catch {
|
||||
navigationItem.rightBarButtonItem = saveButton
|
||||
saveButton.isEnabled = true
|
||||
show(error: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func configure(
|
||||
_ field: UITextField,
|
||||
placeholder: String,
|
||||
contentType: UITextContentType?
|
||||
) {
|
||||
field.placeholder = placeholder
|
||||
field.textContentType = contentType
|
||||
field.clearButtonMode = .whileEditing
|
||||
field.delegate = self
|
||||
field.returnKeyType = .next
|
||||
field.adjustsFontForContentSizeCategory = true
|
||||
field.font = .preferredFont(forTextStyle: .body)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user