mirror of
https://github.com/streetwriters/notesnook.git
synced 2025-12-16 03:37:55 +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);
|
||||
if (!key) throw new Error(strings.databaseSetupFailed());
|
||||
|
||||
// const base = `http://192.168.100.88`;
|
||||
// const base = `http://192.168.100.92`;
|
||||
|
||||
// database.host({
|
||||
// API_HOST: `${base}:5264`,
|
||||
|
||||
@@ -44,7 +44,8 @@
|
||||
"@trpc/react-query": "^10.45.2",
|
||||
"@trpc/server": "^10.45.2",
|
||||
"@tanstack/react-query": "^4.36.1",
|
||||
"async-mutex": "0.5.0"
|
||||
"async-mutex": "0.5.0",
|
||||
"react-async-hook": "^4.0.0"
|
||||
},
|
||||
"sideEffects": false
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ import { ServersConfiguration } from "./server-config";
|
||||
import SoundPicker from "./sound-picker";
|
||||
import ThemeSelector from "./theme-selector";
|
||||
import { TitleFormat } from "./title-format";
|
||||
import { NotesnookCircle } from "./notesnook-circle";
|
||||
|
||||
export const components: { [name: string]: ReactElement } = {
|
||||
homeselector: <HomePicker />,
|
||||
@@ -69,5 +70,6 @@ export const components: { [name: string]: ReactElement } = {
|
||||
),
|
||||
"sidebar-tab-selector": <SidebarTabPicker />,
|
||||
"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-native": "0.77.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,6 +70,7 @@ const EXTRA_ICON_NAMES = [
|
||||
"chevron-down",
|
||||
"calendar",
|
||||
"minus-circle-outline",
|
||||
"circle-outline",
|
||||
"close-circle-outline",
|
||||
"qrcode",
|
||||
"text",
|
||||
|
||||
@@ -53,8 +53,8 @@ async function initializeDatabase(persistence: DatabasePersistence) {
|
||||
AUTH_HOST: "https://auth.streetwriters.co",
|
||||
SSE_HOST: "https://events.streetwriters.co",
|
||||
ISSUES_HOST: "https://issues.streetwriters.co",
|
||||
MONOGRAPH_HOST: "https://monogr.ph",
|
||||
SUBSCRIPTIONS_HOST: "https://subscriptions.streetwriters.co",
|
||||
MONOGRAPH_HOST: "https://monogr.ph",
|
||||
NOTESNOOK_HOST: "https://notesnook.com",
|
||||
...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,
|
||||
ShieldLock,
|
||||
Sync,
|
||||
Inbox
|
||||
Inbox,
|
||||
CircleEmpty
|
||||
} from "../../components/icons";
|
||||
import NavigationItem from "../../components/navigation-menu/navigation-item";
|
||||
import { FlexScrollContainer } from "../../components/scroll-container";
|
||||
@@ -80,6 +81,7 @@ import { strings } from "@notesnook/intl";
|
||||
import { mdToHtml } from "../../utils/md";
|
||||
import { InboxSettings } from "./inbox-settings";
|
||||
import { withFeatureCheck } from "../../common";
|
||||
import { NotesnookCircleSettings } from "./notesnook-circle-settings";
|
||||
|
||||
type SettingsDialogProps = BaseDialogProps<false> & {
|
||||
activeSection?: SectionKeys;
|
||||
@@ -109,6 +111,12 @@ const sectionGroups: SectionGroup[] = [
|
||||
icon: Sync,
|
||||
isHidden: () => !useUserStore.getState().isLoggedIn
|
||||
},
|
||||
{
|
||||
key: "circle",
|
||||
title: "Notesnook Circle",
|
||||
icon: CircleEmpty,
|
||||
isHidden: () => !useUserStore.getState().isLoggedIn
|
||||
},
|
||||
{
|
||||
key: "inbox",
|
||||
title: "Inbox",
|
||||
@@ -185,7 +193,8 @@ const SettingsGroups = [
|
||||
...AboutSettings,
|
||||
...SubscriptionSettings,
|
||||
...ServersSettings,
|
||||
...InboxSettings
|
||||
...InboxSettings,
|
||||
...NotesnookCircleSettings
|
||||
];
|
||||
|
||||
// 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"
|
||||
| "developer"
|
||||
| "about"
|
||||
| "inbox";
|
||||
| "inbox"
|
||||
| "circle";
|
||||
|
||||
export type SectionGroupKeys =
|
||||
| "account"
|
||||
|
||||
@@ -447,6 +447,17 @@ const features = {
|
||||
believer: 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 { LazyPromise } from "../utils/lazy-promise.js";
|
||||
import { InboxApiKeys } from "./inbox-api-keys.js";
|
||||
import { Circle } from "./circle.js";
|
||||
|
||||
type EventSourceConstructor = new (
|
||||
uri: string,
|
||||
@@ -192,6 +193,7 @@ class Database {
|
||||
tokenManager = new TokenManager(this.kv);
|
||||
mfa = new MFAManager(this.tokenManager);
|
||||
subscriptions = new Subscriptions(this);
|
||||
circle = new Circle(this);
|
||||
offers = Offers;
|
||||
debug = new Debug();
|
||||
pricing = Pricing;
|
||||
|
||||
@@ -39,6 +39,7 @@ export * from "./api/debug.js";
|
||||
export * from "./api/monographs.js";
|
||||
export * from "./api/subscriptions.js";
|
||||
export * from "./api/pricing.js";
|
||||
export * from "./api/circle.js";
|
||||
export { VAULT_ERRORS } from "./api/vault.js";
|
||||
export type { SyncOptions } from "./api/sync/index.js";
|
||||
export { sanitizeTag } from "./collections/tags.js";
|
||||
|
||||
@@ -4217,6 +4217,14 @@ msgstr "notes imported"
|
||||
msgid "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
|
||||
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."
|
||||
@@ -5006,6 +5014,10 @@ msgstr "Recovery successful!"
|
||||
msgid "Redeem"
|
||||
msgstr "Redeem"
|
||||
|
||||
#: src/strings.ts:2595
|
||||
msgid "Redeem code"
|
||||
msgstr "Redeem code"
|
||||
|
||||
#: src/strings.ts:2431
|
||||
msgid "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"
|
||||
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
|
||||
msgid "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"
|
||||
msgstr "Upgrade to Pro"
|
||||
|
||||
#: src/strings.ts:2594
|
||||
msgid "Upgrade to redeem"
|
||||
msgstr "Upgrade to redeem"
|
||||
|
||||
#: src/strings.ts:509
|
||||
msgid "Upload"
|
||||
msgstr "Upload"
|
||||
|
||||
@@ -4197,6 +4197,14 @@ msgstr ""
|
||||
msgid "Notesnook"
|
||||
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
|
||||
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 ""
|
||||
@@ -4980,6 +4988,10 @@ msgstr ""
|
||||
msgid "Redeem"
|
||||
msgstr ""
|
||||
|
||||
#: src/strings.ts:2595
|
||||
msgid "Redeem code"
|
||||
msgstr ""
|
||||
|
||||
#: src/strings.ts:2431
|
||||
msgid "Redeem gift code"
|
||||
msgstr ""
|
||||
@@ -6326,6 +6338,10 @@ msgstr ""
|
||||
msgid "The information above will be publically available at"
|
||||
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
|
||||
msgid "The password/pin for unlocking the app."
|
||||
msgstr ""
|
||||
@@ -6723,6 +6739,10 @@ msgstr ""
|
||||
msgid "Upgrade to Pro"
|
||||
msgstr ""
|
||||
|
||||
#: src/strings.ts:2594
|
||||
msgid "Upgrade to redeem"
|
||||
msgstr ""
|
||||
|
||||
#: src/strings.ts:509
|
||||
msgid "Upload"
|
||||
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`,
|
||||
announcement: () => t`ANNOUNCEMENT`,
|
||||
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" : ""
|
||||
),
|
||||
cmd(
|
||||
TSCGO,
|
||||
TSC,
|
||||
"--emitDeclarationOnly",
|
||||
"--outDir",
|
||||
"dist/types",
|
||||
|
||||
Reference in New Issue
Block a user