Skip to content

Commit

Permalink
Dashboard: Redesign course cards (#170)
Browse files Browse the repository at this point in the history
* Redesign Header

* Redesign body of course cards

* Add direct navigation to next exercise

* Improve alignment of multi-line titles

* Show slightly more course cards at once on large screens
  • Loading branch information
anian03 authored Sep 19, 2024
1 parent adbfb2b commit 9b2e3e3
Show file tree
Hide file tree
Showing 6 changed files with 86 additions and 63 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/ls1intum/artemis-ios-core-modules",
"state" : {
"revision" : "1ce04cfa8bd86f121632c3309e2bb44c70d6f311",
"version" : "14.3.0"
"revision" : "e10992600af821dab51ef29b5f28db7ae457c78c",
"version" : "14.4.0"
}
},
{
Expand Down
2 changes: 1 addition & 1 deletion ArtemisKit/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ let package = Package(
dependencies: [
.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: "14.3.0")),
.package(url: "https://github.com/ls1intum/artemis-ios-core-modules", .upToNextMajor(from: "14.4.0")),
.package(url: "https://github.com/mac-cain13/R.swift.git", from: "7.0.0")
],
targets: [
Expand Down
2 changes: 1 addition & 1 deletion ArtemisKit/Sources/Dashboard/CourseGrid.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import DesignLibrary
import SwiftUI

struct CourseGrid: View {
private static let layout = [GridItem(.adaptive(minimum: 400, maximum: .infinity), spacing: .l, alignment: .center)]
private static let layout = [GridItem(.adaptive(minimum: 380, maximum: .infinity), spacing: .l, alignment: .center)]

@ObservedObject var viewModel: DashboardViewModel
@State private var isCourseRegistrationPresented = false
Expand Down
133 changes: 78 additions & 55 deletions ArtemisKit/Sources/Dashboard/CourseGridCell.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,89 +35,112 @@ struct CourseGridCell: View {
} label: {
VStack(alignment: .leading, spacing: 0) {
header
statistics
footer
content
}
.cardModifier(backgroundColor: .clear, hasBorder: true)
}
.buttonStyle(.plain)
}
}

private extension CourseGridCell {
var header: some View {
HStack(alignment: .center) {
VStack {
if let imageURL = courseForDashboard.course.courseIconURL {
ArtemisAsyncImage(imageURL: imageURL) {
EmptyView()
}
.clipShape(.circle)
.frame(width: .extraLargeImage)
HStack(alignment: .center, spacing: .m) {
if let imageURL = courseForDashboard.course.courseIconURL {
ArtemisAsyncImage(imageURL: imageURL) {
EmptyView()
}
.clipShape(.circle)
.frame(width: .largeImage * 1.25)
}
.frame(height: .extraLargeImage)
.padding([.leading, .vertical], .m)
VStack(alignment: .leading, spacing: 0) {
Text(courseForDashboard.course.title ?? "")
.font(.system(size: UIFontMetrics.default.scaledValue(for: 21)))
Spacer()

// If title spans multiple lines, push it to the leading edge
// Otherwise it looks stupid
let title = courseForDashboard.course.title ?? ""
ViewThatFits(in: .horizontal) {
Text(title)
.font(.title3)
.lineLimit(1)

Text(title)
.font(.title3)
.multilineTextAlignment(.leading)
.lineLimit(2)
Text(R.string.localizable.dashboardExercisesLabel(courseForDashboard.course.exercises?.count ?? 0))
let numberOfLectures = courseForDashboard.course.numberOfLectures ?? courseForDashboard.course.lectures?.count ?? 0
Text(R.string.localizable.dashboardLecturesLabel(numberOfLectures))
.frame(maxWidth: .infinity, alignment: .leading)
}
.foregroundStyle(.white)
.padding(.m)

Spacer()
}
.frame(height: .largeImage * 1.25)
.padding(.m)
.frame(maxWidth: .infinity)
.background(courseForDashboard.course.courseColor)
}

var statistics: some View {
HStack {
Spacer()
if let totalScore = courseForDashboard.totalScores {
ProgressBar(
value: Int(totalScore.studentScores.absoluteScore),
total: Int(totalScore.reachablePoints)
)
.frame(height: .xxxl)
} else {
Text(R.string.localizable.dashboardNoStatisticsAvailable())
}
Spacer()
var content: some View {
HStack(alignment: .center, spacing: .m) {
information
statisticsChart
}
.foregroundStyle(Color.Artemis.secondaryLabel)
.padding(.vertical, .m)
.frame(height: .xxxl)
.padding(.l)
.background(Color(uiColor: .secondarySystemGroupedBackground))
}

var footer: some View {
HStack {
if let nextExercise,
let nextExerciseTitle = nextExercise.baseExercise.title {
HStack {
Text(R.string.localizable.dashboardNextExerciseLabel())
.padding(.trailing, .m)
nextExercise.image
.renderingMode(.template)
.resizable()
.scaledToFit()
.frame(width: .extraSmallImage)
var information: some View {
VStack(alignment: .leading) {
Text(R.string.localizable.dashboardScoreTitle())
.foregroundStyle(.secondary)
.fontWeight(.medium)
Group {
if let totalScore = courseForDashboard.totalScores,
totalScore.studentScores.absoluteScore > 0 {
let achieved = Int(totalScore.studentScores.absoluteScore)
let possible = Int(totalScore.reachablePoints)
Text(R.string.localizable.dashboardScoreLabel(achieved, possible))
} else {
Text(R.string.localizable.dashboardNoStatisticsAvailable())
}
}
.fontWeight(.semibold)

Divider()
.frame(height: .xxs)
.overlay(.gray)
.padding(.vertical, .s)

Text(R.string.localizable.dashboardNextExerciseLabel())
.foregroundStyle(.secondary)
.fontWeight(.medium)
Group {
if let nextExercise,
let nextExerciseTitle = nextExercise.baseExercise.title {
Text(nextExerciseTitle)
.bold()
.lineLimit(1)
.onTapGesture {
navigationController.goToExercise(courseId: courseForDashboard.id, exerciseId: nextExercise.id)
}
} else {
Text(R.string.localizable.dashboardNoExercisePlannedLabel())
}
.padding(.l)
} else {
Text(R.string.localizable.dashboardNoExercisePlannedLabel())
.padding(.l)
}
Spacer()
.fontWeight(.semibold)
}
}

@ViewBuilder var statisticsChart: some View {
if let totalScore = courseForDashboard.totalScores,
totalScore.studentScores.absoluteScore > 0 {
ProgressBar(
value: totalScore.studentScores.absoluteScore,
total: totalScore.reachablePoints
)
.foregroundStyle(Color.Artemis.secondaryLabel)
.frame(width: .xxxl)
.padding(.leading)
}
.frame(maxWidth: .infinity)
.background(Color.Artemis.dashboardCardBackgroundColor)
.foregroundStyle(Color.Artemis.secondaryLabel)
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"dashboardExercisesLabel" = "Exercises: %d";
"dashboardLecturesLabel" = "Lectures: %d";
"dashboardRegisterForCourseButton" = "Course Enrollment";
"dashboardTitle" = "Courses";
"dashboardNextExerciseLabel" = "Next Exercise:";
"dashboardNextExerciseLabel" = "Next Exercise";
"dashboardNoExercisePlannedLabel" = "No exercise planned";
"dashboardNoStatisticsAvailable" = "No statistics available";
"dashboardScoreLabel" = "%d / %d Points";
"dashboardScoreTitle" = "Score";
2 changes: 1 addition & 1 deletion ArtemisKit/Sources/Messages/Models/Schema/SchemaV1.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ enum SchemaV1: VersionedSchema {
@Attribute(.unique)
var courseId: Int

@Relationship(deleteRule: .cascade, inverse: \Conversation.course)
@Relationship(deleteRule: .cascade)
var conversations: [Conversation]

init(server: Server, courseId: Int, conversations: [Conversation] = []) {
Expand Down

0 comments on commit 9b2e3e3

Please sign in to comment.