Skip to content

Commit

Permalink
General: Release 1.5.2 (#263)
Browse files Browse the repository at this point in the history
  • Loading branch information
anian03 authored Dec 17, 2024
2 parents dcd8570 + 3de04a5 commit 4212ca4
Show file tree
Hide file tree
Showing 23 changed files with 535 additions and 143 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"originHash" : "5c8626acffff005e2642c0e07e1f755598c39323b3bb020fb01711f64303781e",
"originHash" : "60201ec1c26be7adad9fdf6611cc7c649da23b2d45f1aa466264a869dc6de3a1",
"pins" : [
{
"identity" : "apollon-ios-module",
Expand All @@ -15,8 +15,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/ls1intum/artemis-ios-core-modules",
"state" : {
"revision" : "83a8eafae4d7b098e303aa0c06b392c505852b7f",
"version" : "15.1.2"
"revision" : "f28438e6960df1c91bbfa59e53f46670430f509e",
"version" : "15.1.4"
}
},
{
Expand Down Expand Up @@ -85,10 +85,10 @@
{
"identity" : "smile",
"kind" : "remoteSourceControl",
"location" : "https://github.com/onmyway133/Smile",
"location" : "https://github.com/onmyway133/Smile.git",
"state" : {
"revision" : "40604722a7a56f735124e069fcbb58307637744b",
"version" : "2.1.0"
"branch" : "6bacbf7",
"revision" : "6bacbf74638eb725fb0dd3e728341bc17acf8958"
}
},
{
Expand Down
2 changes: 1 addition & 1 deletion Artemis/Supporting/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.5.1</string>
<string>1.5.2</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>ITSAppUsesNonExemptEncryption</key>
Expand Down
4 changes: 3 additions & 1 deletion ArtemisKit/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ let package = Package(
])
],
dependencies: [
.package(url: "https://github.com/onmyway133/Smile.git", revision: "6bacbf7"),
.package(url: "https://github.com/Kelvas09/EmojiPicker.git", from: "1.0.0"),
.package(url: "https://github.com/ls1intum/apollon-ios-module", .upToNextMajor(from: "1.0.2")),
.package(url: "https://github.com/ls1intum/artemis-ios-core-modules", .upToNextMajor(from: "15.1.2")),
Expand Down Expand Up @@ -119,7 +120,8 @@ let package = Package(
.product(name: "SharedModels", package: "artemis-ios-core-modules"),
.product(name: "SharedServices", package: "artemis-ios-core-modules"),
.product(name: "UserStore", package: "artemis-ios-core-modules"),
.product(name: "RswiftLibrary", package: "R.swift")
.product(name: "RswiftLibrary", package: "R.swift"),
.product(name: "Smile", package: "Smile")
],
plugins: [
.plugin(name: "RswiftGeneratePublicResources", package: "R.swift")
Expand Down
6 changes: 3 additions & 3 deletions ArtemisKit/Sources/Faq/Services/FaqService/FaqService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@
import Common
import SharedModels

protocol FaqService {
public protocol FaqService {
func getFaqs(for courseId: Int) async -> DataState<[FaqDTO]>
func getFaq(with faqId: Int64, for courseId: Int) async -> DataState<FaqDTO>
}

enum FaqServiceFactory {
static let shared: FaqService = FaqServiceImpl()
public enum FaqServiceFactory {
public static let shared: FaqService = FaqServiceImpl()
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
"lecture" = "Lecture";
"channelsUnavailable" = "No Channels";
"lecturesUnavailable" = "No Lectures";
"faqs" = "FAQs";
"faqsUnavailable" = "No FAQs";
"membersUnavailable" = "No Members";
"messageAction" = "Message %@";
"mentionSlideNumber" = "Slide %i";
Expand All @@ -24,6 +26,7 @@
"code" = "Code";
"link" = "Link";
"uploadImage" = "Upload Image";
"uploadFile" = "Upload File";

// MARK: SendMessageMentionContentView
"members" = "Members";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,9 @@ protocol MessagesService {
func sendAnswerMessage(for courseId: Int, message: Message, content: String) async -> NetworkResponse

/**
* Perform a post request for uploading a jpeg image in a specific conversation to the server.
*/
func uploadImage(for courseId: Int, and conversationId: Int64, image: Data) async -> DataState<String>
* Perform a post request for uploading a file in a specific conversation to the server.
*/
func uploadFile(for courseId: Int, and conversationId: Int64, file: Data, filename: String, mimeType: String) async -> DataState<String>

/**
* Perform a delete request for a message in a specific course to the server.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -233,30 +233,32 @@ struct MessagesServiceImpl: MessagesService {
}
}

struct UploadImageResult: Codable {
struct UploadFileResult: Codable {
let path: String
}

func uploadImage(for courseId: Int, and conversationId: Int64, image: Data) async -> DataState<String> {
if image.count > 5 * 1024 * 1024 {
return .failure(error: .init(title: "File too big to upload"))
}

let request = MultipartFormDataRequest(path: "api/files/courses/\(courseId)/conversations/\(conversationId)")
request.addDataField(named: "file",
filename: "\(UUID().uuidString).jpg",
data: image,
mimeType: "image/jpeg")

let result: Swift.Result<(UploadImageResult, Int), APIClientError> = await client.sendRequest(request)

switch result {
case .success(let response):
return .done(response: response.0.path)
case .failure(let failure):
return .failure(error: .init(error: failure))
}
}
func uploadFile(for courseId: Int, and conversationId: Int64, file: Data, filename: String, mimeType: String) async -> DataState<String> {
// Check file size limit
let maxFileSize = 5 * 1024 * 1024
if file.count > maxFileSize {
return .failure(error: .init(title: "File too big to upload"))
}

let request = MultipartFormDataRequest(path: "api/files/courses/\(courseId)/conversations/\(conversationId)")
request.addDataField(named: "file",
filename: filename,
data: file,
mimeType: mimeType)

let result: Swift.Result<(UploadFileResult, Int), APIClientError> = await client.sendRequest(request)

switch result {
case .success(let response):
return .done(response: response.0.path)
case .failure(let failure):
return .failure(error: .init(error: failure))
}
}

struct DeleteMessageRequest: APIRequest {
typealias Response = RawResponse
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -204,8 +204,8 @@ extension MessagesServiceStub: MessagesService {
func unarchiveChannel(for courseId: Int, channelId: Int64) async -> NetworkResponse {
.loading
}

func uploadImage(for courseId: Int, and conversationId: Int64, image: Data) async -> DataState<String> {
func uploadFile(for courseId: Int, and conversationId: Int64, file: Data, filename: String, mimeType: String) async -> DataState<String> {
.loading
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,10 @@ class SwipeToReplyState {

/// Update all view properies associated with swiping to reply
func update(with distance: CGFloat) {
overlayOffset = 200 * exp((distance - 10) / 30)
messageBlur = max((-distance - 25) * 0.2 * blurIntensity, 0)
overlayOpacity = max(0, min(-(distance + 40) * 0.05, 1))
overlayScale = max(0, min(-(distance + 40) * 0.03, 1))
overlayOffset = 200 * exp((distance - 15) / 30)
messageBlur = max((-distance - 30) * 0.2 * blurIntensity, 0)
overlayOpacity = max(0, min(-(distance + 35) * 0.05, 1))
overlayScale = max(0, min(-(distance + 35) * 0.03, 1))

// If user dragged far enough to activate reply, let them know
if !swiped && distance < -70 {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
//
// SendMessageUploadFileViewModel.swift
// ArtemisKit
//
// Created by Eylul Naz Can on 9.12.2024.
//

import Foundation
import UniformTypeIdentifiers
import Common
import SwiftUI

@Observable
final class SendMessageUploadFileViewModel: UploadViewModel {

var fileData: Data?
var fileName: String?
var isFilePickerPresented = false

private let messagesService: MessagesService

static let allowedFileTypes: [UTType] = [
.png,
.jpeg,
.gif,
.svg,
.pdf,
.rtf,
.plainText,
.json,
.spreadsheet,
.presentation,
UTType(filenameExtension: "doc") ?? .data,
UTType(filenameExtension: "docx") ?? .data,
UTType(filenameExtension: "xls") ?? .spreadsheet,
UTType(filenameExtension: "xlsx") ?? .spreadsheet,
UTType(filenameExtension: "ppt") ?? .presentation,
UTType(filenameExtension: "pptx") ?? .presentation
]

init(
courseId: Int,
conversationId: Int64,
messagesService: MessagesService = MessagesServiceFactory.shared
) {
self.messagesService = messagesService
super.init(courseId: courseId, conversationId: conversationId)
}

/// Handles changes to the selected file URL
func filePicked(at url: URL) {
isFilePickerPresented = false
loadFileData(from: url)
}

/// Reads the file data from the provided URL
private func loadFileData(from url: URL) {
uploadState = .compressing
filePath = nil

do {
guard url.startAccessingSecurityScopedResource() else {
throw NSFileProviderError(.notAuthenticated)
}
let fileData = try Data(contentsOf: url)
url.stopAccessingSecurityScopedResource()
let fileName = url.lastPathComponent
handleFileSelection(fileData: fileData, fileName: fileName)
} catch {
uploadState = .failed(error: .init(title: "Failed to read the selected file. Please try again."))
}
}

/// Validates and handles the selected file data
private func handleFileSelection(fileData: Data, fileName: String) {
self.fileData = fileData
self.fileName = fileName

if fileData.count > 5 * 1024 * 1024 {
uploadState = .failed(
error: .init(title: "The file size exceeds the 5MB limit. Please choose a smaller file.")
)
return
}

let fileExtension = (fileName as NSString).pathExtension.lowercased()
guard Self.allowedFileTypes.contains(where: { $0.preferredFilenameExtension == fileExtension }) else {
uploadState = .failed(
error: .init(title: "The file type '\(fileExtension)' is not supported.")
)
return
}

upload(data: fileData, fileName: fileName, mimeType: fileExtension)
}

private func upload(data: Data, fileName: String, mimeType: String) {
uploadState = .uploading

uploadTask = Task {
let result = await messagesService.uploadFile(for: courseId, and: conversationId, file: data, filename: fileName, mimeType: mimeType)
if Task.isCancelled { return }

switch result {
case .loading:
break
case .failure(let error):
uploadState = .failed(error: error)
case .done(let response):
filePath = response
uploadState = .done
}
}
}

func resetFileSelection() {
fileData = nil
fileName = nil
}
}
Loading

0 comments on commit 4212ca4

Please sign in to comment.