diff --git a/MacMagazine/Features/MacMagazineLibrary/Sources/MacMagazineLibrary/SidebarVisibilityKey.swift b/MacMagazine/Features/MacMagazineLibrary/Sources/MacMagazineLibrary/SidebarVisibilityKey.swift
new file mode 100644
index 00000000..0235fa0b
--- /dev/null
+++ b/MacMagazine/Features/MacMagazineLibrary/Sources/MacMagazineLibrary/SidebarVisibilityKey.swift
@@ -0,0 +1,12 @@
+import SwiftUI
+
+public struct SidebarVisibilityKey: EnvironmentKey {
+ public static let defaultValue: Bool = false
+}
+
+public extension EnvironmentValues {
+ var isSidebarVisible: Bool {
+ get { self[SidebarVisibilityKey.self] }
+ set { self[SidebarVisibilityKey.self] = newValue }
+ }
+}
diff --git a/MacMagazine/MacMagazine/Features/Social/InstagramNavigationDelegate.swift b/MacMagazine/MacMagazine/Features/Social/InstagramNavigationDelegate.swift
new file mode 100644
index 00000000..76ade5d9
--- /dev/null
+++ b/MacMagazine/MacMagazine/Features/Social/InstagramNavigationDelegate.swift
@@ -0,0 +1,60 @@
+import SwiftUI
+import WebKit
+
+final class InstagramNavigationDelegate: NSObject, WKNavigationDelegate {
+ var onStart: (() -> Void)?
+ var onFinish: (() -> Void)?
+ var onFail: ((Error) -> Void)?
+
+ func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation?) {
+ onStart?()
+ }
+
+ func webView(_ webView: WKWebView, didFinish navigation: WKNavigation?) {
+ onFinish?()
+ }
+
+ func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation?, withError error: Error) {
+ onFail?(error)
+ }
+
+ func webView(_ webView: WKWebView, didFail navigation: WKNavigation?, withError error: Error) {
+ onFail?(error)
+ }
+
+ func webView(
+ _ webView: WKWebView,
+ decidePolicyFor navigationAction: WKNavigationAction,
+ decisionHandler: @escaping (WKNavigationActionPolicy) -> Void
+ ) {
+ guard let url = navigationAction.request.url else {
+ decisionHandler(.cancel)
+ return
+ }
+
+ guard navigationAction.navigationType == .linkActivated else {
+ decisionHandler(.allow)
+ return
+ }
+
+ if url.host?.lowercased().contains("instagram.com") == true {
+ if let appURL = makeInstagramAppURL(from: url),
+ UIApplication.shared.canOpenURL(appURL) {
+ UIApplication.shared.open(appURL)
+ } else {
+ UIApplication.shared.open(url)
+ }
+ decisionHandler(.cancel)
+ return
+ }
+
+ decisionHandler(.allow)
+ }
+
+ private func makeInstagramAppURL(from webURL: URL) -> URL? {
+ var components = URLComponents(url: webURL, resolvingAgainstBaseURL: false)
+ components?.scheme = "instagram"
+ components?.host = nil
+ return components?.url
+ }
+}
diff --git a/MacMagazine/MacMagazine/Features/Social/InstagramPostsWebView.swift b/MacMagazine/MacMagazine/Features/Social/InstagramPostsWebView.swift
new file mode 100644
index 00000000..d9c2c510
--- /dev/null
+++ b/MacMagazine/MacMagazine/Features/Social/InstagramPostsWebView.swift
@@ -0,0 +1,133 @@
+import MacMagazineLibrary
+import SwiftUI
+import UIComponentsLibrary
+import WebKit
+#if canImport(UIKit)
+import UIKit
+#endif
+
+struct InstagramPostsWebView: View {
+ let url: URL
+ let userAgent: String
+ let shouldUseSidebar: Bool
+
+ private let darkMode: Bool
+
+ @Environment(\.theme) private var theme: ThemeColor
+
+ @State private var isPresenting = true
+ @State private var isLoading = true
+ @State private var loadError: String?
+
+ private let navigationDelegate = InstagramNavigationDelegate()
+
+ init(
+ colorSchema: ColorScheme?,
+ url: URL,
+ userAgent: String,
+ shouldUseSidebar: Bool
+ ) {
+ self.url = url
+ self.userAgent = userAgent
+ self.shouldUseSidebar = shouldUseSidebar
+
+ self.darkMode = if colorSchema == nil {
+ Self.isDarkMode()
+ } else {
+ colorSchema == .dark
+ }
+ }
+
+ private var userScripts: [WKUserScript] {
+ [
+ WKUserScript(
+ source: """
+ (function() {
+ var style = document.createElement('style');
+ style.innerHTML = `
+ html, body {
+ padding-top: 50px !important;
+ }
+ `;
+ document.head.appendChild(style);
+ })();
+ """,
+ injectionTime: .atDocumentEnd,
+ forMainFrameOnly: true
+ )
+ ]
+ }
+
+ var body: some View {
+ ZStack {
+ (theme.main.background.color ?? Color.secondary)
+ .ignoresSafeArea()
+
+ Webview(
+ title: nil,
+ url: url.absoluteString,
+ isPresenting: $isPresenting,
+ standAlone: true,
+ navigationDelegate: navigationDelegate,
+ userScripts: userScripts,
+ cookies: makeCookies(),
+ userAgent: userAgent
+ )
+ .ignoresSafeArea(.container, edges: [.top, .bottom])
+ .opacity(isLoading ? 0 : 1)
+ .animation(.easeInOut(duration: 0.25), value: isLoading)
+
+ if isLoading {
+ ProgressView()
+ .transition(.opacity)
+ }
+
+ if loadError != nil {
+ ContentUnavailableView(
+ "Estamos com um problema",
+ systemImage: "square.and.arrow.down.badge.xmark",
+ description: Text("No momento estamos com um problema técnico. Tente novamente mais tarde.")
+ )
+ }
+ }
+ .onAppear {
+ navigationDelegate.onStart = { isLoading = true; loadError = nil }
+ navigationDelegate.onFinish = {
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) {
+ isLoading = false
+ }
+ }
+ navigationDelegate.onFail = { error in
+ isLoading = false
+ loadError = error.localizedDescription
+ }
+ }
+ }
+}
+
+private extension InstagramPostsWebView {
+ func makeCookies() -> [HTTPCookie]? {
+ var cookies = [HTTPCookie]()
+ if let darkMode = Cookies.createDarkMode(darkMode ? "true" : "false") {
+ cookies.append(darkMode)
+ }
+ return cookies
+ }
+}
+
+#if canImport(UIKit)
+private extension InstagramPostsWebView {
+ static func isDarkMode() -> Bool {
+ (UIApplication.shared.connectedScenes.first as? UIWindowScene)?
+ .windows.first?
+ .rootViewController?
+ .traitCollection.userInterfaceStyle == .dark
+ }
+}
+#else
+private extension InstagramPostsWebView {
+ static func isDarkMode() -> Bool {
+ false
+ }
+}
+#endif
diff --git a/MacMagazine/MacMagazine/Features/Social/SocialView.swift b/MacMagazine/MacMagazine/Features/Social/SocialView.swift
index f72780ea..2352b0d6 100644
--- a/MacMagazine/MacMagazine/Features/Social/SocialView.swift
+++ b/MacMagazine/MacMagazine/Features/Social/SocialView.swift
@@ -32,7 +32,8 @@ struct SocialView: View {
content
}
.contentMargins(.top, 20, for: .scrollContent)
- .navigation(shouldUseSidebar: shouldUseSidebar, title: viewModel.social.rawValue)
+ .navigation(shouldUseSidebar: shouldUseSidebar,
+ title: viewModel.social.rawValue)
.toolbar {
ToolbarItem(placement: .primaryAction) {
menuView
@@ -75,11 +76,24 @@ private extension SocialView {
scrollPosition: $scrollPosition
).transition(.opacity)
case .instagram:
- ContentUnavailableView(
- "Página em construção",
- systemImage: "square.and.arrow.down.badge.xmark",
- description: Text("Conteúdo ainda em desenvolvimento e estará disponível em breve.")
- )
+ if let url = URL(string: "https://macmagazine.com.br/posts-instagram-app/") {
+ InstagramPostsWebView(
+ colorSchema: viewModel.settingsViewModel.colorSchema,
+ url: url,
+ userAgent: "MacMagazine",
+ shouldUseSidebar: shouldUseSidebar
+ )
+ .transition(.opacity)
+ } else {
+ ContentUnavailableView(
+ "Estamos com um problema",
+ systemImage: "square.and.arrow.down.badge.xmark",
+ description: Text(
+ "No momento estamos com um problema técnico. Tente novamente mais tarde."
+ )
+ )
+ .transition(.opacity)
+ }
}
}
diff --git a/MacMagazine/MacMagazine/MainApp/MainView.swift b/MacMagazine/MacMagazine/MainApp/MainView.swift
index ea932475..74a7b7d6 100644
--- a/MacMagazine/MacMagazine/MainApp/MainView.swift
+++ b/MacMagazine/MacMagazine/MainApp/MainView.swift
@@ -17,6 +17,7 @@ struct MainView: View {
@Environment(MainViewModel.self) var viewModel
@State var searchText: String = ""
+ @State var splitViewVisibility: NavigationSplitViewVisibility = .all
@State private var currentlayout: LayoutType = .tabbar
diff --git a/MacMagazine/MacMagazine/MainApp/Sizecalss/MainView+sidebar.swift b/MacMagazine/MacMagazine/MainApp/Sizecalss/MainView+sidebar.swift
index 4bd876c4..839f5c08 100644
--- a/MacMagazine/MacMagazine/MainApp/Sizecalss/MainView+sidebar.swift
+++ b/MacMagazine/MacMagazine/MainApp/Sizecalss/MainView+sidebar.swift
@@ -5,11 +5,15 @@ import SwiftUI
extension MainView {
var sideBarContentView: some View {
- NavigationSplitView {
+ let isSidebarVisible =
+ splitViewVisibility == .all || splitViewVisibility == .doubleColumn
+
+ return NavigationSplitView(columnVisibility: $splitViewVisibility) {
sidebar
.searchable(text: $searchText, prompt: "Search items")
} detail: {
animateContentStackView(for: navigationState.selectedItem)
+ .environment(\.isSidebarVisible, isSidebarVisible)
.podcastMiniPlayer()
}
.navigationSplitViewStyle(.balanced)
diff --git a/MacMagazine/MacMagazine/Modifiers/NavigationModifier.swift b/MacMagazine/MacMagazine/Modifiers/NavigationModifier.swift
index 5f65aa77..d64d9f44 100644
--- a/MacMagazine/MacMagazine/Modifiers/NavigationModifier.swift
+++ b/MacMagazine/MacMagazine/Modifiers/NavigationModifier.swift
@@ -1,7 +1,10 @@
import SwiftUI
extension View {
- func navigation(shouldUseSidebar: Bool, title: String? = nil) -> some View {
+ func navigation(
+ shouldUseSidebar: Bool,
+ title: String? = nil
+ ) -> some View {
modifier(NavigationModifier(
shouldUseSidebar: shouldUseSidebar,
title: title
@@ -13,11 +16,14 @@ private struct NavigationModifier: ViewModifier {
let shouldUseSidebar: Bool
let title: String?
+ @Environment(\.isSidebarVisible) private var isSidebarVisible
+
func body(content: Content) -> some View {
if shouldUseSidebar {
if let title {
content
.navigationTitle(title)
+ .navigationBarTitleDisplayMode(isSidebarVisible ? .inline : .large )
} else {
content
}
diff --git a/MacMagazine/MacMagazine/Resources/Info.plist b/MacMagazine/MacMagazine/Resources/Info.plist
index 656a9c28..dfc55d0e 100644
--- a/MacMagazine/MacMagazine/Resources/Info.plist
+++ b/MacMagazine/MacMagazine/Resources/Info.plist
@@ -8,5 +8,9 @@
audio
fetch
+ LSApplicationQueriesSchemes
+
+ instagram
+