A Mac app for working with Mongo databases.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

214 lines
8.1 KiB

//
// DatabaseViewController.swift
// MongoView
//
// Created by Shadowfacts on 1/10/20.
// Copyright © 2020 Shadowfacts. All rights reserved.
//
import Cocoa
import MongoSwift
import NIO
struct DatabaseCollections {
let database: String
let collections: [String]
}
struct DatabaseCollection {
let database: String
let name: String
}
class DatabaseViewController: NSViewController {
@IBOutlet weak var collectionsOutlineView: NSOutlineView!
@IBOutlet weak var detailContainerView: NSView!
let mongoController: MongoController
private var databaseCollections: [DatabaseCollections] = []
private var selectedCollection: DatabaseCollection?
private var placeholderLabel: NSTextField?
private var queryViewController: QueryViewController?
init(mongoController: MongoController) {
self.mongoController = mongoController
super.init(nibName: "DatabaseViewController", bundle: .main)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
mongoController.client.listDatabaseNames().flatMap { (databaseNames) -> EventLoopFuture<[DatabaseCollections]> in
let futures = databaseNames.map { (name) in
self.mongoController.client.db(name).listCollectionNames().map { (collectionNames) in
DatabaseCollections(database: name, collections: collectionNames)
}
}
return EventLoopFuture.whenAllSucceed(futures, on: futures.first!.eventLoop)
}.whenComplete { [weak self] (res) in
guard let self = self else { return }
switch res {
case let .success(databaseCollections):
self.databaseCollections = databaseCollections
DispatchQueue.main.async {
self.collectionsOutlineView.reloadData()
}
case let .failure(error):
fatalError("error getting database names: \(error)")
}
}
collectionsOutlineView.dataSource = self
collectionsOutlineView.delegate = self
collectionsOutlineView.target = self
collectionsOutlineView.doubleAction = #selector(cellDoubleClicked)
updateDetailView()
}
func showCollection(_ collection: DatabaseCollection) {
selectedCollection = collection
updateDetailView()
}
private func updateDetailView() {
if let collection = selectedCollection {
if let placeholderLabel = placeholderLabel {
placeholderLabel.removeFromSuperview()
}
if let oldQueryViewController = self.queryViewController {
oldQueryViewController.view.removeFromSuperview()
oldQueryViewController.removeFromParent()
}
let queryViewController = QueryViewController(mongoController: mongoController, collection: collection)
self.queryViewController = queryViewController
queryViewController.view.translatesAutoresizingMaskIntoConstraints = false
addChild(queryViewController)
detailContainerView.addSubview(queryViewController.view)
NSLayoutConstraint.activate([
queryViewController.view.leadingAnchor.constraint(equalTo: detailContainerView.leadingAnchor),
queryViewController.view.trailingAnchor.constraint(equalTo: detailContainerView.trailingAnchor),
queryViewController.view.topAnchor.constraint(equalTo: detailContainerView.topAnchor),
queryViewController.view.bottomAnchor.constraint(equalTo: detailContainerView.bottomAnchor)
])
self.title = queryViewController.title
} else {
if let queryViewController = queryViewController {
queryViewController.view.removeFromSuperview()
queryViewController.removeFromParent()
}
if self.placeholderLabel == nil {
let label = NSTextField(labelWithString: "Select a collection to begin")
self.placeholderLabel = label
label.translatesAutoresizingMaskIntoConstraints = false
detailContainerView.addSubview(label)
NSLayoutConstraint.activate([
label.centerXAnchor.constraint(equalTo: detailContainerView.centerXAnchor),
label.centerYAnchor.constraint(equalTo: detailContainerView.centerYAnchor)
])
self.title = "No Query"
}
}
}
@objc func cellDoubleClicked() {
let item = collectionsOutlineView.item(atRow: collectionsOutlineView.clickedRow)!
if item is DatabaseCollections {
if collectionsOutlineView.isItemExpanded(item) {
collectionsOutlineView.collapseItem(item)
} else {
collectionsOutlineView.expandItem(item)
}
} else if let collection = item as? DatabaseCollection {
// only open a new window/tab if our own query has changed from the default, otherwise replace our query controller
if let queryViewController = queryViewController, queryViewController.hasQueryChanged {
(NSApplication.shared.delegate as! AppDelegate).newWindow(mongoController: mongoController, collection: collection)
} else {
self.selectedCollection = collection
updateDetailView()
}
}
}
@IBAction func runQuery(_ sender: Any) {
guard let queryViewController = queryViewController else {
return
}
queryViewController.runQuery()
}
}
extension DatabaseViewController: NSMenuItemValidation {
func validateMenuItem(_ menuItem: NSMenuItem) -> Bool {
if menuItem.action == #selector(runQuery(_:)) {
return queryViewController != nil
}
return true
}
}
extension DatabaseViewController: NSOutlineViewDataSource {
func outlineView(_ outlineView: NSOutlineView, numberOfChildrenOfItem item: Any?) -> Int {
if item == nil {
return databaseCollections.count
} else if let database = item as? DatabaseCollections {
return database.collections.count
} else {
return 0
}
}
func outlineView(_ outlineView: NSOutlineView, isItemExpandable item: Any) -> Bool {
return item is DatabaseCollections
}
func outlineView(_ outlineView: NSOutlineView, child index: Int, ofItem item: Any?) -> Any {
if item == nil {
return databaseCollections[index]
} else if let databaseCollections = item as? DatabaseCollections {
let collection = databaseCollections.collections[index]
return DatabaseCollection(database: databaseCollections.database, name: collection)
} else {
fatalError()
}
}
}
extension DatabaseViewController: NSOutlineViewDelegate {
func outlineView(_ outlineView: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView? {
if let database = item as? DatabaseCollections {
let cell = outlineView.makeView(withIdentifier: .databaseNameCell, owner: nil) as! NSTableCellView
cell.textField!.stringValue = database.database
cell.textField!.isEditable = false
return cell
} else if let collection = item as? DatabaseCollection {
let cell = outlineView.makeView(withIdentifier: .collectionNameCell, owner: nil) as! NSTableCellView
cell.textField!.stringValue = collection.name
cell.textField!.isEditable = false
return cell
} else {
fatalError()
}
}
}
extension NSUserInterfaceItemIdentifier {
static let databaseNameCell = NSUserInterfaceItemIdentifier(rawValue: "DatabaseNameCell")
static let collectionNameCell = NSUserInterfaceItemIdentifier(rawValue: "CollectionNameCell")
}