mirror of
https://github.com/streetwriters/notesnook.git
synced 2025-12-16 19:57:52 +01:00
global: introducing Notesnook Circle (#8779)
* core: add api for getting Notesnook Circle partners * web: introduce notesnook circle * core: export CirclePartner type * mobile: add notesnook-circle * web: use strings from intl for notesnook circle * common: add notesnook circle to features * web: fix local ips set for hosts * mobile: fix db hosts --------- Co-authored-by: Ammar Ahmed <ammarahmed6506@gmail.com>
This commit is contained in:
@@ -39,7 +39,7 @@ export async function setupDatabase(password?: string) {
|
|||||||
const key = await getDatabaseKey(password);
|
const key = await getDatabaseKey(password);
|
||||||
if (!key) throw new Error(strings.databaseSetupFailed());
|
if (!key) throw new Error(strings.databaseSetupFailed());
|
||||||
|
|
||||||
// const base = `http://192.168.100.88`;
|
// const base = `http://192.168.100.92`;
|
||||||
|
|
||||||
// database.host({
|
// database.host({
|
||||||
// API_HOST: `${base}:5264`,
|
// API_HOST: `${base}:5264`,
|
||||||
|
|||||||
@@ -44,7 +44,8 @@
|
|||||||
"@trpc/react-query": "^10.45.2",
|
"@trpc/react-query": "^10.45.2",
|
||||||
"@trpc/server": "^10.45.2",
|
"@trpc/server": "^10.45.2",
|
||||||
"@tanstack/react-query": "^4.36.1",
|
"@tanstack/react-query": "^4.36.1",
|
||||||
"async-mutex": "0.5.0"
|
"async-mutex": "0.5.0",
|
||||||
|
"react-async-hook": "^4.0.0"
|
||||||
},
|
},
|
||||||
"sideEffects": false
|
"sideEffects": false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ import { ServersConfiguration } from "./server-config";
|
|||||||
import SoundPicker from "./sound-picker";
|
import SoundPicker from "./sound-picker";
|
||||||
import ThemeSelector from "./theme-selector";
|
import ThemeSelector from "./theme-selector";
|
||||||
import { TitleFormat } from "./title-format";
|
import { TitleFormat } from "./title-format";
|
||||||
|
import { NotesnookCircle } from "./notesnook-circle";
|
||||||
|
|
||||||
export const components: { [name: string]: ReactElement } = {
|
export const components: { [name: string]: ReactElement } = {
|
||||||
homeselector: <HomePicker />,
|
homeselector: <HomePicker />,
|
||||||
@@ -69,5 +70,6 @@ export const components: { [name: string]: ReactElement } = {
|
|||||||
),
|
),
|
||||||
"sidebar-tab-selector": <SidebarTabPicker />,
|
"sidebar-tab-selector": <SidebarTabPicker />,
|
||||||
"change-password": <ChangePassword />,
|
"change-password": <ChangePassword />,
|
||||||
"change-email": <ChangeEmail />
|
"change-email": <ChangeEmail />,
|
||||||
|
"notesnook-circle": <NotesnookCircle />
|
||||||
};
|
};
|
||||||
|
|||||||
190
apps/mobile/app/screens/settings/notesnook-circle.tsx
Normal file
190
apps/mobile/app/screens/settings/notesnook-circle.tsx
Normal file
@@ -0,0 +1,190 @@
|
|||||||
|
import { CirclePartner, SubscriptionStatus } from "@notesnook/core";
|
||||||
|
import { strings } from "@notesnook/intl";
|
||||||
|
import { useThemeColors } from "@notesnook/theme";
|
||||||
|
import Clipboard from "@react-native-clipboard/clipboard";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import {
|
||||||
|
ActivityIndicator,
|
||||||
|
Image,
|
||||||
|
ScrollView,
|
||||||
|
TouchableOpacity,
|
||||||
|
View
|
||||||
|
} from "react-native";
|
||||||
|
import { db } from "../../common/database";
|
||||||
|
import AppIcon from "../../components/ui/AppIcon";
|
||||||
|
import { Button } from "../../components/ui/button";
|
||||||
|
import Heading from "../../components/ui/typography/heading";
|
||||||
|
import Paragraph from "../../components/ui/typography/paragraph";
|
||||||
|
import { ToastManager } from "../../services/event-manager";
|
||||||
|
import Navigation from "../../services/navigation";
|
||||||
|
import PremiumService from "../../services/premium";
|
||||||
|
import { useUserStore } from "../../stores/use-user-store";
|
||||||
|
import { AppFontSize, defaultBorderRadius } from "../../utils/size";
|
||||||
|
import { DefaultAppStyles } from "../../utils/styles";
|
||||||
|
import { useAsync } from "react-async-hook";
|
||||||
|
import { Notice } from "../../components/ui/notice";
|
||||||
|
|
||||||
|
export const NotesnookCircle = () => {
|
||||||
|
const user = useUserStore((state) => state.user);
|
||||||
|
const isOnTrial =
|
||||||
|
PremiumService.get() &&
|
||||||
|
user?.subscription?.status === SubscriptionStatus.TRIAL;
|
||||||
|
const isFree = !PremiumService.get();
|
||||||
|
const partners = useAsync(db.circle.partners, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ScrollView
|
||||||
|
contentContainerStyle={{
|
||||||
|
gap: DefaultAppStyles.GAP_VERTICAL,
|
||||||
|
paddingHorizontal: DefaultAppStyles.GAP,
|
||||||
|
paddingTop: DefaultAppStyles.GAP_VERTICAL
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{!isFree && !isOnTrial ? null : (
|
||||||
|
<View>
|
||||||
|
<Paragraph>
|
||||||
|
{isFree
|
||||||
|
? strings.freeUserCircleNotice()
|
||||||
|
: strings.trialUserCircleNotice()}
|
||||||
|
</Paragraph>
|
||||||
|
|
||||||
|
{!isOnTrial ? null : (
|
||||||
|
<Button
|
||||||
|
title={strings.upgradePlan()}
|
||||||
|
onPress={() => {
|
||||||
|
Navigation.navigate("PayWall", {
|
||||||
|
canGoBack: true,
|
||||||
|
context: useUserStore.getState().user
|
||||||
|
? "logged-in"
|
||||||
|
: "logged-out"
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
style={{
|
||||||
|
alignSelf: "flex-start",
|
||||||
|
paddingHorizontal: 0
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{partners.loading ? <ActivityIndicator /> : null}
|
||||||
|
|
||||||
|
{partners.error ? (
|
||||||
|
<Notice type="alert" text={partners.error.message} />
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
{partners.result?.map((item) => (
|
||||||
|
<Partner item={item} available={!isFree && !isOnTrial} />
|
||||||
|
))}
|
||||||
|
</ScrollView>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const Partner = ({
|
||||||
|
item,
|
||||||
|
available
|
||||||
|
}: {
|
||||||
|
item: CirclePartner;
|
||||||
|
available: boolean;
|
||||||
|
}) => {
|
||||||
|
const { colors } = useThemeColors();
|
||||||
|
const [code, setCode] = useState<string>();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
borderRadius: defaultBorderRadius,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: colors.primary.border,
|
||||||
|
padding: DefaultAppStyles.GAP,
|
||||||
|
gap: DefaultAppStyles.GAP_VERTICAL
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
flexDirection: "row",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
alignItems: "center"
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Heading>{item.name}</Heading>
|
||||||
|
<Image
|
||||||
|
src={item.logoBase64}
|
||||||
|
style={{
|
||||||
|
width: 40,
|
||||||
|
height: 40
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<Paragraph style={{ textAlign: "justify" }}>
|
||||||
|
{item.longDescription.trim()}
|
||||||
|
</Paragraph>
|
||||||
|
|
||||||
|
<Paragraph
|
||||||
|
style={{
|
||||||
|
alignSelf: "center"
|
||||||
|
}}
|
||||||
|
color={colors.primary.accent}
|
||||||
|
>
|
||||||
|
{item.offerDescription}
|
||||||
|
</Paragraph>
|
||||||
|
|
||||||
|
{available ? (
|
||||||
|
<>
|
||||||
|
{!code ? (
|
||||||
|
<Button
|
||||||
|
type="accent"
|
||||||
|
title={strings.redeemCode()}
|
||||||
|
width="100%"
|
||||||
|
onPress={() => {
|
||||||
|
if (!PremiumService.get()) {
|
||||||
|
Navigation.navigate("PayWall", {
|
||||||
|
canGoBack: true,
|
||||||
|
context: useUserStore.getState().user
|
||||||
|
? "logged-in"
|
||||||
|
: "logged-out"
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
db.circle
|
||||||
|
.redeem(item.id)
|
||||||
|
.then((result) => setCode(result?.code))
|
||||||
|
.catch((e) => ToastManager.error(e));
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<TouchableOpacity
|
||||||
|
style={{
|
||||||
|
backgroundColor: colors.secondary.background,
|
||||||
|
borderRadius: defaultBorderRadius,
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
padding: DefaultAppStyles.GAP_SMALL,
|
||||||
|
borderWidth: 0.5,
|
||||||
|
borderColor: colors.secondary.border,
|
||||||
|
flexDirection: "row",
|
||||||
|
gap: DefaultAppStyles.GAP_SMALL
|
||||||
|
}}
|
||||||
|
activeOpacity={0.9}
|
||||||
|
onPress={() => {
|
||||||
|
Clipboard.setString(code);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Paragraph
|
||||||
|
size={AppFontSize.lg}
|
||||||
|
color={colors.secondary.paragraph}
|
||||||
|
>
|
||||||
|
{code}
|
||||||
|
</Paragraph>
|
||||||
|
|
||||||
|
<AppIcon name="content-copy" />
|
||||||
|
</TouchableOpacity>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -615,6 +615,14 @@ export const settingsGroups: SettingSection[] = [
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "notesnook-circle",
|
||||||
|
name: strings.notesnookCircle(),
|
||||||
|
icon: "circle-outline",
|
||||||
|
type: "screen",
|
||||||
|
description: strings.notesnookCircleDesc(),
|
||||||
|
component: "notesnook-circle"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
342
apps/mobile/package-lock.json
generated
342
apps/mobile/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -56,4 +56,4 @@
|
|||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-native": "0.77.2"
|
"react-native": "0.77.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ const EXTRA_ICON_NAMES = [
|
|||||||
"chevron-down",
|
"chevron-down",
|
||||||
"calendar",
|
"calendar",
|
||||||
"minus-circle-outline",
|
"minus-circle-outline",
|
||||||
|
"circle-outline",
|
||||||
"close-circle-outline",
|
"close-circle-outline",
|
||||||
"qrcode",
|
"qrcode",
|
||||||
"text",
|
"text",
|
||||||
|
|||||||
@@ -53,8 +53,8 @@ async function initializeDatabase(persistence: DatabasePersistence) {
|
|||||||
AUTH_HOST: "https://auth.streetwriters.co",
|
AUTH_HOST: "https://auth.streetwriters.co",
|
||||||
SSE_HOST: "https://events.streetwriters.co",
|
SSE_HOST: "https://events.streetwriters.co",
|
||||||
ISSUES_HOST: "https://issues.streetwriters.co",
|
ISSUES_HOST: "https://issues.streetwriters.co",
|
||||||
MONOGRAPH_HOST: "https://monogr.ph",
|
|
||||||
SUBSCRIPTIONS_HOST: "https://subscriptions.streetwriters.co",
|
SUBSCRIPTIONS_HOST: "https://subscriptions.streetwriters.co",
|
||||||
|
MONOGRAPH_HOST: "https://monogr.ph",
|
||||||
NOTESNOOK_HOST: "https://notesnook.com",
|
NOTESNOOK_HOST: "https://notesnook.com",
|
||||||
...Config.get("serverUrls", {})
|
...Config.get("serverUrls", {})
|
||||||
});
|
});
|
||||||
|
|||||||
173
apps/web/src/dialogs/settings/components/circle-partners.tsx
Normal file
173
apps/web/src/dialogs/settings/components/circle-partners.tsx
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
/*
|
||||||
|
This file is part of the Notesnook project (https://notesnook.com/)
|
||||||
|
|
||||||
|
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { useState } from "react";
|
||||||
|
import { Copy, Loading } from "../../../components/icons";
|
||||||
|
import { Button, Link, Flex, Text, Grid } from "@theme-ui/components";
|
||||||
|
import { usePromise } from "@notesnook/common";
|
||||||
|
import { db } from "../../../common/db";
|
||||||
|
import { writeToClipboard } from "../../../utils/clipboard";
|
||||||
|
import { showToast } from "../../../utils/toast";
|
||||||
|
import { ErrorText } from "../../../components/error-text";
|
||||||
|
import { useStore as useUserStore } from "../../../stores/user-store";
|
||||||
|
import { SubscriptionPlan, SubscriptionStatus } from "@notesnook/core";
|
||||||
|
import { BuyDialog } from "../../buy-dialog";
|
||||||
|
import { strings } from "@notesnook/intl";
|
||||||
|
|
||||||
|
export function CirclePartners() {
|
||||||
|
const partners = usePromise(() => db.circle.partners(), []);
|
||||||
|
const [redeemedCode, setRedeemedCode] = useState<{
|
||||||
|
partnerId: string;
|
||||||
|
code: string;
|
||||||
|
}>();
|
||||||
|
const subscription = useUserStore((store) => store.user?.subscription);
|
||||||
|
|
||||||
|
const isFree =
|
||||||
|
!subscription ||
|
||||||
|
subscription?.plan === SubscriptionPlan.FREE ||
|
||||||
|
subscription?.status === SubscriptionStatus.EXPIRED;
|
||||||
|
const isTrial = subscription?.status === SubscriptionStatus.TRIAL;
|
||||||
|
const canRedeem = !isFree && !isTrial;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{partners.status === "pending" ? (
|
||||||
|
<Loading sx={{ mt: 2 }} />
|
||||||
|
) : partners.status === "rejected" ? (
|
||||||
|
<ErrorText error={partners.reason} />
|
||||||
|
) : (
|
||||||
|
<Grid columns={2} sx={{ mt: 2 }}>
|
||||||
|
{partners.value?.map((partner) => (
|
||||||
|
<Flex
|
||||||
|
key={partner.id}
|
||||||
|
sx={{
|
||||||
|
flexDirection: "column",
|
||||||
|
border: "1px solid var(--border)",
|
||||||
|
borderRadius: "dialog",
|
||||||
|
padding: 2,
|
||||||
|
gap: 1
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Flex
|
||||||
|
sx={{ justifyContent: "space-between", alignItems: "center" }}
|
||||||
|
>
|
||||||
|
<Text variant="title">{partner.name}</Text>
|
||||||
|
<img
|
||||||
|
src={partner.logoBase64}
|
||||||
|
alt={partner.name}
|
||||||
|
style={{ width: 30, height: "auto", borderRadius: 8 }}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
<Text variant="body" sx={{ whiteSpace: "pre-wrap" }}>
|
||||||
|
{partner.shortDescription}
|
||||||
|
</Text>
|
||||||
|
<Text
|
||||||
|
variant="subBody"
|
||||||
|
sx={{
|
||||||
|
fontStyle: "italic",
|
||||||
|
color: "accent",
|
||||||
|
textAlign: "center"
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{partner.offerDescription}
|
||||||
|
</Text>
|
||||||
|
{redeemedCode?.partnerId === partner.id ? (
|
||||||
|
<>
|
||||||
|
<Flex
|
||||||
|
sx={{
|
||||||
|
bg: "background-secondary",
|
||||||
|
border: "1px solid var(--border)",
|
||||||
|
borderRadius: "default",
|
||||||
|
alignSelf: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
gap: 1,
|
||||||
|
p: "small"
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Text
|
||||||
|
variant="body"
|
||||||
|
className="selectable"
|
||||||
|
sx={{
|
||||||
|
fontFamily: "monospace",
|
||||||
|
fontSize: 14
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{redeemedCode.code}
|
||||||
|
</Text>
|
||||||
|
<Button
|
||||||
|
variant="icon"
|
||||||
|
onClick={() =>
|
||||||
|
writeToClipboard({ "text/plain": redeemedCode.code })
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Copy size={12} />
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
|
{partner.codeRedeemUrl ? (
|
||||||
|
<Text variant="subBody" sx={{ textAlign: "center" }}>
|
||||||
|
<Link
|
||||||
|
target="_blank"
|
||||||
|
href={partner.codeRedeemUrl.replace(
|
||||||
|
"{{code}}",
|
||||||
|
redeemedCode.code
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
Click here
|
||||||
|
</Link>{" "}
|
||||||
|
to directly claim the promotion.
|
||||||
|
</Text>
|
||||||
|
) : null}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<Button
|
||||||
|
variant="accent"
|
||||||
|
sx={{ mt: 1 }}
|
||||||
|
onClick={async () => {
|
||||||
|
if (isFree) return BuyDialog.show({});
|
||||||
|
if (isTrial) {
|
||||||
|
return showToast(
|
||||||
|
"error",
|
||||||
|
strings.trialUserCircleNotice()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (!canRedeem) return;
|
||||||
|
const result = await db.circle
|
||||||
|
.redeem(partner.id)
|
||||||
|
.catch((e) => {
|
||||||
|
showToast("error", e.message);
|
||||||
|
});
|
||||||
|
if (result?.code) {
|
||||||
|
setRedeemedCode({
|
||||||
|
partnerId: partner.id,
|
||||||
|
code: result.code
|
||||||
|
});
|
||||||
|
showToast("success", "Code redeemed successfully");
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{isFree ? strings.upgradeToRedeem() : strings.redeemCode()}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</Flex>
|
||||||
|
))}
|
||||||
|
</Grid>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -39,7 +39,8 @@ import {
|
|||||||
Servers,
|
Servers,
|
||||||
ShieldLock,
|
ShieldLock,
|
||||||
Sync,
|
Sync,
|
||||||
Inbox
|
Inbox,
|
||||||
|
CircleEmpty
|
||||||
} from "../../components/icons";
|
} from "../../components/icons";
|
||||||
import NavigationItem from "../../components/navigation-menu/navigation-item";
|
import NavigationItem from "../../components/navigation-menu/navigation-item";
|
||||||
import { FlexScrollContainer } from "../../components/scroll-container";
|
import { FlexScrollContainer } from "../../components/scroll-container";
|
||||||
@@ -80,6 +81,7 @@ import { strings } from "@notesnook/intl";
|
|||||||
import { mdToHtml } from "../../utils/md";
|
import { mdToHtml } from "../../utils/md";
|
||||||
import { InboxSettings } from "./inbox-settings";
|
import { InboxSettings } from "./inbox-settings";
|
||||||
import { withFeatureCheck } from "../../common";
|
import { withFeatureCheck } from "../../common";
|
||||||
|
import { NotesnookCircleSettings } from "./notesnook-circle-settings";
|
||||||
|
|
||||||
type SettingsDialogProps = BaseDialogProps<false> & {
|
type SettingsDialogProps = BaseDialogProps<false> & {
|
||||||
activeSection?: SectionKeys;
|
activeSection?: SectionKeys;
|
||||||
@@ -109,6 +111,12 @@ const sectionGroups: SectionGroup[] = [
|
|||||||
icon: Sync,
|
icon: Sync,
|
||||||
isHidden: () => !useUserStore.getState().isLoggedIn
|
isHidden: () => !useUserStore.getState().isLoggedIn
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: "circle",
|
||||||
|
title: "Notesnook Circle",
|
||||||
|
icon: CircleEmpty,
|
||||||
|
isHidden: () => !useUserStore.getState().isLoggedIn
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: "inbox",
|
key: "inbox",
|
||||||
title: "Inbox",
|
title: "Inbox",
|
||||||
@@ -185,7 +193,8 @@ const SettingsGroups = [
|
|||||||
...AboutSettings,
|
...AboutSettings,
|
||||||
...SubscriptionSettings,
|
...SubscriptionSettings,
|
||||||
...ServersSettings,
|
...ServersSettings,
|
||||||
...InboxSettings
|
...InboxSettings,
|
||||||
|
...NotesnookCircleSettings
|
||||||
];
|
];
|
||||||
|
|
||||||
// Thoughts:
|
// Thoughts:
|
||||||
|
|||||||
43
apps/web/src/dialogs/settings/notesnook-circle-settings.ts
Normal file
43
apps/web/src/dialogs/settings/notesnook-circle-settings.ts
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
This file is part of the Notesnook project (https://notesnook.com/)
|
||||||
|
|
||||||
|
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { strings } from "@notesnook/intl";
|
||||||
|
import { CirclePartners } from "./components/circle-partners";
|
||||||
|
import { SettingsGroup } from "./types";
|
||||||
|
|
||||||
|
export const NotesnookCircleSettings: SettingsGroup[] = [
|
||||||
|
{
|
||||||
|
header: strings.notesnookCircle(),
|
||||||
|
key: "notesnook-circle",
|
||||||
|
section: "circle",
|
||||||
|
settings: [
|
||||||
|
{
|
||||||
|
key: "partners",
|
||||||
|
title: "",
|
||||||
|
description: strings.notesnookCircleDesc(),
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
type: "custom",
|
||||||
|
component: CirclePartners
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
@@ -41,7 +41,8 @@ export type SectionKeys =
|
|||||||
| "legal"
|
| "legal"
|
||||||
| "developer"
|
| "developer"
|
||||||
| "about"
|
| "about"
|
||||||
| "inbox";
|
| "inbox"
|
||||||
|
| "circle";
|
||||||
|
|
||||||
export type SectionGroupKeys =
|
export type SectionGroupKeys =
|
||||||
| "account"
|
| "account"
|
||||||
|
|||||||
@@ -447,6 +447,17 @@ const features = {
|
|||||||
believer: createLimit(true),
|
believer: createLimit(true),
|
||||||
legacyPro: createLimit(true)
|
legacyPro: createLimit(true)
|
||||||
}
|
}
|
||||||
|
}),
|
||||||
|
notesnookCircle: createFeature({
|
||||||
|
id: "notesnookCircle",
|
||||||
|
title: "Notesnook Circle",
|
||||||
|
availability: {
|
||||||
|
free: createLimit(false),
|
||||||
|
essential: createLimit(true),
|
||||||
|
pro: createLimit(true),
|
||||||
|
believer: createLimit(true),
|
||||||
|
legacyPro: createLimit(true)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
48
packages/core/src/api/circle.ts
Normal file
48
packages/core/src/api/circle.ts
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
This file is part of the Notesnook project (https://notesnook.com/)
|
||||||
|
|
||||||
|
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import hosts from "../utils/constants.js";
|
||||||
|
import http from "../utils/http.js";
|
||||||
|
import Database from "./index.js";
|
||||||
|
|
||||||
|
export type CirclePartner = {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
url: string;
|
||||||
|
logoBase64: string;
|
||||||
|
shortDescription: string;
|
||||||
|
longDescription: string;
|
||||||
|
offerDescription: string;
|
||||||
|
codeRedeemUrl?: string;
|
||||||
|
};
|
||||||
|
export class Circle {
|
||||||
|
constructor(private readonly db: Database) {}
|
||||||
|
|
||||||
|
partners(): Promise<CirclePartner[] | undefined> {
|
||||||
|
return http.get(`${hosts.SUBSCRIPTIONS_HOST}/circle/partners`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async redeem(partnerId: string): Promise<{ code?: string } | undefined> {
|
||||||
|
const token = await this.db.tokenManager.getAccessToken();
|
||||||
|
return http.get(
|
||||||
|
`${hosts.SUBSCRIPTIONS_HOST}/circle/redeem?partnerId=${partnerId}`,
|
||||||
|
token
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -82,6 +82,7 @@ import { NNMigrationProvider } from "../database/migrations.js";
|
|||||||
import { ConfigStorage } from "../database/config.js";
|
import { ConfigStorage } from "../database/config.js";
|
||||||
import { LazyPromise } from "../utils/lazy-promise.js";
|
import { LazyPromise } from "../utils/lazy-promise.js";
|
||||||
import { InboxApiKeys } from "./inbox-api-keys.js";
|
import { InboxApiKeys } from "./inbox-api-keys.js";
|
||||||
|
import { Circle } from "./circle.js";
|
||||||
|
|
||||||
type EventSourceConstructor = new (
|
type EventSourceConstructor = new (
|
||||||
uri: string,
|
uri: string,
|
||||||
@@ -192,6 +193,7 @@ class Database {
|
|||||||
tokenManager = new TokenManager(this.kv);
|
tokenManager = new TokenManager(this.kv);
|
||||||
mfa = new MFAManager(this.tokenManager);
|
mfa = new MFAManager(this.tokenManager);
|
||||||
subscriptions = new Subscriptions(this);
|
subscriptions = new Subscriptions(this);
|
||||||
|
circle = new Circle(this);
|
||||||
offers = Offers;
|
offers = Offers;
|
||||||
debug = new Debug();
|
debug = new Debug();
|
||||||
pricing = Pricing;
|
pricing = Pricing;
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ export * from "./api/debug.js";
|
|||||||
export * from "./api/monographs.js";
|
export * from "./api/monographs.js";
|
||||||
export * from "./api/subscriptions.js";
|
export * from "./api/subscriptions.js";
|
||||||
export * from "./api/pricing.js";
|
export * from "./api/pricing.js";
|
||||||
|
export * from "./api/circle.js";
|
||||||
export { VAULT_ERRORS } from "./api/vault.js";
|
export { VAULT_ERRORS } from "./api/vault.js";
|
||||||
export type { SyncOptions } from "./api/sync/index.js";
|
export type { SyncOptions } from "./api/sync/index.js";
|
||||||
export { sanitizeTag } from "./collections/tags.js";
|
export { sanitizeTag } from "./collections/tags.js";
|
||||||
|
|||||||
@@ -4217,6 +4217,14 @@ msgstr "notes imported"
|
|||||||
msgid "Notesnook"
|
msgid "Notesnook"
|
||||||
msgstr "Notesnook"
|
msgstr "Notesnook"
|
||||||
|
|
||||||
|
#: src/strings.ts:2596
|
||||||
|
msgid "Notesnook Circle"
|
||||||
|
msgstr "Notesnook Circle"
|
||||||
|
|
||||||
|
#: src/strings.ts:2598
|
||||||
|
msgid "Notesnook Circle brings together trusted partners who share our commitment to privacy, transparency, and user freedom."
|
||||||
|
msgstr "Notesnook Circle brings together trusted partners who share our commitment to privacy, transparency, and user freedom."
|
||||||
|
|
||||||
#: src/strings.ts:2066
|
#: src/strings.ts:2066
|
||||||
msgid "Notesnook encrypts everything offline before syncing to your other devices. This means that no one can read your notes except you. Not even us."
|
msgid "Notesnook encrypts everything offline before syncing to your other devices. This means that no one can read your notes except you. Not even us."
|
||||||
msgstr "Notesnook encrypts everything offline before syncing to your other devices. This means that no one can read your notes except you. Not even us."
|
msgstr "Notesnook encrypts everything offline before syncing to your other devices. This means that no one can read your notes except you. Not even us."
|
||||||
@@ -5006,6 +5014,10 @@ msgstr "Recovery successful!"
|
|||||||
msgid "Redeem"
|
msgid "Redeem"
|
||||||
msgstr "Redeem"
|
msgstr "Redeem"
|
||||||
|
|
||||||
|
#: src/strings.ts:2595
|
||||||
|
msgid "Redeem code"
|
||||||
|
msgstr "Redeem code"
|
||||||
|
|
||||||
#: src/strings.ts:2431
|
#: src/strings.ts:2431
|
||||||
msgid "Redeem gift code"
|
msgid "Redeem gift code"
|
||||||
msgstr "Redeem gift code"
|
msgstr "Redeem gift code"
|
||||||
@@ -6367,6 +6379,10 @@ msgstr "The {title} at {url} is not compatible with this client."
|
|||||||
msgid "The information above will be publically available at"
|
msgid "The information above will be publically available at"
|
||||||
msgstr "The information above will be publically available at"
|
msgstr "The information above will be publically available at"
|
||||||
|
|
||||||
|
#: src/strings.ts:2602
|
||||||
|
msgid "The Notesnook Circle is exclusive to subscribers. Please consider subscribing to gain access to Notesnook Circle and enjoy additional benefits."
|
||||||
|
msgstr "The Notesnook Circle is exclusive to subscribers. Please consider subscribing to gain access to Notesnook Circle and enjoy additional benefits."
|
||||||
|
|
||||||
#: src/strings.ts:2081
|
#: src/strings.ts:2081
|
||||||
msgid "The password/pin for unlocking the app."
|
msgid "The password/pin for unlocking the app."
|
||||||
msgstr "The password/pin for unlocking the app."
|
msgstr "The password/pin for unlocking the app."
|
||||||
@@ -6764,6 +6780,10 @@ msgstr "Upgrade to Notesnook Pro to create more tags."
|
|||||||
msgid "Upgrade to Pro"
|
msgid "Upgrade to Pro"
|
||||||
msgstr "Upgrade to Pro"
|
msgstr "Upgrade to Pro"
|
||||||
|
|
||||||
|
#: src/strings.ts:2594
|
||||||
|
msgid "Upgrade to redeem"
|
||||||
|
msgstr "Upgrade to redeem"
|
||||||
|
|
||||||
#: src/strings.ts:509
|
#: src/strings.ts:509
|
||||||
msgid "Upload"
|
msgid "Upload"
|
||||||
msgstr "Upload"
|
msgstr "Upload"
|
||||||
|
|||||||
@@ -4197,6 +4197,14 @@ msgstr ""
|
|||||||
msgid "Notesnook"
|
msgid "Notesnook"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/strings.ts:2596
|
||||||
|
msgid "Notesnook Circle"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/strings.ts:2598
|
||||||
|
msgid "Notesnook Circle brings together trusted partners who share our commitment to privacy, transparency, and user freedom."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/strings.ts:2066
|
#: src/strings.ts:2066
|
||||||
msgid "Notesnook encrypts everything offline before syncing to your other devices. This means that no one can read your notes except you. Not even us."
|
msgid "Notesnook encrypts everything offline before syncing to your other devices. This means that no one can read your notes except you. Not even us."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -4980,6 +4988,10 @@ msgstr ""
|
|||||||
msgid "Redeem"
|
msgid "Redeem"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/strings.ts:2595
|
||||||
|
msgid "Redeem code"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/strings.ts:2431
|
#: src/strings.ts:2431
|
||||||
msgid "Redeem gift code"
|
msgid "Redeem gift code"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -6326,6 +6338,10 @@ msgstr ""
|
|||||||
msgid "The information above will be publically available at"
|
msgid "The information above will be publically available at"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/strings.ts:2602
|
||||||
|
msgid "The Notesnook Circle is exclusive to subscribers. Please consider subscribing to gain access to Notesnook Circle and enjoy additional benefits."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/strings.ts:2081
|
#: src/strings.ts:2081
|
||||||
msgid "The password/pin for unlocking the app."
|
msgid "The password/pin for unlocking the app."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -6723,6 +6739,10 @@ msgstr ""
|
|||||||
msgid "Upgrade to Pro"
|
msgid "Upgrade to Pro"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/strings.ts:2594
|
||||||
|
msgid "Upgrade to redeem"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: src/strings.ts:509
|
#: src/strings.ts:509
|
||||||
msgid "Upload"
|
msgid "Upload"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|||||||
@@ -2590,5 +2590,14 @@ Use this if changes from other devices are not appearing on this device. This wi
|
|||||||
t`You can change your subscription plan from the web app`,
|
t`You can change your subscription plan from the web app`,
|
||||||
announcement: () => t`ANNOUNCEMENT`,
|
announcement: () => t`ANNOUNCEMENT`,
|
||||||
cannotChangePlan: () =>
|
cannotChangePlan: () =>
|
||||||
t`Your current subscription does not allow changing plans`
|
t`Your current subscription does not allow changing plans`,
|
||||||
|
upgradeToRedeem: () => t`Upgrade to redeem`,
|
||||||
|
redeemCode: () => t`Redeem code`,
|
||||||
|
notesnookCircle: () => t`Notesnook Circle`,
|
||||||
|
notesnookCircleDesc: () =>
|
||||||
|
t`Notesnook Circle brings together trusted partners who share our commitment to privacy, transparency, and user freedom.`,
|
||||||
|
trialUserCircleNotice: () =>
|
||||||
|
`Notesnook Circle is reserved for members with an active subscription. You'll get full access after your trial period is over and your subscription is confirmed.`,
|
||||||
|
freeUserCircleNotice: () =>
|
||||||
|
t`The Notesnook Circle is exclusive to subscribers. Please consider subscribing to gain access to Notesnook Circle and enjoy additional benefits.`
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ await Promise.all([
|
|||||||
IS_WATCH ? "--watch" : ""
|
IS_WATCH ? "--watch" : ""
|
||||||
),
|
),
|
||||||
cmd(
|
cmd(
|
||||||
TSCGO,
|
TSC,
|
||||||
"--emitDeclarationOnly",
|
"--emitDeclarationOnly",
|
||||||
"--outDir",
|
"--outDir",
|
||||||
"dist/types",
|
"dist/types",
|
||||||
|
|||||||
Reference in New Issue
Block a user