Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 0 additions & 27 deletions MacMagazine/WatchApp/ContentView.swift

This file was deleted.

71 changes: 71 additions & 0 deletions MacMagazine/WatchApp/Extension/FeedDB.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import FeedLibrary
import Foundation

extension FeedDB {
var linkURL: URL? {
guard !link.isEmpty else { return nil }
return URL(string: link)
}

var dateText: String {
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "pt_BR")
formatter.dateFormat = "dd/MM/yyyy 'Γ s' HH:mm"
return formatter.string(from: pubDate)
}

var displaySubtitle: String? {
let subtitle = subtitle.trimmingCharacters(in: .whitespacesAndNewlines)
return subtitle.isEmpty ? nil : subtitle
}

var displayBody: String? {
let full = fullContent.trimmingCharacters(in: .whitespacesAndNewlines)
if !full.isEmpty { return full }

let excerpt = excerpt.trimmingCharacters(in: .whitespacesAndNewlines)
return excerpt.isEmpty ? nil : excerpt
}

var artworkRemoteURL: URL? {
guard !artworkURL.isEmpty else { return nil }
return URL(string: artworkURL)
}
}

#if DEBUG
extension FeedDB {

static var previewItem: FeedDB {
FeedDB(
postId: UUID().uuidString,
title: "Apple lanΓ§a atualizaΓ§Γ£o do watchOS",
subtitle: "MudanΓ§as importantes para o Apple Watch",
pubDate: Date().addingTimeInterval(-3600),
artworkURL: "https://picsum.photos/400/400",
link: "https://macmagazine.com.br",
categories: ["watchos", "news", "teste1", "teste2", "teste 3"],
excerpt: "Resumo curto para teste no relΓ³gio…",
fullContent: "",
favorite: false
)
}

static var previewItems: [FeedDB] {
(1...10).map { index in
FeedDB(
postId: UUID().uuidString,
title: "NotΓ­cia \(index): tΓ­tulo de teste para o Watch",
subtitle: "SubtΓ­tulo \(index)",
pubDate: Date().addingTimeInterval(TimeInterval(-index * 900)),
artworkURL: "https://picsum.photos/seed/\(index)/600/600",
link: "https://macmagazine.com.br",
categories: ["news", "teste1", "teste2", "teste 3"],
excerpt: "Excerpt \(index) – texto curto para validar layout.",
fullContent: "",
favorite: index % 3 == 0
)
}
}
}
#endif
44 changes: 44 additions & 0 deletions MacMagazine/WatchApp/Helper/FeedDotsIndicatorView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import SwiftUI

// MARK: - Dots Indicator

public struct FeedDotsIndicatorView: View {
let count: Int
let selectedIndex: Int

public var body: some View {
VStack(spacing: 5) {
dots
}
.padding(6)
.glassEffect(.clear)
.allowsHitTesting(false)
}

private var dots: some View {
ForEach(0..<count, id: \.self) { index in
Circle()
.fill(dotColor(for: index))
.frame(width: dotSize(for: index), height: dotSize(for: index))
.shadow(color: index == selectedIndex ? .white.opacity(0.5) : .clear, radius: 2)
.animation(.easeInOut(duration: 0.2), value: selectedIndex)
}
}

private func dotColor(for index: Int) -> Color {
index == selectedIndex ? .white : .white.opacity(0.4)
}

private func dotSize(for index: Int) -> CGFloat {
index == selectedIndex ? 8 : 5
}
}

#if DEBUG
#Preview {
ZStack {
Color.gray
FeedDotsIndicatorView(count: 10, selectedIndex: 3)
}
}
#endif
106 changes: 106 additions & 0 deletions MacMagazine/WatchApp/Helper/FlowTagsView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import SwiftUI

struct FlowTagsView<Tag: Hashable, Content: View>: View {

// MARK: - Properties

let tags: [Tag]
let horizontalSpacing: CGFloat
let verticalSpacing: CGFloat
let content: (Tag) -> Content

@State private var measuredHeight: CGFloat = 0

// MARK: - Init

init(
tags: [Tag],
horizontalSpacing: CGFloat,
verticalSpacing: CGFloat,
@ViewBuilder content: @escaping (Tag) -> Content
) {
self.tags = tags
self.horizontalSpacing = horizontalSpacing
self.verticalSpacing = verticalSpacing
self.content = content
}

// MARK: - Body

var body: some View {
GeometryReader { proxy in
let rows = makeRows(availableWidth: proxy.size.width)

VStack(alignment: .leading, spacing: verticalSpacing) {
ForEach(rows.indices, id: \.self) { rowIndex in
HStack(spacing: horizontalSpacing) {
ForEach(rows[rowIndex], id: \.self) { tag in
content(tag)
.fixedSize(horizontal: true, vertical: true)
}
}
}
}
.background(
GeometryReader { innerProxy in
Color.clear
.preference(key: HeightPreferenceKey.self, value: innerProxy.size.height)
}
)
}
.frame(height: measuredHeight)
.onPreferenceChange(HeightPreferenceKey.self) { height in
if height != measuredHeight {
measuredHeight = height
}
}
.frame(maxWidth: .infinity, alignment: .leading)
.fixedSize(horizontal: false, vertical: true)
}

// MARK: - Layout

private func makeRows(availableWidth: CGFloat) -> [[Tag]] {
var rows: [[Tag]] = [[]]
var currentRowWidth: CGFloat = 0

for tag in tags {
let tagWidth = estimatedTagWidth(tag: tag)

if rows[rows.count - 1].isEmpty {
rows[rows.count - 1].append(tag)
currentRowWidth = tagWidth
continue
}

let nextWidth = currentRowWidth + horizontalSpacing + tagWidth

if nextWidth <= availableWidth {
rows[rows.count - 1].append(tag)
currentRowWidth = nextWidth
} else {
rows.append([tag])
currentRowWidth = tagWidth
}
}

return rows
}

private func estimatedTagWidth(tag: Tag) -> CGFloat {
let text = String(describing: tag)
let estimatedCharacterWidth: CGFloat = 6
let horizontalPadding: CGFloat = 16
return CGFloat(text.count) * estimatedCharacterWidth + horizontalPadding
}
}

// MARK: - Height Preference

private struct HeightPreferenceKey: PreferenceKey {
static var defaultValue: CGFloat = 0

static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
value = max(value, nextValue())
}
}
24 changes: 24 additions & 0 deletions MacMagazine/WatchApp/MainApp/WatchApp.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import FeedLibrary
import StorageLibrary
import SwiftData
import SwiftUI

@main
struct WatchApp: App {

private let database = Database(models: [FeedDB.self], inMemory: true)

var body: some Scene {
WindowGroup {
FeedRootView(
viewModel: FeedRootViewModel(
feedViewModel: FeedViewModel(
network: nil,
storage: database
)
)
)
.modelContainer(database.sharedModelContainer)
}
}
}
14 changes: 14 additions & 0 deletions MacMagazine/WatchApp/Model/SelectedPost.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import FeedLibrary
import SwiftUI

// MARK: - Navigation Payload

struct SelectedPost: Hashable, Identifiable {
let id: String
let post: FeedDB

init(post: FeedDB) {
id = post.postId
self.post = post
}
}
Loading