Skip to content
Open
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
3 changes: 2 additions & 1 deletion RIADigiDoc/DI/AppContainer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -481,7 +481,8 @@ extension Container {
var signingRootViewModel: Factory<SigningRootViewModel> {
self { @MainActor in
SigningRootViewModel(
dataStore: self.dataStore()
dataStore: self.dataStore(),
sharedContainerViewModel: self.sharedContainerViewModel()
)
}
}
Expand Down
12 changes: 3 additions & 9 deletions RIADigiDoc/UI/Component/Container/Signing/SigningRootView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,7 @@ struct SigningRootView: View {
signedContainer: container,
onSuccess: { container in
isSuccess = true
sharedContainerViewModel.removeLastContainer()
sharedContainerViewModel.setSignedContainer(container)
sharedContainerViewModel.setIsSignatureAdded(true)
viewModel.applySignedContainer(container, replacing: signedContainer)
}
)
}
Expand All @@ -71,9 +69,7 @@ struct SigningRootView: View {
MobileIdView(
signedContainer: container,
onSuccess: { container in
sharedContainerViewModel.removeLastContainer()
sharedContainerViewModel.setSignedContainer(container)
sharedContainerViewModel.setIsSignatureAdded(true)
viewModel.applySignedContainer(container, replacing: signedContainer)
}
)
}
Expand All @@ -82,9 +78,7 @@ struct SigningRootView: View {
SmartIdView(
signedContainer: container,
onSuccess: { container in
sharedContainerViewModel.removeLastContainer()
sharedContainerViewModel.setSignedContainer(container)
sharedContainerViewModel.setIsSignatureAdded(true)
viewModel.applySignedContainer(container, replacing: signedContainer)
}
)
}
Expand Down
19 changes: 18 additions & 1 deletion RIADigiDoc/UI/Component/HomeView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -309,14 +309,31 @@ struct HomeView: View {
}

private func handleFiles(_ files: [URL]) {
if !files.isEmpty {
guard !files.isEmpty else { return }

dismissHomeModals()
ModalPresentationUtil.dismissPresentedModals()

DispatchQueue.main.async {
pathManager.popToRoot()
viewModel.closeOpenContainers()
isFileOpeningLoading = true
viewModel.isImporting = false
viewModel.setFileOpeningMethod(.all)
viewModel.setChosenFiles(.success(files))
}
}

private func dismissHomeModals() {
showFilesBottomSheet = false
showSignatureBottomSheet = false
showCryptoBottomSheet = false
showHomeMenuBottomSheet = false
isAllFilesImporting = false
isSigningImporting = false
isCryptoImporting = false
}

private func handleIncoming(url: URL) {
let webEidURL = (WebEidUriUtil.isWebEidUri(url)) ? url : nil

Expand Down
35 changes: 35 additions & 0 deletions RIADigiDoc/Util/Presentation/ModalPresentationUtil.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright 2017 - 2026 Riigi Infosüsteemi Amet
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/

import UIKit

class ModalPresentationUtil {

@MainActor
public static func dismissPresentedModals() {
let rootViewControllers = UIApplication.shared.connectedScenes
.compactMap { $0 as? UIWindowScene }
.flatMap { $0.windows }
.compactMap { $0.rootViewController }

for rootViewController in rootViewControllers where rootViewController.presentedViewController != nil {
rootViewController.dismiss(animated: false)
}
}
}
6 changes: 6 additions & 0 deletions RIADigiDoc/ViewModel/HomeViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,12 @@ class HomeViewModel: HomeViewModelProtocol, Loggable {
sharedContainerViewModel.setFileOpeningMethod(method)
}

func closeOpenContainers() {
sharedContainerViewModel.setSignedContainer(nil)
sharedContainerViewModel.setCryptoContainer(nil)
sharedContainerViewModel.clearContainers()
}

func getRecentDocumentsFolder() -> URL? {
do {
return try Directories.getCacheDirectory(fileManager: fileManager)
Expand Down
1 change: 1 addition & 0 deletions RIADigiDoc/ViewModel/Protocols/HomeViewModelProtocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,5 @@ public protocol HomeViewModelProtocol: Sendable {
func getRecentDocumentsFolder() -> URL?
func getSharedFiles() async -> [URL]
func setFileOpeningMethod(_ method: FileOpeningMethod)
func closeOpenContainers()
}
26 changes: 25 additions & 1 deletion RIADigiDoc/ViewModel/Signing/RootView/SigningRootViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,42 @@
import UtilsLib
import Foundation
import Observation
import CommonsLib
import LibdigidocLibSwift

@Observable
@MainActor
class SigningRootViewModel: SigningRootViewModelProtocol, Loggable {

private let dataStore: DataStoreProtocol
private let sharedContainerViewModel: SharedContainerViewModelProtocol

init(dataStore: DataStoreProtocol) {
init(
dataStore: DataStoreProtocol,
sharedContainerViewModel: SharedContainerViewModelProtocol
) {
self.dataStore = dataStore
self.sharedContainerViewModel = sharedContainerViewModel
}

func getSelectedSigningMethod() async -> ActionMethod {
return await dataStore.getSelectedSigningMethod()
}

func applySignedContainer(
_ signedContainer: SignedContainerProtocol,
replacing containerBeingSigned: GeneralContainer?
) {
guard let containerBeingSigned,
sharedContainerViewModel.currentContainer() === containerBeingSigned else {
SigningRootViewModel.logger().info(
"Container was replaced while signing. Dropping the signed container"
)
return
}

sharedContainerViewModel.removeLastContainer()
sharedContainerViewModel.setSignedContainer(signedContainer)
sharedContainerViewModel.setIsSignatureAdded(true)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,15 @@
*/

import Foundation
import CommonsLib
import LibdigidocLibSwift

/// @mockable
@MainActor
public protocol SigningRootViewModelProtocol: Sendable {
func getSelectedSigningMethod() async -> ActionMethod
func applySignedContainer(
_ signedContainer: SignedContainerProtocol,
replacing containerBeingSigned: GeneralContainer?
)
}
67 changes: 67 additions & 0 deletions RIADigiDocTests/ViewModel/HomeViewModelTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,21 @@ import CommonsLib
import CommonsLibMocks
import UtilsLib
import UtilsLibMocks
import LibdigidocLibSwift

@MainActor
struct HomeViewModelTests {
private let mockSharedContainerViewModel: SharedContainerViewModelProtocolMock
private let mockFileManager: FileManagerProtocolMock
private let mockFileUtil: FileUtilProtocolMock
private let mockContainerUtil: ContainerUtilProtocolMock
private let viewModel: HomeViewModel

init() async throws {
mockSharedContainerViewModel = SharedContainerViewModelProtocolMock()
mockFileManager = FileManagerProtocolMock()
mockFileUtil = FileUtilProtocolMock()
mockContainerUtil = ContainerUtilProtocolMock()
viewModel = HomeViewModel(
sharedContainerViewModel: mockSharedContainerViewModel,
fileManager: mockFileManager,
Expand Down Expand Up @@ -134,4 +137,68 @@ struct HomeViewModelTests {
return
}
}

@Test
func closeOpenContainers_clearsTheOpenContainer() async {
let sharedContainerViewModel = SharedContainerViewModel()
let viewModel = makeViewModel(sharedContainerViewModel: sharedContainerViewModel)
sharedContainerViewModel.setSignedContainer(makeSignedContainer())

viewModel.closeOpenContainers()

#expect(sharedContainerViewModel.containers().isEmpty)
#expect(sharedContainerViewModel.currentContainer() == nil)
}

@Test
func closeOpenContainers_clearsEveryNestedContainer() async {
let sharedContainerViewModel = SharedContainerViewModel()
let viewModel = makeViewModel(sharedContainerViewModel: sharedContainerViewModel)
sharedContainerViewModel.setSignedContainer(makeSignedContainer())
sharedContainerViewModel.setSignedContainer(makeSignedContainer())

#expect(sharedContainerViewModel.containers().count == 2)

viewModel.closeOpenContainers()

#expect(sharedContainerViewModel.containers().isEmpty)
#expect(sharedContainerViewModel.currentContainer() == nil)
}

@Test
func closeOpenContainers_doesNothingWhenNoContainerIsOpen() async {
let sharedContainerViewModel = SharedContainerViewModel()
let viewModel = makeViewModel(sharedContainerViewModel: sharedContainerViewModel)

viewModel.closeOpenContainers()

#expect(sharedContainerViewModel.containers().isEmpty)
#expect(sharedContainerViewModel.currentContainer() == nil)
}

@Test
func closeOpenContainers_releasesSignedAndCryptoContainerReferences() async {
viewModel.closeOpenContainers()

#expect(mockSharedContainerViewModel.setSignedContainerCallCount == 1)
#expect(mockSharedContainerViewModel.setSignedContainerArgValues.allSatisfy { $0 == nil })
#expect(mockSharedContainerViewModel.setCryptoContainerCallCount == 1)
#expect(mockSharedContainerViewModel.setCryptoContainerArgValues.allSatisfy { $0 == nil })
#expect(mockSharedContainerViewModel.clearContainersCallCount == 1)
}

private func makeViewModel(sharedContainerViewModel: SharedContainerViewModelProtocol) -> HomeViewModel {
HomeViewModel(
sharedContainerViewModel: sharedContainerViewModel,
fileManager: mockFileManager,
fileUtil: mockFileUtil
)
}

private func makeSignedContainer() -> SignedContainer {
SignedContainer(
fileManager: mockFileManager,
containerUtil: mockContainerUtil
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/*
* Copyright 2017 - 2026 Riigi Infosüsteemi Amet
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/

import Foundation
import Testing
import LibdigidocLibSwift
import CommonsLibMocks
import UtilsLibMocks

@MainActor
struct SigningRootViewModelTests {
private let mockDataStore: DataStoreProtocolMock
private let mockFileManager: FileManagerProtocolMock
private let mockContainerUtil: ContainerUtilProtocolMock
private let sharedContainerViewModel: SharedContainerViewModel
private let viewModel: SigningRootViewModel

init() async throws {
mockDataStore = DataStoreProtocolMock()
mockFileManager = FileManagerProtocolMock()
mockContainerUtil = ContainerUtilProtocolMock()
sharedContainerViewModel = SharedContainerViewModel()
viewModel = SigningRootViewModel(
dataStore: mockDataStore,
sharedContainerViewModel: sharedContainerViewModel
)
}

private func makeSignedContainer() -> SignedContainer {
SignedContainer(
fileManager: mockFileManager,
containerUtil: mockContainerUtil
)
}

@Test
func applySignedContainer_replacesContainerWhenItIsStillTheOpenOne() async {
let containerBeingSigned = makeSignedContainer()
sharedContainerViewModel.setSignedContainer(containerBeingSigned)
let signedContainer = makeSignedContainer()

viewModel.applySignedContainer(signedContainer, replacing: containerBeingSigned)

#expect(sharedContainerViewModel.currentContainer() === signedContainer)
#expect(sharedContainerViewModel.containers().count == 1)
#expect(sharedContainerViewModel.getIsSignatureAdded())
}

@Test
func applySignedContainer_dropsResultWhenContainersWereCleared() async {
let containerBeingSigned = makeSignedContainer()
sharedContainerViewModel.setSignedContainer(containerBeingSigned)
sharedContainerViewModel.clearContainers()
let signedContainer = makeSignedContainer()

viewModel.applySignedContainer(signedContainer, replacing: containerBeingSigned)

#expect(sharedContainerViewModel.containers().isEmpty)
#expect(!sharedContainerViewModel.getIsSignatureAdded())
}

@Test
func applySignedContainer_dropsResultWhenAnotherContainerWasOpened() async {
let containerBeingSigned = makeSignedContainer()
sharedContainerViewModel.setSignedContainer(containerBeingSigned)
sharedContainerViewModel.clearContainers()

let externallyOpenedContainer = makeSignedContainer()
sharedContainerViewModel.setSignedContainer(externallyOpenedContainer)

let signedContainer = makeSignedContainer()

viewModel.applySignedContainer(signedContainer, replacing: containerBeingSigned)

#expect(sharedContainerViewModel.currentContainer() === externallyOpenedContainer)
#expect(sharedContainerViewModel.containers().count == 1)
#expect(!sharedContainerViewModel.getIsSignatureAdded())
}

@Test
func applySignedContainer_dropsResultWhenThereWasNoContainerBeingSigned() async {
let externallyOpenedContainer = makeSignedContainer()
sharedContainerViewModel.setSignedContainer(externallyOpenedContainer)
let signedContainer = makeSignedContainer()

viewModel.applySignedContainer(signedContainer, replacing: nil)

#expect(sharedContainerViewModel.currentContainer() === externallyOpenedContainer)
#expect(!sharedContainerViewModel.getIsSignatureAdded())
}
}
Loading