Skip to content

Commit

Permalink
Add feature to export a note
Browse files Browse the repository at this point in the history
  • Loading branch information
Kévin Commaille committed Feb 22, 2021
1 parent 312ffbe commit 593b6d6
Show file tree
Hide file tree
Showing 15 changed files with 429 additions and 11 deletions.
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
9 changes: 9 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 @@ -44,6 +49,7 @@
"react-native-reanimated": "~1.13.0",
"react-native-safe-area-context": "3.1.4",
"react-native-screens": "~2.16.0",
"react-native-share": "^5.1.1",
"react-native-sodium": "^0.3.8",
"react-native-unimodules": "~0.12.0",
"react-native-vector-icons": "^7.1.0",
Expand All @@ -61,11 +67,14 @@
"@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",
"@types/react-native": "~0.63.2",
"@types/react-native-share": "^3.3.1",
"@types/react-native-vector-icons": "^6.4.6",
"@types/react-redux": "^7.1.9",
"@types/redux": "^3.6.0",
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");
}
}
16 changes: 16 additions & 0 deletions src/import-export/export.ios.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import * as Etebase from "etebase";
import Share from "react-native-share";
import { getItemsZip } from "./utils";

export function canExport() {
return true;
}

export async function exportItems(items: Etebase.Item[]) {
const content = await getItemsZip(items, "base64");

await Share.open({
url: `data:application/zip;base64,${content}`,
type: "application/zip",
});
}
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

0 comments on commit 593b6d6

Please sign in to comment.