Merge pull request #316 from SimplyDanny/colors-and-localization
Colors and localization
This commit is contained in:
commit
f69f8bb867
13 changed files with 99 additions and 62 deletions
|
|
@ -44,6 +44,7 @@
|
|||
30697C5321F63E0B0064FCAC /* PasscodeExtensionDisplay.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30697C5121F63E0B0064FCAC /* PasscodeExtensionDisplay.swift */; };
|
||||
30697C5421F63E0B0064FCAC /* CredentialProviderViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30697C5221F63E0B0064FCAC /* CredentialProviderViewController.swift */; };
|
||||
30697C5F21F674800064FCAC /* String+UtilitiesTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30697C5E21F674800064FCAC /* String+UtilitiesTest.swift */; };
|
||||
3087574F2343E42A00B971A2 /* Colors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3087574E2343E42A00B971A2 /* Colors.swift */; };
|
||||
308C273A2279F9CB0016D0E2 /* SearchBarScope.swift in Sources */ = {isa = PBXBuildFile; fileRef = 302202EE222F14E400555236 /* SearchBarScope.swift */; };
|
||||
30A1D29C21AF451E00E2D1F7 /* PasswordGeneratorFlavourTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30A1D29B21AF451E00E2D1F7 /* PasswordGeneratorFlavourTest.swift */; };
|
||||
30A1D2A221B2BC6F00E2D1F7 /* TokenBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30A1D2A121B2BC6F00E2D1F7 /* TokenBuilder.swift */; };
|
||||
|
|
@ -261,6 +262,7 @@
|
|||
30697C5121F63E0B0064FCAC /* PasscodeExtensionDisplay.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PasscodeExtensionDisplay.swift; sourceTree = "<group>"; };
|
||||
30697C5221F63E0B0064FCAC /* CredentialProviderViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CredentialProviderViewController.swift; sourceTree = "<group>"; };
|
||||
30697C5E21F674800064FCAC /* String+UtilitiesTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+UtilitiesTest.swift"; sourceTree = "<group>"; };
|
||||
3087574E2343E42A00B971A2 /* Colors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Colors.swift; sourceTree = "<group>"; };
|
||||
30A1D29B21AF451E00E2D1F7 /* PasswordGeneratorFlavourTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasswordGeneratorFlavourTest.swift; sourceTree = "<group>"; };
|
||||
30A1D2A121B2BC6F00E2D1F7 /* TokenBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TokenBuilder.swift; sourceTree = "<group>"; };
|
||||
30A1D2A521B2D46100E2D1F7 /* OtpType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OtpType.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -668,6 +670,7 @@
|
|||
children = (
|
||||
30697C2921F63C590064FCAC /* AppError.swift */,
|
||||
302B2C9722C2BDE700D831EE /* AppKeychain.swift */,
|
||||
3087574E2343E42A00B971A2 /* Colors.swift */,
|
||||
3032328D22CBD4CD009EBD9C /* CryptographicKeys.swift */,
|
||||
30697C2821F63C590064FCAC /* DefaultsKeys.swift */,
|
||||
30697C2521F63C590064FCAC /* FileManagerExtension.swift */,
|
||||
|
|
@ -1322,6 +1325,7 @@
|
|||
30CCA91623258C380048CA51 /* PgpInterface.swift in Sources */,
|
||||
30697C4721F63CAB0064FCAC /* PasscodeLock.swift in Sources */,
|
||||
30697C3421F63C8B0064FCAC /* PasscodeLockViewController.swift in Sources */,
|
||||
3087574F2343E42A00B971A2 /* Colors.swift in Sources */,
|
||||
30697C2C21F63C5A0064FCAC /* FileManagerExtension.swift in Sources */,
|
||||
30697C3321F63C8B0064FCAC /* PasscodeLockPresenter.swift in Sources */,
|
||||
30697C3D21F63C990064FCAC /* UIViewExtension.swift in Sources */,
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ class AdvancedSettingsTableViewController: UITableViewController {
|
|||
|
||||
let encryptInASCIIArmoredSwitch: UISwitch = {
|
||||
let uiSwitch = UISwitch()
|
||||
uiSwitch.onTintColor = Globals.blue
|
||||
uiSwitch.onTintColor = Colors.systemBlue
|
||||
uiSwitch.sizeToFit()
|
||||
uiSwitch.addTarget(self, action: #selector(encryptInASCIIArmoredAction(_:)), for: UIControl.Event.valueChanged)
|
||||
return uiSwitch
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ class GeneralSettingsTableViewController: BasicStaticTableViewController {
|
|||
|
||||
let hideUnknownSwitch: UISwitch = {
|
||||
let uiSwitch = UISwitch()
|
||||
uiSwitch.onTintColor = Globals.blue
|
||||
uiSwitch.onTintColor = Colors.systemBlue
|
||||
uiSwitch.sizeToFit()
|
||||
uiSwitch.addTarget(self, action: #selector(hideUnknownSwitchAction(_:)), for: UIControl.Event.valueChanged)
|
||||
return uiSwitch
|
||||
|
|
@ -22,7 +22,7 @@ class GeneralSettingsTableViewController: BasicStaticTableViewController {
|
|||
|
||||
let hideOTPSwitch: UISwitch = {
|
||||
let uiSwitch = UISwitch()
|
||||
uiSwitch.onTintColor = Globals.blue
|
||||
uiSwitch.onTintColor = Colors.systemBlue
|
||||
uiSwitch.sizeToFit()
|
||||
uiSwitch.addTarget(self, action: #selector(hideOTPSwitchAction(_:)), for: UIControl.Event.valueChanged)
|
||||
return uiSwitch
|
||||
|
|
@ -30,7 +30,7 @@ class GeneralSettingsTableViewController: BasicStaticTableViewController {
|
|||
|
||||
let rememberPGPPassphraseSwitch: UISwitch = {
|
||||
let uiSwitch = UISwitch()
|
||||
uiSwitch.onTintColor = Globals.blue
|
||||
uiSwitch.onTintColor = Colors.systemBlue
|
||||
uiSwitch.sizeToFit()
|
||||
uiSwitch.addTarget(self, action: #selector(rememberPGPPassphraseSwitchAction(_:)), for: UIControl.Event.valueChanged)
|
||||
uiSwitch.isOn = SharedDefaults[.isRememberPGPPassphraseOn]
|
||||
|
|
@ -39,7 +39,7 @@ class GeneralSettingsTableViewController: BasicStaticTableViewController {
|
|||
|
||||
let rememberGitCredentialPassphraseSwitch: UISwitch = {
|
||||
let uiSwitch = UISwitch()
|
||||
uiSwitch.onTintColor = Globals.blue
|
||||
uiSwitch.onTintColor = Colors.systemBlue
|
||||
uiSwitch.sizeToFit()
|
||||
uiSwitch.addTarget(self, action: #selector(rememberGitCredentialPassphraseSwitchAction(_:)), for: UIControl.Event.valueChanged)
|
||||
uiSwitch.isOn = SharedDefaults[.isRememberGitCredentialPassphraseOn]
|
||||
|
|
@ -48,7 +48,7 @@ class GeneralSettingsTableViewController: BasicStaticTableViewController {
|
|||
|
||||
let showFolderSwitch: UISwitch = {
|
||||
let uiSwitch = UISwitch()
|
||||
uiSwitch.onTintColor = Globals.blue
|
||||
uiSwitch.onTintColor = Colors.systemBlue
|
||||
uiSwitch.sizeToFit()
|
||||
uiSwitch.addTarget(self, action: #selector(showFolderSwitchAction(_:)), for: UIControl.Event.valueChanged)
|
||||
uiSwitch.isOn = SharedDefaults[.isShowFolderOn]
|
||||
|
|
@ -57,7 +57,7 @@ class GeneralSettingsTableViewController: BasicStaticTableViewController {
|
|||
|
||||
let hidePasswordImagesSwitch: UISwitch = {
|
||||
let uiSwitch = UISwitch()
|
||||
uiSwitch.onTintColor = Globals.blue
|
||||
uiSwitch.onTintColor = Colors.systemBlue
|
||||
uiSwitch.sizeToFit()
|
||||
uiSwitch.addTarget(self, action: #selector(hidePasswordImagesSwitchAction(_:)), for: UIControl.Event.valueChanged)
|
||||
uiSwitch.isOn = SharedDefaults[.isHidePasswordImagesOn]
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ class GitSSHKeyArmorSettingTableViewController: AutoCellHeightUITableViewControl
|
|||
armorPrivateKeyTextView.delegate = self
|
||||
|
||||
scanPrivateKeyCell?.textLabel?.text = "ScanPrivateKeyQrCodes".localize()
|
||||
scanPrivateKeyCell?.textLabel?.textColor = Globals.blue
|
||||
scanPrivateKeyCell?.textLabel?.textColor = Colors.systemBlue
|
||||
scanPrivateKeyCell?.selectionStyle = .default
|
||||
scanPrivateKeyCell?.accessoryType = .disclosureIndicator
|
||||
}
|
||||
|
|
|
|||
|
|
@ -92,12 +92,12 @@ class PGPKeyArmorSettingTableViewController: AutoCellHeightUITableViewController
|
|||
super.viewDidLoad()
|
||||
|
||||
scanPublicKeyCell?.textLabel?.text = "ScanPublicKeyQrCodes".localize()
|
||||
scanPublicKeyCell?.textLabel?.textColor = Globals.blue
|
||||
scanPublicKeyCell?.textLabel?.textColor = Colors.systemBlue
|
||||
scanPublicKeyCell?.selectionStyle = .default
|
||||
scanPublicKeyCell?.accessoryType = .disclosureIndicator
|
||||
|
||||
scanPrivateKeyCell?.textLabel?.text = "ScanPrivateKeyQrCodes".localize()
|
||||
scanPrivateKeyCell?.textLabel?.textColor = Globals.blue
|
||||
scanPrivateKeyCell?.textLabel?.textColor = Colors.systemBlue
|
||||
scanPrivateKeyCell?.selectionStyle = .default
|
||||
scanPrivateKeyCell?.accessoryType = .disclosureIndicator
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,18 +48,18 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl
|
|||
|
||||
deletePasswordCell = UITableViewCell(style: .default, reuseIdentifier: "default")
|
||||
deletePasswordCell!.textLabel?.text = "DeletePassword".localize()
|
||||
deletePasswordCell!.textLabel?.textColor = Globals.red
|
||||
deletePasswordCell!.textLabel?.textColor = Colors.systemRed
|
||||
deletePasswordCell?.selectionStyle = .default
|
||||
|
||||
scanQRCodeCell = UITableViewCell(style: .default, reuseIdentifier: "default")
|
||||
scanQRCodeCell?.textLabel?.text = "AddOneTimePassword".localize()
|
||||
scanQRCodeCell?.textLabel?.textColor = Globals.blue
|
||||
scanQRCodeCell?.textLabel?.textColor = Colors.systemBlue
|
||||
scanQRCodeCell?.selectionStyle = .default
|
||||
scanQRCodeCell?.accessoryType = .disclosureIndicator
|
||||
|
||||
memorablePasswordGeneratorCell = UITableViewCell(style: .default, reuseIdentifier: "default")
|
||||
memorablePasswordGeneratorCell?.textLabel?.text = "GetMemorableOne".localize()
|
||||
memorablePasswordGeneratorCell?.textLabel?.textColor = Globals.blue
|
||||
memorablePasswordGeneratorCell?.textLabel?.textColor = Colors.systemBlue
|
||||
memorablePasswordGeneratorCell?.selectionStyle = .default
|
||||
memorablePasswordGeneratorCell?.accessoryType = .disclosureIndicator
|
||||
}
|
||||
|
|
@ -125,9 +125,7 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl
|
|||
additionsCell = tableView.dequeueReusableCell(withIdentifier: "textViewCell", for: indexPath) as?TextViewTableViewCell
|
||||
additionsCell?.contentTextView.delegate = self
|
||||
additionsCell?.setContent(content: cellData[PasswordEditorCellKey.content] as? String)
|
||||
if #available(iOS 13.0, *) {
|
||||
additionsCell?.contentTextView.textColor = .label
|
||||
}
|
||||
additionsCell?.contentTextView.textColor = Colors.label
|
||||
return additionsCell!
|
||||
case .deletePasswordCell:
|
||||
return deletePasswordCell!
|
||||
|
|
|
|||
|
|
@ -20,8 +20,6 @@ class RawPasswordViewController: UIViewController {
|
|||
rawPasswordTextView.textContainer.lineFragmentPadding = 0
|
||||
rawPasswordTextView.textContainerInset = .zero
|
||||
rawPasswordTextView.text = password?.plainText
|
||||
if #available(iOS 13.0, *) {
|
||||
rawPasswordTextView.textColor = .label
|
||||
}
|
||||
rawPasswordTextView.textColor = Colors.label
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ class LabelTableViewCell: UITableViewCell {
|
|||
if type == .password {
|
||||
if cellData?.content.isEmpty == false {
|
||||
contentLabel.text = Globals.passwordDots
|
||||
contentLabel.textColor = Globals.black
|
||||
contentLabel.textColor = Colors.label
|
||||
} else {
|
||||
contentLabel.text = ""
|
||||
}
|
||||
|
|
|
|||
|
|
@ -156,6 +156,11 @@
|
|||
"DiscardExplanation." = "Möchtest du wirklich alle Änderungen an den lokalen Daten für immer verwerfen? Diese Aktion kann nicht rückgängig gemacht werden.";
|
||||
"Resetting..." = "Verwerfe ...";
|
||||
|
||||
// Forget passcode
|
||||
"ForgotYourPasscode?" = "Passcode vergessen?";
|
||||
"ResetPass" = "Pass zurücksetzen";
|
||||
"ResetPassExplanation." = "Die App kann nur vollständig zurückgesetzt werden. Diese Aktion löscht alle lokalen Daten und Einstellungen. Daten im Password Store auf einem entfernten Server bleiben unberührt.";
|
||||
|
||||
// Time related
|
||||
"Unknown" = "Unbekannt";
|
||||
"JustNow" = "Gerade eben";
|
||||
|
|
|
|||
|
|
@ -40,17 +40,13 @@ open class PasscodeLockViewController: UIViewController, UITextFieldDelegate {
|
|||
self.view.addSubview(passcodeTextField)
|
||||
self.passcodeTextField = passcodeTextField
|
||||
|
||||
if #available(iOSApplicationExtension 13.0, *) {
|
||||
view.backgroundColor = .systemBackground
|
||||
passcodeTextField.backgroundColor = .secondarySystemBackground
|
||||
passcodeTextField.textColor = .secondaryLabel
|
||||
} else {
|
||||
view.backgroundColor = .white
|
||||
}
|
||||
view.backgroundColor = Colors.systemBackground
|
||||
passcodeTextField.backgroundColor = Colors.secondarySystemBackground
|
||||
passcodeTextField.textColor = Colors.secondaryLabel
|
||||
|
||||
let biometryAuthButton = UIButton(type: .custom)
|
||||
biometryAuthButton.setTitle("", for: .normal)
|
||||
biometryAuthButton.setTitleColor(Globals.blue, for: .normal)
|
||||
biometryAuthButton.setTitleColor(Colors.systemBlue, for: .normal)
|
||||
biometryAuthButton.addTarget(self, action: #selector(bioButtonPressedAction(_:)), for: .touchUpInside)
|
||||
biometryAuthButton.isHidden = true
|
||||
biometryAuthButton.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
|
@ -59,24 +55,20 @@ open class PasscodeLockViewController: UIViewController, UITextFieldDelegate {
|
|||
|
||||
let myContext = LAContext()
|
||||
var authError: NSError?
|
||||
if #available(iOS 8.0, macOS 10.12.1, *) {
|
||||
if myContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &authError) {
|
||||
var biometryType = "TouchId".localize()
|
||||
if #available(iOS 11.0, *) {
|
||||
if myContext.biometryType == LABiometryType.faceID {
|
||||
biometryType = "FaceId".localize()
|
||||
}
|
||||
}
|
||||
biometryAuthButton.setTitle(biometryType, for: .normal)
|
||||
biometryAuthButton.titleLabel?.font = UIFont.systemFont(ofSize: UIFont.systemFontSize)
|
||||
biometryAuthButton.isHidden = false
|
||||
if myContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &authError) {
|
||||
if #available(iOS 11.0, *), myContext.biometryType == .faceID {
|
||||
biometryAuthButton.setTitle("FaceId".localize(), for: .normal)
|
||||
} else {
|
||||
biometryAuthButton.setTitle("TouchId".localize(), for: .normal)
|
||||
}
|
||||
biometryAuthButton.titleLabel?.font = UIFont.systemFont(ofSize: UIFont.systemFontSize)
|
||||
biometryAuthButton.isHidden = false
|
||||
}
|
||||
|
||||
|
||||
let forgotPasscodeButton = UIButton(type: .custom)
|
||||
forgotPasscodeButton.setTitle("ForgotYourPasscode?".localize(), for: .normal)
|
||||
forgotPasscodeButton.titleLabel?.font = UIFont.systemFont(ofSize: UIFont.systemFontSize)
|
||||
forgotPasscodeButton.setTitleColor(Globals.blue, for: .normal)
|
||||
forgotPasscodeButton.setTitleColor(Colors.systemBlue, for: .normal)
|
||||
forgotPasscodeButton.addTarget(self, action: #selector(forgotPasscodeButtonPressedAction(_:)), for: .touchUpInside)
|
||||
// hide the forgotPasscodeButton if the native app is running
|
||||
forgotPasscodeButton.isHidden = self.isCancellable
|
||||
|
|
@ -86,7 +78,7 @@ open class PasscodeLockViewController: UIViewController, UITextFieldDelegate {
|
|||
|
||||
let cancelButton = UIButton(type: .custom)
|
||||
cancelButton.setTitle("Cancel".localize(), for: .normal)
|
||||
cancelButton.setTitleColor(Globals.blue, for: .normal)
|
||||
cancelButton.setTitleColor(Colors.systemBlue, for: .normal)
|
||||
cancelButton.addTarget(self, action: #selector(passcodeLockDidCancel), for: .touchUpInside)
|
||||
cancelButton.isHidden = !self.isCancellable
|
||||
cancelButton.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
|
@ -183,15 +175,12 @@ open class PasscodeLockViewController: UIViewController, UITextFieldDelegate {
|
|||
let myContext = LAContext()
|
||||
let myLocalizedReasonString = "AuthenticationNeeded.".localize()
|
||||
var authError: NSError?
|
||||
|
||||
if #available(iOS 8.0, *) {
|
||||
if myContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &authError) {
|
||||
myContext.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: myLocalizedReasonString) { success, evaluateError in
|
||||
if success {
|
||||
DispatchQueue.main.async {
|
||||
// user authenticated successfully, take appropriate action
|
||||
self.passcodeLockDidSucceed()
|
||||
}
|
||||
if myContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &authError) {
|
||||
myContext.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: myLocalizedReasonString) { success, evaluateError in
|
||||
if success {
|
||||
DispatchQueue.main.async {
|
||||
// user authenticated successfully, take appropriate action
|
||||
self.passcodeLockDidSucceed()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
51
passKit/Helpers/Colors.swift
Normal file
51
passKit/Helpers/Colors.swift
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
//
|
||||
// Colors.swift
|
||||
// passKit
|
||||
//
|
||||
// Created by Danny Moesch on 01.10.19.
|
||||
// Copyright © 2019 Bob Sun. All rights reserved.
|
||||
//
|
||||
|
||||
public struct Colors {
|
||||
public static let label: UIColor = {
|
||||
if #available(iOS 13.0, *) {
|
||||
return .label
|
||||
}
|
||||
return .black
|
||||
}()
|
||||
|
||||
public static let secondaryLabel: UIColor = {
|
||||
if #available(iOS 13.0, *) {
|
||||
return .secondaryLabel
|
||||
}
|
||||
return .init(red: 60.0, green: 60.0, blue: 67.0, alpha: 0.6)
|
||||
}()
|
||||
|
||||
public static let systemBackground: UIColor = {
|
||||
if #available(iOS 13.0, *) {
|
||||
return .systemBackground
|
||||
}
|
||||
return .white
|
||||
}()
|
||||
|
||||
public static let secondarySystemBackground: UIColor = {
|
||||
if #available(iOS 13.0, *) {
|
||||
return .secondarySystemBackground
|
||||
}
|
||||
return .init(red: 242.0, green: 242.0, blue: 247.0, alpha: 1.0)
|
||||
}()
|
||||
|
||||
public static let systemRed: UIColor = {
|
||||
if #available(iOS 13.0, *) {
|
||||
return .systemRed
|
||||
}
|
||||
return .red
|
||||
}()
|
||||
|
||||
public static let systemBlue: UIColor = {
|
||||
if #available(iOS 13.0, *) {
|
||||
return .systemBlue
|
||||
}
|
||||
return .blue
|
||||
}()
|
||||
}
|
||||
|
|
@ -41,14 +41,6 @@ public class Globals {
|
|||
public static let passwordFont = UIFont(name: "Courier-Bold", size: UIFont.labelFontSize - 1)
|
||||
|
||||
// UI related
|
||||
public static let red = UIColor.systemRed
|
||||
public static let blue = UIColor.systemBlue
|
||||
public static let black: UIColor = {
|
||||
if #available(iOSApplicationExtension 13.0, *) {
|
||||
return UIColor.label
|
||||
}
|
||||
return UIColor.black
|
||||
}()
|
||||
public static let tableCellButtonSize = CGFloat(20.0)
|
||||
|
||||
private init() { }
|
||||
|
|
|
|||
|
|
@ -22,11 +22,11 @@ public class Utils {
|
|||
for (index, element) in plainPassword.unicodeScalars.enumerated() {
|
||||
var charColor = UIColor.darkText
|
||||
if NSCharacterSet.decimalDigits.contains(element) {
|
||||
charColor = Globals.red
|
||||
charColor = Colors.systemRed
|
||||
} else if !NSCharacterSet.letters.contains(element) {
|
||||
charColor = Globals.blue
|
||||
charColor = Colors.systemBlue
|
||||
} else {
|
||||
charColor = Globals.black
|
||||
charColor = Colors.label
|
||||
}
|
||||
attributedPassword.addAttribute(NSAttributedString.Key.foregroundColor, value: charColor, range: NSRange(location: index, length: 1))
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue