Skip to content

Commit

Permalink
Merge branch 'dev' into i18n/add-de
Browse files Browse the repository at this point in the history
  • Loading branch information
upupnoah authored Sep 24, 2024
2 parents 47177a3 + a45071b commit 22c8012
Show file tree
Hide file tree
Showing 58 changed files with 957 additions and 62 deletions.
15 changes: 15 additions & 0 deletions .github/workflows/pr-title-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
name: PR Conventional Commit Validation

on:
pull_request:
types: [opened, synchronize, reopened, edited]

jobs:
validate-pr-title:
runs-on: ubuntu-latest
steps:
- name: PR Conventional Commit Validation
uses: ytanikin/PRConventionalCommits@1.1.0
with:
task_types: '["feat","fix","docs","test","ci","refactor","perf","chore","revert"]'
custom_labels: '{"feat": "feature", "fix": "fix", "docs": "documentation", "test": "test", "ci": "CI/CD", "refactor": "refactor", "perf": "performance", "chore": "chore", "revert": "revert"}'
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,16 @@
Next generation information browser.
<br />
<br />
<a href="https://discord.gg/xHCVQZ6wmZ">Discord</a>
<a href="https://discord.gg/followapp">Discord</a>
·
<a href="https://x.com/intent/follow?screen_name=follow_app_">Twitter</a>
·
<a href="https://github.com/RSSNext/Follow/releases">Releases</a>
<br />
<br />
<a href="https://trendshift.io/repositories/9938" target="_blank"><img src="https://trendshift.io/api/badge/repositories/9938" alt="RSSNext%2Ffollow | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
<a href="https://trendshift.io/repositories/9938" target="_blank"><img src="https://github.com/user-attachments/assets/7d394847-f019-46e5-94ec-5bf4ccda6146" alt="RSSNext%2Ffollow | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
<a href="https://trendshift.io/repositories/9938" target="_blank"><img src="https://github.com/user-attachments/assets/0696530c-cfe3-4f2e-975f-07ef31521092" alt="RSSNext%2Ffollow | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
<a href="https://trendshift.io/repositories/9938" target="_blank"><img src="https://github.com/user-attachments/assets/c3c6ee92-f81a-4f0a-b9d7-c03832c8a3ee" alt="RSSNext%2Ffollow | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
</p>
</div>

Expand Down Expand Up @@ -73,7 +75,7 @@ The project is currently under active development. The following screenshots are

## Releases

[![Discord](https://img.shields.io/discord/1243823539426033696?logo=discord&label=Discord&style=flat-square&color=5865F2)](https://discord.gg/xHCVQZ6wmZ) [![](https://img.shields.io/badge/any_text-Follow-blue?color=2CA5E0&label=_&logo=x&cacheSeconds=3600&style=flat-square)](https://x.com/intent/follow?screen_name=follow_app_)
[![Discord](https://img.shields.io/discord/1243823539426033696?logo=discord&label=Discord&style=flat-square&color=5865F2)](https://discord.gg/followapp) [![](https://img.shields.io/badge/any_text-Follow-blue?color=2CA5E0&label=_&logo=x&cacheSeconds=3600&style=flat-square)](https://x.com/intent/follow?screen_name=follow_app_)

Currently, Follow is still in the early developer preview stage (alpha) and is only available to a limited number of users through an invitation system.

Expand Down
19 changes: 14 additions & 5 deletions apps/main/src/lib/cleaner.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import { callGlobalContextMethod } from "@follow/shared/bridge"
import { callWindowExpose } from "@follow/shared/bridge"
import { dialog } from "electron"

import { getMainWindow } from "~/window"

import { t } from "./i18n"

export const clearAllData = async () => {
export const clearAllDataAndConfirm = async () => {
const win = getMainWindow()
if (!win) return
const ses = win.webContents.session

// Dialog to confirm
const result = await dialog.showMessageBox({
Expand All @@ -21,6 +20,15 @@ export const clearAllData = async () => {
if (result.response === 1) {
return
}
return clearAllData()
}

export const clearAllData = async () => {
const win = getMainWindow()
if (!win) return
const ses = win.webContents.session
const caller = callWindowExpose(win)

try {
await ses.clearCache()

Expand All @@ -36,11 +44,12 @@ export const clearAllData = async () => {
"cookies",
],
})
callGlobalContextMethod(win, "toast.success", ["App data reset successfully"])

caller.toast.success("App data reset successfully")

// reload the app
win.reload()
} catch (error: any) {
callGlobalContextMethod(win, "toast.error", [`Error resetting app data: ${error.message}`])
caller.toast.error(`Error resetting app data: ${error.message}`)
}
}
4 changes: 2 additions & 2 deletions apps/main/src/menu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type { BrowserWindow, MenuItem, MenuItemConstructorOptions } from "electr
import { Menu } from "electron"

import { isDev, isMacOS } from "./env"
import { clearAllData } from "./lib/cleaner"
import { clearAllDataAndConfirm } from "./lib/cleaner"
import { t } from "./lib/i18n"
import { revealLogFile } from "./logger"
import { checkForUpdates, quitAndInstall } from "./updater"
Expand Down Expand Up @@ -38,7 +38,7 @@ export const registerAppMenu = () => {
{ type: "separator" },
{
label: t("menu.clearAllData"),
click: clearAllData,
click: clearAllDataAndConfirm,
},
{ role: "quit", label: t("menu.quit", { name }) },
],
Expand Down
10 changes: 7 additions & 3 deletions apps/main/src/tipc/app.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import path from "node:path"

import { getRendererHandlers } from "@egoist/tipc/main"
import { callGlobalContextMethod } from "@follow/shared/bridge"
import { callWindowExpose } from "@follow/shared/bridge"
import type { BrowserWindow } from "electron"
import { app, clipboard, dialog, screen } from "electron"

Expand Down Expand Up @@ -199,13 +199,17 @@ export const appRoute = {
await downloadFile(input, result.filePath).catch((err) => {
const senderWindow = (sender as Sender).getOwnerBrowserWindow()
if (!senderWindow) return
callGlobalContextMethod(senderWindow, "toast.error", ["Download failed!"])
callWindowExpose(senderWindow).toast.error("Download failed!", {
duration: 1000,
})
throw err
})

const senderWindow = (sender as Sender).getOwnerBrowserWindow()
if (!senderWindow) return
callGlobalContextMethod(senderWindow, "toast.success", ["Download success!"])
callWindowExpose(senderWindow).toast.success("Download success!", {
duration: 1000,
})
}),

getAppPath: t.procedure.action(async () => app.getAppPath()),
Expand Down
20 changes: 7 additions & 13 deletions apps/main/src/tipc/reader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import fs from "node:fs"
import { createRequire } from "node:module"
import path from "node:path"

import { callGlobalContextMethod } from "@follow/shared/bridge"
import { callWindowExpose } from "@follow/shared/bridge"
import { app, BrowserWindow } from "electron"
import { MsEdgeTTS, OUTPUT_FORMAT } from "msedge-tts"

Expand Down Expand Up @@ -54,12 +54,9 @@ export const readerRoute = {
if (!window) {
return
}
callGlobalContextMethod(window, "toast.error", [
error.message,
{
duration: 1000,
},
])
callWindowExpose(window).toast.error(error.message, {
duration: 1000,
})
}
}),

Expand All @@ -70,12 +67,9 @@ export const readerRoute = {
}

await tts.setMetadata(input, OUTPUT_FORMAT.WEBM_24KHZ_16BIT_MONO_OPUS).catch((error) => {
callGlobalContextMethod(window, "toast.error", [
error.message,
{
duration: 1000,
},
])
callWindowExpose(window).toast.error(error.message, {
duration: 1000,
})
})
}),

Expand Down
14 changes: 9 additions & 5 deletions apps/main/src/window.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import path from "node:path"
import { fileURLToPath } from "node:url"

import { is } from "@electron-toolkit/utils"
import { callGlobalContextMethod } from "@follow/shared/bridge"
import { callWindowExpose } from "@follow/shared/bridge"
import { imageRefererMatches } from "@follow/shared/image"
import type { BrowserWindowConstructorOptions } from "electron"
import { BrowserWindow, screen, shell } from "electron"
Expand Down Expand Up @@ -219,7 +219,8 @@ export const createMainWindow = () => {
window.hide()
}

callGlobalContextMethod(window, "electronClose")
const caller = callWindowExpose(window)
caller.electronClose()
} else {
windows.mainWindow = null
}
Expand All @@ -228,11 +229,13 @@ export const createMainWindow = () => {
window.on("show", () => {
cancelPollingUpdateUnreadCount()

callGlobalContextMethod(window, "electronShow")
const caller = callWindowExpose(window)
caller.electronShow()
})

window.on("hide", async () => {
const settings = await callGlobalContextMethod(window, "getUISettings")
const caller = callWindowExpose(window)
const settings = await caller.getUISettings()

if (settings.showDockBadge) {
pollingUpdateUnreadCount()
Expand All @@ -247,7 +250,8 @@ export const createSettingWindow = (path?: string) => {
// if we open a new window then the state between the two windows will be out of sync.
if (windows.mainWindow && windows.mainWindow.isVisible()) {
windows.mainWindow.show()
callGlobalContextMethod(windows.mainWindow, "showSetting", [path])

callWindowExpose(windows.mainWindow).showSetting(path)
return
}
if (windows.settingWindow) {
Expand Down
7 changes: 7 additions & 0 deletions apps/renderer/global.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,11 @@ declare global {
platform: NodeJS.Platform
}
}

declare module "react" {
export interface AriaAttributes {
"data-testid"?: string
}
}

export {}
2 changes: 2 additions & 0 deletions apps/renderer/src/@types/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export const currentSupportedLanguages = [
"ru",
"es",
"ko",
"tr",
]
export const dayjsLocaleImportMap = {
en: ["en", () => import("dayjs/locale/en")],
Expand All @@ -41,6 +42,7 @@ export const dayjsLocaleImportMap = {
["zh-HK"]: ["zh-hk", () => import("dayjs/locale/zh-hk")],
["de"]: ["de", () => import("dayjs/locale/de")],
["ko"]: ["ko", () => import("dayjs/locale/ko")],
["tr"]: ["tr", () => import("dayjs/locale/tr")],
}
export const ns = ["app", "common", "lang", "settings", "shortcuts", "errors"] as const
export const defaultNS = "app" as const
3 changes: 3 additions & 0 deletions apps/renderer/src/@types/default-resource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import common_ja from "../../../../locales/common/ja.json"
import common_ko from "../../../../locales/common/ko.json"
import common_pt from "../../../../locales/common/pt.json"
import common_ru from "../../../../locales/common/ru.json"
import common_tr from "../../../../locales/common/tr.json"
import common_zhCN from "../../../../locales/common/zh-CN.json"
import common_zhHK from "../../../../locales/common/zh-HK.json"
import common_zhTW from "../../../../locales/common/zh-TW.json"
Expand All @@ -38,6 +39,7 @@ import lang_ja from "../../../../locales/lang/ja.json"
import lang_ko from "../../../../locales/lang/ko.json"
import lang_pt from "../../../../locales/lang/pt.json"
import lang_ru from "../../../../locales/lang/ru.json"
import lang_tr from "../../../../locales/lang/tr.json"
import lang_zhCN from "../../../../locales/lang/zh-CN.json"
import lang_zhHK from "../../../../locales/lang/zh-HK.json"
import lang_zhTW from "../../../../locales/lang/zh-TW.json"
Expand Down Expand Up @@ -88,4 +90,5 @@ export const defaultResources = {
"ar-TN": { lang: lang_artn, common: common_artn },
de: { lang: lang_de, common: common_de },
ko: { lang: lang_ko, common: common_ko },
tr: { lang: lang_tr, common: common_tr },
}
99 changes: 99 additions & 0 deletions apps/renderer/src/components/ui/lottie-container/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import type { DotLottie } from "@lottiefiles/dotlottie-react"
import { DotLottieReact } from "@lottiefiles/dotlottie-react"
import { atom, useAtomValue } from "jotai"
import type { FC, ReactNode, RefCallback } from "react"
import { useEffect, useState } from "react"

import { jotaiStore } from "~/lib/jotai"

import { RootPortal } from "../portal"

const portalElementsAtom = atom([] as ReactNode[])

export function LottieRenderContainer() {
const elements = useAtomValue(portalElementsAtom, { store: jotaiStore })

return (
<RootPortal>
<div className="pointer-events-none fixed z-[999]" data-testid="lottie-render-container">
{elements.map((element) => element)}
</div>
</RootPortal>
)
}

type LottieOptions = {
once?: boolean

x: number
y: number

height?: number
width?: number
className?: string

onComplete?: () => void

speed?: number
}
export const mountLottie = (url: string, options: LottieOptions) => {
const { once = true, height, width, x, y, className, speed } = options

const Lottie: FC = () => {
const [dotLottie, setDotLottie] = useState<DotLottie | null>(null)

useEffect(() => {
function onComplete() {
if (once) {
unmount()
}

options.onComplete?.()
}

if (dotLottie) {
dotLottie.addEventListener("complete", onComplete)
}

return () => {
if (dotLottie) {
dotLottie.removeEventListener("complete", onComplete)
}
}
}, [dotLottie])

const dotLottieRefCallback: RefCallback<DotLottie> = (dotLottie) => {
setDotLottie(dotLottie)
}

return (
<DotLottieReact
speed={speed}
dotLottieRefCallback={dotLottieRefCallback}
src={url}
autoplay
loop={false}
height={height}
width={width}
style={{
height,
width,
position: "fixed",

left: 0,
top: 0,
transform: `translate(${x}px, ${y}px)`,
}}
className={className}
/>
)
}

const element = <Lottie />
const unmount = () => {
jotaiStore.set(portalElementsAtom, (prev) => prev.filter((e) => e !== element))
}

jotaiStore.set(portalElementsAtom, (prev) => [...prev, element])
return unmount
}
Loading

0 comments on commit 22c8012

Please sign in to comment.