fix(mobile): improve layout and styling consistency across components

- Adjusted padding in EntryTitle and EntryDetailScreen for uniformity.
- Updated HTML component to enhance structure and readability.
- Modified CSS classes in HTML and image components for better alignment.
- Cleaned up link component styles to remove unnecessary classes.

These changes enhance the overall user interface and maintain a consistent design throughout the application.

Signed-off-by: Innei <tukon479@gmail.com>
This commit is contained in:
Innei 2025-04-22 12:57:03 +08:00
parent c7afad6b27
commit 017d9864cd
No known key found for this signature in database
GPG Key ID: 0F62D33977F021F7
7 changed files with 63 additions and 71 deletions

View File

@ -23,7 +23,6 @@ class FOWebView: WKWebView {
if #available(iOS 16.4, *) {
isInspectable = true
}
}
private var state: WebViewState!
@ -39,46 +38,45 @@ class FOWebView: WKWebView {
navigationDelegate = self
uiDelegate = self
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
private class FOWKWebViewConfiguration: WKWebViewConfiguration {
static public let shared = FOWKWebViewConfiguration()
public static let shared = FOWKWebViewConfiguration()
override init() {
super.init()
let configuration = self
let hexAccentColor = Utils.accentColor.toHex()
let css = """
:root { overflow: hidden !important; overflow-behavior: none !important; }
body {
overflow-y: visible !important;
position: absolute !important;
width: 100% !important;
height: auto !important;
-webkit-overflow-scrolling: touch !important;
}
::selection {
background-color: \(hexAccentColor) !important;
}
"""
:root { overflow: hidden !important; overflow-behavior: none !important; }
body {
overflow-y: visible !important;
position: absolute !important;
width: 100% !important;
height: auto !important;
-webkit-overflow-scrolling: touch !important;
}
::selection {
background-color: \(hexAccentColor) !important;
}
"""
let script = WKUserScript(
_ = WKUserScript(
source: """
var style = document.createElement('style');
style.textContent = '\(css)';
document.head.appendChild(style);
""",
var style = document.createElement('style');
style.textContent = '\(css)';
document.head.appendChild(style);
""",
injectionTime: .atDocumentStart,
forMainFrameOnly: true
)
let atStartScripts = WKWebView.loadInjectedJs(forResource: "at_start")
if let jsString = atStartScripts {
let script = WKUserScript(
@ -104,54 +102,54 @@ private class FOWKWebViewConfiguration: WKWebViewConfiguration {
let schemeHandler = FollowImageURLSchemeHandler()
configuration.setURLSchemeHandler(
schemeHandler, forURLScheme: FollowImageURLSchemeHandler.rewriteScheme)
schemeHandler, forURLScheme: FollowImageURLSchemeHandler.rewriteScheme
)
let customSchemeScript = WKUserScript(
source: """
(function() {
const originalXHROpen = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function(method, url, ...args) {
const modifiedUrl = url.replace(/^https?:/, '\(FollowImageURLSchemeHandler.rewriteScheme):');
originalXHROpen.call(this, method, modifiedUrl, ...args);
};
(function() {
const originalXHROpen = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function(method, url, ...args) {
const modifiedUrl = url.replace(/^https?:/, '\(FollowImageURLSchemeHandler.rewriteScheme):');
originalXHROpen.call(this, method, modifiedUrl, ...args);
};
const originalFetch = window.fetch;
window.fetch = function(url, options) {
const modifiedUrl = url.replace(/^https?:/, '\(FollowImageURLSchemeHandler.rewriteScheme):');
return originalFetch(modifiedUrl, options);
};
const originalFetch = window.fetch;
window.fetch = function(url, options) {
const modifiedUrl = url.replace(/^https?:/, '\(FollowImageURLSchemeHandler.rewriteScheme):');
return originalFetch(modifiedUrl, options);
};
const originalImageSrc = Object.getOwnPropertyDescriptor(Image.prototype, 'src');
Object.defineProperty(Image.prototype, 'src', {
set: function(url) {
const modifiedUrl = url.replace(/^https?:/, '\(FollowImageURLSchemeHandler.rewriteScheme):');
originalImageSrc.set.call(this, modifiedUrl);
}
});
})();
""",
const originalImageSrc = Object.getOwnPropertyDescriptor(Image.prototype, 'src');
Object.defineProperty(Image.prototype, 'src', {
set: function(url) {
const modifiedUrl = url.replace(/^https?:/, '\(FollowImageURLSchemeHandler.rewriteScheme):');
originalImageSrc.set.call(this, modifiedUrl);
}
});
})();
""",
injectionTime: .atDocumentStart,
forMainFrameOnly: false
)
configuration.userContentController.addUserScript(customSchemeScript)
configuration.userContentController.addUserScript(script2)
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension WKWebView {
fileprivate static func loadInjectedJs(forResource: String) -> String? {
private extension WKWebView {
static func loadInjectedJs(forResource: String) -> String? {
if let bundleURL = Bundle(for: WebViewView.self).url(
forResource: "js", withExtension: "bundle"),
forResource: "js", withExtension: "bundle"
),
let resourceBundle = Bundle(url: bundleURL)
{
if let jsPath = resourceBundle.path(forResource: forResource, ofType: "js") {
do {
let initJsContent = try String(contentsOfFile: jsPath, encoding: .utf8)
@ -161,15 +159,12 @@ extension WKWebView {
print("Error reading JS file:", error)
}
}
}
return nil
}
}
extension FOWebView: WKNavigationDelegate, WKScriptMessageHandler, WKUIDelegate {
func userContentController(
_ userContentController: WKUserContentController, didReceive message: WKScriptMessage
) {
@ -184,7 +179,8 @@ extension FOWebView: WKNavigationDelegate, WKScriptMessageHandler, WKUIDelegate
switch data.type {
case "setContentHeight":
let data = try? JSONDecoder().decode(
SetContentHeightPayload.self, from: decode)
SetContentHeightPayload.self, from: decode
)
guard let data = data else { return }
DispatchQueue.main.async {
@ -192,11 +188,12 @@ extension FOWebView: WKNavigationDelegate, WKScriptMessageHandler, WKUIDelegate
}
case "measure":
self.measureWebView(SharedWebViewModule.sharedWebView!)
measureWebView(SharedWebViewModule.sharedWebView!)
case "previewImage":
let data = try? JSONDecoder().decode(
PreviewImagePayload.self, from: decode)
PreviewImagePayload.self, from: decode
)
guard let data = data else { return }
DispatchQueue.main.async {
@ -209,15 +206,13 @@ extension FOWebView: WKNavigationDelegate, WKScriptMessageHandler, WKUIDelegate
default:
break
}
}
}
}
private func measureWebView(_ webView: WKWebView) {
let jsCode = "document.querySelector('#root').scrollHeight"
webView.evaluateJavaScript(jsCode) { (height, error) in
webView.evaluateJavaScript(jsCode) { height, _ in
if let height = height as? CGFloat, height > 0 {
DispatchQueue.main.async {
self.state.contentHeight = height
@ -233,7 +228,6 @@ extension FOWebView: WKNavigationDelegate, WKScriptMessageHandler, WKUIDelegate
Timer.scheduledTimer(withTimeInterval: 1, repeats: false) { [weak self] _ in
self?.measureWebView(webView)
}
}
func webView(
@ -241,7 +235,7 @@ extension FOWebView: WKNavigationDelegate, WKScriptMessageHandler, WKUIDelegate
for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures
) -> WKWebView? {
if let url = navigationAction.request.url,
let viewController = Utils.getRootVC()
let viewController = Utils.getRootVC()
{
WebViewManager.presentModalWebView(url: url, from: viewController)
}
@ -254,7 +248,7 @@ extension FOWebView: WKNavigationDelegate, WKScriptMessageHandler, WKUIDelegate
) {
if navigationAction.targetFrame == nil {
if let url = navigationAction.request.url,
let viewController = Utils.getRootVC()
let viewController = Utils.getRootVC()
{
WebViewManager.presentModalWebView(url: url, from: viewController)
decisionHandler(.cancel)

View File

@ -24,7 +24,7 @@ export const EntryTitle = ({ title, entryId }: { title: string; entryId: string
}}
>
<EntryTranslation
className="text-label px-4 text-4xl font-bold leading-snug"
className="text-label px-5 text-4xl font-bold leading-snug"
source={title}
target={translation?.title}
/>

View File

@ -133,7 +133,7 @@ const EntryInfo = ({ entryId }: { entryId: string }) => {
const { publishedAt } = entry
return (
<View className="mt-4 flex flex-row items-center gap-4 px-4">
<View className="mt-4 flex flex-row items-center gap-4 px-5">
{feed && (
<View className="flex shrink flex-row items-center gap-2">
<FeedIcon feed={feed} />

View File

@ -40,11 +40,9 @@ export const App = () => {
return (
<Provider store={store}>
<HTML
children={entry?.content}
renderInlineStyle={readerRenderInlineStyle}
noMedia={noMedia}
/>
<HTML renderInlineStyle={readerRenderInlineStyle} noMedia={noMedia}>
{entry?.content}
</HTML>
</Provider>
)
}

View File

@ -65,7 +65,7 @@ export const HTML = <A extends keyof JSX.IntrinsicElements = "div">(props: HTMLP
{
...rest,
ref: setRefElement,
className: clsx("prose mx-auto px-3", "dark:prose-invert"),
className: clsx("prose mx-auto px-5 pb-8", "dark:prose-invert"),
},
markdownElement,
)}

View File

@ -37,7 +37,7 @@ export const MarkdownImage = (props: HTMLProps<"img">) => {
return (
<button
type="button"
className="relative -mx-3 overflow-hidden bg-gray-300 dark:bg-neutral-800"
className="relative -mx-5 overflow-hidden bg-gray-300 dark:bg-neutral-800"
style={{
width: scaleWidth || undefined,
height: scaleHeight || undefined,

View File

@ -21,7 +21,7 @@ export const MarkdownLink = (props: LinkProps) => {
<TooltipTrigger asChild>
<a
draggable="false"
className="follow-link--underline text-foreground font-semibold no-underline"
className="text-foreground font-semibold no-underline"
href={populatedFullHref}
title={props.title}
target="_blank"