Skip to content

Commit 38e4531

Browse files
authored
Updates BrowserView to 1.8
1 parent 00945d4 commit 38e4531

File tree

1 file changed

+150
-121
lines changed

1 file changed

+150
-121
lines changed

Weave/BrowserView.swift

Lines changed: 150 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import SwiftUI
55
import WebKit
66

7-
// Fetches Accent Color values to use within CSS code
7+
// Fetches Accent Color values to use within CSS code, future usage I swear.
88
extension NSColor {
99
var hexString: String {
1010
guard let rgbColor = usingColorSpace(NSColorSpace.sRGB) else { return "#000000" }
@@ -22,149 +22,174 @@ extension NSColor {
2222
// Sets up BrowserView and does basic customizations
2323
struct BrowserView: View {
2424
@State private var webView = WKWebView()
25-
@State private var urlString = ""
25+
@State private var URLString = ""
2626
@State private var pageTitle = "Weave"
2727
@State private var faviconImage: NSImage?
2828
@State private var userAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.3.1 Safari/605.1.15"
2929

3030
var body: some View {
31-
VStack(spacing: 0) {
32-
Divider()
33-
34-
WebView(webView: webView, pageTitle: $pageTitle, urlString: $urlString, faviconImage: $faviconImage, userAgent: $userAgent)
35-
.onAppear {
36-
// CSS Style Injection
37-
let sysAccent = NSColor.systemAccentColor.hexString
38-
let userScript = """
39-
var style = document.createElement('style');
40-
style.innerHTML = `
41-
body, p, h1, h2, h3, h4, h5, h6, ul, ol, li, a {
42-
font-family: -apple-system !important;
43-
}
44-
`;
45-
document.head.appendChild(style);
46-
"""
31+
WebView(webView: webView, pageTitle: $pageTitle, URLString: $URLString, faviconImage: $faviconImage, userAgent: $userAgent)
32+
.onAppear {
33+
// CSS Style Injection
34+
var sysAccent = NSColor.systemAccentColor.hexString
35+
let styleSheet = """
36+
var style = document.createElement('style');
37+
style.innerHTML = `
38+
* {
39+
font-family: -apple-system !important;
40+
}
41+
`;
42+
document.head.appendChild(style);
43+
"""
4744

48-
// Simplistic Adblocking Injection
49-
let adBlockerScript = """
50-
var adBlockStyle = document.createElement('style');
51-
adBlockStyle.innerHTML = `
52-
/* Hide common ad classes */
53-
.ad-banner, .ad-wrapper, .ad-container, .ad, .ads, .adsense, .adslot, .ad-badge {
54-
display: none !important;
55-
}
45+
// Extremely basic CSS Adblock Injection
46+
let adBlockerScript = """
47+
var adBlockStyle = document.createElement('style');
48+
adBlockStyle.innerHTML = `
49+
/* Hide common ad classes */
50+
.ad-banner, .ad-wrapper, .ad-container, .ad, .ads, .adsense, .adslot, .ad-badge {
51+
display: none !important;
52+
}
5653
57-
/* Hide ads from specific URLs */
58-
[href*="doubleclick.net"], [href*="googleadservices.com"], [href*="advertising.com"], [src*="adserver.com"] {
59-
display: none !important;
60-
}
61-
`;
62-
document.head.appendChild(adBlockStyle);
63-
"""
64-
let adBlockScript = WKUserScript(source: adBlockerScript, injectionTime: .atDocumentEnd, forMainFrameOnly: true)
65-
webView.configuration.userContentController.addUserScript(adBlockScript)
54+
/* Hide ads from specific URLs */
55+
[href*="doubleclick.net"], [href*="googleadservices.com"], [href*="advertising.com"], [src*="adserver.com"] {
56+
display: none !important;
57+
}
58+
`;
59+
document.head.appendChild(adBlockStyle);
60+
"""
61+
let adBlockInject = WKUserScript(source: adBlockerScript, injectionTime: .atDocumentEnd, forMainFrameOnly: true)
62+
webView.configuration.userContentController.addUserScript(adBlockInject)
6663

67-
let userScriptInjection = WKUserScript(source: userScript, injectionTime: .atDocumentEnd, forMainFrameOnly: true)
68-
webView.configuration.userContentController.addUserScript(userScriptInjection)
64+
let CSSInject = WKUserScript(source: styleSheet, injectionTime: .atDocumentEnd, forMainFrameOnly: true)
65+
webView.configuration.userContentController.addUserScript(CSSInject)
66+
67+
// Loads URL when app opens
68+
loadURL()
69+
}
70+
71+
// Sets minimum sizing for window and defines toolbar title's content
72+
.frame(minWidth: 1000, minHeight: 600)
73+
.navigationTitle(pageTitle)
74+
75+
// Toolbar with keyboard shortcuts and tooltips
76+
.toolbar {
77+
// Back button
78+
ToolbarItem(placement: .navigation) {
79+
Button(action: goBack) {
80+
Image(systemName: "chevron.left")
81+
}
82+
.help("Go back")
83+
.keyboardShortcut(KeyEquivalent.leftArrow, modifiers: [.command])
84+
.disabled(!webView.canGoBack)
6985
}
70-
}
71-
// Toolbar and navigation stuff
72-
.frame(minWidth: 1000, minHeight: 600)
73-
.navigationTitle("")
74-
.toolbar {
75-
ToolbarItem(placement: .navigation) {
76-
Button(action: goBack) {
77-
Image(systemName: "chevron.left")
86+
87+
// Forward button
88+
ToolbarItem(placement: .navigation) {
89+
Button(action: goForward) {
90+
Image(systemName: "chevron.right")
91+
}
92+
.help("Go forward")
93+
.keyboardShortcut(KeyEquivalent.rightArrow, modifiers: [.command])
94+
.disabled(!webView.canGoForward)
7895
}
79-
.help("Go back")
80-
}
81-
82-
ToolbarItem(placement: .navigation) {
83-
Button(action: goForward) {
84-
Image(systemName: "chevron.right")
96+
97+
// Address Bar
98+
ToolbarItem(placement: .status) {
99+
TextField("Search or enter URL", text: $URLString, onCommit: loadURL)
100+
.textFieldStyle(RoundedBorderTextFieldStyle())
101+
.frame(width: 500, height: nil) // Set a fixed width
102+
.multilineTextAlignment(.center)
103+
.lineLimit(1) // Limit to 1 line
104+
.truncationMode(.tail) // Truncates at the end
105+
.onAppear {
106+
self.URLString = webView.url?.absoluteString ?? ""
107+
}
108+
.help("Enter a URL or search term")
85109
}
86-
.help("Go forward")
87-
}
88-
89-
ToolbarItem(placement: .primaryAction) {
90-
TextField("Search or enter URL", text: $urlString, onCommit: loadUrl)
91-
.textFieldStyle(RoundedBorderTextFieldStyle())
92-
.frame(width: 400, height: nil) // Set a fixed width
93-
.multilineTextAlignment(.center)
94-
.lineLimit(1) // Limit to 1 line
95-
.truncationMode(.tail) // Truncates at the end
96-
.onAppear {
97-
self.urlString = webView.url?.absoluteString ?? ""
110+
111+
// Refresh button
112+
ToolbarItem(placement: .navigation) {
113+
Button(action: refresh) {
114+
Image(systemName: "arrow.clockwise")
98115
}
99-
.help("Enter a URL or search term")
100-
}
101-
102-
ToolbarItem(placement: .primaryAction) {
103-
Button(action: loadUrl) {
104-
Image(systemName: "arrow.up.circle.fill")
116+
.help("Refresh this page")
117+
.keyboardShortcut("r", modifiers: [.command])
105118
}
106-
.help("Begin loading page")
107-
}
108-
109-
ToolbarItem(placement: .status) {
110-
if let faviconImage = faviconImage {
111-
Image(nsImage: faviconImage)
112-
.resizable()
113-
.frame(width: 18, height: 18) // Size set to 18x18 for favicon
114-
} else {
115-
Image(systemName: "link")
116-
.resizable()
117-
.frame(width: 18, height: 18) // Placeholder icon
119+
120+
// Favicon display
121+
ToolbarItem(placement: .navigation) {
122+
if let faviconImage = faviconImage {
123+
Image(nsImage: faviconImage)
124+
.resizable()
125+
.frame(width: 18, height: 18) // Size set to 18x18 for favicon
126+
} else {
127+
Image(systemName: "globe.americas.fill")
128+
.resizable()
129+
.frame(width: 18, height: 18) // Placeholder icon
130+
.font(Font.title.weight(.bold))
131+
}
118132
}
119-
}
120-
121-
ToolbarItem(placement: .status) {
122-
Text(pageTitle)
123-
.font(.headline)
124-
.lineLimit(1)
125-
}
126-
127-
ToolbarItem(placement: .status) {
128-
Spacer()
129-
}
130-
131-
ToolbarItem(placement: .navigation) {
132-
Button(action: refresh) {
133-
Image(systemName: "arrow.clockwise")
133+
134+
// Spacer between address bar and next button chunk
135+
ToolbarItem(placement: .primaryAction) {
136+
Spacer()
137+
}
138+
139+
// Share button
140+
ToolbarItem(placement: .primaryAction) {
141+
Button(action: {
142+
shareURL(URLString)
143+
}) {
144+
Image(systemName: "square.and.arrow.up")
145+
}
146+
.help("Share this page")
134147
}
135-
.help("Reload this page")
136-
}
137-
}
138-
.onAppear {
139-
if let userAgent = webView.value(forKey: "userAgent") as? String {
140-
self.userAgent = userAgent.replacingOccurrences(of: "Mobile", with: "Safari")
141148
}
142-
loadUrl()
143-
}
144149
}
145150

146-
// Basic functions
151+
// Sets up functions for webpage commands
147152
private func goBack() {
148153
if webView.canGoBack {
149154
webView.goBack()
155+
// Adds extremely slight delay before refreshing page when navigating
156+
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
157+
refresh()
158+
}
150159
}
151160
}
152161

153162
private func goForward() {
154163
if webView.canGoForward {
155164
webView.goForward()
165+
// Adds extremely slight delay before refreshing page when navigating
166+
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
167+
refresh()
168+
}
156169
}
157170
}
158171

159172
private func refresh() {
160173
webView.reload()
161174
}
162175

163-
private func loadUrl() {
164-
let trimmedUrlString = urlString.trimmingCharacters(in: .whitespacesAndNewlines)
165-
if trimmedUrlString.contains(".") {
176+
private func shareURL(_ URLString: String) {
177+
guard let url = URL(string: URLString) else {
178+
print("Invalid URL")
179+
return
180+
}
181+
182+
let items: [Any] = [url]
183+
let sharingServicePicker = NSSharingServicePicker(items: items)
184+
185+
sharingServicePicker.show(relativeTo: webView.bounds, of: webView, preferredEdge: .minY)
186+
}
187+
188+
private func loadURL() {
189+
let trimmedURLString = URLString.trimmingCharacters(in: .whitespacesAndNewlines)
190+
if trimmedURLString.contains(".") {
166191
// Contains a dot, likely a URL
167-
if let url = URL(string: addHttpIfNeeded(trimmedUrlString)) {
192+
if let url = URL(string: addHttpIfNeeded(trimmedURLString)) {
168193
let request = URLRequest(url: url)
169194
webView.load(request)
170195
}
@@ -175,26 +200,28 @@ struct BrowserView: View {
175200
}
176201

177202
private func search() {
178-
guard let searchQuery = urlString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else { return }
203+
guard let searchQuery = URLString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else { return }
179204
if let searchURL = URL(string: "https://google.com/search?q=\(searchQuery)") {
180205
let request = URLRequest(url: searchURL)
181206
webView.load(request)
182207
}
183208
}
184209

185-
private func addHttpIfNeeded(_ urlString: String) -> String {
186-
if urlString.hasPrefix("http://") || urlString.hasPrefix("https://") {
187-
return urlString
210+
private func addHttpIfNeeded(_ URLString: String) -> String {
211+
if URLString.hasPrefix("http://") || URLString.hasPrefix("https://") {
212+
return URLString
188213
} else {
189-
return "https://\(urlString)"
214+
return "https://\(URLString)"
190215
}
191216
}
192217
}
193218

219+
// I genuinely do not know
220+
194221
struct WebView: NSViewRepresentable {
195222
let webView: WKWebView
196223
@Binding var pageTitle: String // Binding for the pageTitle
197-
@Binding var urlString: String // Binding for the urlString
224+
@Binding var URLString: String // Binding for the URLString
198225
@Binding var faviconImage: NSImage? // Binding for the favicon image
199226
@Binding var userAgent: String // Binding for the user agent
200227

@@ -211,32 +238,34 @@ struct WebView: NSViewRepresentable {
211238
}
212239

213240
func makeCoordinator() -> Coordinator {
214-
Coordinator(webView: webView, pageTitle: $pageTitle, urlString: $urlString, faviconImage: $faviconImage)
241+
Coordinator(webView: webView, pageTitle: $pageTitle, URLString: $URLString, faviconImage: $faviconImage)
215242
}
216243

217244
class Coordinator: NSObject, WKNavigationDelegate {
218245
let webView: WKWebView
219246
@Binding var pageTitle: String // Binding for the pageTitle
220-
@Binding var urlString: String // Binding for the urlString
247+
@Binding var URLString: String // Binding for the URLString
221248
@Binding var faviconImage: NSImage? // Binding for the favicon image
222249

223-
init(webView: WKWebView, pageTitle: Binding<String>, urlString: Binding<String>, faviconImage: Binding<NSImage?>) {
250+
init(webView: WKWebView, pageTitle: Binding<String>, URLString: Binding<String>, faviconImage: Binding<NSImage?>) {
224251
self.webView = webView
225252
self._pageTitle = pageTitle
226-
self._urlString = urlString
253+
self._URLString = URLString
227254
self._faviconImage = faviconImage
228255
}
256+
257+
// Sets up functions to gather webpage information to display in toolbar
229258

230259
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
231260
webView.evaluateJavaScript("document.title") { (result, error) in
232261
if let title = result as? String {
233262
self.pageTitle = title
234-
self.urlString = webView.url?.absoluteString ?? ""
263+
self.URLString = webView.url?.absoluteString ?? ""
235264
self.loadFavicon()
236265
}
237266
}
238267
}
239-
268+
240269
private func loadFavicon() {
241270
let script = """
242271
var favicon = document.querySelector('link[rel="shortcut icon"]') || document.querySelector('link[rel="icon"]');

0 commit comments

Comments
 (0)