Skip to content

Commit

Permalink
feat: fullscreen sheets for news
Browse files Browse the repository at this point in the history
  • Loading branch information
khcrysalis committed Feb 4, 2025
1 parent 0639a56 commit d4dee2c
Show file tree
Hide file tree
Showing 7 changed files with 214 additions and 67 deletions.
2 changes: 1 addition & 1 deletion Shared/Data/CoreData/Models/SourcesModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public struct SourcesData: Codable, Hashable {
}

public struct NewsData: Codable, Hashable {
public let title: String
public let title: String?
public let identifier: String
public let caption: String?
public let tintColor: String?
Expand Down
8 changes: 8 additions & 0 deletions feather.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
335F0C302C5E175B00A4F0AE /* CertData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335F0C2F2C5E175B00A4F0AE /* CertData.swift */; };
336EEE812C32367C0011188D /* ZIPFoundation in Frameworks */ = {isa = PBXBuildFile; productRef = 336EEE802C32367C0011188D /* ZIPFoundation */; };
3386515B2D52343500A85670 /* CardContextMenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3386515A2D52343500A85670 /* CardContextMenuView.swift */; };
3386515D2D52A51400A85670 /* NewsCardContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3386515C2D52A51400A85670 /* NewsCardContainerView.swift */; };
3386515F2D52A5DF00A85670 /* View+NavTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3386515E2D52A5DF00A85670 /* View+NavTransition.swift */; };
338FFCD92C682469006C3BE0 /* PopupViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 338FFCD82C682469006C3BE0 /* PopupViewController.swift */; };
338FFCDB2C6870E2006C3BE0 /* LibraryViewController+Import.swift in Sources */ = {isa = PBXBuildFile; fileRef = 338FFCDA2C6870E2006C3BE0 /* LibraryViewController+Import.swift */; };
338FFCE42C695118006C3BE0 /* IconsListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 338FFCE32C695118006C3BE0 /* IconsListViewController.swift */; };
Expand Down Expand Up @@ -181,6 +183,8 @@
335F0C2D2C5E0FFF00A4F0AE /* CoreDataManager+Certificates.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CoreDataManager+Certificates.swift"; sourceTree = "<group>"; };
335F0C2F2C5E175B00A4F0AE /* CertData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CertData.swift; sourceTree = "<group>"; };
3386515A2D52343500A85670 /* CardContextMenuView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardContextMenuView.swift; sourceTree = "<group>"; };
3386515C2D52A51400A85670 /* NewsCardContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewsCardContainerView.swift; sourceTree = "<group>"; };
3386515E2D52A5DF00A85670 /* View+NavTransition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+NavTransition.swift"; sourceTree = "<group>"; };
338B0F0C2D0C0E8200C5EA11 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = "<group>"; };
338FFCD82C682469006C3BE0 /* PopupViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopupViewController.swift; sourceTree = "<group>"; };
338FFCDA2C6870E2006C3BE0 /* LibraryViewController+Import.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "LibraryViewController+Import.swift"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -339,6 +343,7 @@
children = (
3386515A2D52343500A85670 /* CardContextMenuView.swift */,
33089A292D51F117006E1068 /* NewsCardView.swift */,
3386515C2D52A51400A85670 /* NewsCardContainerView.swift */,
33089A2B2D51F2CE006E1068 /* NewsCardsScrollView.swift */,
);
path = News;
Expand Down Expand Up @@ -749,6 +754,7 @@
33E4D8352C6593F4006A1C26 /* CGSize+aspectFit.swift */,
33BE87602C6C37CE0044D245 /* UIImage+resize.swift */,
33C29F1F2C7BD73800EF7608 /* UIUserInterfaceStyle+allCases.swift */,
3386515E2D52A5DF00A85670 /* View+NavTransition.swift */,
);
path = Extensions;
sourceTree = "<group>";
Expand Down Expand Up @@ -1084,6 +1090,7 @@
33BA37A82BF8168900FF530A /* TabbarController.swift in Sources */,
AFAC50952C48DF9300EDEAB6 /* AppSigner.swift in Sources */,
339FE33E2CCE06A100C297BA /* AddIdentifierViewController.swift in Sources */,
3386515F2D52A5DF00A85670 /* View+NavTransition.swift in Sources */,
33BA37AB2BF8196000FF530A /* Preferences.swift in Sources */,
3386515B2D52343500A85670 /* CardContextMenuView.swift in Sources */,
D0651C392BFEEA4B00D40829 /* signing.cpp in Sources */,
Expand Down Expand Up @@ -1123,6 +1130,7 @@
33E5A5A22CC85CAA00532930 /* ServerOptionsViewController.swift in Sources */,
33AC87F32C3B7D24003D1175 /* CertificatesViewController.swift in Sources */,
338FFCF42C6971B2006C3BE0 /* IconsListTableViewCell.swift in Sources */,
3386515D2D52A51400A85670 /* NewsCardContainerView.swift in Sources */,
33BA37AD2BF8197200FF530A /* Storage.swift in Sources */,
33CE81A92C4295F300C05327 /* CertImportingViewController.swift in Sources */,
33570F612C32B80E008CB560 /* AppsTableViewCell.swift in Sources */,
Expand Down
28 changes: 28 additions & 0 deletions iOS/Extensions/View+NavTransition.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//
// View+NavTransition.swift
// Luce
//
// Created by samara on 30.01.2025.
//

import SwiftUI

extension View {
@ViewBuilder
func compatNavigationTransition(id: String, ns: Namespace.ID) -> some View {
if #available(iOS 18.0, *) {
self.navigationTransition(.zoom(sourceID: id, in: ns))
} else {
self
}
}

@ViewBuilder
func compatMatchedTransitionSource(id: String, ns: Namespace.ID) -> some View {
if #available(iOS 18.0, *) {
self.matchedTransitionSource(id: id, in: ns)
} else {
self
}
}
}
138 changes: 105 additions & 33 deletions iOS/Views/Sources/SourceAppViews/News/CardContextMenuView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
import SwiftUI

struct CardContextMenuView: View {
@Environment(\.dismiss) var dismiss

let news: NewsData

var formattedDate: String {
Expand All @@ -20,44 +22,114 @@ struct CardContextMenuView: View {
}

var body: some View {
VStack(alignment: .leading, spacing: 12) {
AsyncImage(url: URL(string: news.imageURL ?? "")) { image in
image.resizable()
.aspectRatio(contentMode: .fill)
} placeholder: {
Color.gray
}
.frame(width: 280, height: 160)
.clipShape(RoundedRectangle(cornerRadius: 12, style: .continuous))
.overlay(
LinearGradient(
gradient: Gradient(colors: [.clear, .black.opacity(0.2)]),
startPoint: .top,
endPoint: .bottom
)
)

VStack(alignment: .leading, spacing: 8) {
Text(news.title)
.font(.title3)
.fontWeight(.bold)
.lineLimit(2)
NavigationView {
VStack(spacing: 12) {
if (news.imageURL != nil) {
AsyncImage(url: URL(string: news.imageURL ?? "")) { image in
Color.clear.overlay(
image
.resizable()
.aspectRatio(contentMode: .fill)
)
.transition(.opacity.animation(.easeInOut(duration: 0.3)))
} placeholder: {
Color.black
.opacity(0.2)
.overlay(
ProgressView()
.progressViewStyle(.circular)
.tint(.white)
)
}
.background(Color(uiColor: UIColor(hex: news.tintColor ?? "000000")))
.clipShape(
RoundedRectangle(cornerRadius: 12, style: .continuous)
)
.overlay(
LinearGradient(
gradient: Gradient(colors: [.clear, .black.opacity(0.2)]),
startPoint: .top,
endPoint: .bottom
)
)
.overlay(
RoundedRectangle(cornerRadius: 12, style: .continuous)
.stroke(Color.white.opacity(0.15), lineWidth: 2)
)
.frame(height: 250)
}

if let caption = news.caption {
Text(caption)
.font(.subheadline)
VStack(alignment: .leading, spacing: 16) {
if let title = news.title {
Text(title)
.font(.title)
.fontWeight(.bold)
.lineLimit(2)
.foregroundStyle(.primary)
.multilineTextAlignment(.leading)
}

if let caption = news.caption {
Text(caption)
.font(.headline)
.foregroundColor(.secondary)
.multilineTextAlignment(.leading)
}

if (news.url != nil) {
Button(action: {
UIApplication.shared.open(news.url!)
}) {
Label("Open URL", systemImage: "arrow.up.right")
.frame(maxWidth: .infinity)
}
.padding()
.foregroundColor(.accentColor)
.background(Color(uiColor: .secondarySystemBackground))
.cornerRadius(10)
}

Text(formattedDate)
.font(.caption)
.foregroundColor(.secondary)
.lineLimit(2)
}
.frame(maxWidth: .infinity)

Text(formattedDate)
.font(.caption)
.foregroundColor(.secondary)
Spacer()
}
.frame(
minWidth: 0,
maxWidth: .infinity,
minHeight: 0,
maxHeight: .infinity,
alignment: .topLeading
)
.padding()
.background(Color(uiColor: .systemBackground))
.clipShape(RoundedRectangle(cornerRadius: 16, style: .continuous))
.toolbar {
ToolbarItem(placement: .topBarLeading) {
Button(action: {
dismiss()
}) {
Image(systemName: "chevron.left")
.padding(10)
.compatFontWeight(.bold)
.background(Color(uiColor: .secondarySystemBackground))
.clipShape(Circle())
}
}
}
}
}
}

extension View {
func compatFontWeight(_ _weight: Font.Weight) -> some View {
if #available(iOS 16.0, *) {
return self.fontWeight(_weight)
} else {
return self
}
.frame(width: 280)
.padding()
.background(Color(uiColor: .systemBackground))
.clipShape(RoundedRectangle(cornerRadius: 16, style: .continuous))
}
}
50 changes: 50 additions & 0 deletions iOS/Views/Sources/SourceAppViews/News/NewsCardContainerView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//
// NewsCardContainerView.swift
// feather
//
// Created by samara on 4.02.2025.
//

import SwiftUI

struct NewsCardContainerView: View {
@Binding var isSheetPresented: Bool
var news: NewsData
@Namespace private var namespace

let uuid = UUID().uuidString

var body: some View {
Button(action: {
isSheetPresented = true
}) {
NewsCardView(news: news)
.fullScreenCover(isPresented: $isSheetPresented) {
CardContextMenuView(news: news)
.compatNavigationTransition(id: uuid, ns: namespace)
}
.compatMatchedTransitionSource(id: uuid, ns: namespace)
.compactContentMenuPreview(news: news)
}
}
}

extension View {
func compactContentMenuPreview(news: NewsData) -> some View {
if #available(iOS 16.0, *) {
return self.contextMenu {
if (news.url != nil) {
Button(action: {
UIApplication.shared.open(news.url!)
}) {
Label("Open URL", systemImage: "arrow.up.right")
}
}
} preview: {
CardContextMenuView(news: news)
}
} else {
return self
}
}
}
43 changes: 12 additions & 31 deletions iOS/Views/Sources/SourceAppViews/News/NewsCardView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,26 @@
import SwiftUI

struct NewsCardView: View {
let news: NewsData
var news: NewsData

var body: some View {
ZStack(alignment: .bottomLeading) {
if (news.imageURL != nil) {
AsyncImage(url: URL(string: news.imageURL!)) { image in
AsyncImage(url: URL(string: news.imageURL ?? "")) { image in
Color.clear.overlay(
image
.resizable()
.aspectRatio(contentMode: .fill)
)
.transition(.opacity.animation(.easeInOut(duration: 0.3)))
} placeholder: {
Color.black.opacity(0.2)
Color.black
.opacity(0.2)
.overlay(
ProgressView()
.progressViewStyle(.circular)
.tint(.white)
)
}

LinearGradient(
Expand All @@ -37,11 +44,12 @@ struct NewsCardView: View {

VStack {
Spacer()
Text(news.title)
Text(news.title ?? "")
.font(.headline)
.fontWeight(.bold)
.foregroundColor(.white)
.lineLimit(2)
.multilineTextAlignment(.leading)
.padding()
}
}
Expand All @@ -54,32 +62,5 @@ struct NewsCardView: View {
RoundedRectangle(cornerRadius: 12, style: .continuous)
.stroke(Color.white.opacity(0.15), lineWidth: 2)
)
.compactContentMenuPreview(news: news)
}
}

extension View {
func compactContentMenuPreview(news: NewsData) -> some View {
if #available(iOS 16.0, *) {
return self.contextMenu {
if (news.url != nil) {
Button(action: {
UIApplication.shared.open(news.url!)
}) {
Label("Open URL", systemImage: "arrow.up.right")
}
} else {
Button(action: {
UIApplication.shared.open(URL(string: "https://github.com/khcrysalis/feather")!)
}) {
Label("Give us a star!", systemImage: "star")
}
}
} preview: {
CardContextMenuView(news: news)
}
} else {
return self
}
}
}
12 changes: 10 additions & 2 deletions iOS/Views/Sources/SourceAppViews/News/NewsCardsScrollView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,24 @@ import SwiftUI

struct NewsCardsScrollView: View {
@State private var newsData: [NewsData]
@State private var sheetStates: [String: Bool] = [:]
@State var isSheetPresented = false

init(newsData: [NewsData]) {
_newsData = State(initialValue: newsData)
_newsData = State(initialValue: newsData)
print(newsData)
}

var body: some View {
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 10) {
ForEach(newsData.reversed(), id: \.self) { new in
NewsCardView(news: new)
let binding = Binding(
get: { sheetStates[new.identifier] ?? false },
set: { sheetStates[new.identifier] = $0 }
)

NewsCardContainerView(isSheetPresented: binding, news: new)
}
}
.padding()
Expand Down

0 comments on commit d4dee2c

Please sign in to comment.