Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
a7e277f
Add strings
samwyndham Nov 24, 2025
40cea11
add helpers to error type
samwyndham Nov 24, 2025
87912b0
add rest API method for moving folders.
samwyndham Nov 24, 2025
2355b2e
Create a new version of Fetch Nodes Use Case, renaming the old versio…
samwyndham Nov 24, 2025
12c6cb6
Add convenience method for getting the name of a node
samwyndham Nov 24, 2025
023a80b
create WireCellsMoveNodeUseCase
samwyndham Nov 24, 2025
9618344
move CreateFolderCTA to its own file
samwyndham Nov 24, 2025
9bc7fae
Create Move to Folder feature views.
samwyndham Nov 24, 2025
12b0cd8
Integrate move to folder views
samwyndham Nov 24, 2025
ff43480
Lint & format
samwyndham Nov 24, 2025
fa70f4e
delete Old comment?
samwyndham Nov 24, 2025
b5f37d7
update cells sdk
samwyndham Nov 24, 2025
316c769
Add a method to fetch editor URL to the nodes API.
samwyndham Nov 25, 2025
d2cb5c1
Add WireCellsGetEditingURLUseCase
samwyndham Nov 25, 2025
a5a98f6
Integrate use case
samwyndham Nov 25, 2025
b8c18eb
Add edit button.
samwyndham Nov 25, 2025
694a6e6
Add edit file as a sheet navigation case.
samwyndham Nov 25, 2025
0c7b27b
Add EditFileView
samwyndham Nov 27, 2025
b99cdb8
Refactor View Model
samwyndham Nov 27, 2025
694eb11
conditionally enable editing files
samwyndham Nov 27, 2025
be23b58
Handle errors.
samwyndham Nov 27, 2025
9f783b8
Fix not opening the latest version in the conversation
samwyndham Nov 27, 2025
c422882
Fix not opening the latest version in the Files view.
samwyndham Nov 27, 2025
f01f328
Use items name in navigation bar title.
samwyndham Nov 28, 2025
1be1c41
Reload files after closing the editing view.
samwyndham Nov 28, 2025
3491cea
Tiny refactor.
samwyndham Nov 28, 2025
84a6c02
Fix memory leak.
samwyndham Nov 28, 2025
4009049
add polling
samwyndham Nov 28, 2025
8f3b787
Rename polling methods.
samwyndham Nov 28, 2025
041dc6c
Fix tests
samwyndham Dec 1, 2025
f5bb8a0
Add EditFileViewModelTests
samwyndham Dec 1, 2025
68c221d
lint and format
samwyndham Dec 1, 2025
8090c89
Have the back-end decide if something is editable.
samwyndham Dec 2, 2025
8e7b62f
Add fulu and imai deep links.
samwyndham Dec 2, 2025
39eed30
Add missing divider to menu
samwyndham Dec 2, 2025
1053b05
Merge branch 'develop' into feat/move-file-folder-WPB-21619-v2
samwyndham Dec 2, 2025
ab34a8e
Rename onTap to action.
samwyndham Dec 2, 2025
bd59a78
Rename MoveToFolderContent to MoveToFolderPageViewModel.MainContent.
samwyndham Dec 2, 2025
5b6b520
Rename MoveButtonState to MoveToFolderPageViewModel.MoveButtonState.
samwyndham Dec 2, 2025
ae363f1
Merge branch 'feat/move-file-folder-WPB-21619-v2' into feat/integrate…
samwyndham Dec 2, 2025
c0c7fb7
Merge branch 'develop' into feat/integrate-collabora-WPB-21651
samwyndham Dec 4, 2025
29884d2
Fix editing being available in recycle bin
samwyndham Dec 4, 2025
bcb4695
Remove from menu when it's in the recycle bin.
samwyndham Dec 4, 2025
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
2 changes: 1 addition & 1 deletion WireMessaging/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ let package = Package(
.library(name: "WireMessagingUI", targets: ["WireMessagingUI"])
],
dependencies: [
.package(url: "https://github.com/pydio/cells-sdk-swift.git", from: "0.1.1-alpha15"),
.package(url: "https://github.com/pydio/cells-sdk-swift.git", from: "0.1.1-alpha17"),
.package(url: "https://github.com/awslabs/aws-sdk-swift.git", from: "1.0.0"),
.package(url: "https://github.com/apple/swift-collections.git", from: "1.1.4"),
.package(name: "WireFoundation", path: "../WireFoundation"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public struct WireMessagingFactory {
private let lastOpenRequest: WireCellsLastOpenRequest
private let nodeCache = WireCellsNodeCache()
private let isFoldersEnabled: Bool
private let isCollaboraEnabled: Bool
private let nodeRenameNotifier: WireCellsNodeRenameNotifier

@MainActor var lastOpenRequestNodeID: UUID?
Expand All @@ -47,7 +48,8 @@ public struct WireMessagingFactory {
accessToken: any AccessTokenProvider,
fileCache: any FileCache,
contextProvider: any ManagedObjectContextProvider,
isFoldersEnabled: Bool
isFoldersEnabled: Bool,
isCollaboraEnabled: Bool
) {
// TODO: [WPB-18798] Remove serverURL temporary override when there exists a method to obtain the correct URL.
let serverURL = switch serverURL.host {
Expand Down Expand Up @@ -75,6 +77,7 @@ public struct WireMessagingFactory {
)
self.lastOpenRequest = WireCellsLastOpenRequest()
self.isFoldersEnabled = isFoldersEnabled
self.isCollaboraEnabled = isCollaboraEnabled
self.nodeRenameNotifier = WireCellsNodeRenameNotifier()
}

Expand Down Expand Up @@ -154,6 +157,7 @@ public extension WireMessagingFactory {
nodeRenameNotifier: nodeRenameNotifier,
fileCache: fileCache,
isFoldersEnabled: isFoldersEnabled,
isCollaboraEnabled: isCollaboraEnabled,
accentColorProvider: accentColorProvider
).environment(\.wireAccentColor, accentColorProvider())
)
Expand Down Expand Up @@ -190,12 +194,18 @@ public extension WireMessagingFactory {
updateTags: WireCellsUpdateTagsUseCase(nodesAPI: nodesAPI),
getTagSuggestions: WireCellsGetTagSuggestionsUseCase(nodesAPI: nodesAPI),
createFolder: WireCellsCreateFolderUseCase(nodesRepository: nodesAPI),
getEditingURL: WireCellsGetEditingURLUseCase(editingURLRepository: nodesAPI),
getAssetUseCase: WireCellsGetAssetUseCase(
localAssetRepository: localAssetRepository,
fileCache: fileCache
)
),
isCellsStatePending: false,
localAssetRepository: localAssetRepository,
nodesRepository: nodesAPI,
fileCache: fileCache,
isFoldersEnabled: false,
isCollaboraEnabled: isCollaboraEnabled,
accentColorProvider: accentColorProvider
)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ package struct WireCellsNodeNetworkModel: Equatable, Hashable, Sendable {
package let type: String?
package let isRecycled: Bool
package let isDraft: Bool
package let isEditable: Bool
package let contentUrl: URL?
package let contentHash: String?
package let mimeType: String?
Expand All @@ -51,6 +52,7 @@ package struct WireCellsNodeNetworkModel: Equatable, Hashable, Sendable {
type: String? = nil,
isRecycled: Bool = false,
isDraft: Bool = false,
isEditable: Bool = false,
contentUrl: URL? = nil,
contentHash: String? = nil,
mimeType: String? = nil,
Expand All @@ -70,6 +72,7 @@ package struct WireCellsNodeNetworkModel: Equatable, Hashable, Sendable {
self.type = type
self.isRecycled = isRecycled
self.isDraft = isDraft
self.isEditable = isEditable
self.contentUrl = contentUrl
self.contentHash = contentHash
self.mimeType = mimeType
Expand All @@ -94,6 +97,7 @@ package extension WireCellsNodeNetworkModel {
type: type.flatMap { WireCellsNodeType(rawValue: $0) },
isRecycled: isRecycled,
isDraft: isDraft,
isEditable: isEditable,
contentUrl: contentUrl,
contentHash: contentHash,
mimeType: mimeType,
Expand Down Expand Up @@ -145,6 +149,7 @@ package extension RestNode {
type: type?.rawValue ?? "",
isRecycled: isRecycled ?? false,
isDraft: isDraft ?? false,
isEditable: editorURLsKeys?.contains("collabora") ?? false,
contentUrl: preSignedGET?.url.flatMap(URL.init(string:)),
contentHash: contentHash,
mimeType: contentType,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
case moveFailed
}

package final actor NodesAPI: NodesAPIProtocol, WireCellsNodesRepositoryProtocol {
package final actor NodesAPI: NodesAPIProtocol, WireCellsNodesRepositoryProtocol,
WireCellsEditingURLRepositoryProtocol {
private let awsClient: AWSClient
private let restAPI: RestAPI
private let fileManager: FileManager
Expand Down Expand Up @@ -105,7 +106,7 @@
out: URL,
cellPath: String,
onProgressUpdate: @escaping @Sendable (UInt64) -> Void
) async throws {

Check warning on line 109 in WireMessaging/Sources/WireMessagingData/WireCells/NodesAPI/NodesAPI.swift

View workflow job for this annotation

GitHub Actions / Test Results

Value 'stream' was defined but never used; consider replacing with boolean test

Value 'stream' was defined but never used; consider replacing with boolean test
guard let stream = OutputStream(url: out, append: true) else {
throw NodesAPIError.failedToCreateWriteStream
}
Expand Down Expand Up @@ -149,6 +150,10 @@
}
}

package func getEditorURL(id: UUID) async throws -> (url: URL, date: Date)? {
try await restAPI.getEditorURL(id: id)
}

package func createPublicLink(nodeID: UUID, fileName: String) async throws -> WireCellsPublicLink {
try await restAPI.createPublicLink(uuid: nodeID, fileName: fileName)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,19 @@ final class RestAPI: Sendable {
}
}

func getEditorURL(id: UUID) async throws -> (url: URL, date: Date)? {
do {
let response = try await NodeServiceAPI.getByUuid(
uuid: id.transportString(),
flags: [.withEditorURLs],
apiConfiguration: makeConfiguration()
)
return response.editorURLs?["collabora"]?.info
} catch let error as ErrorResponse {
throw error.underlyingError
}
}

/// Renames a node.
///
/// - Parameters:
Expand Down Expand Up @@ -449,3 +462,18 @@ private extension ErrorResponse {
}
}
}

private extension RestPreSignedURL {

var info: (url: URL, date: Date)? {
guard
let urlString = url,
let url = URL(string: urlString),
let expiresAtString = expiresAt,
let expiresAtTimeInterval = TimeInterval(expiresAtString)
else {
return nil
}
return (url: url, date: Date(timeIntervalSinceNow: expiresAtTimeInterval))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,6 @@ public enum WireCellsLocalAssetRepositoryError: Error, Equatable {

case missingETag

/// A download for the requested asset is already in progress.

case downloadAlreadyInProgress

/// The requested asset is unknown to the repository.

case unknownAsset
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ public struct WireCellsNode: Equatable, Identifiable, Sendable {
public let type: WireCellsNodeType?
public let isRecycled: Bool
public let isDraft: Bool
public let isEditable: Bool
public let contentUrl: URL?
public let contentHash: String?
public let mimeType: String?
Expand All @@ -59,6 +60,7 @@ public struct WireCellsNode: Equatable, Identifiable, Sendable {
type: WireCellsNodeType? = nil,
isRecycled: Bool = false,
isDraft: Bool = false,
isEditable: Bool = false,
contentUrl: URL? = nil,
contentHash: String? = nil,
mimeType: String? = nil,
Expand All @@ -78,6 +80,7 @@ public struct WireCellsNode: Equatable, Identifiable, Sendable {
self.type = type
self.isRecycled = isRecycled
self.isDraft = isDraft
self.isEditable = isEditable
self.contentUrl = contentUrl
self.contentHash = contentHash
self.mimeType = mimeType
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//
// Wire
// Copyright (C) 2025 Wire Swiss GmbH
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see http://www.gnu.org/licenses/.
//

package import Foundation

// sourcery: AutoMockable
package protocol WireCellsEditingURLRepositoryProtocol: Sendable {

/// Returns a URL to an online editor where the document can be edited.
func getEditorURL(id: UUID) async throws -> (url: URL, date: Date)?

}
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,10 @@ package struct WireCellsGetAssetUseCase {
self.fileCache = fileCache
}

package func invoke(nodeID: UUID) async throws -> URL {
package func invoke(nodeID: UUID, eTag: String?) async throws -> URL {
// If the file is already downloaded, return the local URL.
if
let cacheKey = try await localAssetRepository.asset(nodeID: nodeID)?.downloadState.cacheKey,
let url = fileCache.fileURL(forKey: cacheKey) {
return url
if let fileURL = try await cachedAssetURL(for: nodeID, eTag: eTag) {
return fileURL
}

try await localAssetRepository.downloadAsset(nodeID: nodeID)
Expand All @@ -57,4 +55,24 @@ package struct WireCellsGetAssetUseCase {
return fileURL
}

// MARK: - Private helpers

/// Returns the cached asset URL if it exists and matches the provided eTag.
private func cachedAssetURL(for nodeID: UUID, eTag: String?) async throws -> URL? {
if
let eTag,
let asset = try await localAssetRepository.asset(nodeID: nodeID),
eTag == asset.eTag,
let cacheKey = asset.downloadState.cacheKey {
return fileCache.fileURL(forKey: cacheKey)
}

if let cacheKey = try await localAssetRepository.refreshAssetMetadata(nodeID: nodeID).asset.downloadState
.cacheKey {
return fileCache.fileURL(forKey: cacheKey)
}

return nil
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//
// Wire
// Copyright (C) 2025 Wire Swiss GmbH
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see http://www.gnu.org/licenses/.
//

package import Foundation

/// Returns a URL to an online editor where a document can be edited, along with the date when the URL was generated.
package struct WireCellsGetEditingURLUseCase {

private let editingURLRepository: any WireCellsEditingURLRepositoryProtocol

package init(
editingURLRepository: any WireCellsEditingURLRepositoryProtocol
) {
self.editingURLRepository = editingURLRepository
}

/// Fetches the editor URL for a given node ID.
///
/// - Parameter nodeID: The unique identifier of the node/document.
/// - Returns: The editor URL, or `nil` if no URL is available.
/// - Throws: An error if the request fails.
package func invoke(nodeID: UUID) async throws -> URL? {
try await editingURLRepository.getEditorURL(id: nodeID)?.url
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@
"conversation.wireCells.files.loadMore.title" = "Load more";
"conversation.wireCells.files.item.menu.open" = "Open";
"conversation.wireCells.files.item.menu.download" = "Download";
"conversation.wireCells.files.item.menu.editFile" = "Edit file";
"conversation.wireCells.files.item.menu.rename" = "Rename";
"conversation.wireCells.files.item.menu.moveToFolder" = "Move to folder";
"conversation.wireCells.files.item.menu.addOrRemoveTags" = "Add or remove tags";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,8 @@ struct WireCellsAttachmentsPreviewItemView: View {
}
}
.contentShape(Rectangle()) // Constrains the tappable content area of the view.
.onAppear(perform: refresh)
.onAppear(perform: viewModel.startPolling)
.onDisappear(perform: viewModel.stopPolling)
.onTapGesture(perform: open)
.quickLookPreview($viewModel.viewingURL)
}
Expand Down
Loading
Loading