mobile: prepare release v3.3.0

This commit is contained in:
Ammar Ahmed
2025-10-03 14:38:34 +05:00
parent 16b52e9633
commit 02e37c6ed4
29 changed files with 337 additions and 142 deletions

View File

@@ -33,34 +33,35 @@ import { getDatabaseKey } from "./encryption";
import "./logger"; import "./logger";
import { RNSqliteDriver } from "./sqlite.kysely"; import { RNSqliteDriver } from "./sqlite.kysely";
import { Storage } from "./storage"; import { Storage } from "./storage";
import SettingsService from "../../services/settings";
export async function setupDatabase(password?: string) { 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.88`;
// database.host({
// API_HOST: `${base}:5264`,
// AUTH_HOST: `${base}:8264`,
// SSE_HOST: `${base}:7264`,
// ISSUES_HOST: `${base}:2624`,
// SUBSCRIPTIONS_HOST: `${base}:9264`,
// MONOGRAPH_HOST: `${base}:6264`,
// NOTESNOOK_HOST: `${base}:8788`
// });
database.host({ database.host({
API_HOST: `${base}:5264`, API_HOST: "https://api.notesnook.com",
AUTH_HOST: `${base}:8264`, AUTH_HOST: "https://auth.streetwriters.co",
SSE_HOST: `${base}:7264`, SSE_HOST: "https://events.streetwriters.co",
ISSUES_HOST: `${base}:2624`, SUBSCRIPTIONS_HOST: "https://subscriptions.streetwriters.co",
SUBSCRIPTIONS_HOST: `${base}:9264`, ISSUES_HOST: "https://issues.streetwriters.co",
MONOGRAPH_HOST: `${base}:6264`, MONOGRAPH_HOST: "https://monogr.ph",
NOTESNOOK_HOST: `${base}:8788` NOTESNOOK_HOST: "https://notesnook.com",
...(SettingsService.getProperty("serverUrls") || {})
}); });
// database.host( {
// API_HOST: "https://api.notesnook.com",
// AUTH_HOST: "https://auth.streetwriters.co",
// SSE_HOST: "https://events.streetwriters.co",
// SUBSCRIPTIONS_HOST: "https://subscriptions.streetwriters.co",
// ISSUES_HOST: "https://issues.streetwriters.co",
// MONOGRAPH_HOST: "https://monogr.ph",
// NOTESNOOK_HOST: "https://notesnook.com",
// ...(SettingsService.getProperty("serverUrls") || {})
// });
database.setup({ database.setup({
storage: Storage, storage: Storage,
eventsource: (Platform.OS === "ios" eventsource: (Platform.OS === "ios"

View File

@@ -44,9 +44,8 @@ export const Header = React.memo(
}: ListHeaderProps) => { }: ListHeaderProps) => {
const { colors } = useThemeColors(); const { colors } = useThemeColors();
const announcements = useMessageStore((state) => state.announcements); const announcements = useMessageStore((state) => state.announcements);
const selectionMode = useSelectionStore((state) => state.selectionMode);
return selectionMode ? null : ( return (
<> <>
{announcements.length !== 0 && !noAnnouncement ? ( {announcements.length !== 0 && !noAnnouncement ? (
<Announcement color={color || colors.primary.accent} /> <Announcement color={color || colors.primary.accent} />

View File

@@ -371,8 +371,7 @@ export const Items = ({
style={{ style={{
flexDirection: "row", flexDirection: "row",
paddingHorizontal: DefaultAppStyles.GAP, paddingHorizontal: DefaultAppStyles.GAP,
gap: 5, gap: 5
width: width
}} }}
> >
{item.map(renderTopBarItem)} {item.map(renderTopBarItem)}

View File

@@ -87,7 +87,7 @@ export const SelectionHeader = React.memo(
const restoreItem = async () => { const restoreItem = async () => {
if (!selectedItemsList.length) return; if (!selectedItemsList.length) return;
if ((await db.trash.restore(...selectedItemsList)) === false) return; await db.trash.restore(...selectedItemsList);
Navigation.queueRoutesForUpdate(); Navigation.queueRoutesForUpdate();
clearSelection(); clearSelection();

View File

@@ -63,7 +63,6 @@ const SheetWrapper = ({
let width = dimensions.width > 600 ? 600 : 500; let width = dimensions.width > 600 ? 600 : 500;
const isGestureNavigationEnabled = const isGestureNavigationEnabled =
NotesnookModule.isGestureNavigationEnabled(); NotesnookModule.isGestureNavigationEnabled();
const style = React.useMemo(() => { const style = React.useMemo(() => {
return { return {
width: largeTablet || smallTablet ? width : "100%", width: largeTablet || smallTablet ? width : "100%",
@@ -78,8 +77,10 @@ const SheetWrapper = ({
...getContainerBorder(colors.primary.border, 0.5), ...getContainerBorder(colors.primary.border, 0.5),
borderBottomWidth: 0, borderBottomWidth: 0,
paddingBottom: paddingBottom:
isGestureNavigationEnabled && Platform.OS === "android" Platform.OS === "android"
? insets.bottom ? isGestureNavigationEnabled
? insets.bottom
: 30
: 0 : 0
}; };
}, [ }, [

View File

@@ -47,7 +47,6 @@ export const Archive = ({ navigation, route }: NavigationProps<"Archive">) => {
return ( return (
<> <>
<SelectionHeader id={route.name} items={archive} type="note" />
<Header <Header
renderedInRoute={route.name} renderedInRoute={route.name}
title={strings.routes[route.name]()} title={strings.routes[route.name]()}
@@ -81,6 +80,8 @@ export const Archive = ({ navigation, route }: NavigationProps<"Archive">) => {
headerTitle={strings.routes.Archive()} headerTitle={strings.routes.Archive()}
/> />
</DelayLayout> </DelayLayout>
<SelectionHeader id={route.name} items={archive} type="note" />
</> </>
); );
}; };

View File

@@ -50,7 +50,6 @@ export const Favorites = ({
return ( return (
<> <>
<SelectionHeader id={route.name} items={favorites} type="note" />
<Header <Header
renderedInRoute={route.name} renderedInRoute={route.name}
title={strings.routes[route.name]()} title={strings.routes[route.name]()}
@@ -84,6 +83,8 @@ export const Favorites = ({
headerTitle={strings.routes.Favorites()} headerTitle={strings.routes.Favorites()}
/> />
</DelayLayout> </DelayLayout>
<SelectionHeader id={route.name} items={favorites} type="note" />
</> </>
); );
}; };

View File

@@ -49,7 +49,6 @@ export const Home = ({ navigation, route }: NavigationProps<"Notes">) => {
return ( return (
<> <>
<SelectionHeader id={route.name} items={notes} type="note" />
<Header <Header
renderedInRoute={route.name} renderedInRoute={route.name}
title={strings.routes[route.name]()} title={strings.routes[route.name]()}
@@ -86,6 +85,7 @@ export const Home = ({ navigation, route }: NavigationProps<"Notes">) => {
<FloatingButton onPress={openEditor} alwaysVisible /> <FloatingButton onPress={openEditor} alwaysVisible />
)} )}
</DelayLayout> </DelayLayout>
<SelectionHeader id={route.name} items={notes} type="note" />
</> </>
); );
}; };

View File

@@ -300,7 +300,12 @@ const LinkNotebooks = (props: NavigationProps<"LinkNotebooks">) => {
/> />
{hasSelection ? ( {hasSelection ? (
<FloatingButton icon="check" alwaysVisible onPress={() => onSave()} /> <FloatingButton
testID="floating-save-button"
icon="check"
alwaysVisible
onPress={() => onSave()}
/>
) : null} ) : null}
</SafeAreaView> </SafeAreaView>
); );

View File

@@ -150,12 +150,6 @@ const NotebookScreen = ({ route, navigation }: NavigationProps<"Notebook">) => {
return ( return (
<> <>
<SelectionHeader
id={route.params?.item?.id}
items={notes}
type="note"
renderedInRoute="Notebook"
/>
<Header <Header
renderedInRoute={route.name} renderedInRoute={route.name}
title={params.current.item?.title} title={params.current.item?.title}
@@ -241,6 +235,12 @@ const NotebookScreen = ({ route, navigation }: NavigationProps<"Notebook">) => {
}} }}
/> />
</View> </View>
<SelectionHeader
id={route.params?.item?.id}
items={notes}
type="note"
renderedInRoute="Notebook"
/>
</> </>
); );
}; };

View File

@@ -187,12 +187,6 @@ const NotesPage = ({
return ( return (
<> <>
<SelectionHeader
id={route.params?.item?.id || route.name}
items={notes}
type="note"
renderedInRoute={route.name}
/>
<Header <Header
renderedInRoute={route.name} renderedInRoute={route.name}
title={ title={
@@ -243,6 +237,12 @@ const NotesPage = ({
/> />
) : null} ) : null}
</DelayLayout> </DelayLayout>
<SelectionHeader
id={route.params?.item?.id || route.name}
items={notes}
type="note"
renderedInRoute={route.name}
/>
</> </>
); );
}; };

View File

@@ -55,7 +55,6 @@ export const Reminders = ({
return ( return (
<> <>
<SelectionHeader id={route.name} items={reminders} type="reminder" />
<Header <Header
renderedInRoute={route.name} renderedInRoute={route.name}
title={strings.routes[route.name]()} title={strings.routes[route.name]()}
@@ -138,6 +137,8 @@ export const Reminders = ({
alwaysVisible alwaysVisible
/> />
</DelayLayout> </DelayLayout>
<SelectionHeader id={route.name} items={reminders} type="reminder" />
</> </>
); );
}; };

View File

@@ -141,12 +141,6 @@ export const Search = ({ route, navigation }: NavigationProps<"Search">) => {
return ( return (
<> <>
<SelectionHeader
id={route.name}
items={results}
type={route.params?.type}
renderedInRoute={route.name}
/>
<SearchBar <SearchBar
onChangeText={(query) => { onChangeText={(query) => {
clearTimeout(timer.current); clearTimeout(timer.current);
@@ -167,6 +161,12 @@ export const Search = ({ route, navigation }: NavigationProps<"Search">) => {
loading: strings.searchingFor(currentQuery.current as string) loading: strings.searchingFor(currentQuery.current as string)
}} }}
/> />
<SelectionHeader
id={route.name}
items={results}
type={route.params?.type}
renderedInRoute={route.name}
/>
</> </>
); );
}; };

View File

@@ -79,12 +79,6 @@ export const Trash = ({ navigation, route }: NavigationProps<"Trash">) => {
return ( return (
<> <>
<SelectionHeader
id={route.name}
items={trash}
type="trash"
renderedInRoute={route.name}
/>
<Header <Header
renderedInRoute={route.name} renderedInRoute={route.name}
title={route.name} title={route.name}
@@ -117,6 +111,12 @@ export const Trash = ({ navigation, route }: NavigationProps<"Trash">) => {
/> />
) : null} ) : null}
</DelayLayout> </DelayLayout>
<SelectionHeader
id={route.name}
items={trash}
type="trash"
renderedInRoute={route.name}
/>
</> </>
); );
}; };

View File

@@ -17,37 +17,33 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
import { Tests } from "./utils"; import { TestBuilder } from "./utils";
describe("APP LAUNCH AND NAVIGATION", () => { describe("APP LAUNCH AND NAVIGATION", () => {
it("App should launch successfully & hide welcome screen", async () => { it("App should launch successfully & hide welcome screen", async () => {
await Tests.prepare(); await TestBuilder.create().prepare().run();
}); });
it("Basic navigation should work", async () => { it("Basic navigation should work", async () => {
await Tests.prepare(); await TestBuilder.create()
await Tests.navigate("Favorites"); .prepare()
await Tests.navigate("Reminders"); .navigate("Favorites")
await Tests.navigate("Monographs"); .navigate("Reminders")
await Tests.navigate("Trash"); .navigate("Monographs")
await Tests.openSideMenu(); .navigate("Trash")
await Tests.fromId("sidemenu-settings-icon").waitAndTap(); .openSideMenu()
await Tests.fromText("Settings").waitAndTap(); .waitAndTapById("sidemenu-settings-icon")
await Tests.fromText("Settings").isVisible(); .wait(500)
await device.pressBack(); .waitAndTapByText("Settings")
.isVisibleByText("Settings")
await Tests.fromId("tab-notebooks").tap(); .pressBack(1)
await Tests.fromText("No notebooks").isVisible(); .tapById("tab-notebooks")
await Tests.fromId("tab-tags").tap(); .isVisibleByText("No notebooks")
await Tests.fromText("No tags").isVisible(); .tapById("tab-tags")
await Tests.fromId("tab-home").tap(); .isVisibleByText("No tags")
.tapById("tab-home")
await Tests.fromText("Notes").tap(); .tapByText("Notes")
await Tests.fromText("Search in Notes").isVisible(); .isVisibleByText("Search in Notes")
.run();
// await Tests.navigate("Tags");
// await Tests.navigate("Settings");
// await Tests.navigate("Notebooks");
}); });
}); });

View File

@@ -37,6 +37,7 @@ describe("NOTE TESTS", () => {
await Tests.prepare(); await Tests.prepare();
await Tests.createNote(); await Tests.createNote();
await Tests.fromId(notesnook.listitem.menu).waitAndTap(); await Tests.fromId(notesnook.listitem.menu).waitAndTap();
await Tests.sleep(500);
await Tests.fromText("Created at").isVisible(); await Tests.fromText("Created at").isVisible();
}); });
@@ -44,12 +45,16 @@ describe("NOTE TESTS", () => {
await Tests.prepare(); await Tests.prepare();
let note = await Tests.createNote(); let note = await Tests.createNote();
await Tests.fromId(notesnook.listitem.menu).waitAndTap(); await Tests.fromId(notesnook.listitem.menu).waitAndTap();
await Tests.sleep(500);
await Tests.fromId("icon-favorite").waitAndTap(); await Tests.fromId("icon-favorite").waitAndTap();
await device.pressBack();
await Tests.fromId("icon-star").isVisible(); await Tests.fromId("icon-star").isVisible();
await Tests.navigate("Favorites"); await Tests.navigate("Favorites");
await Tests.fromText(note.body).isVisible(); await Tests.fromText(note.body).isVisible();
await Tests.fromId(notesnook.listitem.menu).waitAndTap(); await Tests.fromId(notesnook.listitem.menu).waitAndTap();
await Tests.sleep(500);
await Tests.fromId("icon-favorite").waitAndTap(); await Tests.fromId("icon-favorite").waitAndTap();
await device.pressBack();
await Tests.fromText(note.body).isNotVisible(); await Tests.fromText(note.body).isNotVisible();
await Tests.navigate("Notes"); await Tests.navigate("Notes");
}); });
@@ -58,15 +63,19 @@ describe("NOTE TESTS", () => {
await Tests.prepare(); await Tests.prepare();
await Tests.createNote(); await Tests.createNote();
await Tests.fromId(notesnook.listitem.menu).waitAndTap(); await Tests.fromId(notesnook.listitem.menu).waitAndTap();
await Tests.sleep(500);
await Tests.fromId("icon-pin").waitAndTap(); await Tests.fromId("icon-pin").waitAndTap();
await device.pressBack();
await Tests.fromText("PINNED").isVisible(); await Tests.fromText("PINNED").isVisible();
await Tests.fromId("icon-pinned").isVisible(); await Tests.fromId("icon-pinned").isVisible();
await Tests.fromId(notesnook.listitem.menu).waitAndTap(); await Tests.fromId(notesnook.listitem.menu).waitAndTap();
await Tests.sleep(500);
await Tests.fromId("icon-pin").waitAndTap(); await Tests.fromId("icon-pin").waitAndTap();
await device.pressBack();
await Tests.fromText("icon-pinned").isNotVisible(); await Tests.fromText("icon-pinned").isNotVisible();
}); });
it("Pin a note in notifications", async () => { it.skip("Pin a note in notifications", async () => {
await Tests.prepare(); await Tests.prepare();
await Tests.createNote(); await Tests.createNote();
await Tests.fromId(notesnook.listitem.menu).waitAndTap(); await Tests.fromId(notesnook.listitem.menu).waitAndTap();
@@ -80,6 +89,7 @@ describe("NOTE TESTS", () => {
await Tests.prepare(); await Tests.prepare();
await Tests.createNote(); await Tests.createNote();
await Tests.fromId(notesnook.listitem.menu).waitAndTap(); await Tests.fromId(notesnook.listitem.menu).waitAndTap();
await Tests.sleep(500);
await Tests.fromId("icon-copy").isVisible(); await Tests.fromId("icon-copy").isVisible();
await Tests.fromId("icon-copy").waitAndTap(); await Tests.fromId("icon-copy").waitAndTap();
}); });
@@ -88,6 +98,7 @@ describe("NOTE TESTS", () => {
await Tests.prepare(); await Tests.prepare();
let note = await Tests.createNote(); let note = await Tests.createNote();
await Tests.fromId(notesnook.listitem.menu).waitAndTap(); await Tests.fromId(notesnook.listitem.menu).waitAndTap();
await Tests.sleep(500);
await Tests.fromText("Add color").waitAndTap(); await Tests.fromText("Add color").waitAndTap();
await Tests.fromId("color-title-input").element.typeText("Test color"); await Tests.fromId("color-title-input").element.typeText("Test color");
await Tests.fromText("Add color").waitAndTap(); await Tests.fromText("Add color").waitAndTap();
@@ -100,13 +111,15 @@ describe("NOTE TESTS", () => {
await Tests.fromText(note.body).isVisible(); await Tests.fromText(note.body).isVisible();
}); });
it.only("Delete & restore a note", async () => { it("Delete & restore a note", async () => {
await Tests.prepare(); await Tests.prepare();
await Tests.createNote(); await Tests.createNote();
await Tests.fromId(notesnook.listitem.menu).waitAndTap(); await Tests.fromId(notesnook.listitem.menu).waitAndTap();
await Tests.sleep(500);
await Tests.fromId("icon-trash").waitAndTap(); await Tests.fromId("icon-trash").waitAndTap();
await Tests.navigate("Trash"); await Tests.navigate("Trash");
await Tests.fromId(notesnook.listitem.menu).waitAndTap(); await Tests.fromId(notesnook.listitem.menu).waitAndTap();
await Tests.sleep(500);
await Tests.fromText("Restore").waitAndTap(); await Tests.fromText("Restore").waitAndTap();
await device.pressBack(); await device.pressBack();
await Tests.fromText( await Tests.fromText(

View File

@@ -27,7 +27,7 @@ describe("NOTEBOOKS", () => {
await Tests.fromId("tab-notebooks").waitAndTap(); await Tests.fromId("tab-notebooks").waitAndTap();
await Tests.fromId("sidebar-add-button").waitAndTap(); await Tests.fromId("sidebar-add-button").waitAndTap();
await Tests.createNotebook("Notebook 1", false); await Tests.createNotebook("Notebook 1", false);
await device.pressBack(); await Tests.sleep(500);
await Tests.fromText("Notebook 1").isVisible(); await Tests.fromText("Notebook 1").isVisible();
}); });
@@ -37,39 +37,26 @@ describe("NOTEBOOKS", () => {
await Tests.fromId("tab-notebooks").waitAndTap(); await Tests.fromId("tab-notebooks").waitAndTap();
await Tests.fromId("sidebar-add-button").waitAndTap(); await Tests.fromId("sidebar-add-button").waitAndTap();
await Tests.createNotebook("Notebook 1", true); await Tests.createNotebook("Notebook 1", true);
await device.pressBack(); await Tests.sleep(500);
await Tests.fromText("Notebook 1").isVisible(); await Tests.fromText("Notebook 1").isVisible();
}); });
it("Create a notebook, move notes", async () => {
await Tests.prepare();
let note = await Tests.createNote();
await Tests.openSideMenu();
await Tests.fromId("tab-notebooks").waitAndTap();
await Tests.fromId("sidebar-add-button").waitAndTap();
await Tests.createNotebook("Notebook 1", true);
await Tests.fromId("listitem.select").waitAndTap();
await Tests.fromText("Move selected notes").waitAndTap();
await Tests.fromText("Notebook 1").waitAndTap();
await Tests.fromText(note.body).isVisible();
});
it("Add a sub notebook to a notebook", async () => { it("Add a sub notebook to a notebook", async () => {
await Tests.prepare(); await Tests.prepare();
await Tests.openSideMenu(); await Tests.openSideMenu();
await Tests.fromId("tab-notebooks").waitAndTap(); await Tests.fromId("tab-notebooks").waitAndTap();
await Tests.fromId("sidebar-add-button").waitAndTap(); await Tests.fromId("sidebar-add-button").waitAndTap();
await Tests.createNotebook("Notebook 1", true); await Tests.createNotebook("Notebook 1", true);
await device.pressBack(); await Tests.sleep(500);
await Tests.fromText("Notebook 1").element.longPress(); await Tests.fromText("Notebook 1").element.longPress();
await Tests.sleep(500);
await Tests.fromText("Add notebook").waitAndTap(); await Tests.fromText("Add notebook").waitAndTap();
await Tests.createNotebook("Sub notebook", true); await Tests.createNotebook("Sub notebook", true);
await device.pressBack();
await Tests.sleep(500); await Tests.sleep(500);
await Tests.fromId("expand-notebook-0").waitAndTap(); await Tests.fromId("expand-notebook-0").waitAndTap();
await Tests.fromText("Sub notebook").isVisible(); await Tests.fromText("Sub notebook").isVisible();
await Tests.fromText("Sub notebook").element.longPress(); await Tests.fromText("Sub notebook").element.longPress();
await Tests.sleep(500);
await Tests.fromText("Move to trash").waitAndTap(); await Tests.fromText("Move to trash").waitAndTap();
await Tests.fromText("Delete").waitAndTap(); await Tests.fromText("Delete").waitAndTap();
await Tests.fromText("Sub notebook").isNotVisible(); await Tests.fromText("Sub notebook").isNotVisible();
@@ -81,9 +68,9 @@ describe("NOTEBOOKS", () => {
await Tests.fromId("tab-notebooks").waitAndTap(); await Tests.fromId("tab-notebooks").waitAndTap();
await Tests.fromId("sidebar-add-button").waitAndTap(); await Tests.fromId("sidebar-add-button").waitAndTap();
await Tests.createNotebook("Notebook 1", true); await Tests.createNotebook("Notebook 1", true);
await device.pressBack();
await Tests.sleep(500); await Tests.sleep(500);
await Tests.fromText("Notebook 1").element.longPress(); await Tests.fromText("Notebook 1").element.longPress();
await Tests.sleep(500);
await Tests.fromText("Edit notebook").waitAndTap(); await Tests.fromText("Edit notebook").waitAndTap();
await Tests.fromId( await Tests.fromId(
notesnook.ids.dialogs.notebook.inputs.title notesnook.ids.dialogs.notebook.inputs.title
@@ -98,15 +85,14 @@ describe("NOTEBOOKS", () => {
await Tests.openSideMenu(); await Tests.openSideMenu();
await Tests.fromId("tab-notebooks").waitAndTap(); await Tests.fromId("tab-notebooks").waitAndTap();
await Tests.fromId("sidebar-add-button").waitAndTap(); await Tests.fromId("sidebar-add-button").waitAndTap();
await Tests.createNotebook("Notebook 1", true); await Tests.createNotebook("Notebook 1", true);
await device.pressBack(); await Tests.sleep(500);
await Tests.fromText("Notebook 1").element.longPress(); await Tests.fromText("Notebook 1").element.longPress();
await Tests.sleep(500);
await Tests.fromText("Add notebook").waitAndTap(); await Tests.fromText("Add notebook").waitAndTap();
await Tests.createNotebook("Sub notebook", true); await Tests.createNotebook("Sub notebook", true);
await device.pressBack();
await Tests.sleep(500); await Tests.sleep(500);
await Tests.fromId("expand-notebook-0").waitAndTap(); await Tests.fromId("expand-notebook-0").waitAndTap();
@@ -128,7 +114,7 @@ describe("NOTEBOOKS", () => {
await Tests.fromId("sidebar-add-button").waitAndTap(); await Tests.fromId("sidebar-add-button").waitAndTap();
await Tests.createNotebook("Notebook 1", true); await Tests.createNotebook("Notebook 1", true);
await device.pressBack(); await Tests.sleep(500);
await Tests.fromText("Notebook 1").waitAndTap(); await Tests.fromText("Notebook 1").waitAndTap();
await Tests.createNote(); await Tests.createNote();
@@ -142,12 +128,13 @@ describe("NOTEBOOKS", () => {
await Tests.fromId("sidebar-add-button").waitAndTap(); await Tests.fromId("sidebar-add-button").waitAndTap();
await Tests.createNotebook("Notebook 1", true); await Tests.createNotebook("Notebook 1", true);
await device.pressBack(); await Tests.sleep(500);
await Tests.fromText("Notebook 1").waitAndTap(); await Tests.fromText("Notebook 1").waitAndTap();
await Tests.sleep(500); await Tests.sleep(500);
let note = await Tests.createNote(); let note = await Tests.createNote();
await Tests.fromText(note.body).element.longPress(); await Tests.fromText(note.body).element.longPress();
await Tests.sleep(500);
await Tests.fromId("select-minus").waitAndTap(); await Tests.fromId("select-minus").waitAndTap();
await Tests.fromId(note.title).isNotVisible(); await Tests.fromId(note.title).isNotVisible();
@@ -161,7 +148,7 @@ describe("NOTEBOOKS", () => {
await Tests.fromId("sidebar-add-button").waitAndTap(); await Tests.fromId("sidebar-add-button").waitAndTap();
await Tests.createNotebook("Notebook 1", true); await Tests.createNotebook("Notebook 1", true);
await device.pressBack(); await Tests.sleep(500);
await Tests.fromId("tab-home").waitAndTap(); await Tests.fromId("tab-home").waitAndTap();
await Tests.fromText("Notes").waitAndTap(); await Tests.fromText("Notes").waitAndTap();
@@ -174,7 +161,7 @@ describe("NOTEBOOKS", () => {
await Tests.fromId("icon-notebooks").waitAndTap(); await Tests.fromId("icon-notebooks").waitAndTap();
await Tests.fromText("Notebook 1").waitAndTap(); await Tests.fromText("Notebook 1").waitAndTap();
await Tests.fromText("Save").waitAndTap(); await Tests.fromId("floating-save-button").waitAndTap();
await Tests.fromText("Notebook 1").isVisible(); await Tests.fromText("Notebook 1").isVisible();
}); });
@@ -187,7 +174,7 @@ describe("NOTEBOOKS", () => {
await Tests.createNotebook(); await Tests.createNotebook();
await device.pressBack(); await Tests.sleep(500);
await Tests.fromText("Notebook 1").isVisible(); await Tests.fromText("Notebook 1").isVisible();
@@ -206,7 +193,6 @@ describe("NOTEBOOKS", () => {
).element.typeText("Description of Notebook 1 (Edited)"); ).element.typeText("Description of Notebook 1 (Edited)");
await Tests.fromText("Save").waitAndTap(); await Tests.fromText("Save").waitAndTap();
await Tests.fromText("Notebook 1 (Edited)").isVisible(); await Tests.fromText("Notebook 1 (Edited)").isVisible();
// await Tests.fromText("Description of Notebook 1 (Edited)").isVisible();
}); });
it("Move notebook to trash", async () => { it("Move notebook to trash", async () => {
@@ -217,7 +203,7 @@ describe("NOTEBOOKS", () => {
await Tests.fromId("sidebar-add-button").waitAndTap(); await Tests.fromId("sidebar-add-button").waitAndTap();
await Tests.createNotebook("Notebook 1", false); await Tests.createNotebook("Notebook 1", false);
await device.pressBack(); await Tests.sleep(500);
await Tests.fromText("Notebook 1").isVisible(); await Tests.fromText("Notebook 1").isVisible();
await Tests.fromText("Notebook 1").element.longPress(); await Tests.fromText("Notebook 1").element.longPress();
@@ -232,7 +218,7 @@ describe("NOTEBOOKS", () => {
await Tests.fromText("Notebook 1").isVisible(); await Tests.fromText("Notebook 1").isVisible();
}); });
it("Move notebook to trash with notes", async () => { it.skip("Move notebook to trash with notes", async () => {
await Tests.prepare(); await Tests.prepare();
let note = await Tests.createNote(); let note = await Tests.createNote();
@@ -241,8 +227,18 @@ describe("NOTEBOOKS", () => {
await Tests.fromId("sidebar-add-button").waitAndTap(); await Tests.fromId("sidebar-add-button").waitAndTap();
await Tests.createNotebook("Notebook 1", false); await Tests.createNotebook("Notebook 1", false);
await Tests.fromId("listitem.select").waitAndTap(); await device.pressBack();
await Tests.fromText("Move selected notes").waitAndTap(); await Tests.fromId(notesnook.ids.note.get(0)).element.longPress();
await Tests.sleep(500);
await Tests.fromId("select-plus").waitAndTap();
await Tests.fromText("Notebook 1").isVisible();
await Tests.fromText("Notebook 1").waitAndTap();
await Tests.fromId("floating-save-button").waitAndTap();
await Tests.sleep(500);
await Tests.openSideMenu();
await Tests.fromId("tab-home").waitAndTap();
await Tests.fromId("tab-notebooks").waitAndTap();
await Tests.sleep(500);
await Tests.fromText("Notebook 1").isVisible(); await Tests.fromText("Notebook 1").isVisible();
await Tests.fromText("Notebook 1").element.longPress(); await Tests.fromText("Notebook 1").element.longPress();
await Tests.sleep(500); await Tests.sleep(500);
@@ -269,7 +265,7 @@ describe("NOTEBOOKS", () => {
await Tests.createNotebook("Notebook 1", false); await Tests.createNotebook("Notebook 1", false);
await device.pressBack(); await Tests.sleep(500);
await Tests.fromText("Notebook 1").isVisible(); await Tests.fromText("Notebook 1").isVisible();

View File

@@ -25,9 +25,7 @@ describe("Search", () => {
let note = await Tests.createNote(); let note = await Tests.createNote();
await Tests.fromId("search-header").waitAndTap(); await Tests.fromId("search-header").waitAndTap();
await Tests.fromId("search-input").element.typeText("Test"); await Tests.fromId("search-input").element.typeText("Test");
await Tests.fromText(note.body).waitAndTap(); await Tests.sleep(1000);
await device.pressBack(); await Tests.fromText("1").isVisible();
await device.pressBack();
await Tests.fromText(note.body).isVisible();
}); });
}); });

View File

@@ -22,6 +22,7 @@ import { Tests } from "./utils";
async function sortBy(sorting: string) { async function sortBy(sorting: string) {
await Tests.fromId("icon-sort").waitAndTap(); await Tests.fromId("icon-sort").waitAndTap();
await Tests.sleep(500);
await Tests.fromText(sorting).waitAndTap(); await Tests.fromText(sorting).waitAndTap();
await device.pressBack(); await device.pressBack();
} }
@@ -38,12 +39,12 @@ describe("Sort & filter", () => {
.element(by.web.className("ProseMirror")) .element(by.web.className("ProseMirror"))
.typeText("Edited ", true); .typeText("Edited ", true);
await device.pressBack(); await device.pressBack();
await device.pressBack();
await sortBy("Date created"); await sortBy("Date created");
await Tests.fromId(notesnook.listitem.menu).waitAndTap(); await Tests.fromId(notesnook.listitem.menu).waitAndTap();
await device.pressBack(); await device.pressBack();
await sortBy("Date edited"); await sortBy("Date edited");
await Tests.fromId(notesnook.listitem.menu).waitAndTap(); await Tests.fromId(notesnook.listitem.menu).waitAndTap();
await Tests.sleep(500);
await device.pressBack(); await device.pressBack();
}); });

View File

@@ -72,6 +72,7 @@ class Element {
const Tests = { const Tests = {
awaitLaunch: async () => { awaitLaunch: async () => {
await device.disableSynchronization();
await waitFor(element(by.id(notesnook.ids.default.root))) await waitFor(element(by.id(notesnook.ids.default.root)))
.toBeVisible() .toBeVisible()
//@ts-ignore //@ts-ignore
@@ -147,4 +148,170 @@ const Tests = {
} }
}; };
export { Element, Tests }; class TestBuilder {
private steps: (() => Promise<void> | void)[] = [];
private result: any;
constructor() {}
addStep(step: () => Promise<void> | void) {
this.steps.push(step);
return this;
}
awaitLaunch() {
return this.addStep(async () => {
this.result = await Tests.awaitLaunch();
});
}
wait(duration: number) {
return this.addStep(() => {
this.result = Tests.sleep(duration);
});
}
fromId(id: string) {
return this.addStep(() => {
this.result = Tests.fromId(id);
});
}
fromText(text: string) {
return this.addStep(() => {
this.result = Tests.fromText(text);
});
}
exitEditor() {
return this.addStep(async () => {
this.result = await Tests.exitEditor();
});
}
createNote(title?: string, body?: string) {
return this.addStep(async () => {
this.result = await Tests.createNote(title, body);
});
}
navigate(screen: RouteName | ({} & string)) {
return this.addStep(async () => {
this.result = await Tests.navigate(screen);
});
}
openSideMenu() {
return this.addStep(async () => {
this.result = await Tests.openSideMenu();
});
}
prepare() {
return this.addStep(async () => {
this.result = await Tests.prepare();
});
}
createNotebook(title = "Notebook 1", description = true) {
return this.addStep(async () => {
this.result = await Tests.createNotebook(title, description);
});
}
matchSnapshot(element: Element, name: string) {
return this.addStep(async () => {
this.result = await Tests.matchSnapshot(element, name);
});
}
isVisibleById(id: string, timeout?: number) {
return this.addStep(async () => {
const element = new Element("id", id);
this.result = await element.isVisible(timeout);
});
}
isVisibleByText(text: string, timeout?: number) {
return this.addStep(async () => {
const element = new Element("text", text);
this.result = await element.isVisible(timeout);
});
}
isNotVisibleById(id: string, timeout?: number) {
return this.addStep(async () => {
const element = new Element("id", id);
this.result = await element.isNotVisible(timeout);
});
}
isNotVisibleByText(text: string, timeout?: number) {
return this.addStep(async () => {
const element = new Element("text", text);
this.result = await element.isNotVisible(timeout);
});
}
waitAndTapById(id: string, timeout?: number) {
return this.addStep(async () => {
const element = new Element("id", id);
this.result = await element.waitAndTap(timeout);
});
}
waitAndTapByText(text: string, timeout?: number) {
return this.addStep(async () => {
const element = new Element("text", text);
this.result = await element.waitAndTap(timeout);
});
}
tapById(id: string, point?: Detox.Point2D) {
return this.addStep(async () => {
const element = new Element("id", id);
this.result = await element.tap(point);
});
}
tapByText(text: string, point?: Detox.Point2D) {
return this.addStep(async () => {
const element = new Element("text", text);
this.result = await element.tap(point);
});
}
processResult(callback: (result: any) => void) {
return this.addStep(async () => {
if (this.result !== undefined) {
callback(this.result);
this.result = undefined; // Clear the result after processing
} else {
throw new Error("No result to process.");
}
});
}
pressBack(count = 1) {
return this.addStep(async () => {
for (let i = 0; i < count; i++) {
await device.pressBack();
}
});
}
async run() {
for (const step of this.steps) {
const result = step();
if (result instanceof Promise) {
await result;
}
}
this.steps = []; // Clear steps after execution
}
static create() {
return new TestBuilder();
}
}
export { Element, Tests, TestBuilder };

View File

@@ -56,8 +56,10 @@ async function openLockedNote(pwd?: string) {
} }
async function goToPrivacySecuritySettings() { async function goToPrivacySecuritySettings() {
await Tests.navigate("Settings"); await Tests.openSideMenu();
await Tests.fromId("sidemenu-settings-icon").waitAndTap();
await Tests.sleep(300); await Tests.sleep(300);
await Tests.fromText("Settings").waitAndTap();
await Tests.fromText("Vault").waitAndTap(); await Tests.fromText("Vault").waitAndTap();
} }
@@ -91,6 +93,7 @@ describe("VAULT", () => {
await Tests.fromText("Change").waitAndTap(); await Tests.fromText("Change").waitAndTap();
await device.pressBack(); await device.pressBack();
await device.pressBack(); await device.pressBack();
await device.pressBack();
await Tests.sleep(500); await Tests.sleep(500);
await openLockedNote("2362"); await openLockedNote("2362");
}); });
@@ -109,6 +112,7 @@ describe("VAULT", () => {
await Tests.fromText("Create vault").isVisible(); await Tests.fromText("Create vault").isVisible();
await device.pressBack(); await device.pressBack();
await device.pressBack(); await device.pressBack();
await device.pressBack();
await Tests.fromId(notesnook.listitem.menu).isVisible(); await Tests.fromId(notesnook.listitem.menu).isVisible();
}); });
@@ -127,6 +131,7 @@ describe("VAULT", () => {
await Tests.fromText("Create vault").isVisible(); await Tests.fromText("Create vault").isVisible();
await device.pressBack(); await device.pressBack();
await device.pressBack(); await device.pressBack();
await device.pressBack();
await Tests.fromId(notesnook.listitem.menu).isNotVisible(); await Tests.fromId(notesnook.listitem.menu).isNotVisible();
}); });

View File

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

View File

@@ -65,7 +65,6 @@
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:label="@string/app_name" android:label="@string/app_name"
android:largeHeap="true" android:largeHeap="true"
android:networkSecurityConfig="@xml/network_security_config"
android:requestLegacyExternalStorage="true" android:requestLegacyExternalStorage="true"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/BootTheme"> android:theme="@style/BootTheme">

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application android:networkSecurityConfig="@xml/network_security_config">
</application>
</manifest>

View File

@@ -1,5 +1,5 @@
- Improved editor UI for better usability - We have launches new pricing plans, Essential, Pro and Believer
- Improved UX for creating and editing reminders - Free users can now upload attachments and create private vault
- Minor bug fixes and improvements - Minor bug fixes and improvements
Thank you for using Notesnook! Thank you for using Notesnook!

View File

@@ -1089,7 +1089,7 @@
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2147; CURRENT_PROJECT_VERSION = 2148;
DEVELOPMENT_TEAM = 53CWBG3QUC; DEVELOPMENT_TEAM = 53CWBG3QUC;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64; "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1163,7 +1163,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 3.2.12; MARKETING_VERSION = 3.3.0;
OTHER_LDFLAGS = ( OTHER_LDFLAGS = (
"$(inherited)", "$(inherited)",
"-ObjC", "-ObjC",
@@ -1194,7 +1194,7 @@
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual; CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 2147; CURRENT_PROJECT_VERSION = 2148;
DEVELOPMENT_TEAM = ""; DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64; "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1268,7 +1268,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 3.2.12; MARKETING_VERSION = 3.3.0;
ONLY_ACTIVE_ARCH = NO; ONLY_ACTIVE_ARCH = NO;
OTHER_LDFLAGS = ( OTHER_LDFLAGS = (
"$(inherited)", "$(inherited)",
@@ -1427,7 +1427,7 @@
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2147; CURRENT_PROJECT_VERSION = 2148;
DEBUG_INFORMATION_FORMAT = dwarf; DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = 53CWBG3QUC; DEVELOPMENT_TEAM = 53CWBG3QUC;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64; "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1439,7 +1439,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 3.2.12; MARKETING_VERSION = 3.3.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget; PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
@@ -1470,7 +1470,7 @@
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual; CODE_SIGN_STYLE = Manual;
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 2147; CURRENT_PROJECT_VERSION = 2148;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = ""; DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
@@ -1483,7 +1483,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 3.2.12; MARKETING_VERSION = 3.3.0;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget; PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.notewidget;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
@@ -1513,7 +1513,7 @@
CODE_SIGN_ENTITLEMENTS = "Make Note/Make Note.entitlements"; CODE_SIGN_ENTITLEMENTS = "Make Note/Make Note.entitlements";
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2147; CURRENT_PROJECT_VERSION = 2148;
DEBUG_INFORMATION_FORMAT = dwarf; DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = 53CWBG3QUC; DEVELOPMENT_TEAM = 53CWBG3QUC;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64; "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
@@ -1594,7 +1594,7 @@
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift$(inherited)"; LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift$(inherited)";
MARKETING_VERSION = 3.2.12; MARKETING_VERSION = 3.3.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share; PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
@@ -1625,7 +1625,7 @@
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual; CODE_SIGN_STYLE = Manual;
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 2147; CURRENT_PROJECT_VERSION = 2148;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = ""; DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = 53CWBG3QUC;
@@ -1707,7 +1707,7 @@
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift$(inherited)"; LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift$(inherited)";
MARKETING_VERSION = 3.2.12; MARKETING_VERSION = 3.3.0;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share; PRODUCT_BUNDLE_IDENTIFIER = org.streetwriters.notesnook.share;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";

View File

@@ -1,7 +1,7 @@
const isGithubRelease = false; const isGithubRelease = false;
const config = { const config = {
// commands: require('@callstack/repack/commands/rspack'), commands: require('@callstack/repack/commands/rspack'),
project: { project: {
android: { android: {
sourceDir: './android' sourceDir: './android'

View File

@@ -1,6 +1,6 @@
{ {
"name": "@notesnook/mobile", "name": "@notesnook/mobile",
"version": "3.2.12", "version": "3.3.0",
"private": true, "private": true,
"license": "GPL-3.0-or-later", "license": "GPL-3.0-or-later",
"workspaces": [ "workspaces": [

View File

@@ -0,0 +1,5 @@
- We have launches new pricing plans, Essential, Pro and Believer
- Free users can now upload attachments and create private vault
- Minor bug fixes and improvements
Thank you for using Notesnook!