Compare commits

...

44 Commits

Author SHA1 Message Date
Ammar Ahmed
1432553897 mobile: fix editor features not updated on login 2026-01-15 09:29:48 +05:00
Abdullah Atta
e635ace338 Merge pull request #9174 from streetwriters/fix/theme-server-crashing
Fix theme server crashing on fetching total installs
2026-01-08 01:03:18 +05:00
Abdullah Atta
e729086015 server(themes): bump version to 1.0.4 2026-01-08 01:02:25 +05:00
Abdullah Atta
3fbb7ce548 server(themes): fix crash when fetching install count 2026-01-08 01:01:59 +05:00
Abdullah Atta
cf71809f6c Merge pull request #9132 from streetwriters/fix/themes-not-instaling
Fix themes not installing
2026-01-03 14:40:18 +05:00
Abdullah Atta
3b7d0ddb36 server(themes): remove _test key suffix 2026-01-03 14:40:06 +05:00
Abdullah Atta
482fea91f3 server(themes): bump version to 1.0.3 2026-01-03 14:39:20 +05:00
Abdullah Atta
4bace10bab server(themes): make counter increment faster 2026-01-03 14:38:45 +05:00
Abdullah Atta
603b7da9e6 server(theme): update theme's install count immediately on install 2026-01-03 14:38:00 +05:00
Abdullah Atta
bf4d168c96 server(theme): sort themes by total installs 2026-01-03 14:37:19 +05:00
Abdullah Atta
756bb82fb7 server(themes): sync themes every 1 hour (roughly) 2026-01-03 14:36:41 +05:00
Abdullah Atta
81b1005b02 Merge pull request #9128 from streetwriters/fix/themes-server-fetch-perf
Fix themes server fetch perf
2026-01-02 13:04:40 +05:00
Abdullah Atta
7ba52d1358 server(themes): bump version to 1.0.2 2026-01-02 13:03:36 +05:00
Abdullah Atta
fcfb2c7e01 server(themes): fetch installs once on sync instead of on every fetch 2026-01-02 13:03:19 +05:00
Ammar Ahmed
ec9c40bd64 Merge pull request #9125 from streetwriters/fix-subscription-wording
mobile: fix wrong trial end date shown in settings
2026-01-01 12:53:04 +05:00
Ammar Ahmed
f0f7857355 mobile: fix wrong trial end date shown in settings 2026-01-01 12:51:05 +05:00
Ammar Ahmed
0aea1deea0 Merge pull request #9124 from streetwriters/fix-invalid-file-crash-on-restore
mobile: fix app crash when trying to restore an invalid file
2026-01-01 10:26:28 +05:00
Ammar Ahmed
f041b92360 mobile: fix app crash when trying to restore an invalid file 2026-01-01 10:15:54 +05:00
Abdullah Atta
64074030dd Merge pull request #9123 from streetwriters/fix/make-themes-host-configurable
Make themes server host configurable
2026-01-01 09:48:49 +05:00
Abdullah Atta
3387dda22b server(themes): bump version to 1.0.1 2026-01-01 09:46:50 +05:00
Abdullah Atta
534b810350 server(themes): make themes server port configurable 2026-01-01 09:46:25 +05:00
Abdullah Atta
93c07d6842 Merge pull request #9122 from streetwriters/fix/themes-server-ci
Fix themes server docker image build & publish
2025-12-31 12:09:19 +05:00
Abdullah Atta
5d9f18ed77 ci: set build context to root for themes server 2025-12-31 12:08:11 +05:00
Abdullah Atta
313d47d9ff Merge pull request #8108 from StLyn4/docker-themes-server
docker: theme server
2025-12-31 11:57:11 +05:00
Abdullah Atta
304a9f3218 ci: add workflow for publishing theme server 2025-12-31 11:56:15 +05:00
Abdullah Atta
a160725acb server(themes): use bun in production 2025-12-31 11:53:22 +05:00
Abdullah Atta
05535b50da server(themes): no need for default env 2025-12-31 11:38:57 +05:00
Abdullah Atta
70fdcf2b44 server(theme): add support for cloudflare kv for counter persistance 2025-12-31 11:08:27 +05:00
Ammar Ahmed
509b308419 Merge pull request #9119 from streetwriters/fix-navigation-unresponsive-ios
mobile: add simple tab bar implementation
2025-12-31 09:23:45 +05:00
Ammar Ahmed
9d9c3b7db5 mobile: add simple tab bar implementation
This fixes most bugs faced by users in side-menu on ios like swipe gesture working when it is disabled and navigation screen stuck on older ios versions.
2025-12-30 14:23:39 +05:00
Abdullah Atta
9659a12d70 Merge pull request #9117 from streetwriters/fix/monograph-ui
Fix monograph ui
2025-12-30 10:06:31 +05:00
Abdullah Atta
6abc25f58a monograph: bump version to 1.2.7 2025-12-30 10:05:51 +05:00
Abdullah Atta
a164ac67ce monograph: fix monograph ui 2025-12-30 10:04:27 +05:00
Ammar Ahmed
8435e69cb5 Merge pull request #9110 from streetwriters/mobile-release/3.3.12
Mobile release/3.3.12
2025-12-29 10:24:05 +05:00
Ammar Ahmed
1d6a33e3be mobile: update export options 2025-12-27 11:47:31 +05:00
Ammar Ahmed
3f4d78af7d mobile: update ios provisioning 2025-12-27 10:19:31 +05:00
Abdullah Atta
f94012d184 Merge pull request #9103 from streetwriters/fix/monograph-build
monograph: fix build
2025-12-27 00:40:06 +05:00
Abdullah Atta
ca73c0c2ce monograph: fix build 2025-12-27 00:39:13 +05:00
Abdullah Atta
742bf31038 Merge pull request #9102 from streetwriters/feat/monograph/secrets-support
Add support for reading secrets from file
2025-12-27 00:28:45 +05:00
Abdullah Atta
1369b64cb1 monograph: bump version to 1.2.6 2025-12-27 00:27:15 +05:00
Abdullah Atta
bc3488512f monograph: add support for reading secrets from file 2025-12-27 00:26:51 +05:00
Ammar Ahmed
51f87911e1 mobile: release 3.3.12 2025-12-26 14:37:49 +05:00
Ammar Ahmed
a6fcff4d86 Merge pull request #9099 from streetwriters/fix-file-errors
Fix bugs in file picker, pdf preview, backup restoration and vault unlock with biometrics
2025-12-26 11:23:39 +05:00
Vsevolod Volkov
c6d471cec9 docker: ability to build container with theme server 2025-11-26 19:24:04 +01:00
76 changed files with 1598 additions and 509 deletions

78
.github/workflows/themes.publish.yml vendored Normal file
View File

@@ -0,0 +1,78 @@
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
# GitHub recommends pinning actions to a commit SHA.
# To get a newer version, you will need to update the SHA.
# You can also reference a tag or branch, but the action may change without warning.
name: Publish @notesnook/themes-server
on: workflow_dispatch
jobs:
push_to_registry:
name: Push Docker image to Docker Hub
runs-on: ubuntu-22.04
permissions:
packages: write
contents: read
attestations: write
id-token: write
steps:
- name: Check out the repo
uses: actions/checkout@v4
- name: Collect package metadata
id: package_metadata
working-directory: ./servers/themes
run: |
echo ::set-output name=app_version::$(cat package.json | jq -r .version)
# Setup Buildx
- name: Docker Setup Buildx
uses: docker/setup-buildx-action@v3
with:
platforms: linux/amd64,linux/arm64
- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
ecr: auto
logout: true
# Pull previous image from docker hub to use it as cache to improve the image build time.
- name: docker pull cache image
continue-on-error: true
run: docker pull streetwriters/themes-server:latest
# Setup QEMU
# - name: Set up QEMU
# uses: docker/setup-qemu-action@v2
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: streetwriters/themes-server
- name: Build and push Docker image
id: push
uses: docker/build-push-action@v6
with:
context: .
file: servers/themes/Dockerfile
push: true
platforms: linux/amd64,linux/arm64
tags: streetwriters/themes-server:${{ steps.package_metadata.outputs.app_version }},streetwriters/themes-server:latest
cache-from: streetwriters/themes-server:latest
- name: Generate artifact attestation
uses: actions/attest-build-provenance@v1
with:
subject-name: index.docker.io/streetwriters/themes-server
subject-digest: ${{ steps.push.outputs.digest }}
push-to-registry: true

View File

@@ -125,7 +125,7 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
multiDexEnabled true
versionCode 3087
versionCode 3088
versionName getNpmVersion()
testBuildType System.getProperty('testBuildType', 'debug')
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'

View File

@@ -1,6 +1,3 @@
- Get an overview of your year with Notesnook with Wrapped 2025
- Cell selection and column resizing in tables
- Added support for uploading large files in background
- Bug fixes and minor improvements
Thank you for using Notesnook!

View File

@@ -21,15 +21,6 @@ import { strings } from "@notesnook/intl";
import { useThemeColors } from "@notesnook/theme";
import React from "react";
import { View } from "react-native";
import { SafeAreaView } from "react-native-safe-area-context";
import {
NavigationState,
Route,
SceneMap,
SceneRendererProps,
TabDescriptor,
TabView
} from "react-native-tab-view";
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import { db } from "../../common/database";
import { useGroupOptions } from "../../hooks/use-group-options";
@@ -59,7 +50,104 @@ import SettingsService from "../../services/settings";
import { isFeatureAvailable } from "@notesnook/common";
import PaywallSheet from "../sheets/paywall";
import useGlobalSafeAreaInsets from "../../hooks/use-global-safe-area-insets";
const renderScene = SceneMap({
/**
* Simple Tab View Implementation for the Side bar
*/
type SimpleRoute = {
key: string;
title?: string;
};
type SimpleNavigationState = {
index: number;
routes: SimpleRoute[];
};
type SimpleTabBarProps = {
navigationState: SimpleNavigationState;
jumpTo: (key: string) => void;
};
type SimpleTabViewProps = {
navigationState: SimpleNavigationState;
renderScene: ({ route }: { route: SimpleRoute }) => React.ReactNode;
renderTabBar?: (props: SimpleTabBarProps) => React.ReactNode;
onIndexChange?: (index: number) => void;
};
const createSceneMap = (
scenes: Record<string, React.ComponentType<any>>
): ((props: { route: SimpleRoute }) => React.ReactNode) => {
return ({ route }: { route: SimpleRoute }) => {
const SceneComponent = scenes[route.key];
if (!SceneComponent) return null;
return <SceneComponent />;
};
};
const SimpleTabView = ({
navigationState,
renderScene,
renderTabBar,
onIndexChange
}: SimpleTabViewProps) => {
const loadedKeysRef = React.useRef(new Set<string>());
const scenesRef = React.useRef(new Map<string, React.ReactNode>());
const jumpTo = React.useCallback(
(key: string) => {
const nextIndex = navigationState.routes.findIndex(
(route) => route.key === key
);
if (nextIndex !== -1 && nextIndex !== navigationState.index) {
onIndexChange?.(nextIndex);
}
},
[navigationState.index, navigationState.routes, onIndexChange]
);
const activeKey = navigationState.routes[navigationState.index]?.key;
if (activeKey && !loadedKeysRef.current.has(activeKey)) {
loadedKeysRef.current.add(activeKey);
}
const getSceneForRoute = React.useCallback(
(route: SimpleRoute) => {
const cached = scenesRef.current.get(route.key);
if (cached) return cached;
const created = renderScene({ route });
scenesRef.current.set(route.key, created);
return created;
},
[renderScene]
);
return (
<View style={{ flex: 1 }}>
<View style={{ flex: 1 }}>
{navigationState.routes.map((route, routeIndex) => (
<View
key={route.key}
style={{
flex: 1,
display: navigationState.index === routeIndex ? "flex" : "none"
}}
>
{loadedKeysRef.current.has(route.key)
? getSceneForRoute(route)
: navigationState.index === routeIndex
? getSceneForRoute(route)
: null}
</View>
))}
</View>
{renderTabBar ? renderTabBar({ navigationState, jumpTo }) : null}
</View>
);
};
const renderScene = createSceneMap({
home: SideMenuHome,
notebooks: SideMenuNotebooks,
tags: SideMenuTags,
@@ -73,7 +161,7 @@ export const SideMenu = React.memo(
const [index, setIndex] = React.useState(
SettingsService.getProperty("defaultSidebarTab")
);
const [routes] = React.useState<Route[]>([
const [routes] = React.useState<SimpleRoute[]>([
{
key: "home",
title: "Home"
@@ -98,15 +186,11 @@ export const SideMenu = React.memo(
paddingLeft: insets.left
}}
>
<TabView
<SimpleTabView
navigationState={{ index, routes }}
renderTabBar={(props) => <TabBar {...props} />}
tabBarPosition="bottom"
renderScene={renderScene}
onIndexChange={setIndex}
swipeEnabled={false}
animationEnabled={false}
lazy
/>
</View>
);
@@ -114,12 +198,7 @@ export const SideMenu = React.memo(
() => true
);
const TabBar = (
props: SceneRendererProps & {
navigationState: NavigationState<Route>;
options: Record<string, TabDescriptor<Route>> | undefined;
}
) => {
const TabBar = (props: SimpleTabBarProps) => {
const dragging = useSideBarDraggingStore((state) => state.dragging);
const { colors, isDark } = useThemeColors();
const groupOptions = useGroupOptions(

View File

@@ -85,6 +85,7 @@ export function SideMenuHome() {
keyboardDismissMode="interactive"
keyboardShouldPersistTaps="handled"
keyExtractor={() => "scroll-items"}
bounces={false}
renderItem={() => (
<>
<ReorderableList

View File

@@ -155,6 +155,7 @@ export const useEditorEvents = (
"outlineList",
"taskList"
]);
const deviceMode = useSettingStore((state) => state.deviceMode);
const fullscreen = useSettingStore((state) => state.fullscreen);
const corsProxy = useSettingStore((state) => state.settings.corsProxy);
@@ -244,7 +245,8 @@ export const useEditorEvents = (
loading,
fontScale,
markdownShortcuts,
loggedIn
loggedIn,
features
]);
const onBackPress = useCallback(async () => {

View File

@@ -95,6 +95,15 @@ const restoreBackup = async (options: {
deleteFile?: boolean;
}) => {
try {
if (
!options.uri.endsWith(".nnbackup") &&
!options.uri.endsWith(".nnbackupz")
) {
throw new Error(
`Invalid backup file selected. Only .nnbackup and .nnbackupz files can be restored.`
);
}
const isLegacyBackup = options.uri.endsWith(".nnbackup");
startProgress({

View File

@@ -143,21 +143,20 @@ export const settingsGroups: SettingSection[] = [
"dddd, MMMM D, YYYY h:mm A"
);
const trialEndDate = dayjs(user?.subscription?.start)
.add(
user?.subscription?.productId?.includes("monthly") ? 7 : 14,
"day"
)
.format("dddd, MMMM D, YYYY h:mm A");
if (
user.subscription?.plan !== SubscriptionPlan.FREE &&
user.subscription?.productId
) {
const status = user.subscription?.status;
return status === SubscriptionStatus.TRIAL
? strings.trialEndsOn(
dayjs(user?.subscription?.start)
.add(
user?.subscription?.productId?.includes("monthly")
? 7
: 14
)
.format("dddd, MMMM D, YYYY h:mm A")
)
? strings.trialOnGoing(trialEndDate)
: status === SubscriptionStatus.ACTIVE
? strings.subRenewOn(expiryDate)
: status === SubscriptionStatus.CANCELED ||

View File

@@ -463,9 +463,11 @@ async function loadNote(id: string, jump: boolean) {
const tab = useTabStore.getState().getTabForNote(id);
if (useTabStore.getState().currentTab !== tab) {
eSendEvent(eOnLoadNote, {
item: note
});
setTimeout(() => {
eSendEvent(eOnLoadNote, {
item: note
});
}, 300);
}
}

View File

@@ -5,11 +5,11 @@
<key>provisioningProfiles</key>
<dict>
<key>org.streetwriters.notesnook</key>
<string>Notesnook App Distribution 2025</string>
<string>Notesnook 2026</string>
<key>org.streetwriters.notesnook.notewidget</key>
<string>Notesnook Widget Distribution 2025</string>
<string>Notesnook Note Widget 2026</string>
<key>org.streetwriters.notesnook.share</key>
<string>Notesnook Share Distribution 2025</string>
<string>Notesnook Share 2026</string>
</dict>
</dict>
</plist>

View File

@@ -1029,7 +1029,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2167;
CURRENT_PROJECT_VERSION = 2168;
DEVELOPMENT_TEAM = 53CWBG3QUC;
ENABLE_BITCODE = NO;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1104,7 +1104,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 3.3.11;
MARKETING_VERSION = 3.3.12;
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
@@ -1135,7 +1135,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 2167;
CURRENT_PROJECT_VERSION = 2168;
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1210,7 +1210,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 3.3.11;
MARKETING_VERSION = 3.3.12;
ONLY_ACTIVE_ARCH = NO;
OTHER_LDFLAGS = (
"$(inherited)",
@@ -1220,7 +1220,7 @@
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook;
PRODUCT_NAME = Notesnook;
PROVISIONING_PROFILE_SPECIFIER = "";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Notesnook App Distribution 2025";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Notesnook 2026";
SWIFT_OBJC_BRIDGING_HEADER = "Notesnook-Bridging-Header.h";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
@@ -1367,7 +1367,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2167;
CURRENT_PROJECT_VERSION = 2168;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = 53CWBG3QUC;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1379,7 +1379,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 3.3.11;
MARKETING_VERSION = 3.3.12;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
@@ -1410,7 +1410,7 @@
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 2167;
CURRENT_PROJECT_VERSION = 2168;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
@@ -1423,12 +1423,12 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 3.3.11;
MARKETING_VERSION = 3.3.12;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Notesnook Widget Distribution 2025";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Notesnook Note Widget 2026";
SKIP_INSTALL = YES;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
@@ -1453,7 +1453,7 @@
CODE_SIGN_ENTITLEMENTS = "Make Note/Make Note.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2167;
CURRENT_PROJECT_VERSION = 2168;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = 53CWBG3QUC;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1534,7 +1534,7 @@
"@executable_path/../../Frameworks",
);
LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift$(inherited)";
MARKETING_VERSION = 3.3.11;
MARKETING_VERSION = 3.3.12;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
@@ -1565,7 +1565,7 @@
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 2167;
CURRENT_PROJECT_VERSION = 2168;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
@@ -1647,12 +1647,12 @@
"@executable_path/../../Frameworks",
);
LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift$(inherited)";
MARKETING_VERSION = 3.3.11;
MARKETING_VERSION = 3.3.12;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Notesnook Share Distribution 2025";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Notesnook Share 2026";
SKIP_INSTALL = YES;
SWIFT_OBJC_BRIDGING_HEADER = "Make Note/Make Note-Bridging-Header.h";
SWIFT_VERSION = 5.0;

View File

@@ -2004,35 +2004,6 @@ PODS:
- React
- react-native-orientation-locker (1.7.0):
- React-Core
- react-native-pager-view (8.0.0):
- boost
- DoubleConversion
- fast_float
- fmt
- glog
- hermes-engine
- RCT-Folly
- RCT-Folly/Fabric
- RCTRequired
- RCTTypeSafety
- React-Core
- React-debug
- React-Fabric
- React-featureflags
- React-graphics
- React-ImageManager
- React-jsi
- React-NativeModulesApple
- React-RCTFabric
- React-renderercss
- React-rendererdebug
- React-utils
- ReactCodegen
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- SocketRocket
- SwiftUIIntrospect (~> 1.0)
- Yoga
- react-native-pdf (7.0.3):
- boost
- DoubleConversion
@@ -3062,10 +3033,10 @@ PODS:
- Yoga
- RNKeychain (4.0.5):
- React
- RNNotifee (7.4.10):
- RNNotifee (7.4.11):
- React-Core
- RNNotifee/NotifeeCore (= 7.4.10)
- RNNotifee/NotifeeCore (7.4.10):
- RNNotifee/NotifeeCore (= 7.4.11)
- RNNotifee/NotifeeCore (7.4.11):
- React-Core
- RNPrivacySnapshot (1.0.0):
- React-Core
@@ -3161,7 +3132,7 @@ PODS:
- RNWorklets
- SocketRocket
- Yoga
- RNScreens (4.18.0):
- RNScreens (4.19.0):
- boost
- DoubleConversion
- fast_float
@@ -3188,10 +3159,10 @@ PODS:
- ReactCodegen
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- RNScreens/common (= 4.18.0)
- RNScreens/common (= 4.19.0)
- SocketRocket
- Yoga
- RNScreens/common (4.18.0):
- RNScreens/common (4.19.0):
- boost
- DoubleConversion
- fast_float
@@ -3414,7 +3385,6 @@ PODS:
- pop (~> 1.0)
- SocketRocket (0.7.1)
- SSZipArchive (2.4.3)
- SwiftUIIntrospect (1.3.0)
- SwiftyRSA (1.7.0):
- SwiftyRSA/ObjC (= 1.7.0)
- SwiftyRSA/ObjC (1.7.0)
@@ -3486,7 +3456,6 @@ DEPENDENCIES:
- "react-native-netinfo (from `../node_modules/@react-native-community/netinfo`)"
- react-native-notification-sounds (from `../node_modules/react-native-notification-sounds`)
- react-native-orientation-locker (from `../node_modules/react-native-orientation-locker`)
- react-native-pager-view (from `../node_modules/react-native-pager-view`)
- react-native-pdf (from `../node_modules/react-native-pdf`)
- react-native-quick-sqlite (from `../node_modules/react-native-quick-sqlite`)
- react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`)
@@ -3570,7 +3539,6 @@ SPEC REPOS:
- SDWebImage
- SocketRocket
- SSZipArchive
- SwiftUIIntrospect
- SwiftyRSA
- TOCropViewController
@@ -3694,8 +3662,6 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native-notification-sounds"
react-native-orientation-locker:
:path: "../node_modules/react-native-orientation-locker"
react-native-pager-view:
:path: "../node_modules/react-native-pager-view"
react-native-pdf:
:path: "../node_modules/react-native-pdf"
react-native-quick-sqlite:
@@ -3906,7 +3872,6 @@ SPEC CHECKSUMS:
react-native-netinfo: cec9c4e86083cb5b6aba0e0711f563e2fbbff187
react-native-notification-sounds: ce106d58df0dd384bccbd2e84fb53accab7cc068
react-native-orientation-locker: cc6f357b289a2e0dd2210fea0c52cb8e0727fdaa
react-native-pager-view: d7d2aa47f54343bf55fdcee3973503dd27c2bd37
react-native-pdf: edc236298f13f1609e42d41e45b8b6ea88ed10f9
react-native-quick-sqlite: 1ed8d3db1e22a8604d006be69f06053382e93bb0
react-native-safe-area-context: c6e2edd1c1da07bdce287fa9d9e60c5f7b514616
@@ -3962,10 +3927,10 @@ SPEC CHECKSUMS:
RNIap: 61f183ac917792fae42b0326b1bef33598c1adf6
RNImageCropPicker: 5fd4ceaead64d8c53c787e4e559004f97bc76df7
RNKeychain: ffd0513e676445c637410b47249460cbf56bc9cb
RNNotifee: 3840cc81add9954a5dec040f96442498f3f95d7b
RNNotifee: c4827fa2cec60ab634d62bafdb5cf57cd2999d54
RNPrivacySnapshot: ccad3a548338c2f526bb7b1789af3fb0618b7d1d
RNReanimated: f1868b36f4b2b52a0ed00062cfda69506f75eaee
RNScreens: d821082c6dd1cb397cc0c98b026eeafaa68be479
RNScreens: ffbb0296608eb3560de641a711bbdb663ed1f6b4
RNSecureRandom: b64d263529492a6897e236a22a2c4249aa1b53dc
RNShare: 5d39a36f2d3d9a6c9344bfb3232c8ed607924a35
RNSVG: 8c0bbfa480a24b24468f1c76bd852a4aac3178e6
@@ -3976,7 +3941,6 @@ SPEC CHECKSUMS:
SexyTooltip: 5c9b4dec52bfb317938cb0488efd9da3717bb6fd
SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748
SSZipArchive: fe6a26b2a54d5a0890f2567b5cc6de5caa600aef
SwiftUIIntrospect: fee9aa07293ee280373a591e1824e8ddc869ba5d
SwiftyRSA: 8c6dd1ea7db1b8dc4fb517a202f88bb1354bc2c6
TOCropViewController: 797deaf39c90e6e9ddd848d88817f6b9a8a09888
toolbar-android: c426ed5bd3dcccfed20fd79533efc0d1ae0ef018

View File

@@ -1,16 +1,16 @@
{
"name": "@notesnook/mobile",
"version": "3.3.11",
"version": "3.3.12",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@notesnook/mobile",
"version": "3.3.11",
"version": "3.3.12",
"hasInstallScript": true,
"license": "GPL-3.0-or-later",
"dependencies": {
"@ammarahmed/notifee-react-native": "^7.4.10",
"@ammarahmed/notifee-react-native": "^7.4.11",
"@ammarahmed/react-native-background-fetch": "^4.2.2",
"@ammarahmed/react-native-eventsource": "1.1.0",
"@ammarahmed/react-native-fingerprint-scanner": "^5.0.1",
@@ -102,7 +102,6 @@
"react-native-navigation-bar-color": "2.0.2",
"react-native-notification-sounds": "0.5.5",
"react-native-orientation-locker": "^1.7.0",
"react-native-pager-view": "^8.0.0",
"react-native-pdf": "^7.0.3",
"react-native-privacy-snapshot": "github:standardnotes/react-native-privacy-snapshot",
"react-native-progress": "5.0.0",
@@ -113,12 +112,11 @@
"react-native-safe-area-context": "^5.6.1",
"react-native-scoped-storage": "^1.9.5",
"react-native-screenguard": "1.0.0",
"react-native-screens": "^4.16.0",
"react-native-screens": "^4.19.0",
"react-native-securerandom": "^1.0.1",
"react-native-share": "^12.0.3",
"react-native-svg": "^15.12.0",
"react-native-swiper-flatlist": "3.2.2",
"react-native-tab-view": "^4.2.2",
"react-native-theme-switch-animation": "^0.6.0",
"react-native-tooltips": "^1.0.3",
"react-native-url-polyfill": "^2.0.0",
@@ -485,9 +483,9 @@
}
},
"node_modules/@ammarahmed/notifee-react-native": {
"version": "7.4.10",
"resolved": "https://registry.npmjs.org/@ammarahmed/notifee-react-native/-/notifee-react-native-7.4.10.tgz",
"integrity": "sha512-19t1vhIddFneOI7JVmXghunEt5mEmDBY/G1YVMuRNNT/HFQ1EcArJWuAaFAJrhrl/2CBS7xoPGLusKbkbT/JjA==",
"version": "7.4.11",
"resolved": "https://registry.npmjs.org/@ammarahmed/notifee-react-native/-/notifee-react-native-7.4.11.tgz",
"integrity": "sha512-9zKhONgir2FN/sMj/rsKAhDwfxadDuxB6LJILKxpGwzqvcIuG70oCKWmBBG9LJJpSfcVpNTdxBoH9RtNZTZSsQ==",
"license": "Apache-2.0",
"peerDependencies": {
"react-native": "*"
@@ -17742,16 +17740,6 @@
}
}
},
"node_modules/react-native-pager-view": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/react-native-pager-view/-/react-native-pager-view-8.0.0.tgz",
"integrity": "sha512-oAwlWT1lhTkIs9HhODnjNNl/owxzn9DP1MbP+az6OTUdgbmzA16Up83sBH8NRKwrH8rNm7iuWnX1qMqiiWOLhg==",
"license": "MIT",
"peerDependencies": {
"react": "*",
"react-native": "*"
}
},
"node_modules/react-native-pdf": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/react-native-pdf/-/react-native-pdf-7.0.3.tgz",
@@ -17900,9 +17888,9 @@
}
},
"node_modules/react-native-screens": {
"version": "4.18.0",
"resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-4.18.0.tgz",
"integrity": "sha512-mRTLWL7Uc1p/RFNveEIIrhP22oxHduC2ZnLr/2iHwBeYpGXR0rJZ7Bgc0ktxQSHRjWTPT70qc/7yd4r9960PBQ==",
"version": "4.19.0",
"resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-4.19.0.tgz",
"integrity": "sha512-qSDAO3AL5bti0Ri7KZRSVmWlhDr8MV86N5GruiKVQfEL7Zx2nUi3Dl62lqHUAD/LnDvOPuDDsMHCfIpYSv3hPQ==",
"license": "MIT",
"dependencies": {
"react-freeze": "^1.0.0",
@@ -17958,20 +17946,6 @@
"react-native": ">=0.59.0"
}
},
"node_modules/react-native-tab-view": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/react-native-tab-view/-/react-native-tab-view-4.2.2.tgz",
"integrity": "sha512-NXtrG6OchvbGjsvbySJGVocXxo4Y2vA17ph4rAaWtA2jh+AasD8OyikKBRg2SmllEfeQ+GEhcKe8kulHv8BhTg==",
"license": "MIT",
"dependencies": {
"use-latest-callback": "^0.2.4"
},
"peerDependencies": {
"react": ">= 18.2.0",
"react-native": "*",
"react-native-pager-view": ">= 6.0.0"
}
},
"node_modules/react-native-theme-switch-animation": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/react-native-theme-switch-animation/-/react-native-theme-switch-animation-0.6.0.tgz",

View File

@@ -1,6 +1,6 @@
{
"name": "@notesnook/mobile",
"version": "3.3.11",
"version": "3.3.12",
"private": true,
"license": "GPL-3.0-or-later",
"scripts": {
@@ -26,7 +26,7 @@
"react": "19.1.1"
},
"dependencies": {
"@ammarahmed/notifee-react-native": "^7.4.10",
"@ammarahmed/notifee-react-native": "^7.4.11",
"@ammarahmed/react-native-background-fetch": "^4.2.2",
"@ammarahmed/react-native-eventsource": "1.1.0",
"@ammarahmed/react-native-fingerprint-scanner": "^5.0.1",
@@ -118,7 +118,6 @@
"react-native-navigation-bar-color": "2.0.2",
"react-native-notification-sounds": "0.5.5",
"react-native-orientation-locker": "^1.7.0",
"react-native-pager-view": "^8.0.0",
"react-native-pdf": "^7.0.3",
"react-native-privacy-snapshot": "github:standardnotes/react-native-privacy-snapshot",
"react-native-progress": "5.0.0",
@@ -129,12 +128,11 @@
"react-native-safe-area-context": "^5.6.1",
"react-native-scoped-storage": "^1.9.5",
"react-native-screenguard": "1.0.0",
"react-native-screens": "^4.16.0",
"react-native-screens": "^4.19.0",
"react-native-securerandom": "^1.0.1",
"react-native-share": "^12.0.3",
"react-native-svg": "^15.12.0",
"react-native-swiper-flatlist": "3.2.2",
"react-native-tab-view": "^4.2.2",
"react-native-theme-switch-animation": "^0.6.0",
"react-native-tooltips": "^1.0.3",
"react-native-url-polyfill": "^2.0.0",

View File

@@ -1,72 +0,0 @@
diff --git a/node_modules/react-native-pdf/android/.gradle/5.6.1/fileChanges/last-build.bin b/node_modules/react-native-pdf/android/.gradle/5.6.1/fileChanges/last-build.bin
deleted file mode 100644
index f76dd23..0000000
Binary files a/node_modules/react-native-pdf/android/.gradle/5.6.1/fileChanges/last-build.bin and /dev/null differ
diff --git a/node_modules/react-native-pdf/android/.gradle/5.6.1/fileHashes/fileHashes.lock b/node_modules/react-native-pdf/android/.gradle/5.6.1/fileHashes/fileHashes.lock
deleted file mode 100644
index 52d8866..0000000
Binary files a/node_modules/react-native-pdf/android/.gradle/5.6.1/fileHashes/fileHashes.lock and /dev/null differ
diff --git a/node_modules/react-native-pdf/android/.gradle/5.6.1/gc.properties b/node_modules/react-native-pdf/android/.gradle/5.6.1/gc.properties
deleted file mode 100644
index e69de29..0000000
diff --git a/node_modules/react-native-pdf/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/node_modules/react-native-pdf/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock
deleted file mode 100644
index 1508dc9..0000000
Binary files a/node_modules/react-native-pdf/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock and /dev/null differ
diff --git a/node_modules/react-native-pdf/android/.gradle/buildOutputCleanup/cache.properties b/node_modules/react-native-pdf/android/.gradle/buildOutputCleanup/cache.properties
deleted file mode 100644
index f4a32a7..0000000
--- a/node_modules/react-native-pdf/android/.gradle/buildOutputCleanup/cache.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-#Sat Oct 12 21:44:37 CST 2019
-gradle.version=5.6.1
diff --git a/node_modules/react-native-pdf/android/.gradle/vcs-1/gc.properties b/node_modules/react-native-pdf/android/.gradle/vcs-1/gc.properties
deleted file mode 100644
index e69de29..0000000
diff --git a/node_modules/react-native-pdf/android/build.gradle b/node_modules/react-native-pdf/android/build.gradle
index 3dfe1dc..70ef9da 100644
--- a/node_modules/react-native-pdf/android/build.gradle
+++ b/node_modules/react-native-pdf/android/build.gradle
@@ -126,6 +126,6 @@ dependencies {
// The repo from zacharee is based on PdfiumAndroidKt, a much newer fork of PdfiumAndroid, with better maintenance and updated native libraries.
implementation 'com.github.zacharee:AndroidPdfViewer:4.0.1'
// Depend on PdfiumAndroidKt directly so this can be updated independently of AndroidPdfViewer as updates are provided.
- implementation 'io.legere:pdfiumandroid:1.0.24'
+ implementation 'io.legere:pdfiumandroid:1.0.32'
implementation 'com.google.code.gson:gson:2.8.5'
}
diff --git a/node_modules/react-native-pdf/android/src/main/java/org/wonday/pdf/PdfView.java b/node_modules/react-native-pdf/android/src/main/java/org/wonday/pdf/PdfView.java
index 7b7a125..361924b 100644
--- a/node_modules/react-native-pdf/android/src/main/java/org/wonday/pdf/PdfView.java
+++ b/node_modules/react-native-pdf/android/src/main/java/org/wonday/pdf/PdfView.java
@@ -12,6 +12,8 @@ import java.io.File;
import android.content.ContentResolver;
import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
import android.util.SizeF;
import android.view.View;
import android.view.ViewGroup;
@@ -105,7 +107,7 @@ public class PdfView extends PDFView implements OnPageChangeListener,OnLoadCompl
TopChangeEvent tce = new TopChangeEvent(surfaceId, getId(), event);
if (dispatcher != null) {
- dispatcher.dispatchEvent(tce);
+ new Handler(Looper.getMainLooper()).postDelayed(() -> dispatcher.dispatchEvent(tce), 10);
}
// ReactContext reactContext = (ReactContext)this.getContext();
diff --git a/node_modules/react-native-pdf/index.js b/node_modules/react-native-pdf/index.js
index 56df005..dd14412 100644
--- a/node_modules/react-native-pdf/index.js
+++ b/node_modules/react-native-pdf/index.js
@@ -364,7 +364,6 @@ export default class Pdf extends Component {
}
_onChange = (event) => {
-
let message = event.nativeEvent.message.split('|');
//__DEV__ && console.log("onChange: " + message);
if (message.length > 0) {

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -39,7 +39,16 @@ export function Header() {
>
<span style={{ color: "var(--accent)" }}>Mono</span>graph
</Text>
<Button variant="accent">Publish a note</Button>
<Button
as="a"
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
href="https://app.notesnook.com/"
target="_blank"
variant="accent"
>
Publish a note
</Button>
</Flex>
);
}

View File

@@ -139,7 +139,7 @@ const ChatItem = ({
>
<Flex
sx={{
backgroundColor: incoming ? "background-secondary" : "shade",
backgroundColor: incoming ? "background" : "shade",
borderRadius: 10,
p: 2,
pb: 1,
@@ -147,7 +147,7 @@ const ChatItem = ({
maxWidth: "70%"
}}
>
<Text sx={{ fontSize: "13px", color: "paragraph" }}>{message}</Text>
<Text sx={{ fontSize: "subBody", color: "paragraph" }}>{message}</Text>
<Timer time={time} />
</Flex>
</Flex>

View File

@@ -72,7 +72,23 @@ export function BaseThemeProvider(
return (
<>
<EmotionThemeProvider {...restProps} scope={scope}>
<EmotionThemeProvider
{...restProps}
theme={{
fontSizes: {
heading: "24px",
subheading: "20px",
input: "12px",
title: "16px",
subtitle: "14px",
body: "14px",
subBody: "12px",
menu: "12px",
code: "14px"
}
}}
scope={scope}
>
{children}
</EmotionThemeProvider>
</>

View File

@@ -1,98 +1,75 @@
/* open-sans-regular - vietnamese_latin-ext_latin_hebrew_greek-ext_greek_cyrillic-ext_cyrillic */
@font-face {
font-family: "Open Sans";
font-family: "Inter";
font-style: normal;
font-display: fallback;
font-display: swap;
font-weight: 400;
src: local(""),
url("./assets/fonts/open-sans-v34-vietnamese_latin-ext_latin_hebrew_greek-ext_greek_cyrillic-ext_cyrillic-regular.woff2")
format("woff2"),
/* Super Modern Browsers */
url("./assets/fonts/open-sans-v34-vietnamese_latin-ext_latin_hebrew_greek-ext_greek_cyrillic-ext_cyrillic-regular.woff")
format("woff"),
/* Modern Browsers */
url("./assets/fonts/open-sans-v34-vietnamese_latin-ext_latin_hebrew_greek-ext_greek_cyrillic-ext_cyrillic-regular.ttf")
format("truetype");
src: local(""), url("./assets/fonts/Inter-Regular.woff2") format("woff2"),
url("./assets/fonts/Inter-Regular.ttf") format("truetype");
}
/* open-sans-600 - vietnamese_latin-ext_latin_hebrew_greek-ext_greek_cyrillic-ext_cyrillic */
@font-face {
font-family: "Open Sans";
font-family: "Inter";
font-style: normal;
font-display: swap;
font-weight: 600;
font-display: fallback;
src: local(""),
url("./assets/fonts/open-sans-v34-vietnamese_latin-ext_latin_hebrew_greek-ext_greek_cyrillic-ext_cyrillic-600.woff2")
format("woff2"),
/* Super Modern Browsers */
url("./assets/fonts/open-sans-v34-vietnamese_latin-ext_latin_hebrew_greek-ext_greek_cyrillic-ext_cyrillic-600.woff")
format("woff"),
/* Modern Browsers */
url("./assets/fonts/open-sans-v34-vietnamese_latin-ext_latin_hebrew_greek-ext_greek_cyrillic-ext_cyrillic-600.ttf")
format("truetype");
src: local(""), url("./assets/fonts/Inter-SemiBold.woff2") format("woff2"),
url("./assets/fonts/Inter-SemiBold.ttf") format("truetype");
}
/* open-sans-700 - vietnamese_latin-ext_latin_hebrew_greek-ext_greek_cyrillic-ext_cyrillic */
@font-face {
font-family: "Open Sans";
font-family: "Inter";
font-style: normal;
font-display: swap;
font-weight: 700;
font-display: fallback;
src: local(""),
url("./assets/fonts/open-sans-v34-vietnamese_latin-ext_latin_hebrew_greek-ext_greek_cyrillic-ext_cyrillic-700.woff2")
format("woff2"),
/* Super Modern Browsers */
url("./assets/fonts/open-sans-v34-vietnamese_latin-ext_latin_hebrew_greek-ext_greek_cyrillic-ext_cyrillic-700.woff")
format("woff"),
/* Modern Browsers */
url("./assets/fonts/open-sans-v34-vietnamese_latin-ext_latin_hebrew_greek-ext_greek_cyrillic-ext_cyrillic-700.ttf")
format("truetype");
src: local(""), url("./assets/fonts/Inter-Bold.woff2") format("woff2"),
url("./assets/fonts/Inter-Bold.ttf") format("truetype");
}
/* open-sans-italic - vietnamese_latin-ext_latin_hebrew_greek-ext_greek_cyrillic-ext_cyrillic */
@font-face {
font-family: "Open Sans";
font-family: "Inter";
font-style: normal;
font-display: swap;
font-weight: 500;
src: local(""), url("./assets/fonts/Inter-Medium.woff2") format("woff2"),
url("./assets/fonts/Inter-Medium.ttf") format("truetype");
}
@font-face {
font-family: "Inter";
font-style: italic;
font-display: swap;
font-weight: 400;
font-display: fallback;
src: local(""),
url("./assets/fonts/open-sans-v34-vietnamese_latin-ext_latin_hebrew_greek-ext_greek_cyrillic-ext_cyrillic-italic.woff2")
format("woff2"),
/* Super Modern Browsers */
url("./assets/fonts/open-sans-v34-vietnamese_latin-ext_latin_hebrew_greek-ext_greek_cyrillic-ext_cyrillic-italic.woff")
format("woff"),
/* Modern Browsers */
url("./assets/fonts/open-sans-v34-vietnamese_latin-ext_latin_hebrew_greek-ext_greek_cyrillic-ext_cyrillic-italic.ttf")
format("truetype");
src: local(""), url("./assets/fonts/Inter-Italic.woff2") format("woff2"),
url("./assets/fonts/Inter-Italic.ttf") format("truetype");
}
/* open-sans-600italic - vietnamese_latin-ext_latin_hebrew_greek-ext_greek_cyrillic-ext_cyrillic */
@font-face {
font-family: "Open Sans";
font-family: "Inter";
font-style: italic;
font-display: swap;
font-weight: 600;
font-display: fallback;
src: local(""),
url("./assets/fonts/open-sans-v34-vietnamese_latin-ext_latin_hebrew_greek-ext_greek_cyrillic-ext_cyrillic-600italic.woff2")
format("woff2"),
/* Super Modern Browsers */
url("./assets/fonts/open-sans-v34-vietnamese_latin-ext_latin_hebrew_greek-ext_greek_cyrillic-ext_cyrillic-600italic.woff")
format("woff"),
/* Modern Browsers */
url("./assets/fonts/open-sans-v34-vietnamese_latin-ext_latin_hebrew_greek-ext_greek_cyrillic-ext_cyrillic-600italic.ttf")
format("truetype");
url("./assets/fonts/Inter-SemiBoldItalic.woff2") format("woff2"),
url("./assets/fonts/Inter-SemiBoldItalic.ttf") format("truetype");
}
/* open-sans-700italic - vietnamese_latin-ext_latin_hebrew_greek-ext_greek_cyrillic-ext_cyrillic */
@font-face {
font-family: "Open Sans";
font-family: "Inter";
font-style: italic;
font-display: swap;
font-weight: 700;
font-display: fallback;
src: local(""),
url("./assets/fonts/open-sans-v34-vietnamese_latin-ext_latin_hebrew_greek-ext_greek_cyrillic-ext_cyrillic-700italic.woff2")
format("woff2"),
/* Super Modern Browsers */
url("./assets/fonts/open-sans-v34-vietnamese_latin-ext_latin_hebrew_greek-ext_greek_cyrillic-ext_cyrillic-700italic.woff")
format("woff"),
/* Modern Browsers */
url("./assets/fonts/open-sans-v34-vietnamese_latin-ext_latin_hebrew_greek-ext_greek_cyrillic-ext_cyrillic-700italic.ttf")
format("truetype");
src: local(""), url("./assets/fonts/Inter-BoldItalic.woff2") format("woff2"),
url("./assets/fonts/Inter-BoldItalic.ttf") format("truetype");
}
@font-face {
font-family: "Inter";
font-style: italic;
font-display: swap;
font-weight: 500;
src: local(""), url("./assets/fonts/Inter-MediumItalic.woff2") format("woff2"),
url("./assets/fonts/Inter-MediumItalic.ttf") format("truetype");
}
body {

View File

@@ -127,6 +127,7 @@ export default function Monograph() {
mt={6}
>
<ButtonLink
as="a"
variant="accent"
href="https://app.notesnook.com/"
target="_blank"
@@ -134,6 +135,7 @@ export default function Monograph() {
Publish a note
</ButtonLink>
<ButtonLink
as="a"
variant="secondary"
sx={{
bg: "background-secondary",
@@ -182,7 +184,7 @@ export default function Monograph() {
>
<Icon path={feature.Icon} color="var(--accent)" size={42} />
<Text variant="heading">{feature.name}</Text>
<Text>{feature.body}</Text>
<Text variant="body">{feature.body}</Text>
</Flex>
))}
</Box>

View File

@@ -0,0 +1,40 @@
/*
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 { readFileSync } from "node:fs";
export function readSecrets<T extends string>(
names: T[]
): Record<T, string | undefined> {
const result: Record<T, string | undefined> = {} as Record<
T,
string | undefined
>;
for (const name of names) result[name] = readSecret(name);
return result;
}
export function readSecret(name: string): string | undefined {
const value = process.env[name];
if (value) return value;
const file = process.env[`${name}_FILE`];
if (file) {
return readFileSync(file, "utf-8");
}
}

View File

@@ -18,10 +18,16 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import WorkersKVREST from "@sagi.io/workers-kv";
import { readSecrets } from "../secrets";
const cfAccountId = process.env.CLOUDFLARE_ACCOUNT_ID!;
const cfAuthToken = process.env.CLOUDFLARE_AUTH_TOKEN!;
const namespaceId = process.env.CLOUDFLARE_KV_NAMESPACE_ID!;
const env = readSecrets([
"CLOUDFLARE_ACCOUNT_ID",
"CLOUDFLARE_AUTH_TOKEN",
"CLOUDFLARE_KV_NAMESPACE_ID"
]);
const cfAccountId = env.CLOUDFLARE_ACCOUNT_ID;
const cfAuthToken = env.CLOUDFLARE_AUTH_TOKEN;
const namespaceId = env.CLOUDFLARE_KV_NAMESPACE_ID;
if (!cfAccountId || !cfAuthToken || !namespaceId)
throw new Error(

View File

@@ -1,12 +1,12 @@
{
"name": "@notesnook/monograph",
"version": "1.2.5",
"version": "1.2.7",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@notesnook/monograph",
"version": "1.2.5",
"version": "1.2.7",
"dependencies": {
"@emotion/cache": "11.11.0",
"@emotion/react": "11.11.1",
@@ -26,6 +26,7 @@
"@sagi.io/workers-kv": "^0.0.14",
"@theme-ui/components": "^0.16.2",
"@theme-ui/core": "^0.16.2",
"buffer": "^6.0.3",
"canvas-hypertxt": "^1.0.3",
"comlink": "^4.4.1",
"date-fns": "^4.1.0",
@@ -76,6 +77,7 @@
"@streetwriters/kysely": "^0.27.4",
"@streetwriters/showdown": "^3.0.9-alpha",
"@types/mime-db": "^1.43.5",
"alfaaz": "^1.1.0",
"async-mutex": "0.5.0",
"dayjs": "1.11.13",
"dom-serializer": "^2.0.0",
@@ -163,9 +165,6 @@
"@tiptap/extension-placeholder": "2.6.6",
"@tiptap/extension-subscript": "2.6.6",
"@tiptap/extension-superscript": "2.6.6",
"@tiptap/extension-table": "2.6.6",
"@tiptap/extension-table-cell": "2.6.6",
"@tiptap/extension-table-header": "2.6.6",
"@tiptap/extension-table-row": "2.6.6",
"@tiptap/extension-task-item": "2.6.6",
"@tiptap/extension-task-list": "2.6.6",
@@ -2948,6 +2947,22 @@
"@styled-system/css": "^5.1.5"
}
},
"node_modules/@theme-ui/color-modes": {
"version": "0.16.2",
"resolved": "https://registry.npmjs.org/@theme-ui/color-modes/-/color-modes-0.16.2.tgz",
"integrity": "sha512-jWEWx53lxNgWCT38i/kwLV2rsvJz8lVZgi5oImnVwYba9VejXD23q1ckbNFJHosQ8KKXY87ht0KPC6BQFIiHtQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"@theme-ui/core": "^0.16.2",
"@theme-ui/css": "^0.16.2",
"deepmerge": "^4.2.2"
},
"peerDependencies": {
"@emotion/react": "^11.11.1",
"react": ">=18"
}
},
"node_modules/@theme-ui/components": {
"version": "0.16.2",
"resolved": "https://registry.npmjs.org/@theme-ui/components/-/components-0.16.2.tgz",
@@ -2993,6 +3008,22 @@
"@emotion/react": "^11.11.1"
}
},
"node_modules/@theme-ui/theme-provider": {
"version": "0.16.2",
"resolved": "https://registry.npmjs.org/@theme-ui/theme-provider/-/theme-provider-0.16.2.tgz",
"integrity": "sha512-LRnVevODcGqO0JyLJ3wht+PV3ZoZcJ7XXLJAJWDoGeII4vZcPQKwVy4Lpz/juHsZppQxKcB3U+sQDGBnP25irQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"@theme-ui/color-modes": "^0.16.2",
"@theme-ui/core": "^0.16.2",
"@theme-ui/css": "^0.16.2"
},
"peerDependencies": {
"@emotion/react": "^11.11.1",
"react": ">=18"
}
},
"node_modules/@types/acorn": {
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/@types/acorn/-/acorn-4.0.6.tgz",
@@ -3122,14 +3153,14 @@
"version": "15.7.13",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz",
"integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==",
"dev": true,
"devOptional": true,
"license": "MIT"
},
"node_modules/@types/react": {
"version": "18.3.10",
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.10.tgz",
"integrity": "sha512-02sAAlBnP39JgXwkAq3PeU9DVaaGpZyF3MGcC0MKgQVkZor5IiiDAipVaxQHtDJAmO4GIy/rVBy/LzVj76Cyqg==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@types/prop-types": "*",
@@ -3507,7 +3538,6 @@
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
"dev": true,
"funding": [
{
"type": "github",
@@ -3758,6 +3788,30 @@
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
}
},
"node_modules/buffer": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
"integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT",
"dependencies": {
"base64-js": "^1.3.1",
"ieee754": "^1.2.1"
}
},
"node_modules/buffer-from": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-0.1.2.tgz",
@@ -5829,7 +5883,6 @@
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
"dev": true,
"funding": [
{
"type": "github",
@@ -12215,7 +12268,7 @@
"version": "3.23.8",
"resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz",
"integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==",
"dev": true,
"devOptional": true,
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/colinhacks"

View File

@@ -1,6 +1,6 @@
{
"name": "@notesnook/monograph",
"version": "1.2.5",
"version": "1.2.7",
"private": true,
"sideEffects": false,
"type": "module",
@@ -30,6 +30,7 @@
"@sagi.io/workers-kv": "^0.0.14",
"@theme-ui/components": "^0.16.2",
"@theme-ui/core": "^0.16.2",
"buffer": "^6.0.3",
"canvas-hypertxt": "^1.0.3",
"comlink": "^4.4.1",
"date-fns": "^4.1.0",

View File

@@ -31,10 +31,10 @@ const remix = createRequestHandler(
build as unknown as ServerBuild,
Bun.env.NODE_ENV
);
process.env.PORT = process.env.PORT || "3000";
export default {
port: process.env.PORT,
port: process.env.PORT || "3000",
hostname: process.env.HOST || "localhost",
async fetch(request) {
// First we need to send handle static files
const { pathname } = new URL(request.url);

View File

@@ -20,7 +20,7 @@ import { vitePlugin as remix } from "@remix-run/dev";
import { defineConfig } from "vite";
import tsconfigPaths from "vite-tsconfig-paths";
import arraybuffer from "vite-plugin-arraybuffer";
import ThemeDark from "@notesnook/theme/theme-engine/themes/default-dark.json" with {type:"json"};
import ThemeDark from "@notesnook/theme/theme-engine/themes/default-dark.json" with { type:"json" };
import type { Plugin, ResolvedConfig } from "vite";
import { writeFile } from "fs/promises";
import path from "path";
@@ -51,8 +51,9 @@ export default defineConfig(({ isSsrBuild }) => ({
scripts: { start: pkg.scripts.start },
dependencies: {
"@napi-rs/canvas": pkg.dependencies["@napi-rs/canvas"],
"@remix-run/server-runtime": pkg.devDependencies["@remix-run/server-runtime"],
},
"@remix-run/server-runtime":
pkg.devDependencies["@remix-run/server-runtime"]
}
})
}),
remix({
@@ -65,13 +66,15 @@ export default defineConfig(({ isSsrBuild }) => ({
}),
tsconfigPaths(),
arraybuffer(),
isSsrBuild ? viteStaticCopy({
targets: [
{ src: "./server.ts", dest: "../../" },
{ src: "./app/assets", dest: "../" },
{ src: "./fonts", dest: "../" }
]
}) : undefined
isSsrBuild
? viteStaticCopy({
targets: [
{ src: "./server.ts", dest: "../../" },
{ src: "./app/assets", dest: "../" },
{ src: "./fonts", dest: "../" }
]
})
: undefined
],
worker: {
format: "es",
@@ -90,7 +93,7 @@ export default defineConfig(({ isSsrBuild }) => ({
build: {
target: isSsrBuild ? "node20" : undefined,
rollupOptions: {
external: ["@napi-rs/canvas"]
external: ["@napi-rs/canvas"],
}
},
define: {

View File

@@ -0,0 +1,3 @@
- Bug fixes and minor improvements
Thank you for using Notesnook!

View File

@@ -33,6 +33,7 @@
"start:editor-mobile": "npm run tx editor-mobile:start",
"start:editor": "npm run tx editor:start",
"start:server:themes": "npm run tx themes-server:start",
"build:server:themes": "npm run tx themes-server:build",
"build:intl": "npm run tx intl:build",
"start:intl": "npm run tx intl:start",
"prettier": "npx prettier . --write",

View File

@@ -7471,6 +7471,10 @@ msgstr "Your free trial has started"
msgid "Your free trial is ending soon"
msgstr "Your free trial is ending soon"
#: src/strings.ts:2614
msgid "Your free trial is on-going. Your subscription will start on {trialExpiryDate}"
msgstr "Your free trial is on-going. Your subscription will start on {trialExpiryDate}"
#: src/strings.ts:1777
msgid "Your full name"
msgstr "Your full name"

View File

@@ -7417,6 +7417,10 @@ msgstr ""
msgid "Your free trial is ending soon"
msgstr ""
#: src/strings.ts:2614
msgid "Your free trial is on-going. Your subscription will start on {trialExpiryDate}"
msgstr ""
#: src/strings.ts:1777
msgid "Your full name"
msgstr ""

View File

@@ -2609,5 +2609,7 @@ Use this if changes from other devices are not appearing on this device. This wi
views: () => t`Views`,
clickToUpdate: () => t`Click to update`,
noPassword: () => t`No password`,
publishToTheWeb: () => t`Publish to the web`
publishToTheWeb: () => t`Publish to the web`,
trialOnGoing: (trialExpiryDate: string) =>
t`Your free trial is on-going. Your subscription will start on ${trialExpiryDate}`
};

32
servers/themes/Dockerfile Normal file
View File

@@ -0,0 +1,32 @@
# Build stage
FROM node:22.20.0-alpine AS build
# Install dependencies for gyp to work
RUN --mount=type=cache,target=/var/cache/apk,sharing=locked apk add --update build-base git python3 py3-setuptools
WORKDIR /app
# Copy files
COPY . .
# Install dependencies
RUN --mount=type=cache,target=/root/.npm npm ci --ignore-scripts --prefer-offline --no-audit
# Bootstrap the project
RUN --mount=type=cache,target=/root/.npm npm run bootstrap -- --scope=themes
# Build the server
RUN npm run build:server:themes
# Production stage
FROM oven/bun:1.3.5-alpine AS production
# Install git to pull the themes repo
RUN --mount=type=cache,target=/var/cache/apk,sharing=locked apk add --update git
WORKDIR /app
# Copy server files from previous stage
COPY --from=build /app/servers/themes/dist ./dist
CMD ["bun", "run", "dist/server.mjs"]

File diff suppressed because it is too large Load Diff

View File

@@ -1,26 +1,29 @@
{
"name": "@notesnook/themes-server",
"version": "1.0.0",
"version": "1.0.4",
"description": "A simple rest api for notesnook themes",
"private": "true",
"main": "src/index.ts",
"scripts": {
"start": "vite-node -w src/server.ts"
"start": "vite-node -w src/server.ts",
"build": "esbuild src/server.ts --bundle --platform=node --sourcemap --format=esm --outfile=dist/server.mjs"
},
"author": "",
"license": "ISC",
"dependencies": {
"@notesnook/theme": "file:../../packages/theme",
"@orama/orama": "^1.0.8",
"@trpc/server": "10.45.2",
"async-mutex": "0.5.0",
"cloudflare": "^5.2.0",
"cors": "^2.8.5",
"react": "18.3.1",
"util": "^0.12.5",
"zod": "3.23.8"
},
"devDependencies": {
"@notesnook/theme": "file:../../packages/theme",
"@types/cors": "^2.8.13",
"react": "18.3.1",
"esbuild": "0.21.5",
"vite-node": "2.1.8"
}
}
}

View File

@@ -19,7 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import { z } from "zod";
import { InstallsCounter } from "./constants";
import { findTheme, getThemes } from "./orama";
import { findTheme, getThemes, updateTotalInstalls } from "./orama";
import { syncThemes } from "./sync";
import { publicProcedure, router } from "./trpc";
import { THEME_COMPATIBILITY_VERSION } from "@notesnook/theme";
@@ -41,7 +41,12 @@ export const ThemesAPI = router({
const theme = await findTheme(id, compatibilityVersion);
if (!theme) return;
if (userId) await InstallsCounter.increment(theme.id, userId);
if (userId) {
updateTotalInstalls(
theme,
await InstallsCounter.increment(theme.id, userId)
);
}
return theme;
}),
updateTheme: publicProcedure

View File

@@ -18,12 +18,35 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import path from "path";
import { Counter } from "./counter";
import { fileURLToPath } from "url";
import { FsCounter } from "./counter/fs";
import { readSecrets } from "./secrets";
import { KVCounter } from "./counter/kv";
const env = readSecrets([
"CLOUDFLARE_ACCOUNT_ID",
"CLOUDFLARE_AUTH_TOKEN",
"CLOUDFLARE_INSTALLS_KV_NAMESPACE_ID"
]);
const __dirname = path.dirname(fileURLToPath(import.meta.url));
export const THEMES_REPO_URL =
process.env.THEMES_REPO_URL ||
"https://github.com/streetwriters/notesnook-themes.git";
export const THEMES_REPO_URL = `https://github.com/streetwriters/notesnook-themes.git`;
export const THEME_REPO_DIR_NAME = "notesnook-themes";
export const THEME_METADATA_JSON = path.join(__dirname, "themes-metadata.json");
export const THEME_REPO_DIR_PATH = path.resolve(
path.join(__dirname, "..", THEME_REPO_DIR_NAME)
);
export const InstallsCounter = new Counter("installs", path.dirname(__dirname));
export const InstallsCounter =
env.CLOUDFLARE_ACCOUNT_ID &&
env.CLOUDFLARE_AUTH_TOKEN &&
env.CLOUDFLARE_INSTALLS_KV_NAMESPACE_ID
? new KVCounter({
cfAccountId: env.CLOUDFLARE_ACCOUNT_ID,
cfAuthToken: env.CLOUDFLARE_AUTH_TOKEN,
namespaceId: env.CLOUDFLARE_INSTALLS_KV_NAMESPACE_ID
})
: new FsCounter(path.join(path.dirname(__dirname), "..", "installs.json"));

View File

@@ -18,25 +18,25 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { readFile, writeFile } from "fs/promises";
import path from "path";
import { Mutex } from "async-mutex";
type Counts = Record<string, string[]>;
export class Counter {
export class FsCounter {
private path: string;
private readonly mutex: Mutex;
constructor(id: string, baseDirectory: string) {
this.path = path.join(baseDirectory, `${id}.json`);
constructor(id: string) {
this.path = id;
this.mutex = new Mutex();
}
async increment(key: string, uid: string) {
await this.mutex.runExclusive(async () => {
const counts = await this.counts();
return await this.mutex.runExclusive(async () => {
const counts = await this.all();
counts[key] = counts[key] || [];
if (counts[key].includes(uid)) return;
if (counts[key].includes(uid)) return counts[key].length;
counts[key].push(uid);
await this.save(counts);
return counts[key].length;
});
}
@@ -44,7 +44,16 @@ export class Counter {
await writeFile(this.path, JSON.stringify(counts));
}
async counts(): Promise<Counts> {
async counts(keys: string[]): Promise<Record<string, number>> {
const all = await this.all();
const result: Record<string, number> = {};
for (const key of keys) {
result[key] = (all[key] || []).length;
}
return result;
}
private async all(): Promise<Counts> {
try {
return JSON.parse(await readFile(this.path, "utf-8"));
} catch {

View File

@@ -0,0 +1,93 @@
/*
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 { Mutex } from "async-mutex";
import { Cloudflare } from "cloudflare";
type WorkersKVRESTConfig = {
cfAccountId: string;
cfAuthToken: string;
namespaceId: string;
};
export class KVCounter {
private readonly client: Cloudflare;
private readonly mutex: Mutex;
private installs: Record<string, string[] | null> = {};
constructor(private readonly config: WorkersKVRESTConfig) {
this.mutex = new Mutex();
this.client = new Cloudflare({
apiToken: this.config.cfAuthToken
});
}
async increment(key: string, uid: string) {
return await this.mutex.runExclusive(async () => {
const existing = this.installs[key] || [];
const installsSet = Array.from(new Set([...existing, uid]));
await write(this.client, this.config, key, installsSet);
this.installs[key] = installsSet;
return installsSet.length;
});
}
async counts(keys: string[]): Promise<Record<string, number>> {
const result: Record<string, number> = {};
const installs = await readMulti(this.client, this.config, keys);
for (const [key, value] of Object.entries(installs)) {
result[key] = value?.length ?? 0;
}
this.installs = installs;
return result;
}
}
async function readMulti(
client: Cloudflare,
config: WorkersKVRESTConfig,
keys: string[]
): Promise<Record<string, string[] | null>> {
try {
const response = await client.kv.namespaces.bulkGet(config.namespaceId, {
account_id: config.cfAccountId,
keys,
type: "json",
withMetadata: false
});
const result: Record<string, string[]> = {};
for (const [key, value] of Object.entries(response?.values || {})) {
result[key] = value;
}
return result;
} catch (e) {
console.error(e);
return {};
}
}
function write<T>(
client: Cloudflare,
config: WorkersKVRESTConfig,
key: string,
data: T
) {
return client.kv.namespaces.values.update(config.namespaceId, key, {
account_id: config.cfAccountId,
value: JSON.stringify(data)
});
}

View File

@@ -16,7 +16,7 @@ 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 { Orama, SearchParams, create, search } from "@orama/orama";
import { Orama, SearchParams, create, search, update } from "@orama/orama";
import { CompiledThemeDefinition, ThemeMetadata } from "./sync";
import { ThemeQuerySchema } from "./schemas";
@@ -30,7 +30,8 @@ export async function initializeDatabase(): Promise<Orama> {
colorScheme: "string",
compatibilityVersion: "number",
description: "string",
tags: "string[]"
tags: "string[]",
totalInstalls: "number"
},
id: "notesnook-themes"
});
@@ -53,6 +54,14 @@ export async function findTheme(
return results.hits[0].document as CompiledThemeDefinition;
}
export async function updateTotalInstalls(
theme: CompiledThemeDefinition,
totalInstalls: number
) {
if (!ThemesDatabase) await initializeDatabase();
await update(ThemesDatabase!, theme.id, { ...theme, totalInstalls });
}
export async function getThemes(query: (typeof ThemeQuerySchema)["_type"]) {
if (!ThemesDatabase) await initializeDatabase();
@@ -60,6 +69,10 @@ export async function getThemes(query: (typeof ThemeQuerySchema)["_type"]) {
const count = query.limit;
const searchParams: SearchParams = {
sortBy: {
property: "totalInstalls",
order: "DESC"
},
where: {
compatibilityVersion: {
eq: query.compatibilityVersion
@@ -89,13 +102,15 @@ export async function getThemes(query: (typeof ThemeQuerySchema)["_type"]) {
}
}
const results = await search(ThemesDatabase!, searchParams);
const themes = results.hits.map((hit) => {
return {
...(hit.document as CompiledThemeDefinition),
const themes: ThemeMetadata[] = [];
for (const hit of results.hits) {
const theme = hit.document as CompiledThemeDefinition;
themes.push({
...theme,
scopes: undefined,
codeBlockCSS: undefined
} as ThemeMetadata;
});
} as ThemeMetadata);
}
return {
themes,

View File

@@ -0,0 +1,40 @@
/*
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 { readFileSync } from "node:fs";
export function readSecrets<T extends string>(
names: T[]
): Record<T, string | undefined> {
const result: Record<T, string | undefined> = {} as Record<
T,
string | undefined
>;
for (const name of names) result[name] = readSecret(name);
return result;
}
export function readSecret(name: string): string | undefined {
const value = process.env[name];
if (value) return value;
const file = process.env[`${name}_FILE`];
if (file) {
return readFileSync(file, "utf-8");
}
}

View File

@@ -27,10 +27,14 @@ const server = createHTTPServer({
router: ThemesAPI
});
const PORT = parseInt(process.env.PORT || "9000");
server.listen(PORT);
console.log(`Server started successfully on: http://localhost:${PORT}/`);
const HOST = process.env.HOST || "localhost";
server.listen(PORT, HOST);
console.log(`Server started successfully on: http://${HOST}:${PORT}/`);
syncThemes();
setInterval(() => {
syncThemes();
}, 1000 * 60 * 60); // every hour
if (import.meta.hot) {
import.meta.hot.on("vite:beforeFullReload", () => {

View File

@@ -17,7 +17,7 @@ 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 { execSync } from "child_process";
import fs from "fs";
import fs from "node:fs";
import { readdir, readFile } from "fs/promises";
import path from "path";
import {
@@ -70,7 +70,6 @@ async function generateThemesMetadata() {
const THEMES_PATH = path.join(THEME_REPO_DIR_PATH, "themes");
const themes = await readdir(THEMES_PATH);
const counts = await InstallsCounter.counts();
for (const themeId of themes) {
for (const version of THEME_COMPATIBILITY_VERSIONS) {
@@ -96,11 +95,17 @@ async function generateThemesMetadata() {
...theme,
sourceURL: `https://github.com/streetwriters/notesnook-themes/tree/main/themes/${themeId}/v${version}/`,
codeBlockCSS,
totalInstalls: counts[theme.id]?.length || 0,
totalInstalls: 0,
previewColors: getPreviewColors(theme)
});
}
}
const installs = await InstallsCounter.counts(
themeDefinitions.map((t) => t.id)
);
for (const theme of themeDefinitions) {
theme.totalInstalls = installs[theme.id] || 0;
}
await insertMultiple(db, themeDefinitions);
console.log("Metadata generated and cached.");
}