Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
158 changes: 75 additions & 83 deletions DashWallet.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"images" : [
{
"filename" : "app-review.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "app-review@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "app-review@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"images" : [
{
"filename" : "logo.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "logo@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "logo@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"images" : [
{
"filename" : "support.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "support@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "support@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import DashUIKit
import SwiftUI
import DashUIKit

// MARK: - MayaAmountView

Expand Down
184 changes: 0 additions & 184 deletions DashWallet/Sources/UI/Menu/Settings/About/About.storyboard

This file was deleted.

119 changes: 119 additions & 0 deletions DashWallet/Sources/UI/Menu/Settings/About/AboutDashView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
//
// Created by Roman Chornyi
// Copyright © 2026 Dash Core Group. All rights reserved.
//
// Licensed under the MIT License (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

import SwiftUI
import DashUIKit

struct AboutDashView: View {

@StateObject private var viewModel = AboutDashViewModel()

var onBack: (() -> Void)? = nil
var onContactSupport: (() -> Void)? = nil

init(onBack: (() -> Void)? = nil, onContactSupport: (() -> Void)? = nil) {
self.onBack = onBack
self.onContactSupport = onContactSupport
}

var body: some View {
VStack(alignment: .leading, spacing: 0) {
NavigationBar(
leading: { NavigationBarElement.back.button { onBack?() } }
)

ScrollView {
VStack(alignment: .leading, spacing: 20) {
Image(dash: .custom("logo"))
.padding(.vertical, 20)
.frame(maxWidth: .infinity, alignment: .center)

VStack(alignment: .leading, spacing: 2) {
List1View(label: NSLocalizedString("App version", comment: "AboutDash"), value: viewModel.appVersion)
List1View(label: NSLocalizedString("DashSync", comment: "AboutDash"), value: viewModel.dashSyncVersion)
}
.modifier(MenuViewModifier())

VStack(alignment: .leading, spacing: 2) {
List1View(label: NSLocalizedString("Explore Dash", comment: "AboutDash"), value: viewModel.exploreStatus)
List1View(label: NSLocalizedString("Last device sync", comment: "AboutDash"), value: viewModel.lastDeviceSync)
List1View(label: NSLocalizedString("Last device update", comment: "AboutDash"), value: viewModel.lastDeviceUpdate)
}
.modifier(MenuViewModifier())

VStack(alignment: .leading, spacing: 2) {
MenuItem(
title: NSLocalizedString("Review app", comment: "AboutDash"),
icon: .custom("app-review"),
action: { viewModel.reviewApp() }
)
MenuItem(
title: NSLocalizedString("Contact support", comment: "AboutDash"),
icon: .custom("support"),
action: { onContactSupport?() }
)
}
.modifier(MenuViewModifier())

VStack(alignment: .center, spacing: 2) {
VStack(alignment: .center, spacing: 2) {
Text(NSLocalizedString("Dash Wallet is an open sourced app forked from Bitcoin Wallet", comment: "AboutDash"))
.font(Font.dash.footnote)
.foregroundStyle(Color.dash.tertiaryText)
.multilineTextAlignment(.center)

repositoryLink
}
.padding(.horizontal, 14)
.padding(.vertical, 8)

Text(NSLocalizedString("Copyright © 2026 Dash Core Group", comment: "AboutDash"))
.font(Font.dash.footnote)
.foregroundStyle(Color.dash.tertiaryText)
.multilineTextAlignment(.center)
.padding(.horizontal, 14)
.padding(.vertical, 8)
}
.shadow(color: Color.dash.shadow, radius: 10, x: 0, y: 5)
.padding(.horizontal, 20)
}
.padding(.horizontal, 20)
.padding(.bottom, 20)
}
}
.background(Color.dash.primaryBackground)
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

private var repositoryLink: some View {
guard let url = URL(string: viewModel.repositoryURL) else {
return AnyView(EmptyView())
}

return AnyView(
Link(destination: url) {
Text(viewModel.repositoryURL)
.font(Font.dash.footnote)
.foregroundStyle(Color.dash.blueText)
.multilineTextAlignment(.center)
}
)
}
}

#Preview {
AboutDashView()
}
150 changes: 150 additions & 0 deletions DashWallet/Sources/UI/Menu/Settings/About/AboutDashViewModel.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
//
// Created by Roman Chornyi
// Copyright © 2026 Dash Core Group. All rights reserved.
//
// Licensed under the MIT License (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://opensource.org/licenses/MIT
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

import SwiftUI
import UIKit
import StoreKit

@MainActor
final class AboutDashViewModel: ObservableObject {

@Published private(set) var appVersion: String = ""
@Published private(set) var dashSyncVersion: String = ""
@Published private(set) var exploreStatus: String = ""
@Published private(set) var lastDeviceSync: String = ""
@Published private(set) var lastDeviceUpdate: String = ""

let repositoryURL = "https://github.com/dashpay/dashwallet-ios"

private var databaseObserver: NSObjectProtocol?

private static let syncDateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateStyle = .medium
formatter.timeStyle = .short
return formatter
}()

private static let updateDateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateStyle = .medium
formatter.timeStyle = .none
return formatter
}()

init() {
// Touching `ExploreDash.shared` boots Firebase Storage, which throws an
// NSException inside SwiftUI previews. Short-circuit to sample data there.
guard !Self.isRunningInPreview else {
applyPreviewData()
return
}

appVersion = Self.makeAppVersion()
dashSyncVersion = Self.makeDashSyncVersion()
reloadExploreState()

databaseObserver = NotificationCenter.default.addObserver(
forName: ExploreDatabaseSyncManager.databaseHasBeenUpdatedNotification,
object: nil,
queue: .main
) { [weak self] _ in
Task { @MainActor in
self?.reloadExploreState()
}
}
}

deinit {
if let databaseObserver {
NotificationCenter.default.removeObserver(databaseObserver)
}
}

// MARK: - Actions

func reviewApp() {
guard let scene = UIApplication.shared.connectedScenes
.first(where: { $0.activationState == .foregroundActive }) as? UIWindowScene else {
return
}
SKStoreReviewController.requestReview(in: scene)
}

// MARK: - Explore Dash state

private func reloadExploreState() {
let explore = ExploreDash.shared

switch explore.syncState {
case .inititialing, .fetchingInfo:
exploreStatus = NSLocalizedString("Fetching Info", comment: "Explore Dash")
case .syncing:
exploreStatus = NSLocalizedString("Syncing...", comment: "Explore Dash")
case .synced:
exploreStatus = NSLocalizedString("Synced", comment: "Explore Dash")
case .error:
exploreStatus = NSLocalizedString("Sync failed", comment: "Explore Dash")
}

if let syncDate = explore.lastSyncTryDate ?? explore.lastFailedSyncDate {
lastDeviceSync = Self.syncDateFormatter.string(from: syncDate)
} else {
lastDeviceSync = "-"
}

lastDeviceUpdate = Self.updateDateFormatter.string(from: explore.lastServerUpdateDate)
}

// MARK: - Version strings

private static func makeAppVersion() -> String {
let info = Bundle.main.infoDictionary
let shortVersion = info?["CFBundleShortVersionString"] as? String ?? "?"
let buildVersion = info?["CFBundleVersion"] as? String ?? "?"

let chain = DWEnvironment.sharedInstance().currentChain
let networkSuffix = chain.isMainnet() ? "" : " (\(chain.name))"

return "\(shortVersion) - \(buildVersion)\(networkSuffix)"
}

private static func makeDashSyncVersion() -> String {
guard let path = Bundle.main.path(forResource: "DashSyncCurrentCommit", ofType: nil),
let raw = try? String(contentsOfFile: path, encoding: .utf8) else {
return "DashSync ?"
}

let commit = raw.trimmingCharacters(in: .whitespacesAndNewlines)
let shortCommit = commit.count > 7 ? String(commit.prefix(7)) : commit
return "\(shortCommit.isEmpty ? "?" : shortCommit)"
}

// MARK: - Preview

private static var isRunningInPreview: Bool {
ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] == "1"
}

private func applyPreviewData() {
appVersion = "8.6.0 - 1"
dashSyncVersion = "1a2b3c4"
exploreStatus = "Synced"
lastDeviceSync = "Jun 22, 2026 at 7:26 PM"
lastDeviceUpdate = "Jun 22, 2026"
}
}
28 changes: 0 additions & 28 deletions DashWallet/Sources/UI/Menu/Settings/About/DWAboutViewController.h

This file was deleted.

Loading
Loading