Skip to content

Commit e69d120

Browse files
committed
feat(legal): integrate privacy policy and terms of service into the application, #3577
- Added a new internal package for legal documents, including privacy policy and terms of service. - Implemented a build script to convert markdown files to HTML for easy access. - Updated the mobile and desktop applications to link to the new legal documents. - Enhanced the login modal to include links to the privacy policy and terms of service. - Updated routing in the SSR application to serve the legal documents. These changes ensure compliance with legal requirements and improve user access to important information. Signed-off-by: Innei <[email protected]>
1 parent e2c3bdd commit e69d120

File tree

24 files changed

+585
-84
lines changed

24 files changed

+585
-84
lines changed
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { legalHtml } from "@follow/legal"
2+
import { stopPropagation } from "@follow/utils/dom"
3+
import { m } from "motion/react"
4+
import type { FC } from "react"
5+
6+
type LegalModalProps = {
7+
type: "privacy" | "tos"
8+
}
9+
10+
export const LegalModalContent: FC<LegalModalProps> = ({ type }) => {
11+
const content = type === "privacy" ? legalHtml.privacy : legalHtml.tos
12+
13+
return (
14+
<m.div className="size-full overflow-hidden">
15+
<div className="bg-background size-full overflow-auto rounded-lg" onClick={stopPropagation}>
16+
<iframe
17+
sandbox="allow-scripts"
18+
srcDoc={content}
19+
title={type === "privacy" ? "Privacy Policy" : "Terms of Service"}
20+
className="size-full border-0"
21+
/>
22+
</div>
23+
</m.div>
24+
)
25+
}

apps/desktop/layer/renderer/src/modules/auth/LoginModalContent.tsx

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,12 @@ import { m } from "motion/react"
1010
import { useState } from "react"
1111
import { Trans, useTranslation } from "react-i18next"
1212

13-
import { useCurrentModal } from "~/components/ui/modal/stacked/hooks"
13+
import { useCurrentModal, useModalStack } from "~/components/ui/modal/stacked/hooks"
1414
import { loginHandler } from "~/lib/auth"
1515
import { useAuthProviders } from "~/queries/users"
1616

1717
import { LoginWithPassword, RegisterForm } from "./Form"
18+
import { LegalModalContent } from "./LegalModal"
1819

1920
interface LoginModalContentProps {
2021
runtime: LoginRuntime
@@ -23,6 +24,7 @@ interface LoginModalContentProps {
2324

2425
export const LoginModalContent = (props: LoginModalContentProps) => {
2526
const modal = useCurrentModal()
27+
const { present } = useModalStack()
2628

2729
const { canClose = true, runtime } = props
2830

@@ -36,6 +38,17 @@ export const LoginModalContent = (props: LoginModalContentProps) => {
3638
const [isRegister, setIsRegister] = useState(true)
3739
const [isEmail, setIsEmail] = useState(false)
3840

41+
const handleOpenLegal = (type: "privacy" | "tos") => {
42+
present({
43+
id: `legal-${type}`,
44+
title: type === "privacy" ? t("login.privacy") : t("login.terms"),
45+
content: () => <LegalModalContent type={type} />,
46+
resizeable: true,
47+
clickOutsideToDismiss: true,
48+
max: true,
49+
})
50+
}
51+
3952
const Inner = (
4053
<>
4154
<div className="-mt-9 mb-4 flex items-center justify-center">
@@ -103,6 +116,23 @@ export const LoginModalContent = (props: LoginModalContentProps) => {
103116
/>
104117
</div>
105118
)}
119+
120+
<div className="text-text-secondary mt-3 text-center text-xs leading-5">
121+
<span>{t("login.agree_to")}</span> <br />
122+
<a
123+
onClick={() => handleOpenLegal("tos")}
124+
className="text-accent cursor-pointer hover:underline"
125+
>
126+
{t("login.terms")}
127+
</a>{" "}
128+
&{" "}
129+
<a
130+
onClick={() => handleOpenLegal("privacy")}
131+
className="text-accent cursor-pointer hover:underline"
132+
>
133+
{t("login.privacy")}
134+
</a>
135+
</div>
106136
</>
107137
)
108138
if (isMobile) {

apps/desktop/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
"@follow/configs": "workspace:*",
5353
"@follow/constants": "workspace:*",
5454
"@follow/hooks": "workspace:*",
55+
"@follow/legal": "workspace:*",
5556
"@follow/models": "workspace:*",
5657
"@follow/shared": "workspace:*",
5758
"@follow/utils": "workspace:*",

apps/mobile/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
"@follow/components": "workspace:*",
2525
"@follow/constants": "workspace:*",
2626
"@follow/hooks": "workspace:*",
27+
"@follow/legal": "workspace:*",
2728
"@follow/models": "workspace:*",
2829
"@follow/shared": "workspace:*",
2930
"@follow/tracker": "workspace:*",

apps/mobile/src/modules/login/index.tsx

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { Logo } from "@/src/components/ui/logo"
1010
import { useNavigation } from "@/src/lib/navigation/hooks"
1111
import { NavigationLink } from "@/src/lib/navigation/NavigationLink"
1212
import { useScaleHeight } from "@/src/lib/responsive"
13+
import { PrivacyPolicyScreen } from "@/src/screens/(headless)/privacy"
1314
import { TermsMarkdown, TermsScreen } from "@/src/screens/(headless)/terms"
1415

1516
import { EmailLogin, EmailSignUp } from "./email"
@@ -110,16 +111,26 @@ const TermsText = () => {
110111
return (
111112
<ContextMenu.Root>
112113
<ContextMenu.Trigger className="w-full overflow-hidden rounded-full">
113-
<Text className="text-secondary-label text-sm">
114+
<Text className="text-secondary-label text-center text-sm">
114115
By continuing, you agree to our{" "}
116+
</Text>
117+
<View className="flex-row items-center">
115118
<NavigationLink
116119
destination={TermsScreen}
117120
suppressHighlighting
118-
className="text-primary-label"
121+
className="text-secondary-label"
119122
>
120123
<Text className="font-semibold">Terms of Service</Text>
121124
</NavigationLink>
122-
</Text>
125+
<Text className="text-secondary-label">&nbsp;&&nbsp;</Text>
126+
<NavigationLink
127+
destination={PrivacyPolicyScreen}
128+
suppressHighlighting
129+
className="text-secondary-label"
130+
>
131+
<Text className="font-semibold">Privacy Policy</Text>
132+
</NavigationLink>
133+
</View>
123134
</ContextMenu.Trigger>
124135

125136
<ContextMenu.Content>

apps/mobile/src/modules/settings/routes/Privacy.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
GroupedInsetListNavigationLink,
1010
} from "@/src/components/ui/grouped/GroupedList"
1111
import { useNavigation } from "@/src/lib/navigation/hooks"
12+
import { PrivacyPolicyScreen } from "@/src/screens/(headless)/privacy"
1213
import { TermsScreen } from "@/src/screens/(headless)/terms"
1314

1415
export const PrivacyScreen = () => {
@@ -26,6 +27,12 @@ export const PrivacyScreen = () => {
2627
pushControllerView(TermsScreen)
2728
}}
2829
/>
30+
<GroupedInsetListNavigationLink
31+
label={t("privacy.privacy")}
32+
onPress={() => {
33+
pushControllerView(PrivacyPolicyScreen)
34+
}}
35+
/>
2936
</GroupedInsetListCard>
3037
</SafeNavigationScrollView>
3138
)
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { legalMarkdown } from "@follow/legal/dist/index"
2+
3+
import {
4+
NavigationBlurEffectHeaderView,
5+
SafeNavigationScrollView,
6+
} from "@/src/components/layouts/views/SafeNavigationScrollView"
7+
import { Markdown } from "@/src/components/ui/typography/Markdown"
8+
import type { NavigationControllerView } from "@/src/lib/navigation/types"
9+
10+
export const PrivacyMarkdown = () => {
11+
return (
12+
<Markdown
13+
value={legalMarkdown.privacy}
14+
webViewProps={{ scrollEnabled: false, matchContents: true }}
15+
style={{ padding: 16, flex: 1 }}
16+
/>
17+
)
18+
}
19+
20+
export const PrivacyPolicyScreen: NavigationControllerView = () => {
21+
return (
22+
<SafeNavigationScrollView
23+
className="bg-system-background"
24+
contentInsetAdjustmentBehavior="never"
25+
Header={<NavigationBlurEffectHeaderView title="Privacy Policy" />}
26+
>
27+
<PrivacyMarkdown />
28+
</SafeNavigationScrollView>
29+
)
30+
}

apps/mobile/src/screens/(headless)/terms.tsx

Lines changed: 3 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1,91 +1,16 @@
1+
import { legalMarkdown } from "@follow/legal/dist/index"
2+
13
import {
24
NavigationBlurEffectHeaderView,
35
SafeNavigationScrollView,
46
} from "@/src/components/layouts/views/SafeNavigationScrollView"
57
import { Markdown } from "@/src/components/ui/typography/Markdown"
68
import type { NavigationControllerView } from "@/src/lib/navigation/types"
79

8-
const txt = `# Terms of Service
9-
10-
**Effective Date:** 2025-01-17
11-
12-
Welcome to Folo, your personalized RSS reader and content hub. By using our application, you agree to these Terms of Service ("Terms"). Please read them carefully as they govern your use of the Service and the rights and obligations that come with it.
13-
14-
Folo is designed to give you an intuitive, efficient, and user-friendly experience in managing your RSS feeds. We aim to provide a seamless and secure environment, but it’s important for you to understand how your rights are protected and the scope of your responsibilities while using the Service.
15-
16-
## 1. Acceptance of Terms
17-
By accessing or using Folo ("the Service"), you agree to comply with and be bound by these Terms and our Privacy Policy. If you do not agree to these Terms, you may not use the Service. These Terms are a legally binding contract between you and Natural Selection Limited, which owns and operates Folo. By using the Service, you acknowledge that you are responsible for your actions and for ensuring that your usage of Folo is consistent with these Terms.
18-
19-
## 2. Eligibility
20-
You must be at least 13 years old to use Folo. By using the Service, you represent and warrant that you meet this eligibility requirement. If you are under the age of 13, you are prohibited from using the Service. Natural Selection Limited reserves the right to suspend or terminate the access of any user who violates these eligibility requirements. Additionally, if you are using Folo on behalf of a company, you confirm that you have the authority to bind the company to these Terms.
21-
22-
## 3. User Account
23-
To access certain features of the Service, you may be required to create an account. You are responsible for maintaining the confidentiality of your account credentials, such as your username and password, and for all activities that occur under your account. If you suspect any unauthorized access or use of your account, you must notify us immediately to avoid any potential security breaches. You agree to provide accurate, up-to-date information when creating or maintaining your account and understand that failure to do so may result in limitations to your access or functionality of the Service.
24-
25-
## 4. Permitted Use
26-
You agree to use Folo solely for lawful purposes and in a manner that does not violate the rights of others. You shall not use the Service for any unlawful, harmful, or malicious activities. You are prohibited from transmitting harmful content such as malware, viruses, or phishing attempts, and from interfering with the operation or security features of the Service. Unauthorized attempts to gain access to the Service through hacking, password mining, or any other unlawful means are strictly prohibited and may result in immediate termination of your account.
27-
28-
You also agree not to exploit any part of the Service, including features, tools, or content, for commercial purposes unless explicitly authorized by Natural Selection Limited.
29-
30-
## 5. Content and Intellectual Property
31-
32-
### 5.1 User Content
33-
Folo enables you to import, subscribe to, and read content via RSS feeds. You retain full ownership of any content you post, upload, or submit to the Service. By submitting or sharing content, you grant us a worldwide, royalty-free, and non-exclusive license to host, display, modify, and distribute your content as necessary to operate, improve, and provide the Service. You are solely responsible for ensuring that the content you share does not infringe on the intellectual property rights of any third party. You also agree to respect the rights of content creators and copyright holders.
34-
35-
### 5.2 Intellectual Property Rights
36-
The Service and its underlying technology, including software, designs, and content, are owned by Natural Selection Limited or its licensors. You are granted a limited, non-exclusive, non-transferable right to access and use the Service solely for personal, non-commercial purposes. You may not copy, modify, reverse-engineer, distribute, or otherwise exploit any part of the Service without explicit permission from us. All trademarks, logos, and service marks displayed on the Service are the property of Natural Selection Limited or their respective owners. Unauthorized use of any intellectual property displayed on the Service is strictly prohibited.
37-
38-
### 5.3 AI Features and Usage
39-
Folo incorporates AI-powered features that assist in content translation, summarization, intelligent recommendations, and more. While these features are designed to enhance your user experience, you acknowledge that the accuracy and usefulness of AI-generated content may vary. We do not guarantee the correctness, completeness, or reliability of AI outputs and disclaim all responsibility for any adverse effects resulting from their use. Use of these features is entirely at your own risk, and you agree to hold Natural Selection Limited harmless for any errors, misunderstandings, or unintended outcomes arising from the use of AI functionality.
40-
41-
### 5.4 $POWER Economy
42-
Folo introduces the $POWER system, a way for users to support content creators and contributors by tipping or rewarding them with $POWER. You can acquire $POWER by participating in the community, completing designated tasks, or purchasing it through the app. You agree to abide by the rules governing the $POWER system, including: (i) not engaging in fraudulent activities or exploiting the system for illegal or malicious purposes; (ii) acknowledging that $POWER cannot be exchanged for real-world currency or transferred outside of the Service; (iii) accepting that $POWER transactions are final and non-refundable.
43-
44-
Folo reserves the right to modify, suspend, or terminate the $POWER system at any time without prior notice.
45-
46-
## 6. Prohibited Activities
47-
You agree not to engage in any of the following activities while using the Service:
48-
- Engage in any illegal or harmful activities, including distributing malicious software or engaging in data breaches.
49-
- Attempt to gain unauthorized access to any part of the Service or its security features.
50-
- Misuse the $POWER system by gaming the economy or making fraudulent transactions.
51-
- Engage in any behavior that disrupts the normal functionality of the Service or harms other users’ experiences.
52-
53-
Violations of these activities may result in the immediate suspension or termination of your account. In some cases, legal action may be taken if necessary to protect our rights or the rights of others.
54-
55-
## 7. Disclaimer of Warranties
56-
The Service is provided on an "AS IS" and "AS AVAILABLE" basis. We do not make any representations or warranties regarding the availability, functionality, or reliability of the Service. We disclaim all express or implied warranties, including but not limited to warranties of merchantability, fitness for a particular purpose, and non-infringement. We do not guarantee that the Service will be uninterrupted, error-free, or free from security vulnerabilities.
57-
58-
## 8. Limitation of Liability
59-
To the fullest extent permitted by law, Natural Selection Limited shall not be liable for any indirect, incidental, special, or consequential damages arising from your use of the Service, including but not limited to loss of profits, data, or goodwill. In no event shall our liability exceed the total amount you have paid to access the Service during the past 12 months. You agree that your use of the Service is at your own risk.
60-
61-
## 9. Modifications to the Terms
62-
We reserve the right to modify these Terms at any time. Any changes will be effective immediately upon posting on the Service. We will notify you of any significant changes, but it is your responsibility to review the Terms periodically. Your continued use of the Service after any modifications will constitute your acceptance of the updated Terms. If you do not agree to the changes, you must cease using the Service and delete your account.
63-
64-
## 10. Termination
65-
We may suspend, disable, or terminate your access to the Service at any time, for any reason, including but not limited to violations of these Terms, fraudulent behavior, or other actions that disrupt the normal operation of the Service. Upon termination, your account will be deactivated, and you may lose access to your content and any other account-related data. If you wish to terminate your account, you can do so by contacting us or using the account settings feature within the app.
66-
67-
## 11. Governing Law
68-
These Terms are governed by and construed in accordance with the laws of [Insert Jurisdiction]. Any disputes arising out of or in connection with these Terms will be subject to the exclusive jurisdiction of the courts of [Insert Jurisdiction].
69-
70-
## 12. Contact Us
71-
If you have any questions, concerns, or inquiries about these Terms, please contact us at:
72-
73-
74-
By using Folo, you acknowledge that you have read, understood, and agree to these Terms of Service, as well as our Privacy Policy.
75-
76-
## 13. Community Participation and Contribution
77-
Folo is an open-source project, and we welcome contributions from users and developers. If you are eligible to use Folo, you may participate in the development of the Service by submitting bug reports, feature requests, and improvements. All contributions must adhere to our [code of conduct](https://github.com/RSSNext/Folo/blob/main/CODE_OF_CONDUCT.md).
78-
79-
Before contributing, ensure that you have read and understood our contributing guidelines and the [Corepack](https://nodejs.org/api/corepack.html) setup instructions. By contributing, you agree that your submissions will be licensed under the terms of the [GNU General Public License](https://www.gnu.org/licenses/gpl-3.0.html) version 3.
80-
81-
## 14. Privacy and Data Use
82-
Folo takes your privacy seriously. As a user, you acknowledge that we may collect, store, and process your personal information, including your usage patterns and interactions with content. We are committed to ensuring that your data is handled securely and transparently. Please refer to our [Privacy Policy](#) for more information on how we collect, process, and protect your data.
83-
`
84-
8510
export const TermsMarkdown = () => {
8611
return (
8712
<Markdown
88-
value={txt}
13+
value={legalMarkdown.tos}
8914
webViewProps={{ scrollEnabled: false, matchContents: true }}
9015
style={{ padding: 16, flex: 1 }}
9116
/>

apps/mobile/src/sitemap.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { Navigation } from "./lib/navigation/Navigation"
66
import { NavigationSitemapRegistry } from "./lib/navigation/sitemap/registry"
77
import type { NavigationControllerView } from "./lib/navigation/types"
88
import { OTPWindow } from "./modules/settings/components/OTPWindow"
9+
import { PrivacyPolicyScreen } from "./screens/(headless)/privacy"
910
import { TermsScreen } from "./screens/(headless)/terms"
1011
import { ForgetPasswordScreen } from "./screens/(modal)/ForgetPasswordScreen"
1112
import { InvitationScreen } from "./screens/(modal)/InvitationScreen"
@@ -14,7 +15,7 @@ import { TwoFactorAuthScreen } from "./screens/(modal)/TwoFactorAuthScreen"
1415
import { OnboardingScreen } from "./screens/OnboardingScreen"
1516

1617
export function registerSitemap() {
17-
;[TermsScreen].forEach((Component) => {
18+
;[TermsScreen, PrivacyPolicyScreen].forEach((Component) => {
1819
NavigationSitemapRegistry.registerByComponent(Component)
1920
})
2021
;[LoginScreen, InvitationScreen, ForgetPasswordScreen, TwoFactorAuthScreen].forEach(

apps/ssr/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { FetchError } from "ofetch"
1010

1111
import { isDev } from "~/lib/env"
1212
import { MetaError } from "~/meta-handler"
13+
import { staticRoute } from "~/router/static"
1314

1415
import { globalRoute } from "./src/router/global"
1516
import { ogRoute } from "./src/router/og"
@@ -77,6 +78,7 @@ export const createApp = async () => {
7778

7879
ogRoute(app)
7980
globalRoute(app)
81+
staticRoute(app)
8082

8183
return app
8284
}

0 commit comments

Comments
 (0)