diff --git a/PodcastsClone/Assets.xcassets/podcast_logo_1.imageset/Contents.json b/PodcastsClone/Assets.xcassets/podcast_logo_1.imageset/Contents.json new file mode 100644 index 0000000..ab3c669 --- /dev/null +++ b/PodcastsClone/Assets.xcassets/podcast_logo_1.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "maxfun_logo.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "maxfun_logo 1.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "maxfun_logo 2.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/PodcastsClone/Assets.xcassets/podcast_logo_1.imageset/maxfun_logo 1.png b/PodcastsClone/Assets.xcassets/podcast_logo_1.imageset/maxfun_logo 1.png new file mode 100644 index 0000000..2467dff Binary files /dev/null and b/PodcastsClone/Assets.xcassets/podcast_logo_1.imageset/maxfun_logo 1.png differ diff --git a/PodcastsClone/Assets.xcassets/podcast_logo_1.imageset/maxfun_logo 2.png b/PodcastsClone/Assets.xcassets/podcast_logo_1.imageset/maxfun_logo 2.png new file mode 100644 index 0000000..2467dff Binary files /dev/null and b/PodcastsClone/Assets.xcassets/podcast_logo_1.imageset/maxfun_logo 2.png differ diff --git a/PodcastsClone/Assets.xcassets/podcast_logo_1.imageset/maxfun_logo.png b/PodcastsClone/Assets.xcassets/podcast_logo_1.imageset/maxfun_logo.png new file mode 100644 index 0000000..2467dff Binary files /dev/null and b/PodcastsClone/Assets.xcassets/podcast_logo_1.imageset/maxfun_logo.png differ diff --git a/PodcastsClone/Colors.xcassets/backgroundSecondary.colorset/Contents.json b/PodcastsClone/Colors.xcassets/backgroundSecondary.colorset/Contents.json index 4734bcd..b09c324 100644 --- a/PodcastsClone/Colors.xcassets/backgroundSecondary.colorset/Contents.json +++ b/PodcastsClone/Colors.xcassets/backgroundSecondary.colorset/Contents.json @@ -23,9 +23,9 @@ "color-space" : "srgb", "components" : { "alpha" : "1.000", - "blue" : "0.600", - "green" : "0.600", - "red" : "0.600" + "blue" : "0.650", + "green" : "0.650", + "red" : "0.650" } }, "idiom" : "universal" diff --git a/PodcastsClone/Colors.xcassets/shadow.colorset/Contents.json b/PodcastsClone/Colors.xcassets/shadow.colorset/Contents.json new file mode 100644 index 0000000..4e332df --- /dev/null +++ b/PodcastsClone/Colors.xcassets/shadow.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.833", + "green" : "0.833", + "red" : "0.833" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.333", + "green" : "0.333", + "red" : "0.333" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/PodcastsClone/Model/Podcast.swift b/PodcastsClone/Model/Podcast.swift index 9749d39..c3d3736 100644 --- a/PodcastsClone/Model/Podcast.swift +++ b/PodcastsClone/Model/Podcast.swift @@ -6,15 +6,37 @@ // import Foundation +import SwiftUI -class Podcast { +struct Podcast { + var id: UUID + var image: UIImage var title: String - var length: Int - var id: Int + var by: String + private var _episodes: [Episode] + var rating: Double + var reviews: Int + var genre: String - init(title: String = "test title", length: Int = 400, id: Int) { - self.title = title - self.length = length + init( + id: UUID, image: UIImage, title: String, by: String, episodes: [Episode], rating: Double, reviews: Int, genre: String) { self.id = id + self.image = image + self.title = title + self.by = by + self._episodes = episodes + self.rating = rating + self.reviews = reviews + self.genre = genre + } + + + var episodes: [Episode] { + _episodes.sorted(by: { $0.publicationDate > $1.publicationDate }) + } + + var latestEpisodeDescription: String { + guard !episodes.isEmpty else { return "No episodes available" } + return episodes.first?.description ?? "No description available" } } diff --git a/PodcastsClone/View/Extensions/Color.swift b/PodcastsClone/View/Extensions/Color.swift index a023396..31adeee 100644 --- a/PodcastsClone/View/Extensions/Color.swift +++ b/PodcastsClone/View/Extensions/Color.swift @@ -18,4 +18,5 @@ struct ColorTheme { let background = Color("background") let backgroundSecondary = Color("backgroundSecondary") let accent = Color("accent") + let shadow = Color("shadow") } diff --git a/PodcastsClone/View/LibraryView.swift b/PodcastsClone/View/LibraryView.swift index d7d0fb0..f4dce47 100644 --- a/PodcastsClone/View/LibraryView.swift +++ b/PodcastsClone/View/LibraryView.swift @@ -4,7 +4,7 @@ // // Created by etudiant on 2023-05-16. // - +/* import SwiftUI @@ -34,3 +34,4 @@ struct LibraryView_Previews: PreviewProvider { LibraryView() } } +*/ diff --git a/PodcastsClone/View/PodcastDetailView.swift b/PodcastsClone/View/PodcastDetailView.swift index 7fb9eb7..657e8df 100644 --- a/PodcastsClone/View/PodcastDetailView.swift +++ b/PodcastsClone/View/PodcastDetailView.swift @@ -9,47 +9,118 @@ import SwiftUI struct PodcastDetailView: View { + let podcast: Podcast + var body: some View { ScrollView { - VStack(alignment: .leading) { - // TODO image goes here - - // TODO center this text - Text("Podcast Title goes here") - .font(.largeTitle) - .padding() - - // TODO center this text - Text("Author Name goes here") - .font(.title) - .foregroundColor(.secondary) - - // TODO add centered 'play' button that says "|> Latest Episode" - - Text("Latest Episode Description goes here") - // TODO make this just 3 lines, with a "MORE" touchable string that makes a full "about" page pop up? Or maybe the "MORE" string won't become clickable at all - - // TODO add star, rating /5, (nb review), mid-line '.', category, '.', frequency - - Divider() - - Text("Episodes") - .font(.title2) - .padding() - - VStack(alignment: .leading) { - Text("Episode View Cell goes here") - Text("Episode View Cell goes here") - Text("Episode View Cell goes here") + ZStack() { + // TODO make this background the same as the dominant color of the podcast logo + Color.theme.background.ignoresSafeArea(.all, edges: .all) + VStack(alignment: .center) { + // TODO make content of the VStack below switch to dark mode if the ZStack's background is dark, and vice versa + VStack(alignment: .center) { + + Image(uiImage: podcast.image) + .resizable() + .scaledToFit() + .cornerRadius(10) + .shadow(color: Color.theme.shadow, radius: 10, x: 0, y: 10) + .padding(.horizontal, 48) + .padding(.vertical, 16) + + Text(podcast.title) + .font(.title) + .foregroundColor(Color.theme.primary) + .multilineTextAlignment(.center) + + Text(podcast.by) + .font(.headline) + .foregroundColor(Color.theme.secondary) + .multilineTextAlignment(.center) + + Button(action: {}) { + HStack { + Image(systemName: "play.fill") + Text(Strings.latestEpisode) + }} + .foregroundColor(Color.theme.primary) + .padding(.vertical) + .padding(.horizontal, 64) + .background(Color.theme.background) + .clipShape(RoundedRectangle(cornerSize: CGSize(width: 12.0, height: 12.0))) + + // TODO replace '...' with Strings.readFurtherPrompt + Text(podcast.latestEpisodeDescription) + .lineLimit(3) + .truncationMode(.tail) + .padding() + + HStack() { + Text("\(Image(systemName: "star.fill")) \(podcast.rating, specifier: "%.1f") (\(podcast.reviews)) \(Strings.classySeparator) \(podcast.genre)") + .padding(.horizontal) + + Spacer() + } + } + + Divider() + .foregroundColor(Color.theme.backgroundSecondary) + + ZStack() { + Color.theme.background.ignoresSafeArea(.all, edges: .all) + + VStack(alignment: .leading) { + Text("Episodes") + .font(.title2) + .fontWeight(.bold) + .foregroundColor(Color.theme.primary) + .padding() + + ForEach(podcast.episodes, id: \.id) { episode in + EpisodeViewCell(episode: episode) + } + } + } } } - .padding() } } } + struct PodcastDetailView_Previews: PreviewProvider { static var previews: some View { - PodcastDetailView() + PodcastDetailView(podcast: Podcast( + id: UUID(), + image: UIImage(named: "podcast_logo_1")!, + title: "Podcast Title 1", + by: "Author 1", + episodes: [ + Episode( + id: UUID(), + publicationDate: Date.now.addingTimeInterval(-1000000), + title: "A New Ipsum", + description: "Stand in doorway, unwilling to chose whether to stay in or go out more napping, more napping all the napping is exhausting sleep i'm bored inside, let me out i'm lonely outside, let me in i can't make up my mind whether to go in or out, guess i'll just stand partway in and partway out, contemplating the universe for half an hour how dare you nudge me with your foot?!?! leap into the air in greatest offense!", + duration: 3463 + ), + Episode( + id: UUID(), + publicationDate: Date.now, + title: "Return of the Hooman", + description: "Catch mouse and gave it as a present mewl for food at 4am drink water out of the faucet and have secret plans. Stretch chase dog then run away. Kitty. Mouse if it fits, i sits. Bite off human's toes. If human is on laptop sit on the keyboard.", + duration: 4480 + ), + Episode( + id: UUID(), + publicationDate: Date.now.addingTimeInterval(-100000), + title: "Cat Ipsum Strikes Back", + description: "Chase after silly colored fish toys around the house i want to go outside let me go outside nevermind inside is better or get video posted to internet for chasing red dot eat owner's food wack the mini furry mouse so cat meoooow i iz master of hoomaan, not hoomaan master of i, oooh damn dat dog but stuff and things. Cats making all the muffins.", + duration: 4028 + ), + ], + rating: 4.2, + reviews: 2139, + genre: "Genre 1" + )) } } diff --git a/PodcastsClone/View/Strings/Strings.swift b/PodcastsClone/View/Strings/Strings.swift index 583f524..66f2cfb 100644 --- a/PodcastsClone/View/Strings/Strings.swift +++ b/PodcastsClone/View/Strings/Strings.swift @@ -9,4 +9,7 @@ import Foundation struct Strings { static let threeDots = "···" + static let classySeparator = "·" + static let latestEpisode = "Latest Episode" + static let readFurtherPrompt = "MORE" }