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 Jan 18, 2021
1 parent e91b01d commit 558defe
Show file tree
Hide file tree
Showing 11 changed files with 160 additions and 14 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
6 changes: 6 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,22 @@
"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",
"immutable": "^4.0.0-rc.12",
"js-yaml": "^4.0.0",
"localforage": "^1.9.0",
"moment": "^2.29.0",
"react": "16.13.1",
"react-dom": "16.13.1",
"react-native": "0.63.4",
"react-native-appearance": "~0.3.3",
"react-native-etebase": "^0.1.5",
"react-native-fs": "^2.16.6",
"react-native-gesture-handler": "~1.7.0",
"react-native-get-random-values": "^1.5.0",
"react-native-keyboard-aware-scroll-view": "^0.9.2",
Expand All @@ -58,6 +62,8 @@
"devDependencies": {
"@babel/core": "~7.9.0",
"@expo/webpack-config": "^0.12.53",
"@types/file-saver": "^2.0.1",
"@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
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 { getItemData } from "./utils";

export function canExport() {
return true;
}

export async function exportItem(item: Etebase.Item) {
const { name, content } = await getItemData(item, "export");
const result = await IntentLauncher.startActivityAsync("android.intent.action.CREATE_DOCUMENT", {
type: "text/markdown",
category: "android.intent.category.OPENABLE",
extra: { "android.intent.extra.TITLE": `${name}.md` },
});
if (result.resultCode === IntentLauncher.ResultCode.Success && result?.data) {
await RNFS.writeFile(result.data, content);
}
}
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";
import { getItemData } from "./utils";

export function canExport() {
return true;
}

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

await Share.share({
message: content,
title: name,
});
}
10 changes: 10 additions & 0 deletions src/import-export/export.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import * as Etebase from "etebase";

export function canExport() {
return false;
}

export async function exportItem(item: Etebase.Item) {
const { name } = item.getMeta();
throw Error(`Cannot export item "${name}". Exporting is not implemented on this Platform`);
}
18 changes: 18 additions & 0 deletions src/import-export/export.web.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import * as Etebase from "etebase";
import FileSaver from "file-saver";
import { getItemData } from "./utils";

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

export async function exportItem(item: Etebase.Item) {
const { name, content } = await getItemData(item, "export");
const blob = new Blob([content], { type: "text/markdown;charset=utf-8" });
FileSaver.saveAs(blob, `${name}.md`);
}
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,
});
}
24 changes: 18 additions & 6 deletions src/import-export/utils.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,22 @@
import * as Etebase from "etebase";
import YAML from "js-yaml";

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;
}
22 changes: 18 additions & 4 deletions src/screens/NoteEditScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import ConfirmationDialog from "../widgets/ConfirmationDialog";
import NotFound from "../widgets/NotFound";
import { fontFamilies } from "../helpers";
import { RootStackParamList } from "../RootStackParamList";
import { canShare, shareItem } from "../import-export";
import { canExport, exportItem, canShare, shareItem } from "../import-export";

type NavigationProp = StackNavigationProp<RootStackParamList, "NoteEdit">;

Expand Down Expand Up @@ -88,6 +88,7 @@ export default function NoteEditScreen(props: PropsType) {
onEdit={() => navigation.navigate("NoteProps", { colUid, itemUid })}
onDelete={() => setNoteDeleteDialogShow(true)}
onShare={onShare}
onExport={() => onShare(false)}
changed={changed}
/>
),
Expand Down Expand Up @@ -171,13 +172,17 @@ export default function NoteEditScreen(props: PropsType) {
}));
}

function onShare() {
function onShare(share = true) {
(async () => {
const colMgr = etebase.getCollectionManager();
const col = colMgr.cacheLoad(cacheCollections.get(colUid)!.cache);
const itemMgr = colMgr.getItemManager(col);
const item = itemMgr.cacheLoad(cacheItem!.cache);
await shareItem(item);
if (share) {
await shareItem(item);
} else {
await exportItem(item);
}
})();
}

Expand Down Expand Up @@ -246,9 +251,10 @@ interface RightActionViewProps {
onSave: () => void;
onDelete: () => void;
onShare: () => void;
onExport: () => void;
}

function RightAction({ viewMode, setViewMode, onSave, onEdit, onDelete, onShare, changed }: RightActionViewProps) {
function RightAction({ viewMode, setViewMode, onSave, onEdit, onDelete, onShare, onExport, changed }: RightActionViewProps) {
const [showMenu, setShowMenu] = React.useState(false);

return (
Expand Down Expand Up @@ -290,6 +296,14 @@ function RightAction({ viewMode, setViewMode, onSave, onEdit, onDelete, onShare,
}}
/>
) : null}
{(canExport()) ? (
<Menu.Item icon="export" title="Export"
onPress={() => {
setShowMenu(false);
onExport();
}}
/>
) : null}
</Menu>
</View>
);
Expand Down
50 changes: 50 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2829,6 +2829,11 @@
dependencies:
"@babel/types" "^7.3.0"

"@types/file-saver@^2.0.1":
version "2.0.1"
resolved "https://registry.yarnpkg.com/@types/file-saver/-/file-saver-2.0.1.tgz#e18eb8b069e442f7b956d313f4fadd3ef887354e"
integrity sha512-g1QUuhYVVAamfCifK7oB7G3aIl4BbOyzDOqVyUfEr4tfBKrXfeH+M+Tg7HKCXSrbzxYdhyCP7z9WbKo0R2hBCw==

"@types/glob@^7.1.1":
version "7.1.3"
resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.3.tgz#e6ba80f36b7daad2c685acd9266382e68985c183"
Expand Down Expand Up @@ -2887,6 +2892,11 @@
"@types/istanbul-lib-coverage" "*"
"@types/istanbul-lib-report" "*"

"@types/js-yaml@^4.0.0":
version "4.0.0"
resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-4.0.0.tgz#d1a11688112091f2c711674df3a65ea2f47b5dfb"
integrity sha512-4vlpCM5KPCL5CfGmTbpjwVKbISRYhduEJvvUWsH5EB7QInhEj94XPZ3ts/9FPiLZFqYO0xoW4ZL8z2AabTGgJA==

"@types/json-schema@^7.0.3", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.6":
version "7.0.6"
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.6.tgz#f4c7ec43e81b319a9815115031709f26987891f0"
Expand Down Expand Up @@ -3568,6 +3578,11 @@ argparse@^1.0.7:
dependencies:
sprintf-js "~1.0.2"

argparse@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==

arr-diff@^1.0.1:
version "1.1.0"
resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-1.1.0.tgz#687c32758163588fef7de7b36fabe495eb1a399a"
Expand Down Expand Up @@ -3953,6 +3968,11 @@ balanced-match@^1.0.0:
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=

base-64@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/base-64/-/base-64-0.1.0.tgz#780a99c84e7d600260361511c4877613bf24f6bb"
integrity sha1-eAqZyE59YAJgNhURxId2E78k9rs=

base64-js@^1.0.2:
version "1.3.1"
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1"
Expand Down Expand Up @@ -6068,6 +6088,11 @@ expo-image-loader@~1.3.0:
resolved "https://registry.yarnpkg.com/expo-image-loader/-/expo-image-loader-1.3.0.tgz#06982a1a02f443ba6afa2cc7a4f0ccebc26a5415"
integrity sha512-kn+9hm42TtHi1wFEp/1nq63vp33/cIbypI2hYjGsL22PAC9yk1go1hs/ktKgVWVlgmi0ruwR09SrM6ndOs7s7w==

expo-intent-launcher@^8.4.0:
version "8.4.0"
resolved "https://registry.yarnpkg.com/expo-intent-launcher/-/expo-intent-launcher-8.4.0.tgz#c413a3aca06db2a02f3f7b0e40f5ffebc9ff63ed"
integrity sha512-nlsNEf7XpqWWXvGy6Bv8llGP348yvgg3dpCBIMveCosC49IjwDeXVJPT0VZWr6Q8leM/n+e+BWBDRiU6mfuwQA==

expo-permissions@~10.0.0:
version "10.0.0"
resolved "https://registry.yarnpkg.com/expo-permissions/-/expo-permissions-10.0.0.tgz#5b31c54d561d00c7e46cd02321bc3704c51c584b"
Expand Down Expand Up @@ -6319,6 +6344,11 @@ file-loader@~6.0.0:
loader-utils "^2.0.0"
schema-utils "^2.6.5"

file-saver@^2.0.5:
version "2.0.5"
resolved "https://registry.yarnpkg.com/file-saver/-/file-saver-2.0.5.tgz#d61cfe2ce059f414d899e9dd6d4107ee25670c38"
integrity sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==

file-type@^9.0.0:
version "9.0.0"
resolved "https://registry.yarnpkg.com/file-type/-/file-type-9.0.0.tgz#a68d5ad07f486414dfb2c8866f73161946714a18"
Expand Down Expand Up @@ -8198,6 +8228,13 @@ js-yaml@^3.13.1:
argparse "^1.0.7"
esprima "^4.0.0"

js-yaml@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.0.0.tgz#f426bc0ff4b4051926cd588c71113183409a121f"
integrity sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q==
dependencies:
argparse "^2.0.1"

jsbn@~0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
Expand Down Expand Up @@ -10908,6 +10945,14 @@ react-native-fit-image@^1.5.5:
dependencies:
prop-types "^15.5.10"

react-native-fs@^2.16.6:
version "2.16.6"
resolved "https://registry.yarnpkg.com/react-native-fs/-/react-native-fs-2.16.6.tgz#2901789a43210a35a0ef0a098019bbef3af395fd"
integrity sha512-ZWOooD1AuFoAGY3HS2GY7Qx2LZo4oIg6AK0wbC68detxwvX75R/q9lRqThXNKP6vIo2VHWa0fYUo/SrLw80E8w==
dependencies:
base-64 "^0.1.0"
utf8 "^3.0.0"

react-native-gesture-handler@~1.7.0:
version "1.7.0"
resolved "https://registry.yarnpkg.com/react-native-gesture-handler/-/react-native-gesture-handler-1.7.0.tgz#0ef74a5ba836832e497dc49eb1ce58baa6c617e5"
Expand Down Expand Up @@ -13028,6 +13073,11 @@ use@^3.1.0:
resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"
integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==

utf8@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/utf8/-/utf8-3.0.0.tgz#f052eed1364d696e769ef058b183df88c87f69d1"
integrity sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==

utif@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/utif/-/utif-2.0.1.tgz#9e1582d9bbd20011a6588548ed3266298e711759"
Expand Down

0 comments on commit 558defe

Please sign in to comment.