Skip to content

Commit b0082ff

Browse files
authored
General: Release 1.2.0 - Merge pull request #154
2 parents 43091d8 + 8134e54 commit b0082ff

39 files changed

+1443
-426
lines changed

Artemis.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved

Lines changed: 10 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Artemis/Supporting/Info.plist

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
<key>CFBundlePackageType</key>
2222
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
2323
<key>CFBundleShortVersionString</key>
24-
<string>1.1.0</string>
24+
<string>1.2.0</string>
2525
<key>CFBundleVersion</key>
2626
<string>1</string>
2727
<key>LSRequiresIPhoneOS</key>

ArtemisKit/Package.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ let package = Package(
2222
.package(url: "https://github.com/daltoniam/Starscream.git", exact: "4.0.4"),
2323
.package(url: "https://github.com/Kelvas09/EmojiPicker.git", from: "1.0.0"),
2424
.package(url: "https://github.com/ls1intum/apollon-ios-module", .upToNextMajor(from: "1.0.2")),
25-
.package(url: "https://github.com/ls1intum/artemis-ios-core-modules", .upToNextMajor(from: "13.2.0")),
25+
.package(url: "https://github.com/ls1intum/artemis-ios-core-modules", .upToNextMajor(from: "14.0.3")),
2626
.package(url: "https://github.com/mac-cain13/R.swift.git", from: "7.0.0")
2727
],
2828
targets: [

ArtemisKit/Sources/CourseView/ExerciseTab/ExerciseDetailView.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ public struct ExerciseDetailView: View {
4747
}
4848
.task {
4949
await viewModel.loadExercise()
50+
await viewModel.loadAssociatedChannel()
5051
}
5152
.refreshable {
5253
await viewModel.refreshExercise()
@@ -218,6 +219,19 @@ private extension ExerciseDetailView {
218219
}
219220
}
220221
}
222+
223+
if let channel = viewModel.channel.value {
224+
Divider()
225+
.frame(height: 1.0)
226+
.overlay(Color.Artemis.artemisBlue)
227+
228+
ExerciseDetailCell(descriptionText: R.string.localizable.communication() + ":") {
229+
NavigationLink(value: ConversationPath(conversation: .channel(conversation: channel),
230+
coursePath: .init(id: viewModel.courseId))) {
231+
Text("\(channel.conversationName) \(Image(systemName: "chevron.forward"))")
232+
}
233+
}
234+
}
221235
}
222236
.background {
223237
RoundedRectangle(cornerRadius: 3.0)

ArtemisKit/Sources/CourseView/ExerciseTab/ExerciseDetailViewModel.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ final class ExerciseDetailViewModel {
1717
let exerciseId: Int
1818

1919
var exercise: DataState<Exercise>
20+
var channel: DataState<Channel> = .loading
2021

2122
var isFeedbackPresented = false
2223
var latestResultId: Int?
@@ -77,6 +78,10 @@ final class ExerciseDetailViewModel {
7778
webViewId = UUID()
7879
}
7980

81+
func loadAssociatedChannel() async {
82+
channel = await ExerciseChannelServiceFactory.shared.getAssociatedChannel(for: exerciseId, in: courseId)
83+
}
84+
8085
private func setParticipationAndResultId(from exercise: Exercise) {
8186
isWebViewLoading = true
8287

ArtemisKit/Sources/CourseView/LectureTab/LectureDetailView.swift

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import Common
1010
import SharedModels
1111
import ArtemisMarkdown
1212
import DesignLibrary
13+
import Navigation
1314

1415
public struct LectureDetailView: View {
1516

@@ -58,11 +59,23 @@ public struct LectureDetailView: View {
5859
AttachmentCell(attachment: attachment)
5960
}
6061
}
62+
if let channel = viewModel.channel.value {
63+
Text(R.string.localizable.communication())
64+
.font(.headline)
65+
ChannelCell(courseId: viewModel.courseId, channel: channel)
66+
}
6167
Spacer()
6268
}
6369
Spacer()
6470
}.padding(.l)
6571
}
72+
.onChange(of: viewModel.course.value, initial: true) { _, newValue in
73+
if newValue != nil {
74+
Task {
75+
await viewModel.loadAssociatedChannel()
76+
}
77+
}
78+
}
6679
}
6780
.navigationTitle(viewModel.lecture.value?.title ?? R.string.localizable.loading())
6881
.navigationBarTitleDisplayMode(.inline)
@@ -73,6 +86,40 @@ public struct LectureDetailView: View {
7386
}
7487
}
7588

89+
private struct ChannelCell: View {
90+
91+
@EnvironmentObject var navigationController: NavigationController
92+
let courseId: Int
93+
let channel: Channel
94+
95+
var body: some View {
96+
Button {
97+
navigationController.path.append(ConversationPath(id: channel.id, coursePath: CoursePath(id: courseId)))
98+
} label: {
99+
HStack {
100+
VStack(alignment: .leading, spacing: .l) {
101+
Label {
102+
Text(channel.conversationName)
103+
} icon: {
104+
channel.icon
105+
}
106+
.font(.title3)
107+
108+
if let description = channel.description {
109+
Text(description)
110+
}
111+
}
112+
.foregroundColor(Color.Artemis.primaryLabel)
113+
Spacer()
114+
Image(systemName: "chevron.forward")
115+
}
116+
.frame(maxWidth: .infinity, alignment: .leading)
117+
.padding(.l)
118+
.cardModifier(backgroundColor: .Artemis.exerciseCardBackgroundColor, cornerRadius: .m)
119+
}
120+
}
121+
}
122+
76123
private struct AttachmentCell: View {
77124

78125
let attachment: Attachment

ArtemisKit/Sources/CourseView/LectureTab/LectureDetailViewModel.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ class LectureDetailViewModel: BaseViewModel {
1414

1515
@Published var lecture: DataState<Lecture> = .loading
1616
@Published var course: DataState<Course> = .loading
17+
@Published var channel: DataState<Channel> = .loading
1718

1819
let lectureId: Int
1920
let courseId: Int
@@ -55,6 +56,13 @@ class LectureDetailViewModel: BaseViewModel {
5556
}
5657
}
5758

59+
func loadAssociatedChannel() async {
60+
// We only have a channel if communication is enabled
61+
guard course.value?.courseInformationSharingConfiguration != .disabled else { return }
62+
63+
channel = await LectureServiceFactory.shared.getAssociatedChannel(for: lectureId, in: courseId)
64+
}
65+
5866
func updateLectureUnitCompletion(lectureUnit: LectureUnit, completed: Bool) async -> LectureUnit {
5967
let result = await LectureServiceFactory.shared.updateLectureUnitCompletion(lectureId: lectureId,
6068
lectureUnitId: lectureUnit.id,

ArtemisKit/Sources/CourseView/Resources/en.lproj/Localizable.strings

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,3 +96,4 @@
9696
"lectureUnits" = "Lecture Units";
9797
"lecturesGroupTitle" = "%s (Lectures: %i)";
9898
"attachments" = "Attachments";
99+
"communication" = "Communication";
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//
2+
// ExerciseChannelService.swift
3+
//
4+
//
5+
// Created by Anian Schleyer on 18.08.24.
6+
//
7+
8+
import Foundation
9+
import Common
10+
import SharedModels
11+
12+
protocol ExerciseChannelService {
13+
func getAssociatedChannel(for exerciseId: Int, in courseId: Int) async -> DataState<Channel>
14+
}
15+
16+
enum ExerciseChannelServiceFactory {
17+
static let shared: ExerciseChannelService = ExerciseChannelServiceImpl()
18+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
//
2+
// ExerciseChannelServiceImpl.swift
3+
//
4+
//
5+
// Created by Anian Schleyer on 18.08.24.
6+
//
7+
8+
import APIClient
9+
import Common
10+
import SharedModels
11+
12+
struct ExerciseChannelServiceImpl: ExerciseChannelService {
13+
14+
let client = APIClient()
15+
16+
struct GetExerciseChannelRequest: APIRequest {
17+
typealias Response = Channel
18+
19+
let courseId: Int
20+
let exerciseId: Int
21+
22+
var method: HTTPMethod {
23+
.get
24+
}
25+
26+
var resourceName: String {
27+
"api/courses/\(courseId)/exercises/\(exerciseId)/channel"
28+
}
29+
}
30+
31+
func getAssociatedChannel(for exerciseId: Int, in courseId: Int) async -> DataState<Channel> {
32+
let result = await client.sendRequest(GetExerciseChannelRequest(courseId: courseId, exerciseId: exerciseId))
33+
34+
switch result {
35+
case let .success((response, _)):
36+
return .done(response: response)
37+
case let .failure(error):
38+
return .failure(error: UserFacingError(error: error))
39+
}
40+
}
41+
}

0 commit comments

Comments
 (0)