Skip to content

Commit

Permalink
chore: Release v0.2.7-beta.0 (#2125)
Browse files Browse the repository at this point in the history
  • Loading branch information
Innei authored Dec 12, 2024
2 parents 91fdc87 + 40dc4c6 commit 123320b
Show file tree
Hide file tree
Showing 112 changed files with 4,633 additions and 3,475 deletions.
32 changes: 31 additions & 1 deletion CHANGELOG.md

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions apps/main/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,13 @@
"builder-util-runtime": "9.2.10",
"dompurify": "~3.2.2",
"electron-context-menu": "4.0.4",
"electron-log": "5.2.3",
"electron-log": "5.2.4",
"electron-squirrel-startup": "1.0.1",
"electron-updater": "^6.3.9",
"es-toolkit": "1.29.0",
"fast-folder-size": "2.3.0",
"font-list": "1.5.1",
"i18next": "^24.0.2",
"i18next": "^24.0.5",
"js-yaml": "4.1.0",
"linkedom": "^0.18.5",
"lowdb": "7.0.1",
Expand All @@ -55,6 +55,6 @@
"@types/node": "^22.10.1",
"electron": "33.2.0",
"electron-devtools-installer": "3.2.0",
"hono": "4.6.12"
"hono": "4.6.13"
}
}
21 changes: 16 additions & 5 deletions apps/main/src/lib/readability.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,19 @@ import { isDev } from "~/env"

const userAgents = `Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36 ${name}/${version}`

// For avoiding xss attack from readability, the raw document string should be sanitized.
// The xss attack in electron may lead to more serious outcomes than browser environment.
// It may allows remotely execute malicious scripts in main process.
// Before the sanitizing, the DOMPurify requires a `window` environment provided by linkedom.
function sanitizeHTMLString(dirtyDocumentString: string) {
const parser = parseHTML(dirtyDocumentString)
const purify = DOMPurify(parser.window)
// How do DOMPurify changes the origin html structure,
// You can refer its document https://github.com/cure53/DOMPurify?tab=readme-ov-file#can-i-configure-dompurify
const sanitizedDocumentString = purify.sanitize(dirtyDocumentString)
return sanitizedDocumentString
}

export async function readability(url: string) {
const dirtyDocumentString = await fetch(url, {
headers: {
Expand All @@ -27,15 +40,13 @@ export async function readability(url: string) {
return res.text()
})

// For avoid xss attack from readability, the raw document string should be purified.
const cleanedDocumentString = DOMPurify.sanitize(dirtyDocumentString)
const sanitizedDocumentString = sanitizeHTMLString(dirtyDocumentString)
const baseUrl = new URL(url).origin

// FIXME: linkedom does not handle relative addresses in strings. Refer to
// @see https://github.com/WebReflection/linkedom/issues/153
// JSDOM handles it correctly, but JSDOM introduces canvas binding.

const { document } = parseHTML(cleanedDocumentString)
const baseUrl = new URL(url).origin
const { document } = parseHTML(sanitizedDocumentString)

document.querySelectorAll("a").forEach((a) => {
a.href = replaceRelativeAddress(baseUrl, a.href)
Expand Down
1 change: 1 addition & 0 deletions apps/main/vitest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export default defineProject({
},

plugins: [
// @ts-expect-error
tsconfigPath({
projects: ["./tsconfig.json"],
}),
Expand Down
1 change: 1 addition & 0 deletions apps/renderer/.env
48 changes: 24 additions & 24 deletions apps/renderer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@dnd-kit/core": "^6.2.0",
"@dnd-kit/core": "^6.3.1",
"@egoist/tipc": "0.3.2",
"@electron-toolkit/preload": "^3.0.1",
"@follow/electron-main": "workspace:*",
"@follow/shared": "workspace:*",
"@fontsource/sn-pro": "5.1.0",
"@headlessui/react": "2.2.0",
"@hookform/resolvers": "3.9.1",
"@lottiefiles/dotlottie-react": "0.10.1",
"@lottiefiles/dotlottie-react": "0.11.0",
"@microflash/remark-callout-directives": "4.3.2",
"@openpanel/web": "1.0.1",
"@radix-ui/react-avatar": "1.1.1",
Expand All @@ -31,13 +31,13 @@
"@radix-ui/react-popover": "1.1.2",
"@radix-ui/react-slider": "^1.2.1",
"@radix-ui/react-slot": "1.1.0",
"@sentry/react": "8.41.0",
"@shikijs/transformers": "1.24.0",
"@tanstack/query-sync-storage-persister": "5.62.0",
"@tanstack/react-query": "5.62.0",
"@tanstack/react-query-devtools": "5.62.0",
"@tanstack/react-query-persist-client": "5.62.0",
"@tanstack/react-virtual": "3.10.9",
"@sentry/react": "8.42.0",
"@shikijs/transformers": "1.24.1",
"@tanstack/query-sync-storage-persister": "5.62.3",
"@tanstack/react-query": "5.62.3",
"@tanstack/react-query-devtools": "5.62.3",
"@tanstack/react-query-persist-client": "5.62.3",
"@tanstack/react-virtual": "3.11.0",
"@use-gesture/react": "10.3.1",
"@welldone-software/why-did-you-render": "8.0.3",
"@yornaath/batshit": "0.10.1",
Expand All @@ -49,24 +49,24 @@
"dexie": "4.0.10",
"dexie-export-import": "^4.1.4",
"dnum": "^2.14.0",
"electron-log": "5.2.3",
"electron-log": "5.2.4",
"embla-carousel-react": "8.5.1",
"embla-carousel-wheel-gestures": "8.0.1",
"es-toolkit": "1.29.0",
"firebase": "11.0.2",
"foxact": "0.2.42",
"framer-motion": "11.12.0",
"foxact": "0.2.43",
"framer-motion": "11.13.1",
"franc-min": "6.2.0",
"fuse.js": "7.0.0",
"hast-util-to-jsx-runtime": "2.3.2",
"hast-util-to-mdast": "^10.1.1",
"hast-util-to-text": "4.0.2",
"i18next": "^24.0.2",
"i18next-browser-languagedetector": "8.0.0",
"i18next": "^24.0.5",
"i18next-browser-languagedetector": "8.0.2",
"idb-keyval": "6.2.1",
"immer": "10.1.1",
"jotai": "2.10.3",
"katex": "0.16.11",
"katex": "0.16.14",
"lethargy": "1.0.9",
"masonic": "4.0.1",
"mdast-util-gfm-table": "^2.0.0",
Expand All @@ -79,13 +79,13 @@
"react-blurhash": "^0.3.0",
"react-error-boundary": "4.1.2",
"react-fast-marquee": "1.6.5",
"react-hook-form": "7.53.2",
"react-hook-form": "7.54.0",
"react-hotkeys-hook": "4.6.1",
"react-i18next": "^15.1.3",
"react-intersection-observer": "9.13.1",
"react-ios-pwa-prompt": "^2.0.6",
"react-resizable-layout": "npm:@innei/react-resizable-layout@0.7.3-fork.1",
"react-router": "7.0.1",
"react-router": "7.0.2",
"react-selecto": "^1.26.3",
"react-shadow": "20.5.0",
"react-zoom-pan-pinch": "^3.6.1",
Expand All @@ -98,22 +98,22 @@
"remark-gh-alerts": "0.0.3",
"remark-parse": "11.0.0",
"remark-rehype": "11.1.1",
"shiki": "1.24.0",
"sonner": "1.7.0",
"tldts": "6.1.65",
"shiki": "1.24.1",
"sonner": "1.7.1",
"tldts": "6.1.66",
"unified": "11.0.5",
"unist-util-visit-parents": "^6.0.1",
"use-context-selector": "2.0.0",
"use-pull-to-refresh": "2.4.1",
"use-sync-external-store": "1.2.2",
"use-sync-external-store": "1.4.0",
"usehooks-ts": "3.1.0",
"vfile": "6.0.3",
"web-push": "3.6.7",
"zod": "3.23.8",
"zustand": "5.0.1"
"zustand": "5.0.2"
},
"devDependencies": {
"@babel/generator": "7.26.2",
"@babel/generator": "7.26.3",
"@follow/atoms": "workspace:*",
"@follow/components": "workspace:*",
"@follow/constants": "workspace:*",
Expand All @@ -133,6 +133,6 @@
"happy-dom": "15.11.7",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-scan": "0.0.35"
"react-scan": "0.0.41"
}
}
5 changes: 5 additions & 0 deletions apps/renderer/src/atoms/feed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export const useFeedUnreadIsDirty = (feedId: string) => {
() =>
selectAtom(feedUnreadDirtySetAtom, (set) => {
const isRealFeedId = isBizId(feedId)

if (isRealFeedId) return set.has(feedId)

if (feedId.startsWith(ROUTE_FEED_IN_LIST) || feedId.startsWith(INBOX_PREFIX_ID)) {
Expand Down Expand Up @@ -67,3 +68,7 @@ export const clearFeedUnreadDirty = (feedId: string) => {
return newSet
})
}

export const clearAllFeedUnreadDirty = () => {
jotaiStore.set(feedUnreadDirtySetAtom, new Set())
}
7 changes: 3 additions & 4 deletions apps/renderer/src/atoms/player.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { getStorageNS } from "@follow/utils/ns"
import { parseSafeUrl } from "@follow/utils/utils"
import { noop } from "foxact/noop"
import { atomWithStorage, createJSONStorage } from "jotai/utils"
import type { SyncStorage } from "jotai/vanilla/utils/atomWithStorage"
Expand Down Expand Up @@ -80,10 +81,8 @@ export const AudioPlayer = {
show: true,
listId: routeParams.listId,
})
const currentUrl = URL.canParse(this.audio.src)
? new URL(this.audio.src).toString()
: this.audio.src
const newUrl = URL.canParse(v.src) ? new URL(v.src).toString() : v.src
const currentUrl = parseSafeUrl(this.audio.src)?.toString() ?? this.audio.src
const newUrl = parseSafeUrl(v.src)?.toString() ?? v.src

if (currentUrl !== newUrl) {
this.audio.src = v.src
Expand Down
3 changes: 2 additions & 1 deletion apps/renderer/src/atoms/settings/general.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,9 @@ const createDefaultSettings = (): GeneralSettings => ({
hoverMarkUnread: true,
renderMarkUnread: false,
// UX

groupByDate: true,
autoExpandLongSocialMedia: false,

// Secure
jumpOutLinkWarn: true,
// TTS
Expand Down
6 changes: 3 additions & 3 deletions apps/renderer/src/components/ui/media/SwipeMedia.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,10 @@ export function SwipeMedia({
)}
>
{uniqMedia?.length ? (
<div ref={emblaRef} className="w-full overflow-hidden">
<div className="flex">
<div ref={emblaRef} className="size-full overflow-hidden">
<div className="flex size-full">
{uniqMedia?.slice(0, 5).map((med, i) => (
<div className="mr-2 w-full flex-none" key={med.url}>
<div className="mr-2 size-full flex-none" key={med.url}>
<Media
className={cn(imgClassName, "size-full rounded-none")}
alt="cover"
Expand Down
2 changes: 1 addition & 1 deletion apps/renderer/src/components/ui/media/preview-media.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -435,7 +435,7 @@ const FallbackableImage: FC<
)}
{isAllError && (
<div
className="center pointer-events-none absolute inset-0 flex-col gap-6 text-white/80"
className="center pointer-events-none absolute inset-0 flex-col gap-6"
onClick={stopPropagation}
tabIndex={-1}
>
Expand Down
12 changes: 9 additions & 3 deletions apps/renderer/src/components/ui/modal/stacked/constants.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Target } from "framer-motion"
import type { MotionProps, Target } from "framer-motion"

import { microReboundPreset } from "../../constants/spring"

Expand All @@ -14,9 +14,15 @@ const initialStyle: Target = {
export const modalMontionConfig = {
initial: initialStyle,
animate: enterStyle,
exit: initialStyle,
exit: {
...initialStyle,
// no spring
transition: {
type: "tween",
},
},
transition: microReboundPreset,
}
} satisfies MotionProps

// Radix context menu z-index 999
export const MODAL_STACK_Z_INDEX = 1001
4 changes: 0 additions & 4 deletions apps/renderer/src/components/ui/placeholder.tsx

This file was deleted.

41 changes: 30 additions & 11 deletions apps/renderer/src/components/ux/transition/icon.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { cn } from "@follow/utils/utils"
import type { Target } from "framer-motion"
import { AnimatePresence, m } from "framer-motion"
import { useEffect, useState } from "react"
import * as React from "react"
import { cloneElement, useEffect, useState } from "react"

type TransitionType = {
initial: Target | boolean
Expand All @@ -10,8 +11,8 @@ type TransitionType = {
}

type IconTransitionProps = {
icon1: string
icon2: string
icon1: string | React.JSX.Element
icon2: string | React.JSX.Element
status: "init" | "done"
className?: string
icon1ClassName?: string
Expand All @@ -34,21 +35,39 @@ const createIconTransition =
return (
<AnimatePresence mode="popLayout">
{status === "init" ? (
<m.i
className={cn(icon1ClassName, className, icon1)}
key="1"
initial={initial}
animate={animate}
exit={exit}
/>
) : (
typeof icon1 === "string" ? (
<m.i
className={cn(icon1ClassName, className, icon1)}
key="1"
initial={initial}
animate={animate}
exit={exit}
/>
) : (
cloneElement(icon1, {
className: cn(icon1ClassName, className),
key: "1",
initial,
animate,
exit,
})
)
) : typeof icon2 === "string" ? (
<m.i
className={cn(icon2ClassName, className, icon2)}
key="2"
initial={initial}
animate={animate}
exit={exit}
/>
) : (
cloneElement(icon2, {
className: cn(icon2ClassName, className),
key: "2",
initial,
animate,
exit,
})
)}
</AnimatePresence>
)
Expand Down
Loading

0 comments on commit 123320b

Please sign in to comment.