Skip to content
This repository has been archived by the owner on Jul 21, 2020. It is now read-only.

Commit

Permalink
Merge branch 'release/1.1'
Browse files Browse the repository at this point in the history
  • Loading branch information
adamwaite committed Oct 11, 2017
2 parents ad1deba + 5cd51ee commit 969bc99
Show file tree
Hide file tree
Showing 23 changed files with 272 additions and 220 deletions.
8 changes: 4 additions & 4 deletions CoinBar.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
objects = {

/* Begin PBXBuildFile section */
9939F5251F8C1B1700853157 /* PreferencesChangeInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9939F5241F8C1B1700853157 /* PreferencesChangeInterval.swift */; };
994CA9681F70414000FFE717 /* PreferencesCurrency.swift in Sources */ = {isa = PBXBuildFile; fileRef = 994CA9671F70414000FFE717 /* PreferencesCurrency.swift */; };
994CA96E1F7078EA00FFE717 /* PreferencesServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 994CA96D1F7078EA00FFE717 /* PreferencesServiceTests.swift */; };
994CA9701F7078F900FFE717 /* PersistenceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 994CA96F1F7078F900FFE717 /* PersistenceTests.swift */; };
Expand Down Expand Up @@ -51,7 +52,6 @@
99F4EA481F7584E100319783 /* RefreshMenuItemView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 99F4EA471F7584E100319783 /* RefreshMenuItemView.xib */; };
99F4EA4A1F758B3700319783 /* MenuItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99F4EA491F758B3700319783 /* MenuItemView.swift */; };
99F4EA4E1F77BD7700319783 /* NSApplication+CoinBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99F4EA4D1F77BD7700319783 /* NSApplication+CoinBar.swift */; };
99F4EA501F77C44000319783 /* DonationAddresses.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99F4EA4F1F77C44000319783 /* DonationAddresses.swift */; };
FFAE0B781F7185EA00CE26E2 /* JSONFixtures.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFAE0B771F7185EA00CE26E2 /* JSONFixtures.swift */; };
FFAE0B7B1F71863600CE26E2 /* coins.json in Resources */ = {isa = PBXBuildFile; fileRef = FFAE0B7A1F71863600CE26E2 /* coins.json */; };
FFAE0B7F1F71866F00CE26E2 /* coin.json in Resources */ = {isa = PBXBuildFile; fileRef = FFAE0B7E1F71866F00CE26E2 /* coin.json */; };
Expand All @@ -68,6 +68,7 @@
/* End PBXContainerItemProxy section */

/* Begin PBXFileReference section */
9939F5241F8C1B1700853157 /* PreferencesChangeInterval.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesChangeInterval.swift; sourceTree = "<group>"; };
994CA9671F70414000FFE717 /* PreferencesCurrency.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesCurrency.swift; sourceTree = "<group>"; };
994CA96D1F7078EA00FFE717 /* PreferencesServiceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesServiceTests.swift; sourceTree = "<group>"; };
994CA96F1F7078F900FFE717 /* PersistenceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PersistenceTests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -118,7 +119,6 @@
99F4EA471F7584E100319783 /* RefreshMenuItemView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = RefreshMenuItemView.xib; sourceTree = "<group>"; };
99F4EA491F758B3700319783 /* MenuItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuItemView.swift; sourceTree = "<group>"; };
99F4EA4D1F77BD7700319783 /* NSApplication+CoinBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSApplication+CoinBar.swift"; sourceTree = "<group>"; };
99F4EA4F1F77C44000319783 /* DonationAddresses.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DonationAddresses.swift; sourceTree = "<group>"; };
FFAE0B771F7185EA00CE26E2 /* JSONFixtures.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JSONFixtures.swift; sourceTree = "<group>"; };
FFAE0B7A1F71863600CE26E2 /* coins.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = coins.json; sourceTree = "<group>"; };
FFAE0B7E1F71866F00CE26E2 /* coin.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = coin.json; sourceTree = "<group>"; };
Expand Down Expand Up @@ -302,7 +302,7 @@
99D3E98C1F6D016B003C1CB3 /* Coin.swift */,
99D3E98E1F6D0180003C1CB3 /* Preferences.swift */,
994CA9671F70414000FFE717 /* PreferencesCurrency.swift */,
99F4EA4F1F77C44000319783 /* DonationAddresses.swift */,
9939F5241F8C1B1700853157 /* PreferencesChangeInterval.swift */,
);
path = Model;
sourceTree = "<group>";
Expand Down Expand Up @@ -471,14 +471,14 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
99F4EA501F77C44000319783 /* DonationAddresses.swift in Sources */,
99A0C6641F6E5D5500390B95 /* ValueStore.swift in Sources */,
99D3E9971F6D05A6003C1CB3 /* WebServices.swift in Sources */,
99D3E98F1F6D0180003C1CB3 /* Preferences.swift in Sources */,
998136C51F6F1150003381BA /* NibLoadable.swift in Sources */,
99F4EA4E1F77BD7700319783 /* NSApplication+CoinBar.swift in Sources */,
99F4EA461F7584C900319783 /* RefreshMenuItemView.swift in Sources */,
99DF633F1F7476DE000A0FEE /* NSView+CoinBar.swift in Sources */,
9939F5251F8C1B1700853157 /* PreferencesChangeInterval.swift in Sources */,
998136C21F6F1129003381BA /* CoinMenuItemView.swift in Sources */,
99684A1B1F73A39500985612 /* Service.swift in Sources */,
99D3E9851F6D0014003C1CB3 /* Result.swift in Sources */,
Expand Down
29 changes: 20 additions & 9 deletions CoinBar/App/Menu/MenuController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,9 @@ final class MenuController: NSObject, NSMenuDelegate {
}

private func refreshMenu() {
let currencyCode = service.preferencesService.getPreferences().currency
let preferredCurrency = Preferences.Currency(rawValue: currencyCode) ?? .bitcoin
let prefs = service.preferencesService.getPreferences()
let preferredCurrency = Preferences.Currency(rawValue: prefs.currency) ?? .bitcoin
let preferredChangeInterval = Preferences.ChangeInterval(rawValue: prefs.changeInterval) ?? .oneDay

refreshView?.configure(lastRefreshed: service.coinsService.lastUpdated)

Expand All @@ -109,31 +110,41 @@ final class MenuController: NSObject, NSMenuDelegate {
return
}

coinItemView.configure(with: coin, currency: preferredCurrency, imagesService: service.imagesService)
coinItemView.configure(
with: coin,
currency: preferredCurrency,
changeInterval: preferredChangeInterval,
imagesService: service.imagesService)
}
}

private func makeCoinItems() -> [NSMenuItem] {
let currencyCode = service.preferencesService.getPreferences().currency
let preferredCurrency = Preferences.Currency(rawValue: currencyCode) ?? .bitcoin

let prefs = service.preferencesService.getPreferences()
let preferredCurrency = Preferences.Currency(rawValue: prefs.currency) ?? .bitcoin
let preferredChangeInterval = Preferences.ChangeInterval(rawValue: prefs.changeInterval) ?? .oneDay

return coins.enumerated().map {
let item = NSMenuItem(title: $0.element.symbol, action: #selector(viewCoin(_:)), keyEquivalent: "")
item.tag = $0.offset
item.target = self
if let coinMenuItemView = makeCoinMenuItemView(coin: $0.element, currency: preferredCurrency) {
if let coinMenuItemView = makeCoinMenuItemView(coin: $0.element, currency: preferredCurrency, changeInterval: preferredChangeInterval) {
item.view = coinMenuItemView
}
return item
}
}

private func makeCoinMenuItemView(coin: Coin, currency: Preferences.Currency) -> CoinMenuItemView? {
private func makeCoinMenuItemView(coin: Coin, currency: Preferences.Currency, changeInterval: Preferences.ChangeInterval) -> CoinMenuItemView? {
guard let coinMenuItemView = CoinMenuItemView.createFromNib() else {
return nil
}

coinMenuItemView.configure(with: coin, currency: currency, imagesService: service.imagesService)
coinMenuItemView.configure(
with: coin,
currency: currency,
changeInterval: changeInterval,
imagesService: service.imagesService)

coinMenuItemView.onClick = viewCoin
return coinMenuItemView
}
Expand Down
120 changes: 69 additions & 51 deletions CoinBar/App/Menu/Views/CoinMenuItemView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,77 +4,95 @@ final class CoinMenuItemView: MenuItemView, NibLoadable {

@IBOutlet private(set) var imageView: NSImageView!
@IBOutlet private(set) var symbolLabel: NSTextField!
@IBOutlet private(set) var priceLabel: NSTextField!
@IBOutlet private(set) var percentChangeLabel: NSTextField!
@IBOutlet private(set) var valueLabel: NSTextField!

func configure(with coin: Coin, currency: Preferences.Currency, imagesService: ImagesServiceProtocol) {

// Symbol

func configure(with coin: Coin, currency: Preferences.Currency, changeInterval: Preferences.ChangeInterval, imagesService: ImagesServiceProtocol) {
configureSymbol(coin: coin)
configureImage(coin: coin, imagesService: imagesService)
configureValue(coin: coin, currency: currency, changeInterval: changeInterval)
}

private func configureSymbol(coin: Coin) {
symbolLabel.stringValue = coin.symbol
// Image
}

private func configureImage(coin: Coin, imagesService: ImagesServiceProtocol) {
imagesService.getImage(for: coin) { result in
guard let image = result.value else { return }
DispatchQueue.main.async {
self.imageView.image = image
}
}
}

private func configureValue(coin: Coin, currency: Preferences.Currency, changeInterval: Preferences.ChangeInterval) {
let attributedValue = NSMutableAttributedString()

// Value label
let attributedPrice = makeAttributedPrice(coin: coin, currency: currency)
attributedValue.append(attributedPrice)

switch currency {
attributedValue.append(NSAttributedString(string: " "))

case .bitcoin:
if let priceBTC = coin.priceBTC {
priceLabel.stringValue = currency.formattedValue(priceBTC) ?? ""
} else {
priceLabel.stringValue = ""
let attributedChange = makeAttributedChange(coin: coin, changeInterval: changeInterval)
attributedValue.append(attributedChange)

valueLabel.attributedStringValue = attributedValue
}

private func makeAttributedPrice(coin: Coin, currency: Preferences.Currency) -> NSMutableAttributedString {
let price: String? = {
switch currency {
case .bitcoin: return coin.priceBTC
case .unitedStatesDollar: return coin.priceUSD
default: return coin.pricePreferredCurrency
}
}()

case .unitedStatesDollar:
if let priceUSD = coin.priceUSD {
priceLabel.stringValue = currency.formattedValue(priceUSD) ?? ""
} else {
priceLabel.stringValue = ""
}
return price
.flatMap(currency.formattedValue)
.flatMap(NSMutableAttributedString.init)
?? NSMutableAttributedString()
}

private func makeAttributedChange(coin: Coin, changeInterval: Preferences.ChangeInterval) -> NSMutableAttributedString {
let attributedChange = NSMutableAttributedString()

default:
if let pricePreferredCurrency = coin.pricePreferredCurrency {
priceLabel.stringValue = currency.formattedValue(pricePreferredCurrency) ?? ""
} else {
priceLabel.stringValue = ""
let preferencePercentChange: String? = {
switch changeInterval {
case .oneHour: return coin.percentChange1h
case .oneDay: return coin.percentChange24h
case .oneWeek: return coin.percentChange7d
}
}()

guard let percentChange = preferencePercentChange else {
return attributedChange
}

// Percent change label

if let percentChange = coin.percentChange1h {
switch Double(percentChange) {

switch Double(percentChange) {

case let positive? where positive > 0.0:
percentChangeLabel.stringValue = "\(percentChange)%"
percentChangeLabel.textColor = NSColor.green

case let negative? where negative < 0.0:
percentChangeLabel.stringValue = "\(percentChange)%"
percentChangeLabel.textColor = NSColor.red

case let zero? where zero == 0.0:
percentChangeLabel.stringValue = "\(percentChange)%"

default:
percentChangeLabel.stringValue = "-"
percentChangeLabel.textColor = NSColor.red
}
}
case let positive? where positive > 0.0:
let percentChange = "(\(percentChange)%)"
attributedChange.append(NSAttributedString(string: percentChange))

else {
percentChangeLabel.stringValue = "-"
case let negative? where negative < 0.0:
let percentChange = "(\(percentChange)%)"
attributedChange.append(NSAttributedString(string: percentChange))

case let zero? where zero == 0.0:
let percentChange = "(\(percentChange)%)"
attributedChange.append(NSAttributedString(string: percentChange))

default:
break
}

attributedChange.setAttributes([
NSAttributedStringKey.font: NSFont.systemFont(ofSize: valueLabel.font!.pointSize - 2)
], range: NSRange(location: 0, length: attributedChange.length))

return attributedChange

}

}

22 changes: 5 additions & 17 deletions CoinBar/App/Menu/Views/CoinMenuItemView.xib
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customView id="c22-O7-iKe" customClass="CoinMenuItemView" customModule="CoinBar" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="250" height="30"/>
<rect key="frame" x="0.0" y="0.0" width="300" height="30"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="22Z-53-Sm9">
Expand All @@ -30,39 +30,27 @@
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="WN8-iE-thm">
<rect key="frame" x="167" y="6" width="40" height="18"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Label" id="eJP-H7-Odc">
<font key="font" metaFont="system" size="14"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="vF4-jN-qcZ">
<rect key="frame" x="207" y="6" width="25" height="18"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="3.2" id="hQG-8b-mxp">
<rect key="frame" x="258" y="6" width="24" height="18"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="---" id="hQG-8b-mxp">
<font key="font" metaFont="system" size="14"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<constraints>
<constraint firstItem="vF4-jN-qcZ" firstAttribute="leading" secondItem="WN8-iE-thm" secondAttribute="trailing" constant="4" id="20O-wD-Tps"/>
<constraint firstItem="WN8-iE-thm" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="D21-rs-aM9" secondAttribute="trailing" constant="8" id="4ck-ni-WaR"/>
<constraint firstItem="vF4-jN-qcZ" firstAttribute="centerY" secondItem="D21-rs-aM9" secondAttribute="centerY" id="1nd-is-llJ"/>
<constraint firstAttribute="trailing" secondItem="vF4-jN-qcZ" secondAttribute="trailing" constant="20" id="Kax-af-b9M"/>
<constraint firstItem="D21-rs-aM9" firstAttribute="leading" secondItem="22Z-53-Sm9" secondAttribute="trailing" constant="8" id="LOa-FZ-T9M"/>
<constraint firstItem="22Z-53-Sm9" firstAttribute="leading" secondItem="c22-O7-iKe" secondAttribute="leading" constant="20" id="QDB-x1-uyM"/>
<constraint firstItem="vF4-jN-qcZ" firstAttribute="centerY" secondItem="WN8-iE-thm" secondAttribute="centerY" id="Xru-Ka-C1h"/>
<constraint firstItem="22Z-53-Sm9" firstAttribute="centerY" secondItem="c22-O7-iKe" secondAttribute="centerY" id="m9t-MX-i9c"/>
<constraint firstItem="D21-rs-aM9" firstAttribute="centerY" secondItem="22Z-53-Sm9" secondAttribute="centerY" id="uKJ-jD-UvY"/>
<constraint firstItem="WN8-iE-thm" firstAttribute="centerY" secondItem="D21-rs-aM9" secondAttribute="centerY" id="zB4-Sv-1OL"/>
</constraints>
<connections>
<outlet property="imageView" destination="22Z-53-Sm9" id="m8E-fH-CG2"/>
<outlet property="percentChangeLabel" destination="vF4-jN-qcZ" id="laL-XM-1d5"/>
<outlet property="priceLabel" destination="WN8-iE-thm" id="CGK-bb-rKp"/>
<outlet property="symbolLabel" destination="D21-rs-aM9" id="6lR-O9-gb0"/>
<outlet property="valueLabel" destination="vF4-jN-qcZ" id="6uG-19-l46"/>
</connections>
<point key="canvasLocation" x="80" y="83.5"/>
</customView>
Expand Down
29 changes: 0 additions & 29 deletions CoinBar/App/Menu/Views/MenuItemView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,43 +11,14 @@ class MenuItemView: NSView {

override func awakeFromNib() {
super.awakeFromNib()
applyMenuHighlightStyles(rect: .zero)

gestureRecognizer = NSClickGestureRecognizer(target: self, action: #selector(clicked(_:)))
addGestureRecognizer(gestureRecognizer)
}

override func draw(_ dirtyRect: NSRect) {
if let isHighlighted = enclosingMenuItem?.isHighlighted, isHighlighted {
applyMenuHighlightStyles(rect: dirtyRect)
super.draw(dirtyRect)
return
}

super.draw(dirtyRect)
}

override func acceptsFirstMouse(for event: NSEvent?) -> Bool {
return true
}

// MARK: - UI

/// stackoverflow.com/questions/26851306/trouble-matching-the-vibrant-background-of-a-yosemite-nsmenuitem-containing-a-cu
private func applyMenuHighlightStyles(rect: NSRect) {
if (subviews.lazy.first { $0 is NSImageView && $0.tag == 1234 }) == nil {
let bgImageView = NSImageView()
bgImageView.tag = 1234
bgImageView.frame = bounds
addSubview(bgImageView)
}

if NSGraphicsContext.currentContextDrawingToScreen() {
NSColor.selectedMenuItemColor.setFill()
NSBezierPath.fill(rect)
}
}

// MARK: - Actions

@objc private func clicked(_ sender: NSClickGestureRecognizer) {
Expand Down
Loading

0 comments on commit 969bc99

Please sign in to comment.