Paste the link to the mobile app on the Google Play Store.
E.g. https://play.google.com/store/apps/details?id=com.x8bit.bitwarden
@@ -213,17 +292,28 @@
Subreddit
-
+
If the service has a subreddit, please provide the name here.
Don't include `r/` in the name, nor the full URL - just the sub name.
@@ -233,70 +323,95 @@
Privacy Checklist
- Finally, check the boxes that apply to the service you are submitting,
- and then provide any additional information to back this up in the text area below.
+ Finally, check the boxes that apply to the service you are submitting, and
+ then provide any additional information to back this up in the text area
+ below.
-
+
-
- Finally, please provide any supporting material, including:
-
+
Finally, please provide any supporting material, including:
A justification of why this app/service should be included in the list
+ Links to any published security audit, if they exist
- Links to any published security audit, if they exist
+ Links to the services privacy policy, terms of service and other
+ relevant documents where applicable
- Links to the services privacy policy, terms of service and other relevant
- documents where applicable
+ Your affiliation with the service. For transparency, you must disclose
+ if you are associated with them or any similar items in any way
- Your affiliation with the service.
- For transparency, you must disclose if you are associated
- with them or any similar items in any way
+ Links to relevant discussions, past issues/PRs related to this service
- Links to relevant discussions, past issues/PRs related to this service
-
+
Submit
-
Open in GitHub Issues
+
Open in GitHub Issues
-
Below is the YAML content, which will be appended to the appropriate section
- within awesome-privacy.yml
+
+ Below is the YAML content, which will be appended to the appropriate section
+ within awesome-privacy.yml
upon approval.
{#if !interactiveActivated || !codeBlock}
+
{@html yamlText}
{/if}
-
Your submission will need to be reviewed by a maintainer and the community before it can be merged.
+
+ Your submission will need to be reviewed by a maintainer and the community
+ before it can be merged.
+
diff --git a/web/src/components/things/Comments.svelte b/web/src/components/things/Comments.svelte
index 1a69a52..554e416 100644
--- a/web/src/components/things/Comments.svelte
+++ b/web/src/components/things/Comments.svelte
@@ -1,19 +1,25 @@
diff --git a/web/src/components/things/DataActions.svelte b/web/src/components/things/DataActions.svelte
index 04ccad4..c519441 100644
--- a/web/src/components/things/DataActions.svelte
+++ b/web/src/components/things/DataActions.svelte
@@ -1,18 +1,23 @@
{#if lineNumbers}
+
Edit {serviceName} Data
+
+ You can view or edit this {serviceName}'s entry in
+ this section
+ of awesome-privacy.yml in our GitHub repo.
+
-
Edit {serviceName} Data
-
- You can view or edit this {serviceName}'s entry in
-
- this section
-
- of awesome-privacy.yml in our GitHub repo.
-
-
-
Origin Data
-
-
-
Modify Data
-
+
Origin Data
+
+
Modify Data
+
{/if}
-
diff --git a/web/src/components/things/DeleteListing.svelte b/web/src/components/things/DeleteListing.svelte
index 2e825d0..ac26b73 100644
--- a/web/src/components/things/DeleteListing.svelte
+++ b/web/src/components/things/DeleteListing.svelte
@@ -1,15 +1,17 @@
-
diff --git a/web/src/components/things/DiscordDetailedInfo.astro b/web/src/components/things/DiscordDetailedInfo.astro
index cbf47b8..7b0eb84 100644
--- a/web/src/components/things/DiscordDetailedInfo.astro
+++ b/web/src/components/things/DiscordDetailedInfo.astro
@@ -1,110 +1,111 @@
---
-
import type { DiscordInfo } from '@utils/fetch-discord-info';
-import { formatDate, timeAgo } from '@utils/dates-n-stuff';
-import FontAwesome from "@components/form/FontAwesome.svelte"
-
interface Props {
- discordData: DiscordInfo;
-};
+ discordData: DiscordInfo;
+}
const { discordData } = Astro.props;
-
-
---
+
Discord
-
Discord
+
+
+ Server Name
+ {discordData.name}
+
+
+ Member Count
+ {discordData.memberCount} ({discordData.memberOnlineCount} online)
+
+
+ Initial Channel
+ {discordData.channel}
+
+
+ Inviter
+ {discordData.inviter || 'Anon'}
+
+
+ Join Link
+ discord.com/invite/{discordData.inviteCode}
+
+
-
-
- Server Name
- {discordData.name}
-
-
- Member Count
- {discordData.memberCount} ({discordData.memberOnlineCount} online)
-
-
- Initial Channel
- {discordData.channel}
-
-
- Inviter
- {discordData.inviter || 'Anon'}
-
-
- Join Link
- discord.com/invite/{discordData.inviteCode}
-
-
-
- { discordData.banner && (
)}
-
-
+ {
+ discordData.banner && (
+
+ )
+ }
-
diff --git a/web/src/components/things/GetSharableLink.svelte b/web/src/components/things/GetSharableLink.svelte
index 0fcc694..5af0bdf 100644
--- a/web/src/components/things/GetSharableLink.svelte
+++ b/web/src/components/things/GetSharableLink.svelte
@@ -1,37 +1,39 @@
-
diff --git a/web/src/components/things/IosAppDetailedInfo.astro b/web/src/components/things/IosAppDetailedInfo.astro
index 3884098..1979c08 100644
--- a/web/src/components/things/IosAppDetailedInfo.astro
+++ b/web/src/components/things/IosAppDetailedInfo.astro
@@ -1,7 +1,7 @@
---
import type { IoSApiResponse } from '@utils/fetch-ios-info';
-import { formatDate, timeAgo } from '@utils/dates-n-stuff';
+import { formatDate } from '@utils/dates-n-stuff';
import FontAwesome from "@components/form/FontAwesome.svelte"
@@ -18,7 +18,7 @@ const makeRatingPercentage = (rating: number) => (rating / 5) * 100;
const roundRatings = (rating: number) => Math.round(rating * 100) / 100;
-const putCommaInNumber = (num: number | any) => {
+const putCommaInNumber = (num: number | string | undefined) => {
if (!num) return 'Unknown';
return typeof num === 'number' ? num.toLocaleString() : num;
}
diff --git a/web/src/components/things/ItemGitHubMetrics.astro b/web/src/components/things/ItemGitHubMetrics.astro
index ff95855..ebb7ad4 100644
--- a/web/src/components/things/ItemGitHubMetrics.astro
+++ b/web/src/components/things/ItemGitHubMetrics.astro
@@ -2,13 +2,19 @@
import FontAwesome from "@components/form/FontAwesome.svelte";
const { github } = Astro.props;
-// const [user, repo] = github.split("/");
+interface GitHubRepoData {
+ stargazers_count?: number;
+ forks_count?: number;
+ open_issues_count?: number;
+ language?: string;
+ license?: { spdx_id?: string; name?: string };
+}
/**
* For a given `user/repo` fetch repository stats from the GitHub API data
* If API key is available through env var, use it to increase rate limit
* Returns the response data and status code (200 == success)
- * @param repo
+ * @param repo
*/
const fetchGitHubData = async (repo: string) => {
const apiKey = import.meta.env.GITHUB_API_KEY;
@@ -17,8 +23,8 @@ const fetchGitHubData = async (repo: string) => {
headers.append("Authorization", `token ${apiKey}`);
}
- let data = {};
- let statusCode = 0;
+ let data: GitHubRepoData = {};
+ let statusCode;
const response = await fetch(`https://api.github.com/repos/${repo}`, {
headers: headers,
@@ -50,7 +56,7 @@ const fetchGitHubData = async (repo: string) => {
* Given a license object, return SPDX ID, or a formatted name
* @param license
*/
-const formatLicense = (license: { spdx_id?: string, name?: string }) => {
+const formatLicense = (license?: { spdx_id?: string, name?: string }) => {
if (!license) {
return "Unknown";
}
@@ -65,7 +71,8 @@ const formatLicense = (license: { spdx_id?: string, name?: string }) => {
* E.g. If greater than thousand, then return in k format
* @param num
*/
-const formatBigNumber = (num: number) => {
+const formatBigNumber = (num: number | undefined) => {
+ if (num == null) return 0;
if (num > 1000) {
return `${(num / 1000).toFixed(1)}k`;
}
@@ -73,7 +80,7 @@ const formatBigNumber = (num: number) => {
}
// Initiate GitHub fetch, and make available to the component
-const stats = (await fetchGitHubData(github)) as any;
+const stats = await fetchGitHubData(github);
const {
stargazers_count, forks_count, open_issues_count, language, license,
diff --git a/web/src/components/things/RedditDetailedInfo.astro b/web/src/components/things/RedditDetailedInfo.astro
index 546e27b..e7776bb 100644
--- a/web/src/components/things/RedditDetailedInfo.astro
+++ b/web/src/components/things/RedditDetailedInfo.astro
@@ -1,172 +1,191 @@
---
-
import type { RedditData } from '@utils/fetch-reddit-info';
-import { timestampToDate, timeAgo } from '@utils/dates-n-stuff';
-import FontAwesome from "@components/form/FontAwesome.svelte"
-
+import { timestampToDate } from '@utils/dates-n-stuff';
interface Props {
- redditData: RedditData;
-};
+ redditData: RedditData;
+}
const { redditData } = Astro.props;
-
---
-
-
Reddit
-
-
- {redditData.info.title || redditData.info.name}
-
-
{redditData.info.description}
- {redditData.info.banner && (
)}
-
- {redditData.info.dateCreated && (
-
- Created at
- {timestampToDate(redditData.info.dateCreated * 1000)}
-
- )}
-
- Members
- {redditData.info.subscribers}
-
-
- Join
- {redditData.info.name}
-
-
-
-
-
Posts
-
- {redditData.posts.map((post) => (
-
- โ {post.title}
- (โฒ {post.upVotes} โผ {post.downVotes})
-
- ))}
-
-
+
+
Reddit
+
+
+ {redditData.info.title || redditData.info.name}
+
+
{redditData.info.description}
+ {
+ redditData.info.banner && (
+
+ )
+ }
+
+ {
+ redditData.info.dateCreated && (
+
+ Created at
+
+ {timestampToDate(redditData.info.dateCreated * 1000)}
+
+
+ )
+ }
+
+ Members
+ {redditData.info.subscribers}
+
+
+ Join
+ {redditData.info.name}
+
+
+
+
+
Posts
+
+ {
+ redditData.posts.map((post) => (
+
+ โ{' '}
+
+ {post.title}
+
+
+ (โฒ {post.upVotes} โผ {post.downVotes})
+
+
+ ))
+ }
+
+
-
diff --git a/web/src/components/things/SaveListing.svelte b/web/src/components/things/SaveListing.svelte
index a725971..2ea7a90 100644
--- a/web/src/components/things/SaveListing.svelte
+++ b/web/src/components/things/SaveListing.svelte
@@ -1,7 +1,7 @@
-
- {#if showLabel }
-
- {isSaved ? 'Saved' : 'Save'}
-
- {/if}
-
-
+
+ {#if showLabel}
+
+ {isSaved ? 'Saved' : 'Save'}
+
+ {/if}
+
+
-{#if showLabel && isSaved }
-
- You can view all saved items in your
Inventory
-
-{/if}
+ {#if showLabel && isSaved}
+
+ You can view all saved items in your
Inventory
+
+ {/if}
diff --git a/web/src/components/things/SectionList.astro b/web/src/components/things/SectionList.astro
index 76e9777..ad6e746 100644
--- a/web/src/components/things/SectionList.astro
+++ b/web/src/components/things/SectionList.astro
@@ -1,6 +1,4 @@
---
-
-
import FontAwesome from '@components/form/FontAwesome.svelte';
import { slugify } from '../../utils/fetch-data';
@@ -9,144 +7,161 @@ import type { Section } from '../../types/Service';
interface Props {
title: string;
sections: Section[];
- bigTitle?: boolean;
+ bigTitle?: boolean;
}
const { title, sections, bigTitle } = Astro.props;
-
---
+ {
+ bigTitle ? (
+
+
+ {title}{' '}
+
+ ) : (
+
+ {title}
+
+ )
+ }
-{ bigTitle ?
-
{title} :
-
{title}
-}
+ {
+ !bigTitle && (
+
+
+
+ )
+ }
-{ !bigTitle &&
}
-
-
+
-
diff --git a/web/src/components/things/ServiceCard.astro b/web/src/components/things/ServiceCard.astro
index f7756f5..312d0c0 100644
--- a/web/src/components/things/ServiceCard.astro
+++ b/web/src/components/things/ServiceCard.astro
@@ -1,5 +1,4 @@
---
-
import { formatLink } from '@utils/parse-markdown';
import type { Service } from 'src/types/Service';
import FontAwesome from '@components/form/FontAwesome.svelte';
@@ -7,86 +6,91 @@ import SaveListing from '@components/things/SaveListing.svelte';
import { slugify } from '@utils/fetch-data';
interface Props {
- service: Service;
- categoryName: string;
- sectionName: string;
+ service: Service;
+ categoryName: string;
+ sectionName: string;
}
-const {
- service,
- sectionName,
- categoryName,
-} = Astro.props;
-
+const { service, sectionName, categoryName } = Astro.props;
---
-
-
-
+
-
-
-
+
+
+
-
-
-
-
+
+
+
+
-
-
-
-
-
+
diff --git a/web/src/components/things/ServiceCard.svelte b/web/src/components/things/ServiceCard.svelte
index d76be99..3b06ed1 100644
--- a/web/src/components/things/ServiceCard.svelte
+++ b/web/src/components/things/ServiceCard.svelte
@@ -17,7 +17,10 @@
-
+
@@ -45,6 +44,7 @@
src={service.icon || `https://icon.horse/icon/${formatLink(service.url)}`}
/>
+
{@html service.description}
@@ -65,5 +65,5 @@
diff --git a/web/src/components/things/ServiceList.astro b/web/src/components/things/ServiceList.astro
index db4f69b..2be6a08 100644
--- a/web/src/components/things/ServiceList.astro
+++ b/web/src/components/things/ServiceList.astro
@@ -1,5 +1,4 @@
---
-
import Button from '@components/form/Button.astro';
import { parseMarkdown, formatLink } from '@utils/parse-markdown';
import type { Service } from 'src/types/Service';
@@ -11,326 +10,387 @@ import GitHubMetrics from '@components/things/ItemGitHubMetrics.astro';
import SaveListing from '@components/things/SaveListing.svelte';
interface Props {
- services: Service[];
- subHeading?: boolean;
- buttonLink?: string;
- noGitHubMetrics?: boolean;
- sectionName: string;
- categoryName: string;
+ services: Service[];
+ subHeading?: boolean;
+ buttonLink?: string;
+ noGitHubMetrics?: boolean;
+ sectionName: string;
+ categoryName: string;
}
const {
- services,
- subHeading,
- buttonLink,
- noGitHubMetrics,
- sectionName,
- categoryName,
+ services,
+ subHeading,
+ buttonLink,
+ noGitHubMetrics,
+ sectionName,
+ categoryName,
} = Astro.props;
-
---
-
-
- {services && services.length > 0 ? (
-
- {services.map((service: Service) => (
-
-
-
-
-
-
-
+ {
+ services && services.length > 0 ? (
+
+ {services.map((service: Service) => (
+
+
+
+
+
+
-
-
-
-
- { service.securityAudited && (
-
- Security Audited
-
- )}
- { service.acceptsCrypto && (
-
- Crypto Payments Accepted
-
- )}
- { service.securityAudited === false && (
-
- No Security Audit
-
- )}
- { (service.openSource === false) && (
-
-
- Not Open Source
-
- )}
- { service.openSource || (service.github && service.openSource !== false) ? (
-
- Open Source
-
- ) : null }
- { service.github && !noGitHubMetrics &&
}
- { service.github && noGitHubMetrics && (
-
-
- {service.github}
-
-
- ) }
-
-
-
-
-
- ))}
-
- ) : (
-
- โ ๏ธ This section is still a work in progress โ ๏ธ
- Check back soon, or help us complete it by submiting a pull request on GitHub.
-
- Or submit an entry here
-
- )}
+
+ {subHeading ? {service.name} : {service.name} }
+
+ {service.followWith && (
+
({service.followWith})
+ )}
+
+ {formatLink(service.url)}
+
+
+
+
+
+
+ {service.securityAudited && (
+
+ Security
+ Audited
+
+ )}
+ {service.acceptsCrypto && (
+
+ Crypto Payments
+ Accepted
+
+ )}
+ {service.securityAudited === false && (
+
+ No Security
+ Audit
+
+ )}
+ {service.openSource === false && (
+
+
+ Not Open Source
+
+ )}
+ {service.openSource ||
+ (service.github && service.openSource !== false) ? (
+
+ Open Source
+
+ ) : null}
+ {service.github && !noGitHubMetrics && (
+
+ )}
+ {service.github && noGitHubMetrics && (
+
+
+ {service.github}
+
+
+ )}
+
+
+
+
+
+ ))}
+
+ ) : (
+
+ <>
+ โ ๏ธ This section is still a work in progress โ ๏ธ
+
+ >
+ Check back soon, or help us complete it by submiting a pull request on
+ GitHub.
+
+
+ Or submit an entry here
+
+
+ )
+ }
- {buttonLink && (
-
- )}
+ {
+ buttonLink && (
+
+ )
+ }
-
diff --git a/web/src/components/things/SmartSuggestions.svelte b/web/src/components/things/SmartSuggestions.svelte
index 8dc0609..3df98b1 100644
--- a/web/src/components/things/SmartSuggestions.svelte
+++ b/web/src/components/things/SmartSuggestions.svelte
@@ -1,7 +1,7 @@
+ src="https://no-track.as93.net/js/script.js">
- {breadcrumbs && (
-
- )}
- {customSchemaJson && (
-
- )}
-
+ {
+ breadcrumbs && (
+
+ )
+ }
+ {
+ customSchemaJson && (
+
+ )
+ }
+
- {!hideNav &&
}
+ {!hideNav &&
}
@@ -124,11 +152,9 @@ const makeSearchLd = () => {
-
+
diff --git a/web/src/pages/404.astro b/web/src/pages/404.astro
index 9d800ab..c9e61ce 100644
--- a/web/src/pages/404.astro
+++ b/web/src/pages/404.astro
@@ -1,98 +1,91 @@
---
-
import Layout from '@layouts/Layout.astro';
import Buton from '@components/form/Button.astro';
-
---
+
+ 404
+ Page not found ๐ข
+
+ It seems this page doesn't exist (yet). We're sorry about that.
+
+ Go back home
-
- 404
- Page not found ๐ข
-
- It seems this page doesn't exist (yet). We're sorry about that.
-
- Go back home
-
-
- Looking for somewhere else?
-
-
-
-
+
+ Looking for somewhere else?
+
+
+
-
diff --git a/web/src/pages/[...listing].astro b/web/src/pages/[...listing].astro
index ca0bdc9..f780da8 100644
--- a/web/src/pages/[...listing].astro
+++ b/web/src/pages/[...listing].astro
@@ -1,5 +1,4 @@
---
-
import Layout from '@layouts/Layout.astro';
import ServiceList from '@components/things/ServiceList.astro';
import Comments from '@components/things/Comments.svelte';
@@ -30,123 +29,132 @@ import Button from '@components/form/Button.astro';
import SaveListing from '@components/things/SaveListing.svelte';
interface Props extends Service {
- slug: string;
- parentSection: Section;
- categoryName: string;
+ slug: string;
+ parentSection: Section;
+ categoryName: string;
}
const {
- name,
- description,
- url,
- github,
- tosdrId,
- discordInvite,
- subreddit,
- iosApp,
- androidApp,
- icon,
- followWith,
- links,
- securityAudited,
- openSource,
- acceptsCrypto,
- parentSection,
- categoryName,
+ name,
+ description,
+ url,
+ github,
+ tosdrId,
+ discordInvite,
+ subreddit,
+ iosApp,
+ androidApp,
+ icon,
+ followWith,
+ links,
+ securityAudited,
+ openSource,
+ acceptsCrypto,
+ parentSection,
+ categoryName,
} = Astro.props;
-
/**
* Make a list of keywords, for the
tag
*/
const makeKeyWordTag = () => {
- const keywords: string[] = [];
- keywords.push(`open source ${parentSection.name}`);
- keywords.push(`${parentSection.name} comparison`);
- (parentSection.alternativeTo || []).forEach((alt: string) => {
- keywords.push(`${alt} alternative`);
- });
- (parentSection.services || []).forEach((serv: Service) => {
- keywords.push(`${serv.name} vs`);
- });
- keywords.push('private');
- keywords.push('ad free');
- keywords.push('encrypted');
- keywords.push('open source software');
- keywords.push('privacy respecting apps');
- keywords.push('free tools');
- return keywords.join(', ');
+ const keywords: string[] = [];
+ keywords.push(`open source ${parentSection.name}`);
+ keywords.push(`${parentSection.name} comparison`);
+ (parentSection.alternativeTo || []).forEach((alt: string) => {
+ keywords.push(`${alt} alternative`);
+ });
+ (parentSection.services || []).forEach((serv: Service) => {
+ keywords.push(`${serv.name} vs`);
+ });
+ keywords.push('private');
+ keywords.push('ad free');
+ keywords.push('encrypted');
+ keywords.push('open source software');
+ keywords.push('privacy respecting apps');
+ keywords.push('free tools');
+ return keywords.join(', ');
};
/**
* Make a page title
*/
const makePageTitle = () => {
- return `${name} | ${parentSection.name} | ${categoryName} | Awesome Privacy`;
+ return `${name} | ${parentSection.name} | ${categoryName} | Awesome Privacy`;
};
const makeBreadcrumbs = () => {
- return [
- { name: 'Home', item: '/' },
- { name: categoryName, item: slugify(categoryName) },
- { name: parentSection.name, item: `${slugify(categoryName)}/${slugify(parentSection.name)}` },
- { name: name, item: `${slugify(categoryName)}/${slugify(parentSection.name)}/${slugify(name)}` },
- ];
+ return [
+ { name: 'Home', item: '/' },
+ { name: categoryName, item: slugify(categoryName) },
+ {
+ name: parentSection.name,
+ item: `${slugify(categoryName)}/${slugify(parentSection.name)}`,
+ },
+ {
+ name: name,
+ item: `${slugify(categoryName)}/${slugify(parentSection.name)}/${slugify(name)}`,
+ },
+ ];
};
/**
* Make a string page intro, for the description tag
*/
const makeDescriptionTag = () => {
- let description = `Analysis of ${name}, and other privacy-respecting ${parentSection.name} `;
- if (parentSection.services) {
- const serviceList = parentSection.services.map((serv: Service) => serv.name);
- description += `Compare ${serviceList.join(' vs ')} more apps. `
- }
- if (parentSection.alternativeTo && parentSection.alternativeTo.length > 0) {
- description += `The best secure encrypted alternatives to ${parentSection.alternativeTo.join(', ')} in 2024. `;
- }
- description += 'All this, and many more free and awesome tools and software.'
- return description;
+ let description = `Analysis of ${name}, and other privacy-respecting ${parentSection.name} `;
+ if (parentSection.services) {
+ const serviceList = parentSection.services.map(
+ (serv: Service) => serv.name,
+ );
+ description += `Compare ${serviceList.join(' vs ')} more apps. `;
+ }
+ if (parentSection.alternativeTo && parentSection.alternativeTo.length > 0) {
+ description += `The best secure encrypted alternatives to ${parentSection.alternativeTo.join(', ')} in 2024. `;
+ }
+ description += 'All this, and many more free and awesome tools and software.';
+ return description;
};
// Return a list of Services, except for the currtent one
const filterServices = () => {
- return parentSection.services.filter((service: Service) => service.name !== name);
+ return parentSection.services.filter(
+ (service: Service) => service.name !== name,
+ );
};
export async function getStaticPaths() {
- const pages = await fetchData().then((data) => {
- const results: Array
= [];
- if (!data || !data.categories) return results;
- (data as AwesomePrivacy).categories.forEach((category) => {
- category.sections.forEach((section) => {
- const services = (section.services || []).map((service) => {
- return {
- slug: `${slugify(category.name)}/${slugify(section.name)}/${slugify(service.name)}`,
- parentSection: section,
- categoryName: category.name,
- ...service,
- };
- });
- results.push(...services);
- });
-
- });
- return results;
- });
+ const pages = await fetchData().then((data) => {
+ const results: Array = [];
+ if (!data || !data.categories) return results;
+ (data as AwesomePrivacy).categories.forEach((category) => {
+ category.sections.forEach((section) => {
+ const services = (section.services || []).map((service) => {
+ return {
+ slug: `${slugify(category.name)}/${slugify(section.name)}/${slugify(service.name)}`,
+ parentSection: section,
+ categoryName: category.name,
+ ...service,
+ };
+ });
+ results.push(...services);
+ });
+ });
+ return results;
+ });
- return pages.map((props: Props) => {
- return {params: { listing: props.slug }, props };
- });
+ return pages.map((props: Props) => {
+ return { params: { listing: props.slug }, props };
+ });
}
const makePaginationLinks = () => {
- const allServices = parentSection.services || [];
- const index = allServices.findIndex(item => item.name === name);
- const previousItem = index > 0 ? allServices[index - 1].name : null;
- const nextItem = index < allServices.length - 1 ? allServices[index + 1].name : null;
- return { previous: previousItem, next: nextItem };
+ const allServices = parentSection.services || [];
+ const index = allServices.findIndex((item) => item.name === name);
+ const previousItem = index > 0 ? allServices[index - 1].name : null;
+ const nextItem =
+ index < allServices.length - 1 ? allServices[index + 1].name : null;
+ return { previous: previousItem, next: nextItem };
};
const { previous, next } = makePaginationLinks();
@@ -158,565 +166,659 @@ const githubData = github ? await fetchGitHubStats(github) : null;
const privacyData = tosdrId ? await fetchTosdrPrivacy(tosdrId) : null;
const iosData = iosApp ? await fetchIosInfo(iosApp) : null;
const androidData = androidApp ? await fetchAndroidInfo(androidApp) : null;
-const discordData = discordInvite ? await fetchDiscordInfo(discordInvite) : null;
+const discordData = discordInvite
+ ? await fetchDiscordInfo(discordInvite)
+ : null;
const redditData = subreddit ? await fetchRedditInfo(subreddit) : null;
const dockerData = await fetchDockerData(name);
-const websiteData = (url && !ignoredSites.some(ignoredSite => url.includes(ignoredSite))) ? await fetchWebsiteInfo(url) : null;
+const websiteData =
+ url && !ignoredSites.some((ignoredSite) => url.includes(ignoredSite))
+ ? await fetchWebsiteInfo(url)
+ : null;
const findPrivacyPolicyLink = () => {
- if (!privacyData) return null;
- // const docs = privacyData?.parameters?.documents;
- // if (docs) {
- // const privacyPolicy = (docs || []).find((doc) => doc.name.toLowerCase().includes('privacy policy'));
- // if (privacyPolicy) return privacyPolicy.url;
- // }
- return `https://tosdr.org/en/service/${tosdrId}`;
+ if (!privacyData) return null;
+ // const docs = privacyData?.parameters?.documents;
+ // if (docs) {
+ // const privacyPolicy = (docs || []).find((doc) => doc.name.toLowerCase().includes('privacy policy'));
+ // if (privacyPolicy) return privacyPolicy.url;
+ // }
+ return `https://tosdr.org/en/service/${tosdrId}`;
};
const privacyPolicyLink = findPrivacyPolicyLink();
const getApiEndpoint = () => {
- return `https://api.awesome-privacy.xyz/${slugify(categoryName)}/${slugify(parentSection.name)}/${slugify(name)}`;
-}
-
+ return `https://api.awesome-privacy.xyz/${slugify(categoryName)}/${slugify(parentSection.name)}/${slugify(name)}`;
+};
---
-
-
-
-
-
-
-
-
-
-
-
-
- { securityAudited && (
-
- Security Audited
-
- )}
- { acceptsCrypto && (
-
- Crypto Payments Accepted
-
- )}
- { securityAudited === false && (
-
- No Security Audit
-
- )}
- { (openSource === false) && (
-
-
- Not Open Source
-
- )}
- { openSource || (github && openSource !== false) ? (
-
- Open Source
-
- ) : null }
-
-
- { privacyData && privacyData.parameters.points.length > 0 && (
-
- {name} Privacy Policy
-
-
- )}
- { github && githubData && (
-
- )}
- { websiteData && (
-
- )}
- { (androidData && !androidData.error) && (
-
- )}
- { iosData && typeof iosData === 'object' && Object.keys(iosData).length > 0 && (
-
- )}
- { dockerData && dockerData.found && (
-
- )}
- { (discordData || redditData) && (
-
- {name} Socials
-
- { discordData && }
- { redditData && }
-
-
- )}
+
+
+
+
+
+
+
+
+
+
+
+
+ {
+ securityAudited && (
+
+ Security Audited
+
+ )
+ }
+ {
+ acceptsCrypto && (
+
+ Crypto Payments Accepted
+
+ )
+ }
+ {
+ securityAudited === false && (
+
+ No Security Audit
+
+ )
+ }
+ {
+ openSource === false && (
+
+
+ Not Open Source
+
+ )
+ }
+ {
+ openSource || (github && openSource !== false) ? (
+
+ Open Source
+
+ ) : null
+ }
+
+
+ {
+ privacyData && privacyData.parameters.points.length > 0 && (
+
+ {name} Privacy Policy
+
+
+ )
+ }
+ {
+ github && githubData && (
+
+ )
+ }
+ {
+ websiteData && (
+
+ )
+ }
+ {
+ androidData && !androidData.error && (
+
+ )
+ }
+ {
+ iosData &&
+ typeof iosData === 'object' &&
+ Object.keys(iosData).length > 0 && (
+
+ )
+ }
+ {
+ dockerData && dockerData.found && (
+
+ )
+ }
+ {
+ (discordData || redditData) && (
+
+ {name} Socials
+
+ {discordData && }
+ {redditData && }
+
+
+ )
+ }
-
+
-
- More {parentSection.name}
-
-
-
- About the Data: {name}
-
- API
-
- You can access {name}'s data programmatically via our API.
- Simply make a GET request to:
-
- {getApiEndpoint()}
-
- The REST API is free, no-auth and CORS-enabled.
- To learn more, view the Swagger Docs or read the API Usage Guide .
-
+
+ More {parentSection.name}
+
+
+
+ About the Data: {name}
+
+ API
+
+ You can access {name}'s data programmatically via our API. Simply make a GET request to:
+
+ {getApiEndpoint()}
+
+ The REST API is free, no-auth and CORS-enabled. To learn more, view the Swagger Docs or read the API Usage Guide .
+
- About the Data
-
- Beyond the user-submitted YAML you see above, we also augment each listing with
- additional data dynamically fetched from several sources.
- To learn more about where the rest of data included in this page comes from,
- and how it is computed,
- see the About the Data section of our About page.
-
-
+ About the Data
+
+ Beyond the user-submitted YAML you see above, we also augment each
+ listing with additional data dynamically fetched from several sources.
+ To learn more about where the rest of data included in this page comes
+ from, and how it is computed, see the About the Data section of our About page.
+
+
-
- Share {name}
-
- Help your friends compare {parentSection.name}, and pick privacy-respecting software and services.
- Share {name} and Awesome Privacy with your network!
-
-
-
-
-
+
+ Share {name}
+
+ Help your friends compare {parentSection.name}, and pick
+ privacy-respecting software and services.
+ Share {name} and Awesome Privacy with your network!
+
+
+
+
+
-
-
+
+
diff --git a/web/src/pages/[category].astro b/web/src/pages/[category].astro
index c20c430..b080d1b 100644
--- a/web/src/pages/[category].astro
+++ b/web/src/pages/[category].astro
@@ -1,5 +1,4 @@
---
-
import Layout from '@layouts/Layout.astro';
import SectionList from '@components/things/SectionList.astro';
import Main from '@components/scafold/MainCard.astro';
@@ -29,129 +28,137 @@ export async function getStaticPaths() {
});
});
- return pages.map(({ category, title, sections }) => {
- return {
- params: { category },
- props: { title, sections },
- };
- });
+ return pages.map(({ category, title, sections }) => {
+ return {
+ params: { category },
+ props: { title, sections },
+ };
+ });
}
const makeCarasolData = () => {
- return {
- "@context": "https://schema.org",
- "@type": "ItemList",
- "itemListElement": [
- sections.map((section, index) => ({
- "@type": "ListItem",
- "position": index + 1,
- "url": `https://awesomeprivacy.com/${slugify(title)}/${slugify(section.name)}`,
- "item": {
- "@type": "Service",
- "name": section.name,
- "url": `https://awesomeprivacy.com/${slugify(title)}/${slugify(section.name)}`,
- }
- })),
- ]
- }
+ return {
+ '@context': 'https://schema.org',
+ '@type': 'ItemList',
+ itemListElement: [
+ sections.map((section, index) => ({
+ '@type': 'ListItem',
+ position: index + 1,
+ url: `https://awesomeprivacy.com/${slugify(title)}/${slugify(section.name)}`,
+ item: {
+ '@type': 'Service',
+ name: section.name,
+ url: `https://awesomeprivacy.com/${slugify(title)}/${slugify(section.name)}`,
+ },
+ })),
+ ],
+ };
};
const makeBreadcrumbs = () => {
- return [
- { name: 'Home', item: '/' },
- { name: title, item: slugify(title) },
- ];
+ return [
+ { name: 'Home', item: '/' },
+ { name: title, item: slugify(title) },
+ ];
};
-
const makePaginationLinks = () => {
- const index = categories.findIndex(category => category.name === title);
- const previousCategory = index > 0 ? categories[index - 1].name : null;
- const nextCategory = index < categories.length - 1 ? categories[index + 1].name : null;
- return { previous: previousCategory, next: nextCategory };
+ const index = categories.findIndex((category) => category.name === title);
+ const previousCategory = index > 0 ? categories[index - 1].name : null;
+ const nextCategory =
+ index < categories.length - 1 ? categories[index + 1].name : null;
+ return { previous: previousCategory, next: nextCategory };
};
const { previous, next } = makePaginationLinks();
-
---
-
+
-
+
- Or, Browse All {title}
+ Or, Browse All {title}
+ {
+ previous ? (
+
+ โ Previous
+ {previous}
+
+ ) : (
+
+ )
+ }
+ {
+ next && (
+
+ Next โ
+ {next}
+
+ )
+ }
+
diff --git a/web/src/pages/about.astro b/web/src/pages/about.astro
index d59d91b..8462c98 100644
--- a/web/src/pages/about.astro
+++ b/web/src/pages/about.astro
@@ -1,359 +1,478 @@
---
-
import Layout from '@layouts/Layout.astro';
import Main from '@components/scafold/MainCard.astro';
import Icon from '@components/form/Icon.astro';
import SocialShare from '@components/form/Social.astro';
import { parseMarkdown } from '@utils/parse-markdown';
-import { authorProjects, authorSocials, aboutOurData, projectRequirements, appDescription } from '../site-config';
+import {
+ authorProjects,
+ authorSocials,
+ aboutOurData,
+ projectRequirements,
+ appDescription,
+} from '../site-config';
-const contributorsResource = async () => {
- const url = 'https://api.github.com/repos/lissy93/personal-security-checklist/contributors?per_page=100';
- const response = await fetch(url);
- if (!response.ok) {
- throw new Error('Failed to fetch contributors');
- }
+interface GitHubContributor {
+ login: string;
+ avatar_url: string;
+ html_url: string;
+}
+
+interface Sponsor {
+ login: string;
+ avatarUrl: string;
+ name?: string;
+}
+
+const githubHeaders: Record