mirror of
https://github.com/RSSNext/Folo.git
synced 2026-10-26 18:29:58 +08:00
+1








913b1178c8
* fix(path): change path package to pathe * fix: handle special chat in filename, fixed #3342 - Integrated @sindresorhus/slugify to format file names based on titles. - Updated pnpm-lock.yaml to include new dependencies. - Minor adjustments in package.json files to ensure consistency. Signed-off-by: Innei <tukon479@gmail.com> * fix: add eslint rule for use pathe first * refactor: useWhoami from user store (#4061) * refactor: useWhoami from user store * update * update * update * update * return all * fix: invalidate entries query after unsubscribe * feat: implement imperative modal (#4041) * feat: implement ImperativeModalProvider with modal context and controls * fix: make onClose prop optional for bottom modal * chore: add ImperativeModalProvider to context providers * feat: wrap BottomModal content in KeyboardAvoidingView for better keyboard handling * chore: add polyfill for Promise.withResolvers * fix: wrap ImperativeModal in a View and update onClose to accept modal ID * feat: implement ImperativeModal with context and modal control hooks * refactor: redesign imperative modal with root-siblings * fix: wrap BottomModal in a View to resolve rendering issues on Android * refactor: replace Alert.prompt with modalPrompt for category creation and renaming * fix(desktop): show login modal when auth fail * feat: invalidate entry readability cache * refactor: long press to open text select sheet for ai summary * feat: new domain folo.is * fix: enhance auth client with mobile-specific headers (#4064) * feat(desktop): add missing i18n key (#4066) * feat: update twitter handle * docs: use google play badge * feat: add hideAvatar prop to ProfileSettingForm for conditional avatar display Signed-off-by: Innei <tukon479@gmail.com> * docs: update images * feat(ssr): add Google Analytics tracking to index.html - Integrated Google Tag Manager for enhanced tracking and analytics capabilities. - Added asynchronous loading of gtag.js and configuration for tracking ID G-DZMBZBW3EC. Signed-off-by: Innei <tukon479@gmail.com> * chore: Update Japanese Languages (#4078) * chore: update ja.json * chore: update 6 ja.json files chore: update and bump 6 ja.json files. * chore: update error/ja.json fix locale/error/ja.json * chore: auto-fix linting and formatting issues * chore: update app/ja.json * chore: update ja.json * chore: update ja.json * chore: update ja.json and tested local. * chore: update ja.json update settings/ja.json * feat(i18n): update Japanese locale files with new error messages and dialog options * chore(vscode): set default formatter for JSON and JSONC files * Remove JSON and JSONC formatter settings so sorry, @hyoban 😢 Perhaps my local settings are in. I have restored it to the original. Thanks for your kindness. You have always been a great help. * chore: auto-fix linting and formatting issues * chore: update ja.json sync and update settings/ja.json * chore: update Japanese translations for clarity and consistency * chore: update some ja.json * chore(locales): update Japanese translations with new entry actions and settings * fix: update Japanese translations for user actions and login prompts * feat(locales): add Japanese translations for login and registration features * feat(locales): update Japanese translations for RSSHub and related settings * chore: auto-fix linting and formatting issues * feat(i18n): update Japanese locale with new toolbar customization and profile link social fields * feat(locales): update Japanese translations for search results and two-factor authentication * feat: 日本語ローカライズの更新 --------- Co-authored-by: dai <dai@users.noreply.github.com> Co-authored-by: Innei <i@innei.in> Co-authored-by: Innei <Innei@users.noreply.github.com> * feat(desktop): real user deletion * feat(mobile): real user deletion * feat(settings): add accent color customization and update UI components - Introduced accent color setting to allow users to customize the app's interface. - Updated various components to utilize the new accent color, including buttons, sliders, and text elements. - Added translations for accent color settings in English, Japanese, and Chinese. - Refactored related styles in Tailwind CSS configuration to support the new accent color feature. Signed-off-by: Innei <tukon479@gmail.com> * fix: update text styling for feed parameters message * feat: use insertedBefore for mark above read * feat(mobile): use insertedBefore for mark above read * fix: improved lightbox component for iOS (#4089) * feat: replace Image component with ProxyImage for improved performance and styling * fix: update Image component style prop * fix: improved layout in ImageItem component * feat: integrate animated ref for media preview in EntryPictureItem * fix: option can not be press when out of screen in the FlatList * fix: lint * fix: types * fix: update lock * fix: lint * chore: auto-fix linting and formatting issues --------- Signed-off-by: Innei <tukon479@gmail.com> Co-authored-by: Innei <tukon479@gmail.com> Co-authored-by: Stephen Zhou <38493346+hyoban@users.noreply.github.com> Co-authored-by: Whitewater <me@waterwater.moe> Co-authored-by: lawvs <18554747+lawvs@users.noreply.github.com> Co-authored-by: DIYgod <i@diygod.me> Co-authored-by: Konv Suu <2583695112@qq.com> Co-authored-by: dai <dosada@gmail.com> Co-authored-by: dai <dai@users.noreply.github.com> Co-authored-by: Innei <i@innei.in> Co-authored-by: Innei <Innei@users.noreply.github.com>
119 lines
3.1 KiB
TypeScript
119 lines
3.1 KiB
TypeScript
import fs from "node:fs"
|
|
|
|
import path from "pathe"
|
|
import { parse } from "svg-parser"
|
|
|
|
const DIST_DIR = "apps/mobile/src/icons"
|
|
|
|
const DEFAULT_COLOR = "#10161F"
|
|
interface SvgNode {
|
|
type: string
|
|
tagName: string
|
|
properties: Record<string, any>
|
|
children?: SvgNode[]
|
|
}
|
|
|
|
const generateElement = (node: SvgNode): string => {
|
|
const props = Object.entries(node.properties)
|
|
.map(([key, value]) => {
|
|
const camelKey = key.replaceAll(/-([a-z])/g, (_match, p1: string) => p1.toUpperCase())
|
|
|
|
if (
|
|
["stroke", "fill"].includes(key) &&
|
|
(!value || value === "currentColor" || value === DEFAULT_COLOR)
|
|
) {
|
|
return `${camelKey}={color}`
|
|
}
|
|
|
|
if (typeof value === "number") {
|
|
return `${camelKey}={${value}}`
|
|
}
|
|
|
|
return `${camelKey}="${value}"`
|
|
})
|
|
.join(" ")
|
|
|
|
const tagName = `${node.tagName.charAt(0).toUpperCase()}${node.tagName.slice(1)}`
|
|
|
|
return ` <${tagName} ${props} />`
|
|
}
|
|
|
|
const isPureBackgroundPath = (node: any) => {
|
|
return (
|
|
node.tagName === "path" &&
|
|
node.properties.fill === "#fff" &&
|
|
node.properties["fill-opacity"] === 0.01 &&
|
|
node.properties.d === "M24 0v24H0V0z"
|
|
)
|
|
}
|
|
|
|
const supportedTags = new Set(["path", "circle"])
|
|
|
|
const convertSvgToRN = (svgContent: string, componentName: string) => {
|
|
const ast = parse(svgContent)
|
|
const svgNode = ast.children[0] as SvgNode
|
|
const { width, height } = svgNode.properties
|
|
|
|
const pathElements =
|
|
svgNode.children
|
|
?.filter((child) => supportedTags.has(child.tagName))
|
|
.filter((child) => !isPureBackgroundPath(child))
|
|
.map((node) => generateElement(node))
|
|
.join("\n") ?? ""
|
|
|
|
return `import * as React from "react"
|
|
import Svg, { Circle, Path } from "react-native-svg"
|
|
|
|
interface ${componentName}Props {
|
|
width?: number
|
|
height?: number
|
|
color?: string
|
|
}
|
|
|
|
export const ${componentName} = ({
|
|
width = ${width},
|
|
height = ${height},
|
|
${pathElements.includes(`{color}`) ? `color = "${DEFAULT_COLOR}",` : ""}
|
|
}: ${componentName}Props) => {
|
|
return (
|
|
<Svg width={width} height={height} fill="none" viewBox="0 0 ${width} ${height}">
|
|
${pathElements}
|
|
</Svg>
|
|
)
|
|
}
|
|
`
|
|
}
|
|
|
|
const processFile = (filePath: string) => {
|
|
const svgContent = fs.readFileSync(filePath, "utf-8")
|
|
const fileName = path.basename(filePath, ".svg")
|
|
const componentName = `${fileName
|
|
.split("_")
|
|
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
|
|
.join("")}Icon`
|
|
|
|
const rnComponent = convertSvgToRN(svgContent, componentName)
|
|
|
|
const outputDir = path.join(process.cwd(), DIST_DIR)
|
|
if (!fs.existsSync(outputDir)) {
|
|
fs.mkdirSync(outputDir, { recursive: true })
|
|
}
|
|
|
|
const outputPath = path.join(outputDir, `${fileName}.tsx`)
|
|
fs.writeFileSync(outputPath, rnComponent)
|
|
// eslint-disable-next-line no-console
|
|
console.log(`Converted ${filePath} to ${outputPath}`)
|
|
}
|
|
|
|
// 处理整个目录
|
|
const processDirectory = (dirPath: string) => {
|
|
const files = fs.readdirSync(dirPath)
|
|
files.forEach((file) => {
|
|
if (file.endsWith(".svg")) {
|
|
processFile(path.join(dirPath, file))
|
|
}
|
|
})
|
|
}
|
|
|
|
processDirectory(path.join(process.cwd(), "icons/mgc"))
|