[PR #3146] [CLOSED] feat: added import a page from notion feature #5599

Closed
opened 2026-03-23 22:19:27 +00:00 by mirror · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/AppFlowy-IO/AppFlowy/pull/3146
Author: @Mukund-Tandon
Created: 8/8/2023
Status: Closed

Base: mainHead: import_notion_page_from_zip_file


📝 Commits (10+)

  • 1629a1e feat: added import a page from notion feature
  • 572142f refactor: use tuple instead of pair
  • 1476c0a refactor: fixed test file according to reviews on reviews
  • 0de1f4f refactor: use local keys for from markdown zip
  • 5868ef4 fix: close import panel after importing
  • 2f51230 refactor: refactored the code based on reviews
  • 4057f80 chore: added comments
  • f24ae36 refactor: fixed minor mistakes in code
  • ee63731 refactor: resolved merge conflicts
  • bfda147 fix: fixed merge conflicts

📊 Changes

17 files changed (+1299 additions, -31 deletions)

View changed files

frontend/appflowy_flutter/assets/images/external/notion-logo.svg (+6 -0)
frontend/appflowy_flutter/assets/test/workspaces/import_page_from_notion_test.zip (+0 -0)
frontend/appflowy_flutter/integration_test/import_page_from_notion_test.dart (+195 -0)
📝 frontend/appflowy_flutter/lib/startup/deps_resolver.dart (+3 -0)
frontend/appflowy_flutter/lib/workspace/application/sidebar/background_task_notification/background_task_notification_bloc.dart (+208 -0)
frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/background_task_notification/background_task_notifications.dart (+99 -0)
frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/background_task_notification/tasknotification.dart (+133 -0)
frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/import/import_from_notion_widget.dart (+166 -0)
📝 frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/import/import_panel.dart (+72 -28)
📝 frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/import/import_type.dart (+33 -0)
frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/import/importer/custom_parsers/subpage_import_parser.dart (+18 -0)
frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/import/importer/notion_importer.dart (+344 -0)
📝 frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/sidebar.dart (+13 -0)
📝 frontend/appflowy_flutter/lib/workspace/presentation/home/menu/view/view_add_button.dart (+1 -1)
📝 frontend/appflowy_flutter/pubspec.lock (+1 -1)
📝 frontend/appflowy_flutter/pubspec.yaml (+2 -0)
📝 frontend/resources/translations/en.json (+5 -1)

📄 Description

Fixes: #2911

Feature Preview

https://github.com/AppFlowy-IO/AppFlowy/assets/71614009/89eca69a-81b3-449a-b162-8652c71f18ce

This PR is related to https://github.com/AppFlowy-IO/AppFlowy/pull/2981


How This feature is implemented -

What does a page look like when exported from Notion-

We get a zip file containing the the main page markdown and a folder containing assets of the main page and also markdown files of subpages of the main page and folders containing the assets of the subpages and the sub-sub pages markdown files and so on.

image

How our main page markdown file look

We can see on line 5,6 we have two images inside the round brackets is the path where images are located and on line 19 ,21,23 are subpages

image

Markdown file of subpage

Here we have to look at the image path it starts with AppFlowy Subpage 1 instead of instead of AppFlowy Test which is the parent folder. This is because it’s asset lies in AppFlowy Sub 1 folder which is at the same level where the subpage markdown file is . This fact will be used late while importing.

image

image

How is the Importing working

  • First we iterate through the unzipfiles list and store all files and assets in there respective level in the levels list
  • Next step will be importing all the pages present starting from the last level.we are importing from the last level to handle the case of subpages . For parent page to show MentionBlock of the subpage it requires the SubPage viewID which can only be generated once it is imported so we import the lowest level sub page first and then go up storing each page viewID in a map (nameToID) which has key as the page name and value as the page viewID.
  • Next once we have all page imported we would move them under their respective parent

image
This class contains details of each page to be imported and the parent of that page which will be used for moving the page under correct parent in later steps
image
This class contains details of each level of the files. Like For example the main page will be level one , if it has a subpage it will be level 2 and if that subpage will have another subpage that would belong to level 3

image

When we are importing a markdown page how are we dealing with images

  • To deal with images we take all contents of a markdown file and pass it through _preProcessMarkdownFile function which returns us a string which is the contents of the markdown file but with changes. The changes this function performs are related to images . It will iterate through each line and if it finds something like ![name](path) this is how a image is represented in markdown . When we get this line is detected we get the path from this this path is actually the file name of image from the above list of unzip images so with the help of path we will get the image file and save it locally and change the current path to the path where the image is saved locally
  • Now the problem we can see here is as discussed before the path name in a sub page starts with AppFlowy Sub page instead of Appflowy test which is the root . In the above list also you can see that all the file name are starting from AppFlowy Test so if we directly pass this list to _preProcessMarkdownFile it wont be able find the image file.So this issue was solve by only passing the assets of a particular level to the _preProcessMarkdownFile function instead of all the assets and also during the process of adding files/assets in the levels list if a file is not added in the particular level then the first part(parent) from it name would be removed

How does Subpages are handled while importing a page ?

image

We are imported from level 3 to level 1. This is done because to handle the subpages . We use markdownToDocument function of appflowy-editor package to chick we pass the markdown contents of our page and it return us a Document. We also pass a custom-parser to this function which handles our subpage. Whenever it detects a subpage it replace the part with a MentionBlock(It contains the pageID of the page that is being Mentioned) . This is reason we are importing the lower level page first . We will import them and store their viewID in a map where the key is pageName and value is the pageID. When above level are being imported and there contents are passed through _preProcessMarkdownFile function this map(nameToId) is also passed in the function , whenever it finds a subpage which looks like name to {{AppFlowy-Subpage}}{$subpageName}{$subPageID} , this pattern is used in custom parser to detect the subpage part and convert it to mention block. We have stored the pageIds with name as key so we can easily get the pageID of subpage by using name from name .

PR Checklist

  • My code adheres to the AppFlowy Style Guide
  • I've listed at least one issue that this PR fixes in the description above.
  • I've added a test(s) to validate changes in this PR, or this PR only contains semantic changes.
  • All existing tests are passing.

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/AppFlowy-IO/AppFlowy/pull/3146 **Author:** [@Mukund-Tandon](https://github.com/Mukund-Tandon) **Created:** 8/8/2023 **Status:** ❌ Closed **Base:** `main` ← **Head:** `import_notion_page_from_zip_file` --- ### 📝 Commits (10+) - [`1629a1e`](https://github.com/AppFlowy-IO/AppFlowy/commit/1629a1eec55706e65a42e3053c19755913e90da3) feat: added import a page from notion feature - [`572142f`](https://github.com/AppFlowy-IO/AppFlowy/commit/572142f11d7a5f7f38ee7bc4f180628d822a0add) refactor: use tuple instead of pair - [`1476c0a`](https://github.com/AppFlowy-IO/AppFlowy/commit/1476c0a861d050fbe72c4c5a332e14fd13b57e86) refactor: fixed test file according to reviews on reviews - [`0de1f4f`](https://github.com/AppFlowy-IO/AppFlowy/commit/0de1f4f7d62f8552f591732a7b6b65563d1dd08d) refactor: use local keys for from markdown zip - [`5868ef4`](https://github.com/AppFlowy-IO/AppFlowy/commit/5868ef48cd7c6ff682d26dc3556bf2b0431e6a03) fix: close import panel after importing - [`2f51230`](https://github.com/AppFlowy-IO/AppFlowy/commit/2f512301126c7e02fa4ceef64fc208430067d411) refactor: refactored the code based on reviews - [`4057f80`](https://github.com/AppFlowy-IO/AppFlowy/commit/4057f807a2327ae7d1fc8f44ef46f4250bcf0cf1) chore: added comments - [`f24ae36`](https://github.com/AppFlowy-IO/AppFlowy/commit/f24ae36d61d0d5719f9f575651637fd2870a1648) refactor: fixed minor mistakes in code - [`ee63731`](https://github.com/AppFlowy-IO/AppFlowy/commit/ee637310db62392eefd91dc2c12ddb9dba9cabe8) refactor: resolved merge conflicts - [`bfda147`](https://github.com/AppFlowy-IO/AppFlowy/commit/bfda1479cf8cc73b39885f48bdcaa6584a900a67) fix: fixed merge conflicts ### 📊 Changes **17 files changed** (+1299 additions, -31 deletions) <details> <summary>View changed files</summary> ➕ `frontend/appflowy_flutter/assets/images/external/notion-logo.svg` (+6 -0) ➕ `frontend/appflowy_flutter/assets/test/workspaces/import_page_from_notion_test.zip` (+0 -0) ➕ `frontend/appflowy_flutter/integration_test/import_page_from_notion_test.dart` (+195 -0) 📝 `frontend/appflowy_flutter/lib/startup/deps_resolver.dart` (+3 -0) ➕ `frontend/appflowy_flutter/lib/workspace/application/sidebar/background_task_notification/background_task_notification_bloc.dart` (+208 -0) ➕ `frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/background_task_notification/background_task_notifications.dart` (+99 -0) ➕ `frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/background_task_notification/tasknotification.dart` (+133 -0) ➕ `frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/import/import_from_notion_widget.dart` (+166 -0) 📝 `frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/import/import_panel.dart` (+72 -28) 📝 `frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/import/import_type.dart` (+33 -0) ➕ `frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/import/importer/custom_parsers/subpage_import_parser.dart` (+18 -0) ➕ `frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/import/importer/notion_importer.dart` (+344 -0) 📝 `frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/sidebar.dart` (+13 -0) 📝 `frontend/appflowy_flutter/lib/workspace/presentation/home/menu/view/view_add_button.dart` (+1 -1) 📝 `frontend/appflowy_flutter/pubspec.lock` (+1 -1) 📝 `frontend/appflowy_flutter/pubspec.yaml` (+2 -0) 📝 `frontend/resources/translations/en.json` (+5 -1) </details> ### 📄 Description <!--- Thank you for submitting a pull request to AppFlowy. The team will dedicate their best efforts to reviewing and approving your pull request. If you have any questions about the project or feedback for us, please join our [Discord](https://discord.gg/wdjWUXXhtw). --> <!--- If your pull request adds a new feature, please drag and drop a video into this section to showcase what you've done! If not, you may delete this section. --> Fixes: #2911 ### Feature Preview https://github.com/AppFlowy-IO/AppFlowy/assets/71614009/89eca69a-81b3-449a-b162-8652c71f18ce This PR is related to https://github.com/AppFlowy-IO/AppFlowy/pull/2981 <!--- List at least one issue here that this PR addresses. If it fixes the issue, please use the [fixes](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/using-keywords-in-issues-and-pull-requests) keyword to close the issue. For example: fixes https://github.com/AppFlowy-IO/AppFlowy/pull/2106 --> --- <!--- Before you mark this PR ready for review, run through this checklist! --> # How This feature is implemented - ### What does a page look like when exported from Notion- We get a zip file containing the the main page markdown and a folder containing assets of the main page and also markdown files of subpages of the main page and folders containing the assets of the subpages and the sub-sub pages markdown files and so on. <img width="465" alt="image" src="https://github.com/AppFlowy-IO/AppFlowy/assets/71614009/ddf32214-5ef6-49ab-9343-ea5cf99edb98"> ### How our main page markdown file look We can see on line 5,6 we have two images inside the round brackets is the path where images are located and on line 19 ,21,23 are subpages ![image](https://github.com/AppFlowy-IO/AppFlowy/assets/71614009/a6b68b74-6e8c-4da4-88bc-e344b559c60b) ### Markdown file of subpage Here we have to look at the image path it starts with AppFlowy Subpage 1 instead of instead of AppFlowy Test which is the parent folder. This is because it’s asset lies in AppFlowy Sub 1 folder which is at the same level where the subpage markdown file is . This fact will be used late while importing. ![image](https://github.com/AppFlowy-IO/AppFlowy/assets/71614009/d09bb597-cdce-4b9a-bb3e-6b133db54fee) <img width="1036" alt="image" src="https://github.com/AppFlowy-IO/AppFlowy/assets/71614009/c50093b6-ea43-4f5f-ad39-94e5eac5f27a"> ### How is the Importing working - First we iterate through the unzipfiles list and store all files and assets in there respective level in the `levels` list - Next step will be importing all the pages present starting from the last level.we are importing from the last level to handle the case of subpages . For parent page to show `MentionBlock` of the subpage it requires the SubPage viewID which can only be generated once it is imported so we import the lowest level sub page first and then go up storing each page viewID in a map (**nameToID**) which has key as the page name and value as the page viewID. - Next once we have all page imported we would move them under their respective parent ![image](https://github.com/AppFlowy-IO/AppFlowy/assets/71614009/4e54a584-0990-4933-95e4-439ba9af58b7) This class contains details of each page to be imported and the parent of that page which will be used for moving the page under correct parent in later steps ![image](https://github.com/AppFlowy-IO/AppFlowy/assets/71614009/8ab013d4-9fa2-4715-854f-f7c46a973a8f) This class contains details of each level of the files. Like For example the main page will be level one , if it has a subpage it will be level 2 and if that subpage will have another subpage that would belong to level 3 <img width="939" alt="image" src="https://github.com/AppFlowy-IO/AppFlowy/assets/71614009/eebc8b57-588f-4ec7-a6b1-fa3f7fc7809b"> ### When we are importing a markdown page how are we dealing with images - To deal with images we take all contents of a markdown file and pass it through `_preProcessMarkdownFile` function which returns us a string which is the contents of the markdown file but with changes. The changes this function performs are related to images . It will iterate through each line and if it finds something like `![name](path)` this is how a image is represented in markdown . When we get this line is detected we get the path from this this path is actually the file name of image from the above list of unzip images so with the help of path we will get the image file and save it locally and change the current path to the path where the image is saved locally - Now the problem we can see here is as discussed before the path name in a sub page starts with AppFlowy Sub page instead of Appflowy test which is the root . In the above list also you can see that all the file name are starting from AppFlowy Test so if we directly pass this list to `_preProcessMarkdownFile` it wont be able find the image file.So this issue was solve by only passing the assets of a particular level to the _preProcessMarkdownFile function instead of all the assets and also during the process of adding files/assets in the levels list if a file is not added in the particular level then the first part(parent) from it name would be removed ### How does Subpages are handled while importing a page ? <img width="570" alt="image" src="https://github.com/AppFlowy-IO/AppFlowy/assets/71614009/aa503f27-c736-4b19-b7a8-8a2a36235d79"> We are imported from level 3 to level 1. This is done because to handle the subpages . We use `markdownToDocument` function of appflowy-editor package to chick we pass the markdown contents of our page and it return us a Document. We also pass a custom-parser to this function which handles our subpage. Whenever it detects a subpage it replace the part with a `MentionBlock`(It contains the pageID of the page that is being Mentioned) . This is reason we are importing the lower level page first . We will import them and store their viewID in a map where the key is pageName and value is the pageID. When above level are being imported and there contents are passed through `_preProcessMarkdownFile` function this map(nameToId) is also passed in the function , whenever it finds a subpage which looks like [name](path) to `{{AppFlowy-Subpage}}{$subpageName}{$subPageID} `, this pattern is used in custom parser to detect the subpage part and convert it to mention block. We have stored the pageIds with name as key so we can easily get the pageID of subpage by using name from [name](page) . #### PR Checklist - [X] My code adheres to the [AppFlowy Style Guide](https://appflowy.gitbook.io/docs/essential-documentation/contribute-to-appflowy/software-contributions/submitting-code/style-guides) - [X] I've listed at least one issue that this PR fixes in the description above. - [X] I've added a test(s) to validate changes in this PR, or this PR only contains semantic changes. - [X] All existing tests are passing. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
mirror 2026-03-23 22:19:27 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
AppFlowy-IO/AppFlowy#5599
No description provided.