Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Export note #111

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public List<Package> getPackageList() {
new expo.modules.filesystem.FileSystemPackage(),
new expo.modules.font.FontLoaderPackage(),
new expo.modules.imageloader.ImageLoaderPackage(),
new expo.modules.intentlauncher.IntentLauncherPackage(),
new expo.modules.permissions.PermissionsPackage(),
new expo.modules.splashscreen.SplashScreenPackage(),
new expo.modules.taskManager.TaskManagerPackage(),
Expand Down
7 changes: 7 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,24 @@
"etebase": "^0.41.0",
"expo-background-fetch": "^8.6.0",
"expo-font": "^8.3.0",
"expo-intent-launcher": "^8.4.0",
"expo-splash-screen": "^0.6.1",
"expo-status-bar": "~1.0.2",
"expo-task-manager": "^8.6.0",
"expo-updates": "~0.3.2",
"file-saver": "^2.0.5",
"highlight-words-core": "^1.2.2",
"immutable": "^4.0.0-rc.12",
"js-yaml": "^4.0.0",
"jszip": "^3.6.0",
"localforage": "^1.9.0",
"minisearch": "^3.0.2",
"moment": "^2.29.0",
"react": "16.13.1",
"react-dom": "16.13.1",
"react-native": "0.63.4",
"react-native-etebase": "^0.1.5",
"react-native-fs": "^2.16.6",
"react-native-gesture-handler": "~1.9.0",
"react-native-get-random-values": "^1.5.0",
"react-native-keyboard-aware-scroll-view": "^0.9.2",
Expand All @@ -61,7 +66,9 @@
"@babel/core": "~7.9.0",
"@expo/webpack-config": "^0.12.53",
"@types/color": "^3.0.1",
"@types/file-saver": "^2.0.1",
"@types/highlight-words-core": "^1.2.0",
"@types/js-yaml": "^4.0.0",
"@types/markdown-it": "^12.0.0",
"@types/react": "~16.9.35",
"@types/react-dom": "~16.9.8",
Expand Down
1 change: 1 addition & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ const rootStackScreens: RootStackScreens = {
Settings: "settings",
About: "settings/about",
Password: "settings/password",
Export: "settings/export",
DebugLogs: "settings/logs",
AccountWizard: "account-wizard",
"404": "*",
Expand Down
8 changes: 8 additions & 0 deletions src/RootNavigator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import SettingsScreen from "./screens/SettingsScreen";
import AboutScreen from "./screens/AboutScreen";
import ChangePasswordScreen from "./screens/ChangePasswordScreen";
import DebugLogsScreen from "./screens/DebugLogsScreen";
import ExportScreen from "./screens/ExportScreen";
import HomeScreen from "./screens/HomeScreen";
import NoteEditScreen from "./screens/NoteEditScreen";
import NotePropertiesScreen from "./screens/NotePropertiesScreen";
Expand Down Expand Up @@ -158,6 +159,13 @@ export default React.memo(function RootNavigator() {
title: "Change Your Account Password",
}}
/>
<Stack.Screen
name="Export"
component={ExportScreen}
options={{
title: "Export Your Notes",
}}
/>
</>
)}
<Stack.Screen name="Settings" component={SettingsScreen} />
Expand Down
1 change: 1 addition & 0 deletions src/RootStackParamList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export type RootStackParamList = {
Invitations: undefined;
Settings: undefined;
Password: undefined;
Export: undefined;
About: undefined;
DebugLogs: undefined;
AccountWizard: undefined;
Expand Down
20 changes: 20 additions & 0 deletions src/import-export/export.android.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import * as Etebase from "etebase";
import * as IntentLauncher from "expo-intent-launcher";
import RNFS from "react-native-fs";
import { getItemsZip } from "./utils";

export function canExport() {
return true;
}

export async function exportItems(items: Etebase.Item[]) {
const content = await getItemsZip(items, "base64");
const result = await IntentLauncher.startActivityAsync("android.intent.action.CREATE_DOCUMENT", {
type: "application/zip",
category: "android.intent.category.OPENABLE",
extra: { "android.intent.extra.TITLE": "etesync-notes-export.zip" },
});
if (result.resultCode === IntentLauncher.ResultCode.Success && result?.data) {
await RNFS.writeFile(result.data, content, "base64");
}
}
19 changes: 19 additions & 0 deletions src/import-export/export.ios.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import * as Etebase from "etebase";
import { Share } from "react-native";
import RNFS from "react-native-fs";
import { getItemsZip } from "./utils";

export function canExport() {
return true;
}

export async function exportItems(items: Etebase.Item[]) {
const content = await getItemsZip(items, "base64");
const path = `${RNFS.CachesDirectoryPath}/etesync-notes-export.zip`;

await RNFS.writeFile(path, content, "base64");

await Share.share({ url: path });

await RNFS.unlink(path);
}
9 changes: 9 additions & 0 deletions src/import-export/export.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import * as Etebase from "etebase";

export function canExport() {
return false;
}

export async function exportItems(items: Etebase.Item[]) {
throw Error(`Cannot export ${items.length} items. Exporting is not implemented on this Platform`);
}
17 changes: 17 additions & 0 deletions src/import-export/export.web.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import * as Etebase from "etebase";
import FileSaver from "file-saver";
import { getItemsZip } from "./utils";

export function canExport() {
try {
const canBlob = !!new Blob;
return canBlob;
} catch (e) {
return false;
}
}

export async function exportItems(items: Etebase.Item[]) {
const blob = await getItemsZip(items, "blob");
FileSaver.saveAs(blob, "etesync-notes-export.zip");
}
1 change: 1 addition & 0 deletions src/import-export/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from "./export";
export * from "./share";
6 changes: 2 additions & 4 deletions src/import-export/share.native.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,9 @@ export function canShare() {

export async function shareItem(item: Etebase.Item) {
const { name, content } = await getItemData(item);

// Adding the name as a title at the beginning of the content because it is not shared otherwise
const message = `# ${name}\n\n${content}`;

await Share.share({
message,
message: content,
title: name,
});
}
35 changes: 29 additions & 6 deletions src/import-export/utils.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,33 @@
import * as Etebase from "etebase";
import YAML from "js-yaml";
import JSZip from "jszip";

export async function getItemData(item: Etebase.Item) {
const { name } = item.getMeta();
const content = await item.getContent(Etebase.OutputFormat.String);
return {
name,
content,
export async function getItemData(item: Etebase.Item, format = "") {
const data = {
name: item.getMeta().name,
content: "",
};
const content = await item.getContent(Etebase.OutputFormat.String);

switch (format) {
case "export": {
const frontmatter = YAML.dump({ title: data.name });
data.content = `---\n${frontmatter}---\n\n${content}`;
break;
}
default: {
data.content = `# ${data.name}\n\n${content}`;
}
}
return data;
}

export async function getItemsZip<T extends JSZip.OutputType>(items: Etebase.Item[], format: T) {
const zip = new JSZip();

for (const item of items) {
const itemData = await getItemData(item, "export");
zip.file(`${itemData.name?.replace(/[/\\]/g, "-")}.md`, itemData.content);
}
return zip.generateAsync<T>({ type: format });
}
Loading